Browse Source

VERSION 1.0.0

=============
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
master
djib 16 years ago
parent
commit
8854c787f9
  1. 139
      src/suicideChess/ComputerPlayer.java
  2. 2
      src/suicideChess/Move.java
  3. 74
      src/suicideChess/SuicideChess.java

139
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<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]);
}
}
}

2
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)));
}

74
src/suicideChess/SuicideChess.java

@ -40,53 +40,75 @@ 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;
/**
* Quiescence search -> don't evaluate if captures are possible.
* Number of Plies the computes searches to
*/
public static final boolean QUIESCENCE_SEARCH = true;
private static int plyDepth = 4;
/**
* The name to be displayed
* What is the current ply depth
* @return integer
*/
public static final String NAME = "djib's SuShi v0.9.2";
public static int getPlyDepth() { return plyDepth; }
/**
* Displays informations in the console.
* Quiescence search -> don't evaluate if captures are possible.
*/
private static boolean asciiGame = true;
public static final boolean QUIESCENCE_SEARCH = true;
/**
* Should the game be played in acsii
* @return boolean
* Quiescence limit (ie. if more than that many possibilities of capturing, don't analyse further.
*/
public static boolean playInACSII() {
return asciiGame;
}
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;
/**
* Number of Plies the computes searches to
* Adaptative depth -> ie: when only one possible move, don't search. When very few moves, add one to depth.
*/
private static int plyDepth = 4;
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;
/**
* What is the current ply depth
* @return integer
* Try the primary variation from the earliest iteration first
*/
public static int getPlyDepth() { return plyDepth; }
public static final boolean PRINCIPAL_VARIATION_FIRST = true;
/**
* Maximum number of Plies the computer will ever go to
* The name to be displayed
*/
public static final int MAX_PLY_DEPTH = 8;
public static final String NAME = "djib's SuShi v0.9.2";
/**
* Quiescence limit (ie. if more than that many possibilities of capturing, don't analyse further.
* Displays informations in the console.
*/
public static final int QUIESCENCE_LIMIT = 5;
private static boolean asciiGame = true;
/**
* Should the game be played in acsii
* @return boolean
*/
public static boolean playInACSII() {
return asciiGame;
}
/**
* 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++) {

Loading…
Cancel
Save