diff --git a/src/suicideChess/Board.java b/src/suicideChess/Board.java index 5e04bd1..0288421 100644 --- a/src/suicideChess/Board.java +++ b/src/suicideChess/Board.java @@ -10,6 +10,9 @@ import suicideChess.Square.NotAValidSquare; *
  • a2 is square 8
  • *
  • ... and so on
  • * + * Note that the evaluation of the board balance is in this class since I considered that it is + * very closely related to the board position. + * * @author Jean-Baptiste Hétier * @version $LastChangedRevision$, $LastChangedDate$ * @@ -37,7 +40,17 @@ public class Board { NoPieceOnSquare(String s) { super(s); }; } - + /** + * Value returned by the evaluation function when White wins + */ + public static final int WHITE_WINS = 99999; + + /** + * Value returned by the evaluation function when Black wins + */ + public static final int BLACK_WINS = -99999; + + /*======* * DATA * *======*/ @@ -60,6 +73,9 @@ public class Board { private boolean enPassant=false; //is there an 'en passant pawn' on the board private Square enPassantSquare; + private int numberOfBlackPieces = NB_OF_FILES*2; + private int numberOfWhitePieces = NB_OF_FILES*2; + private int boardValue = 0; //evaluation of the board value /*=============* @@ -111,12 +127,29 @@ public class Board { } + /** + * A constructor that simply copies a bitboard + * @param bitboard The bitboard to be copied + */ + + public Board(Board bitboard) { + this.numberOfBlackPieces = bitboard.numberOfBlackPieces; + this.numberOfWhitePieces = bitboard.numberOfWhitePieces; + this.boardValue = bitboard.boardValue; + this.bitBoards = new long[NB_OF_BITBOARDS]; + for (int i=0; i allLegalMoves = Rules.getLegalMovesCapture(); @@ -39,8 +43,73 @@ public class ComputerPlayer { return allLegalMoves.get(generator.nextInt(allLegalMoves.size())); } - throw new RuntimeException(); + throw new RuntimeException("**Error** in doRandomMove"); } + /** + * Basic MinMax + * @param bitboard The bitboard + * @param color The color to play + * @return The best Move found + * @throws NotAValidSquare + * @throws NoPieceOnSquare + * @see Board + * @see Move + */ + public Move doMinMaxMove(Board bitboard, int color) throws NotAValidSquare, NoPieceOnSquare { + bestMove = null; + MinMax(bitboard, color, 0); + return bestMove; + } + + + private int MinMax(Board bitboard, int color, int currentDepth) throws NotAValidSquare, NoPieceOnSquare { + if (currentDepth >= SuicideChess.PLY_DEPTH) { + return bitboard.getBoardValue(); + } + Rules.legalMovesForPlayer(bitboard,color); + ArrayList allLegalMoves = Rules.getLegalMovesCapture(); + if (allLegalMoves.size()==0) { + allLegalMoves = Rules.getLegalMovesNonCapture(); + } + if (allLegalMoves.size()==0) { + if (color==Piece.BLACK) { + return Board.BLACK_WINS; + } else { + return Board.WHITE_WINS; + } + } else { + int bestMoveIndex=0; + int currentScore; + int bestScoreSoFar; + if (color==Piece.BLACK) { + bestScoreSoFar=Board.WHITE_WINS+1; //any move even a WHITE_WINS will be better than that + for (int i=0; i bestScoreSoFar) { //white tries to maximise his score + bestScoreSoFar = currentScore; + bestMoveIndex = i; + } + } + } + bestMove = allLegalMoves.get(bestMoveIndex); + return bestScoreSoFar; + } + } + + private static Move bestMove = null; } \ No newline at end of file diff --git a/src/suicideChess/Rules.java b/src/suicideChess/Rules.java index 2238a38..3152acb 100644 --- a/src/suicideChess/Rules.java +++ b/src/suicideChess/Rules.java @@ -15,7 +15,7 @@ public class Rules { private static ArrayList legalMovesNonCapture; private static ArrayList legalMovesCapture; - + // public class UnexpectedError extends Exception { // /* // * Generated by Eclipse diff --git a/src/suicideChess/SuicideChess.java b/src/suicideChess/SuicideChess.java index bafb6a9..709596d 100644 --- a/src/suicideChess/SuicideChess.java +++ b/src/suicideChess/SuicideChess.java @@ -33,13 +33,18 @@ public class SuicideChess { /** * The name to be displayed */ - public static final String NAME = "djib's SuicideChess v0.3 beta 2"; + public static final String NAME = "djib's SuicideChess v0.4.1"; /** * Displays informations in the console. */ public static final boolean ASCII_GAME = false; - + + /** + * Number of Plies the computes searches to + */ + public static final int PLY_DEPTH = 5; + /** * The color of the current player */ @@ -60,167 +65,200 @@ public class SuicideChess { } } + /** + * If feature usermove has not been accepted by XBoard then consider all unknown commands + * as moves + */ + private static boolean acceptedUsermove = false; + /** * The main function * @param args No parameters should be transmitted to this function. + * @throws NotAValidSquare */ - public static void main(String[] args) { + 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(); + + + BufferedReader moveInput = new BufferedReader(new InputStreamReader(System.in)); + Board bitboard = new Board(); + + if (ASCII_GAME) { + bitboard.display(); + System.out.println("White: "); + } + + ComputerPlayer computer = new ComputerPlayer(); + boolean computerPlaying = true; //the computer does not play in foce mode. + + boolean playing = true; + + while (playing) { + try { + String whatMove= moveInput.readLine(); + boolean playedALegalMove = false; - try { - - BufferedReader moveInput = new BufferedReader(new InputStreamReader(System.in)); - Board bitboard = new Board(); - - if (ASCII_GAME) { - bitboard.display(); - System.out.println("White: "); - } - - ComputerPlayer computer = new ComputerPlayer(); - - boolean playing = true; - - while (playing) { - try { - String whatMove= moveInput.readLine(); - boolean playedALegalMove = false; - - int xBoardCommand = XBoardProtocol.getCommand(whatMove); - - switch (xBoardCommand) { - case XBoardProtocol.XBOARD: - break; - case XBoardProtocol.PROTOVER: - XBoardProtocol.initialise(); - break; - case XBoardProtocol.NOT_ACCEPTED_SUICIDE: - System.out.println("tellusererror \"This game only plays suicide chess.\""); - playing=false; - break; - case XBoardProtocol.NOT_ACCEPTED_USERMOVE: - System.out.println("tellusererror \"XBoard must send moves starting with 'usermove'\""); - playing=false; - break; - case XBoardProtocol.NOPROTOVER: - System.out.println("tellusererror \"You must use an engine with XBoard protocol 2 or higher.\""); - playing=false; - break; - case XBoardProtocol.QUIT: - System.out.println("Goodbye!"); - playing=false; - break; - case XBoardProtocol.NEW: - System.out.println("variant suicide"); - break; - case XBoardProtocol.UNKNOWN: + int xBoardCommand = XBoardProtocol.getCommand(whatMove); + + switch (xBoardCommand) { + case XBoardProtocol.XBOARD: + break; + case XBoardProtocol.PROTOVER: + XBoardProtocol.initialise(); + break; + case XBoardProtocol.NOT_ACCEPTED_SUICIDE: + System.out.println("tellusererror \"This game only plays suicide chess.\""); + playing=false; + break; + case XBoardProtocol.ACCEPTED_USERMOVE: + acceptedUsermove=true; + break; + case XBoardProtocol.NOPROTOVER: + System.out.println("tellusererror \"You must use an engine with XBoard protocol 2 or higher.\""); + playing=false; + break; + case XBoardProtocol.QUIT: + System.out.println("Goodbye!"); + playing=false; + break; + case XBoardProtocol.NEW: + System.out.println("variant suicide"); + break; + case XBoardProtocol.HINT: + System.out.println("Hint: "+computer.doRandomMove(bitboard,currentPlayerColor)); + break; + case XBoardProtocol.FORCE: + computerPlaying = false; + break; + case XBoardProtocol.UNKNOWN: + if (acceptedUsermove) { System.out.println("Error (unknown command): "+whatMove); break; - case XBoardProtocol.MOVE: - Move theMove = new Move(whatMove.substring(9), bitboard); - - boolean needToCapture = false; - int foundMoveIndex = -1; - if(theMove.getMovingPiece().getColor() == currentPlayerColor) { - Rules.legalMovesForPlayer(bitboard,currentPlayerColor); - ArrayList allLegalMoves = Rules.getLegalMovesCapture(); - if (allLegalMoves.size()==0) { - allLegalMoves = Rules.getLegalMovesNonCapture(); - } else { - needToCapture = true; - } - for (int moveIndex = 0; moveIndex < allLegalMoves.size(); moveIndex++) { - if (allLegalMoves.get(moveIndex).isSimpleEqual(theMove)) { - if(theMove.isPromotionMove()&& - theMove.getPromotionPiece().getPieceNumber()!=allLegalMoves.get(moveIndex).getPromotionPiece().getPieceNumber()) { - continue; - } - foundMoveIndex=moveIndex; - break; - } - } - if (foundMoveIndex == -1) { - if (needToCapture) { - if (ASCII_GAME) - System.out.println("Capturing is mandatory."); - } - System.out.println("Illegal move: "+theMove.toString()); - } else { - bitboard.doMove(allLegalMoves.get(foundMoveIndex)); - if (ASCII_GAME) { - allLegalMoves.get(foundMoveIndex).display(); - bitboard.display(); - } - playedALegalMove=true; - } + } + //if XBoard did not accept usermove command we try and interpret every unknown command + //as a move. + case XBoardProtocol.MOVE: + Move theMove; + if (acceptedUsermove) { + theMove = new Move(whatMove.substring(9), bitboard); + } else { + theMove = new Move(whatMove, bitboard); + } + + boolean needToCapture = false; + int foundMoveIndex = -1; + if(theMove.getMovingPiece().getColor() == currentPlayerColor) { + Rules.legalMovesForPlayer(bitboard,currentPlayerColor); + ArrayList allLegalMoves = Rules.getLegalMovesCapture(); + if (allLegalMoves.size()==0) { + allLegalMoves = Rules.getLegalMovesNonCapture(); } else { - if (ASCII_GAME) - System.out.println("Please play a piece of the right color."); + needToCapture = true; + } + for (int moveIndex = 0; moveIndex < allLegalMoves.size(); moveIndex++) { + if (allLegalMoves.get(moveIndex).isSimpleEqual(theMove)) { + if(theMove.isPromotionMove()&& + theMove.getPromotionPiece().getPieceNumber()!=allLegalMoves.get(moveIndex).getPromotionPiece().getPieceNumber()) { + continue; + } + foundMoveIndex=moveIndex; + break; + } + } + if (foundMoveIndex == -1) { + if (needToCapture) { + if (ASCII_GAME) + System.out.println("Capturing is mandatory."); + } System.out.println("Illegal move: "+theMove.toString()); + } else { + bitboard.doMove(allLegalMoves.get(foundMoveIndex)); + if (ASCII_GAME) { + allLegalMoves.get(foundMoveIndex).display(); + System.out.println("Board value: "+bitboard.getBoardValue()); + bitboard.display(); + } + playedALegalMove=true; } - - if (!playedALegalMove) { - break; - } - changeCurrentPlayerColor(); - //No break statement here on purpose. - case XBoardProtocol.GO: - Move computerMove = computer.doMove(bitboard,currentPlayerColor); + } else { + if (ASCII_GAME) + System.out.println("Please play a piece of the right color."); + System.out.println("Illegal move: "+theMove.toString()); + } + + if (!playedALegalMove) { + break; + } + changeCurrentPlayerColor(); + //No break statement here on purpose. + case XBoardProtocol.GO: + //this is not really nice but it avoids having to write twice the code + //I check if I did really receive a XBoardProtocol.GO or it I just got there + //because there is no break statement. + if (xBoardCommand==XBoardProtocol.GO) + computerPlaying = true; + if (computerPlaying) { + Move computerMove = computer.doMinMaxMove(bitboard,currentPlayerColor); bitboard.doMove(computerMove); XBoardProtocol.doMove(computerMove); if (ASCII_GAME) { computerMove.display(); + System.out.println("Board value: "+bitboard.getBoardValue()); bitboard.display(); } changeCurrentPlayerColor(); - - break; - - } - -// if (whatMove.startsWith("hint")) { -// Rules rules = new Rules(); -// rules.legalMovesForPlayer(bitboard,currentPlayerColor); -// ArrayList allLegalMoves = rules.getLegalMovesCapture(); -// if (allLegalMoves.size()==0) { -// allLegalMoves = rules.getLegalMovesNonCapture(); -// } -// for(int i = 0; i allLegalMoves = rules.getLegalMovesCapture(); +// if (allLegalMoves.size()==0) { +// allLegalMoves = rules.getLegalMovesNonCapture(); +// } +// for(int i = 0; i