Exemple #1
0
        /// <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);
        }
Exemple #2
0
        /// <summary>
        /// Check if there's a 5-in-a-row on the current board containing the given square for the given player.
        /// </summary>
        private bool checkWin(Square move, bool black)
        {
            byte color;

            if (black)
            {
                color = 2;
            }
            else
            {
                color = 1;
            }

            int boardX = move.x + 4; //to accomodate board padding
            int boardY = move.y + 4;

            //copypasta & padding usage to optimize compared to how Gamestate checks for win
            //horizontal
            int counter = 0;

            for (int i = -4; i <= 4; i++)
            {
                if (board[boardX + i, boardY] == color)
                {
                    counter++;
                }
                else
                {
                    counter = 0;
                }
                if (counter == 5)
                {
                    return(true);
                }
            }
            //vertical
            counter = 0;
            for (int i = -4; i <= 4; i++)
            {
                if (board[boardX, boardY + i] == color)
                {
                    counter++;
                }
                else
                {
                    counter = 0;
                }
                if (counter == 5)
                {
                    return(true);
                }
            }
            //asc diagonal
            counter = 0;
            for (int i = -4; i <= 4; i++)
            {
                if (board[boardX + i, boardY + i] == color)
                {
                    counter++;
                }
                else
                {
                    counter = 0;
                }
                if (counter == 5)
                {
                    return(true);
                }
            }
            //desc diagonal
            counter = 0;
            for (int i = -4; i <= 4; i++)
            {
                if (board[boardX + i, boardY - i] == color)
                {
                    counter++;
                }
                else
                {
                    counter = 0;
                }
                if (counter == 5)
                {
                    return(true);
                }
            }

            return(false);
        }
Exemple #3
0
 public void UndoMove(Square sq)
 {
     PauseThink();
     decrementBoard(sq);
     ResumeThink();
 }
Exemple #4
0
        /// <summary>
        /// Assigns to "bestMove" the result of a search of fixed max depth and returns its score.
        /// </summary>
        /// <param name="depth">Depth must be greater than 0.</param>
        public int DepthLimitedSearch(int depth)
        {
            //it is like a negamax step but we work with the 'bestMove' variable and disregard terminal state and omit TT writing

            int color;

            if (moveCount % 2 == 0)
            {
                color = 1;
            }
            else
            {
                color = -1;
            }

            int value = -123456789;
            int alpha = -123456789;
            int beta  = 123456789;

            //TT lookup
            TTEntry ttEntry;

            if (tt.TryGetValue(currentHash, out ttEntry))
            {
                if (ttEntry.Depth >= depth)
                {
                    bestMove = ttEntry.BestMove; //as opposed to "inside the negamax", we only rewrite bestMove with a "deeper" recorded value!
                    switch (ttEntry.Flag)
                    {
                    case TTFlag.Exact:
                        return(ttEntry.Score);

                    case TTFlag.Upper:
                        beta = ttEntry.Score;
                        break;

                    case TTFlag.Lower:
                        alpha = ttEntry.Score;
                        break;

                    default:
                        break;
                    }
                }
            }

            int candidateValue;

            foreach (Square child in GenerateShuffledMoves(bestMove))
            {
                incrementBoard(child, color == 1);
                candidateValue = -negamax(child, depth - 1, -color, -beta, -alpha);
                decrementBoard(child);

                if (stopSearch)
                {
                    break;             // do not overwrite bestMove with a result that came from an interrupted branch
                }
                if (candidateValue > value)
                {
                    value = candidateValue;
                    if (value > alpha)
                    {
                        alpha = value;
                    }
                    bestMove = child;
                }
            }

            //first move of the game goes in the middle, there are no 'considered moves' in such case anyway.
            if (moveCount == 0)
            {
                bestMove = new Square(Gamestate.sizeX / 2, Gamestate.sizeY / 2);
                return(0);
            }

            return(value);
        }
Exemple #5
0
        //before every move update, the search must be paused (returned to root node) because otherwise the engine's internal board and/or search algo would crash and burn

        public void DoMove(Square sq)
        {
            PauseThink();
            incrementBoard(sq, moveCount % 2 == 0); //mark the move from outside
            ResumeThink();
        }