/// <summary> /// Look up a position's hash in the table. /// </summary> /// <param name="hash">The hash to search for.</param> /// <param name="entry">The found entry.</param> /// <returns>True if the lookup was successful and the out-param contains the correct record.</returns> public bool TryGetValue(ulong hash, out TTEntry entry) { entry = table[GetIndex(hash)]; if (null == entry) return false; if (entry.Hash == hash) return true; return false; }
/// <summary> /// Submit an entry to the tablebase. It will decide for itself whether it keeps it or prefers the previous record. /// </summary> public void Write(TTEntry entry) { //CURRENT STRATEGY: "Replace with higher depth" int index = GetIndex(entry.Hash); TTEntry oldEntry = table[index]; if ((null == oldEntry) || (entry.Depth > oldEntry.Depth)) table[index] = entry; }
/// <summary> /// Recursively calculates the value of the current position, up to the given depth. /// </summary> /// <param name="move">The last move. Win is checked in its neighborhood.</param> /// <param name="depth">The remaining available depth.</param> /// <param name="color">The color of the player to move. 1 ~ black, -1 ~ white.</param> /// <param name="alpha">The lower boundary on "interesting" scores.</param> /// <param name="beta">The upper boundary on "interesting" scores.</param> private int negamax(Square move, int depth, int color, int alpha, int beta) { Square localBestMove = move; int origAlpha = alpha; //TT lookup TTEntry ttEntry; if (tt.TryGetValue(currentHash, out ttEntry)) { localBestMove = ttEntry.BestMove; //for ordering purposes, just take the best move regardless of depth. For the rest, depth does matter. if (ttEntry.Depth >= depth) { switch (ttEntry.Flag) { case TTFlag.Exact: return(ttEntry.Score); case TTFlag.Upper: if (ttEntry.Score < beta) { beta = ttEntry.Score; } break; case TTFlag.Lower: if (ttEntry.Score > alpha) { alpha = ttEntry.Score; } break; default: break; } if (alpha >= beta) { return(ttEntry.Score); } } } //if state is terminal, return: if (checkWin(move, color == -1)) { return(-12345678); //someone won, so the score [calculated always for black] is +inf * (previous color). We return (color to move)*(score for black) --> always -inf } if (moveCount >= Gamestate.maxMoves) { return(0); //draw, return 0 } if (depth <= 0) { int result = color * evaluate(); //not a win, not a draw, but reached maximum depth (0 remaining depth), return evaluation return(result); } //otherwise keep searching children int value = -12345678; int candidateValue; foreach (Square child in GenerateShuffledMoves(localBestMove)) { if (stopSearch) { return(value); //do not visit any more children if search is stopped } incrementBoard(child, color == 1); candidateValue = -negamax(child, depth - 1, -color, -beta, -alpha); decrementBoard(child); if (candidateValue > value) { value = candidateValue; localBestMove = child; } if (value > alpha) { alpha = value; } if (alpha >= beta) { break; } } if (stopSearch) { return(value); //do not update TT if search is stopped } //TT update TTFlag flag; if (value <= origAlpha) { flag = TTFlag.Upper; } else if (value >= beta) { flag = TTFlag.Lower; } else { flag = TTFlag.Exact; } ttEntry = new TTEntry(currentHash, localBestMove, depth, value, flag); tt.Write(ttEntry); return(value); }