Transposition Tables and Alpha-Beta Pruning - java

I got the following problem. I have implemented transposition tables, which seem to work well or at least I can not see they are not working.
Additionally, I wanted to implement some move-ordering.
Basically, I search the move first that's saved in my transposition table. For some reason, that doesn't improve the search at all. Either I haven't implemented move-ordering properly or my implementation of the alpha-beta algorithm along with transposition tables is just wrong.
To measure the performance I counted the number of nodes visited during search. My implementation of move-ordering didn't have any effect at all.
Here is the Java-Code I have come up with:
public int alphabeta(final CheckerBoard board,int alpha,int beta,int depth,boolean nullMoveAvailable){
nodeCount++;
int alphaOrig=alpha;
Transposition entry =table.find(board.key);
if(entry!=null && entry.depth>=depth){
if(entry.flag==Transposition.EXACT){
return entry.value;
}else if(entry.flag==Transposition.UPPER){
beta = Math.min(beta,entry.value);
}else if(entry.flag == Transposition.LOWER){
alpha =Math.max(alpha,entry.value);
}
if(alpha>=beta){
return entry.value;
}
}
if(depth==0 || board.isTerminalState()){
return quiesceneSearch2(board,alpha,beta);
}
ArrayList<Move>sucessors =MGenerator.getMoves(board);
Move currentBest =null;
for( int i=0;i<sucessors.size();i++){
if(entry!=null && entry.depth<depth && (entry.flag == Transposition.UPPER || entry.flag == Transposition.EXACT) && sucessors.get(i).equals(entry.best)){
Collections.swap(sucessors,0,i);
break;
}
}
int bestValue =Integer.MIN_VALUE;
for(Move move : sucessors){
board.makeMove(move);
int value =-alphabeta(board, -beta, -alpha, depth - 1, true);
board.undoMove(move);
if(value>bestValue){
bestValue =value;
currentBest = move;
}
alpha =Math.max(alpha,value);
if(alpha>=beta){
break;
}
}
Transposition next =new Transposition();
next.depth=depth;
next.value =bestValue;
next.zobrisKey=board.key;
next.best = currentBest;
if(bestValue<=alphaOrig){
next.flag =Transposition.UPPER;
}else if(bestValue>=beta){
next.flag = Transposition.LOWER ;
}else{
next.flag = Transposition.EXACT;
}
table.insert(next);
return alpha;
}
and the following code to start the search:
public int findBestMove(int depth){
if (table != null) {
table.clear();
}
ArrayList<Move> sucessors =MGenerator.getMoves(board);
int max = Integer.MIN_VALUE;
for(Move m : sucessors){
board.makeMove(m);
int value =-alphabeta(board, -1000000, 1000000, depth, true);
board.undoMove(m);
if(value>max){
max=value;
bestMove=m;
}
}
board.makeMove(bestMove);
return max;
}
Would appreciate it if someone looked at my code. Maybe there isn't anything wrong with this part of my code but I didn't want to post everything since it's already quite a lot of code to look at.

I see a couple of things "off" compared to this reference site for alphabeta: https://web.archive.org/web/20071031100051/http://www.brucemo.com/compchess/programming/hashing.htm
1) You're not storing any transposition entries for depth 0; and
2) You're storing a transposition value of alpha instead of beta when alpha>=beta.
Don't know if that would make the difference, but...
And, oh, 3) You're also returning alpha instead of beta from the function when alpha>=beta.

Related

Problems building A* algorithm

