예제 #1
0
    private ScoredMove minimax(int depth, int alpha, int beta, Board board, char player)
    {
        ScoredMove bestMove = resetBestScore(player);

        if (board.hasFinished() || depth == 0)
        {
            return(new ScoredMove(score(board, depth), bestMove.score, this));
        }
        foreach (int position in board.availablePositions())
        {
            Board      newBoard = board.update(position, player);
            ScoredMove score    = minimax(depth - 1, alpha, beta, newBoard, player == 'x' ? 'o' : 'x');
            bestMove = updateScore(player, bestMove, position, score);
            if (player == mark)
            {
                alpha = Math.Max(alpha, bestMove.score);
            }
            else
            {
                beta = Math.Min(beta, bestMove.score);
            }
            if (alpha >= beta)
            {
                break;
            }
        }
        return(bestMove);
    }
예제 #2
0
 private ScoredMove updateScore(char player, ScoredMove currentBestMove, int position, ScoredMove score)
 {
     if (score.isBetter(currentBestMove, player))
     {
         currentBestMove = new ScoredMove(score.score, position, this);
     }
     return(currentBestMove);
 }
예제 #3
0
파일: MiniMax.cs 프로젝트: jpstam/ruler
    public static Vector2 GetBestMove(bool player1, GameState gs, StrategyHandler sh, ScoreFunction sf)
    {
        // First let the strategy handler determine all possible move options for this round
        List <Vector2> options = sh.ComputeOptions(gs, player1);

        // Convert the options to scored moves
        List <ScoredMove> scoredMoves = options.ConvertAll(o => new ScoredMove(o));

        foreach (ScoredMove scoredMove in scoredMoves)
        {
            // The depth specified is the number of moves after the first next move
            float value = DoMiniMax(scoredMove.Move, scoredMove, 1, !player1, gs, sh, sf);
            // Debug.Log("==============================> score: " + value);
            // Debug.Log("-----> Move: " + scoredMove.Move);
            scoredMove.SetScore(value);
        }

        float bestScore;

        if (player1)
        {
            bestScore = float.MinValue;
        }
        else
        {
            bestScore = float.MaxValue;
        }
        ScoredMove bestMove = scoredMoves[0];

        foreach (ScoredMove scoredMove in scoredMoves)
        {
            if (player1)
            {
                // Maximize the score if minimax is used for player 1
                if (scoredMove.Score > bestScore)
                {
                    bestScore = scoredMove.Score;
                    bestMove  = scoredMove;
                }
            }
            else
            {
                // Minimize the score if minimax is used for player 2
                if (scoredMove.Score < bestScore)
                {
                    bestScore = scoredMove.Score;
                    bestMove  = scoredMove;
                }
            }
        }

        Vector2 move = bestMove.Move;

        storedOptions = options;
        return(move);
    }
예제 #4
0
        private ScoredMove getStrongestMove(Piece[,] squares, Piece turn, int depth = 0)
        {
            List <Point> legalMoves = getLegalMoves(squares);
            bool         gameOver   = _gameIsOver(squares);

            if (legalMoves.Count == 0 || gameOver)
            {
                int score = evaluateScore(squares) - depth;
                return(new ScoredMove()
                {
                    Score = score
                });
            }

            ScoredMove bestMove = new ScoredMove()
            {
                Score = (turn == _computerPiece ? -10000 : 10000)
            };

            foreach (Point move in legalMoves)
            {
                squares[move.y, move.x] = turn;
                ScoredMove nextMove = getStrongestMove(squares, nextTurn(turn), depth + 1);
                if (
                    (turn == _computerPiece && nextMove.Score > bestMove.Score) ||
                    (turn == _humanPiece && nextMove.Score < bestMove.Score)
                    )
                {
                    bestMove = new ScoredMove()
                    {
                        Score = nextMove.Score, Square = move
                    };
                }
                squares[move.y, move.x] = Piece.Empty;
            }
            return(bestMove);
        }
