Files
Sushi/src/suicideChess/ComputerPlayer.java
djib a0ee66638d Version 0.8.0
=============
Just before creating the external config file
2006-07-04 01:45:39 +00:00

333 lines
15 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package suicideChess;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import suicideChess.Board.NoPieceOnSquare;
import suicideChess.Square.NotAValidSquare;
/**
* This class will contain all the AI.
* @author Jean-Baptiste Hétier
* @version $LastChangedRevision$, $LastChangedDate$
*/
public class ComputerPlayer {
//best move found
private static Move bestMove = null;
//to tell the number of nodes searched
private static int nodesSearched;
/**
* This asks the computer to compute a move
* @param bitboard The current status of the {@link Board}
* @return move A {@link Move}
* @throws NotAValidSquare
* @see Board
* @see Move
*/
public static Move doRandomMove(Board bitboard) throws NotAValidSquare {
Random generator = new Random();
Rules.legalMovesForPlayer(bitboard);
ArrayList<Move> allLegalMoves = Rules.getLegalMovesCapture();
if (allLegalMoves.size()==0) {
allLegalMoves = Rules.getLegalMovesNonCapture();
}
if (allLegalMoves.size()!=0) {
return allLegalMoves.get(generator.nextInt(allLegalMoves.size()));
}
throw new RuntimeException("**Error** in doRandomMove");
}
/**
* Basic MinMax
* @param bitboard The bitboard
* @return The best Move found
* @throws NotAValidSquare
* @throws NoPieceOnSquare
* @see Board
* @see Move
*/
public static Move doMinMaxMove(Board bitboard) throws NotAValidSquare, NoPieceOnSquare {
bestMove = null;
nodesSearched = 0;
int bestScore = MinMax(bitboard, 0);
if (SuicideChess.postThinkingOutput()) {
System.out.println(SuicideChess.getPlyDepth()+" "+bestScore*100+" 0 "+nodesSearched+" "+bestMove);
}
return bestMove;
}
private static int MinMax(Board bitboard, int currentDepth) throws NotAValidSquare, NoPieceOnSquare {
nodesSearched++;
if (currentDepth >= SuicideChess.getPlyDepth()) {
return bitboard.getBoardValue();
}
Rules.legalMovesForPlayer(bitboard);
ArrayList<Move> allLegalMoves = Rules.getLegalMovesCapture();
if (allLegalMoves.size()==0) {
allLegalMoves = Rules.getLegalMovesNonCapture();
}
if (allLegalMoves.size()==0) {
if (bitboard.getCurrentPlayer()==Piece.BLACK) {
return Board.BLACK_WINS;
} else {
return Board.WHITE_WINS;
}
} else {
ArrayList<Integer> bestMoveIndex=new ArrayList<Integer>();
int currentScore;
int bestScoreSoFar;
if (bitboard.getCurrentPlayer()==Piece.BLACK) {
bestScoreSoFar=Board.WHITE_WINS+1; //any move even a WHITE_WINS will be better than that
for (int i=0; i<allLegalMoves.size(); i++) {
Board boardCopy = new Board(bitboard);
boardCopy.doMove(allLegalMoves.get(i));
currentScore=MinMax(boardCopy,currentDepth+1);
if (currentScore <= bestScoreSoFar) { //black tries to minimise his score
if (currentScore<bestScoreSoFar) {
bestScoreSoFar = currentScore;
bestMoveIndex.clear();
}
bestMoveIndex.add(i);
}
}
} else { //white piece
bestScoreSoFar=Board.BLACK_WINS-1; //any move even a BLACK_WINS will be better than that
for (int i=0; i<allLegalMoves.size(); i++) {
Board boardCopy = new Board(bitboard);
boardCopy.doMove(allLegalMoves.get(i));
currentScore=MinMax(boardCopy,currentDepth+1);
if (currentScore >= bestScoreSoFar) { //white tries to maximise his score
if (currentScore>bestScoreSoFar) {
bestScoreSoFar = currentScore;
bestMoveIndex.clear();
}
bestMoveIndex.add(i);
}
}
}
//select one of the best moves randomly
Random generator = new Random();
if (currentDepth == 0) { System.out.println(bestMoveIndex.size()); }
bestMove = allLegalMoves.get(bestMoveIndex.get(generator.nextInt(bestMoveIndex.size())));
return bestScoreSoFar;
}
}
/**
* Alpha-Beta
* @param bitboard The bitboard
* @return The best Move found
* @throws NotAValidSquare
* @throws NoPieceOnSquare
* @see Board
* @see Move
*/
public static Move doAlphaBetaMove(Board bitboard) throws NotAValidSquare, NoPieceOnSquare {
bestMove = null;
nodesSearched = 0;
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()+
"\t"+((int)(thinkingEndTime.getTime()-thinkingBeginingTime.getTime())/10)+ //search time in centiseconds
"\t"+nodesSearched+"\t"+bestScore.getPrincipalVariation());
}
if((bitboard.getCurrentPlayer()==Piece.BLACK
&& bestScore.getBranchValue()==Board.BLACK_WINS)
|| (bitboard.getCurrentPlayer()==Piece.WHITE
&& bestScore.getBranchValue()==Board.WHITE_WINS)) {
break; //no need to continue iterative deepening.
}
}
if(SuicideChess.playInACSII()) {
System.out.println("Found "+bestMoves.size()+" good moves.");
}
System.out.println(((bitboard.mobility(Piece.WHITE))));
System.out.println(((-bitboard.mobility(Piece.BLACK))));
return bestMove;
}
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;
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,"");
}
if (currentDepth >= maxDepth) {
//System.out.println("'-> Evaluate: "+bitboard.getBoardValue());
return new ReturnWrapper(bitboard.getBoardValue(),bitboard.getBoardValue(),"");
}
Rules.legalMovesForPlayer(bitboard);
ArrayList<Move> allLegalMoves = Rules.getLegalMovesCapture();
if (allLegalMoves.size()==0) {
allLegalMoves = Rules.getLegalMovesNonCapture();
}
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,"");
} else {
//System.out.println("'-> Evaluate *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++) {
Board boardCopy = new Board(bitboard);
boardCopy.doMove(allLegalMoves.get(i));
//System.out.println("Analysing "+currentDepth+":"+allLegalMoves.get(i));
ReturnWrapper returnValue = AlphaBeta(boardCopy,currentDepth+1,Board.MIN_VALUE,beta);
currentScore = returnValue.getBranchValue();
currentAlphaBeta = returnValue.getAlphaBeta();
//System.out.println("| CurrentScore, BestScore:" + currentScore + ", " + bestScoreSoFar);
//System.out.println("| CurrentBeta, beta:" + currentAlphaBeta + ", " + beta);
//calculating new value of beta
if (currentAlphaBeta<=beta) {
beta = currentAlphaBeta;
}
//calculating branch value
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()+
"\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));
return new ReturnWrapper(beta,bestScoreSoFar,bestVariationSoFar);
}
}
}
if(currentDepth==0) {
bestMoves.add(allLegalMoves.get(i));
//System.out.println("*** Adding "+allLegalMoves.get(i));
}
}
if(beta<alpha) {
//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,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);
currentScore = returnValue.getBranchValue();
currentAlphaBeta = returnValue.getAlphaBeta();
//System.out.println("| CurrentScore, BestScore:" + currentScore + ", " + bestScoreSoFar);
//System.out.println("| CurrentAlpha, alpha:" + currentAlphaBeta + ", " + alpha);
//calculating new value of alpha
if (currentAlphaBeta>=alpha) {
alpha = currentAlphaBeta;
}
//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()+
"\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));
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(alpha>beta) {
//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,bestVariationSoFar);
}
}
}
}