예제 #1
0
    public static void AIMinMaxSearchAsyncBegin(this IGameBoardState gbs, int searchLevels, Player.PlayerNumber currentPlayer)
    {
        if (jobStatus != AIMinMaxJobStatus.None)
        {
            throw new System.Exception("Only 1 async job should run at a time. This must be enforced in the game logic.");
        }

        jobStatus      = AIMinMaxJobStatus.Started;
        LevelsSearched = 0;
        StatesSearched = 0;
        MovesSearched  = 0;
        Debug.Log("AI Thread Started");

        //clone gameboardstate to ensure it is not a MonoBehavior - we need to pass it to a new thread
        IGameBoardState gbs_clone = gbs.Clone();

        jobThread = new Thread(() =>
        {
            ///must run at least one level to complete
            jobResult = gbs_clone.AIMinMaxSearch(1, currentPlayer);
            LevelsSearched++;
            //code is in an iterative deepening pattern, but I is initialized to the deepest level for testing
            //usually i should start at 2 and iterate forward
            for (int i = searchLevels; i <= searchLevels; i++)
            {
                AIMinMaxResult res = gbs_clone.AIMinMaxSearch(i, currentPlayer);

                if (jobStatus == AIMinMaxJobStatus.StopRequested)
                {
                    break;
                }

                LevelsSearched++;
                jobResult = res;
            }

            lock (jobLock)
            {
                jobStatus = AIMinMaxJobStatus.Finished;
                Debug.Log("AI Thread Finished");
            }
        });
        jobThread.IsBackground = true;
        jobThread.Start();
    }
예제 #2
0
    private IEnumerator MovePieceEvent()
    {
        AIMinMaxResult aiResult = null;
        Move           nextMove = null;

        yield return(new WaitForSeconds(1f));

        //render the gameboard to the 2d representation
        uiController.RenderBoard(gameBoard);

        while (true)
        {
            foreach (Player p in players)
            {
                currentPlayer             = p;
                currentPlayer.playerState = Player.PlayerState.Thinking;

                StartCoroutine(uiController.FadeText(uiController.PlayerTurnText, "Go " + currentPlayer.playerNumber.ToString() + "!", currentPlayer.PieceTint));

                if (currentPlayer.playerType == Player.PlayerType.Human)
                {
                    while (currentPlayer.playerState == Player.PlayerState.Thinking)
                    {
                        yield return(new WaitForSeconds(MoveTime));

                        if (currentPlayer.playerType == Player.PlayerType.Computer)
                        {
                            break;
                        }
                    }

                    if (currentPlayer.playerType == Player.PlayerType.Human)
                    {
                        nextMove = currentPlayer.selectedMove;
                    }
                }

                if (currentPlayer.playerType == Player.PlayerType.Computer)
                {
                    //start thinking asyncronously
                    gameBoard.AIMinMaxSearchAsyncBegin(currentPlayer.AIThoughtDepth + 1, currentPlayer.playerNumber);

                    while (AIMinMax.jobStatus == AIMinMaxJobStatus.Started || AIMinMax.jobStatus == AIMinMaxJobStatus.StopRequested)
                    {
                        yield return(new WaitForSeconds(MoveTime));

                        if (AIMinMax.jobStatus == AIMinMaxJobStatus.Started && AIMinMax.StatesSearched > MaxStatesToSearch)
                        {
                            gameBoard.AiMinMaxSearchAsyncStopRequest();
                        }
                    }
                    //finish thinking and get the move
                    aiResult   = gameBoard.AIMinMaxSearchAsyncEnd();
                    lastResult = aiResult;
                    nextMove   = aiResult.Move;
                    gameBoard.GetPiece(nextMove.piece).ZoomToPiece();
                    yield return(new WaitForSeconds(1f));

                    currentPlayer.selectedMove = aiResult.Move;
                    currentPlayer.playerState  = Player.PlayerState.Moving;
                }

                //this is the best place to stop if the game should be paused
                while (Paused)
                {
                    yield return(new WaitForSeconds(MoveTime));
                }

                Piece movedPiece = gameBoard.GetPiece(nextMove.piece);
                gameBoard.Move(nextMove);
                movedPiece.ZoomToPiece(true);
                //render the gameboard to the 2d representation
                uiController.RenderBoard(gameBoard);

                yield return(new WaitForSeconds(2f));


                currentPlayer.playerState = Player.PlayerState.Waiting;

                if (CheckGameOver())
                {
                    yield break;
                }
            }
        }
    }
