private int alphaBeta( Board board, int depth, int alpha, int beta, int player, int move, boolean nullMove) { if (forceHalt || interupted) return 0; // if (timeCheck == 0) { // // Check if still time left. // if (System.currentTimeMillis() >= endTime) { // forceHalt = true; // return 0; // } // timeCheck = TIME_CHECK_INT; // } // timeCheck--; nodes++; // For win/loss depth int inv_depth = maxDepth - depth, value = N_INF, bestValue = N_INF; int capsw, capsb, olda = alpha, oldb = beta; int plyBestMove = -1, hashPos = 0, color = (player == myPlayer) ? 1 : -1; boolean valuefound = false, collision = false; int[] currentMoves; // Transposition tp = null; if (transpositions) { hashPos = getHashPos(board.zobristHash); tp = tt[hashPos]; // Check if present in transposition table if (tp != null) { tt_lookups++; // Position was evaluated previously // Check for a collision if (tp.hash != board.zobristHash) { collisions++; collision = true; } else if (depth <= tp.depth) { if (tp.flag == Transposition.REAL) return tp.value; if (tp.flag == Transposition.L_BOUND && tp.value > alpha) alpha = tp.value; else if (tp.flag == Transposition.U_BOUND && tp.value < beta) beta = tp.value; if (alpha >= beta) return tp.value; } } } // Check if position is terminal. if (move != -1) { int winstate = board.checkWin(board.board[move]); if (winstate != Board.NONE_WIN) { if (winstate == player) { // Prefer shallow wins! bestValue = (WIN_VAL - (D_DECR * inv_depth)); valuefound = true; } else if (winstate == Board.DRAW) { return 0; } else { // Deeper losses are "less worse" :) than shallow losses bestValue = -(WIN_VAL - (D_DECR * inv_depth)); return bestValue; } } } // Leaf-node, evaluate the node if (depth == 0 && !valuefound) { bestValue = color * evaluate(board, inv_depth); valuefound = true; } else if (!valuefound) { // Don't do null moves at the first move, it messes up the swap rule if (nullmoves && !nullMove && depth < maxDepth && depth > R && !board.firstMove) { board.pass(); // Check for a null-move cut-off value = -alphaBeta(board, depth - 1 - R, -beta, -alpha, getOpponent(player), -1, true); board.undoPass(); if (value >= beta) { return beta; } } // if (tp == null || collision) { currentMoves = board.getAvailableMoves(killermove[inv_depth]); } else { currentMoves = board.getAvailableMoves( killermove[inv_depth][0], killermove[inv_depth][1], tp.bestMove); } int startindex = board.startindex, currentmove; double maxHistVal = 1.; for (int i = 0; i < currentMoves.length; i++) { // Try the killer and transposition moves first, then try the hh moves if (i >= startindex && maxHistVal > 0. && historyHeuristic) { board.getNextMove(history[player - 1], bfboard[player - 1], currentMoves, i); // If the previous max history value was 0, we can just follow the indexed list maxHistVal = board.maxHistVal; } currentmove = currentMoves[i]; if (board.doMove(currentmove, player)) { // Returns false if suicide if (board.capturePieces(currentmove)) { // capsw = board.playerCaps[0]; capsb = board.playerCaps[1]; // Keep track of the captured pieces. captures[0] += capsw; captures[1] += capsb; // value = -alphaBeta( board, depth - 1, -beta, -alpha, getOpponent(player), currentmove, nullMove); // if (value > bestValue) { // for detemining the move to return if (depth == maxDepth && value > bestValue) { bestMove = currentmove; } // bestValue = value; plyBestMove = currentmove; } // alpha = Math.max(alpha, bestValue); // Substract the captures from this move captures[0] -= capsw; captures[1] -= capsb; board.undoMove(); // Update the butterfly board for the relative history heuristic bfboard[player - 1][currentmove]++; if (alpha >= beta) { if (killermoves && currentmove != killermove[inv_depth][0]) { killermove[inv_depth][1] = killermove[inv_depth][0]; killermove[inv_depth][0] = currentmove; } break; } } } else { System.err.println("error making move!"); } } } // Update the history heuristics for move-ordering if (plyBestMove > -1) history[player - 1][plyBestMove]++; // Replace if deeper or doesn't exist if (transpositions && (tp == null || (collision && depth > tp.depth))) { tp = new Transposition(); tt[hashPos] = tp; tp.bestMove = plyBestMove; tp.depth = depth; tp.hash = board.zobristHash; // if (bestValue <= olda) { tp.flag = Transposition.U_BOUND; } else if (bestValue >= oldb) { tp.flag = Transposition.L_BOUND; } else { tp.flag = Transposition.REAL; } tp.value = bestValue; } return bestValue; }