コード例 #1
0
        /// <summary>
        /// Recursively search for the best move for the given board
        /// </summary>
        /// <param name="depth">How many recursive iterations you want to take(each iteration is 1 turn of 1 of the players, so steps ahead is depth/2</param>
        /// <param name="tiles">The Tile to iterate over</param>
        /// <param name="initialSequence">Initial sequence of moves, which is used as state to know what sequence </param>
        /// <param name="priority"></param>
        /// <param name="max"></param>
        /// <param name="currentPlayer"></param>
        /// <param name="enemyPlayer"></param>
        /// <returns></returns>
        public MinimaxResult GetBestTurn(BoardTileCollection tiles, IPlayer currentPlayer)
        {
#if DEBUG
            this._amountOfCheckups = 0;
#endif
            IPlayer enemyPlayer = this.Game.GetEnemyPlayer(currentPlayer);
            var     results     = new List <MinimaxResult>();

            //get all possible initial moves for the player
            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);

            Parallel.ForEach(checkerMovePairs, (pair, loopState) => {
                List <MoveSequence> possibleSequences = null;
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)                  //for each initial move of the checker
                {
                    if (move is AttackMove attMove)
                    {
                        //all possible followup move sequences
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else if (move is WalkMove)
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }
                    //in case theres only 1 possible move, we dont need to calculate anything, so cut of.
                    if (checkerMovePairs.Count == 1 && possibleSequences.Count == 1)
                    {
                        var seq = possibleSequences.First();
                        results.Add(new MinimaxResult(seq.Moves, seq.Turn, 0));
                        loopState.Break();
                    }

                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        float prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }
                        //now we need to call search the move tree for the value of the sequence.
                        var sequenceValue = GetValueOfMoveSequence(this.MaxDepth - 1, clonedTiles, prio, enemyPlayer, currentPlayer, false);

                        var result = new MinimaxResult(sequence.Moves, sequence.Turn, sequenceValue);
                        lock (_resultAddLock)
                            results.Add(result);
                    }
                }
            });

#if DEBUG
            Console.WriteLine($"amount of checkups: {this._amountOfCheckups}");
#endif
            return(ChooseBestResult(results));
        }
コード例 #2
0
        public void WinEndsTheSearchForPlayerY()
        {
            HexBoard board = new HexBoard(5);

            PlayToWinInOneMove(board, false);

            Minimax   minimax     = MakeMinimaxForBoard(board);
            const int SearchDepth = 4;

            MinimaxResult bestMove = minimax.DoMinimax(SearchDepth, false);
            Location      win      = new Location(0, 3);

            Assert.AreEqual(win, bestMove.Move, "wrong win location");

            // do: test that locations after 0, 4 aren't even looked at. A win ends the search
            IList <Location> locationsExamined = minimax.DebugDataItems
                                                 .Where(d => d.Lookahead == SearchDepth)
                                                 .Select(d => d.Location).ToList();

            Assert.IsTrue(locationsExamined.Count > 0, "No locations examined");
            Assert.IsTrue(locationsExamined.Contains(win), "Locations examined does not contain win");
            Location unexpected = new Location(4, 4);

            Assert.IsFalse(locationsExamined.Contains(unexpected), "Should not have examined location " + unexpected + " after win");
        }
コード例 #3
0
ファイル: PlayerVsCpu.cs プロジェクト: celosama/ai-minimax
        private void checkMoves(MouseState mouseState)
        {
            MouseState currentMouseState = mouseState;
            Point      mousePosition     = new Point(currentMouseState.X, currentMouseState.Y);

            if (board.CurrentPlayer() == human)
            {
                if (lastMouseState.LeftButton == ButtonState.Pressed && currentMouseState.LeftButton == ButtonState.Released)
                {
                    foreach (KeyValuePair <Point, Rectangle> boundingBox in boundingBoxes)
                    {
                        if (boundingBox.Value.Contains(mousePosition))
                        {
                            board.MakeMove(new Move(boundingBox.Key), human);
                        }
                    }
                }
            }
            else
            {
                MinimaxResult result = Minimax.Do(board, cpu, GameSettings.Difficulty, 0);

                Move move = result.GetMove();

                board.MakeMove(move, cpu);
            }

            currentTurn = board.CurrentPlayer();
        }
