Пример #1
0
    public void PlaySkilledMirrorMatch(Game game)
    {
        MCTSAgent player1 = new MCTSAgent(1);
        MCTSAgent player2 = new MCTSAgent(2);

        PlayGame(game, player1, player2);
    }
Пример #2
0
    void Start()
    {
        return;

        Debug.Log("---");
        Game g = new Game(4, 4);

        g.winCondition  = new InARowCondition(Direction.LINE, 3);
        g.loseCondition = new InARowCondition(Direction.LINE, 2);
        MCTSAgent p = new MCTSAgent(1);
        MCTSAgent q = new MCTSAgent(2);

        g.PrintBoard();
        for (int i = 0; i < 5; i++)
        {
            p.TakeTurn(g);
            g.PrintBoard();
            if (g.endStatus > 0)
            {
                break;
            }
            q.TakeTurn(g);
            g.PrintBoard();
            if (g.endStatus > 0)
            {
                break;
            }
        }
        Debug.Log(g.endStatus);
        Debug.Log("---");
    }
Пример #3
0
        private static void Main()
        {
            Console.WriteLine("Setup gameConfig");

            var gameConfig = new GameConfig()
            {
                StartPlayer          = 1,
                Player1HeroClass     = CardClass.WARRIOR,
                Player2HeroClass     = CardClass.SHAMAN,
                FillDecks            = true,
                Shuffle              = true,
                Logging              = false,
                FillDecksPredictably = true
            };

            Console.WriteLine("Setup POGameHandler");
            AbstractAgent player1     = new MCTSAgent();
            AbstractAgent player2     = new GreedyAgent();
            var           gameHandler = new POGameHandler(gameConfig, player1, player2, repeatDraws: false);

            Console.WriteLine("Simulate Games");
            //gameHandler.PlayGame();
            gameHandler.PlayGames(nr_of_games: 10, addResultToGameStats: true, debug: false);
            GameStats gameStats = gameHandler.getGameStats();

            gameStats.printResults();

            Console.WriteLine("Test successful");
            Console.ReadLine();
        }
Пример #4
0
        public static void TestPOGameMyAgent(int count, CardClass player, CardClass opponent, List <Card> playerDeck, List <Card> opponentDeck, AbstractAgent opponentAgent)
        {
            Console.WriteLine("Setup gameConfig");

            var gameConfig = new GameConfig()
            {
                StartPlayer      = -1,
                Player1HeroClass = player,
                Player2HeroClass = opponent,
                Player1Deck      = playerDeck,
                Player2Deck      = opponentDeck,
                FillDecks        = false,
                Shuffle          = true,
                Logging          = true
            };

            Console.WriteLine("Setup POGameHandler");
            AbstractAgent player1     = new MCTSAgent();
            AbstractAgent player2     = opponentAgent;
            var           gameHandler = new POGameHandler(gameConfig, player1, player2, repeatDraws: false);

            Console.WriteLine("Simulate Games");
            gameHandler.PlayGames(nr_of_games: count, addResultToGameStats: true, debug: false);

            GameStats gameStats = gameHandler.getGameStats();

            Console.WriteLine(player + " vs " + opponent);
            gameStats.printResults();
            Console.WriteLine("Test successful");
        }
Пример #5
0
    public static AAgent CreateAgent(AgentType a, Game g, PlayerTag t)
    {
        AAgent agent;

        if (a == AgentType.Player)
        {
            agent = new PlayerAgent();
        }
        else if (a == AgentType.Random)
        {
            agent = new RandomAgent();
        }
        else
        {
            agent = new MCTSAgent();
        }

        agent.SetGame(g);
        agent.SetPlayerTag(t);

        return(agent);
    }
