Exemplo n.º 1
0
    private int Minimize(CheckersNode node, int depth)
    {
        if (depth == 0)
        {
            return(node.GetScore());
        }

        GenerateChildNodes(node);
        for (int i = 0; i < node.children.Count; i++)
        {
            node.UpdateScore(false, Maximize(node.children[i], depth - 1));
        }

        return(node.GetScore());
    }
Exemplo n.º 2
0
    //Reference:https://www.youtube.com/watch?v=zp3VMe0Jpf8
    private CheckersNode RunMinimax(int player)
    {
        CheckersNode root = null;

        if (player == 0)
        {
            root = new CheckersNode(size, player, null, player1Tokens, player2Tokens);
        }
        else
        {
            root = new CheckersNode(size, player, null, player2Tokens, player1Tokens);
        }

        root.BoardState = mainBoard;
        int depth = maxMinimaxDepth;

        Maximize(root, depth);
        return(root.GetMaxChildNode());
    }
Exemplo n.º 3
0
    private void AddMoveChildNode(Token token, int[,] board, int x, int y, Vector3 position, CheckersNode node, List <Token> opponentTokens, List <Token> playerTokens, bool isJump)
    {
        int originalX = token.xPosition;
        int originalY = token.yPosition;

        board[originalX, originalY] = -1;
        board[x, y] = token.player;
        token.gameObject.transform.position = position;

        CheckersNode childNode = new CheckersNode(size, (token.player + 1) % 2, node, opponentTokens, playerTokens);

        childNode.BoardState = board;
        childNode.xStart     = originalX;
        childNode.yStart     = originalY;
        childNode.xEnd       = x;
        childNode.yEnd       = y;
        childNode.isJump     = isJump;

        board[originalX, originalY] = token.player;
        board[x, y] = -1;
        token.gameObject.transform.position = new Vector3(originalX, originalY, -1.0f);

        node.children.Add(childNode);
    }
Exemplo n.º 4
0
    private void GenerateChildNodes(CheckersNode node)
    {
        int[,] board = node.BoardState;
        List <Token> playerTokens = new List <Token>();

        for (int i = 0; i < node.playerTokens.Count; i++)
        {
            Token token = new Token(node.playerTokens[i]);
            playerTokens.Add(token);
        }

        List <Token> opponentTokens = new List <Token>();

        for (int i = 0; i < node.opponentTokens.Count; i++)
        {
            Token token = new Token(node.opponentTokens[i]);
            opponentTokens.Add(token);
        }

        bool hasJumped = false;

        //Find all possible moveable token positions
        for (int i = 0; i < playerTokens.Count; i++)
        {
            Token token = playerTokens[i];
            if (token.IsAlive)
            {
                //Check jumps
                int     x        = token.xPosition - 2;
                int     y        = token.yPosition + (token.player == 0 ? 2 : -2);
                Vector3 position = new Vector3(x, y);
                if (IsValidJumpPosition(position, token, board))
                {
                    RemoveJumpedToken(position, token, opponentTokens, board);
                    AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, true);
                    RevertJumpedToken(position, token, opponentTokens, board);
                    hasJumped = true;
                }

                x        = token.xPosition + 2;
                position = new Vector3(x, y);
                if (IsValidJumpPosition(position, token, board))
                {
                    RemoveJumpedToken(position, token, opponentTokens, board);
                    AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, true);
                    RevertJumpedToken(position, token, opponentTokens, board);
                    hasJumped = true;
                }

                //We always want to prioritise jumping
                if (!hasJumped)
                {
                    //Check moves
                    x        = token.xPosition - 1;
                    y        = token.yPosition + (token.player == 0 ? 1 : -1);
                    position = new Vector3(x, y);
                    if (IsValidMovePosition(position, token, board))
                    {
                        AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, false);
                    }

                    x        = token.xPosition + 1;
                    position = new Vector3(x, y);
                    if (IsValidMovePosition(position, token, board))
                    {
                        AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, false);
                    }
                }

                if (token.IsKing)
                {
                    //Check king jumps
                    x        = token.xPosition - 2;
                    y        = token.yPosition + (token.player == 0 ? -2 : 2);
                    position = new Vector3(x, y);
                    if (IsValidJumpPosition(position, token, board))
                    {
                        RemoveJumpedToken(position, token, opponentTokens, board);
                        AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, true);
                        RevertJumpedToken(position, token, opponentTokens, board);
                        hasJumped = true;
                    }

                    x        = token.xPosition + 2;
                    position = new Vector3(x, y);
                    if (IsValidJumpPosition(position, token, board))
                    {
                        RemoveJumpedToken(position, token, opponentTokens, board);
                        AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, true);
                        RevertJumpedToken(position, token, opponentTokens, board);
                        hasJumped = true;
                    }

                    if (!hasJumped)
                    {
                        //Check king moves
                        x        = token.xPosition - 1;
                        y        = token.yPosition + (token.player == 0 ? -1 : 1);
                        position = new Vector3(x, y);
                        if (IsValidMovePosition(position, token, board))
                        {
                            AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, false);
                        }

                        x        = token.xPosition + 1;
                        position = new Vector3(x, y);
                        if (IsValidMovePosition(position, token, board))
                        {
                            AddMoveChildNode(token, board, x, y, position, node, opponentTokens, playerTokens, false);
                        }
                    }
                }
            }
        }
    }