コード例 #4
0
        /// <summary>
        /// public wrapper for recursion
        /// try each possible move, find the one with the best score
        /// </summary>
        /// <param name="lookahead">how far to look ahead</param>
        /// <param name="playerX">is this for player x</param>
        /// <returns>The data on the best move location and score</returns>
        public MinimaxResult DoMinimax(int lookahead, bool playerX)
        {
            // set up inital state
            DateTime startTime = DateTime.Now;

            if (lookahead < 1)
            {
                throw new Exception("Invalid lookahead of " + lookahead);
            }

            this.debugDataItems.Clear();

            Occupied player = playerX.ToPlayer();
            int      alpha  = MoveScoreConverter.ConvertWin(player.Opponent(), 0);
            int      beta   = MoveScoreConverter.ConvertWin(player, 0);

            MinimaxResult bestMove = this.ScoreBoard(lookahead, this.ActualBoard, playerX, alpha, beta);

            if (bestMove.Move != Location.Null)
            {
                GoodMoves.AddGoodMove(0, bestMove.Move);
            }

            DateTime endTime = DateTime.Now;

            this.MoveTime = endTime - startTime;

            return(bestMove);
        }
コード例 #5
0
        private void DoMove()
        {
            Minimax       hexPlayer     = new Minimax(this.hexGame.Board, this.hexGame.GoodMoves, this.MakeCandiateMovesFinder());
            MinimaxResult minimaxResult = hexPlayer.DoMinimax(4, this.MoveIsPlayerX());

            this.PlayLocation = this.GetBestMove(minimaxResult);
            this.MoveTime     = hexPlayer.MoveTime;

            this.CallCompletedAction();
        }
コード例 #6
0
        private static void DoTestTestNoLingering2Win(Minimax minimax, int depth)
        {
            MinimaxResult bestMove = minimax.DoMinimax(depth, true);

            // play here to win
            Location expectedMove = new Location(2, 2);

            Assert.AreEqual(expectedMove, bestMove.Move, "Wrong best move at depth " + depth);
            AssertWinner(bestMove.Score, Occupied.PlayerX);
        }
コード例 #7
0
    private MinimaxResult MinimaxStep(BoardData p_board, Players p_player, int p_currentDepth)
    {
        GameResults t_gameResult = p_board.GetGameResult();

        if (t_gameResult != GameResults.Unfinished)
        {
            int t_score = t_gameResult == GameResults.Win ? MAX_SCORE : -MAX_SCORE;
            return(new MinimaxResult(t_score, null));
        }
        else if (p_currentDepth > MAX_DEPTH)
        {
            return(m_boardEvaluator.EvaluateBoard(p_board, p_player));
        }

        Move t_bestMove  = null;
        int  t_bestScore = 0;

        if (p_board.CurrentPlayer == Players.Human)
        {
            t_bestScore = -INFINITE;
        }
        else
        {
            t_bestScore = INFINITE;
        }

        List <Move> t_moves = new List <Move>();

        p_board.GetMoves(t_moves);

        foreach (Move t_move in t_moves)
        {
            BoardData     t_newBoard = p_board.GetNewBoard(t_move);
            MinimaxResult t_result   = MinimaxStep(t_newBoard, p_player, p_currentDepth + 1);

            if (p_board.CurrentPlayer == Players.Human)
            {
                if (t_bestScore < t_result.Score)
                {
                    t_bestScore = t_result.Score;
                    t_bestMove  = t_move;
                }
            }
            else
            {
                if (t_result.Score < t_bestScore)
                {
                    t_bestScore = t_result.Score;
                    t_bestMove  = t_move;
                }
            }
        }

        return(new MinimaxResult(t_bestScore, t_bestMove));
    }