Пример #6
0
    public void SetupBoard(Game game)
    {
        currentGame = game;

        Camera.main.backgroundColor = backgroundColor;

        pieces = new PlayerPiece[game.boardWidth, game.boardHeight];

        GameObject board = new GameObject("Board");

        //! Render the board. By default we start at (0,0) and reposition the camera after
        BoardPiece boardPiece;

        for (int i = 0; i < game.boardWidth; i++)
        {
            for (int j = 0; j < game.boardHeight; j++)
            {
                boardPiece = Instantiate(templateBoardPiece);

                boardPiece.x = i; boardPiece.y = j;
                boardPiece.transform.position = new Vector3(i, j, 1);
                if ((i + j) % 2 == 0)
                {
                    boardPiece.sprite.color = boardOffColor;
                }
                else
                {
                    boardPiece.sprite.color = boardOnColor;
                }

                boardPiece.transform.parent = board.transform;
            }
        }

        player1Text.color  = playerOneColor;
        player1Image.color = playerOneColor;
        player2Text.color  = playerTwoColor;
        player2Image.color = playerTwoColor;

        if (rulesText != null)
        {
            //Setup the little sidebar user interface bit
            rulesText.text = game.GameToString();
        }

        if (aiVersus && agent1Text != null && agent2Text != null)
        {
            //If it's an AI game, set up the little labels
            agent1Text.text = agentOne.ToString() + " Agent";
            agent2Text.text = agentTwo.ToString() + " Agent";
        }

        //Set camera centre
        float cx = -0.5f + (float)game.boardWidth / 2f;
        float cy = -0.5f + (float)game.boardHeight / 2f;

        Camera.main.transform.position = new Vector3(cx, cy, -10);

        //Set zoom
        //With thanks to: https://pressstart.vip/tutorials/2018/06/6/37/understanding-orthographic-size.html
        float screenRatio = (float)Screen.width / (float)Screen.height;
        float targetRatio = game.boardWidth / game.boardHeight;

        if (screenRatio >= targetRatio)
        {
            Camera.main.orthographicSize = 1.5f + (game.boardHeight / 2);
        }
        else
        {
            float differenceInSize = targetRatio / screenRatio;
            Camera.main.orthographicSize = 0.5f + (game.boardWidth / 2 * differenceInSize);
        }

        //Attach our view to the game itself
        game.playableGame    = this;
        game.interactiveMode = true;

        if (aiOpponent)
        {
            npcPlayer = new MCTSAgent(2);
        }

        //Again, very messy, a lot of the UI code especially is rushed here.
        if (aiVersus)
        {
            switch (agentOne)
            {
            case AGENT_TYPE.Greedy:
                botPlayer1 = new GreedyAgent(1); break;

            case AGENT_TYPE.Random:
                botPlayer1 = new RandomAgent(1); break;

            case AGENT_TYPE.MCTS:
                botPlayer1 = new MCTSAgent(1); break;

            case AGENT_TYPE.Conservative:
                botPlayer1 = new MCTSAgent(1)
                {
                    rolloutLength = 2
                }; break;
            }
            switch (agentTwo)
            {
            case AGENT_TYPE.Greedy:
                botPlayer2 = new GreedyAgent(2); break;

            case AGENT_TYPE.Random:
                botPlayer2 = new RandomAgent(2); break;

            case AGENT_TYPE.MCTS:
                botPlayer2 = new MCTSAgent(2); break;

            case AGENT_TYPE.Conservative:
                botPlayer2 = new MCTSAgent(2)
                {
                    rolloutLength = 2
                }; break;
            }

            StartCoroutine(VersusMode());
        }
    }