I am trying to build an app implementing A*, and I am having trouble working on the logic. The method here takes in 4 ints (startX/Y, goalX/Y) and then using the A* algorithm, it will build an ArrayList and return it, so the main method can take iterate through and display the path A* builds. But what I am getting is a jumpy path that eventually builds a very thick path to the goal node. Can anybody pinpoint where my mistake is.
Note: open and closed are priority queues and Tile implements comparable.
public ArrayList<Tile> findPath(int sX, int sY, int gX, int gY)
{
ArrayList<Tile> path = new ArrayList<Tile>();
open.offer(gameMap[sX][sY]);
Tile currentNode = gameMap[sX][sY];
Tile goalNode = gameMap[gX][gY];
int cX;
int cY;
while(open.size() > 0){
currentNode = open.poll();
closed.offer(currentNode);
path.add(currentNode);
cX = currentNode.getX();
cY = currentNode.getY();
if(currentNode == goalNode){
break;
}
if((cX > 0 && cX < gameMap.length - 1) && (cY > 0 && cY < gameMap.length -1)){
for(int i = -1; i < 2; i++){
for(int j = 1; j > -2; j--){
if(i == 0 && j == 0){}
else{
if((gameMap[cX + i][cX + j].type != 1) && !closed.contains(gameMap[cX + i][cX + j])){
if(!open.contains(gameMap[cX + i][cX + j])){
open.offer(gameMap[cX + i][cX + j]);
gameMap[cX + i][cX + j].parent = currentNode;
}
}
}
}
}
}
}
// while(currentNode != gameMap[sX][sY]){
// path.push(currentNode);
// currentNode = currentNode.parent;
// }
return path;
}
First off, I don't think your closed set needs to be a priority queue. It's just a set of nodes that have been looked at.
You seem to be missing the core part of how A* works, which is why I think this path finder is not working to well for you.
Here's the main idea:
Have a heuristic function that guesses how far away the destination is. Ideally, that function will be admissible, meaning that it will never overestimate the distance.
For tile grids, this can be done using manhattan distance (x difference + y difference) since that is the minimum distance, so it will always be admissible.
Whenever you take a tile out of your open list and add it to the closed set, you need to update the known value of how far away the neighboring tiles are (keeping the lowest known value). Since you have the known value for the tile you are putting in the closed set, you just add 1 to all the neighbors' known values.
By updating these values, the open list may shift order (which is why a priority queue is a good choice here). The heuristic value will probably remain the same, but the known value will get more refined.
Once you reach the destination, you will have a set of closed nodes that all have a known distance. You then backtrack from the destination, looking at each neighbor that is also in the closed set and choosing the one with the lowest known distance.
In terms of how to implement this, you may want to consider having your Tile class be wrapped in another class called SearchTile or something like that. It could look like this:
//You may not want to use public variables, depending on your needs
public class SearchTile implements Comparable<SearchTile> {
public final Tile tile;
//These need to be updated
public int knownDistance = 0;
public int heuristicDistance = 0;
public SearchTile(final Tile tile) {
this.tile = tile;
}
#Override
public int compareTo(final SearchTile other) {
if (knownDistance + heuristicDistance > other.knownDistance + other.heuristicDistance) {
return 1;
} else if (knownDistance + heuristicDistance < other.knownDistance + other.heuristicDistance) {
return -1;
} else {
return 0;
}
}
}
The cool thing about A* is that in the ideal case, it should go straight to the destination. In cases with walls, it will take the best guess and as long as the heuristic is admissible, it will come up with the optimal solution.
I've not completely entered in the details of your implementation, but it comes to my mind that the way in which you are inserting the nodes in OPEN might be a cause of trouble:
if(!open.contains(gameMap[cX + i][cX + j])){
open.offer(gameMap[cX + i][cX + j]);
gameMap[cX + i][cX + j].parent = currentNode;
}
Your goal here is to manage avoiding repeated elementes in your OPEN list, but it might happen that sometimes you have to replace the element because you have encountered a way in which you reach it with a better cost. In this case you need to remove the node already inserted in OPEN and reintroduce it with a lower cost (and thus with highest priority). If you do not allow this, you might be generating suboptimal paths as it seems to be your case.
Additionaly, some logic of the algorithm is missing. You should store the accumulated cost from the start, G, and the estimated cost to goal, H, for each node you create. The OPEN list is ordered according to the value of G+H, which I didn't notice in your code to be done this way. Anyway, I recommend you to take a look of some existing implementation of A* like one of the Hipster4j library to have more details on how this works.
Hope my answer helped!

Wrap Around Grid - errors on east/west only

I have four methods that check whether or not a given grid location is next to an occupied location (value of 1). The grid is assumed to wrap around, ie, if in a 50x50 grid[0][1] is the given location and grid[49][1] is occupied, the method should return true/ My checkNorth and checkEast method are working fine, but I get an ArrayIndexOutofBoundsException: -1 error for either the south or west methods every time I run the program. I checked my math and I think it should work - am I using the modulo incorrectly, or am I missing something else?
EDIT: Clarified the wrapping criterion, word use correction.
boolean checkWest(int indexA, int indexB)
{
if (indexA-1 > 0)
{
if (grid[indexA-1][indexB] == 1)
{
return true;
}
}
if (indexA-1 < 0)
{
if (grid[(indexA-1)%width][indexB] == 1)
{return true;}
else return false;
}
return false;
}
I see a couple problems. First, Java arrays are zero-indexed, which means that the first element is at index 0. So it's okay to check grid[indexA-1][indexB] when indexA-1 is equal to 0. Second, you're not properly handling when indexA equals 0. Here is my implementation. I also simplified the logic a bit.
boolean checkWest(int indexA, int indexB)
{
if (indexA > 0)
return grid[indexA - 1][indexB] == 1;
else
return grid[width + indexA - 2][indexB] == 1;
}
EDIT: I'm pretty sure I butchered the math with the second return statement. It should be right now...