コード例 #8
0
        public void GoodMoveAtLevel3()
        {
            HexBoard board = new HexBoard(6);

            PlayToWinInThreeMoves(board);

            Minimax minimax = MakeMinimaxForBoard(board);

            MinimaxResult bestMove = minimax.DoMinimax(3, true);
            Location      win      = new Location(1, 3);

            Assert.AreEqual(win, bestMove.Move, "Wrong play location");
        }
コード例 #9
0
ファイル: AlphaBeta.cs プロジェクト: LuukEbenau/SacaCheckers
        /// <summary>
        /// Recursively search for the best move for the given board
        /// </summary>
        /// <param name="depth">How many recursive iterations you want to take(each iteration is 1 turn of 1 of the players, so steps ahead is depth/2</param>
        /// <param name="tiles">The Tile to iterate over</param>
        /// <param name="initialSequence">Initial sequence of moves, which is used as state to know what sequence </param>
        /// <param name="priority"></param>
        /// <param name="max"></param>
        /// <param name="currentPlayer"></param>
        /// <param name="enemyPlayer"></param>
        /// <returns></returns>
        public MinimaxResult GetBestTurn(BoardTileCollection tiles, IPlayer currentPlayer)
        {
            IPlayer enemyPlayer = this.Game.GetEnemyPlayer(currentPlayer);
            var     results     = new List <MinimaxResult>();

            //get all possible initial moves for the player
            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);

            //loop variables

            Parallel.ForEach(checkerMovePairs, (pair) => {
                //foreach(var pair in checkerMovePairs) { //for each checker
                List <MoveSequence> possibleSequences = null;
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)                  //for each initial move of the checker
                {
                    if (move is AttackMove attMove)
                    {
                        //all possible followup move sequences
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else if (move is WalkMove)
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }
                    //Parallel.ForEach(possibleSequences, (sequence) => { // Not useful, its max 2 moves
                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        float prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }

                        //now we need to call the same method from the enemy perception again
                        var sequenceValue = GetBestMoveSequence(this.MaxDepth, clonedTiles, prio, enemyPlayer, currentPlayer);

                        var result = new MinimaxResult(sequence.Moves, sequence.Turn, sequenceValue);
                        results.Add(result);
                    }
                    //});
                }
            });
            //}
            return(ChooseBestResult(results));
        }
コード例 #10
0
        public void TestCalculateMove2MinimaxPlayerY()
        {
            HexBoard board = new HexBoard(3);

            PlayTwoMoves(board);

            Minimax       minimax            = MakeMinimaxForBoard(board);
            MinimaxResult secondPlayerResult = minimax.DoMinimax(2, false);

            Location expectedPlay = new Location(0, 2);

            Assert.AreEqual(expectedPlay, secondPlayerResult.Move, "Wrong play location");
            Assert.IsFalse(MoveScoreConverter.IsWin(secondPlayerResult.Score));
        }
コード例 #11
0
        public void TestCalculateMove3PlayerX()
        {
            HexBoard board = new HexBoard(5);

            PlayFourMoves(board);

            Minimax minimax = MakeMinimaxForBoard(board);

            MinimaxResult firstPlayerResult = minimax.DoMinimax(4, true);

            Location firstPlayerExpectedMove = new Location(2, 1);

            Assert.AreEqual(firstPlayerExpectedMove, firstPlayerResult.Move, "Wrong first player location");
        }
コード例 #12
0
        public void TestCalculateMoveLookahead1Player1()
        {
            HexBoard board = new HexBoard(3);

            PlayFourMoves(board);

            Minimax       minimax           = MakeMinimaxForBoard(board);
            MinimaxResult firstPlayerResult = minimax.DoMinimax(1, true);

            Location expectedMove = new Location(1, 1);

            Assert.AreEqual(expectedMove, firstPlayerResult.Move, "playerOneBestMove");
            AssertWinner(firstPlayerResult.Score, Occupied.PlayerX);
        }