Пример #7
0
    //? To score a game we set up three kinds of matchup, condense each one into a score,
    //? add the scores together and then normalise them between 0 and 1.
    public IEnumerator ScoreGame(Game game)
    {
        //? Reset
        playerBiasScore       = 0f;
        greedIsGoodScore      = 0f;
        skillIsBetterScore    = 0f;
        drawsAreBadScore      = 0f;
        highSkillBalanceScore = 0f;
        string scoresSoFar = "";

        if (interfaceEnabled)
        {
            currentRulesText.text  = game.GameToString();
            scoresSoFar            = "First Play Bias: " + ToScore(playerBiasScore) + "\n";
            currentScoresText.text = scoresSoFar;
            yield return(0);
        }

        //? Random vs. Random: These games can go either way, the only thing we're interested
        //? is if there's a clear bias towards playing first or second. This is a good indicator.
        //? Score is therefore proportional to the amount one agent won over the other.
        RandomAgent randomAgent1 = new RandomAgent(1);
        RandomAgent randomAgent2 = new RandomAgent(2);
        int         firstWon = 0; int secondWon = 0;

        for (int i = 0; i < randomRandomMatches; i++)
        {
            if (interfaceEnabled)
            {
                currentScoresText.text = "First Play Bias: Playing (" + i + "/" + randomRandomMatches + ")\n";
            }

            //NOTE MJ: Playing the games could be coroutines, so they don't block UI.
            //res is redundant game.endStatus already has info.
            yield return(GameEvaluation.instance.PlayGame(game, randomAgent1, randomAgent2));

            if (game.endStatus == 1)
            {
                firstWon++;
            }
            if (game.endStatus == 2)
            {
                secondWon++;
            }
            //? Yield after each playout - we could yield more frequently, this is OK though.
            yield return(0);
        }

        playerBiasScore = 1 - (Mathf.Abs(firstWon - secondWon) / randomRandomMatches);

        if (interfaceEnabled)
        {
            scoresSoFar            = "First Play Bias: " + ToScore(playerBiasScore) + "\n";
            currentScoresText.text = scoresSoFar;
            yield return(0);
        }

        //? We could also add in a measure of 'decisiveness' - i.e. games shouldn't end in draws.
        //? However for random agents this might happen just because they aren't very good.

        //? Random vs. Greedy: Greedy may not always win the game, but we expect it to
        //? win more than random. Score is proportion to the number of games greedy won or tied.
        int randomAgentWon = 0;

        for (int i = 0; i < greedyRandomMatches; i++)
        {
            if (interfaceEnabled)
            {
                currentScoresText.text = scoresSoFar + "Simple Beats Random: Playing (" + i + "/" + greedyRandomMatches + ")\n";
                yield return(0);
            }

            //? Small detail: note that we swap who plays first, to compensate
            //? for first-player advantage
            RandomAgent randomAgent = new RandomAgent(1 + (i % 2));
            GreedyAgent greedyAgent = new GreedyAgent(2 - (i % 2));

            //NOTE MJ: Playing the games could be coroutines, so they don't block UI. res could be an out parameter.
            yield return(GameEvaluation.instance.PlayGame(game, randomAgent, greedyAgent));

            if (game.endStatus == 1 + (i % 2))
            {
                randomAgentWon++;
            }
            yield return(0);
        }

        greedIsGoodScore = 1 - ((float)randomAgentWon / greedyRandomMatches);

        if (interfaceEnabled)
        {
            scoresSoFar           += "Simple Beats Random: " + ToScore(greedIsGoodScore) + "\n";
            currentScoresText.text = scoresSoFar;
            yield return(0);
        }

        //? Greedy vs. MCTS: We know that greedy players will avoid causing their own loss, and
        //? win if given the opportunity, but MCTS players can look ahead and plan. As a result,
        //? a more strategic game should be won by MCTS agents. Score is proportion of games MCTS
        //? agent won. Note that we might need to give the MCTS agent more computational resources
        //? for some games to ensure it is performing better.
        int mctsAgentWon = 0;

        for (int i = 0; i < greedySkilledMatches; i++)
        {
            if (interfaceEnabled)
            {
                currentScoresText.text = scoresSoFar + "Clever Beats Simple: Playing (" + i + "/" + greedySkilledMatches + ")\n";
                yield return(0);
            }

            MCTSAgent   skilledAgent = new MCTSAgent(1 + (i % 2));
            GreedyAgent greedyAgent  = new GreedyAgent(2 - (i % 2));

            //NOTE MJ: Playing the games could be coroutines, so they don't block UI. res could be an out parameter.
            yield return(GameEvaluation.instance.PlayGame(game, skilledAgent, greedyAgent));

            if (game.endStatus == 1 + (i % 2))
            {
                mctsAgentWon++;
            }
            yield return(0);
        }

        skillIsBetterScore = (float)mctsAgentWon / greedySkilledMatches;

        if (interfaceEnabled)
        {
            scoresSoFar           += "Clever Beats Simple: " + ToScore(skillIsBetterScore) + "\n";
            currentScoresText.text = scoresSoFar;
            yield return(0);
        }

        //? Finally, MCTS vs MCTS. If we wanted more depth, we could do two version of this,
        //? one with MCTS agents that are given different amounts of computation, to really
        //? test to see if more thinking time = better play. However, here we're just going to
        //? test a good old fashioned mirror matchup. For two good equal players, we want
        //? a) not too much imbalance in favour of either player and b) not too many draws.
        int       drawnGames = 0;
        int       firstPlayerWon = 0; int secondPlayerWon = 0;
        MCTSAgent skilledAgent1 = new MCTSAgent(1);
        MCTSAgent skilledAgent2 = new MCTSAgent(2);

        for (int i = 0; i < skilledMirrorMatches; i++)
        {
            if (interfaceEnabled)
            {
                currentScoresText.text = scoresSoFar +
                                         "Avoid Draws: Playing (" + i + "/" + skilledMirrorMatches + ")\n" +
                                         "High Skill Mirror Matchup: Playing (" + i + "/" + skilledMirrorMatches + ")\n";
                yield return(0);
            }

            //NOTE MJ: Playing the games could be coroutines, so they don't block UI. res could be an out parameter.
            yield return(GameEvaluation.instance.PlayGame(game, skilledAgent1, skilledAgent2));

            if (game.endStatus == 1)
            {
                firstPlayerWon++;
            }
            if (game.endStatus == 2)
            {
                secondPlayerWon++;
            }
            if (game.endStatus == 3 || game.endStatus == 0)
            {
                drawnGames++;
            }
            yield return(0);
        }

        drawsAreBadScore      = 1 - ((float)drawnGames / skilledMirrorMatches);
        highSkillBalanceScore = Mathf.Abs(firstPlayerWon - secondPlayerWon) / skilledMirrorMatches;

        if (interfaceEnabled)
        {
            currentScoresText.text = scoresSoFar + "Avoid Draws: " + ToScore(drawsAreBadScore) + "\n" +
                                     "High Skill Mirror Matchup: " + ToScore(highSkillBalanceScore) + "\n";
            yield return(0);
        }

        //? Now we can add up the scores and return them. If we wanted we could balance them so
        //? some scores are more important than others, or we could partition them into "must-haves"
        //? and "nice-to-haves". I discuss this in the tutorial video.

        // Debug.Log("Random vs. Random: "+playerBiasScore);
        // Debug.Log("Greedy vs. Random: "+greedIsGoodScore);
        // Debug.Log("MCTS vs. Greedy: "+skillIsBetterScore);
        // Debug.Log("MCTS vs. MCTS (draws): "+drawsAreBadScore);
        // Debug.Log("MCTS vs. MCTS (win balance): "+highSkillBalanceScore);

        game.evaluatedScore = (playerBiasScore + greedIsGoodScore + skillIsBetterScore + drawsAreBadScore + highSkillBalanceScore) / 5f;
    }
