// Store entry into TTable public void storeTTable(UInt64 key, TTEntry entry) { int index = (int) (key % Constants.TT_SIZE); // If entry in bucket has same hash key, then replace for (int i = index; i < index + Constants.BUCKET_SIZE; i++) { if (this.transpositionTable[i].key == key) { this.transpositionTable[i] = entry; return; } } // If there is an empty spot in the bucket, then store it there for (int i = index; i < index + Constants.BUCKET_SIZE; i++) { if (this.transpositionTable[i].key == 0) { this.transpositionTable[i] = entry; return; } } // If all spots full, then replace entry with lowest depth int shallowestDepth = Constants.INF; int indexOfShallowestEntry = -1; for (int i = index; i < index + Constants.BUCKET_SIZE; i++) { if (this.transpositionTable[i].depth < shallowestDepth) { shallowestDepth = this.transpositionTable[i].depth; indexOfShallowestEntry = i; } } this.transpositionTable[indexOfShallowestEntry] = entry; }
// Store entry into TTable public void storeTTable(UInt64 key, TTEntry entry) { int index = (int)(key % Constants.TT_SIZE); // If entry in bucket has same hash key, then replace for (int i = index; i < index + Constants.BUCKET_SIZE; i++) { if (this.transpositionTable[i].key == key) { this.transpositionTable[i] = entry; return; } } // If there is an empty spot in the bucket, then store it there for (int i = index; i < index + Constants.BUCKET_SIZE; i++) { if (this.transpositionTable[i].key == 0) { this.transpositionTable[i] = entry; return; } } // If all spots full, then replace entry with lowest depth int shallowestDepth = Constants.INF; int indexOfShallowestEntry = -1; for (int i = index; i < index + Constants.BUCKET_SIZE; i++) { if (this.transpositionTable[i].depth < shallowestDepth) { shallowestDepth = this.transpositionTable[i].depth; indexOfShallowestEntry = i; } } this.transpositionTable[indexOfShallowestEntry] = entry; }
public static int solve(int nodeType, Position inputBoard, int ply, int alpha, int beta, int depth) { // return score for terminal state if (inputBoard.HasWon(inputBoard.arrayOfBitboard[(inputBoard.nPlies - 1) & 1])) { return(-Constants.WIN + ply); } else if (inputBoard.nPlies == 42) { Debug.Assert(depth == 0); return(Constants.DRAW); } // "Mate" distance pruning alpha = Math.Max(ply - Constants.WIN, alpha); beta = Math.Min(Constants.WIN - (ply + 1), beta); if (alpha >= beta) { return(alpha); } // probe transposition table TTEntry entry = Solve.TranspositionTable.probeTTable(inputBoard.key); // If entry has a flag type, then key will match (probe function performs check), only empty entries will have a key that doesn't match if (entry.flag == Constants.EXACT || entry.flag == Constants.L_BOUND && entry.evaluationScore >= beta || entry.flag == Constants.U_BOUND && entry.evaluationScore <= alpha) { Debug.Assert(entry.key == inputBoard.key && entry.depth == depth); // Only exact and lower bound entries can satisfy this condition and they all have valid moves stored, so don't have to check if move == -1 (only the case with upper bound entries) if (entry.evaluationScore >= beta) { Debug.Assert(entry.move != Constants.NO_MOVE); updateKillers(entry.move, ply); } return(entry.evaluationScore); } // hash move, if entry is exact then code will not be reached and if entry is an upper bound then it will have no move int hashMove = (entry.flag == Constants.L_BOUND && entry.evaluationScore < beta) ? entry.move : Constants.NO_MOVE; int bestScore = -Constants.INF; int movesMade = 0; int numberOfMoves = 0, moveIndex = 0; bool raisedAlpha = false; Move[] moveList = moveGenerator(ply, hashMove, ref numberOfMoves, inputBoard); int bestMove = Constants.NO_MOVE; // loop through all moves while (true) { int move = getNextMove(numberOfMoves, ref moveIndex, moveList); if (move == Constants.NO_MOVE) { break; } inputBoard.MakeMove(move); int score = -solve(Constants.NON_ROOT, inputBoard, ply + 1, -beta, -alpha, depth - 1); inputBoard.UnmakeMove(); nodesVisited++; movesMade++; if (score >= beta) { TTEntry newEntry = new TTEntry(inputBoard.key, Constants.L_BOUND, depth, score, move); Solve.TranspositionTable.storeTTable(inputBoard.key, newEntry); updateKillers(move, ply); updateHistory(depth, ply, move); if (movesMade == 1) { fh1++; } else { fh++; } return(score); } else if (score > bestScore) { bestScore = score; bestMove = move; if (score > alpha) { alpha = score; raisedAlpha = true; } } } // Store in transposition table if (raisedAlpha) { TTEntry newEntry = new TTEntry(inputBoard.key, Constants.EXACT, depth, alpha, bestMove); Solve.TranspositionTable.storeTTable(inputBoard.key, newEntry); } else { TTEntry newEntry = new TTEntry(inputBoard.key, Constants.U_BOUND, depth, bestScore, Constants.NO_MOVE); // no best move Solve.TranspositionTable.storeTTable(inputBoard.key, newEntry); } return(bestScore); }
public static int solve(int nodeType, Position inputBoard, int ply, int alpha, int beta, int depth) { // return score for terminal state if (inputBoard.HasWon(inputBoard.arrayOfBitboard[(inputBoard.nPlies - 1) & 1])) { return -Constants.WIN + ply; } else if (inputBoard.nPlies == 42) { Debug.Assert(depth == 0); return Constants.DRAW; } // "Mate" distance pruning //if (nodeType != Constants.ROOT) { alpha = Math.Max(ply-Constants.WIN, alpha); beta = Math.Min(Constants.WIN - (ply + 1), beta); if (alpha >= beta) { return alpha; } //} // probe transposition table TTEntry entry = Solve.TranspositionTable.probeTTable(inputBoard.key); // If entry has a flag type, then key will match (probe function performs check), only empty entries will have a key that doesn't match if (entry.flag == Constants.EXACT || entry.flag == Constants.L_BOUND && entry.evaluationScore >= beta || entry.flag == Constants.U_BOUND && entry.evaluationScore <= alpha) { Debug.Assert(entry.key == inputBoard.key && entry.depth == depth); // Only exact and lower bound entries can satisfy this condition and they all have valid moves stored, so don't have to check if move == -1 (only the case with upper bound entries) if (entry.evaluationScore >= beta) { Debug.Assert(entry.move != Constants.NO_MOVE); updateKillers(entry.move, ply); } return entry.evaluationScore; } // hash move, if entry is exact then code will not be reached and if entry is an upper bound then it will have no move int hashMove = (entry.flag == Constants.L_BOUND && entry.evaluationScore < beta) ? entry.move : Constants.NO_MOVE; int bestScore = -Constants.INF; int movesMade = 0; bool raisedAlpha = false; movePicker mPicker = new movePicker(inputBoard, ply, hashMove); int bestMove = Constants.NO_MOVE; // loop through all moves while (true) { int move = mPicker.getNextMove(); if (move == Constants.NO_MOVE) { break; } inputBoard.MakeMove(move); int score = -solve(Constants.NON_ROOT, inputBoard, ply + 1, -beta, -alpha, depth - 1); inputBoard.UnmakeMove(); nodesVisited++; movesMade++; if (score >= beta) { TTEntry newEntry = new TTEntry(inputBoard.key, Constants.L_BOUND, depth, score, move); Solve.TranspositionTable.storeTTable(inputBoard.key, newEntry); updateKillers(move, ply); updateHistory(depth, ply, move); if (movesMade == 1) { fh1++; } else { fh++; } return score; } else if (score > bestScore) { bestScore = score; bestMove = move; if (score > alpha) { alpha = score; raisedAlpha = true; } } } // Store in transposition table if (raisedAlpha) { TTEntry newEntry = new TTEntry(inputBoard.key, Constants.EXACT, depth, alpha, bestMove); Solve.TranspositionTable.storeTTable(inputBoard.key, newEntry); } else { TTEntry newEntry = new TTEntry(inputBoard.key, Constants.U_BOUND, depth, bestScore, Constants.NO_MOVE); // no best move Solve.TranspositionTable.storeTTable(inputBoard.key, newEntry); } return bestScore; }