コード例 #13
0
        private static void TestBestMove(HexGame game, int level, Location expectedBestMove)
        {
            Minimax       hexPlayer = new Minimax(game.Board, game.GoodMoves, new CandidateMovesAll());
            MinimaxResult bestMove  = hexPlayer.DoMinimax(level, true);

            // test the location of the move
            Assert.AreEqual(expectedBestMove, bestMove.Move, "Wrong move at level " + level);

            // test the expected score
            if (level >= 3)
            {
                Assert.AreEqual(Occupied.PlayerX, MoveScoreConverter.Winner(bestMove.Score));
            }
        }
コード例 #14
0
        public void TestCalculateMoveLookahead1Player2()
        {
            HexBoard board = new HexBoard(3);

            PlayFourMoves(board);

            Minimax       minimax            = MakeMinimaxForBoard(board);
            MinimaxResult secondPlayerResult = minimax.DoMinimax(1, false);

            Location expectedMove = new Location(1, 1);

            Assert.AreEqual(expectedMove, secondPlayerResult.Move, "playerTwoBestMove");
            AssertWinner(secondPlayerResult.Score, Occupied.PlayerY);
        }
コード例 #15
0
        public void TestCalculateMove3MinimaxPlayerX()
        {
            HexBoard board = new HexBoard(3);

            PlayTwoMoves(board);

            Minimax       minimax           = MakeMinimaxForBoard(board);
            MinimaxResult firstPlayerResult = minimax.DoMinimax(3, true);
            Location      bestMoveLocation  = firstPlayerResult.Move;
            int           moveScore         = firstPlayerResult.Score;

            Location exectedWin = new Location(0, 2);

            Assert.AreEqual(exectedWin, bestMoveLocation, "Wrong win location");
            AssertWinner(moveScore, Occupied.PlayerX);
        }
コード例 #16
0
        public void TestCalculateMove3MinimaxPlayerY()
        {
            HexBoard board = new HexBoard(3);

            PlayTwoMoves(board);

            Minimax       minimax                  = MakeMinimaxForBoard(board);
            MinimaxResult secondPlayerResult       = minimax.DoMinimax(3, false);
            Location      secondPlayerMoveLocation = secondPlayerResult.Move;
            int           moveScore                = secondPlayerResult.Score;

            Location exectedWin = new Location(0, 2);

            Assert.AreEqual(exectedWin, secondPlayerMoveLocation, "Wrong second player location");
            AssertWinner(moveScore, Occupied.PlayerY);
        }
コード例 #17
0
        public void RightMoveAtLevel5PlayerY()
        {
            HexBoard board = new HexBoard(6);

            PlayToWinInThreeMoves(board);

            Minimax minimax = MakeMinimaxForBoard(board);

            MinimaxResult bestMove = minimax.DoMinimax(5, false);

            List <Location> playerYWinningLocations = new List <Location>
            {
                new Location(1, 3),
                new Location(2, 2)
            };

            Assert.IsTrue(playerYWinningLocations.Contains(bestMove.Move), "Wrong play location");
        }
コード例 #18
0
        public void TestMinimax3()
        {
            HexBoard board   = new HexBoard(5);
            Minimax  minimax = MakeMinimaxForBoard(board);

            /*
             *  on a 5 * 5 board, red(playerx) has 3, 0 and 1, 4
             *  needs to play 2,2 to win - should know this at look ahead 5
             */

            board.PlayMove(3, 0, true);
            board.PlayMove(1, 4, true);

            MinimaxResult bestMove     = minimax.DoMinimax(5, true);
            Location      expectedMove = new Location(2, 2);

            Assert.IsTrue(MoveScoreConverter.IsWin(bestMove.Score), "No win " + bestMove.Score);
            Assert.AreEqual(expectedMove, bestMove.Move, "Wrong expected move");
        }