Exemplo n.º 5
0
    private int Simulate(CheckersNode node, int winPlayer)
    {
        int[,] boardState = node.BoardState;
        List <Token> playerTokens   = new List <Token>();
        List <Token> opponentTokens = new List <Token>();
        int          currentPlayer  = node.player;

        for (int i = 0; i < node.playerTokens.Count; i++)
        {
            Token playerToken   = new Token(node.playerTokens[i]);
            Token opponentToken = new Token(node.opponentTokens[i]);
            playerTokens.Add(playerToken);
            opponentTokens.Add(opponentToken);
        }

        int  score            = 0;
        int  currentMoveCount = moveCount;
        int  winner           = -1;
        bool instaLose        = false;

        while (winner == -1 && currentMoveCount < maxNumberOfMoves)
        {
            int     index;
            Vector3 destination;
            bool    isJump;

            //Get random move
            GetRandomMove(boardState, playerTokens, out index, out destination, out isJump);
            if (index == -1)
            {
                //insta lose fam
                instaLose = true;
                break;
            }

            //Move
            Token token = playerTokens[index];
            if (isJump)
            {
                RemoveJumpedToken(destination, token, opponentTokens, boardState);
            }

            int originalX = token.xPosition;
            int originalY = token.yPosition;
            boardState[originalX, originalY] = -1;
            boardState[(int)destination.x, (int)destination.y] = token.player;
            token.gameObject.transform.position = destination;

            //Swap players
            currentPlayer = (currentPlayer + 1) % 2;
            List <Token> temp = new List <Token>(playerTokens);
            playerTokens.Clear();
            playerTokens.AddRange(opponentTokens);
            opponentTokens.Clear();
            opponentTokens.AddRange(temp);
            temp.Clear();

            winner = CheckForWin(boardState, true);
            currentMoveCount++;
        }

        if (instaLose)
        {
            if (currentPlayer == winPlayer)
            {
                score = 0;
            }
            else
            {
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        if (boardState[i, j] == winPlayer)
                        {
                            score++;
                        }
                    }
                }
            }
        }
        else
        {
            if (winner == -1)
            {
                winner = CheckForWin(boardState, false);
            }

            if (winPlayer == winner)
            {
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        if (boardState[i, j] == winPlayer)
                        {
                            score++;
                        }
                    }
                }
            }
        }

        return(score);
    }