예제 #3
0
    /// <summary>
    /// Recursive alpha beta pruning minimax search strategy.
    /// </summary>
    /// <param name="gbs">The Game Board State.</param>
    /// <param name="searchLevels">The number of levels to search (depth)</param>
    /// <param name="currentPlayer">The current player's turn.</param>
    /// <param name="BestMove">The best move found during the search</param>
    /// <param name="alpha">Starting alpha value.</param>
    /// <param name="beta">Starting beta value.</param>
    /// <returns></returns>
    public static AIMinMaxResult AIMinMaxSearch(this IGameBoardState gbs, int searchLevels, Player.PlayerNumber currentPlayer, bool QuiescenceSearch = false, double alpha = -1.0, double beta = 1.0)
    {
        Move BestMove = null;

        double checkWinner = gbs.CheckWinner();

        //cutoff for search (recursive base cases)
        if (checkWinner == -1.0)
        {
            return(new AIMinMaxResult(BestMove, -1.0, 1));
        }

        if (checkWinner == 1.0)
        {
            return(new AIMinMaxResult(BestMove, 1.0, 1));
        }

        if (searchLevels == 0)
        {
            return(new AIMinMaxResult(BestMove, gbs.CalculateUtility(), 1));
        }

        AIMinMaxResult result         = null;
        long           statesSearched = 0;
        //iterate by looking at all possible moves for each piece
        List <IPieceState> pieces = gbs.GetAlivePieces().Where(s => s.GetPlayer() == currentPlayer).Select(s => s).ToList();

        foreach (IPieceState piece in pieces.Shuffle())
        {
            List <Move> moves;
            if (QuiescenceSearch)
            {
                moves = MoveGenerator.GetCaptureMoves(gbs, piece);
            }
            else
            {
                moves = MoveGenerator.GetMoves(gbs, piece);
            }

            MovesSearched += moves.Count;

            //perform each move on a cloned board and search clone recursively, swapping players each turn
            foreach (Move move in moves.Shuffle())
            {
                IGameBoardState clone = gbs.Clone();
                clone.Move(move.piece, move.space);

                if (currentPlayer == Player.PlayerNumber.Player1)
                {
                    result          = clone.AIMinMaxSearch(searchLevels - 1, Player.PlayerNumber.Player2, true, alpha, beta);
                    statesSearched += result.TotalStatesSearched;
                    if (statesSearched > StatesSearched)
                    {
                        StatesSearched = statesSearched;
                    }
                    if (result.AlphaBeta > alpha)
                    {
                        alpha    = result.AlphaBeta;
                        BestMove = move;
                    }

                    //beta cut off
                    if (beta <= alpha)
                    {
                        break;
                    }
                }
                else /* (currentPlayer == Player.PlayerNumber.Player2) */
                {
                    result          = clone.AIMinMaxSearch(searchLevels - 1, Player.PlayerNumber.Player1, true, alpha, beta);
                    statesSearched += result.TotalStatesSearched;
                    if (statesSearched > StatesSearched)
                    {
                        StatesSearched = statesSearched;
                    }
                    if (result.AlphaBeta < beta)
                    {
                        beta     = result.AlphaBeta;
                        BestMove = move;
                    }

                    //alpha cut off
                    if (beta <= alpha)
                    {
                        break;
                    }
                }

                if (jobStatus == AIMinMaxJobStatus.StopRequested && LevelsSearched > 0)
                {
                    searchLevels = 1;
                }
            }

            if (jobStatus == AIMinMaxJobStatus.StopRequested && LevelsSearched > 0)
            {
                searchLevels = 1;
            }
        }

        //no moves found, treat as a base case
        if (BestMove == null)
        {
            return(new AIMinMaxResult(BestMove, gbs.CalculateUtility(), 1));
        }

        //Create a result and return it
        return(new AIMinMaxResult(BestMove, result.AlphaBeta, statesSearched));
    }