@Override public void chooseMove() { long startTime = System.nanoTime(); Move bestMove = null; double bestScore = 0; // We need to initialize alpha and beta -infinity and + infinity respectively. double alpha = Double.NEGATIVE_INFINITY; double beta = Double.POSITIVE_INFINITY; int maxDepth; for (maxDepth = 1; (System.nanoTime() - startTime) <= maxTime; maxDepth++) { List<Move> legalMoves = GameState2P.getLegalMoves(state, index); bestScore = 0; for (Move m : legalMoves) { // If we run out time we break out. if ((System.nanoTime() - startTime) >= maxTime) { break; } GameState2P next = m.doMove(state); double score = getMinScoreAlphaBeta(next, maxDepth, alpha, beta); if (bestMove == null || score >= bestScore) { bestMove = m; bestScore = score; } } } System.out.println("Depth: " + maxDepth + " trans score: " + bestScore); GameState2P newState = bestMove.doMove(state); game.doMove(index, newState); }
/* * Consider all possible moves by our opponent */ private double getMinScoreAlphaBeta( final GameState2P state, int depth, double alpha, double beta) { double res = Double.POSITIVE_INFINITY; double score; // We try to get the current opponent state from the transposition table. TranspositionEntry entry = minTable.getEntryFromGameState(state); // Get the opponent's moves. final List<Move> opponentMoves = GameState2P.getLegalMoves(state, indexOpponent); // Check if current opp state existed before in table and that the depth is less than the depth // stored in the entry. We need to check the states as well, as it is possible that two state // has the same hashcode. // Then return the previous best minimax score for this state. if (null != entry && state.equals(entry.getGameState2P()) && depth <= entry.getDepth()) { res = entry.getMinimax(); } if (depth == 0 || state.isGameOver()) { res = state.evaluateState(index); } else { Comparator<Move> comparator = new Comparator<Move>() { @Override public int compare(Move move1, Move move2) { // Check if the current opponent moves exist in the transposition table. If it doesn't // set minimax // to positive infinity, in order words the worst result for the opponent. int lhs = (null != minTable.getEntryFromGameState(move1.doMove(state))) ? (int) minTable.getEntryFromGameState(move1.doMove(state)).getMinimax() : (int) Double.POSITIVE_INFINITY; int rhs = (null != minTable.getEntryFromGameState(move2.doMove(state))) ? (int) minTable.getEntryFromGameState(move2.doMove(state)).getMinimax() : (int) Double.POSITIVE_INFINITY; // If right is lower swap. if (lhs > rhs) { return 1; } else if (lhs == rhs) { return 0; } else { return -1; } } }; // For the minimax score we need to sort the opponent's moves by their scores lowest first, in // order to optimise // the search. Collections.sort(opponentMoves, comparator); // The opponent will go through their moves and try to get the best minimax score for them. // Once a promising child move is found store it with the minimax value in the transposition // table. for (Move move : opponentMoves) { GameState2P next = move.doMove(state); score = getMaxScoreAlphaBeta(next, depth - 1, alpha, beta); res = Math.min(res, score); beta = Math.min(beta, score); if (beta <= alpha) { // minTable.addEntry(next, res, depth); break; } } } return res; }