Browse Source

Version 7.0

===========
Corrected a few bugs from 6.9, like the principal variation.
About to add opening book.
master
djib 16 years ago
parent
commit
b02755be4e
  1. 29
      src/suicideChess/Board.java
  2. 90
      src/suicideChess/ComputerPlayer.java
  3. 24
      src/suicideChess/Piece.java
  4. 4
      src/suicideChess/Rules.java
  5. 108
      src/suicideChess/SuicideChess.java
  6. 8
      src/suicideChess/XBoardProtocol.java

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

90
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();
//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()));
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());
}
System.out.println("Found "+bestMoves.size()+" good moves.");
}
if(SuicideChess.playInACSII()) {
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<Move> bestMoves=new ArrayList<Move>();
private static Date thinkingBeginingTime;
private static int maxDepth;
private static ArrayList<Move> bestMoves=new ArrayList<Move>();
//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<allLegalMoves.size(); i++) {
@ -226,8 +240,14 @@ public class ComputerPlayer {
if (currentScore <= bestScoreSoFar) {
if (currentScore < bestScoreSoFar) {
bestScoreSoFar=currentScore;
bestVariationSoFar = allLegalMoves.get(i).toString()+" "+returnValue.getPrincipalVariation();
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 ");
}
}
@ -236,19 +256,19 @@ public class ComputerPlayer {
//System.out.println("*** Adding "+allLegalMoves.get(i));
}
}
if(beta<alpha) {
//if(currentDepth!=SuicideChess.PLY_DEPTH-1) System.out.println("Pruning "+Integer.toString(allLegalMoves.size()-i)+" alternatives at depth "+ currentDepth);
return new ReturnWrapper(alpha,bestScoreSoFar); //pruning
//if(currentDepth!=SuicideChess.getPlyDepth()-1) System.out.println("Pruning "+Integer.toString(allLegalMoves.size()-i)+" alternatives at depth "+ currentDepth);
return new ReturnWrapper(alpha,bestScoreSoFar,bestVariationSoFar); //pruning
}
}
return new ReturnWrapper(beta,bestScoreSoFar);
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++) {
Board boardCopy = new Board(bitboard);
boardCopy.doMove(allLegalMoves.get(i));
//System.out.println("Analysing "+currentDepth+":"+allLegalMoves.get(i));
ReturnWrapper returnValue = AlphaBeta(boardCopy,currentDepth+1,alpha,Board.MAX_VALUE);
@ -265,9 +285,15 @@ public class ComputerPlayer {
//calculating branch value
if (currentScore >= 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);
}
}
}

24
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];

4
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.

108
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=========");
}
}

8
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;

Loading…
Cancel
Save