From b02755be4e645f88a1a1cd1c84a56e6ad3de7776 Mon Sep 17 00:00:00 2001 From: djib Date: Sun, 2 Jul 2006 13:51:14 +0000 Subject: [PATCH] Version 7.0 =========== Corrected a few bugs from 6.9, like the principal variation. About to add opening book. --- src/suicideChess/Board.java | 29 +++++-- src/suicideChess/ComputerPlayer.java | 90 ++++++++++++++-------- src/suicideChess/Piece.java | 24 +++--- src/suicideChess/Rules.java | 4 +- src/suicideChess/SuicideChess.java | 108 ++++++++++++++++++++------- src/suicideChess/XBoardProtocol.java | 8 +- 6 files changed, 183 insertions(+), 80 deletions(-) diff --git a/src/suicideChess/Board.java b/src/suicideChess/Board.java index 728225a..9fcfabb 100644 --- a/src/suicideChess/Board.java +++ b/src/suicideChess/Board.java @@ -70,6 +70,15 @@ public class Board { * Value representing the value of a draw position */ public static final int DRAW_BOARD = 0; + + /** + * Importance of real mobility in position evaluation (ie. how many moves can one make compared to the other) + */ + public static final int REAL_MOBILITY_VALUE = 10; + /** + * Importance of relative mobility (mobility of other pieces that may not be able to play because of a compulsory move) + */ + public static final int RELATIVE_MOBILITY_VALUE = 5; /*======* * DATA * @@ -314,13 +323,9 @@ public class Board { if(currentPlayer==Piece.WHITE) { currentPlayer=Piece.BLACK; - //if (SuicideChess.ASCII_GAME) - // System.out.println("Black: "); } else { fullmoveNumber++; //black has just played currentPlayer=Piece.WHITE; - //if (SuicideChess.ASCII_GAME) - // System.out.println("White: "); } evaluateNewBoardValue(move); @@ -581,6 +586,19 @@ public class Board { } } + //calculates player's mobility + public int mobility(int colour) throws NotAValidSquare { + Board thisCopy = new Board(this); + thisCopy.currentPlayer=colour; + Rules.legalMovesForPlayer(thisCopy); + if (Rules.getLegalMovesCapture().size()!=0) { + return Rules.getLegalMovesCapture().size()*REAL_MOBILITY_VALUE + +Rules.getLegalMovesNonCapture().size()*RELATIVE_MOBILITY_VALUE; + } else { + return Rules.getLegalMovesNonCapture().size()*REAL_MOBILITY_VALUE; + } + } + private void evaluateNewBoardValue (Move move) throws NotAValidSquare { if (move.isCaptureMove()) { @@ -602,7 +620,8 @@ public class Board { //System.out.println("Playing midgame"); for (int i = Piece.OFFSET; i<=Piece.MAX_PIECE_NUMBER; i++) { boardValue += numberOfPieces[i]*Piece.PIECE_VALUE_MIDDLEGAME[i]; - } + } + boardValue += ((mobility(Piece.WHITE)-mobility(Piece.BLACK))); } } } diff --git a/src/suicideChess/ComputerPlayer.java b/src/suicideChess/ComputerPlayer.java index 0df511c..ca91ab3 100644 --- a/src/suicideChess/ComputerPlayer.java +++ b/src/suicideChess/ComputerPlayer.java @@ -60,7 +60,7 @@ public class ComputerPlayer { int bestScore = MinMax(bitboard, 0); if (SuicideChess.postThinkingOutput()) { - System.out.println(SuicideChess.PLY_DEPTH+" "+bestScore*100+" 0 "+nodesSearched+" "+bestMove); + System.out.println(SuicideChess.getPlyDepth()+" "+bestScore*100+" 0 "+nodesSearched+" "+bestMove); } return bestMove; } @@ -69,7 +69,7 @@ public class ComputerPlayer { private static int MinMax(Board bitboard, int currentDepth) throws NotAValidSquare, NoPieceOnSquare { nodesSearched++; - if (currentDepth >= SuicideChess.PLY_DEPTH) { + if (currentDepth >= SuicideChess.getPlyDepth()) { return bitboard.getBoardValue(); } @@ -140,50 +140,63 @@ public class ComputerPlayer { bestMove = null; nodesSearched = 0; - Date thinkingBeginingTime = new Date(); - ReturnWrapper bestScore = AlphaBeta(bitboard, 0, Board.BLACK_WINS-1, Board.WHITE_WINS+1); - Date thinkingEndTime = new Date(); + thinkingBeginingTime = new Date(); + + //iterative deepening + for(maxDepth=2; maxDepth<=SuicideChess.getPlyDepth(); maxDepth++) { + ReturnWrapper bestScore = AlphaBeta(bitboard, 0, Board.MIN_VALUE, Board.MAX_VALUE); + Date thinkingEndTime = new Date(); + + //select one of the best moves randomly + Random generator = new Random(); + //if (currentDepth == 0) { System.out.println(bestMoveIndex.size()); } + bestMove = bestMoves.get(generator.nextInt(bestMoves.size())); + + if (SuicideChess.postThinkingOutput()) { + System.out.println(maxDepth+"\t"+bestScore.getBranchValue()*100+ + "\t"+((int)(thinkingEndTime.getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds + "\t"+nodesSearched+"\t"+bestScore.getPrincipalVariation()); + } - //select one of the best moves randomly - Random generator = new Random(); - //if (currentDepth == 0) { System.out.println(bestMoveIndex.size()); } - bestMove = bestMoves.get(generator.nextInt(bestMoves.size())); + } + if(SuicideChess.playInACSII()) { + System.out.println("Found "+bestMoves.size()+" good moves."); + } - System.out.println("Found "+bestMoves.size()+" good moves."); - - if (SuicideChess.postThinkingOutput()) { - System.out.println(SuicideChess.PLY_DEPTH+" "+bestScore.getAlphaBeta()*100+ - " "+((int)(thinkingEndTime.getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds - " "+nodesSearched+" "+bestMove); - } + System.out.println(((bitboard.mobility(Piece.WHITE)))); + System.out.println(((-bitboard.mobility(Piece.BLACK)))); return bestMove; } - - private static ArrayList bestMoves=new ArrayList(); + private static Date thinkingBeginingTime; + private static int maxDepth; + private static ArrayList bestMoves=new ArrayList(); //this class is used to return two arguments in the next function, the two arguments being //an integer representing the value of alpha or beta //an integer representing the real value of the branch (alpha-beta only give boundaries) private static class ReturnWrapper { private int alphaBeta; private int branchValue; - public ReturnWrapper(int alphaBeta, int branchValue) { + private String principalVariation; + public ReturnWrapper(int alphaBeta, int branchValue, String principalVariation) { this.alphaBeta = alphaBeta; this.branchValue = branchValue; + this.principalVariation = principalVariation; } public int getAlphaBeta() {return this.alphaBeta;} public int getBranchValue() {return this.branchValue;} + public String getPrincipalVariation() {return this.principalVariation;} }; private static ReturnWrapper AlphaBeta(Board bitboard, int currentDepth, int alpha, int beta) throws NotAValidSquare, NoPieceOnSquare { nodesSearched++; if(bitboard.isADraw()) { - return new ReturnWrapper(Board.DRAW_BOARD,Board.DRAW_BOARD); + return new ReturnWrapper(Board.DRAW_BOARD,Board.DRAW_BOARD,""); } - if (currentDepth >= SuicideChess.PLY_DEPTH) { + if (currentDepth >= maxDepth) { //System.out.println("'-> Evaluate: "+bitboard.getBoardValue()); - return new ReturnWrapper(bitboard.getBoardValue(),bitboard.getBoardValue()); + return new ReturnWrapper(bitboard.getBoardValue(),bitboard.getBoardValue(),""); } Rules.legalMovesForPlayer(bitboard); @@ -194,15 +207,16 @@ public class ComputerPlayer { if (allLegalMoves.size()==0) { if (bitboard.getCurrentPlayer()==Piece.BLACK) { //System.out.println("'-> Evaluate *BLACK WINS*: "+Board.BLACK_WINS); - return new ReturnWrapper(Board.BLACK_WINS,Board.BLACK_WINS); + return new ReturnWrapper(Board.BLACK_WINS,Board.BLACK_WINS,""); } else { //System.out.println("'-> Evaluate *WHITE WINS* : "+Board.WHITE_WINS); - return new ReturnWrapper(Board.WHITE_WINS,Board.WHITE_WINS); + return new ReturnWrapper(Board.WHITE_WINS,Board.WHITE_WINS,""); } } else { int currentScore; int bestScoreSoFar; int currentAlphaBeta; + String bestVariationSoFar=""; if (bitboard.getCurrentPlayer()==Piece.BLACK) { bestScoreSoFar=Board.MAX_VALUE; //black tries to minimise for (int i=0; i= bestScoreSoFar) { if (currentScore > bestScoreSoFar) { + bestVariationSoFar = allLegalMoves.get(i).toString()+" "+returnValue.getPrincipalVariation(); bestScoreSoFar=currentScore; if (currentDepth==0) { bestMoves.clear(); + if (SuicideChess.postThinkingOutput()) { + System.out.println(maxDepth+"\t"+returnValue.getBranchValue()*100+ + "\t"+((int)((new Date()).getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds + "\t"+nodesSearched+"\t"+bestVariationSoFar); + } //System.out.println("*** Clear "); } } @@ -278,11 +304,11 @@ public class ComputerPlayer { } if(alpha>beta) { - //if(currentDepth!=SuicideChess.PLY_DEPTH-1) System.out.println("Pruning "+Integer.toString(allLegalMoves.size()-i)+" alternatives at depth "+ currentDepth); - return new ReturnWrapper(beta,bestScoreSoFar); //pruning + //if(currentDepth!=SuicideChess.getPlyDepth()-1) System.out.println("Pruning "+Integer.toString(allLegalMoves.size()-i)+" alternatives at depth "+ currentDepth); + return new ReturnWrapper(beta,bestScoreSoFar,bestVariationSoFar); //pruning } } - return new ReturnWrapper(alpha,bestScoreSoFar); + return new ReturnWrapper(alpha,bestScoreSoFar,bestVariationSoFar); } } } diff --git a/src/suicideChess/Piece.java b/src/suicideChess/Piece.java index c384b6e..ec41c8e 100644 --- a/src/suicideChess/Piece.java +++ b/src/suicideChess/Piece.java @@ -77,24 +77,24 @@ public class Piece { public static final int[] PIECE_VALUE_MIDDLEGAME = new int[Piece.MAX_PIECE_NUMBER+1]; public static final int[] PIECE_VALUE_ENDGAME = new int[Piece.MAX_PIECE_NUMBER+1]; static { - PIECE_VALUE_MIDDLEGAME[WHITE_KING]=50; - PIECE_VALUE_MIDDLEGAME[WHITE_QUEEN]=90; - PIECE_VALUE_MIDDLEGAME[WHITE_ROOK]=35; - PIECE_VALUE_MIDDLEGAME[WHITE_KNIGHT]=30; - PIECE_VALUE_MIDDLEGAME[WHITE_BISHOP]=-10; - PIECE_VALUE_MIDDLEGAME[WHITE_PAWN]=10; + PIECE_VALUE_MIDDLEGAME[WHITE_KING]=500; + PIECE_VALUE_MIDDLEGAME[WHITE_QUEEN]=900; + PIECE_VALUE_MIDDLEGAME[WHITE_ROOK]=350; + PIECE_VALUE_MIDDLEGAME[WHITE_KNIGHT]=300; + PIECE_VALUE_MIDDLEGAME[WHITE_BISHOP]=-400; //started with -100 + PIECE_VALUE_MIDDLEGAME[WHITE_PAWN]=300; //started with 100 PIECE_VALUE_MIDDLEGAME[BLACK_KING]=-PIECE_VALUE_MIDDLEGAME[WHITE_KING]; PIECE_VALUE_MIDDLEGAME[BLACK_QUEEN]=-PIECE_VALUE_MIDDLEGAME[WHITE_QUEEN]; PIECE_VALUE_MIDDLEGAME[BLACK_ROOK]=-PIECE_VALUE_MIDDLEGAME[WHITE_ROOK]; PIECE_VALUE_MIDDLEGAME[BLACK_KNIGHT]=-PIECE_VALUE_MIDDLEGAME[WHITE_KNIGHT]; PIECE_VALUE_MIDDLEGAME[BLACK_BISHOP]=-PIECE_VALUE_MIDDLEGAME[WHITE_BISHOP]; PIECE_VALUE_MIDDLEGAME[BLACK_PAWN]=-PIECE_VALUE_MIDDLEGAME[WHITE_PAWN]; - PIECE_VALUE_ENDGAME[WHITE_KING]=-40; - PIECE_VALUE_ENDGAME[WHITE_QUEEN]=-40; - PIECE_VALUE_ENDGAME[WHITE_ROOK]=-10; - PIECE_VALUE_ENDGAME[WHITE_KNIGHT]=-70; - PIECE_VALUE_ENDGAME[WHITE_BISHOP]=-40; - PIECE_VALUE_ENDGAME[WHITE_PAWN]=-90; + PIECE_VALUE_ENDGAME[WHITE_KING]=-400; + PIECE_VALUE_ENDGAME[WHITE_QUEEN]=-400; + PIECE_VALUE_ENDGAME[WHITE_ROOK]=-100; + PIECE_VALUE_ENDGAME[WHITE_KNIGHT]=-700; + PIECE_VALUE_ENDGAME[WHITE_BISHOP]=-400; + PIECE_VALUE_ENDGAME[WHITE_PAWN]=-900; PIECE_VALUE_ENDGAME[BLACK_KING]=-PIECE_VALUE_ENDGAME[WHITE_KING]; PIECE_VALUE_ENDGAME[BLACK_QUEEN]=-PIECE_VALUE_ENDGAME[WHITE_QUEEN]; PIECE_VALUE_ENDGAME[BLACK_ROOK]=-PIECE_VALUE_ENDGAME[WHITE_ROOK]; diff --git a/src/suicideChess/Rules.java b/src/suicideChess/Rules.java index 4cd56dc..c9d5d91 100644 --- a/src/suicideChess/Rules.java +++ b/src/suicideChess/Rules.java @@ -57,8 +57,8 @@ public class Rules { } legalMovesFromSquare(square,board); } - } - + } + /** * Looks for one legal move according to the current status of the {@link Board} and the * color of the current player. diff --git a/src/suicideChess/SuicideChess.java b/src/suicideChess/SuicideChess.java index 515f86f..c7d3096 100644 --- a/src/suicideChess/SuicideChess.java +++ b/src/suicideChess/SuicideChess.java @@ -36,17 +36,36 @@ public class SuicideChess { /** * The name to be displayed */ - public static final String NAME = "djib's SuShi v0.6.9"; + public static final String NAME = "djib's SuShi v0.7"; /** * Displays informations in the console. */ - public static final boolean ASCII_GAME = true; + private static boolean asciiGame = false; + + /** + * Should the game be played in acsii + * @return boolean + */ + public static boolean playInACSII() { + return asciiGame; + } /** * Number of Plies the computes searches to */ - public static final int PLY_DEPTH = 4; + private static int plyDepth = 4; + + /** + * What is the current ply depth + * @return integer + */ + public static int getPlyDepth() { return plyDepth; } + + /** + * Maximum number of Plies the computer will ever go to + */ + public static final int MAX_PLY_DEPTH = 8; /** * Test and display if the board is in a winning state. @@ -108,6 +127,15 @@ public class SuicideChess { public static boolean postThinkingOutput() { return post; } + + private static void displayPlayer(Board bitboard) { + if(bitboard.getCurrentPlayer()==Piece.BLACK) { + System.out.println("Black: "); + } else { + System.out.println("White: "); + } + + } /** * The main function * @param args No parameters should be transmitted to this function. @@ -116,10 +144,9 @@ public class SuicideChess { public static void main(String[] args) throws NotAValidSquare { - System.out.println("Welcome to SuicideChess "+SuicideChess.NAME+"!"); - if (!SuicideChess.ASCII_GAME) { - System.out.println("This game was not designed to be played in console. Please use it with XBoard, WinBoard or any compatible program."); - } + System.out.println("Welcome to SuicideChess "+SuicideChess.NAME+"!\n"); + System.out.println("Type 'asciiplay' to be able to play in a terminal."); + System.out.println("If you want a graphical interface, you can use XBoard, WinBoard or any compatible program."); System.out.println(); @@ -127,11 +154,6 @@ public class SuicideChess { Board bitboard = new Board(); addPlayedPosition(bitboard); - if (ASCII_GAME) { - bitboard.display(); - System.out.println("White: "); - } - boolean computerPlaying = true; //the computer does not play in foce mode. boolean playing = true; @@ -152,12 +174,20 @@ public class SuicideChess { try { int problemNb = Integer.parseInt(whatMove.substring(8)); bitboard=new Board(SuicideProblems.getProblemNumber(problemNb)); - if(ASCII_GAME) + if(asciiGame) bitboard.display(); } catch (NumberFormatException e) { System.out.println("Not a valid number: "+ whatMove.substring(8)); } } + } else if (whatMove.startsWith("asciiplay")) { + asciiGame=true; + bitboard.display(); + displayPlayer(bitboard); + } else if (whatMove.startsWith("asciiplay")) { + asciiGame=false; + } else if ((whatMove.startsWith("board"))) { + bitboard.display(); } else { int xBoardCommand = XBoardProtocol.getCommand(whatMove); @@ -183,6 +213,10 @@ public class SuicideChess { playing=false; break; case XBoardProtocol.NEW: + bitboard=new Board(); + addPlayedPosition(bitboard); + computerPlaying = true; //the computer does not play in foce mode. + bitboard.display(); //System.out.println("variant suicide"); break; case XBoardProtocol.HINT: @@ -199,7 +233,7 @@ public class SuicideChess { //no break here case XBoardProtocol.UNDO: bitboard=new Board(removePlayedPosition()); - if (ASCII_GAME) { + if (asciiGame) { bitboard.display(); } break; @@ -211,9 +245,16 @@ public class SuicideChess { break; case XBoardProtocol.SETBOARD: bitboard=new Board(whatMove.substring(9)); - if(ASCII_GAME) + if(asciiGame) bitboard.display(); break; + case XBoardProtocol.SETPLY: + try{ + plyDepth = Integer.parseInt(whatMove.substring(3)); + } catch (NumberFormatException e) { + System.out.println("Not a valid depth: "+ whatMove.substring(3)); + } + break; case XBoardProtocol.UNKNOWN: if (acceptedUsermove) { System.out.println("Error (unknown command): "+whatMove); @@ -258,22 +299,22 @@ public class SuicideChess { } if (foundMoveIndex == -1) { if (needToCapture) { - if (ASCII_GAME) + if (asciiGame) System.out.println("Capturing is mandatory."); } System.out.println("Illegal move: "+theMove.toString()); } else { bitboard.doMove(allLegalMoves.get(foundMoveIndex)); addPlayedPosition(bitboard); - if (ASCII_GAME) { - allLegalMoves.get(foundMoveIndex).display(); - System.out.println("Board value: "+bitboard.getBoardValue()); + if (asciiGame) { + //allLegalMoves.get(foundMoveIndex).display(); + //System.out.println("Board value: "+bitboard.getBoardValue()); bitboard.display(); } playedALegalMove=true; } } else { - if (ASCII_GAME) + if (asciiGame) System.out.println("Please play a piece of the right color."); System.out.println("Illegal move: "+theMove.toString()); } @@ -304,13 +345,14 @@ public class SuicideChess { bitboard.doMove(computerMove); addPlayedPosition(bitboard); XBoardProtocol.doMove(computerMove); - if (ASCII_GAME) { - computerMove.display(); - System.out.println("Board value: "+bitboard.getBoardValue()); + if (asciiGame) { + //computerMove.display(); + //System.out.println("Board value: "+bitboard.getBoardValue()); bitboard.display(); + displayPlayer(bitboard); } } - + if (testAndDisplayIfWinningOrDrawPosition(bitboard)) { computerPlaying=false; } @@ -370,7 +412,7 @@ public class SuicideChess { //in the end it says what games where lost by white. private static void autoProblem() throws NotAValidSquare, UnableToParseFENStringException, NoPieceOnSquare, NoSuchSuicideProblem { Board bitboard; - boolean[] result=new boolean[SuicideProblems.numberOfProblems()]; + int[] result=new int[SuicideProblems.numberOfProblems()]; for(int i=1; i<=SuicideProblems.numberOfProblems(); i++) { System.out.println("\n\nProblem "+i); bitboard=new Board(SuicideProblems.getProblemNumber(i)); @@ -379,16 +421,26 @@ public class SuicideChess { bitboard.doMove(computerMove); } if (bitboard.getCurrentPlayer()==Piece.BLACK) { - result[i-1]=false; + result[i-1]=Piece.BLACK; + } else if(bitboard.isADraw()){ + result[i-1]=Piece.NONE; } else { - result[i-1]=true; + result[i-1]=Piece.WHITE; } + } System.out.println("\n\nLost following games:\n========begin========"); + int stats=SuicideProblems.numberOfProblems(); for(int i=1; i<=SuicideProblems.numberOfProblems(); i++) { - if (!result[i-1]) + if (result[i-1]==Piece.BLACK) { System.out.println("Problem "+i+" lost !"); + stats--; + } else if (result[i-1]==Piece.NONE) { + System.out.println("Problem "+i+" drawn !"); + stats--; + } } + System.out.println("---------------------\n"+stats+" problems solved out of "+SuicideProblems.numberOfProblems()); System.out.println("=========end========="); } } \ No newline at end of file diff --git a/src/suicideChess/XBoardProtocol.java b/src/suicideChess/XBoardProtocol.java index 8c05cfc..85cbb9f 100644 --- a/src/suicideChess/XBoardProtocol.java +++ b/src/suicideChess/XBoardProtocol.java @@ -86,6 +86,10 @@ public class XBoardProtocol { * XBoard changes the board position */ public static final int SETBOARD = 17; + /** + * XBoard send a signal to limit ply depth + */ + public static final int SETPLY = 18; /** * Unknown command */ @@ -105,7 +109,7 @@ public class XBoardProtocol { System.out.println("feature ping=1 setboard=1"); System.out.println("feature time=0 draw=0 reuse=0 analyze=0 colors=0"); System.out.println("feature done=1"); - if (SuicideChess.ASCII_GAME) + if (SuicideChess.playInACSII()) System.out.println(); } @@ -160,6 +164,8 @@ public class XBoardProtocol { return NOPOST; } else if (command.startsWith("setboard")) { return SETBOARD; + } else if (command.startsWith("sd")) { + return SETPLY; } return UNKNOWN;