So I got a small board game for my Othello game. In this game the AI should decide what to move to make with a Alpha Beta Prune search algorithm. I used the following Pseudocode form geeksforgeeks:
function minimax(node, depth, isMaximizingPlayer, alpha, beta):
if node is a leaf node :
return value of the node
if isMaximizingPlayer :
bestVal = -INFINITY
for each child node :
value = minimax(node, depth+1, false, alpha, beta)
bestVal = max( bestVal, value)
alpha = max( alpha, bestVal)
if beta <= alpha:
break
return bestVal
else :
bestVal = +INFINITY
for each child node :
value = minimax(node, depth+1, true, alpha, beta)
bestVal = min( bestVal, value)
beta = min( beta, bestVal)
if beta <= alpha:
break
return bestVal
This is how I implemented it:
//Called when it's the AI's turn to make a move.
public Board makeMove(Board board) {
setRoot(board);
int alpha = Integer.MIN_VALUE;
int beta = Integer.MAX_VALUE;
int val = alphaBetaSearch(tree.getRoot(), 0, true, alpha, beta);
Board resultBoard = //Should be AI Board/move
setRoot(resultBoard);
return resultBoard;
}
private int alphaBetaSearch(Node node, int depth, boolean maximizing, int alpha, int beta) {
currentDepth = depth;
if (node.isLeaf()) {
return evaluateUtility(node.getBoard());
}
if (maximizing) {
int bestValue = Integer.MIN_VALUE;
for (Node child : node.getChildren()) {
int value = alphaBetaSearch(child, depth + 1, false, alpha, beta);
bestValue = Integer.max(bestValue, value);
alpha = Integer.max(alpha, bestValue);
if (beta <= alpha) {
break;
}
return alpha;
}
} else {
int bestValue = Integer.MAX_VALUE;
for (Node child : node.getChildren()) {
int value = alphaBetaSearch(child, depth + 1, true, alpha, beta);
bestValue = Integer.min(bestValue, value);
beta = Integer.min(beta, bestValue);
if (beta <= alpha) {
break;
}
return beta;
}
}
return 0; //Not sure what should be returned here
}
private int evaluateUtility(Board board) {
int whitePieces = board.getNumberOfWhiteCells();
int blackPieces = board.getNumberOfBlackCells();
int sum = (blackPieces - whitePieces);
return sum;
}
As my Board is quite small (4x4) I have been able to calculate the complete search tree before the game starts in about 20 seconds. This should improve my search as I don't have build anything when playing. Each Node in my tree contains a Board which themselves have a 2D array of cells. The root node/board looks like this:
EMPTY EMPTY EMPTY EMPTY
EMPTY WHITE BLACK EMPTY
EMPTY BLACK WHITE EMPTY
EMPTY EMPTY EMPTY EMPTY
Now when I start the game, this is the starting board and I call the AI to make a move. When the minimax call has executed, it returns the value 2 at the depth of 12. Depth 12 is a leaf node/board in my tree. After running it with the debugger it seems that my implementation doesn't traverse back up the tree. All it does is going down to the leftmost tree and returns it's evaluation.
Related
I have a grid that represents a map. I have nodes that are ocean, and I have nodes that are land. I would like to assign a distance to every one of them using a recursive function. (So I guess one function call/island).
I have a code currently, which is this:
public int searchOcean(int x, int y, boolean[] visited) {
if (x < 0 || x >= width || y < 0 || y >= height) {
return 1000;
}
Node current = this.get(x, y);
int index = current.getIndex(this);
if (visited[index]) {
return current.oceanDist;
}
visited[index] = true;
if (current.ocean) {
current.oceanDist=0;
return 0;
}
int r1 = searchOcean(x + 1, y, visited);
int r2 = searchOcean(x - 1, y, visited);
int r3 = searchOcean(x, y + 1, visited);
int r4 = searchOcean(x, y - 1, visited);
int min = Math.min(Math.min(r1, r2), Math.min(r3 , r4))+1;
current.oceanDist = min;
return min;
}
The problem is, that it doesnt really work, I think mainly because I dont know how to handle the nodes that are already visited.
You want the Dijkstra algorithm https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
Loop A over nodes that are land
Apply Dijkstra
Loop B over nodes that are land
add distance ( given by Dijkstra ) from A to B to results.
or better ( remove un-needed calls to Dijkstra )
construct empty D to store distances between every pair of land nodes
loop A over every land node
loop B over every land node
if A-B NOT saved into D
apply Dijkstra with source A
loop C over land nodes
save distance A-C into D
break out of loop B
D is a map of distances keyed by the ordered node pair A,B. That is A->B and B->A give the same distance.
I wrote an algorithms for testing graph bipartitness for Graph Algorithms course on edX (initially available on Coursera) and it fails on one of their test cases.
I gave it a thought and cannot find so far what might I be missing, I use BFS traversal to color nodes to test for bipartitness, it works on a few simple test cases and on 17 of 28 test cases on edX.
private static final int WHITE = -1;
private static final int BLACK = -2;
private static final int START_INDEX = 0;
static boolean isBipartite(ArrayList<Integer>[] adj) { // an array of lists of neighbors
int[] colors = new int[adj.length];
boolean[] visited = new boolean[adj.length];
colors[START_INDEX] = WHITE;
Queue<Integer> queue = new LinkedList<>();
// start with some node
queue.add(START_INDEX);
while (!queue.isEmpty()) {
int node = queue.poll();
ArrayList<Integer> neighbors = adj[node];
for (int neighbor : neighbors) {
if (!visited[neighbor]) {
if (node != neighbor) {
// add for traversal and color with an opposite color
queue.add(neighbor);
colors[neighbor] = colors[node] == WHITE ? BLACK : WHITE;
} else {
// self cycle will always be odd
return false;
}
} else {
// return immediately if encountered an already colored node
// with the same color, there is an odd cycle
if (colors[node] == colors[neighbor]) {
return false;
}
}
}
visited[node] = true;
}
// final check of u and v colors for all edges
for (int u = 0; u < adj.length; u++) {
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u].get(i);
if (colors[u] == colors[v]) {
return false;
}
}
}
return true;
}
Any suggesting what could I be missing in my algorithm?
Also without the final check my algorithm would fail on the 3rd test case out of 28 (I do not see the inputs), even though I don't understand why - I should already be finding the odd cycles in the main for loop.
Please help.
Some of the sample graphs I tested myself, the 1st is not bipartite, the 2nd is bipartite.
As has been noted in the comments, you assume that every node is reachable from the starting node. The good news is that this is quite easy to fix. Define an enum Colors of three values: NoColor, Black, and White. The pseudocode goes as follows:
Input: graph G with nodes integers from 0 to n - 1
Initialise array colors of length n with all values set to NoColor;
for each `i` from `0` to `n - 1`, do {
if colors[i] = NoColor then {
Initialise q to an empty queue (or stack - whatever floats your boat);
push i onto queue;
colors[i] <- Black;
while q is not empty do {
pop k off of q;
for each neighbour i of k such that colors[i] = NoColor do
colors[i] <- the opposite color of colors[k];
push i onto q;
}
}
}
}
This gives you the 2-coloring, if the coloring exists. In this case, you'll want to verify that it is in fact a 2-coloring to check if the graph is bipartite.
---------------
Actual Question
---------------
Ok, the real problem is not with alpha-beta pruning vs minimax algorithms. The problem is that minimax algorithm when in a tree will give only the best solutions whereas alpha-beta will give the correct value, but multiple children have that best value and some of these children shouldn't have this value.
I guess the ultimate question is, what is the most efficient way to get the best (could be multiple in the case of a tie) child of the root node.
The algorithm produces the correct value, but multiple nodes tie with that value, even though some of the moves are obviously wrong.
Example:
TickTackToe
-|-|O
-|X|-
-|X|-
will produce the values as:
(0,1) and (1,0) with value of -0.06 with my heuristic
(0,1) is the correct value as it will block my X's but (0,1) is wrong as then next move i can put an X at (0,1) and win.
When i run the same algorithm without the
if(beta<=alpha)
break;
It only returns the (0,1) with value -0.06
---------------
Originally posted question, now just sugar
---------------
I've spent days trying to figure out why my min max algorithm works, but when i add alpha beta pruning to it, it doesn't work. I understand they should give the same results and I even made a quick test of that.
My question, is why doesn't my implementation produce the same results?
This is a tic tak toe implementation in android. I can beat the algorithm sometimes when
if(beta<=alpha) break;
is not commented out, but when it is commented out it is undefeatable.
private static double minimax(Node<Integer,Integer> parent, int player, final int[][] board, double alpha, double beta, int depth) {
List<Pair<Integer, Integer>> moves = getAvailableMoves(board);
int bs = getBoardScore(board);
if (moves.isEmpty() || Math.abs(bs) == board.length)//leaf node
return bs+(player==X?-1:1)*depth/10.;
double bestVal = player == X ? -Integer.MAX_VALUE : Integer.MAX_VALUE;
for(Pair<Integer, Integer> s : moves){
int[][] b = clone(board);
b[s.getFirst()][s.getSecond()]=player;
Node<Integer, Integer> n = new Node<>(bs,b.hashCode());
parent.getChildren().add(n);
n.setParent(parent);
double score = minimax(n,player==O?X:O,b,alpha,beta, depth+1);
n.getValues().put("score",score);
n.getValues().put("pair",s);
if(player == X) {
bestVal = Math.max(bestVal, score);
alpha = Math.max(alpha,bestVal);
} else {
bestVal = Math.min(bestVal, score);
beta = Math.min(beta,bestVal);
}
/*
If i comment these two lines out it works as expected
if(beta<= alpha)
break;
*/
}
return bestVal;
}
Now this wouldn't be a problem for tick tack toe due to the small search tree, but i then developed it for checkers and noticed the same phenomenon.
private double alphaBeta(BitCheckers checkers, int depth, int absDepth, double alpha, double beta){
if(checkers.movesWithoutAnything >= 40)
return 0;//tie game//needs testing
if(depth == 0 || checkers.getVictoryState() != INVALID)
return checkers.getVictoryState()==INVALID?checkers.getBoardScore()-checkers.getPlayer()*moves/100.:
checkers.getPlayer() == checkers.getVictoryState() ? Double.MAX_VALUE*checkers.getPlayer():
-Double.MAX_VALUE*checkers.getPlayer();
List<Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>> moves;
if(absDepth == maxDepth)
moves = (List<Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>>) node.getValues().get("moves");
else
moves = checkers.getAllPlayerMoves();
if(moves.isEmpty()) //no moves left? then this player loses
return checkers.getPlayer() * -Double.MAX_VALUE;
double v = checkers.getPlayer() == WHITE ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
for(Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> i : moves){
BitCheckers c = checkers.clone();
c.movePiece(i.getFirst().getFirst(),i.getFirst().getSecond(),i.getSecond().getFirst(),i.getSecond().getSecond());
int newDepth = c.getPlayer() == checkers.getPlayer() ? depth : depth - 1;
if(checkers.getPlayer() == WHITE) {
v = Math.max(v, alphaBeta(c, newDepth, absDepth - 1, alpha, beta));
alpha = Math.max(alpha,v);
}else {
v = Math.min(v, alphaBeta(c, newDepth, absDepth - 1, alpha, beta));
beta = Math.min(beta,v);
}
if(absDepth == maxDepth) {
double finalScore = v;
for(Node n : node.getChildren())
if(n.getData().equals(i)){
n.setValue(finalScore);
break;
}
}
/*
If i comment these two lines out it works as expected
if(beta<= alpha)
break;
*/
}
return v;
}
I tested it with pvs and it gives the same results as alpha-beta pruning, ie not nearly as good as just minimax.
public double pvs(BitCheckers checkers, int depth, int absDepth, double alpha, double beta){
if(checkers.movesWithoutAnything >= 40)
return 0;//tie game//needs testing
if(depth == 0 || checkers.getVictoryState() != INVALID)
return checkers.getVictoryState()==INVALID?checkers.getBoardScore()-checkers.getPlayer()*moves/100.:
checkers.getPlayer() == checkers.getVictoryState() ? Double.MAX_VALUE*checkers.getPlayer():
-Double.MAX_VALUE*checkers.getPlayer();
List<Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>> moves;
if(absDepth == maxDepth)
moves = (List<Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>>) node.getValues().get("moves");
else
moves = checkers.getAllPlayerMoves();
if(moves.isEmpty()) //no moves left? then this player loses
return checkers.getPlayer() * -Double.MAX_VALUE;
int j = 0;
double score;
for(Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> i : moves){
BitCheckers c = checkers.clone();
c.movePiece(i.getFirst().getFirst(),i.getFirst().getSecond(),i.getSecond().getFirst(),i.getSecond().getSecond());
int newDepth = c.getPlayer() == checkers.getPlayer() ? depth : depth - 1;
double sign = c.getPlayer() == checkers.getPlayer()? -1 : 1;
if(j++==0)
score = -pvs(c,newDepth,absDepth-1,sign*-beta,sign*-alpha);
else {
score = -pvs(c,newDepth, absDepth-1,sign*-(alpha+1),sign*-alpha);
if(alpha<score || score<beta)
score = -pvs(c,newDepth,absDepth-1,sign*-beta,sign*-score);
}
if(absDepth == maxDepth) {
double finalScore = score;
for(Node n : node.getChildren())
if(n.getData().equals(i)){
n.setValue(finalScore);
break;
}
}
alpha = Math.max(alpha,score);
if(alpha>=beta)
break;
}
return alpha;
}
Checkers without alpha beta pruning is good, but not great. I know with a working version of alpha-beta it could be really great. Please help fix my alpha-beta pruning.
I understand it should give the same result, my question is why is my implementation not giving the same results?
To confirm that it should give the same results, i made a quick test class implementation.
public class MinimaxAlphaBetaTest {
public static void main(String[] args) {
Node<Double,Double> parent = new Node<>(0.,0.);
int depth = 10;
createTree(parent,depth);
Timer t = new Timer().start();
double ab = alphabeta(parent,depth+1,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,true);
t.stop();
System.out.println("Alpha Beta: "+ab+", time: "+t.getTime());
t = new Timer().start();
double mm = minimax(parent,depth+1,true);
t.stop();
System.out.println("Minimax: "+mm+", time: "+t.getTime());
t = new Timer().start();
double pv = pvs(parent,depth+1,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,1);
t.stop();
System.out.println("PVS: "+pv+", time: "+t.getTime());
if(ab != mm)
System.out.println(ab+"!="+mm);
}
public static void createTree(Node n, int depth){
if(depth == 0) {
n.getChildren().add(new Node<>(0.,(double) randBetween(1, 100)));
return;
}
for (int i = 0; i < randBetween(2,10); i++) {
Node nn = new Node<>(0.,0.);
n.getChildren().add(nn);
createTree(nn,depth-1);
}
}
public static Random r = new Random();
public static int randBetween(int min, int max){
return r.nextInt(max-min+1)+min;
}
public static double pvs(Node<Double,Double> node, int depth, double alpha, double beta, int color){
if(depth == 0 || node.getChildren().isEmpty())
return color*node.getValue();
int i = 0;
double score;
for(Node<Double,Double> child : node.getChildren()){
if(i++==0)
score = -pvs(child,depth-1,-beta,-alpha,-color);
else {
score = -pvs(child,depth-1,-alpha-1,-alpha,-color);
if(alpha<score || score<beta)
score = -pvs(child,depth-1,-beta,-score,-color);
}
alpha = Math.max(alpha,score);
if(alpha>=beta)
break;
}
return alpha;
}
public static double alphabeta(Node<Double,Double> node, int depth, double alpha, double beta, boolean maximizingPlayer){
if(depth == 0 || node.getChildren().isEmpty())
return node.getValue();
double v = maximizingPlayer ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
for(Node<Double,Double> child : node.getChildren()){
if(maximizingPlayer) {
v = Math.max(v, alphabeta(child, depth - 1, alpha, beta, false));
alpha = Math.max(alpha, v);
}else {
v = Math.min(v,alphabeta(child,depth-1,alpha,beta,true));
beta = Math.min(beta,v);
}
if(beta <= alpha)
break;
}
return v;
}
public static double minimax(Node<Double,Double> node, int depth, boolean maximizingPlayer){
if(depth == 0 || node.getChildren().isEmpty())
return node.getValue();
double v = maximizingPlayer ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
for(Node<Double,Double> child : node.getChildren()){
if(maximizingPlayer)
v = Math.max(v,minimax(child,depth-1,false));
else
v = Math.min(v,minimax(child,depth-1,true));
}
return v;
}
}
This does in fact give what i expected alpha-beta and pvs are about the same speed (pvs is slower because the children are in random order) and produce the same results as minimax. This proves that the algorithms are correct, but for whatever reason, my implementation of them are wrong.
Alpha Beta: 28.0, time: 25.863126 milli seconds
Minimax: 28.0, time: 512.6119160000001 milli seconds
PVS: 28.0, time: 93.357653 milli seconds
Source Code for Checkers implementation
Pseudocode for pvs
Pseudocode for alpha beta i'm following
Full Souce Code for the Tick Tack Toe Implementation
I think you might be misunderstanding AB pruning.
AB pruning should give you the same results as MinMax, it's just a way of not going down certain branches because you know making that move would have been worse than another move that you examined which helps when you have massive trees.
Also, MinMax without using a heuristic and cutting off your search will always be undefeatable because you've computed every possible path to reach every terminating state. So I would've expected that AB pruning and MinMax would both be unbeatable so I think something is wrong with your AB pruning. If your minmax is undefeatable, so should your approach using AB pruning.
I have written my java implementation of a max heap made of nodes that contain two things, a string and a double value that can be accessed from each. They are supposed to be inserted by rank of their double value. I'm not sure if its my insert or remove that isn't working properly, but when I try to remove the top five max values from the heap I'm not getting what I'm supposed to be getting. Any ideas where the hiccup is? There are methods in these such as isfull and isempty to test the base cases of it being empty or full of course... Count is total number of nodes in the array (heap is the array).
public boolean insert(String W, double R){
HeapNode word = new HeapNode(W,R);
if (isFull()){
return false;
}
else {
count++;
heap[count - 1] = word;
siftUp(count - 1);
}
System.out.println("Added");
return true;
}
public boolean siftUp(int place){
int parentNode;
HeapNode tmp;
if (place != 0) {
//parent node of place
//parentNode = getParentNode(place);
parentNode = ((place-1) / 2);
if (heap[parentNode].getDouble() < heap[place].getDouble()) {
tmp = heap[parentNode];
heap[parentNode] = heap[place];
heap[place] = tmp;
siftUp(parentNode);
}
}
return true;
}
Thats the insert, now the remove:
public HeapNode remove(){
HeapNode maxValue;
if (isEmpty()){
return null;
}
else{
// Where does the max value always reside?
maxValue = heap[0];
// What value will take the root? Last one.
heap[0] = heap[count-1];
count--; ;
// Begin percolate down at index of root
int hole = 0;
int child;
HeapNode temp = heap[hole];
while( hole * 2 + 1 < count)
{
// Index of left child of node in hole index
child = 2 * hole + 1;
//find greater child
if(child != count && (heap[child + 1].getDouble()) > (heap[child].getDouble()))
child++; //swap index
if((heap[child].getDouble()) > (temp.getDouble())) //last comparison
heap[hole] = heap[child];
else
break;
hole = child;
}
heap[hole] = temp;
}
return maxValue;
}
Test case I'm using. Entering nodes in this order based on their double values:
1.0, 0.8, 0.9, 0.8, 1.0, 0.6, 1.0, 1.0, 0.8, 1.0, 0.7, 1.0, 0.8
Removing the first five I should be getting all 1.0's? I'm getting 1.0, 0.8, 1.0, 0.7, 1.0 as the five.
I can spot 2 mistakes.
You have parentNode = (place / 2);
in the siftup method. And apparently you're using 0-based array index so Node 0 is supposed to have 1 and 2 as children, but this equation gives 1 as the parent of 2.
Change it to parentNode = ((place-1) / 2);.
The other one is in the next line:
if (heap[parentNode].getDouble() > heap[place].getDouble()).
This will bubbles the min node to the top, not the max node.
Another problem you have is this statement in your remove method:
//find greater child
if(child != count && (heap[child + 1].getDouble()) > (heap[child].getDouble()))
child++; //swap index
Here, you already know that child < count, because you tested that in the loop. But if (child+1) == count, then you're testing the current element against the previous last element in the heap.
I think what you want is:
if ((child < count-1) && (heap[child + 1].getDouble()) > (heap[child].getDouble()))
In an attempt to write a brute force maze solving C program, I've written this java program first to test an idea. I'm very new to C and intend to convert it after getting this right in java. As a result, I'm trying stick away from arraylists, fancy libraries, and such to make it easier to convert to C. The program needs to generate a single width path of shortest steps to solve a maze. I think my problem may be in fragmenting a path-storing array passed through each recursion. Thanks for looking at this. -Joe
maze:
1 3 3 3 3
3 3 3 3 3
3 0 0 0 3
3 0 3 3 3
0 3 3 3 2
Same maze solved by this program:
4 4 4 4 4
4 4 4 4 4
4 0 0 0 4
3 0 3 3 4
0 3 3 3 2
number notation are explained in code
public class javamaze {
static storage[] best_path;
static int best_count;
static storage[] path;
//the maze - 1 = start; 2 = finish; 3 = open path
static int maze[][] = {{1, 3, 3, 3, 3},
{3, 3, 3, 3, 3},
{0, 0, 0, 0, 3},
{0, 0, 3, 3, 3},
{3, 3, 3, 3, 2}};
public static void main(String[] args) {
int count1;
int count2;
//declares variables used in the solve method
best_count = 0;
storage[] path = new storage[10000];
best_path = new storage[10000];
int path_count = 0;
System.out.println("Here is the maze:");
for(count1 = 0; count1 < 5; count1++) {
for(count2 = 0; count2 < 5; count2++) {
System.out.print(maze[count1][count2] + " ");
}
System.out.println("");
}
//solves the maze
solve(findStart()/5, findStart()%5, path, path_count);
//assigns an int 4 path to the maze to visually represent the shortest path
for(int count = 0; count <= best_path.length - 1; count++)
if (best_path[count] != null)
maze[best_path[count].getx()][best_path[count].gety()] = 4;
System.out.print("Here is the solved maze\n");
//prints the solved maze
for(count1 = 0; count1 < 5; count1++) {
for(count2 = 0; count2 < 5; count2++){
System.out.print(maze[count1][count2] + " ");
}
System.out.print("\n");
}
}
//finds maze start marked by int 1 - this works perfectly and isn't related to the problem
public static int findStart() {
int count1, count2;
for(count1 = 0; count1 < 5; count1++) {
for(count2 = 0; count2 < 5; count2++) {
if (maze[count1][count2] == 1)
return (count1 * 5 + count2);
}
}
return -1;
}
//saves path coordinate values into a new array
public static void save_storage(storage[] old_storage) {
int count;
for(count = 0; count < old_storage.length; count++) {
best_path[count] = old_storage[count];
}
}
//solves the maze
public static Boolean solve(int x, int y, storage[] path, int path_count) {
//checks to see if grid squares are valid (3 = open path; 0 = wall
if (x < 0 || x > 4) { //array grid is a 5 by 5
//System.out.println("found row end returning false");
return false;
}
if (y < 0 || y > 4) {
//System.out.println("Found col end returning false");
return false;
}
//when finding finish - records the number of moves in static int best_count
if (maze[x][y] == 2) {
if (best_count == 0 || best_count > path_count) {
System.out.println("Found end with this many moves: " + path_count);
best_count = path_count;
save_storage(path); //copies path counting array into a new static array
}
}
//returns false if it hits a wall
if (maze[x][y] == 0)
return false;
//checks with previously crossed paths to prevent an unnecessary repeat in steps
for(storage i: path)
if (i != null)
if (i.getx() == x && i.gety() == y)
return false;
//saves current recursive x, y (row, col) coordinates into a storage object which is then added to an array.
//this array is supposed to fragment per each recursion which doesn't seem to - this may be the issue
storage storespoints = new storage(x, y);
path[path_count] = storespoints;
//recurses up, down, right, left
if (solve((x-1), y, path, path_count++) == true || solve((x+1), y, path, path_count++) == true ||
solve(x, (y+1), path, path_count++) == true || solve(x, (y-1), path, path_count++) == true) {
return true;
}
return false;
}
}
//stores (x, y) aka row, col coordinate points
class storage {
private int x;
private int y;
public storage(int x, int y) {
this.x = x;
this.y = y;
}
public int getx() {
return x;
}
public int gety() {
return y;
}
public String toString() {
return ("storage coordinate: " + x + ", " + y + "-------");
}
}
This wasn't originally intended to be an answer but it sort of evolved into one. Honestly, I think starting in Java and moving to C is a bad idea because the two languages are really nothing alike, and you won't be doing yourself any favors because you will run into serious issues porting it if you rely on any features java has that C doesn't (i.e. most of them)
That said, I'll sketch out some algorithmic C stuff.
Support Structures
typedef
struct Node
{
int x, y;
// x and y are array indices
}
Node;
typedef
struct Path
{
int maxlen, head;
Node * path;
// maxlen is size of path, head is the index of the current node
// path is the pointer to the node array
}
Path;
int node_compare(Node * n1, Node * n2); // returns true if nodes are equal, else false
void path_setup(Path * p, Node * n); // allocates Path.path and sets first node
void path_embiggen(Path * p); // use realloc to make path bigger in case it fills up
int path_toosmall(Path * p); // returns true if the path needs to be reallocated to add more nodes
Node * path_head(Path * p); // returns the head node of the path
void path_push(Path * p, Node * n); // pushes a new head node onto the path
void path_pop(Path * p); // pops a node from path
You might to change your maze format into an adjacency list sort of thing. You could store each node as a mask detailing which nodes you can travel to from the node.
Maze Format
const int // these constants indicate which directions of travel are possible from a node
N = (1 << 0), // travel NORTH from node is possible
S = (1 << 1), // travel SOUTH from node is possible
E = (1 << 2), // travel EAST from node is possible
W = (1 << 3), // travel WEST from node is possible
NUM_DIRECTIONS = 4; // number of directions (might not be 4. no reason it has to be)
const int
START = (1 << 4), // starting node
FINISH = (1 << 5); // finishing node
const int
MAZE_X = 4, // maze dimensions
MAZE_Y = 4;
int maze[MAZE_X][MAZE_Y] =
{
{E, S|E|W, S|E|W, S|W },
{S|FINISH, N|S, N|START, N|S },
{N|S, N|E, S|E|W, N|S|W },
{N|E, E|W, N|W, N }
};
Node start = {1, 2}; // position of start node
Node finish = {1, 0}; // position of end node
My maze is different from yours: the two formats don't quite map to each other 1:1. For example, your format allows finer movement, but mine allows one-way paths.
Note that your format explicitly positions walls. With my format, walls are conceptually located anywhere where a path is not possible. The maze I created has 3 horizontal walls and 5 vertical ones (and is also enclosed, i.e. there is a continuous wall surrounding the whole maze)
For your brute force traversal, I would use a depth first search. You can map flags to directions in a number of ways, like maybe the following. Since you are looping over each one anyway, access times are irrelevant so an array and not some sort of faster associative container will be sufficient.
Data Format to Offset Mappings
// map directions to array offsets
// format is [flag], [x offset], [y offset]
int mappings[][] =
{
{N, -1, 0},
{S, 1, 0},
{E, 0, 1},
{W, 0, -1}
}
Finally, your search. You could implement it iteratively or recursively. My example uses recursion.
Search Algorithm Pseudocode
int search_for_path(int ** maze, char ** visited, Path * path)
{
Node * head = path_head(path);
Node temp;
int i;
if (node_compare(head, &finish)) return 1; // found finish
if (visited[head->x][head->y]) return 0; // don't traverse again, that's pointless
visited[head->x][head->y] = 1;
if (path_toosmall(path)) path_embiggen(path);
for (i = 0; i < NUM_DIRECTIONS; ++i)
{
if (maze[head->x][head->y] & mappings[i][0]) // path in this direction
{
temp = {head->x + mappings[i][1], head->y + mappings[i][2]};
path_push(path, &temp);
if (search_for_path(maze, visited, path)) return 1; // something found end
path_pop(path);
}
}
return 0; // unable to find path from any unvisited neighbor
}
To call this function, you should set everything up like this:
Calling The Solver
// we already have the maze
// int maze[MAZE_X][MAZE_Y] = {...};
// make a visited list, set to all 0 (unvisited)
int visited[MAZE_X][MAZE_Y] =
{
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}
};
// setup the path
Path p;
path_setup(&p, &start);
if (search_for_path(maze, visited, &path))
{
// succeeded, path contains the list of nodes containing coordinates from start to end
}
else
{
// maze was impossible
}
It's worth noting that because I wrote this all in the edit box, I haven't tested any of it. It probably won't work on the first try and might take a little fiddling. For example, unless start and finish are declared globally, there will be a few issues. It would be better to pass the target node to the search function instead of using a global variable.