i am try to implement 8 queen using depth search for any initial state it work fine for empty board(no queen on the board) ,but i need it to work for initial state if there is a solution,if there is no solution for this initial state it will print there is no solution
Here is my code:
public class depth {
public static void main(String[] args) {
//we create a board
int[][] board = new int[8][8];
board [0][0]=1;
board [1][1]=1;
board [2][2]=1;
board [3][3]=1;
board [4][4]=1;
board [5][5]=1;
board [6][6]=1;
board [7][7]=1;
eightQueen(8, board, 0, 0, false);
System.out.println("the solution as pair");
for(int i=0;i<board.length;i++){
for(int j=0;j<board.length;j++)
if(board[i][j]!=0)
System.out.println(" ("+i+" ,"+j +")");
}
System.out.println("the number of node stored in memory "+count1);
}
public static int count1=0;
public static void eightQueen(int N, int[][] board, int i, int j, boolean found) {
long startTime = System.nanoTime();//time start
if (!found) {
if (IsValid(board, i, j)) {//check if the position is valid
board[i][j] = 1;
System.out.println("[Queen added at (" + i + "," + j + ")");
count1++;
PrintBoard(board);
if (i == N - 1) {//check if its the last queen
found = true;
PrintBoard(board);
double endTime = System.nanoTime();//end the method time
double duration = (endTime - startTime)*Math.pow(10.0, -9.0);
System.out.print("total Time"+"= "+duration+"\n");
}
//call the next step
eightQueen(N, board, i + 1, 0, found);
} else {
//if the position is not valid & if reach the last row we backtracking
while (j >= N - 1) {
int[] a = Backmethod(board, i, j);
i = a[0];
j = a[1];
System.out.println("back at (" + i + "," + j + ")");
PrintBoard(board);
}
//we do the next call
eightQueen(N, board, i, j + 1, false);
}
}
}
public static int[] Backmethod(int[][] board, int i, int j) {
int[] a = new int[2];
for (int x = i; x >= 0; x--) {
for (int y = j; y >= 0; y--) {
//search for the last queen
if (board[x][y] != 0) {
//deletes the last queen and returns the position
board[x][y] = 0;
a[0] = x;
a[1] = y;
return a;
}
}
}
return a;
}
public static boolean IsValid(int[][] board, int i, int j) {
int x;
//check the queens in column
for (x = 0; x < board.length; x++) {
if (board[i][x] != 0) {
return false;
}
}
//check the queens in row
for (x = 0; x < board.length; x++) {
if (board[x][j] != 0) {
return false;
}
}
//check the queens in the diagonals
if (!SafeDiag(board, i, j)) {
return false;
}
return true;
}
public static boolean SafeDiag(int[][] board, int i, int j) {
int xx = i;
int yy = j;
while (yy >= 0 && xx >= 0 && xx < board.length && yy < board.length) {
if (board[xx][yy] != 0) {
return false;
}
yy++;
xx++;
}
xx = i;
yy = j;
while (yy >= 0 && xx >= 0 && xx < board.length && yy < board.length) {
if (board[xx][yy] != 0) {
return false;
}
yy--;
xx--;
}
xx = i;
yy = j;
while (yy >= 0 && xx >= 0 && xx < board.length && yy < board.length) {
if (board[xx][yy] != 0) {
return false;
}
yy--;
xx++;
}
xx = i;
yy = j;
while (yy >= 0 && xx >= 0 && xx < board.length && yy < board.length) {
if (board[xx][yy] != 0) {
return false;
}
yy++;
xx--;
}
return true;
}
public static void PrintBoard(int[][] board) {
System.out.print(" ");
for (int j = 0; j < board.length; j++) {
System.out.print(j);
}
System.out.print("\n");
for (int i = 0; i < board.length; i++) {
System.out.print(i);
for (int j = 0; j < board.length; j++) {
if (board[i][j] == 0) {
System.out.print(" ");
} else {
System.out.print("Q");
}
}
System.out.print("\n");
}
}
}
for example for this initial state it give me the following error:
Exception in thread "main" java.lang.StackOverflowError
i am stuck, i think the error is infinite call for the method how to solve this problem.
any idea will be helpful,thanks in advance.
note:the broad is two dimensional array,when i put (1) it means there queen at this point.
note2:
we i put the initial state as the following it work:
board [0][0]=1;
board [1][1]=1;
board [2][2]=1;
board [3][3]=1;
board [4][4]=1;
board [5][5]=1;
board [6][6]=1;
board [7][1]=1;
[EDIT: Added conditional output tip.]
To add to #StephenC's answer:
This is a heck of a complicated piece of code, especially if you're not experienced in programming Java.
I executed your code, and it outputs this over and over and over and over (and over)
back at (0,0)
01234567
0
1 Q
2 Q
3 Q
4 Q
5 Q
6 Q
7 Q
back at (0,0)
And then crashes with this
Exception in thread "main" java.lang.StackOverflowError
at java.nio.Buffer.<init>(Unknown Source)
...
at java.io.PrintStream.print(Unknown Source)
at java.io.PrintStream.println(Unknown Source)
at Depth.eightQueen(Depth.java:56)
at Depth.eightQueen(Depth.java:60)
at Depth.eightQueen(Depth.java:60)
at Depth.eightQueen(Depth.java:60)
at Depth.eightQueen(Depth.java:60)
...
My first instinct is always to add some System.out.println(...)s to figure out where stuff is going wrong, but that won't work here.
The only two options I see are to
Get familiar with a debugger and use it to step through and analyze why it's never stopping the loop
Break it down man! How can you hope to deal with a massive problem like this without breaking it into digestible chunks???
Not to mention that the concept of 8-queens is complicated to begin with.
One further thought:
System.out.println()s are not useful as currently implemented, because there's infinite output. A debugger is the better solution here, but another option is to somehow limit your output. For example, create a counter at the top
private static final int iITERATIONS = 0;
and instead of
System.out.println("[ANUMBERFORTRACING]: ... USEFUL INFORMATION ...")
use
conditionalSDO((iITERATIONS < 5), "[ANUMBERFORTRACING]: ... USEFUL INFORMATION");
Here is the function:
private static final void conditionalSDO(boolean b_condition, String s_message) {
if(b_condition) {
System.out.println(s_message);
}
}
Another alternative is to not limit the output, but to write it to a file.
I hope this information helps you.
(Note: I edited the OP's code to be compilable.)
You asked for ideas on how to solve it (as distinct from solutions!) so, here's a couple of hints:
Hint #1:
If you get a StackOverflowError in a recursive program it can mean one of two things:
your problem is too "deep", OR
you've got a bug in your code that is causing it to recurse infinitely.
In this case, the depth of the problem is small (8), so this must be a recursion bug.
Hint #2:
If you examine the stack trace, you will see the method names and line numbers for each of the calls in the stack. This ... and some thought ... should help you figure out the pattern of recursion in your code (as implemented!).
Hint #3:
Use a debugger Luke ...
Hint #4:
If you want other people to read your code, pay more attention to style. Your indentation is messed up in the most important method, and you have committed the (IMO) unforgivable sin of ignoring the Java style rules for identifiers. A method name MUST start with a lowercase letter, and a class name MUST start with an uppercase letter.
(I stopped reading your code very quickly ... on principle.)
Try to alter your method IsValid in the lines where for (x = 0; x < board.length - 1; x++).
public static boolean IsValid(int[][] board, int i, int j) {
int x;
//check the queens in column
for (x = 0; x < board.length - 1; x++) {
if (board[i][x] != 0) {
return false;
}
}
//check the queens in row
for (x = 0; x < board.length - 1; x++) {
if (board[x][j] != 0) {
return false;
}
}
//check the queens in the diagonals
if (!SafeDiag(board, i, j)) {
return false;
}
return true;
}
Related
So I have been trying for the past 2 hours to figure out why the following two methods are giving different results. Both of them are methods that take an integer array, remove any consecutive duplicates in it and put a "-1" at the end for each duplicate removed.
Method 1 gives correct output, while method 2 turns into an infinite loop.
//Method 1
private static void smoosh1(int[] ints) {
for (int i=0; i<ints.length-1; i++) {
while (ints[i] == ints[i+1] && ints[i] != -1) {
for (int x=i; x<ints.length-1; x++) {
ints[x] = ints[x+1]; }
ints[ints.length-1] = -1;
}
}
}
//Method 2
public static void smoosh(int[] ints) {
for (int i = 0; i < ints.length - 1; i++) {
int curVal=ints[i];
int nextVal=ints[i+1];
while (curVal == nextVal && curVal !=-1) {
for (int x = i; x < ints.length-1; x++) {
ints[x] = ints[x + 1];
}
ints[ints.length-1] = -1;
}
}
Kindly explain to me the reason why declaring the variables inside the loop is affecting the output. Thanks.
Note:- This problem is from Part 1, Homework 3 of MOOC 61B at Berkeley.
Link:- https://people.eecs.berkeley.edu/~jrs/61b/hw/hw3/
In order for the second snippet to behave the same as the first snippet, the while loop must modify the values of curVal and nextVal in each iteration:
public static void smoosh(int[] ints) {
for (int i = 0; i < ints.length - 1; i++) {
int curVal=ints[i];
int nextVal=ints[i+1];
while (curVal == nextVal && curVal !=-1) {
for (int x = i; x < ints.length-1; x++) {
ints[x] = ints[x + 1];
}
ints[ints.length-1] = -1;
curVal=ints[i];
nextVal=ints[i+1];
}
}
}
Otherwise the vaule of the expression curVal == nextVal && curVal !=-1 always remains the same, and the while loop either never executes or never terminates.
One you get in the while loop (consecutive duplicate found in the array) you will never get out, because the curVal and nextVal never change.
I suugest you to write a debug line like this :
//Method 2
public static void smoosh(int[] ints) {
for (int i = 0; i < ints.length - 1; i++) {
int curVal=ints[i];
int nextVal=ints[i+1];
while (curVal == nextVal && curVal !=-1) {
System.out.println(curVal + " "+ nextVal);
for (int x = i; x < ints.length-1; x++) {
ints[x] = ints[x + 1];
}
ints[ints.length-1] = -1;
}
}
all.
I am working on this problem which comes from Leetcode.
Here is the link if anyone want to see the problem: https://leetcode.com/problems/valid-sudoku/
I think I almost solve this problem, but something wrong with the "java.lang.ArrayIndexOutOfBoundsException" which I used capital comment in my code to mark it out, ==> "if (brain[((int) board[x][y]) - 1])"
I checked it many times and I think the indexes should be perfectly fine within 0~8 by doing "for( int i = 0, i < 9; i++)" and I could not find the reason.
I guess this must be easy stupid question, but I kinda spend a lot of time to find it out. Can someone help me out?
BIG THANKS to whoever help!
public class Solution {
public boolean isValidSudoku(char[][] board) {
if (board.length > 9 || board[0].length > 9 || board == null) {
return false;
}
boolean[] brain;
// 9 digits are corresponding to 1 ~ 9 ==> 0 - 1, 1 - 2 ... 8 - 9
// ex: brain[2] is checking for digit "3" , so using brain[3-1] to check "3"
for (int x = 0; x < board.length; x++) {
// Reset brain
brain = new boolean[9];
for (int y = 0; y < board[0].length; y++) {
if (board[x][y] != '.') {
// THE NEXT LINE IS THE PROBLEM!!!
if (brain[((int) board[x][y]) - 1]) {
// my condition failed by using:
// brain[board[x][y] - 1]
// other's condition Partially passed by using
// (still failed for final submission:
// brain[(int) (board[x][y] - '1')]
// need to compare the difference
return false;
} else {
brain[((int) board[x][y]) - 1] = true;
}
}
}
}
for (int x = 0; x < board.length; x++) {
// Reset brain
brain = new boolean[9];
for (int y = 0; y < board[0].length; y++) {
if (board[x][y] != '.') {
if (brain[((int) board[y][x]) - 1]) {
return false;
} else {
brain[((int) board[y][x]) - 1] = true;
}
}
}
}
for (int block = 0; block < 9; block++) {
// Reset brain
brain = new boolean[9];
for (int r = block / 3 * 3; r < block / 3 * 3 + 3; r++) {
for (int c = block % 3 * 3; c < block % 3 * 3 + 3; c++) {
if (board[r][c] != '.') {
if (brain[((int) board[r][c]) - 1]) {
return false;
} else {
brain[((int) board[r][c]) - 1] = true;
}
}
}
}
}
return true;
}
}
You are trying to cast a char as int. Try to manually cast these values and you will notice that 0=48 and 9=57, which i guess should be the cause of your exception.
You might want to check if the value in there represents a number and get the numeric value of the char by using the Character#getNumericValue method.
if (Character.isDigit(board[x][y]) &&
brain[Character.getNumericValue(board[x][y])) - 1]) {
...
}
I am working a problem that seems to be somewhat famous among beginning programmers, the 8 queens puzzle. I have seen several solutions to this problems using 2D arrays, recursion etc, but this problem is an assignment given in CS course book chapter introducing 1D arrays, so the available techniques to solve this problem are limited.
The procedure I have used, is by first creating a 1D array with the size of 64, which makes possible positions to place queens from index 0 to 63. A random position index is then generated, and a test is preformed to check if there is any queens attacking this position. If this position is not attacked by any queens, a queen is placed by setting the board[position] = true. When a queen is placed, the queenCount is incremented, and this process repeats until 8 queens have been placed.
If queens are placed in such a way that it is impossible to place 8, the board resets after 1 millisecond by preforming a timecheck, and retries to place the 8 queens. At the best I am able to place 7 queens, but the last remaining one is never placed. Each attempt is printed, along with queenCount for this attempt. Is it possible to use this approach, or is it a dead end?
Code example below:
package ch7;
public class Chapter_07_E22_EightQueens64bool {
public static void main(String[] args) {
int queenCount = 0;
int attemptCount = 0;
boolean[] board = new boolean[8 * 8];
final long TIME_LIMIT = 1; //Milliseconds
long startTime = System.currentTimeMillis();
while (queenCount < 8) {
int position = placeQueen(board.length);
if(checkPosition(position, board) && !board[position]) {
board[position] = true;
queenCount++;
}
long timeCheck = System.currentTimeMillis();
if (timeCheck - startTime > TIME_LIMIT) {
clearBoard(board);
queenCount = 0;
startTime = System.currentTimeMillis();
}
System.out.println("Attempt #" + ++attemptCount);
System.out.println(queenCount + " queens placed.");
printBoard(board);
}
}
public static void printBoard(boolean[] board) {
for (int i = 0; i < board.length; i++) {
if (board[i])
System.out.print("|Q");
else
System.out.print("| ");
if ((i + 1) % 8 == 0)
System.out.println("|");
}
}
public static int placeQueen(int boardSize) {
return (int)(Math.random() * boardSize);
}
public static boolean[] clearBoard(boolean[] board) {
for (int i = 0; i < board.length; i++)
board[i] = false;
return board;
}
public static boolean checkPosition(int position, boolean[] board) {
return checkTop(position, board) && checkBottom(position, board) && checkLeft(position, board) &&
checkRight(position, board) && checkTopLeft(position, board) && checkTopRight(position, board) &&
checkBottomLeft(position, board) && checkBottomRight(position, board);
}
public static boolean checkTop(int position, boolean[] board) {
// Checks each field above the current position while i >= 8
for (int i = position; i >= 8; i -= 8) {
if (board[i - 8])
return false;
}
return true;
}
public static boolean checkBottom(int position, boolean[] board) {
// Checks each field below the current position while i <= 55;
for (int i = position; i <= 55; i += 8) {
if (board[i + 8])
return false;
}
return true;
}
public static boolean checkRight(int position, boolean[] board) {
// Checks each field to the right of the current position while i % 8 < 7
for (int i = position; i % 8 < 7; i += 1) {
if (board[i + 1])
return false;
}
return true;
}
public static boolean checkLeft(int position, boolean[] board) {
// Checks each field to the left of the current position while i % 8 != 0
for (int i = position; i % 8 != 0; i -= 1) {
if (board[i - 1])
return false;
}
return true;
}
public static boolean checkTopLeft(int position, boolean[] board) {
// Checks each field top left of the current position while i >= 9
for (int i = position; i >= 9; i -= 9) {
if (board[i - 9])
return false;
}
return true;
}
public static boolean checkTopRight(int position, boolean[] board) {
// Checks each field top right of the current position while i >= 7
for (int i = position; i >= 7; i -= 7) {
if (board[i - 7])
return false;
}
return true;
}
public static boolean checkBottomRight(int position, boolean[] board) {
// Checks each field below the current position while i <= 54
for (int i = position; i <= 54; i += 9) {
if (board[i + 9])
return false;
}
return true;
}
public static boolean checkBottomLeft(int position, boolean[] board) {
// Checks each field below the current position while i <= 56
for (int i = position; i <= 56; i += 7) {
if (board[i + 7])
return false;
}
return true;
}
}
First, array of size 8 is perfectly sufficient.
The array index represents the column in which was the queen placed and the value represents the row.
[0, 2, 4, 6, 1, 3, 5, 7]
Means that queen in the first column was placed in the first row, second queen was placed in the 3rd row, 3rd queen in 5th row, etc...
So when you place a new queen, check if the row you add it in, isn't already in the array. This way, you only need to worry about diagonal collisions.
Simplest way of solving the problem is recursion (backtracking). If that is not allowed, you can simulate recursion with a stack. If that is not allowed either, you could use 8 nested loops - ugly.
You can improve your collision checking using a simple trick. It works like this -
Let's say your queen #0 is on row #3.
Which cells does she attack diagonally?
On the first column, it's row #2 and row #4 (-1 and +1)
On the second column, it's row #1 and row #5 (-2 and +2)
On the third column it's row #0 and row #6 (-3 and +3)
So when you add a new queen, you iterate previous queens checking one diagonal with (newIndex - oldIndex) + oldRow == newRow and the other diagonal with (newIndex - oldIndex) - oldRow == newRow
So, considering all this, the checking function could look like
boolean canAdd(List<Integer> p, int newRow) {
if (p.contains(newRow))
return false;
int insertIndex = p.size();
for (int i = 0; i < p.size(); i++) {
if (p.get(i) + (insertIndex - i) == newRow || p.get(i) - (insertIndex - i) == newRow)
return false;
}
return true;
}
While the main recursive function could look like
void solve(List<Integer> p, int index) {
if (index == 8) {
System.out.println(p);
return;
}
for (int i = 0; i < 8; i++) {
if (canAdd(p, i)) {
p.add(i);
solve(p, index + 1);
p.remove(p.size() - 1);
}
}
}
And you could call it like this
solve(new ArrayList<Integer>(), 0);
After working on this problem for a few days, I now have a solution that works that in a reasonable amount of time for N <= 20. It goes like this.
For i < N, initialize queens[i] = i. Each row can only hold 1 value, so no need to check for collisions on the left or right. As long as there is no duplicate values in the array, there will not be any column collisions either.
Use the method to check if a queen at a given point, shares a diagonal with a queen at another given point. The method checks to see if the distance between x1 and x0 is equal to the distance of y1 and y0. If the distance is equal, then the co-ordinates (x0,y0) and (x1,y1) share the same diagonal.
Use another method to invoke shareDiagonal(int x0, int y0, int x1, int y1) to check if a queen at a given row, for example on row 7, collides with a queen on any rows above row 7. As mentioned, only the rows above the given row are checked. The reason is that if you for example are checking row2 for any diagonal collisions, any collision with rows below row 2 will be revealed when checking a row with a higher index value. If a queen on row 2 collides with a queen on row 4, this will be revealed when checking row 4 and the rows above.
A third checking method invokes checkRowForCollision(int[] queens, int row), where each row is traversed checking for collisions on the rows above. Row 1 is checked if there is any collisions with queens on row 0, Row 2 is checked if there is any collisions on row 0 and 1, row 3 is checked if there is any collisions on row 0, 1 and 2, etc..
While there is diagonal collisions between any of the queens, the board shuffles until it shows a solution where no queens attack each other.
Code example below:
package ch7;
public class Chapter_07_E22_EightQueens {
static final int N = 8;
public static void main(String[] args) {
int[] queens = new int[N];
int attempts = 0;
for (int i = 0; i < N; i++)
queens[i] = i;
while (checkBoardForCollision(queens)) {
shuffleBoard(queens);
attempts++;
}
printBoard(queens);
System.out.println("Solution found in " + attempts + " attempts");
}
public static void printBoard(int[] queens) {
for (int row = 0; row < N; row++) {
System.out.printf("%-1c", '|');
for (int column = 0; column < N; column++) {
System.out.printf("%-1c|", (queens[row] == column) ? 'Q' : ' ');
}
System.out.println();
}
}
public static boolean shareDiagonal(int x0, int y0, int x1, int y1) {
int dy = Math.abs(y1 - y0);
int dx = Math.abs(x1 - x0);
return dx == dy;
}
public static boolean checkRowForCollision(int[] queens, int row) {
for (int i = 0; i < row; i++) {
if (shareDiagonal(i, queens[i], row, queens[row]))
return true;
}
return false;
}
public static boolean checkBoardForCollision(int[] queens) {
for (int row = 0; row < queens.length; row++)
if (checkRowForCollision(queens, row))
return true;
return false;
}
public static int[] shuffleBoard(int[] queens) {
for (int i = queens.length - 1; i > 0; i--) {
int j = (int)(Math.random() * (i + 1));
int temp = queens[i];
queens[i] = queens[j];
queens[j] = temp;
}
return queens;
}
}
One of the problems, there may be more though, is in the checkTop method.
public static boolean checkTop(int position, boolean[] board)
{
// Checks each field above the current position while i - 8 > - 1
for (int i = position; i > (i - 8); i -= 8)
{
if ((i - 8) > -1)
{
if (board[i - 8])
return false;
}
}
return true;
}
There are cases when the method doesn't find a slot (board[i - 8] = true) and i reaches the value of 7. After this point, the condition of the for loop (i > (i - 8)) will always be true and the condition of the outermost if inside the loop (if ( (i - 8) > -1) will always be false. This causes the program to infinitely stay in the loop.
Example (i reaches -5):
i = -5;
i > ( i - 8) : -5 > (-5 -8 = -13) (always true)
(i - 8) > -1 : -13 > -1 (false) always false for i <= 7
I'm trying to learn about artificial intelligence and how to implement it in a program. The easiest place to start is probably with simple games (in this case Tic-Tac-Toe) and Game Search Trees (recursive calls; not an actual data structure). I found this very useful video on a lecture about the topic.
The problem I'm having is that the first call to the algorithm is taking an extremely long amount of time (about 15 seconds) to execute. I've placed debugging log outputs throughout the code and it seems like it is calling parts of the algorithm an excessive amount of times.
Here's the method for choosing the best move for the computer:
public Best chooseMove(boolean side, int prevScore, int alpha, int beta){
Best myBest = new Best();
Best reply;
if (prevScore == COMPUTER_WIN || prevScore == HUMAN_WIN || prevScore == DRAW){
myBest.score = prevScore;
return myBest;
}
if (side == COMPUTER){
myBest.score = alpha;
}else{
myBest.score = beta;
}
Log.d(TAG, "Alpha: " + alpha + " Beta: " + beta + " prevScore: " + prevScore);
Move[] moveList = myBest.move.getAllLegalMoves(board);
for (Move m : moveList){
String choice;
if (side == HUMAN){
choice = playerChoice;
}else if (side == COMPUTER && playerChoice.equals("X")){
choice = "O";
}else{
choice = "X";
}
Log.d(TAG, "Current Move: column- " + m.getColumn() + " row- " + m.getRow());
int p = makeMove(m, choice, side);
reply = chooseMove(!side, p, alpha, beta);
undoMove(m);
if ((side == COMPUTER) && (reply.score > myBest.score)){
myBest.move = m;
myBest.score = reply.score;
alpha = reply.score;
}else if((side == HUMAN) && (reply.score < myBest.score)){
myBest.move = m;
myBest.score = reply.score;
beta = reply.score;
}//end of if-else statement
if (alpha >= beta) return myBest;
}//end of for loop
return myBest;
}
Where the makeMove method makes the move if the spot is empty and returns a value (-1 - human win, 0 - draw, 1 - computer win, -2 or 2 - otherwise). Though I believe the error may be in the getAllLegalMoves method:
public Move[] getAllLegalMoves(String[][] grid){
//I'm unsure whether this method really belongs in this class or in the grid class, though, either way it shouldn't matter.
items = 0;
moveList = null;
Move move = new Move();
for (int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
Log.d(TAG, "At Column: " + i + " At Row: " + j);
if(grid[i][j] == null || grid[i][j].equals("")){
Log.d(TAG, "Is Empty");
items++;
if(moveList == null || moveList.length < items){
resize();
}//end of second if statement
move.setRow(j);
move.setColumn(i);
moveList[items - 1] = move;
}//end of first if statement
}//end of second loop
}//end of first loop
for (int k = 0; k < moveList.length; k++){
Log.d(TAG, "Count: " + k + " Column: " + moveList[k].getColumn() + " Row: " + moveList[k].getRow());
}
return moveList;
}
private void resize(){
Move[] b = new Move[items];
for (int i = 0; i < items - 1; i++){
b[i] = moveList[i];
}
moveList = b;
}
To sum it all up: What's causing my call, to choose the best move, to take so long? What am I missing? Is there an easier way to implement this algorithm? Any help or suggestions will be greatly appreciated, thanks!
A minimax tree with alpha beta pruning should be visualized as a tree, each node of the tree being a possible move that many turns into the future, and its children being all the moves that can be taken from it.
To be as fast as possible and guarantee you'll only need space linear on number of moves you're looking ahead, you do a depth first search and 'sweep' from one side to another. As in, if you imagine the whole tree being constructed, your program would actually only construct a single strand from lead to root one at a time, and discard any parts of it it is done with.
I'm just going to copy the wikipedia pseudo code at this point because it's really, really succinct and clear:
function alphabeta(node, depth, α, β, Player)
if depth = 0 or node is a terminal node
return score
if Player = MaxPlayer
for each child of node
α := max(α, alphabeta(child, depth-1, α, β, not(Player) ))
if β ≤ α
break (* Beta cut-off *)
return α
else
for each child of node
β := min(β, alphabeta(child, depth-1, α, β, not(Player) ))
if β ≤ α
break (* Alpha cut-off *)
return β
Notes:
-'for each child of node' - Rather than editing the state of the current board, create an entirely new board that is the result of applying the move. By using immutable objects, your code will be less prone to bugs and quicker to reason about in general.
-To use this method, call it for every possible move you can make from the current state, giving it depth -1, -Infinity for alpha and +Infinity for beta, and it should start by being the non-moving player's turn in each of these calls - the one that returns the highest value is the best one to take.
It's very very conceptually simple. If you code it right then you never instantiate more than (depth) boards at once, you never consider pointless branches and so on.
I am not going to profile your code for you, but since this is such a nice coding kata I wrote a small ai for tic tac toe:
import java.math.BigDecimal;
public class Board {
/**
* -1: opponent
* 0: empty
* 1: player
*/
int[][] cells = new int[3][3];
/**
* the best move calculated by eval(), or -1 if no more moves are possible
*/
int bestX, bestY;
int winner() {
// row
for (int y = 0; y < 3; y++) {
if (cells[0][y] == cells[1][y] && cells[1][y] == cells[2][y]) {
if (cells[0][y] != 0) {
return cells[0][y];
}
}
}
// column
for (int x = 0; x < 3; x++) {
if (cells[x][0] == cells[x][1] && cells[x][1] == cells[x][2]) {
if (cells[x][0] != 0) {
return cells[x][0];
}
}
}
// 1st diagonal
if (cells[0][0] == cells[1][1] && cells[1][1] == cells[2][2]) {
if (cells[0][0] != 0) {
return cells[0][0];
}
}
// 2nd diagonal
if (cells[2][0] == cells[1][1] && cells[1][1] == cells[0][2]) {
if (cells[2][0] != 0) {
return cells[2][0];
}
}
return 0; // nobody has won
}
/**
* #return 1 if side wins, 0 for a draw, -1 if opponent wins
*/
int eval(int side) {
int winner = winner();
if (winner != 0) {
return side * winner;
} else {
int bestX = -1;
int bestY = -1;
int bestValue = Integer.MIN_VALUE;
loop:
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (cells[x][y] == 0) {
cells[x][y] = side;
int value = -eval(-side);
cells[x][y] = 0;
if (value > bestValue) {
bestValue = value;
bestX = x;
bestY = y;
if (bestValue == 1) {
// it won't get any better, we might as well stop thinking
break loop;
}
}
}
}
}
this.bestX = bestX;
this.bestY = bestY;
if (bestValue == Integer.MIN_VALUE) {
// there were no moves left, it must be a draw!
return 0;
} else {
return bestValue;
}
}
}
void move(int side) {
eval(side);
if (bestX == -1) {
return;
}
cells[bestX][bestY] = side;
System.out.println(this);
int w = winner();
if (w != 0) {
System.out.println("Game over!");
} else {
move(-side);
}
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
char[] c = {'O', ' ', 'X'};
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
sb.append(c[cells[x][y] + 1]);
}
sb.append('\n');
}
return sb.toString();
}
public static void main(String[] args) {
long start = System.nanoTime();
Board b = new Board();
b.move(1);
long end = System.nanoTime();
System.out.println(new BigDecimal(end - start).movePointLeft(9));
}
}
The astute reader will have noticed I don't use alpha/beta cut-off. Still, on my somewhat dated notebook, this plays through a game in 0.015 seconds ...
Not having profiled your code, I can't say for certain what the problem is. However, you logging each possible move at every node in the search tree might have something to do with it.
So I've been writing a program for the game boggle. I create a little board for the user to use, but the problem is I don't know how to check if that word is on the board recursively. I want to be able to check if the word the entered is indeed on the board, and is valid. By valid I mean, the letters of the word must be adjacent to each other. For those who have played boggle you'll know what I mean. All I want to do is check if the word is on the board.
This is what I have so far ....
import java.io.*;
public class boggle {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
private String s = "";
private int [][] lettersNum = new int [5][5];
private char [][] letters = new char [5][5];
private char [] word = new char [45]; // Size at 45, because the longest word in the dictionary is only 45 letters long
private char [] temp;
public void generateNum()
{
for (int row = 0; row < 5; row ++)
{
for (int col = 0; col < 5; col++)
{
lettersNum [row][col] = (int) (Math.random() * 26 + 65);
}
}
}
public void numToChar()
{
for (int row = 0; row < 5; row ++)
{
for (int col = 0; col < 5; col++)
{
letters [row][col] = (char)(lettersNum[row][col]);
}
}
}
public void display()
{
for (int row = 0; row < 5; row ++)
{
for (int col = 0; col < 5; col++)
{
System.out.print(letters[row][col]);
}
System.out.println("");
}
}
public void getInput() throws IOException
{
System.out.println("Please enter a word : ");
s=br.readLine();
s=s.toUpperCase();
word = s.toCharArray();
}
public int search(int row, int col)
{
if((row <0) || (row >= 5) || (col < 0) || (col >= 5))
{
return (0);
}
else
{
temp = word;
return (1+ search(row +1, col) +
search(row -1, col) +
search(row, col + 1) +
search(row, col-1) +
search(row +1, col +1)+
search(row +1, col -1)+
search(row -1, col +1)+
search(row -1, col -1));
}
}
}
The search was my searching algorithm to check if the word is on the board but I don't know if it is correct or if it will work. Furthermore, I don't know how to actually tell the user that the word is valid !
Thanks for all the help :)
SO I tried to use what you suggested below but I dont really understand the int [5][5] thing. So this is what I tried, but I keep getting out of bounds errors ! Here is the soruce ...
public void locate()
{
temp = word[0];
for (int row = 0; row <5; row++)
{
for (int col = 0; col <5; col++)
{
if(temp == letters[row][col])
{
search(row,col);
}
}
}
}
public int search(int row, int col)
{
if(letters[row][col-1]==word[count]) // Checks the letter to the left
{
count++;
letters[row][col-1] = '-'; // Just to make sure the program doesn't go back on itself
return search(row, col-1);
}
else if (letters[row][col+1] == word[count])// Checks the letter to the right
{
count++;
letters[row][col+1] = '-';// Just to make sure the program doesn't go back on itself
return search(row, col +1);
}
else if (letters[row+1][col]== word[count])// Checks the letter below
{
count++;
letters[row+1][col] = '-';// Just to make sure the program doesn't go back on itself
return search(row +1 , col);
}
else if (letters[row-1][col]== word[count])// Checks the letter above
{
count++;
letters[row-1][col] = '-';// Just to make sure the program doesn't go back on itself
return search(row -1 , col);
}
else if (letters[row-1][col-1]== word[count])// Checks the letter to the top left
{
count++;
letters[row-1][col-1] = '-';// Just to make sure the program doesn't go back on itself
return search(row -1 , col-1);
}
else if (letters[row-1][col+1]== word[count])// Checks the letter to the top right
{
count++;
letters[row-1][col+1] = '-';// Just to make sure the program doesn't go back on itself
return search(row -1 , col+1);
}
else if (letters[row+1][col-1]== word[count])// Checks the letter to the bottom left
{
count++;
letters[row+1][col-1] = '-';// Just to make sure the program doesn't go back on itself
return search(row +1 , col-1);
}
else if (letters[row+1][col+1]== word[count])// Checks the letter to the bottom right
{
count++;
letters[row+1][col+1] = '-';// Just to make sure the program doesn't go back on itself
return search(row +1 , col+1);
}
return 0;
}
private int count = 0; (was declared at the top of the class, in case you were wondering where I got the word[count] from
Your current search function doesn't actually do anything. I'm assuming this is homework so, no free lunch ;)
The simplest approach would be to have two recursive functions:
public boolean findStart(String word, int x, int y)
This will do a linear search of the board looking for the first character in word. If your current location doesn't match, you call yourself with the next set of coords. When it finds a match, it calls your second recursive function using word, the current location, and a new, empty 4x4 matrix:
public boolean findWord(String word, int x, int y, int[][] visited)
This function first checks to see if the current location matches the first letter in word. If it does, it marks the current location in visited and loops through all the adjoining squares except ones marked in visited by calling itself with word.substring(1) and those coords. If you run out of letters in word, you've found it and return true. Note that if you're returning false, you need to remove the current location from visited.
You can do this with one function, but by splitting out the logic I personally think it becomes easier to manage in your head. The one downside is that it does do an extra comparison for each first letter in a word. To use a single function you would need to keep track of what "mode" you were in either with a boolean or a depth counter.
Edit: Your longest word should only be 16. Boggle uses a 4x4 board and a word can't use the same location twice. Not that this really matters, but it might for the assignment. Also note that I just did this in my head and don't know that I got it 100% right - comments appreciated.
Edit in response to comments:
Here's what your iterative locate would look like using the method I outline above:
public boolean locate(String word)
{
for (int row = 0; row < 4; row++)
{
for (int col =0; col < 4; col++)
{
if (word.charAt(0) == letters[row][col])
{
boolean found = findWord(word, row, col, new boolean[4][4]);
if (found)
return true;
}
}
}
return false;
}
The same thing recursively looks like the following, which should help:
public boolean findStart(String word, int x, int y)
{
boolean found = false;
if (word.charAt(0) == letters[x][y])
{
found = findWord(word, x, y, new boolean[4][4]);
}
if (found)
return true;
else
{
y++;
if (y > 3)
{
y = 0;
x++;
}
if (x > 3)
return false;
}
return findStart(word, x, y);
}
So here's findWord() and a helper method getAdjoining() to show you how this all works. Note that I changed the visited array to boolean just because it made sense:
public boolean findWord(String word, int x, int y, boolean[][] visited)
{
if (letters[x][y] == word.charAt(0))
{
if (word.length() == 1) // this is the last character in the word
return true;
else
{
visited[x][y] = true;
List<Point> adjoining = getAdjoining(x,y,visited);
for (Point p : adjoining)
{
boolean found = findWord(word.substring(1), p.x, p.y, visited);
if (found)
return true;
}
visited[x][y] = false;
}
}
return false;
}
public List<Point> getAdjoining(int x, int y, boolean[][] visited)
{
List<Point> adjoining = new LinkedList<Point>();
for (int x2 = x-1; x2 <= x+1; x2++)
{
for (int y2 = y-1; y2 <= y+1; y2++)
{
if (x2 < 0 || x2 > 3 ||
y2 < 0 || y2 > 3 ||
(x2 == x && y2 == y) ||
visited[x2][y2])
{
continue;
}
adjoining.add(new Point(x2,y2));
}
}
return adjoining;
}
So now, after you get input from the user as a String (word), you would just call:
boolean isOnBoard = findStart(word,0,0);
I did this in my head originally, then just went down that path to try and show you how it works. If I were to actually implement this I would do some things differently (mainly eliminating the double comparison of the first letter in the word, probably by combining the two into one method though you can do it by rearranging the logic in the current methods), but the above code does function and should help you better understand recursive searching.