VERSION 1.0.0
============= !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
This commit is contained in:
@ -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<Move> bestMoves=new ArrayList<Move>();
|
||||
//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<SuicideChess.MAX_PLY_DEPTH) && (allLegalMoves.size()<=SuicideChess.QUIESCENCE_LIMIT)) {
|
||||
if((currentMaxDepth<SuicideChess.MAX_QUIESCENCE_DEPTH) && (allLegalMoves.size()<=SuicideChess.QUIESCENCE_LIMIT)) {
|
||||
currentMaxDepth++;
|
||||
changedMaxDepth = true;
|
||||
} else {
|
||||
return new ReturnWrapper(bitboard.getBoardValue(),bitboard.getBoardValue(),"");
|
||||
}
|
||||
}
|
||||
if (SuicideChess.INTELLIGENT_DEPTH) {
|
||||
currentMaxDepth++; //go one step further in depth
|
||||
if (adaptativeDepth) {
|
||||
if(currentDepth==0 && allLegalMoves.size()==1) {
|
||||
bestMoves.clear();
|
||||
bestMoves.add(allLegalMoves.get(0));
|
||||
@ -234,6 +265,8 @@ public class ComputerPlayer {
|
||||
boardCopy.doMove(allLegalMoves.get(0));
|
||||
return new ReturnWrapper(bitboard.getBoardValue(),bitboard.getBoardValue(),allLegalMoves.get(0).toString());
|
||||
}
|
||||
if(!changedMaxDepth && allLegalMoves.size()<=SuicideChess.ADAPTATIVE_BRANCHING_LIMIT)
|
||||
currentMaxDepth++; //go one step further in depth
|
||||
}
|
||||
}
|
||||
if (allLegalMoves.size()==0) {
|
||||
@ -249,18 +282,34 @@ public class ComputerPlayer {
|
||||
int bestScoreSoFar;
|
||||
int currentAlphaBeta;
|
||||
String bestVariationSoFar="";
|
||||
ReturnWrapper returnValue;
|
||||
if (SuicideChess.MOVE_ORDERING) {
|
||||
Collections.sort(allLegalMoves,new MoveCompare(bitboard));
|
||||
}
|
||||
if (bitboard.getCurrentPlayer()==Piece.BLACK) {
|
||||
bestScoreSoFar=Board.MAX_VALUE; //black tries to minimise
|
||||
for (int i=0; i<allLegalMoves.size(); i++) {
|
||||
for (int i=-1; i<allLegalMoves.size(); i++) {
|
||||
Board boardCopy = new Board(bitboard);
|
||||
boardCopy.doMove(allLegalMoves.get(i));
|
||||
if (i==-1) { //do the principal variation first
|
||||
if(!inPrincipalVariation || (principalVariation.length <= currentDepth)) {
|
||||
continue;
|
||||
} else {
|
||||
boardCopy.doMove(principalVariation[currentDepth]);
|
||||
returnValue = AlphaBeta(boardCopy,currentDepth+1,true,currentMaxDepth,Board.MIN_VALUE,beta);
|
||||
}
|
||||
} else { //and then don't do it again.
|
||||
if(SuicideChess.PRINCIPAL_VARIATION_FIRST && (principalVariation.length > 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<allLegalMoves.size(); i++) {
|
||||
for (int i=-1; i<allLegalMoves.size(); i++) {
|
||||
Board boardCopy = new Board(bitboard);
|
||||
boardCopy.doMove(allLegalMoves.get(i));
|
||||
if (i==-1) { //do the principal variation first
|
||||
if(!inPrincipalVariation || (principalVariation.length <= currentDepth)) {
|
||||
continue;
|
||||
} else {
|
||||
boardCopy.doMove(principalVariation[currentDepth]);
|
||||
returnValue = AlphaBeta(boardCopy,currentDepth+1,true,currentMaxDepth,alpha,Board.MAX_VALUE);
|
||||
}
|
||||
} else { //and then don't do it again.
|
||||
if(SuicideChess.PRINCIPAL_VARIATION_FIRST && (principalVariation.length > 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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++) {
|
||||
|
Reference in New Issue
Block a user