Пример #8
0
            public static void MoltenTest()
            {
                var rnd    = new Random();
                var p1Deck = new Deck(CardClass.WARRIOR, Archetype.QUEST, "IceFireWarrior", 100,
                                      new List <Card>
                {
                    Cards.FromName("Fire Plume's Heart"),
                    Cards.FromName("Goldshire Footman"),
                    Cards.FromName("Wax Elemental"),
                    Cards.FromName("Wax Elemental"),
                    Cards.FromName("Cleave"),
                    Cards.FromName("Cornered Sentry"),
                    Cards.FromName("Drywhisker Armorer"),
                    Cards.FromName("Drywhisker Armorer"),
                    Cards.FromName("Execute"),
                    Cards.FromName("Execute"),
                    Cards.FromName("Plated Beetle"),
                    Cards.FromName("Plated Beetle"),
                    Cards.FromName("Warpath"),
                    Cards.FromName("Warpath"),
                    Cards.FromName("Phantom Militia"),
                    Cards.FromName("Phantom Militia"),
                    Cards.FromName("Shield Block"),
                    Cards.FromName("Shield Block"),
                    Cards.FromName("Stonehill Defender"),
                    Cards.FromName("Stonehill Defender"),
                    Cards.FromName("Brawl"),
                    Cards.FromName("Direhorn Hatchling"),
                    Cards.FromName("Direhorn Hatchling"),
                    Cards.FromName("Rotten Applebaum"),
                    Cards.FromName("Ornery Direhorn"),
                    Cards.FromName("Unidentified Shield"),
                    Cards.FromName("Unidentified Shield"),
                    Cards.FromName("Geosculptor Yip"),
                    Cards.FromName("Scourgelord Garrosh"),
                    Cards.FromName("Molten Blade")
                });

                var p2Deck = new Deck();

                var gameConfig = new GameConfig
                {
                    StartPlayer      = rnd.Next(1, 2),
                    Player1HeroClass = CardClass.WARRIOR,
                    Player1Name      = "Garrosh Hellscream",
                    Player1Deck      = p1Deck,
                    Player2HeroClass = CardClass.DRUID,
                    Player2Name      = "Malfurion Stormrage",
                    Player2Deck      = p2Deck.FromDB(CardClass.DRUID, deckName: "Malygos_Druid1"),
                    Shuffle          = false,
                    Logging          = true,
                    History          = true,
                    //SkipMulligan = false
                };

                var testGame = new Game(gameConfig);

                var           player1 = new MCTSAgent();
                AbstractAgent player2 = new RandomAgent();

                player1.InitializeAgent();
                player2.InitializeAgent();

                player1.InitializeGame();
                player2.InitializeGame();

                testGame.StartGame();

                //bool check = true;
                //var molten = (IPlayable)testGame.CurrentPlayer.HandZone.Find(c => c.Type == CardType.WEAPON);
                //if (molten?.Cost != molten?.Card.Cost)
                //	check = false;
                var root = new HearthNode(null, testGame, null);

                //molten = (IPlayable)root.Game.CurrentPlayer.HandZone.Find(c => c.Type == CardType.WEAPON);
                //if (molten.Cost != molten.Card.Cost)
                //	check = false;

                HearthNode state = root.Frontier.Find(p => p.IsEndTurn);

                for (int i = 0; i < 5; ++i)
                {
                    state = state.Frontier.Find(p => p.IsEndTurn);
                }
            }
