I'm currently working on an Android App containing TicTacToe with an AI opponent.
I've come real far, but for some reason not all moves the opponent calculates are leading him to victory or a tie.
I've made a recursive MiniMax algorithm (no pruning yet), and I hope somebody is able to see what's wrong here, since I really couldn't find it. I've drawn many trees so far and
I've done a lot of debugging in Eclipse ADE and every step seems to be right, but in the end, it's obviously not.
So here's my function:
public int[] MiniMax( int[][] gameboard, boolean isCPUTurn, int[] move, int depth ){
if (gameboard[1][1]==EMPTY) { // Always take middle if possible
int[] best = {2,1,1};
return best;
}
// Recursion base-case
int winner = checkWinner();
if ( winner == 1 || winner == 2 || winner == 3 ){ // Game is over
int[] returnSet = {-1,-1,-1}; // leeg
// adjust scores for Minimax Player<----Tie---->CPU
// 0 1 2
switch (winner){
case 1:
returnSet[0] = 0 ; // Player wins
break;
case 2:
returnSet[0] = 2; // CPU wins
break;
case 3:
returnSet[0] = 1; // TIE
break;
default: // impossible..
returnSet[0] = 1;
}
// The last move led to the end of the game, return this outcome and move
returnSet[1] = move[0];
returnSet[2] = move[1];
return returnSet;
}
// A move is still possible, the game's not over yet
else{
int[] bestMove = null; // contains the best move found for this turn
for (int i=0; i<3; i++){ // check all child nodes/boards
for (int j=0; j<3; j++){
if( gameboard[i][j] == EMPTY ) // if the spot's not yet filled
{
if (isCPUTurn) gameboard[i][j] = TWO; // this move is CPU's
else gameboard[i][j] = ONE; // this move is Player's
int[] thismove = {i,j}; // {move Row, move Column}
// recursion
int[] result = MiniMax( gameboard, !isCPUTurn, thismove, depth+1);
// result = { winner result, move Row, move Column}
gameboard[i][j] = EMPTY; // delete last move after recursion
// check if child is best option
if (bestMove == null) // first child is always the best initially
bestMove = result;
else {
if (!isCPUTurn) // for CPU, higher is better
{ if (result[0]>bestMove[0])
bestMove = result;
//if (bestMove[0]==2) return bestMove; Pruning thingy
}
else if (isCPUTurn) // for Player, lower is better
{ if (result[0]<bestMove[0])
bestMove = result;
//if (bestMove[0]==0) return bestMove; Pruning thingy
}
}
}
}
}
return bestMove; // returns the best move found after checking all possible childs
}
}
As you can see, this function calls itself directly, using middle recursion.
It also calls the checkWinner function, which has 4 possible outcomes: 0 if nobody has won yet and the board is not filled, 1 if player 1 (you) has won, 2 if player 2 (AI) has won and 3 if it's a tie (and thus the board is filled).
So yea, I hope somebody finds the solution :D
Thanks in advance!
The problem is that you are always returning the move from the deepest level. In the lines where you check if you have improved
, you should set bestMoveto contain the coordinates iand j, not the coordinates from the move one level deeper, namely result.
Related
Okay, I am building a game in my free time for fun. This is not for work or school. I graduated. What I didn't do is graduate in game programming so I am learning it as I go. I have developed a couple of games, but this one is probably my most advanced.
I have about 5 2d arrays. One to hold the Bitmaps, one to hold X locations, one to hold Y locations, one to hold the state of the element (they are birds, status is for flying state and hit state), and one identifying what the element is. Each array works together to give me a game board with birds flapping their wings. I can touch the game board, I get the location back. This part all works.
What I am trying to do is create a way to detect if elements nearby are the same element as the flagged element (the touched item). When this happens I basically want to flag all of these elements as "Hit". This is not just getting the 8 blocks around the element, this is getting every item touching the flagged item and identifying them as "Hit". You probably have played games like this, maybe. They are puzzle like games (mines cooler because the birds poop on you and it kills your character :) ). I created another 2D array to tack hit state (in this latest iteration of my attempt to solve the problem, so I don't double count a hit, and I am now using it to flag items I don't count with a -1.
My arrays are all [8][8] arrays. They don't change, they will always be that way. This should make it easier. So as an example:
sample from game
The circled red birds in the image should have been flagged as hit as well.
On to the code:
public void calculateGunDestruction(){
int currentRow = targetBirdRow;
int currentCol = targetBirdCol;
int hitbird = whichBird[targetBirdRow][targetBirdCol];
boolean[] pathState = new boolean[5];
countedArray = new int[8][8];
for(int j = 0; j < countedArray.length;j++){
for(int jj = 0; jj< countedArray[j].length;jj++){
countedArray[j][jj] = 0;
}
}
for(int i = currentRow;i < whichBird.length; i++) {
for (int ii = currentCol; ii < whichBird[i].length; ii++) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
for(int i = currentRow;i > -1; i--) {
for (int ii = currentCol; ii < whichBird[i].length; ii++) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
for(int i = currentRow;i < whichBird.length; i++) {
for (int ii = currentCol; ii > - 1; ii--) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
for(int i = currentRow;i > -1; i--) {
for (int ii = currentCol; ii > - 1; ii--) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
}
public boolean[] pathMatch(int i, int ii, int hitbird){
boolean pathTop = false;
boolean pathBottom = false;
boolean pathLeft = false;
boolean pathRight = false;
if(findMatch(i,ii,hitbird)) {
if(countedArray[i][ii] == 0) {
countedArray[i][ii] = 1;
destroyedBirds(i, ii);
}
if((i - 1) > -1) {
if (countedArray[i - 1][ii] == 0) {
if (findMatch(i - 1, ii, hitbird)) {
countedArray[i - 1][ii] = 1;
destroyedBirds(i - 1, ii);
pathLeft = true;
} else {countedArray[i - 1][ii] = -1;pathLeft = false;}
} else {pathLeft = false;}
} else {pathLeft = false;}
if((i + 1) < 8) {
if (countedArray[i + 1][ii] == 0) {
if (findMatch(i + 1, ii, hitbird)) {
countedArray[i + 1][ii] = 1;
destroyedBirds(i + 1, ii);
pathRight = true;
} else {countedArray[i + 1][ii] = -1;pathRight = false;}
} else {pathRight = false;}
}else {pathRight = false;}
if((ii - 1) > -1) {
if (countedArray[i][ii - 1] == 0) {
if (findMatch(i, ii - 1, hitbird)) {
countedArray[i][ii - 1] = 1;
destroyedBirds(i, ii - 1);
pathTop = true;
} else {countedArray[i][ii - 1] = -1;pathTop = false;}
} else {pathTop = false;}
} else {pathTop = false;}
if((ii + 1) < 8) {
if (countedArray[i][ii + 1] == 0) {
if (findMatch(i, ii + 1, hitbird)) {
countedArray[i][ii + 1] = 1;
destroyedBirds(i, ii + 1);
pathBottom = true;
} else {countedArray[i][ii + 1] = -1;pathBottom = false;}
} else {pathBottom = false;}
}else {pathBottom = false;}
}else{
countedArray[i][ii] = -1;
}
return new boolean[]{pathTop,pathBottom,pathLeft,pathRight};
}
public boolean findMatch(int row,int col,int hitbird){
if(hitbird == whichBird[row][col]){
return true;
}
return false;
}
public void destroyedBirds(int row,int col){
destroyedBirdsRow.add(0, row);
destroyedBirdsCol.add(0, col);
}
I have tried several different ways, this is just the latest as of this writing. Some were closer than others, but none quite got it. These seems like it should be a real simple thing to do, but I am not a game programmer professionally, game programming is something I am trying to learn.
The code above basically breaks the 2D array out into quadrants from the point at which you touch. I added the control "countedArray" with this attempt because I knew there was no real way to stop double counting with this method without some sort of way to track it. It goes left/down, left,up, right/down, right/up from the touch point.
the touch points near the top:
int currentRow = targetBirdRow;
int currentCol = targetBirdCol;
int hitbird = whichBird[targetBirdRow][targetBirdCol];
hit bird holds the identifier of the touched bird or element. This is not a terribly elegant solution, but it also doesn't work. At this point elegance is thrown out the window, I am about to write a long nasty looking piece of code that no one can read checking each item individual with no looping and hope that works. So cobbled solutions are okay.
For reference sake - the area the above code is called from looks like this:
if(playerObj.getWeaponSelected() == wGun) {
calculateGunDestruction();//CALLING FUNCTION
for (int i = 0; i < destroyedBirdsRow.size(); i++) {
state[destroyedBirdsRow.get(i)][destroyedBirdsCol.get(i)] = hit;
}
destroyedBirdsRow.clear();
destroyedBirdsCol.clear();
Full Game Image
I have edited to add a full game image. Questions on why it is over complicated prompted me to explain a bit. (And yes, there may be better ways to do it - I am certainly no expert on this). The idea is, as the birds are hit, to display a brief hit image, make the hit bird disappear, and then move birds in from the left, adding a new bird(s) to replace the hit birds. That structure all works - if not perhaps over complicated in design. Single hits work, bomb hits work (when you shoot a bomb that randomly appears on the board as a new bird). I use the position arrays to guide the animation of the birds that need to move in to fill the gap left by the hit birds. I wouldn't be surprised to learn of a better way, and I am here and doing this to learn so feel free to comment outside of the original scope of the question. It may help others trying to learn, and me; and I think that really is the most important point of a site like this.
As far as the part I am having difficulty getting to work. When I shoot the gun, the idea was to have it peg all birds of the same color/species that are touching the hit bird, mark them as hit, and make them disappear. And yes, the code I currently have for that section is probably the most complicated version I wrote. I started over at least a dozen times now. The simplest version involved two groups of loops and less outside methods (it was half the size) - it worked about as well as this one, but tended to flag extra birds. This one doesn't seem to flag extra birds, but doesn't get them all.
I have built a few small games in the past - but this one is by far the most complex one I have attempted and its looking pretty good so far - except for the whole grouped kill thing.
Possibly your downfall with this is over complication in design. I am struggling to understand why you need 5 8x8 arrays to store a 2D game board.
Your solution for marking hit tiles and those around it should be something like this for simplicity:
public static final int NOTHING=0,BIRD_FLYING=1,BIRD_HIT=2,
TILE_LAND=0,TILE_WATER=1;
private int[][] gameBoard=new int[8][8]; // Store terrain etc.
private int[][] birds=new int[8][8]; // Store entities on the map
private int tileSize=32; // Store the size of the tile in pixels
private Image landImg,seaImg,flyingImg,hitImg; // Store the tile images (or use 1 tilesheet and segment it in the draw method)
private void hitTile(int x,int y)
{
for(int i=-1;i<=1;i++)
{
for(int j=-1;j<=1;j++)
{
birds[hitX+i,hitY+j]=BIRD_HIT;
}
}
}
private void drawTheTiles()
{
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
// Draw background tiles first
switch(gameBoard[i][j])
{
default:
case TILE_LAND:
g.drawImage(landImg,i*tileSize,j*tileSize,this);
break;
case TILE_SEA:
g.drawImage(seaImg,i*tileSize,j*tileSize,this);
break;
}
// Draw entities on tiles last
switch(gameBoard[i][j])
{
default:
case NOTHING:
// Don't draw anything
break;
case BIRD_FLYING:
g.drawImage(flyingImg,i*tileSize,j*tileSize,this);
break;
case BIRD_HIT:
g.drawImage(hitImg,i*tileSize,j*tileSize,this);
break;
}
}
}
}
Of course, you'll need to add in your own handling, image loading, rendering, and array entries but there should be no need to store positions in an array for a 2D game board like this, as the positions can be implied by the tile index.
I hope this gives you something to go on. But I'm afraid I don't fully understand your original program/premise.
I am making connect 4 AI, except the game continues until all 42 spaces are filled.
Score is kept by every 4 in a row gets 1 point.
public int[] Max_Value(GameBoard playBoard, int depth){
GameBoard temp = new GameBoard(playBoard.playBoard);
int h = 0, tempH = 999, tempCol=0;
int myDepth = depth - 1;
int[] tempH2 = new int[2];
boolean noChildren = true;
if(myDepth != -1){
for(int i = 0; i < 7; i++){
if(temp.isValidPlay(i)){
count++;
temp.playPiece(i);
noChildren = false;
tempH2 = Min_Value(temp, myDepth);
if(tempH2[1] < tempH){
tempH=tempH2[1];
tempCol = i;
}
temp.removePiece(i);
}
}
}
int[] x = new int[2];
if(noChildren){
h = temp.getHeuristic();
}
else{
h = tempH;
x[0]=tempCol;
}
x[1]=h;
return x;
}
public int[] Min_Value(GameBoard playBoard, int depth){
GameBoard temp = new GameBoard(playBoard.playBoard);
int h = 0, tempH = -999, tempCol=0;
int myDepth = depth - 1;
int[] tempH2 = new int[2];
boolean noChildren = true;
if(myDepth != -1){
for(int i = 0; i < 7; i++){
if(temp.isValidPlay(i)){
count++;
temp.playPiece(i);
noChildren = false;
tempH2 = Max_Value(temp, myDepth);
if(tempH2[1] > tempH){
tempH=tempH2[1];
tempCol = i;
}
temp.removePiece(i);
}
}
}
int[] x = new int[2];
if(noChildren){
h = temp.getHeuristic();
}
else{
h = tempH;
x[0]=tempCol;
}
x[1]=h;
return x;
}
I feel like I just stumbled through everything, and it feels like terrible code. However, I have never attempted anything like this before, and would appreciate any input. I can't tell where I am going wrong. My evaluation function just gives 1 point for each 4 in a row it can find for any given state. The main function calls the Min_Value function to start things off with a depth of 10.
I am attempting to return the column as well as the value of the heuristic. I hope I have provided enough information. Thanks for any insight.
Allright, after implementing the methods not shown (like evaluation, playmove, remove etc.) I was able to debug this. Assuming that these functions are implemented in some correct way in your version, the mistake is that you never actually call the evaluation function if depth is -1:
You have this:
[...]if(myDepth != -1)
{/*restofthecode*/}[...]
But what you need is something like this:
[...]if(myDepth == -1)
{
return temp.getHeuristic();
}
/*restofthecode*/
[...]
That way, whenever you reach depth -1 (a leaf in your minimax tree), the board will be evaluated and the value returned (which is excactly what you need in minimax).
Do this modification in both parts (min and max) and everything shuold be allright. If there are other problems, feel free to ask.
Even though it isn't stated in the question, I think you are not getting good moves from your search, right?
Without looking through your while code, I can already say that your program will only work during the last 10 moves of the game (last 10 empty fields or forced win in 10). Otherwise, your program will return either the last or the first move it evaluated. That is because of your evaluation function, where you only handle a win (respectively 4 in a row), but not 2 in a row, traps, 3 in a row, etc.). It will think of all moves as equal if it can't force a win.
This is a problem, because starting with an empty field, a win can only be forced by the starting player, and just with the 2nd last piece to be placed on the board. (In your version 4 in a row forced).
And since your searchdepth (10) is smaller than the maximum game moves (42), your program will always play its first move.
If the rest of your algorithm is correctly implemented, you can fix this by simply improve your evaluation function, so that it can differ between "good" and "bad" game positions.
I am trying to solve this problem. I wrote some code, a recursion that tries to find the solution to this problem, but it takes a lot of time to process. How could I make the method more effective?
public static int placeKnights(Square current, int[][] board, int knightsPlaced) {
ArrayList<Square> canAttackThisSquare = Square.canReachThisSquare(current); //Get a list of squares, that can attack our current square
for (Square square : canAttackThisSquare) { //Take each square from the list
int[][] currBoard = Main.copyMatrix(board); //Copy the board, for next call
Square curr = new Square(current.x, current.y); //Copy the current square, for next call
int knights = knightsPlaced + 1; //Current knights placed (copying, so original knightsPlaced remain the same, to use when backtracking)
ArrayList<Square> canBeAttackedFromSquare = Square.canReachThisSquare(square); //Get a list of squares, that can be attacked by the new knight
for (Square sq : canBeAttackedFromSquare) { //Put 1, to empty squares.
if (currBoard[sq.x][sq.y] != 2) {
currBoard[sq.x][sq.y] = 1;
}
}
// Get next square to attack
while (board[current.x][current.y] != 0) {
if (current.x == 7) {
if (current.y == 7) {
return knights; //If the board is done then finish the recursion
} else {
current.x = 0;
current.y++;
}
} else {
current.x++;
}
}
if (knights == 12) {
return 13; //If the board is not done and we have already placed 12 knights, then end the recursion and return >12 value
}
if (placeKnights(curr, currBoard, knights) == 12) { //Else call the method on the next square, and get the result of recursion
return 12; //If the board got filled with 12 knights, then return 12
}
//Else backtrack and try next way
}
return 13; //If it checks all the possible choices, it returns 13, so we backtrack.
}
}
So, I am making a chess engine in Java. Given a current board configuration, the AI should figure out each possible move it can make, add it to a linked list of all possible moves, and return that list. Currently I am testing the following board configuration:
/*bRbNbBbQbKbBbNbR
bPbPbPbPbPbPbPbP
NuNuNuNuNuNuNuNu
NuNuNuwRNuNuNuNu
NuNuNuNuNuNuNuNu
NuNuNuNuNuNuNuNu
wPwPwPwPwPwPwPwP
NuwNwBwQwKwBwNwR*/
"bR" means black rook, "bN" is black knight, etc. "Nu" means null or no piece. In this configuration I moved the bottom-left white rook to the middle of the board.
The following method, possibleMoves(), in my Mobility class is what should generate and return a linked list of all of the possible board configurations. Each index i corresponds to pieces on the board starting from the left. In this case the AI is white, so 0 is the leftmost white pawn, 7 is the rightmost white pawn, 8 is the white rook that is now in the center of the board, 9 is the other white rook, etc. Right now I'm only testing the rooks, so the other conditionals are empty. nonPawnBoardGen() returns a sublist of possible board configurations.
public LL possibleMoves(){
LL children = new LL();
/*
* check each piece
*/
for(int i = 0; i < 16; i++){
if(i < 8){//pawns
}
else if(i < 10){//rooks
children.joinWith(nonPawnBoardGen(i, 0, -1)); //positions to the left
children.joinWith(nonPawnBoardGen(i, 0, 1)); //right
children.joinWith(nonPawnBoardGen(i, -1, 0)); //up
children.joinWith(nonPawnBoardGen(i, 1, 0)); //down
}
else if(i < 12){
// checkKnight(r, c, int dR, int dC)
}
else if(i < 14){//bishops
}
else{ //king, queen
}
}
return children;
}
joinWith(), in my LL class, joins a sublist with the total children linked list.
public void joinWith(LL newList){
if(newList.isEmpty())
return;
if(this.isEmpty()){
first = newList.getFirst();
last = newList.getLast();
}
else{
last.next = newList.getFirst();
last = newList.getLast();
}
}
The following function, nonPawnBoardGen(), is another function in my Mobility which gets passed a piece index and a unit vector. So, if I want to check all of the possible left moves of the rook in the center of the board, I would call nonPawnBoardGen(8, 0, -1) because the rook is index 8, it will remain in the same row, and it will iterate through columns to the left. That function call should return a sublist of all of the possible board configurations involving this rook because I would still need to check everything to the right, up, and down from the rooks current position.
private LL nonPawnBoardGen(int index, int vecR, int vecC){
LL boardSubLst = new LL();
int sR, sC; //source row and col
if(turn == true){//white
//last 16 coords are white pieces
if(coords[index + 16] == null){//if piece does not exist, return
return null;
}
sR = coords[index + 16].getRow(); //each coord is an object containing a row and col value
sC = coords[index + 16].getCol();
}
else{//black
//first 16 coords are black pieces
if(coords[index] == null){
return null;
}
sR = coords[index].getRow();
sC = coords[index].getCol();
}
int curR = sR; //current row
int curC = sC; //current col
curR+=vecR; //iterate by unit vector
curC+=vecC;
while(curR > -1 && curR < 8 && curC > -1 && curC < 8){ //while in range of board
if(turn == true){//white
if(board[curR][curC].charAt(0) != 'w'){ //if space is free or opposite color, valid move
coords[index + 16].setRow(curR); //move rook to new position
coords[index + 16].setCol(curC);
if(board[curR][curC].charAt(0) == 'b'){ //if space contains piece of opposite color,
int r, c; //piece needs to be removed
for(int j = 0; j < 16; j++){ //iterate through 16 opponent pieces
r = coords[j].getRow();
c = coords[j].getCol();
if(curR == r && curC == c){ //check which opponent's piece's coords match
coords[j] = null; //the rook's current coords, then remove opp's piece
boardSubLst.insert(coords); //add board config to sublist
break;
}
}
break;
}
else{ //if the space is null, simply add board config to sublist
boardSubLst.insert(coords);
}
}
else{ //if space is same color, break
break;
}
}
else{//black
if(board[curR][curC].charAt(0) != 'b'){
coords[index].setRow(curR);
coords[index].setCol(curC);
if(board[curR][curC].charAt(0) == 'w'){
int r, c;
for(int j = 0; j < 16; j++){
r = coords[j + 16].getRow();
c = coords[j + 16].getCol();
if(curR == r && curC == c){
coords[j + 16] = null;
boardSubLst.insert(coords);
break;
}
}
break;
}
else{
boardSubLst.insert(coords);
}
}
else{
break;
}
}
curR+=vecR;
curC+=vecC;
}
return boardSubLst;
}
To make this long story short, in nonPawnBoardGen(), every time I get a new valid board configuration, I edit the board coordinates (white in this case):
coords[index + 16].setRow(curR);
coords[index + 16].setCol(curC);
and add them to a list of board configurations:
boardSubLst.insert(coords);
However, every time I edit coords, each value in the boardSubList linked list changes to the current value of coords. Why is this happening?
EDIT:
I think I can avoid this problem just by having nonPawnBoardGen() generate and return only one set of coordinates. The iterator can be saved in the class rather than locally in the function. Each set of coordinates returned can be added directly to the list of children in possibleMoves(). I will try this and see what happens...
When you call
boardSubLst.insert(coords);
You are passing the same reference to the coords array. I think you will find the easiest solution is to copy the array instead, for example using Arrays.copyOf(T[] original, int newLength)
boardSubLst.insert(Arrays.copyOf(coords, coords.length));
Or, assuming coords is of type Coord[] you could use System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Coord[] coords2 = new Coord[coords.length];
System.arraycopy(coords, 0, coords2, 0, coords.length);
boardSubLst.insert(coords2);
I stopped working on my code for awhile, just came back to it and solved the problem. Thank you Elliott and Anantha. You were both right. My array of chess piece coordinates is actually an array of objects, with each object being a set of coordinates. Originally, I changed my code so that I was making a copy of the array each time before editing the coordinates and adding the array to my list, but this was only making new references to the same set of the array's objects. The solution was to not only make new array references, but to allocate new coordinate objects within the array every time I changed coordinates. I think I'm making this sound a lot more convoluted than it actually is, but yes it was a problem of continuously referencing the same area in memory. It was not too obvious. Thanks a lot!
I have created simple Checkers game in Java with minmax AI, but I doesn't work. I use recursive version of minmax, but there must be something wrong with it, because it returns moves, that aren't best, but maybe first generated.
public int minmax(int [][] board, int depth, int curPlayer){
ArrayList<Move> moves = findMoves(curPlayer, board);
if (depth == 0 || moves.size() == 0){
return heurValue(curPlayer, board);
}
int bestVal = 0;
if (curPlayer == GameCore.BLACK){
bestVal = Integer.MIN_VALUE;
curPlayer = GameCore.RED;
}else{
bestVal = Integer.MAX_VALUE;
curPlayer = GameCore.BLACK;
}
for(int i = 0; i<moves.size(); i++){
Move m = moves.get(i);
int [][] boardNew = makeMove(m, board);
int value = minmax(boardNew, depth-1, curPlayer);
board = undoMove(m, boardNew);
// computer plays as black
if (curPlayer == GameCore.BLACK){
if (value < bestVal){
bestMove = m;
bestVal = value;
}
}else{
if (value >= bestVal){
bestMove = m;
bestVal = value;
}
}
}
return bestVal;
}
If I call minmax with depth = 1 it should "return 7 values (there are 7 possible moves), but it returns only 1 if I move from 2,4 to 3,3... but when I tried to debug it, ArrayList moves has correct size. So I don't know what is wrong with it. :(
EDIT:
By "return" I mistakenly mean that first condition (when depth is 0 or moves are empty) happens only once, but if it was correct it should happen 7 times. Sorry for my bad english.
Do you know some site, where is correct recursive pseudocode for minmax (better with alpha/beta, because I will need to expand it) or could you help me to fix this? It must be only trifle.
Thank you!
You've written this to only return the best value, i.e. return bestVal; If you want it to return them all, store them in a List of some sort and change the method signature accordingly.
EDIT: So it is not a signature problem as I first thought.
I've done a quick search about the MinMax algorythm and this is what i found an article
Quickly what I think is important from this page is :
The values here represents how good a move is. So the MAX player will try to select the move with highest value in the end.
So if I'm rigth, MinMax will only return one move, the one with the bestValue.