From 8854c787f902106029b12fe11d6ae34ee6101ac3 Mon Sep 17 00:00:00 2001 From: djib Date: Tue, 4 Jul 2006 22:13:21 +0000 Subject: [PATCH] VERSION 1.0.0 ============= !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --- src/suicideChess/ComputerPlayer.java | 139 ++++++++++++++++++++++----- src/suicideChess/Move.java | 2 +- src/suicideChess/SuicideChess.java | 74 +++++++++----- 3 files changed, 162 insertions(+), 53 deletions(-) diff --git a/src/suicideChess/ComputerPlayer.java b/src/suicideChess/ComputerPlayer.java index 71f398f..0041ebf 100644 --- a/src/suicideChess/ComputerPlayer.java +++ b/src/suicideChess/ComputerPlayer.java @@ -6,6 +6,7 @@ import java.util.Date; import java.util.Random; import suicideChess.Board.NoPieceOnSquare; +import suicideChess.Move.NotAValidMoveException; import suicideChess.Square.NotAValidSquare; /** @@ -134,21 +135,33 @@ public class ComputerPlayer { * @return The best Move found * @throws NotAValidSquare * @throws NoPieceOnSquare + * @throws NotAValidMoveException * @see Board * @see Move */ - public static Move doAlphaBetaMove(Board bitboard) throws NotAValidSquare, NoPieceOnSquare { + public static Move doAlphaBetaMove(Board bitboard) throws NotAValidSquare, NoPieceOnSquare, NotAValidMoveException { bestMove = null; nodesSearched = 0; thinkingBeginingTime = new Date(); quiescenceSearch = false; + extraSymbol = ""; + principalVariation=new Move[0]; + //iterative deepening - for(maxDepth=2; maxDepth<=SuicideChess.getPlyDepth(); maxDepth++) { - if(SuicideChess.QUIESCENCE_SEARCH && maxDepth==SuicideChess.getPlyDepth()) { - quiescenceSearch = true; //don't do quiescence search till the last level iterative deepening + for(maxDepth=SuicideChess.MIN_PLY_DEPTH; maxDepth<=SuicideChess.getPlyDepth(); maxDepth++) { + if(maxDepth==SuicideChess.getPlyDepth()) { + if(SuicideChess.QUIESCENCE_SEARCH) { + quiescenceSearch = true; //don't do quiescence search till the last level of iterative deepening + extraSymbol = "+"; + } + if(SuicideChess.ADAPTATIVE_DEPTH) { + adaptativeDepth = true; //don't do adaptative search till the last level of iterative deepening + extraSymbol = "+"; + } } - ReturnWrapper bestScore = AlphaBeta(bitboard, 0, maxDepth, Board.MIN_VALUE, Board.MAX_VALUE); + ReturnWrapper bestScore = AlphaBeta(bitboard, 0, SuicideChess.PRINCIPAL_VARIATION_FIRST + , maxDepth, Board.MIN_VALUE, Board.MAX_VALUE); Date thinkingEndTime = new Date(); //select one of the best moves randomly @@ -157,7 +170,8 @@ public class ComputerPlayer { bestMove = bestMoves.get(generator.nextInt(bestMoves.size())); if (SuicideChess.postThinkingOutput()) { - System.out.println(maxDepth+"\t"+bestScore.getBranchValue()+ + + System.out.println(maxDepth+extraSymbol+"\t"+bestScore.getBranchValue()+ "\t"+((int)(thinkingEndTime.getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds "\t"+nodesSearched+"\t"+bestScore.getPrincipalVariation()); } @@ -168,6 +182,17 @@ public class ComputerPlayer { && bestScore.getBranchValue()==Board.WHITE_WINS)) { break; //no need to continue iterative deepening. } + + if(SuicideChess.PRINCIPAL_VARIATION_FIRST) { + String principalVariationString[] = bestScore.getPrincipalVariation().split("\\s"); + principalVariation = new Move[principalVariationString.length]; + Board playing; //need to update bitboard to be able to generate moves + playing = new Board(bitboard); + for (int i = 0; i < principalVariationString.length; i++) { + principalVariation[i] = new Move(principalVariationString[i],playing); + playing.doMove(principalVariation[i]); + } + } } if(SuicideChess.playInACSII()) { @@ -177,10 +202,16 @@ public class ComputerPlayer { return bestMove; } + private static boolean quiescenceSearch; //this will be used to determine is quiescence search is needed. + private static boolean adaptativeDepth; //this will + private static String extraSymbol=""; //display an extra symbol after the depth in the output if doing either quiescence search or adaptative depth + private static Date thinkingBeginingTime; private static int maxDepth; private static ArrayList bestMoves=new ArrayList(); + //private static Move[] killerMoves = new Move[SuicideChess.KILLER_SIZE]; //killer + private static Move[] principalVariation; //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 @@ -198,9 +229,8 @@ public class ComputerPlayer { public int getBranchValue() {return this.branchValue;} public String getPrincipalVariation() {return this.principalVariation;} }; - private static ReturnWrapper AlphaBeta(Board bitboard, int currentDepth, int currentMaxDepth, int alpha, int beta) throws NotAValidSquare, NoPieceOnSquare { + private static ReturnWrapper AlphaBeta(Board bitboard, int currentDepth, boolean inPrincipalVariation, int currentMaxDepth, int alpha, int beta) throws NotAValidSquare, NoPieceOnSquare { nodesSearched++; - if(bitboard.isADraw()) { return new ReturnWrapper(Board.DRAW_BOARD,Board.DRAW_BOARD,""); } @@ -218,15 +248,16 @@ public class ComputerPlayer { } allLegalMoves = Rules.getLegalMovesNonCapture(); } else { //if there are captures, see if we can just abandon search here + boolean changedMaxDepth = false; //to make sure that quiescence and adaptative don't interfere if((quiescenceSearch) && (currentDepth >= currentMaxDepth)) { - if((currentMaxDepth currentDepth)) { + if(principalVariation[currentDepth].isSimpleEqualTo(allLegalMoves.get(i))) { + continue; + } + } + boardCopy.doMove(allLegalMoves.get(i)); + returnValue = AlphaBeta(boardCopy,currentDepth+1,false,currentMaxDepth,Board.MIN_VALUE,beta); + } //System.out.println("Analysing "+currentDepth+":"+allLegalMoves.get(i)); - ReturnWrapper returnValue = AlphaBeta(boardCopy,currentDepth+1,currentMaxDepth,Board.MIN_VALUE,beta); + currentScore = returnValue.getBranchValue(); currentAlphaBeta = returnValue.getAlphaBeta(); @@ -275,24 +324,36 @@ public class ComputerPlayer { if (currentScore <= bestScoreSoFar) { if (currentScore < bestScoreSoFar) { bestScoreSoFar=currentScore; - bestVariationSoFar = allLegalMoves.get(i).toString()+" "+returnValue.getPrincipalVariation(); + if (i!=-1) { + bestVariationSoFar = allLegalMoves.get(i).toString()+" "+returnValue.getPrincipalVariation(); + } else { + bestVariationSoFar = principalVariation[currentDepth].toString()+" "+returnValue.getPrincipalVariation(); + } if (currentDepth==0) { bestMoves.clear(); if (SuicideChess.postThinkingOutput()) { - System.out.println(maxDepth+"\t"+returnValue.getBranchValue()+ + System.out.println(maxDepth+extraSymbol+"\t"+returnValue.getBranchValue()+ "\t"+((int)((new Date()).getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds "\t"+nodesSearched+"\t"+bestVariationSoFar); } //System.out.println("*** Clear "); if(bestScoreSoFar==Board.BLACK_WINS) { //found a win, no need to go further if(SuicideChess.playInACSII()) System.out.println("Found a win !"); - bestMoves.add(allLegalMoves.get(i)); + if (i!=-1) { + bestMoves.add(allLegalMoves.get(i)); + } else { + bestMoves.add(principalVariation[currentDepth]); + } return new ReturnWrapper(beta,bestScoreSoFar,bestVariationSoFar); } } } if(currentDepth==0) { - bestMoves.add(allLegalMoves.get(i)); + if(i!=-1) { + bestMoves.add(allLegalMoves.get(i)); + } else { + bestMoves.add(principalVariation[currentDepth]); + } //System.out.println("*** Adding "+allLegalMoves.get(i)); } } @@ -305,13 +366,28 @@ public class ComputerPlayer { return new ReturnWrapper(beta,bestScoreSoFar,bestVariationSoFar); } else { //white piece bestScoreSoFar=Board.MIN_VALUE; //white tries to maximise - for (int i=0; i currentDepth)) { + if(principalVariation[currentDepth].isSimpleEqualTo(allLegalMoves.get(i))) { + continue; + } + } + boardCopy.doMove(allLegalMoves.get(i)); + returnValue = AlphaBeta(boardCopy,currentDepth+1,false,currentMaxDepth,alpha,Board.MAX_VALUE); + } //System.out.println("Analysing "+currentDepth+":"+allLegalMoves.get(i)); - ReturnWrapper returnValue = AlphaBeta(boardCopy,currentDepth+1,currentMaxDepth,alpha,Board.MAX_VALUE); + currentScore = returnValue.getBranchValue(); currentAlphaBeta = returnValue.getAlphaBeta(); @@ -325,26 +401,37 @@ public class ComputerPlayer { //calculating branch value if (currentScore >= bestScoreSoFar) { if (currentScore > bestScoreSoFar) { - bestVariationSoFar = allLegalMoves.get(i).toString()+" "+returnValue.getPrincipalVariation(); bestScoreSoFar=currentScore; + if (i!=-1) { + bestVariationSoFar = allLegalMoves.get(i).toString()+" "+returnValue.getPrincipalVariation(); + } else { + bestVariationSoFar = principalVariation[currentDepth].toString()+" "+returnValue.getPrincipalVariation(); + } if (currentDepth==0) { bestMoves.clear(); if (SuicideChess.postThinkingOutput()) { - System.out.println(maxDepth+"\t"+returnValue.getBranchValue()+ + System.out.println(maxDepth+extraSymbol+"\t"+returnValue.getBranchValue()+ "\t"+((int)((new Date()).getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds "\t"+nodesSearched+"\t"+bestVariationSoFar); } if(bestScoreSoFar==Board.WHITE_WINS) { //found a win, no need to go further if(SuicideChess.playInACSII()) System.out.println("Found a win !"); - bestMoves.add(allLegalMoves.get(i)); + if (i!=-1) { + bestMoves.add(allLegalMoves.get(i)); + } else { + bestMoves.add(principalVariation[currentDepth]); + } return new ReturnWrapper(alpha,bestScoreSoFar,bestVariationSoFar); } //System.out.println("*** Clear "); } } if(currentDepth==0) { - bestMoves.add(allLegalMoves.get(i)); - //System.out.println("*** Adding "+allLegalMoves.get(i)); + if(i!=-1) { + bestMoves.add(allLegalMoves.get(i)); + } else { + bestMoves.add(principalVariation[currentDepth]); + } } } diff --git a/src/suicideChess/Move.java b/src/suicideChess/Move.java index 5bbaa94..8298760 100644 --- a/src/suicideChess/Move.java +++ b/src/suicideChess/Move.java @@ -310,7 +310,7 @@ public class Move { * @param that A move to test * @return boolean */ - public boolean isSimpleEqual(Move that) { + public boolean isSimpleEqualTo(Move that) { return ((this.fromSquare.isEqual(that.fromSquare))&&(this.toSquare.isEqual(that.toSquare))); } diff --git a/src/suicideChess/SuicideChess.java b/src/suicideChess/SuicideChess.java index ee671c0..7ea1fc8 100644 --- a/src/suicideChess/SuicideChess.java +++ b/src/suicideChess/SuicideChess.java @@ -40,14 +40,56 @@ public class SuicideChess { public static final boolean MOVE_ORDERING = false; /** - * Intelligent depth -> ie: when only one possible move, don't search. When very few moves, add one to depth. + * Minimum ply to search to (when doing iterative deepening) */ - public static final boolean INTELLIGENT_DEPTH = false; + public static final int MIN_PLY_DEPTH = 2; + /** + * Number of Plies the computes searches to + */ + private static int plyDepth = 4; + + /** + * What is the current ply depth + * @return integer + */ + public static int getPlyDepth() { return plyDepth; } + /** * Quiescence search -> don't evaluate if captures are possible. */ - public static final boolean QUIESCENCE_SEARCH = true; + public static final boolean QUIESCENCE_SEARCH = true; + + /** + * Quiescence limit (ie. if more than that many possibilities of capturing, don't analyse further. + */ + public static final int QUIESCENCE_LIMIT = 4; + + /** + * Maximum number of Plies the computer will ever go to + */ + public static final int MAX_QUIESCENCE_DEPTH = 8; + + + /** + * Adaptative depth -> ie: when only one possible move, don't search. When very few moves, add one to depth. + */ + public static final boolean ADAPTATIVE_DEPTH = true; + + /** + * Adaptative branchin limit + */ + public static final int ADAPTATIVE_BRANCHING_LIMIT = 3; + + ///** + // * Killer size (nb of killer moves remembered) + // */ + //public static final int KILLER_SIZE = 10; + + /** + * Try the primary variation from the earliest iteration first + */ + public static final boolean PRINCIPAL_VARIATION_FIRST = true; /** * The name to be displayed @@ -66,27 +108,7 @@ public class SuicideChess { public static boolean playInACSII() { return asciiGame; } - - /** - * Number of Plies the computes searches to - */ - 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; - /** - * Quiescence limit (ie. if more than that many possibilities of capturing, don't analyse further. - */ - public static final int QUIESCENCE_LIMIT = 5; - + /** * Test and display if the board is in a winning state. * @param bitboard A Board @@ -378,7 +400,7 @@ public class SuicideChess { needToCapture = true; } for (int moveIndex = 0; moveIndex < allLegalMoves.size(); moveIndex++) { - if (allLegalMoves.get(moveIndex).isSimpleEqual(theMove)) { + if (allLegalMoves.get(moveIndex).isSimpleEqualTo(theMove)) { if(theMove.isPromotionMove()&& theMove.getPromotionPiece().getPieceNumber()!=allLegalMoves.get(moveIndex).getPromotionPiece().getPieceNumber()) { continue; @@ -514,7 +536,7 @@ public class SuicideChess { //lets the computer try every problem and stop on error. //in the end it says what games where lost by white. - private static void autoProblem() throws NotAValidSquare, UnableToParseFENStringException, NoPieceOnSquare, NoSuchSuicideProblem { + private static void autoProblem() throws NotAValidSquare, UnableToParseFENStringException, NoPieceOnSquare, NoSuchSuicideProblem, NotAValidMoveException { Board bitboard; int[] result=new int[SuicideProblems.numberOfProblems()]; for(int i=1; i<=SuicideProblems.numberOfProblems(); i++) {