i'll keep this concise. My problem is I generate a grid of dxd squares. In ever square on the grid there contains a random integer from 1 - (d-1). And there is one square containing a 0 (the goal). I randomly start on any square that is not the goal and I can move in ANY direction the amount of spaces of the square that I'm currently on (can't go out of bounds) and I need to use recursion to solve this problem. It also may be impossible to reach the goal with where I started and the layout of the grid.
I tried playing around with different algorithms and stumbled upon DFS, with what I have now, i'm not sure how to properly "backtrack" and avoid staying in an infinite loop. For instance if i start on a square with value 3, I'll move right 3 and if the square I moved to is 3 and I cant move right because its out of bounds so i'll go back left, then right, then left and so on.
if(finalArray[startPosX][startPosY] == 0) {
System.out.println("we did it!");
return true;
}
else {
if(((startPosY + squareValue) <= boardSize-1))
MovePlayer(startPosX, startPosY + squareValue); //Move right
if(((startPosY - squareValue) >= 0))
MovePlayer(startPosX, startPosY - squareValue); //Move left
if(((startPosX + squareValue) <= boardSize-1))
MovePlayer(startPosX + squareValue, startPosY); //Move down
if(((startPosX - squareValue) >= 0))
MovePlayer(startPosX - squareValue, startPosY); //Move up
}
This is my recursive method that will end up going in an infinite loop, I'd like some guidance with how to properly be able to not get stuck in a loop. How can I keep track of the previous square I was on and not go in the direction that will cause it to loop?
Thanks in advance!
There are two things that need to be done to fix your algorithm:
everytime you start walking into one path, you need to check the returned value to see if you have reached the goal, if you have, just return true, no need to try other paths (but if you want to find all possible paths, keep trying the rest paths).
mark the square as visited by converting it's value to be negative (or you can create a new array, for example, visitedArray, to mark visited squares if you don't want to or are not allowed to change the original array) to avoid going into infiniate loop:
if(finalArray[startPosX][startPosY] == 0) {
System.out.println("we did it!");
return true;
}
else {
//already visited
if(visitedArray[startPosX][startPosY]) return false;
//mark this square as visited
visitedArray[startPosX][startPosY] = true;
boolean find = false;
if(((startPosY + squareValue) <= boardSize-1))
if(MovePlayer(startPosX, startPosY + squareValue)) find = true; //Move right, if goal can be reached, end recursion.
if((!find && (startPosY - squareValue) >= 0))
if(MovePlayer(startPosX, startPosY - squareValue)) find = true; //Move left
if((!find && (startPosX + squareValue) <= boardSize-1))
if(MovePlayer(startPosX + squareValue, startPosY)) find = true; //Move down
if((!find && (startPosX - squareValue) >= 0))
if(MovePlayer(startPosX - squareValue, startPosY)) find = true; //Move up
//mark the square as unvisited, but to improve performance,
// you can delete this.
visitedArray[startPosX][startPosY] = false;
return find;
}
Related
I have a working TicTacToe game, where I am trying to implement an Ai to act as player "O". The issue I'm having is that it won't block my moves. I have read through the code many times using breakpoints, but I still can't find the issue. Do any of you know what the issue could be?
This is the main minimax function:
public int miniMax(String player, int currentDepth, String[] board) {
if(checkWin("O",board)){ //Checks if O wins. Returns 10-currentDepth to give value to terminal state (Ai wins)
return 10-currentDepth;
}
if(checkWin("X",board)){ //checks if X wins. Returns -10-currentDepth to give value to terminal state (Ai loses)
return -10-currentDepth;
}
if(checkTie(board)){ //Checks for tie. Returns 0-currentDepth
return 0-currentDepth;
}
currentDepth++;//Increases depth
if(player.equals("O")){ //If the player turn is O, I.E. the AI's turn/Max function of minimax
bestVal=Integer.MIN_VALUE;
bestSpot=0;
for(int i=0;i<board.length;i++){ //For loop that iterates through each possible move for O.
if(board[i].equals(" ")){ //Checks if the spot is empty before modifying it
board[i] = "O"; //When spot is empty, sets equal to O
int value = miniMax("X",currentDepth,board); //Recursive part of function. Recalls function changing the current player. Once it hits an end spot, gives it terminal value
if((value)>bestVal){ //Checks if value is better then the best, to determine best possible move.
bestVal = value; //If value is better, then sets new best to it.
bestSpot = i;// The location of the next best move
}
board[i]=" ";//sets the original location to empty to prevent board from being permanently changed
}
else{}//When the spot isn't empty, just skips that check.
}
return bestSpot;//Returns the best spot to allow program to make a move. This is what gets sent to gamelogic
}
else{ //If the player turn is X, I.E. the players turn/Mini function of minimax
minVal=Integer.MAX_VALUE;
bestSpot=0;
for(int i =0;i<board.length;i++){ //For loop that iterates through each possible move for X.
if(board[i].equals(" ")){ //Checks if the spot is empty before modifying it
board[i] = "X"; //When spot is empty, sets equal to X
int value = miniMax("O",currentDepth,board); //Recursive part of function. Recalls function changing the current player. Once it hits an end spot, gives it terminal value
if((value)<minVal){ //Checks if value is worse then the worst, to determine best possible move.
minVal = value; //If value is better, then sets new best to it.
bestSpot = i; //The location of the next best move
}
board[i] = " "; //Sets the original location to empty to prevent board from being permanently changed
}
else{} //When the spot isn't empty, just skips that check.
}
return bestSpot; //Returns the best move for X.
}
}
This is my checkwin and check tie function:
private boolean checkWin(String player, String[] board){
if(
(board[0].equals(player) && board[1].equals(player) && board[2].equals(player)) ||//first col
(board[3].equals(player) && board[4].equals(player) && board[5].equals(player)) ||//sec col
(board[6].equals(player) && board[7].equals(player) && board[8].equals(player)) ||//third col
(board[0].equals(player) && board[3].equals(player) && board[6].equals(player)) ||//first row
(board[1].equals(player) && board[4].equals(player) && board[7].equals(player)) ||//second row
(board[2].equals(player) && board[5].equals(player) && board[8].equals(player)) ||//third row
(board[0].equals(player) && board[4].equals(player) && board[8].equals(player)) ||//diag \
(board[2].equals(player) && board[4].equals(player) && board[6].equals(player)) //diag /
){
return true;
}
else{
return false;
}
}
private boolean checkTie(String[] board){
int inter=0;
for (String s : board) {
if(!s.trim().isEmpty()){
inter++;
}
}
return inter == 9;
}
Let me know if you need any more code from the program.
Have you tried using a more sophisticated reward function given a game state? I see that you only check for terminal states and there is no extra reward for "blocking" the opponent. The AI needs an incentive to perform certain moves, i.e. you need to reward it for certain actions such as blocking.
I have used a DFS-algorithm for noting the moves needed to solve the maze. There's a startVertex and an endVertex. My code is as follows:
private void DFS(int vertex, boolean visited[], LinkedList<Move> output) {
visited[vertex] = true;
for (int neighbor: g.neighbors(vertex)) {
if (visited[neighbor] != true) {
if (neighbor == vertex + 1)
output.add(Move.RIGHT);
else if (neighbor == vertex - 1)
output.add(Move.LEFT);
else if (neighbor == vertex + Math.sqrt(g.size()))
output.add(Move.DOWN);
else
output.add(Move.UP);
if (neighbor == endVertex)
break;
DFS(neighbor, visited, output);
}
else {
output.removeLast();
}
}
}
I am using the removeLast() function when all the surrounding neighbors have been explored and thus that particular vertex remains of no use. But I think that the error lies there only.
The size of graph g is n*n because the original maze is a 2D square matrix with n rows and n columns.
You need to clear visited when leaving DFS (and the endVertex hasn't been found)
output.removeLast should be symmetrical to output.add
break only leaves the innermost loop. Is that what you want?
You probably want to let DFS return whether the endVertex was found, so you know the search can be stopped in higher layers
I've created a Sudoku solver that will solve a Sudoku as a human might- by checking possibilities + definite values in squares corresponding to the square being checked.
(Source: http://pastebin.com/KVrXUDBF)
However, I would like to create a random Sudoku generator (from a blank grid), and so have decided to use a backtracking algorithm. I understand the concept of backtracking, but am confused about one thing:
How do I know which previous node to return to (and change) once I know a certain solution is not allowed?
Should I simply return to the previous node and cycle through all possibilities? (And then if this yields no correct answers, return to the value before, etc.). This seems like a viable method, but also quite inefficient. Is this the correct way of implementing a backtracking method or is there a better way to go about it?
Thanks in advance.
More can be found about backtracking here: http://en.wikipedia.org/wiki/Backtracking
Sudoku Puzzle can be reduced to graph coloring problem which can be solved using simple backtracking like assigning colors to node (1-9) till the there is no violation that all directly connected nodes have no same color.
Constructing Graph from Sudoku : -
There is an direct edge between two grid points if they are in same
row or column or square.
Backtracking :-
Assign one color (1-9) to node
Check if there is no other directly connected node with same color
If valid color move to next node.
else change the color and recheck.
If all color exhausted backtrack to previous node.
Do recursion till all nodes are color.
Once You are done with it you can start removing numbers from the grid at random till you think the problem is unsolvable if any more numbers are removed.
A simple way to generate random Sudoku is that
1) generate a random completing Sudoku, that is, generate random Sudoku no square is blank.
2) Remove numbers from squares of 1).
3) Solve Sudoku of 2). If there are many solutions, then add a number removed at 2).
If there are still many solutions, then repeat 3).
1) sample source code:
public int[][] generateRandomCompleteSudoku() {
int[][] sudoku = new int[10];
for(int i = 1; i <= 9; i++) {
sudoku[i] = new int[10];
Arrays.fill(sudoku[i], 0);
}
generateRandomCompleteSudoku(sudoku, 1, 1);
return sudoku;
}
private boolean generateRandomCompleteSudoku(int[][] sudoku, int x, int y) {
if(x > 9) {
x = 1;
y++;
}
//sudoku of the argument is completing sudoku.
//so return true
if(y > 9) {
return true;
}
// enumerate the possible numbers of the pos(x,y).
List<Integer> possibleNumbers = new ArrayList<Integer>();
for(int i = 1; i <= 9; i++) {
boolean possible = true;
//check i is a possible number.
//check there isn't i in the raw of y .
for(int j = 1; j <= x - 1; j++) {
if(sudoku[j][y] == i) {
possible = false;
break;
}
}
//check there isn't i in the column of x(omitted).
//check there isn't i in the group of x,y(omitted).
if(possible) {
possibleNumbers.add(i);
}
}
//sudoku is wrong so return false.(There is no solution of sudoku)
if(possibleNumbers.size() <= 0) {
return false;
}
Collections.shuffle(possibleNumbers);// This gives sudoku randomness.
for(Integer possibleNumber : possibleNumbers) {
sudoku[x][y] = possibleNumber;
// a sudoku is generated, so return true
if(generateRandomCompleteSudoku(sudoku, x + 1, y)) {
return true;
}
}
// No sudoku is generated, so return false
return false;
}
For a backtracking solution, the first step is to define the state. So for this problem, I think the most straightforward way is (x,y, blank , num) with x , y is the position of the current state, blank is the number of blank position left, and num is the value you want to fill in that position (from 0 to 9 and 0 means blank).
And the return type should be boolean, which determine whether the move is valid or not (which means is there any valid solution for this move).
So, the state transition is column by column, row by row: x, y to x , (y + 1) or x , y to (x + 1), 0.
Similarly, the blank will be from a -> a - 1-> ... 0.
We have a draft solution here:
public boolean move(int x, int y, int blank, int num, int[][]sudoku){
sudoku[x][y] = num;
//checking condition and return if x,y is the last position, code omitted
if(y == sudoku[x].length){
x++;
y = 0;
}else{
y++;
}
for(int i = 1; i < 10; i++){
if(move(x,y,blank,i,sudoku){//Backtrack here
return true;
}
}
if(blank > 0){
if(move(x,y,blank - 1, 0, sudoku){//Backtrack here
return true;
}
}
return false;
}
So when ever there is a false return from the current state, it will backtrack to the last state , and the last state will continue to check for the next num until it find a correct solution (or return false).
I have a program which creates multiple randomly placed objects(balls) at the beginning, now im trying to move the player ball towards the closest object automatically until their centres are the same. I have determined the distance of the closest object stored in an array called distance with index of 'closest', and set the speed as variable delta initialised somewhere else, how do i get the direction right? Right now
It moves just diagonally but not towards the closest ball
coordinatex[closest] and coordinatey[closest] are the x and y coordinates of the closest object.
playerObject.getX and playerObjectgetY gives me the coordinates of the player onject
public void move(int delta) {
for(int i=0; i<distance[closest]; i++) {
if (x<coordinatex[closest] && y<coordinatey[closest]) {
playerObject.setX(playerObject.getX() + 0.1*delta);
playerObject.setY(playerObject.getY() + 0.1*delta);
} else if(x>coordinatex[closest] && y>coordinatey[closest]) {
playerObject.setX(playerObject.getX() - 0.1*delta);
playerObject.setY(playerObject.getY() - 0.1*delta);
} else if(x>coordinatex[closest] && y<coordinatey[closest]) {
playerObject.setX(playerObject.getX() - 0.1*delta);
playerObject.setY(playerObject.getY() + 0.1*delta);
} else if (x<coordinatex[closest] && y>coordinatey[closest]) {
playerObject.setX(playerObject.getX() + 0.1*delta);
playerObject.setY(playerObject.getY() - 0.1*delta);
}
}
}
Something very similar to this:
angle=atan2(ClosestBally-player_y,closestBallx-player_x);
dxperframe = cos(angle);
dyperframe = sin(-angle);
I see your handlers for the four semi-cardinal (diagonal) directions, but not the four cardinal directions. In this game, for example, enemies approach the player in a step-wise manner using the move(int row, int col) method, shown here, from any of eight directions.
I'm having difficulty trying to turn a algorithm that was hinted to us into usable code. We are given a Direction enum that has the 8 coordinates (N, NE, NW, S, SE. SW. E, W) along with the exit HERE.
This is the hinted algorithm:
getPathToExit(row, col):
if (row, col) is outside of the map:
return an empty list
else if (row, col) is an obstacle:
return an empty list
else if (row, col) is marked as visited or as deadend:
return an emtpy list
else if (row, col) is the exit:
//optional: mark exit as visited
return a list containing Direction.HERE
else:
//try to find a path from current square to exit:
mark current square as visited (that is, part of path)
for each neighbor of current square:
path = path from neighbor to exit
if path is not empty:
add (direction to neighbor) to start of path
return path
//after for loop: no path exists from this square to exit
mark current square as deadend
return empty list
This is the code I have been working on for a while:
public java.util.ArrayList<Direction> getPathToExit(){
for (int x=0; x<map.length; x++){
for (int y=0; y<map[x].length; y++){
if (map[x][y]=='S'){
this.startRow=x;
this.startCol=y;
}
}
}
System.out.println("start "+startRow+", "+startCol);
return getPathToExit(this.startRow, this.startCol);
}
private java.util.ArrayList<Direction> getPathToExit(int row, int col){
Direction [] dirs = Direction.values();
ArrayList<Direction> path = new ArrayList<Direction>();
getPathToExit(row, col);
if (row < 0 || col < 0 || row > map.length || col > map[row].length){
return null;
}
else if (map[row][col] != ' '){
return null;
}
else if (map[row][col] == 'E'){
path.add(Direction.HERE);
return path;
}
else {
for (int x=0; x<dirs.length-1; x++){
int nextRow = row + dirs[x].getRowModifier();
int nextCol = col + dirs[x].getColModifier();
path = getPathToExit(nextRow, nextCol);
}
}
return path;
}
This is the enum class:
public enum Direction {
N, NE, E, SE, S, SW, W, NW, HERE;
/**
* Returns the X/column change on the screen that is associated with
* this direction: -1 for W, 0 for N/S, and +1 for E.
*/
public int getColModifier() {
int mod;
switch (this) {
case NW:
case W:
case SW:
mod = -1;
break;
case NE:
case E:
case SE:
mod = +1;
break;
default:
mod = 0;
break;
}
return mod;
}
/**
* Returns the Y/row change on the screen that is associated with
* this direction: -1 for N, 0 for E/W, and +1 for south.
*/
public int getRowModifier() {
int mod;
switch (this) {
case N:
case NE:
case NW:
mod = -1;
break;
case S:
case SE:
case SW:
mod = +1;
break;
default:
mod = 0;
break;
}
return mod;
}
/** As {#link #getColModifier()} */
public int getXModifier() {
return this.getColModifier();
}
/** As {#link #getRowModifier()} */
public int getYModifier() {
return this.getRowModifier();
}
/**
* Returns the direction that is the opposite of this one.
* For example, <code>Direction.NE.reverse() == Direction.SW</code>.
* (The opposite of HERE is still HERE though.)
*/
public Direction reverse() {
if (this == HERE) {
return this;
}else {
int reversed = (this.ordinal() + 4) % 8;
Direction[] dirs = Direction.values();
return dirs[reversed];
}
}
}
Thanks in advance.
There are two issues in the code:
(1)
In the main for loop:
for (int x=0; x<dirs.length-1; x++){
int nextRow = row + dirs[x].getRowModifier();
int nextCol = col + dirs[x].getColModifier();
path = getPathToExit(nextRow, nextCol);
}
You need to check if the recursive call: getPathToExit() returned a not null list. If it had, you should break the loop and push the relevant direction to its start. You already found a path - not point to keep on checking the rest!
(2) In order for your algorithm to be complete (find a solution if one exists), you need to maintain a visited set, and avoid revisiting already visited nodes.
Have a look at the following example:
-------
|S |x1|
-------
|x2|E |
-------
where all are valid squares (no obstacles), S is the start and E is the end.
Now, assume the order of directions is right,left, ....
The code (without visited set) will do the following:
go right (to x1).
go right - out of maze, go back.
go left (to S).
go right (to x1).
go right - out of maze, go back.
go left (to S)
....
You are in an infinite loop! (one known draw back of DFS)
The StackOverflowError is usually an indication that this is the issue, the call stack is full from all the recursive calls - and an error is thrown.
To fix it, you need to maintain a visited set, and avoid revisitting already visited nodes. With this set, and the above maze (order of directions is right, left, down, ...) what will happen is:
go right (to x1)
go right - out of maze, go back.
go left (to S) - already visitted, go back.
go down (to E) - found target, return it.
A more advanced alternative is using Iterative Deepening DFS - which basically mean, you are limitting the length of the path to l, and iteratively increase this l. I'd ignore this alternative for this time, it is a bit more advanced.
As a side note, your algorithm is an implementation of DFS, which is complete with a visited set and in finite graphs (always finds a solution if one exists), but not optimal (does not guarantee to find the shortest path). To find the shortest path, you might want to use BFS instead.
Also: I assume the recursive call in the third line of the method is a leftover that is there for debugging. It shouldn't be there.