Exemplo n.º 6
0
    //Reference:https://www.youtube.com/watch?v=UXW2yZndl7U
    private CheckersNode RunMCTS(int player)
    {
        CheckersNode root = null;

        if (player == 0)
        {
            root = new CheckersNode(size, player, null, player1Tokens, player2Tokens);
        }
        else
        {
            root = new CheckersNode(size, player, null, player2Tokens, player1Tokens);
        }

        root.BoardState = mainBoard;
        DateTime start    = DateTime.Now;
        int      maxDepth = 0;

        while ((DateTime.Now - start).TotalMilliseconds < maxMCTSTime)
        {
            CheckersNode current = root;
            int          depth   = 0;

            //Selection
            while (current.children.Count != 0)
            {
                current = current.GetChildWithBestUCB1();
                depth++;
            }

            if (maxDepth < depth)
            {
                maxDepth = depth;
            }

            //Simulation
            if (current.totalPlayouts == 0)
            {
                int score = Simulate(current, player);

                //Back Propagate
                current.BackPropagate(score);
            }
            else
            {
                //Expansion
                GenerateChildNodes(current);
            }
        }

        totalMCTSDepth += maxDepth;

        if (root.children.Count > 0)
        {
            //Find node with best score
            float bestScore     = root.children[0].GetScore() / root.children[0].totalPlayouts;
            int   selectedIndex = 0;
            for (int i = 1; i < root.children.Count; i++)
            {
                float score = root.children[i].GetScore() / root.children[i].totalPlayouts;
                if (bestScore < score)
                {
                    bestScore     = score;
                    selectedIndex = i;
                }
            }

            return(root.children[selectedIndex]);
        }
        else
        {
            return(null);
        }
    }
