'Undo Move' feature Implementation for a 2D Array Board Game
Hi all
Using an ArrayList, I am trying to implement an undo move feature whereby, a user can select an option ‘Z’, which makes possible a multi-level ‘undo’. In other words, if the user selects ‘Z’ then the previous move is undone, if he immediately selects ‘Z’ again, the move before that is undone, and so on.
I have been able to get the code to add new move objects each time a valid move ‘U’, ’D’, ’L’, ’R’ is made and to also remove the last object, each time ‘Z’ is pressed.
My question is, how do I make the player movements (coordinates) and eaten doughnuts (Boolean) rely on the last object in the ArrayList so that when ‘Z’ is pressed and the last object in the ArrayList is removed, the player movements and eaten doughnuts will now rely on the new last object in the ArrayList to create the ‘undo’ effect? Hope my question makes sense.
The ‘Z’ implementation is the last case in the switch statement in the move method.
My classes are below:
package secondproject;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class Game {
private static final int BOARD_SIZE = 10;
private static final int INITIAL_PLAYER_COL = 0;
private static final int INITIAL_PLAYER_ROW = BOARD_SIZE - 1;
private static final int HOME_COL = BOARD_SIZE - 1;
private static final int HOME_ROW = 0;
private static final int WALL_LENGTH = 5;
private static final char PLAYER_CHAR = 'P';
private static final char HOME_CHAR = 'H';
private static final char WALL_CHAR = 'X';
private static final char FREE_SQUARE_CHAR = '.';
private static final char DOUGHNUT_CHAR = '#';
private static final char UP_MOVE_CHAR = 'U';
private static final char DOWN_MOVE_CHAR = 'D';
private static final char LEFT_MOVE_CHAR = 'L';
private static final char RIGHT_MOVE_CHAR = 'R';
private static final char UNDO_MOVE_CHAR = 'Z';
private static final char TRAIL_CHAR = 'M';
private static char[][] board = new char[BOARD_SIZE][BOARD_SIZE];
private static Scanner scan = new Scanner(System.in);
private static Scanner keyBoard = new Scanner(System.in);
private static int playerCol = INITIAL_PLAYER_COL;
private static int playerRow = INITIAL_PLAYER_ROW;
private static int nbrDoughnuts = 0;
private static int nbrMoves = 0;
private static Random random = new Random();
private static int lives = 1;
private static int doughnutLives;
private static boolean doughnutCheck;
static ArrayList<Move> movement = new ArrayList<Move>();
public static void main(String[] args) {
setUpBoard();
showBoard();
String opt;
do {
System.out.print("Next option ?");
opt = scan.next();
char opt1 = opt.charAt(0);
if (opt1 == UP_MOVE_CHAR || opt1 == DOWN_MOVE_CHAR || opt1 == LEFT_MOVE_CHAR || opt1 == RIGHT_MOVE_CHAR || opt1 == UNDO_MOVE_CHAR) {
move(opt1);
} else {
System.out.println("Allowed commands are: + " + UP_MOVE_CHAR + "," + DOWN_MOVE_CHAR + "," + LEFT_MOVE_CHAR + "," + RIGHT_MOVE_CHAR);
}
showBoard();
System.out.println("Number of moves made = " + nbrMoves);
System.out.println("Number of doughnuts eaten = " + nbrDoughnuts);
System.out.println("Lives = " + lives);
} while (board[HOME_ROW][HOME_COL] == HOME_CHAR);
System.out.println("Thank you and goodbye");
}
/**
* Set up the initial state of the board
*/
private static void setUpBoard() {
intialiseBoard(); // Fill the board with . characters
//Add the first vertical wall
int v1StartCol = 1 + random.nextInt(BOARD_SIZE - 2);
int v1StartRow = 1 + random.nextInt(BOARD_SIZE - WALL_LENGTH - 1);
addVerticalWall(v1StartCol, v1StartRow, WALL_LENGTH);
//Add the second vertical wall
int v2StartCol;
do {
v2StartCol = 1 + random.nextInt(BOARD_SIZE - 2);
} while (v2StartCol == v1StartCol);
int v2StartRow = 1 + random.nextInt(BOARD_SIZE - WALL_LENGTH - 1);
addVerticalWall(v2StartCol, v2StartRow, WALL_LENGTH);
//Add the horizontal wall
int h1StartRow = 1 + random.nextInt(BOARD_SIZE - 2);
int h1StartCol = 1 + random.nextInt(BOARD_SIZE - WALL_LENGTH - 1);
addHorizontalWall(h1StartCol, h1StartRow, WALL_LENGTH);
//Add the dougnuts
int nbrDoughnutsAdded = 0;
while (nbrDoughnutsAdded < 5) {
int dRow = 1 + random.nextInt(BOARD_SIZE - 2);
int dCol = 1 + random.nextInt(BOARD_SIZE - 2);
if (board[dRow][dCol] == FREE_SQUARE_CHAR) {
board[dRow][dCol] = DOUGHNUT_CHAR;
nbrDoughnutsAdded++;
}
}
//Add the player and the home square
board[playerRow][playerCol] = PLAYER_CHAR;
board[HOME_ROW][HOME_COL] = HOME_CHAR;
}
/**
* Add a vertical wall to the board
*
* #param startCol Column on which wall is situated
* #param startRow Row on which top of wall is situated
* #param length Number of squares occupied by wall
*/
private static void addVerticalWall(int startCol, int startRow, int length) {
for (int row = startRow; row < startRow + length; row++) {
board[row][startCol] = WALL_CHAR;
}
}
/**
* Add a horizontal wall to the board
*
* #param startCol Column on which leftmost end of wall is situated
* #param startRow Row on which wall is situated
* #param length Number of squares occupied by wall
*/
private static void addHorizontalWall(int startCol, int startRow, int length) {
for (int col = startCol; col < startCol + length; col++) {
board[startRow][col] = WALL_CHAR;
}
}
/**
* Display the board
*/
private static void showBoard() {
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[row].length; col++) {
System.out.print(board[row][col]);
}
System.out.println();
}
}
/**
* Fill the board with FREE_SQUARE_CHAR characters.
*/
private static void intialiseBoard() {
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[row].length; col++) {
board[row][col] = FREE_SQUARE_CHAR;
}
System.out.println();
}
}
/**
* Move the player
*
* #param moveChar Character indicating the move to be made
*/
private static void move(char moveChar) {
int newCol = playerCol;
int newRow = playerRow;
switch (moveChar) {
case UP_MOVE_CHAR:
if (lives == 1) {
newRow--;
} else if (lives > 1) {
int number = keyBoard.nextInt();
if (number <= lives) {
newRow = newRow - number;
} else {
checkLives();
}
}
break;
case DOWN_MOVE_CHAR:
if (lives == 1) {
newRow++;
} else if (lives > 1) {
squareNumberPrompt();
int number = keyBoard.nextInt();
if (number <= lives) {
newRow = newRow + number;
} else {
checkLives();
}
}
break;
case LEFT_MOVE_CHAR:
if (lives == 1) {
newCol--;
} else if (lives > 1) {
squareNumberPrompt();
int number = keyBoard.nextInt();
if (number <= lives) {
newCol = newCol - number;
} else {
checkLives();
}
}
break;
case RIGHT_MOVE_CHAR:
if (lives == 1) {
newCol++;
} else if (lives > 1) {
squareNumberPrompt();
int number = keyBoard.nextInt();
if (number <= lives) {
newCol = newCol + number;
} else {
checkLives();
}
}
break;
case UNDO_MOVE_CHAR:
if (movement.size() >= 1) {
movement.remove(movement.size() - 1);
System.out.println("The decreasing size of the arraylist is now " + movement.size());
} else if (movement.size() < 1) {
System.out.println("There is no move to be undone!");
}
break;
}
if (newRow < 0 || newRow >= BOARD_SIZE || newCol < 0 || newCol >= BOARD_SIZE) {
System.out.println("Sorry that move takes you off the board!");
} else {
char dest = board[newRow][newCol];
if (dest == WALL_CHAR) {
System.out.println("Sorry you landed on a wall!");
} else {
nbrMoves++;
if (dest == DOUGHNUT_CHAR) {
doughnutCheck = true;
nbrDoughnuts++;
doughnutLives++;
lives = (doughnutLives + 1);
}
board[playerRow][playerCol] = FREE_SQUARE_CHAR;
playerCol = newCol;
playerRow = newRow;
board[playerRow][playerCol] = PLAYER_CHAR;
}
}
if (moveChar == UP_MOVE_CHAR || moveChar == DOWN_MOVE_CHAR || moveChar == LEFT_MOVE_CHAR || moveChar == RIGHT_MOVE_CHAR) {
movement.add(new Move(playerCol, playerRow, newCol, newRow, doughnutCheck));
System.out.println("The increasing size of the arraylist is now " + movement.size());
}
}
public static void squareNumberPrompt() {
System.out.println("Enter the number of squares to be moved");
}
public static void checkLives() {
System.out.println("Invalid number! The number must be"
+ " equal to or less than the number of lives you have");
}
}
package secondproject;
import java.util.ArrayList;
public class Move {
private static int pColumn;
private static int pRow;
private static int nCol;
private static int nRow;
private static boolean dCheck;
public Move(int playerCol, int playerRow, int newCol, int newRow, boolean doughnutCheck) {
pColumn = playerCol;
pRow = playerRow;
nCol = newCol;
nRow = newRow;
dCheck = doughnutCheck;
}
public int getFromCol() {
return pColumn;
}
public int getFromRow() {
return pRow;
}
public int getToCol() {
return nCol;
}
public int getToRow() {
return nRow;
}
public boolean isDoughnutEaten() {
return dCheck;
}
}
After looking a bit closer at your code, you basically already have everything you need for that undo in place.
Maybe even a bit too much :D You don't actually need from and to positions. only the position the move put you into would suffice.
So when you press Z, your code should look somewhat like this:
Move lastMove = movement.remove(movement.size() - 1);
playerCol = movement.get(movement.size() - 1).getToCol();
playerRow = movement.get(movement.size() - 1).getToRow();
board[playerRow][playerCol] = PLAYER_CHAR;
if (lastMove.isDoughnutEaten()) {
int dCol = lastMove.getToCol();
int dRow = lastMove.getToRow();
board[dRow][dCol] = DOUGHNUT_CHAR;
nbrDoughnutsAdded--;
}
Right now, you could read the new player position from the pColumn and pRow values of lastMove, but as I said, that's kind of overkill because you can read it from the new last element of the list anyway.
Keep in mind that you still need to catch special cases (like when you undo the very first move. In this case, you'd need to read the former player position from your static variables that define the starting point instead of the last element of the list (that won't exist anymore))
Related
I am trying to implement an undo move feature using Arraylist. I have created an Arraylist that holds Move objects, upon every valid move (‘U’, ’D’, ’L’, ’R’), it adds a new move object in the Arraylist. That is working properly, at this stage, I am currently trying to get it to remove the last object from the Arraylist, each time ‘Z’ is pressed but it only does one removal and any other subsequent presses of ‘Z’ does not change anything.
Can anyone please look at the code below and let me know what I am doing wrong?
The implementation/issue is in the move method, the last case in the switch statement.
package secondproject;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class Game {
private static final int BOARD_SIZE = 10;
private static final int INITIAL_PLAYER_COL = 0;
private static final int INITIAL_PLAYER_ROW = BOARD_SIZE - 1;
private static final int HOME_COL = BOARD_SIZE - 1;
private static final int HOME_ROW = 0;
private static final int WALL_LENGTH = 5;
private static final char PLAYER_CHAR = 'P';
private static final char HOME_CHAR = 'H';
private static final char WALL_CHAR = 'X';
private static final char FREE_SQUARE_CHAR = '.';
private static final char DOUGHNUT_CHAR = '#';
private static final char UP_MOVE_CHAR = 'U';
private static final char DOWN_MOVE_CHAR = 'D';
private static final char LEFT_MOVE_CHAR = 'L';
private static final char RIGHT_MOVE_CHAR = 'R';
private static final char UNDO_MOVE_CHAR = 'Z';
private static final char TRAIL_CHAR = 'M';
private static char[][] board = new char[BOARD_SIZE][BOARD_SIZE];
private static Scanner scan = new Scanner(System.in);
private static Scanner keyBoard = new Scanner(System.in);
private static int playerCol = INITIAL_PLAYER_COL;
private static int playerRow = INITIAL_PLAYER_ROW;
private static int nbrDoughnuts = 0;
private static int nbrMoves = 0;
private static Random random = new Random();
private static int lives = 1;
private static int doughnutLives;
private static boolean doughnutCheck;
static ArrayList<Move> movement = new ArrayList<Move>();
public static void main(String[] args) {
setUpBoard();
showBoard();
String opt;
do {
System.out.print("Next option ?");
opt = scan.next();
char opt1 = opt.charAt(0);
if (opt1 == UP_MOVE_CHAR || opt1 == DOWN_MOVE_CHAR
|| opt1 == LEFT_MOVE_CHAR || opt1 == RIGHT_MOVE_CHAR
|| opt1 == UNDO_MOVE_CHAR) {
move(opt1);
//undoMove(opt1);
} else {
System.out.println("Allowed commands are: + " + UP_MOVE_CHAR
+ "," + DOWN_MOVE_CHAR + "," + LEFT_MOVE_CHAR
+ "," + RIGHT_MOVE_CHAR);
}
showBoard();
System.out.println("Number of moves made = " + nbrMoves);
System.out.println("Number of doughnuts eaten = " + nbrDoughnuts);
System.out.println("Lives = " + lives);
} while (board[HOME_ROW][HOME_COL] == HOME_CHAR);
System.out.println("Thank you and goodbye");
}
/**
* Set up the initial state of the board
*/
private static void setUpBoard() {
intialiseBoard(); // Fill the board with . characters
//Add the first vertical wall
int v1StartCol = 1 + random.nextInt(BOARD_SIZE - 2);
int v1StartRow = 1 + random.nextInt(BOARD_SIZE - WALL_LENGTH - 1);
addVerticalWall(v1StartCol, v1StartRow, WALL_LENGTH);
//Add the second vertical wall
int v2StartCol;
do {
v2StartCol = 1 + random.nextInt(BOARD_SIZE - 2);
} while (v2StartCol == v1StartCol);
int v2StartRow = 1 + random.nextInt(BOARD_SIZE - WALL_LENGTH - 1);
addVerticalWall(v2StartCol, v2StartRow, WALL_LENGTH);
//Add the horizontal wall
int h1StartRow = 1 + random.nextInt(BOARD_SIZE - 2);
int h1StartCol = 1 + random.nextInt(BOARD_SIZE - WALL_LENGTH - 1);
addHorizontalWall(h1StartCol, h1StartRow, WALL_LENGTH);
//Add the dougnuts
int nbrDoughnutsAdded = 0;
while (nbrDoughnutsAdded < 5) {
int dRow = 1 + random.nextInt(BOARD_SIZE - 2);
int dCol = 1 + random.nextInt(BOARD_SIZE - 2);
if (board[dRow][dCol] == FREE_SQUARE_CHAR) {
board[dRow][dCol] = DOUGHNUT_CHAR;
nbrDoughnutsAdded++;
}
}
//Add the player and the home square
board[playerRow][playerCol] = PLAYER_CHAR;
board[HOME_ROW][HOME_COL] = HOME_CHAR;
}
/**
* Add a vertical wall to the board
*
* #param startCol Column on which wall is situated
* #param startRow Row on which top of wall is situated
* #param length Number of squares occupied by wall
*/
private static void addVerticalWall(int startCol, int startRow, int length) {
for (int row = startRow; row < startRow + length; row++) {
board[row][startCol] = WALL_CHAR;
}
}
/**
* Add a horizontal wall to the board
*
* #param startCol Column on which leftmost end of wall is situated
* #param startRow Row on which wall is situated
* #param length Number of squares occupied by wall
*/
private static void addHorizontalWall(int startCol, int startRow, int length) {
for (int col = startCol; col < startCol + length; col++) {
board[startRow][col] = WALL_CHAR;
}
}
/**
* Display the board
*/
private static void showBoard() {
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[row].length; col++) {
System.out.print(board[row][col]);
}
System.out.println();
}
}
/**
* Fill the board with FREE_SQUARE_CHAR characters.
*/
private static void intialiseBoard() {
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[row].length; col++) {
board[row][col] = FREE_SQUARE_CHAR;
}
System.out.println();
}
}
/**
* Move the player
*
* #param moveChar Character indicating the move to be made
*/
private static void move(char moveChar) {
int newCol = playerCol;
int newRow = playerRow;
switch (moveChar) {
case UP_MOVE_CHAR:
if (lives == 1) {
newRow--;
} else if (lives > 1) {
int number = keyBoard.nextInt();
if (number <= lives) {
newRow = newRow - number;
} else {
checkLives();
}
}
break;
case DOWN_MOVE_CHAR:
if (lives == 1) {
newRow++;
} else if (lives > 1) {
squareNumberPrompt();
int number = keyBoard.nextInt();
if (number <= lives) {
newRow = newRow + number;
} else {
checkLives();
}
}
break;
case LEFT_MOVE_CHAR:
if (lives == 1) {
newCol--;
} else if (lives > 1) {
squareNumberPrompt();
int number = keyBoard.nextInt();
if (number <= lives) {
newCol = newCol - number;
} else {
checkLives();
}
}
break;
case RIGHT_MOVE_CHAR:
if (lives == 1) {
newCol++;
} else if (lives > 1) {
squareNumberPrompt();
int number = keyBoard.nextInt();
if (number <= lives) {
newCol = newCol + number;
} else {
checkLives();
}
}
break;
case UNDO_MOVE_CHAR:
if (movement.size() > 1) {
movement.remove(movement.size() - 1);
System.out.println("The decreasing size of the arraylist is now " + movement.size()); //Test Line
} else if (movement.size() < 1) {
System.out.println("There is no move to be undone!");
}
break;
}
if (newRow < 0 || newRow >= BOARD_SIZE || newCol < 0 || newCol >= BOARD_SIZE) {
System.out.println("Sorry that move takes you off the board!");
} else {
char dest = board[newRow][newCol];
if (dest == WALL_CHAR) {
System.out.println("Sorry you landed on a wall!");
} else {
nbrMoves++;
if (dest == DOUGHNUT_CHAR) {
doughnutCheck = true;
nbrDoughnuts++;
doughnutLives++;
lives = (doughnutLives + 1);
}
board[playerRow][playerCol] = FREE_SQUARE_CHAR;
playerCol = newCol;
playerRow = newRow;
board[playerRow][playerCol] = PLAYER_CHAR;
}
}
movement.add(new Move(playerCol, playerRow, newCol, newRow, doughnutCheck));
System.out.println("The increasing size of the arraylist is now " + movement.size()); //Test Line
}
public static void squareNumberPrompt() {
System.out.println("Enter the number of squares to be moved");
}
public static void checkLives() {
System.out.println("Invalid number! The number must be"
+ " equal to or less than the number of lives you have");
}
}
After running your code, I think I know the problem. When you undo a move, you remove the last move in the ArrayList. The issue is, however, that you count undo as a move itself. So when you undo, then undo again, you "undo" the previous undo. For example, this console output after undoing shows how you increment back to three moves:
Next option ? Z // Do undo
The decreasing size of the arraylist is now 2 // The old move is undone
The increasing size of the arraylist is now 3 // However, the undo is counted as a move
So then, subsequent calls to undo would simply repeat over and over, undoing your undos and then adding them back, basically getting stuck in a loop. To fix this, simply change
movement.add(new Move(playerCol, playerRow, newCol, newRow, doughnutCheck));
to have an if statement checking for the undo move and block it from being added to the array, which would look like:
if (moveChar != UNDO_MOVE_CHAR)
movement.add(new Move(playerCol, playerRow, newCol, newRow, doughnutCheck));
EDIT: Also see #user7254424's answer, as his is another reason why you will have issues.
if (movement.size() > 1) {
movement.remove(movement.size() - 1);
System.out.println("The decreasing size of the arraylist is now " + movement.size()); //Test Line
} else if (movement.size() < 1) {
System.out.println("There is no move to be undone!");
}
You don't account for when movement.size() == 1, this may be where your issue is.
I am currently looking for a way of scanning a 2D matrix in Java for a number. Precisely, if in my matrix, there are numbers from 0 to 9, how do I "locate" the 0? This is intended for creating a Minesweeper game.
Here is what I have written so far. It is not complete. All I want is a clue on how to complete it.
class DemineurStackOverflow {
public static void minesweeper () {
int champDeMine[][];
boolean résultat[][];
int mine;
char réponse;
champDeMine = new int[longueur][largeur]; //Differenf from the matrix "champDeMines" from the second method
Arrays.asList(champDeMine).contains(0);
mine = (int) Math.floor(Math.random()*nbMines + 1);
System.out.println("Ajustement du nombre de mines en cours...");
if (mine < nbMines) {
for (mine = (int) Math.floor(Math.random()*nbMines + 1); mine < nbMines; mine++);
} else {
for (mine = (int) Math.floor(Math.random()*nbMines + 1); mine > nbMines; mine--);
}
if (mine == nbMines){
System.out.println("Chargement des mines OK.");
}
}
public static int [][] calculeProximité ( boolean [][] champDeMines ){
int row; //row index for prescence of 0, same value as longueur
int col; //column index for presence of 0, same value as largeur
int mine;
champDeMines = new boolean[row][col];
if (champDeMines = 0) {
champDeMines = mine;
}
//Here I am trying to figure a way of finding the 0s in this champDeMines matrix.
return (new int[champDeMines.length][champDeMines[0].length]);
}
}
The first method consists in generating an array from variables "longueur" and "largeur". The number of "mines" is supposed to represent the number 0 (which is why I want to scan for a 0), at random places. The second method consists in finding the "mines" in the array created. That is what I have trouble doing. Do you have any clues for completing the second method? I am simply looking for clues because I am learning to program in Java!
Thank you very much, your help is most certainly appreciated!
This is how minesweeper field population could look from the code provided. I hope you get the clue from the comments and do not hesitate to ask if anything is unclear.
import java.util.Random;
public class Demineur
{
// Here come CONSTANTS
public static final int MAX_MINES = 30;
public static final boolean MINE = true;
// Let's have a field 12x12 size
public static final int LONGEUR = 12;
public static final int LARGEUR = 12;
// each field contains number of mines it has access to
public static int champDeMine[][] = new int[LONGEUR][LARGEUR];
// each field can contain mine or be empty
public static boolean champDeMines[][] = new boolean[LONGEUR][LARGEUR];
public static void minesweeper()
{
Random random = new Random();
int mine ;
System.out.println("Ajustement du nombre de mines en cours...");
int nbMines = random.nextInt(MAX_MINES);
/**
* Let's plant mines. :-E
* Unoptimal but will suffice for demonstration purpose.
*/
int minesLeftToPlant = nbMines;
int skip = 0;
boolean planted = false;
while (minesLeftToPlant > 0)
{
skip = random.nextInt(LONGEUR*LARGEUR);
planted = false;
while (!planted && minesLeftToPlant > 0 && skip > 0)
{
for (int y = 0; !planted && minesLeftToPlant > 0 && y < LONGEUR; y++)
{
for (int x = 0; !planted && minesLeftToPlant > 0 && x < LARGEUR; x++)
{
if ( !MINE == champDeMines[y][x]
&& 0 == skip)
{
champDeMines[y][x] = MINE;
minesLeftToPlant--;
planted = true;
}
else
{
skip--;
}
}
}
}
}
System.out.println("Chargement des "+ nbMines +" mines OK.");
}
public static void calculeProximite()
{
int row ; //row index for prescence of 0, same value as longueur
int col ; //column index for presence of 0, same value as largeur
int mine;
//Check for each field it's neighbors and calculate which of them are mines
for (row = 0; row < LONGEUR; row++)
{
for (col = 0; col < LARGEUR; col++)
{
champDeMine[row][col] = numberOfMines(row,col);
}
}
}
public static void printChampDeMine()
{
for (int row = 0; row < LONGEUR; row++)
{
for (int col = 0; col < LARGEUR; col++)
{
System.out.print("'" + champDeMine[row][col] + "' ");
}
System.out.println();
}
}
public static void printChampDemines()
{
for (int row = 0; row < LONGEUR; row++)
{
for (int col = 0; col < LARGEUR; col++)
{
System.out.print("'" + (champDeMines[row][col] ? "m" : "e") + "' ");
}
System.out.println();
}
}
public static int numberOfMines(int row, int col)
{
return add(hasMine(row , col + 1))
+ add(hasMine(row - 1, col + 1))
+ add(hasMine(row - 1, col ))
+ add(hasMine(row - 1, col - 1))
+ add(hasMine(row , col - 1))
+ add(hasMine(row + 1, col - 1))
+ add(hasMine(row + 1, col ))
+ add(hasMine(row + 1, col + 1));
}
public static boolean hasMine(int row, int col)
{
return row >= 0 && col >= 0 && row < LONGEUR && col < LARGEUR
&& isMine(champDeMines[row][col]);
}
public static boolean isMine(boolean x)
{
return MINE == x;
}
public static int add(boolean c)
{
return c ? 1 : 0;
}
public static void main(String[] args)
{
minesweeper();
System.out.println("Champ de mines");
printChampDemines();
System.out.println("Champ de mine");
calculeProximite();
printChampDeMine();
}
}
I am making a tic tac toe game for n number of players on a nxn board, but the winning condition is aways 3 on a row. My so far solution to the problem is: when a move is made the program will check the following square for 3 on a row.
(x-1,y+1) (x,y+1) (x+1,y+1)
(x-1,y) (x,y) (x+1,y)
(x-1,y-1) (x,y-1) (x+1,y-1)
It will check the top (x-1,y+1) (x,y+1) (x+1,y+1) bottom(x-1,y-1) (x,y-1) (x+1,y-1)
sides(x+1,y+1) (x+1,y) (x+1,y-1) , (x-1,y+1) (x-1,y) (x-1,y-1) , the diagonals and the ones going through the middle(x,y).
my code so far is:
public int checkWinning() {
for(int a = 1; a < size-1; a++){
for(int b = 1; b < size-1; b++){
if (board[a][b] == board[a+1][b] && board[a][b] == board[a-1][b]){
return board[a][b];
}else if(board[a][b] == board[a][b+1] && board[a][b] == board[a][b-1]){
return board[a][b];
}else if(board[a][b] == board[a+1][b-1] && board[a][b] == board[a-1][b+1]){
return board[a][b];
}else if(board[a][b] == board[a+1][b+1] && board[a][b] == board[a-1][b-1]){
return board[a][b];
}
}
}
for(int d = 1; d < size-1; d++){
if (board[0][d] == board[0][d-1] && board[0][d] == board[0][d+1]){
return board[0][d];
} else if (board[size-1][d] == board[size-1][d-1] && board[size-1][d] == board[size-1][d+1]){
return board[size-1][d];
}
}
for(int c = 1; c < size-1; c++){
if (board[c][0] == board[c-1][0] && board[c][0] == board[c+1][0]){
return board[c][0];
}else if(board[c][size-1] == board[c-1][size-1] && board[c][size-1] == board[c+1][size-1]){
return board[c][size-1];
}
}
return 0;
}
where the first section is where I check the ones through the middle and diagonals. the second section I check the top an bottom and the top and the thrid section checks the sides.
When it returns 0 is means that there are no winner yet.
#override
public void checkResult() {
int winner = this.board.checkWinning();
if (winner > 0) {
this.ui.showResult("Player "+winner+" wins!");
}
if (this.board.checkFull()) {
this.ui.showResult("This is a DRAW!");
}
}
Board[x][y] -> 2-dimensional array representing the board, The coordinates are counted from top-left (0,0) to bottom-right (size-1, size-1), board[x][y] == 0 signifies free at position (x,y), board[x][y] == i for i > 0 signifies that Player i made a move on (x,y), just so you know it.
my problem is that when i expands the board to a size larger than 3x3 the program somehow overwrites it self or a does not check every thing sides top and bottom every time, and I can't seem too se why.
EDIT:
played with the app for a few minutes... interesting results
java -jar tic-tac-toe.jar 5 20
It was a cats game!!
|1|1|5|5|1|3|5|3|1|5|2|5|1|1|2|
|2|3|2|3|1|5|3|5|3|2|3|1|5|2|2|
|5|4|5|4|1|5|5|4|2|1|4|5|4|2|2|
|3|2|1|5|5|5|2|4|5|3|4|1|2|4|2|
|3|4|1|2|5|4|1|1|4|5|1|3|3|4|1|
|1|5|4|4|3|2|5|1|3|5|1|3|5|3|4|
|2|5|1|4|3|3|3|5|3|1|1|4|3|4|4|
|1|4|5|1|1|5|4|5|2|4|1|1|5|4|3|
|1|3|2|1|4|2|4|3|3|4|5|2|4|3|3|
|5|1|1|3|3|4|4|4|2|2|1|4|3|2|5|
|2|2|3|1|5|5|4|1|3|5|3|2|3|3|2|
|2|4|2|4|4|1|3|1|1|3|1|2|1|2|2|
|2|5|5|1|4|3|4|5|5|4|5|3|3|5|2|
|4|5|2|1|5|3|2|1|3|2|2|2|2|4|4|
|4|1|1|4|5|4|5|4|2|2|3|3|2|2|3|
Played 100 games:
Number wins by Player1: 0
Number wins by Player2: 0
Number wins by Player3: 0
Number wins by Player4: 0
Number wins by Player5: 0
Number of ties: 100
didn't scroll through all 100 games to find the winning board, but I thought this was interesting:
java -jar tic-tac-toe.jar 2 10
Player2 won the game!
|1|1|2|1|2|2| |2|1|2|
|2|2|2|2|2|2|2|2|2|2|
|2|1|2|2|2|1|1|1|1|1|
|1|1|1|1|2|1|2|1|1|1|
|2|2| |1|2|1|1|1|1|2|
|2|2|2|1|1|1| |1|2|2|
|2|2|1|2|2|2|2|2|1|1|
| | |2|2|2|2| |1|1|1|
|1|1|2|2|2|1|1|1|1| |
| | |1|1|1|1|1|2|1| |
Played 100 games:
Number wins by Player1: 0
Number wins by Player2: 1
Number of ties: 99
This does answer your question... but I took it a bit far... decided to implement the solution.
Instead of counting matches... I just check from teh point the last player plays, if all marks in a row column and diagnal match the players, he wins.
package com.clinkworks.example;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TicTacToe {
private static final String TIE = "TIE";
private static final Map<String, Integer> gamesToWinsMap = new HashMap<String, Integer>();
/**
* accepts input in the following format:
*
* playerCount rowCount columnCount (sets the game with the n players, n columns, and n rows)
* - java -jar tic-tac-toe.jar 2 3 3
* PlayerCount squareSize (defaults to a game with rows and cols the same as squareSize and the player count given)
* - java -jar tic-tac-toe.jar 2 3
* PlayerCount (defaults to a 3 by 3 game)
* - java -jar tic-tac-toe.jar 2
* no input (defaults to a 3 by 3 game with 2 players)
* - java -jar tic-tac-toe.jar
* #param args
*/
public static void main(String[] args) {
int playerCount = 2;
int rows = 3;
int cols = 3;
if(args.length == 3){
playerCount = Integer.valueOf(args[0]);
rows = Integer.valueOf(args[1]);
cols = Integer.valueOf(args[2]);
}
if(args.length == 2){
playerCount = Integer.valueOf(args[0]);
rows = Integer.valueOf(args[1]);
cols = rows;
}
if(args.length == 1){
playerCount = Integer.valueOf(args[0]);
}
for(int i = 1; i <= playerCount; i++){
gamesToWinsMap.put("Player" + i, 0);
}
//lets play 100 games and see the wins and ties
playGames(100, playerCount, rows, cols);
for(int i = 1; i <= playerCount; i++){
System.out.println("Number wins by Player" + i + ": " + gamesToWinsMap.get("Player" + i));
}
System.out.println("Number of ties: " + gamesToWinsMap.get(TIE));
}
public static void playGames(int gamesToPlay, int playerCount, int rows, int cols) {
//play a new game each iteration, in our example, count = 100;
for (int i = 0; i < gamesToPlay; i++) {
playGame(playerCount, rows, cols);
}
}
public static void playGame(int playerCount, int rows, int cols) {
//create a new game board. this initalizes our 2d array and lets the complexity of handling that
// array be deligated to the board object.
Board board = new Board(playerCount, rows, cols);
//we are going to generate a random list of moves. Heres where we are goign to store it
List<Move> moves = new ArrayList<Move>();
//we are creating moves for each space on the board.
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
moves.add(new Move(row, col));
}
}
//randomize the move list
Collections.shuffle(moves);
//do each move
for (Move move : moves) {
board.play(move);
if(gameOver(board)){
break;
}
}
}
public static boolean gameOver(Board board){
if (board.whoWon() != null) {
System.out.println(board.whoWon() + " won the game!");
System.out.println(board);
Integer winCount = gamesToWinsMap.get(board.whoWon());
winCount = winCount == null ? 1 : winCount + 1;
gamesToWinsMap.put(board.whoWon(), winCount);
return true;
} else if (board.movesLeft() == 0) {
System.out.println("It was a cats game!!");
System.out.println(board);
Integer tieCount = gamesToWinsMap.get(TIE);
tieCount = tieCount == null ? 1 : tieCount + 1;
gamesToWinsMap.put(TIE, tieCount);
return true;
}
return false;
}
public static class Move {
private int row;
private int column;
public Move(int row, int column) {
this.row = row;
this.column = column;
}
public int getRow() {
return row;
}
public int getColumn() {
return column;
}
}
public static class Board {
private final int rowSize;
private final int columnSize;
private final Integer[][] gameBoard;
private int playerCount;
private int currentPlayer;
private String winningPlayer;
public Board() {
gameBoard = new Integer[3][3];
currentPlayer = 1;
winningPlayer = null;
this.rowSize = 3;
this.columnSize = 3;
playerCount = 2;
}
public Board(int players) {
gameBoard = new Integer[3][3];
currentPlayer = 1;
winningPlayer = null;
this.rowSize = 3;
this.columnSize = 3;
playerCount = players;
}
public Board(int rowSize, int columnSize) {
gameBoard = new Integer[rowSize][columnSize];
currentPlayer = 1;
winningPlayer = null;
playerCount = 2;
this.rowSize = rowSize;
this.columnSize = columnSize;
}
public Board(int players, int rowSize, int columnSize) {
gameBoard = new Integer[rowSize][columnSize];
currentPlayer = 1;
winningPlayer = null;
playerCount = players;
this.rowSize = rowSize;
this.columnSize = columnSize;
}
/**
*
* #return the amount of empty spaces remaining on the game board, or if theres a winning player, zero.
*/
public int movesLeft() {
if(whoWon() != null){
return 0;
}
int moveCount = 0;
for (int x = 0; x < getRowSize(); x++) {
for (int y = 0; y < getColumnSize(); y++) {
moveCount += getMoveAt(x, y) == null ? 1 : 0;
}
}
return moveCount;
}
/**
* If someone won, this will return the winning player.
*
* #return the winning player
*/
public String whoWon() {
return winningPlayer;
}
/**
* This move allows the next player to choose where to place their mark.
*
* #param row
* #param column
* #return if the game is over, play will return true, otherwise false.
*/
public boolean play(Move move) {
if (!validMove(move)) {
// always fail early
throw new IllegalStateException("Player " + getCurrentPlayer() + " cannot play at " + move.getRow() + ", " + move.getColumn() + "\n" + toString());
}
doMove(move);
boolean playerWon = isWinningMove(move);
if (playerWon) {
winningPlayer = "Player" + getCurrentPlayer();
return true;
}
shiftPlayer();
boolean outOfMoves = movesLeft() <= 0;
return outOfMoves;
}
public int getRowSize() {
return rowSize;
}
public int getColumnSize() {
return columnSize;
}
public int getCurrentPlayer() {
return currentPlayer;
}
public Integer getMoveAt(int row, int column) {
return gameBoard[row][column];
}
private void doMove(Move move) {
gameBoard[move.getRow()][move.getColumn()] = getCurrentPlayer();
}
private void shiftPlayer() {
if(getCurrentPlayer() == getPlayerCount()){
currentPlayer = 1;
}else{
currentPlayer++;
}
}
private int getPlayerCount() {
return playerCount;
}
private boolean validMove(Move move) {
boolean noMoveAtIndex = false;
boolean indexesAreOk = move.getRow() >= 0 || move.getRow() < getRowSize();
indexesAreOk = indexesAreOk && move.getColumn() >= 0 || move.getColumn() < getColumnSize();
if (indexesAreOk) {
noMoveAtIndex = getMoveAt(move.getRow(), move.getColumn()) == null;
}
return indexesAreOk && noMoveAtIndex;
}
private boolean isWinningMove(Move move) {
// since we check to see if the player won on each move
// we are safe to simply check the last move
return winsDown(move) || winsAcross(move) || winsDiagnally(move);
}
private boolean winsDown(Move move) {
boolean matchesColumn = true;
for (int i = 0; i < getColumnSize(); i++) {
Integer moveOnCol = getMoveAt(move.getRow(), i);
if (moveOnCol == null || getCurrentPlayer() != moveOnCol) {
matchesColumn = false;
break;
}
}
return matchesColumn;
}
private boolean winsAcross(Move move) {
boolean matchesRow = true;
for (int i = 0; i < getRowSize(); i++) {
Integer moveOnRow = getMoveAt(i, move.getColumn());
if (moveOnRow == null || getCurrentPlayer() != moveOnRow) {
matchesRow = false;
break;
}
}
return matchesRow;
}
private boolean winsDiagnally(Move move) {
// diagnals we only care about x and y being teh same...
// only perfect squares can have diagnals
// so we check (0,0)(1,1)(2,2) .. etc
boolean matchesDiagnal = false;
if (isOnDiagnal(move.getRow(), move.getColumn())) {
matchesDiagnal = true;
for (int i = 0; i < getRowSize(); i++) {
Integer moveOnDiagnal = getMoveAt(i, i);
if (moveOnDiagnal == null || moveOnDiagnal != getCurrentPlayer()) {
matchesDiagnal = false;
break;
}
}
}
return matchesDiagnal;
}
private boolean isOnDiagnal(int x, int y) {
if (boardIsAMagicSquare()) {
return x == y;
} else {
return false;
}
}
private boolean boardIsAMagicSquare() {
return getRowSize() == getColumnSize();
}
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
for(int y = 0; y < getColumnSize(); y++) {
for(int x = 0; x < getRowSize(); x++) {
Integer move = getMoveAt(x, y);
String moveToPrint = "";
if (move == null) {
moveToPrint = " ";
} else {
moveToPrint = move.toString();
}
stringBuffer.append("|").append(moveToPrint);
}
stringBuffer.append("|\n");
}
return stringBuffer.toString();
}
}
}
I have to revise my answer. If you want to have three in a row regardless of your board size, your loop code might be sufficient, but you are always checking whether the values of the fields are the same but never make a difference between empty and non-empty fields.
So “empty” can win too, which would effectively hide a possible win of a player. In other words, your code does not work correctly, even for a field size of three. You didn’t test it enough.
If I initialize the board as
int[][] board={
{ 1, 1, 1 },
{ 0, 0, 0 },
{ 0, 0, 0 },
};
your code returns 0 as the second row contains three zeros. I assumed that 0 represents the empty field but the actual value for “empty” doesn’t matter. You have to exclude empty fields from the three-in-a-row check.
You can simplify this a fair amount by breaking the logic up a bit.
First realize that you only need to check for a win around the piece you just placed.
Now we need a way to check whether that move is a winner.
First we need a simple function to check whether a cell matches a given value, returning true if its within bounds and matches.
private boolean cellMatches(int x, int y, int val) {
if (x<0||x>boardWidth)
return false;
if (y<0||y>boardHeight)
return false;
return board[x][y]==val;
}
Now a function that you give a starting position (x and y) and a delta (dx, dy) and it checks up to two cells in that direction returning a count of how many in a row matched value. The for loop may be overkill for two checks but it would easily allow you to expand up to longer lines being used.
private int countMatches(int x, int y, int dx, int dy, int val) {
int count = 0;
for (int step=1;step<=2;step++) {
if (cellMatches(x+dx*step, y+dy*step, val) {
count++;
} else {
return count;
}
}
return count;
}
Now we can use the previous method. When we place a new piece we can just count out in each matching pair of directions. The combined count is the total number in a row. (i.e. two in a row top + 1 bot = a total run length of 4). If any of those run lengths is three then it is a winning move.
private boolean makeMove(int x, int y, int val) {
board[x][y] = val;
int runlength=countMatches(x,y,0,1,val) + countMatches(x,y,0,-1,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,0,val) + countMatches(x,y,-1,0,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,1,val) + countMatches(x,y,-1,-1,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,-1,val) + countMatches(x,y,-1,1,val);
if (runLength >= 2)
return true;
return false;
}
Note that because we need to count the center piece that we placed we just need a run length of two or more.
I am currently doing Algorithms in collage and we are asked to make a hex game using Weighted quick union, we were given most of the code for the project by the lecturer. But im running into a problem here.`public class Hex implements BoardGame {
private int[][] board; // 2D Board. 0 - empty, 1 - Player 1, 2 - Player 2
private int n1, n2; // height and width of board
private WeightedQuickUnionUF wqu; // Union Find data structure to keep track
// of unions and calculate winner
private int currentPlayer; // Current player in the game, initialised to 1
public Hex(int n1, int n2) // create N-by-N grid, with all sites blocked
{
this.n1 = n1;
this.n2 = n2;
currentPlayer = 1;
// TODO: Create instance of board
// TODO: Create instance WeightedQuickUnionUF class
wqu = new WeightedQuickUnionUF(14);
board = new int[n1][n2];
for(int i=0; i < n1 ; i++){
for(int j = 0; j < n2; j++){
board[i][j] = 0;
}
}
}
/*
* (non-Javadoc)
*
* #see BoardGame#takeTurn(int, int)
*/
#Override
public void takeTurn(int x, int y) {
if(((x > n1) || (x < 0)) || ((y > n2) || (y < 0)))
{
StdOut.println("Wrong");
}
else{
if(board[x][y] == 0){
board[x][y] = currentPlayer;
}
else{
StdOut.println("Taken");
}
}
// TODO: check coords are valid
// TODO: check if location is free and set to player's value(1 or 2).
// TODO: calculate location and neighbours location in
// WeightedQuickUnionUF data structure
// TODO: create unions to neighbour sites in WeightedQuickUnionUF that
// also contain current players value
// TODO: if no winner get the next player
}
/*
* (non-Javadoc)
*
* #see BoardGame#getCurrentPlayer()
*/
#Override
public int getCurrentPlayer() {
return currentPlayer;
}
public void setCurrentPlayer(int currentPlayer) {
this.currentPlayer = currentPlayer;
}
/*
* (non-Javadoc)
*
* #see BoardGame#getBoard()
*/
#Override
public int[][] getBoard() {
return board;
}
private void nextPlayer() {
if (currentPlayer == 1)
currentPlayer = 2;
else
currentPlayer = 1;
}
/*
* (non-Javadoc)
*
* #see BoardGame#isWinner()
*/
#Override
public boolean isWinner() {
// TODO:check if there is a connection between either side of the board.
// You can do this by using the 'virtual site' approach in the
// percolation test.
return false;
}
/**
* THIS IS OPTIONAL:
* Modify the main method if you wish to suit your implementation.
* This is just an example of a test implementation.
* For example you may want to display the board after each turn.
* #param args
*
*/
public static void main(String[] args) {
BoardGame hexGame = new Hex(4, 4);
while (!hexGame.isWinner()) {
System.out.println("It's player " + hexGame.getCurrentPlayer()
+ "'s turn");
System.out.println("Enter x and y location:");
int x = StdIn.readInt();
int y = StdIn.readInt();
hexGame.takeTurn(x, y);
}
System.out.println("It's over. Player " + hexGame.getCurrentPlayer()
+ " wins!");
}
}
`
I have already checked if the coordinates are valid and if the place on the board is free. But I can seem to get my head around finding the location and neighbours locations using WeightedQuickUnionUF. Any help would be great as I have tried everything I know so far. Here is the WeightedQuickUnionUF class.
public class WeightedQuickUnionUF {
private int[] id;
private int[] sz;
private int count;
public WeightedQuickUnionUF(int N){
count = N;
id = new int[N];
sz = new int[N];
for(int i = 0 ; i < N; i++){
id[i] = i;
sz[i] = i;
}
}
public int count(){
return count;
}
public int find(int p){
while(p != id[p])
p = id[p];
return p;
}
public boolean connected(int p, int q ){
return find(p) == find(q);
}
public void union(int p, int q){
int i = find(p);
int j = find(q);
if(i == j) return;
if(sz[i] < sz[j]){id[i] = j; sz[j] += sz[i];}
else {id[j] = i; sz[i] += sz[j];}
count--;
}
public static void main(String[] args) {
int N = StdIn.readInt();
WeightedQuickUnionUF uf = new WeightedQuickUnionUF(N);
while(!StdIn.isEmpty()){
int p = StdIn.readInt();
int q = StdIn.readInt();
if(uf.connected(p,q)) continue;
uf.union(p, q);
StdOut.println(p + " " + q);
}
StdOut.println(uf.count() + "components");
}
}
You have a bug in the initialization code for sz[]
It should be:
for(int i = 0 ; i < N; i++){
id[i] = i;
sz[i] = 1; // changed to 1 so it indicates the number of nodes for this 'root'
}
I'm trying to solve the problem of positioning N queens on NxN board without row, column and diagonal conflicts. I use an algorithm with minimizing the conflicts. Firstly, on each column randomly a queen is positioned. After that, of all conflict queens randomly one is chosen and for her column are calculated the conflicts of each possible position. Then, the queen moves to the best position with min number of conflicts. It works, but it runs extremely slow. My goal is to make it run fast for 10000 queens. Would you, please, suggest me some improvements or maybe notice some mistakes in my logic?
Here is my code:
public class Queen {
int column;
int row;
int d1;
int d2;
public Queen(int column, int row, int d1, int d2) {
super();
this.column = column;
this.row = row;
this.d1 = d1;
this.d2 = d2;
}
#Override
public String toString() {
return "Queen [column=" + column + ", row=" + row + ", d1=" + d1
+ ", d2=" + d2 + "]";
}
#Override
public boolean equals(Object obj) {
return ((Queen)obj).column == this.column && ((Queen)obj).row == this.row;
}
}
And:
import java.util.HashSet;
import java.util.Random;
public class SolveQueens {
public static boolean printBoard = false;
public static int N = 100;
public static int maxSteps = 2000000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static HashSet<Queen> q = new HashSet<Queen>();
public static HashSet rowConfl[] = new HashSet[N];
public static HashSet d1Confl[] = new HashSet[2*N - 1];
public static HashSet d2Confl[] = new HashSet[2*N - 1];
public static void init () {
int r;
rowConfl = new HashSet[N];
d1Confl = new HashSet[2*N - 1];
d2Confl = new HashSet[2*N - 1];
for (int i = 0; i < N; i++) {
r = random.nextInt(N);
queens[i] = r;
Queen k = new Queen(i, r, i + r, N - 1 + i - r);
q.add(k);
if (rowConfl[k.row] == null) {
rowConfl[k.row] = new HashSet<Queen>();
}
if (d1Confl[k.d1] == null) {
d1Confl[k.d1] = new HashSet<Queen>();
}
if (d2Confl[k.d2] == null) {
d2Confl[k.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[k.row]).add(k);
((HashSet<Queen>)d1Confl[k.d1]).add(k);
((HashSet<Queen>)d2Confl[k.d2]).add(k);
}
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "♕ " : "◻◻◻ ");
}
System.out.println();
}
System.out.println();
}
public static boolean checkItLinear() {
Queen r = choseConflictQueen();
if (r == null) {
return true;
}
Queen newQ = findNewBestPosition(r);
q.remove(r);
q.add(newQ);
rowConfl[r.row].remove(r);
d1Confl[r.d1].remove(r);
d2Confl[r.d2].remove(r);
if (rowConfl[newQ.row] == null) {
rowConfl[newQ.row] = new HashSet<Queen>();
}
if (d1Confl[newQ.d1] == null) {
d1Confl[newQ.d1] = new HashSet<Queen>();
}
if (d2Confl[newQ.d2] == null) {
d2Confl[newQ.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[newQ.row]).add(newQ);
((HashSet<Queen>)d1Confl[newQ.d1]).add(newQ);
((HashSet<Queen>)d2Confl[newQ.d2]).add(newQ);
queens[r.column] = newQ.row;
return false;
}
public static Queen choseConflictQueen () {
HashSet<Queen> conflictSet = new HashSet<Queen>();
boolean hasConflicts = false;
for (int i = 0; i < 2*N - 1; i++) {
if (i < N && rowConfl[i] != null) {
hasConflicts = hasConflicts || rowConfl[i].size() > 1;
conflictSet.addAll(rowConfl[i]);
}
if (d1Confl[i] != null) {
hasConflicts = hasConflicts || d1Confl[i].size() > 1;
conflictSet.addAll(d1Confl[i]);
}
if (d2Confl[i] != null) {
hasConflicts = hasConflicts || d2Confl[i].size() > 1;
conflictSet.addAll(d2Confl[i]);
}
}
if (hasConflicts) {
int c = random.nextInt(conflictSet.size());
return (Queen) conflictSet.toArray()[c];
}
return null;
}
public static Queen findNewBestPosition(Queen old) {
int[] row = new int[N];
int min = Integer.MAX_VALUE;
int minInd = old.row;
for (int i = 0; i < N; i++) {
if (rowConfl[i] != null) {
row[i] = rowConfl[i].size();
}
if (d1Confl[old.column + i] != null) {
row[i] += d1Confl[old.column + i].size();
}
if (d2Confl[N - 1 + old.column - i] != null) {
row[i] += d2Confl[N - 1 + old.column - i].size();
}
if (i == old.row) {
row[i] = row[i] - 3;
}
if (row[i] <= min && i != minInd) {
min = row[i];
minInd = i;
}
}
return new Queen(old.column, minInd, old.column + minInd, N - 1 + old.column - minInd);
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(!checkItLinear()) {
if (++steps > maxSteps) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(printBoard){
print();
}
}
}
Edit:
Here is my a-little-bit-optimized solution with removing some unused objects and putting the queens on diagonal positions when initializing.
import java.util.Random;
import java.util.Vector;
public class SolveQueens {
public static boolean PRINT_BOARD = true;
public static int N = 10;
public static int MAX_STEPS = 5000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static int[] rowConfl = new int[N];
public static int[] d1Confl = new int[2*N - 1];
public static int[] d2Confl = new int[2*N - 1];
public static Vector<Integer> conflicts = new Vector<Integer>();
public static void init () {
random = new Random();
for (int i = 0; i < N; i++) {
queens[i] = i;
}
}
public static int getD1Pos (int col, int row) {
return col + row;
}
public static int getD2Pos (int col, int row) {
return N - 1 + col - row;
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "Q " : "* ");
}
System.out.println();
}
System.out.println();
}
public static boolean hasConflicts() {
generateConflicts();
if (conflicts.isEmpty()) {
return false;
}
int r = random.nextInt(conflicts.size());
int conflQueenCol = conflicts.get(r);
int currentRow = queens[conflQueenCol];
int bestRow = currentRow;
int minConfl = getConflicts(conflQueenCol, queens[conflQueenCol]) - 3;
int tempConflCount;
for (int i = 0; i < N ; i++) {
tempConflCount = getConflicts(conflQueenCol, i);
if (i != currentRow && tempConflCount <= minConfl) {
minConfl = tempConflCount;
bestRow = i;
}
}
queens[conflQueenCol] = bestRow;
return true;
}
public static void generateConflicts () {
conflicts = new Vector<Integer>();
rowConfl = new int[N];
d1Confl = new int[2*N - 1];
d2Confl = new int[2*N - 1];
for (int i = 0; i < N; i++) {
int r = queens[i];
rowConfl[r]++;
d1Confl[getD1Pos(i, r)]++;
d2Confl[getD2Pos(i, r)]++;
}
for (int i = 0; i < N; i++) {
int conflictsCount = getConflicts(i, queens[i]) - 3;
if (conflictsCount > 0) {
conflicts.add(i);
}
}
}
public static int getConflicts(int col, int row) {
return rowConfl[row] + d1Confl[getD1Pos(col, row)] + d2Confl[getD2Pos(col, row)];
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(hasConflicts()) {
if (++steps > MAX_STEPS) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(PRINT_BOARD){
print();
}
}
}
Comments would have been helpful :)
Rather than recreating your conflict set and your "worst conflict" queen everything, could you create it once, and then just update the changed rows/columns?
EDIT 0:
I tried playing around with your code a bit. Since the code is randomized, it's hard to find out if a change is good or not, since you might start with a good initial state or a crappy one. I tried making 10 runs with 10 queens, and got wildly different answers, but results are below.
I psuedo-profiled to see which statements were being executed the most, and it turns out the inner loop statements in chooseConflictQueen are executed the most. I tried inserting a break to pull the first conflict queen if found, but it didn't seem to help much.
Grouping only runs that took more than a second:
I realize I only have 10 runs, which is not really enough to be statistically valid, but hey.
So adding breaks didn't seem to help. I think a constructive solution will likely be faster, but randomness will again make it harder to check.
Your approach is good : Local search algorithm with minimum-conflicts constraint. I would suggest try improving your initial state. Instead of randomly placing all queens, 1 per column, try to place them so that you minimize the number of conflicts. An example would be to try placing you next queen based on the position of the previous one ... or maybe position of previous two ... Then you local search will have less problematic columns to deal with.
If you randomly select, you could be selecting the same state as a previous state. Theoretically, you might never find a solution even if there is one.
I think you woud be better to iterate normally through the states.
Also, are you sure boards other than 8x8 are solvable?
By inspection, 2x2 is not, 3x3 is not, 4x4 is not.