package checkers; import java.util.ArrayList; import java.util.List; /** * * @author Tony */ public class Checkers { public static void main(String[] args) { BoardAndAIRules b = new BoardAndAIRules(); b.initializeBoard(); /**for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { b.board[j][i]="OO"; } } b.board[1][6]="xx"; b.printBoard(); b.minMaxAlphaBeta(0, true, -1000000, 1111111111,"xx","aa"); b.board[b.bestMoveForAI.y][b.bestMoveForAI.x] = b.bestMoveForAI.unitPromotedTo; b.board[b.bestMoveForAI.originalPositionY][b.bestMoveForAI.originalPositionX]="OO"; b.printBoard(); System.out.println();**/ b.allPossibleNextMoves("aa"); b.playAIvsAI(1); } } class BoardAndAIRules { int averageRecursiveCallCount = 0; int totalRecusirveCallCount = 0; int recursiveCallsCount = 0; int numberOfDraws = 0; int numberOfAIWins = 0; int numberOfP1Wins = 0; String[][] board = new String[8][8]; List allPossibleSequences; Position bestMoveForAI; String whoWon; public void initializeBoard() { recursiveCallsCount = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 8; j++) { if (i%2==0&&j%2==0)board[j][i] = "OO"; else if (i%2==0&&(j%2!=0))board[j][i]="xx"; else if (i%2!=0&&j%2==0)board[j][i]="xx"; else if (i%2!=0&&j%2!=0)board[j][i]="OO"; } } for (int i =3; i<5;i++) { for (int j = 0; j<8; j++) { board[j][i]="OO"; } } for (int i = 5; i < 8; i++) { for (int j = 0; j < 8; j++) { if (i%2==0&&j%2==0)board[j][i] = "OO"; else if (i%2==0&&(j%2!=0))board[j][i]="aa"; else if (i%2!=0&&j%2==0)board[j][i]="aa"; else if (i%2!=0&&j%2!=0)board[j][i]="OO"; } } } public boolean isThereUnitsLeft() { int xxCount = 0; int XXCount = 0; int aaCount = 0; int AACount = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { switch (board[j][i]) { case "xx": xxCount++; break; case "XX": XXCount++; break; case "AA": AACount++; break; case "aa": aaCount++; break; } } } return xxCount+XXCount!=0||aaCount+AACount!=0; } public void playAIvsAI(int howManyPlays) { for(int i = 1; i<=howManyPlays;i++){ boolean turn = true; do { if (turn == true){ minMaxAlphaBeta(0, true,Integer.MIN_VALUE,Integer.MAX_VALUE,"xx","aa"); board[bestMoveForAI.y][bestMoveForAI.x] = bestMoveForAI.unitPromotedTo; board[bestMoveForAI.originalPositionY][bestMoveForAI.originalPositionX]="OO"; turn =false; if (bestMoveForAI.killedY>=0&&bestMoveForAI.killedX>=0) board[bestMoveForAI.killedY][bestMoveForAI.killedX]="OO"; } else { minMaxAlphaBeta(0, true,Integer.MIN_VALUE,Integer.MAX_VALUE, "aa","xx"); board[bestMoveForAI.y][bestMoveForAI.x] = bestMoveForAI.unitPromotedTo; board[bestMoveForAI.originalPositionY][bestMoveForAI.originalPositionX]="OO"; turn =true; if (bestMoveForAI.killedY>=0&&bestMoveForAI.killedX>=0)board[bestMoveForAI.killedY][bestMoveForAI.killedX]="OO"; } if (turn ==false)System.out.println("xx turn below:"); else System.out.println("aa turn below:"); printBoard(); } while(isThereUnitsLeft()); totalRecusirveCallCount +=recursiveCallsCount; averageRecursiveCallCount = totalRecusirveCallCount/i; } } public void printBoard() { for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { System.out.print(board[j][i] + " "); } System.out.println(); } System.out.println(); } public List allPossibleNextMoves(String whichPlayer) { allPossibleSequences = new ArrayList<>(); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { if (board[j][i].equalsIgnoreCase(whichPlayer)) { String whichPlayerPieceType = board[j][i]; switch (whichPlayerPieceType) { case "xx": if (i+1==7) //no kill into promotion { if (j-1>=0&&j-1<8&&board[j-1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j-1,-1,-1,"OO","xx",i,j,"XX")); if (j+1>=0&&j+1<8&&board[j+1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j+1,-1,-1,"OO","xx",i,j,"XX")); } if(i+1>=0&&i+1<7) //no kill no promotion { if (j-1>=0&&j-1<8&&board[j-1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j-1,-1,-1,"OO","xx",i,j,"xx")); if (j+1>=0&&j+1<8&&board[j+1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j+1,-1,-1,"OO","xx",i,j,"xx")); } if (i+2==7) //kill into promotion { if (j-2>=0&&j-2<8&&j-1>=0&&j-2<8&&board[j-2][i+2].equals("OO")){ switch (board[j-1][i+1]) { case "aa": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"aa","xx",i,j,"XX")); break; case "AA": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"AA","xx",i,j,"XX")); break; }} if (j+2>=0&&j+2<8&&j+1>=0&&j+1<8&&board[j+2][i+2].equals("OO")){ switch (board[j+1][i+1]) { case "aa": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"aa","xx",i,j,"XX")); break; case "AA": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"AA","xx",i,j,"XX")); break; }} } if(i+1>=0&&i+1<=7&&i+2>=0&&i+2<7) //kill into no promo { if ((j-2>=0&&j-2<8)&&j-1>=0&&j-1<8&&board[j-2][i+2].equals("OO")){ switch (board[j-1][i+1]) { case "aa": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"aa","xx",i,j,"xx")); break; case "AA": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"AA","xx",i,j,"xx")); break; }} if ((j+2>=0&&j+2<8)&&j+1>=0&&j+1<8&&board[j+2][i+2].equals("OO")){ switch (board[j+1][i+1]) { case "aa": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"aa","xx",i,j,"xx")); break; case "AA": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"AA","xx",i,j,"xx")); break; }} } break; case "XX": if(i+1>=0&&i+1<8) //no kill moving down { if (j-1>=0&&j-1<8&&board[j-1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j-1,-1,-1,"OO","XX",i,j,"XX")); if (j+1>=0&&j+1<8&&board[j+1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j+1,-1,-1,"OO","XX",i,j,"XX")); } if(i+1>=0&&i+1<8&&i+2>=0&&i+2<8) //kill going down { if (j-1>=0&&j-1<8&&j-2<8&&j-2>=0&&board[j-2][i+2].equals("OO")){ switch (board[j-1][i+1]) { case "aa": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"aa","XX",i,j,"XX")); break; case "AA": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"AA","XX",i,j,"XX")); break; }} if (j+1>=0&&j+1<8&&j+2>=0&&j+2<8&&board[j+2][i+2].equals("OO")){ switch (board[j+1][i+1]) { case "aa": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"aa","XX",i,j,"XX")); break; case "AA": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"AA","XX",i,j,"XX")); break; }} } if(i-1>=0&&i-1<8) //no kill moving up { if (j-1>=0&&j-1<8&&board[j-1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j-1,-1,-1,"OO","XX",i,j,"XX")); if (j+1>=0&&j+1<8&&board[j+1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j+1,-1,-1,"OO","XX",i,j,"XX")); } if(i-1>=0&&i-1<8&&i-2>=0&&i-2<8) //kill going up { if (j-1>=0&&j-1<8&&j-2>=0&&j-2<8&&board[j-2][i-2].equals("OO")){ switch (board[j-1][i-1]) { case "aa": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"aa","XX",i,j,"XX")); break; case "AA": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"AA","XX",i,j,"XX")); break; }} if (j+1>=0&&j+1<8&&j+2>=0&&j+2<8&&board[j+2][i-2].equals("OO")){ switch (board[j+1][i-1]) { case "aa": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"aa","XX",i,j,"XX")); break; case "AA": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"AA","XX",i,j,"XX")); break; }} } break; case "aa": if (i-1==0) //no kill into promo going up { if (j-1>=0&&j-1<8&&board[j-1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j-1,-1,-1,"OO","aa",i,j,"AA")); if (j+1>=0&&j+1<8&&board[j+1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j+1,-1,-1,"OO","aa",i,j,"AA")); } if(i-1>0&&i-1<8) //no kill no promo going up { if (j-1>=0&&j-1<8&&board[j-1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j-1,-1,-1,"OO","aa",i,j,"aa")); if (j+1>=0&&j+1<8&&board[j+1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j+1,-1,-1,"OO","aa",i,j,"aa")); } if(i-2==0) //kill into promo going up { if (j-1>=0&&j-1<8&&j-2>=0&&j-2<8&&board[j-2][i-2].equals("OO")){ switch (board[j-1][i-1]) { case "xx": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"xx","aa",i,j,"AA")); break; case "XX": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"XX","aa",i,j,"AA")); break; }} if (j+1>=0&&j+1<8&&j+2>=0&&j+2<8&&board[j+2][i-2].equals("OO")){ switch (board[j+1][i-1]) { case "xx": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"xx","aa",i,j,"AA")); break; case "XX": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"XX","aa",i,j,"AA")); break; }} } if(i-1>=0&&i-1<8&&i-2>0&&i-2<8) //kill no promo up { if (j-1>=0&&j-1<8&&j-2>=0&&j-2<8&&board[j-2][i-2].equals("OO")){ switch (board[j-1][i-1]) { case "xx": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"xx","aa",i,j,"aa")); break; case "XX": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"XX","aa",i,j,"aa")); break; }} if (j+1>=0&&j+1<8&&j+2>=0&&j+2<8&&board[j+2][i-2].equals("OO")) { switch (board[j+1][i-1]) { case "xx": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"xx","aa",i,j,"aa")); break; case "XX": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"XX","aa",i,j,"aa")); break; }} } break; case "AA": if(i-1>=0&&i-1<8) //no kill no promo up { if (j-1>=0&&j-1<8&&board[j-1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j-1,-1,-1,"OO","AA",i,j,"AA")); if (j+1>=0&&j+1<8&&board[j+1][i-1].equals("OO")) allPossibleSequences.add(new Position(i-1,j+1,-1,-1,"OO","AA",i,j,"AA")); } if(i-1>=0&&i-1<8&&i-2>=0&&i-2<8) //kill no promo up { if (j-1>=0&&j-1<8&&j-2>=0&&j-2<8&&board[j-2][i-2].equals("OO")){ switch (board[j-1][i-1]) { case "xx": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"xx","AA",i,j,"AA")); break; case "XX": allPossibleSequences.add(new Position(i-2,j-2,i-1,j-1,"XX","AA",i,j,"AA")); break; } } if (j+1>=0&&j+1<8&&j+2>=0&&j+2<8&&board[j+2][i-2].equals("OO")){ switch (board[j+1][i-1]) { case "xx": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"xx","AA",i,j,"AA")); break; case "XX": allPossibleSequences.add(new Position(i-2,j+2,i-1,j+1,"XX","AA",i,j,"AA")); break; }} } if(i+1>=0&&i+1<8) //no kill no promo down { if (j-1>=0&&j-1<8&&board[j-1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j-1,-1,-1,"OO","AA",i,j,"AA")); if (j+1>=0&&j+1<8&&board[j+1][i+1].equals("OO")) allPossibleSequences.add(new Position(i+1,j+1,-1,-1,"OO","AA",i,j,"AA")); } if(i+1>=0&&i+1<8&&i+2>=0&&i+2<8) //kill no promo down { if (j-1>=0&&j-1<8&&j-2>=0&&j-2<8&&board[j-2][i+2].equals("OO")) { switch (board[j-1][i+1]) { case "xx": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"xx","AA",i,j,"AA")); break; case "XX": allPossibleSequences.add(new Position(i+2,j-2,i+1,j-1,"XX","AA",i,j,"AA")); break; } } if (j+1>=0&&j+1<8&&j+2>=0&&j+2<8&&board[j+2][i+2].equals("OO")) { switch (board[j+1][i+1]) { case "xx": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"xx","AA",i,j,"AA")); break; case "XX": allPossibleSequences.add(new Position(i+2,j+2,i+1,j+1,"XX","AA",i,j,"AA")); break; }} } break; } } } } return allPossibleSequences; } class Position { int x; int y; int killedX; int killedY; int originalPositionX; int originalPositionY; String killedUnit; String unitBeingMoved; String unitPromotedTo; public Position(int x, int y, int killedX, int killedY, String killedUnit, String unitBeingMoved, int originalPositionX, int originalPositionY, String unitPromotedTo) { this.x = x; this.y = y; this.killedX = killedX; this.killedY = killedY; this.killedUnit = killedUnit; this.unitBeingMoved = unitBeingMoved; this.originalPositionX = originalPositionX; this.originalPositionY = originalPositionY; this.unitPromotedTo = unitPromotedTo; } } public int returnScoreAI(String ai) { int pawnScore = 0; int kingScore = 0; int vertScore = 0; int horiScore = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { if (ai.equals("xx")||ai.equals("XX")) { if (board[j][i].equals("xx"))pawnScore+=50; else if (board[j][i].equals("XX")) kingScore+=150; else if (board[j][i].equals("aa"))pawnScore-=50; else if (board[j][i].equals("AA")) kingScore-=150; /**if (i==7) vertScore = 30; else if (i==6) vertScore = 23; else if (i==5) vertScore = 11; else if (i==4) vertScore = 5; else if (i==3) vertScore = 5; else if (i==2) vertScore = 2; else if (i==1) vertScore = 1; else if (i==0) vertScore = 15; if (j==0||j==7) horiScore = 30; else if (j==1||j==6) horiScore = 3; else if (j==2||j==5) horiScore = 1; **/ } else if (ai.equals("aa")||ai.equals("AA")) { if (board[j][i].equals("xx"))pawnScore-=50; else if (board[j][i].equals("XX")) kingScore-=150; else if (board[j][i].equals("aa"))pawnScore+=50; else if (board[j][i].equals("AA")) kingScore+=150; if (i==7) vertScore = 7; else if (i==6) vertScore = 35; else if (i==5) vertScore = 11; else if (i==4) vertScore = 5; else if (i==3) vertScore = 5; else if (i==2) vertScore = 2; else if (i==1) vertScore = 1; else if (i==0) vertScore = 35; if (j==0||j==7) horiScore = 50; else if (j==1||j==6) horiScore = 6; else if (j==2||j==5) horiScore = 1; } } } return pawnScore + kingScore+vertScore+horiScore; } public boolean eitherSideEmpy() { int xxXX = 0; int aaAA = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { if (board[i][j].equalsIgnoreCase("xx"))xxXX++; else if (board[i][j].equalsIgnoreCase("aa")) aaAA++; } } return xxXX == 0 || aaAA == 0; } public int minMax(int depth, boolean isItAiMove,String ai, String opposingPlayer) { int min = 111111111; int max = -1111111111; recursiveCallsCount++; if (depth == 3||eitherSideEmpy()){ int heuristicScore = returnScoreAI(ai); return heuristicScore; } List PositionsAvailable; if (isItAiMove)PositionsAvailable = allPossibleNextMoves(ai); else PositionsAvailable = allPossibleNextMoves(opposingPlayer); if (depth==0)bestMoveForAI = PositionsAvailable.get(0); if (isItAiMove) { for (Position result:PositionsAvailable){ board[result.y][result.x] = result.unitPromotedTo; //moves unit to next position board[result.originalPositionY][result.originalPositionX]="OO"; //sets unit's original position to 0. if (result.killedY>=0&&result.killedX>=0) //removes killed unit board[result.killedY][result.killedX]="OO"; int heuristic = minMax(depth + 1, false, ai, opposingPlayer); if (heuristic > max) {max = heuristic; if(depth == 0) bestMoveForAI = result;} board[result.y][result.x] = "OO"; board[result.originalPositionY][result.originalPositionX]=result.unitBeingMoved; if (result.killedY>=0||result.killedX>=0) board[result.killedY][result.killedX]=result.killedUnit; } } else if (!isItAiMove) { for (Position result:PositionsAvailable) { board[result.y][result.x] = result.unitPromotedTo; //moves unit to next position board[result.originalPositionY][result.originalPositionX]="OO"; //sets unit's original position to 0. if (result.killedY>=0&&result.killedX>=0) //removes killed unit board[result.killedY][result.killedX]="OO"; int c = minMax(depth + 1, true, ai, opposingPlayer); if (c < min) min = c; board[result.y][result.x] = "OO"; board[result.originalPositionY][result.originalPositionX]=result.unitBeingMoved; if (result.killedY>=0||result.killedX>=0) board[result.killedY][result.killedX]=result.killedUnit; } } int returnValue; if (isItAiMove) returnValue = max; else returnValue = min; return returnValue; } public int minMaxAlphaBeta(int depth, boolean isItAiMove, int alpha, int beta, String ai, String opposingPlayer) { recursiveCallsCount++; List PositionsAvailable; if (depth ==8){ int heuristicScore = returnScoreAI(ai); return heuristicScore; } if (isItAiMove) PositionsAvailable = allPossibleNextMoves(ai); else PositionsAvailable = allPossibleNextMoves(opposingPlayer); if (isItAiMove) { for (Position result:PositionsAvailable){ board[result.y][result.x] = result.unitPromotedTo; //moves unit to next position board[result.originalPositionY][result.originalPositionX]="OO"; //sets unit's original position to 0. if (result.killedY>=0&&result.killedX>=0) //removes killed unit board[result.killedY][result.killedX]="OO"; int heuristic = minMaxAlphaBeta(depth + 1, false, alpha, beta, ai, opposingPlayer); if (heuristic > alpha) {alpha = heuristic;if(depth ==0)bestMoveForAI = result;} if(alpha >= beta){ board[result.y][result.x] = "OO"; board[result.originalPositionY][result.originalPositionX]=result.unitBeingMoved; if (result.killedY>=0&&result.killedX>=0) board[result.killedY][result.killedX]=result.killedUnit; break; } board[result.y][result.x] = "OO"; board[result.originalPositionY][result.originalPositionX]=result.unitBeingMoved; if (result.killedY>=0||result.killedX>=0) board[result.killedY][result.killedX]=result.killedUnit; } } else if (!isItAiMove) { for (Position result:PositionsAvailable) { board[result.y][result.x] = result.unitPromotedTo; board[result.originalPositionY][result.originalPositionX]="OO"; //sets unit's original position to 0. if (result.killedY>=0||result.killedX>=0) board[result.killedY][result.killedX]="OO"; int heuristic = minMaxAlphaBeta(depth + 1, true, alpha, beta, ai, opposingPlayer); if (heuristic < beta) beta = heuristic; if(alpha>=beta){ board[result.y][result.x] = "OO"; board[result.originalPositionY][result.originalPositionX]=result.unitBeingMoved; if (result.killedY>=0||result.killedX>=0) board[result.killedY][result.killedX]=result.killedUnit; break; } board[result.y][result.x] = "OO"; board[result.originalPositionY][result.originalPositionX]=result.unitBeingMoved; if (result.killedY>=0||result.killedX>=0) board[result.killedY][result.killedX]=result.killedUnit; } } int returnValue = 0; if (isItAiMove) returnValue = alpha; else returnValue = beta; return returnValue; } }