Exemplo n.º 7
0
    // Update is called once per frame
    void Update()
    {
        if (!stopGame)
        {
            if (activePlayer == 0)
            {
                if (Input.GetMouseButtonDown(0) && !doubleAI)
                {
                    RaycastHit hit;
                    Ray        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                    if (currentPlayState == PlayState.SELECTION)
                    {
                        if (Physics.Raycast(ray, out hit, Mathf.Infinity, LayerMask.GetMask("Token")))
                        {
                            selectedToken = GetSelectedToken(player1Tokens, hit.transform.position);
                            if (selectedToken != null && selectedToken.IsAlive)
                            {
                                currentPlayState = PlayState.PLACEMENT;
                            }
                            else
                            {
                                selectedToken = null;
                            }
                        }
                    }
                    else
                    {
                        if (Physics.Raycast(ray, out hit, Mathf.Infinity, LayerMask.GetMask("Board")))
                        {
                            Vector3 hitPosition = hit.transform.position;
                            if (IsValidMovePosition(hitPosition, selectedToken, mainBoard))
                            {
                                UpdateSelectedTokenPosition(hitPosition, selectedToken, mainBoard);
                                stopGame      = CheckForWin(mainBoard, true) != -1;
                                gameText.text = "Player " + (activePlayer + 1) + " wins!";
                                activePlayer  = (activePlayer + 1) % 2;
                                moveCount++;
                            }
                            else if (IsValidJumpPosition(hitPosition, selectedToken, mainBoard))
                            {
                                RemoveJumpedToken(hitPosition, selectedToken, player2Tokens, mainBoard);
                                UpdateSelectedTokenPosition(hitPosition, selectedToken, mainBoard);
                                stopGame      = CheckForWin(mainBoard, true) != -1;
                                gameText.text = "Player " + (activePlayer + 1) + " wins!";
                                activePlayer  = (activePlayer + 1) % 2;
                                moveCount++;
                            }
                        }
                    }
                }

                if (doubleAI)
                {
                    bool instaLose = false;
                    if (moveCount == 0)
                    {
                        //Reference:http://www.quadibloc.com/other/bo1211.htm
                        //Make the best move possible (5,2) to (6,3)
                        Vector3 tokenPosition = new Vector3(5, 2, -1.0f);
                        Vector3 movePosition  = new Vector3(6, 3);
                        Token   tokenToMove   = GetSelectedToken(player1Tokens, tokenPosition);
                        UpdateSelectedTokenPosition(movePosition, tokenToMove, mainBoard);
                    }
                    else
                    {
                        //AI code
                        CheckersNode node = RunMCTS(activePlayer);
                        mctsTurns++;
                        if (node != null)
                        {
                            Vector3 tokenPosition = new Vector3(node.xStart, node.yStart, -1.0f);
                            Vector3 movePosition  = new Vector3(node.xEnd, node.yEnd);
                            Token   tokenToMove   = GetSelectedToken(player1Tokens, tokenPosition);

                            Debug.Assert(tokenToMove.IsAlive);

                            if (node.isJump)
                            {
                                RemoveJumpedToken(movePosition, tokenToMove, player2Tokens, mainBoard);
                            }

                            UpdateSelectedTokenPosition(movePosition, tokenToMove, mainBoard);
                        }
                        else
                        {
                            instaLose = true;
                        }
                    }
                    if (!instaLose)
                    {
                        stopGame = CheckForWin(mainBoard, true) != -1;
                        if (stopGame)
                        {
                            gameText.text = "Player " + (activePlayer + 1) + " wins!";
                            player1Wins++;
                        }
                        activePlayer = (activePlayer + 1) % 2;
                        moveCount++;
                    }
                    else
                    {
                        //Player 2 wins
                        stopGame      = true;
                        gameText.text = "Player " + (((activePlayer + 1) % 2) + 1) + " wins!";
                        player2Wins++;
                    }
                }
            }
            else
            {
                //AI code
                DateTime     start = DateTime.Now;
                CheckersNode node  = RunMinimax(activePlayer);
                totalMinimaxTime += (DateTime.Now - start).TotalMilliseconds;
                minimaxTurns++;
                if (node != null)
                {
                    Vector3 tokenPosition = new Vector3(node.xStart, node.yStart, -1.0f);
                    Vector3 movePosition  = new Vector3(node.xEnd, node.yEnd);
                    Token   tokenToMove   = GetSelectedToken(player2Tokens, tokenPosition);

                    Debug.Assert(tokenToMove.IsAlive);

                    if (node.isJump)
                    {
                        RemoveJumpedToken(movePosition, tokenToMove, player1Tokens, mainBoard);
                    }

                    UpdateSelectedTokenPosition(movePosition, tokenToMove, mainBoard);
                    stopGame = CheckForWin(mainBoard, true) != -1;
                    if (stopGame)
                    {
                        gameText.text = "Player " + (activePlayer + 1) + " wins!";
                        player2Wins++;
                    }
                    activePlayer = (activePlayer + 1) % 2;
                    moveCount++;
                }
                else
                {
                    //Player 1 wins
                    stopGame      = true;
                    gameText.text = "Player " + (((activePlayer + 1) % 2) + 1) + " wins!";
                    player1Wins++;
                }
            }

            if (moveCount == maxNumberOfMoves)
            {
                stopGame = true;
                int winner = CheckForWin(mainBoard, false);
                gameText.text = "Player " + (winner + 1) + " wins!";
                if (winner == 0)
                {
                    player1Wins++;
                }
                else
                {
                    player2Wins++;
                }
            }

            if (!stopGame)
            {
                gameText.text = "Player " + (activePlayer + 1);
            }
            DummyObjectPool.Instance.ResetAll();
        }
        else
        {
            currentIteration++;
            if (currentIteration < numberOfIterations)
            {
                ResetBoard();
            }
            else
            {
                if (gameText.text != "Finished")
                {
                    gameText.text = "Finished";
                    WriteLog();
                    Application.Quit();
                }
            }
        }
    }