Пример #9
0
        private static MCTSAgent GetParamsForAgent()
        {
            StringBuilder help  = new StringBuilder();
            MCTSAgent     agent = new MCTSAgent();

            help.AppendLine("<---------- MCTSAgent advanced parameters ---------->");
            help.AppendLine("Turn depth (max depth of search): positive integer (default 1 -> flat MCTS)");
            help.AppendLine("Time budget (max time per move): time in msec >= 1000 (default 2000 -> 2 sec)");
            help.AppendLine("Selection strategy: UCT, MaxChild, RobustChild, MaxRobustChild, MaxRatioChild, SecureChild");
            help.AppendLine("State rate strategy: Aggro, Control, Ramp, Ejnar, Greedy, Fatigue");
            help.Append("Example: 1 2000 UCT Greedy");
            string input = ReadFromConsole(help.ToString());

            if (String.IsNullOrWhiteSpace(input))
            {
                Console.WriteLine("Using deafult agent.");
                return(agent);
            }

            string[] parsedInput = input.Split(' ');

            if (parsedInput.Count() == 4)
            {
                try
                {
                    int turnDepth;
                    int timeBudget;
                    SelectionStrategy selection;
                    StateRateStrategy stateRate;

                    if (!Int32.TryParse(parsedInput[0], out turnDepth) || turnDepth < 1)
                    {
                        Console.WriteLine("Not valid turn depth. Using default.");
                        turnDepth = 1;
                    }

                    if (!Int32.TryParse(parsedInput[1], out timeBudget) || timeBudget < 1000)
                    {
                        Console.WriteLine("Time budget less then 1000 msec. Using default.");
                        timeBudget = 2000;
                    }

                    if (Enum.GetNames(typeof(SelectionStrategy)).Contains(parsedInput[2]))
                    {
                        selection = (SelectionStrategy)Enum.Parse(typeof(SelectionStrategy), parsedInput[2]);
                    }
                    else
                    {
                        Console.WriteLine("Not valid Selection strategy. Using deafult.");
                        selection = SelectionStrategy.UCT;
                    }

                    if (Enum.GetNames(typeof(StateRateStrategy)).Contains(parsedInput[3]))
                    {
                        stateRate = (StateRateStrategy)Enum.Parse(typeof(StateRateStrategy), parsedInput[3]);
                    }
                    else
                    {
                        Console.WriteLine("Not valid State rate strategy. Using deafult.");
                        stateRate = StateRateStrategy.Greedy;
                    }

                    agent.TurnDepth  = turnDepth;
                    agent.TimeBudget = timeBudget;
                    agent.Selection  = selection;
                    agent.StateRate  = stateRate;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }


            return(agent);
        }