コード例 #19
0
        public void TestMinimax4()
        {
            HexBoard board   = new HexBoard(5);
            Minimax  minimax = MakeMinimaxForBoard(board);

            /*
             *  on a 5 * 5 board, red(playerx) has 3, 0 and 1, 4  and 2,2
             *      PlayerX has won, even if PlayerY goes next
             *       should know this at look ahead 5
             */

            board.PlayMove(3, 0, true);
            board.PlayMove(1, 4, true);
            board.PlayMove(2, 2, true);

            MinimaxResult bestMove = minimax.DoMinimax(4, false);

            AssertWinner(bestMove.Score, Occupied.PlayerX);
        }
コード例 #20
0
        private Location GetBestMove(MinimaxResult playResult)
        {
            Location result = playResult.Move;

            int moveScore = playResult.Score;

            this.IsGameWon = MoveScoreConverter.IsWin(moveScore) && MoveScoreConverter.WinDepth(moveScore) == 1;

            Occupied opponent   = (!this.hexGame.PlayerX).ToPlayer();
            bool     losingMove = MoveScoreConverter.Winner(playResult.Score) == opponent;

            if (losingMove)
            {
                Location losingLocation = this.MakeLosingMove();
                if (losingLocation != Location.Null)
                {
                    result = losingLocation;
                }
            }

            return(result);
        }
コード例 #21
0
        public void TestCalculateMove3PlayerY()
        {
            HexBoard board = new HexBoard(5);

            PlayFourMoves(board);

            Minimax minimax = MakeMinimaxForBoard(board);

            // test score at this point
            PathLengthLoop pathLength  = new PathLengthLoop(board);
            int            playerScore = pathLength.PlayerScore(true);

            Assert.AreEqual(3, playerScore);

            playerScore = pathLength.PlayerScore(false);
            Assert.AreEqual(3, playerScore);

            MinimaxResult secondPlayerResult = minimax.DoMinimax(4, false);

            Location secondPlayerExpectedMove = new Location(1, 2);

            Assert.AreEqual(secondPlayerExpectedMove, secondPlayerResult.Move, "Wrong second player location");
        }
コード例 #22
0
ファイル: Minimax.cs プロジェクト: LuukEbenau/SacaCheckers
        /// <summary>
        /// Recursively search for the best move for the given board
        /// </summary>
        /// <param name="depth">How many recursive iterations you want to take(each iteration is 1 turn of 1 of the players, so steps ahead is depth/2</param>
        /// <param name="tiles">The Tile to iterate over</param>
        /// <param name="initialSequence">Initial sequence of moves, which is used as state to know what sequence </param>
        /// <param name="priority"></param>
        /// <param name="max"></param>
        /// <param name="currentPlayer"></param>
        /// <param name="enemyPlayer"></param>
        /// <returns></returns>
        public MinimaxResult GetBestTurn(BoardTileCollection tiles, IPlayer currentPlayer)
        {
            IPlayer enemyPlayer = this.Game.GetEnemyPlayer(currentPlayer);
            var     results     = new List <MinimaxResult>();

            //get all possible initial moves for the player
            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);
            //loop variables
            List <MoveSequence> possibleSequences = null;

            foreach (var pair in checkerMovePairs)              //for each checker
            {
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)                  //for each initial move of the checker
                {
                    if (move is AttackMove attMove)
                    {
                        //all possible followup move sequences
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else if (move is WalkMove)
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }

                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        float prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }

                        //now we need to call the same method from the enemy perception again
                        var sequenceValue = GetBestMoveSequence(this.MaxDepth, clonedTiles, prio, enemyPlayer, currentPlayer);

                        var result = new MinimaxResult(sequence.Moves, sequence.Turn, sequenceValue);
                        results.Add(result);
                    }
                }
            }