Negamax chess algorithm: How to use final return?

I've made a negamax algorithm for a chess-like game and I want to know how to use the final board value result. I understand the final return of the negamax algorithm represents what the board value will be after the player takes his best possible move, but that isn't exactly useful information. I need to know what that move is, not what it's worth.
Here's the code:
public int negamax(Match match, int depth, int alpha, int beta, int color) {
if(depth == 0) {
return color*stateScore(match);
}
ArrayList<Match> matches = getChildren(match, color);
if(matches.size() == 0) {
return color*stateScore(match);
}
int bestValue = Integer.MIN_VALUE;
for(int i = 0; i != matches.size(); i++) {
int value = -negamax(matches.get(i), depth-1, -beta, -alpha, -color);
if(value > bestValue) {
bestValue = value;
}
if(value > alpha) {
alpha = value;
}
if(alpha >= beta) {
break;
}
}
return bestValue;
}
public void getBestMove(Match match, int color) {
int bestValue = negamax(match, 4, Integer.MIN_VALUE, Integer.MAX_VALUE, color);
// What to do with bestValue???
}
I thought of re-evaluating the children of the current match state after bestValue is determined. Then I iterate through them and find which of those children has a stateScore equal to bestValue. But that wouldn't work because a lot of them will have the same stateScore anyway, it's what they can lead to which counts...
I can see you're doing a qsearch and alpha-beta. Your algorithm is well-known but you're missing a key part.
Let me sketch out the basic algorithm for chess search, it applies even to Stockfish (the strongest engine in the world).
search(Position p) {
if (leaf node)
qsearch(p)
if (need to do move reduction)
do_move_reduction_and_cut_off(p)
moves = generate_moves(p)
for_each(move in moves) {
p.move(move)
v = -search(p, -beta, -alpha)
p.undo(move)
store the score and move into a hash table
if (v > beta)
cutoff break;
}
This is just a very brief sketch, but all chess algorithms follow it. Compare your version with it, do you notice that you haven't done p.move(move) and p.undo(move)?
Basically, the traditional approach generates a list of moves for a given position. Loop through the moves, play it and undo it and search it. If you do it, you know exactly which move produces which score.
Also notice the line for storing the move and score into a hash table. If you do this, you can easily reconstruct the entire principal variation from a root node.
I don't know what exactly is inside your Java class Match, but in any case your attempt was close but no exactly the classical way to do a search. Remember you'll need to give a position object in a search algorithm but instead you gave it a Match object, which is wrong.

Binary Search using Recursion in java

I am writing a program for a recursive binary search. I have an input file with a series of sorted numbers, which I added to an ArrayList. The program searches to see if a key given by user input is in the ArrayList.
public static int binarySearch(int key, int median){
if(key == (int)array.get(median)){
return key;
}else if(key < (int)array.get(median)){
binarySearch(key,median-1);
}else if(key > (int)array.get(median)){
binarySearch(key,median+1);
}
return -1;
}
For example, let's say the key is 90. I debugged and placed watches at key and array.get(median). After stepping through the program, I discovered that even when key and array.get(median) are equal to 90, the program continues through the loop without ever returning the key. I know that recursion isn't ideal for this, but it's what I need to use.
This does not look like a correct binary search, as a binary search uses a divide and conquer approach. You only divide your list initially once and then check every element from there on. It would be better to then just divide your list again and so on, see https://en.wikipedia.org/wiki/Binary_search_algorithm
Anyway, to get your code running, why you do not get an result is most likely because you do not return the result of the recursion, but instead -1.
Remove the return -1 and set a return before your recursive binarySearch calls. And you are missing a exit condition when you do not find the element.
Example (still not a correct binary search):
public static int binarySearch(int key, int median){
if (median < 0 || median > array.size() - 1) { // element not found
return -1;
}
if (key == (int)array.get(median)){
return key;
} else if(key < (int)array.get(median)){
return binarySearch(key,median-1);
} else{
return binarySearch(key,median+1);
}
}
If all you care about is whether it is in the data structure...
public static boolean binarySearch(int key, int median){
if(key == (int)array.get(median)){
return true;
}else if(key < (int)array.get(median)){
return binarySearch(key,median-1);
}else if(key > (int)array.get(median)){
return binarySearch(key,median+1);
}
}
your code could be better written, but copied to to get across the important part of it
You should change your code like this:
public static int binarySearch(int key, int median){
if(key == (int)array.get(median)){
return key;
}else if(key < (int)array.get(median)){
return binarySearch(key,median-1);
}else if(key > (int)array.get(median)){
return binarySearch(key,median+1);
}
return -1;
}
If you do this your recursion would end but I will leave it to you to test out your binary search code. There are some things like the start index and the end index that you are ignoring in this method The return statements should be added because if you don't the control moves to the next statement in the method which is undesirable. Hope that helps!
The code posted is not a Binary Search and is, in fact, a Linear Search using recursion. A Binary Search divides the search space in half each step, binary meaning "two halves" in context. The result is that this Linear Search runs in O(n) and not the expected O(lg n) bounds of a Binary Search
Problems/issues (other than it not being a Binary Search):
The values from the recursive cases are lost as they are not themselves returned. The result of a recursive case must be used in some way (or why call the function at all?). In this case the value should be returned directly, eg. return binarySearch(..). This causes "continues through the loop without ever returning the key" - it actually does find the key, but discards the recursive result.
The code does not correctly detect the out-of-bounds terminal condition. This may result in an IndexOutOfBoundsException when the key is not found.
The step-left/right approach might never terminate correctly without further logic. For example, when the list is {1,3,5,7} and the value being sought is 4 the original code will ping-pong between indices 1 (value 3) and 2 (value 4). This will result in a StackOverflowError being thrown.
The key is returned. This makes little sense as the key is already known and it also prevents -1 from being detectable. Return the index found instead, or a boolean if only an existence test is required.
Take some time to understand and fix these issues .. and then read on for a spoiler if needed.
Consider this rewrite fixing the issues outlined above. Note that it is overly complex1 while still employing an inferior algorithm and O(n) performance. In any case;
// By taking in the List we make this function universal and not
// dependent upon a static field. It should probably take in a List<Integer>
// but I don't know what the actual type of "array" is. A more advanced
// implementation would take in List<Comparable> and then be modified to work
// with any objects that correctly implement said interface.
public static int linearSearch(int key, List list, int index, int step) {
if (index < 0 || index >= list.size()) {
// Base case: not found, out of bounds
return -1;
}
int x = (int)list.get(index);
if (key < x && step <= 0) { // need to look left, NOT looking right
// Recursive case: look left, returning result
return linearSearch(key, list, index - 1, -1);
} else if (key > x && step >= 0){ // need to look right, NOT looking left
// Recursive case: look right, returning result
return linearSearch(key, list, index + 1, +1);
} else if (key == x) {
// Base case: found key, return index found
return index;
} else {
// Base case: key not equal, refusing to ping-pong
return -1;
}
}
And then consider the use of this helper/wrapper function;
// Returns the index in "array" where key was found, or -1 if it was not found
public static int linearSearch(int key) {
// Have to start somewhere, might as well be the middle..
// ..but it does NOT make the time complexity any better
// ..and it is still NOT a Binary Search.
return linearSearch(key, array, array.size() / 2, 0);
}
1 Alternatively, since it is a Linear Search it could be rewritten without the extra left/right movement and have the same complexity bounds. (It is also trivial to modify this simpler code to a recursive Binary Search.)
public static int linearSearch(int key, List list, int index) {
if (index >= list.size() {
// Base case: end of list
return -1;
}
int x = (int)list.get(index);
if (key < x) {
// Recursive case: not there yet, keep looking
return linearSearch(key, list, index + 1);
} else if (key == x) {
// Base case: found key, return index
return index;
} else { // -> key > x
// Base case: read past where the key would be found
return -1;
}
}
Here's binary search for ArrayList<Integer> :)
public int binarySearch(List<Integer> list, int find) {
int mid = list.size() / 2;
int val = list.get(mid);
if (val == find) {
return val;
} else if (list.size() == 1) {
return -1;
}
return val > find ?
binarySearch(list.subList(0, mid), find) :
binarySearch(list.subList(mid, list.size()), find);
}

Sudoku Solver brute force algorithm

Still working on my sudoku solver, I have once again run into some trouble. I have gotten the sudoku solver to work, however whenever I attempt to solve a really "hard" sudoku board, my solver tells me there are no possible solutions, due to a stack overflow error. And yes, I know for a fact that these boards DO have a solution.
I am using the brute force algorithm -- I start at square one (or [0][0] if you prefer) and insert the first legal value. I then do a recursive call to the next square, and so on. If my function has not gone through the entire board, and finds there are no possible legal values, it moves to the previous square and attempts to increment the value there. If that fails, it moves further back. If it ends up at square one with no possible solutions, it exits.
I admit my code is not pretty and probably quite inefftective, but please keep in mind that I am a first year student trying to do my best. I don't mind comments on how to effectivize my code though :)
For squares without a final predefined number, here's the solver function:
public void fillRemainderOfBoard(Square sender){
try {
while(true){
if(currentPos > boardDimension){
if(previous != null){
this.setNumrep(-1);
this.setCurrentPos(1);
previous.setCurrentPos(previous.getCurrentPos()+1);
previous.fillRemainderOfBoard(this);
return;
} else if(sender == this){
this.setCurrentPos(1);
} else {
break;
}
}
if((thisBox.hasNumber(currentPos)) || (thisRow.hasNumber(currentPos)) || (thisColumn.hasNumber(currentPos))){
currentPos++;
} else {
this.setNumrep(currentPos);
currentPos++;
break;
}
}
if(this != last){
next.setNumrep(-1);
next.fillRemainderOfBoard(this);
} else {
return;
}
} catch (StackOverflowError e){
return;
}
}
In my code, the numrep value is the value that the square represents on the board. The currentPos variable is a counter that starts at 1 and increments until it reaches a legal value for the square to represent.
For squares with a predefined number, here's the same function:
public void fillRemainderOfBoard(Square sender){
if((sender.equals(this) || sender.equals(previous)) && this != last){
System.out.println(next);
next.fillRemainderOfBoard(this);
} else if (sender.equals(next) && this != first) {
previous.fillRemainderOfBoard(this);
} else {
System.out.println("No possible solutions for this board.");
}
}
Now, my problem is, like I said, that the function DOES solve sudokus very well. Easy ones. The tricky sudokus, like those with many predefined numbers and only a single solution, just makes my program go into a stack overflow and tell me there are no possible solutions. I assume this indicates I am missing something in the terms of memory management, or the program duplicating objects which I call in my recursive functions, but I do not know how to fix them.
Any help is greatly appreciated! Feel free to pick on my code too; I'm still learning (oh, aren't we always?).
___________________EDIT________________
Thanks to Piotr who came up with the good idea of backtracking, I have rewritten my code. However, I still cannot get it to solve any sudoku at all. Even with an empty board, it gets to square number 39, and then returns false all the way back. Here is my current code:
public boolean fillInnRemainingOfBoard(Square sender){
if(this instanceof SquarePredef && next != null){
return next.fillInnRemainingOfBoard(this);
} else if (this instanceof SquarePredef && next == null){
return true;
}
if(this instanceof SquareNoPredef){
currentPos = 1;
if(next != null){
System.out.println(this.index);
for(; currentPos <= boardDimension; currentPos++){
if((thisBox.hasNumber(currentPos)) || (thisRow.hasNumber(currentPos)) || (thisColumn.hasNumber(currentPos))){
continue;
} else {
System.out.println("Box has " + currentPos + "? " + thisBox.hasNumber(currentPos));
this.setNumrep(currentPos);
System.out.println("Got here, square " + index + " i: " + numrep);
}
if(next != null && next.fillInnRemainingOfBoard(this)){
System.out.println("Returnerer true");
return true;
} else {
}
}
}
if(next == null){
return true;
}
}
System.out.println("Returning false. Square: " + index + " currentPos: " + currentPos);
return false;
}
I had to complicate things a bit because I needed to check whether the current object is the last. Hence the additional if tests. Oh, and the reason I am using boardDimension is because this sudoku solver will solve any sudoku -- not just those 9 by 9 sudokus :)
Can you spot my error? Any help is appreciated!
So your problem lies here:
previous.fillRemainderOfBoard(this);
and here
next.fillRemainderOfBoard(this);
Basically you have too many function calls on the stack because you are going forward and backward multiple times. You are not really returning from any call before answer is found (or you get StackOverflowError :)). Instead of increasing stack size by using recursive function try to think how to solve problem using a loop (pretty much always loop is better than recursive solution performance wise).
Ok so you can do something like this (pseudo code):
boolean fillRemainderOfBoard(Square sender){
if (num_defined_on_start) {
return next.fillRemainderOfBoard(this);
} else {
for (i = 1; i < 9; ++i) {
if (setting i a legal_move)
this.setCurrentPos(i);
else
continue;
if (next.fillRemainderOfBoard(this))
return true;
}
return false;
}
The stack size will be ~81.

Categories