예제 #5
0
파일: MiniMax.cs 프로젝트: jpstam/ruler
    private static float DoMiniMax(Vector2 nextMove, ScoredMove scoredMove, int depth, bool maximizingPlayer, GameState gs, StrategyHandler sh, ScoreFunction sf)
    {
        // Create a copy of the gamestate and apply the move option
        GameState gsTemp = gs.Copy();

        gsTemp.AddPoint(nextMove, !maximizingPlayer);

        if (depth <= 0)
        {
            return(sf.ComputeScore(nextMove, gsTemp));
        }

        List <Vector2> options = sh.ComputeOptions(gsTemp, maximizingPlayer);

        if (maximizingPlayer)
        {
            float max = float.MinValue;
            foreach (Vector2 option in options)
            {
                // Execute minimax on the new game state
                float value = DoMiniMax(option, scoredMove, depth - 1, false, gsTemp, sh, sf);
                max = Mathf.Max(max, value);
            }
            return(max);
        }
        else
        {
            float min = float.MaxValue;
            foreach (Vector2 option in options)
            {
                // Execute minimax on the new game state
                float value = DoMiniMax(option, scoredMove, depth - 1, true, gsTemp, sh, sf);
                min = Mathf.Min(min, value);
            }
            return(min);
        }
    }
예제 #6
0
 public bool isBetter(ScoredMove scoredMove, char player)
 {
     return((player == computerPlayer.mark && score > scoredMove.score) || (player != computerPlayer.mark && score < scoredMove.score));
 }
예제 #7
0
        public (Move move, Statistics statistics) Search(Position position, ITimeStrategy timeStrategy, Statistics.PrintInfoDelegate printInfoDelegate)
        {
            // setup
            _timeStrategy = timeStrategy;
            _enteredCount = 0;
            _statistics   = new Statistics();
            _statistics.Timer.Start();
            _statistics.SideCalculating = position.SideToMove;
            _pvTable = new TriangularPVTable(); // TODO: should we be passing this in instead?
            _qSearch.StartSearch(_timeStrategy, _pvTable, _statistics);

            Move bestMove = Move.Null;
            var  moveList = new List <Move>();

            _moveGenerator.Generate(moveList, position);

            var cachedPositionObject = new Position();

            if (!_moveGenerator.OnlyLegalMoves)
            {
                // remove all illegal moves
                for (int i = moveList.Count - 1; i >= 0; i--)
                {
                    var move            = moveList[i];
                    var testingPosition = Position.MakeMove(cachedPositionObject, move, position);
                    if (testingPosition.MovedIntoCheck())
                    {
                        moveList.QuickRemoveAt(i);
                    }
                }
            }

            var scoredMoveList = new List <ScoredMove>();

            foreach (var move in moveList)
            {
                scoredMoveList.Add(new ScoredMove(move, 0));
            }

            // TODO: instead of looping here, why don't we only loop in InnerSearch and get the best value from the PV table?
            // That would simplify things a lot.
            // However, if we have aspiration windows and we get a beta cutoff, how do we retrieve the best move? Or is that even required?
            // The PV table would probably need to handle that case.
            for (int depth = 1;; depth++)
            {
                _statistics.NormalNonLeafNodes++;

                Score alpha = Score.MinValue;
                Score beta  = Score.MaxValue;

                for (int i = 0; i < scoredMoveList.Count; i++)
                {
                    var move = scoredMoveList[i].Move;

                    var nextPosition = Position.MakeMove(cachedPositionObject, move, position);
                    _pvTable.Add(move, 0);

                    _statistics.CurrentDepth = depth;
                    var nextEval = -InnerSearch(nextPosition, depth - 1, false, -beta, -alpha, 1);
                    if (nextEval == alpha)
                    {
                        nextEval -= 1;
                    }

                    scoredMoveList[i] = new ScoredMove(move, nextEval);

                    if (timeStrategy.ShouldStop(_statistics))
                    {
                        _statistics.Timer.Stop();
                        return(bestMove, _statistics);
                    }

                    if (nextEval > alpha)
                    {
                        alpha    = nextEval;
                        bestMove = move; // this is safe, because we search the best move from last pass first in the next pass
                        _pvTable.Commit(0);
                        _statistics.BestLine = _pvTable.GetBestLine();
                        if (position.SideToMove == Color.White)
                        {
                            _statistics.CurrentScore = alpha;
                        }
                        else
                        {
                            _statistics.CurrentScore = -alpha;
                        }

                        printInfoDelegate(_statistics);
                    }
                }

                // if we don't do this, we'll never return from a terminal position (with no moves)
                if (timeStrategy.ShouldStop(_statistics))
                {
                    _statistics.Timer.Stop();
                    return(bestMove, _statistics);
                }

                // sort the moves for next pass
                scoredMoveList.Sort((m1, m2) => (int)(m2.Score - m1.Score));
            }
        }