#if DEBUG
            var str = "";
            foreach (var result in results)
            {
                str += $"move: {result.Moves.First().StartLocation} => {result.Moves.Last().EndLocation}, value:{result.TurnValue}\n";
            }
            Console.WriteLine($"Turn: {this.Game.Turn}");
            Console.WriteLine(str);
#endif

            if (results.Count() == 0)
            {
                return(null);
            }
            IEnumerable <MinimaxResult> bestResults = ((this.MaxDepth & 0b1) == 0b1) ? results.Where(res => res.TurnValue == results.Max(r => r.TurnValue))
                                : results.Where(res => res.TurnValue == results.Min(r => r.TurnValue));

            var random       = new Random();
            var ind          = random.Next(0, bestResults.Count());
            var chosenResult = bestResults.ElementAtOrDefault(ind);
            return(chosenResult);
        }
コード例 #23
0
        /// <summary>
        /// private recursive worker - does the minimax algorithm
        /// </summary>
        /// <param name="lookahead">the current ply, counts down to zero</param>
        /// <param name="stateBoard">the current board</param>
        /// <param name="isPlayerX">player X or player Y</param>
        /// <param name="alpha">alpha value used in alpha-beta pruning</param>
        /// <param name="beta">beta value used in alpha-beta pruning</param>
        /// <returns>the score of the board and best move location</returns>
        private MinimaxResult ScoreBoard(
            int lookahead,
            HexBoard stateBoard,
            bool isPlayerX,
            int alpha,
            int beta)
        {
            this.CountBoards++;

            MinimaxResult bestResult = null;
            Location      cutoffMove = Location.Null;

            var possibleMoves = this.candidateMovesFinder.CandidateMoves(stateBoard, lookahead);

            foreach (Location move in possibleMoves)
            {
                // end on null loc
                if (move.IsNull())
                {
                    break;
                }

                if (this.GenerateDebugData)
                {
                    this.AddDebugDataItem(lookahead, move, isPlayerX, alpha, beta);
                }

                // make a speculative board, like the current, but with this cell played
                HexBoard testBoard = this.boardCache.GetBoard();
                testBoard.CopyStateFrom(stateBoard);

                testBoard.PlayMove(move, isPlayerX);
                MinimaxResult moveScore;

                PathLengthBase staticAnalysis = this.pathLengthFactory.CreatePathLength(testBoard);
                int            situationScore = staticAnalysis.SituationScore();

                if (lookahead <= 1)
                {
                    // we have reached the limits of lookahead - return the situation score
                    moveScore = new MinimaxResult(situationScore);
                }
                else if (MoveScoreConverter.IsWin(situationScore))
                {
                    // stop - someone has won
                    moveScore = new MinimaxResult(situationScore);
                }
                else
                {
                    // recurse
                    moveScore = this.ScoreBoard(lookahead - 1, testBoard, !isPlayerX, beta, alpha);
                    moveScore.MoveWins();
                }

                moveScore.Move = move;

                this.boardCache.Release(testBoard);

                // higher scores are good for player x, lower scores for player y
                if (bestResult == null || MoveScoreConverter.IsBetterFor(moveScore.Score, bestResult.Score, isPlayerX))
                {
                    bestResult = new MinimaxResult(move, moveScore);
                }

                // do the alpha-beta pruning
                alpha = CheckAlpha(alpha, moveScore.Score, isPlayerX);

                if (IsAlphaBetaCutoff(isPlayerX, alpha, beta))
                {
                    cutoffMove       = move;
                    bestResult.Score = alpha;
                    break;
                }

                // end a-b pruning
            }

            if (bestResult != null)
            {
                GoodMoves.AddGoodMove(lookahead, bestResult.Move);
            }

            if (cutoffMove != Location.Null)
            {
                GoodMoves.AddGoodMove(lookahead, cutoffMove);
            }

            return(bestResult);
        }