Exemplo n.º 1
0
        /// <summary>
        /// Creates a deep copy of this player.
        /// </summary>
        /// <returns>Returns a deep copy of this player.</returns>
        public override Player <MahjongMove> Clone()
        {
            MahjongAIPlayer ret = new  MahjongAIPlayer(CardsInHand.Cards, AI);

            ret.CopyData(this);

            return(ret);
        }
Exemplo n.º 2
0
        public static void Main(string[] args)
        {
            // Setup output information
            string fout      = "Game " + DEPTH + "-" + SAMPLES + ".txt";
            string header    = "Minimax Search Depth: " + DEPTH + "\nMonte Carlo Sample Size: " + SAMPLES + "\n\nFinal Scores\n------------\n";
            string results   = "";
            string game_text = "";

            // Create the game
            MahjongGameState state = new MahjongGameState();

            // Add the AI
            state.PlayerLeft(0, new NaiveAI(0));
            state.PlayerLeft(1, new GreedyAI(1));
            state.PlayerLeft(2, new MiniMaxAI(2, DEPTH));
            state.PlayerLeft(3, new MonteCarloAI(3, SAMPLES));

            int i = 4;

            // Run the game to completion
            while (!state.GameFinished)
            {
                MahjongAIPlayer ai   = state.GetPlayer(state.SubActivePlayer) as MahjongAIPlayer;
                MahjongMove     move = ai.AI.GetNextMove(state);

                Log(PlayerToName(state.SubActivePlayer) + " " + move + ".\n", ref game_text);

                if (--i == 0)
                {
                    Log("\n", ref game_text);
                    i = 4;
                }

                if (!state.ApplyMove(move))
                {
                    Log("An invalid move has been provided.\n", ref game_text);
                }

                if (state.HandFinished && !state.GameFinished)
                {
                    Print(state, ref game_text);
                    state.StartHand();
                }
            }

            // Output the final scores
            PrintLight(state, ref results);
            Print(state, ref game_text, true);

            File.WriteAllText(fout, header + results + "\nGame Log\n--------\n" + game_text);
            return;
        }
Exemplo n.º 3
0
        /// <summary>
        /// If a player leaves the game then they are replaced with an AI player.
        /// </summary>
        /// <param name="index">The index of the player that left.</param>
        /// <param name="replacement">The replacement AI. If null then a default behavior will be used.</param>
        /// <remarks>An AI player can leave the game to be replaced by a new AI player.</remarks>
        public void PlayerLeft(int index, AIBehavior <MahjongMove> replacement = null)
        {
            if (index < 0 || index > 4)
            {
                return;
            }

            MahjongPlayer old = players[index] as MahjongPlayer;

            players[index] = new MahjongAIPlayer(players[index].CardsInHand.Cards, replacement);
            (players[index] as MahjongPlayer).CopyData(old);

            return;
        }
Exemplo n.º 4
0
        /// <summary>
        /// Determines the next move for this AI to make.
        /// </summary>
        /// <param name="state">The state of the game. This will be cloned so that the AI does not affect the actual game state.</param>
        /// <returns>Returns the next move to make based on the current game state.</returns>
        public MahjongMove GetNextMove(GameState <MahjongMove> state)
        {
            MahjongGameState s  = state as MahjongGameState;
            MahjongAIPlayer  ai = state.GetPlayer(Player) as MahjongAIPlayer;
            MahjongPlayer    mp = ai as MahjongPlayer;

            // Pass if it's not this AI's turn
            if (s.ActivePlayer != Player)
            {
                return(new MahjongMove());
            }

            if (MahjongStaticFunctions.HasMahjong(ai.CardsInHand, mp.Melds))
            {
                return(new MahjongMove(true));
            }

            // Pick a random tile to discard
            int r = rand.Next(0, (int)ai.CardsInHand.CardsInHand);

            rc++;

            return(new MahjongMove(ai.CardsInHand.Cards[r]));
        }
Exemplo n.º 5
0
        /// <summary>
        /// Returns an enumerator of the possible moves at the given state.
        /// </summary>
        /// <param name="state">The state to enumerate the moves of.</param>
        private IEnumerator <MahjongMove> GetMoveEnumerator(MahjongGameState state)
        {
            List <MahjongMove> moves = new List <MahjongMove>();

            // If the hand is done (or the game), then there's nothing left to enumerate.
            if (state.HandFinished || state.GameFinished)
            {
                return(moves.GetEnumerator());
            }

            // Get the player we're considering
            MahjongAIPlayer ai = state.GetPlayer(state.SubActivePlayer) as MahjongAIPlayer;
            MahjongPlayer   mp = ai as MahjongPlayer;

            // The active player has different choices available than the responding players
            if (state.ActivePlayer == state.SubActivePlayer)
            {
                // Add the kongs first
                foreach (Card c in ai.CardsInHand.Cards)
                {
                    if (!ContainsKong(moves, c) && ai.CardsInHand.CountCard(c) == 4)
                    {
                        List <Card> meld = new List <Card>();

                        for (int i = 0; i < 4; i++)
                        {
                            meld.Add(c.Clone());
                        }

                        moves.Add(new MahjongMove(new MahjongMeld(meld, true), c));                       // We're melding a kong, so mahjong is definitely false here
                    }
                }

                // We can also meld a fourth tile into an existing pung
                foreach (MahjongMeld meld in mp.Melds)
                {
                    if (meld.Pung && ai.CardsInHand.Cards.Contains(meld.Cards[0]))
                    {
                        List <Card> n_meld = new List <Card>();

                        for (int i = 0; i < 4; i++)
                        {
                            n_meld.Add(meld.Cards[0].Clone());
                        }

                        moves.Add(new MahjongMove(new MahjongMeld(n_meld, false), meld.Cards[0]));                       // We're melding a kong, so mahjong is definitely false here
                    }
                }

                // Add all of the discards next
                foreach (Card c in ai.CardsInHand.Cards)
                {
                    moves.Add(new MahjongMove(c));
                }

                // Lastly, we'll add the ability to declare mahjong
                if (MahjongStaticFunctions.HasMahjong(ai.CardsInHand, mp.Melds))
                {
                    moves.Add(new MahjongMove(true));
                }
            }
            else
            {
                // We need a tile available to do anything but pass (there are a few cases were this would not be available)
                if (state.AvailableTile != null)
                {
                    // First, add chow moves if we can
                    if (state.SubActivePlayer == state.NextPlayer)
                    {
                        List <MahjongMeld> chows = GetChows(ai.CardsInHand, state.AvailableTile);

                        foreach (MahjongMeld m in chows)
                        {
                            Hand h; List <MahjongMeld> melds;
                            CreateDuplicateMinusMeld(ai.CardsInHand, mp.Melds, m, state.AvailableTile, out h, out melds);

                            moves.Add(new MahjongMove(m, state.AvailableTile, MahjongStaticFunctions.HasMahjong(h, melds)));
                        }
                    }

                    // Add the ability to meld pungs and kongs
                    int count = ai.CardsInHand.CountCard(state.AvailableTile);                     // We'll use this again after pung/kong checks, so this is extra fine to keep around

                    if (count > 1)
                    {
                        // First, pungs are always possible
                        List <Card> cards = new List <Card>();

                        cards.Add(state.AvailableTile.Clone());
                        cards.Add(state.AvailableTile.Clone());
                        cards.Add(state.AvailableTile.Clone());

                        MahjongMeld m = new MahjongMeld(cards);

                        Hand h; List <MahjongMeld> melds;
                        CreateDuplicateMinusMeld(ai.CardsInHand, mp.Melds, m, state.AvailableTile, out h, out melds);

                        moves.Add(new MahjongMove(m, state.AvailableTile, MahjongStaticFunctions.HasMahjong(h, melds)));

                        // Now create a kong move if possible
                        if (count > 2)
                        {
                            cards.Add(state.AvailableTile.Clone());

                            m = new MahjongMeld(cards);
                            CreateDuplicateMinusMeld(ai.CardsInHand, mp.Melds, m, state.AvailableTile, out h, out melds);

                            moves.Add(new MahjongMove(m, state.AvailableTile, MahjongStaticFunctions.HasMahjong(h, melds)));
                        }
                    }

                    // Now add the ability to declare mahjong through non-standard means
                    // Nine gates can only be won on a self pick, so let's deal with thirteen orphans and seven pair first
                    Hand htemp = new StandardHand(ai.CardsInHand.Cards);
                    htemp.DrawCard(state.AvailableTile.Clone());

                    MahjongMeld mtemp = new MahjongMeld(htemp.Cards);

                    if (mtemp.SevenPair || mtemp.ThirteenOrphans)
                    {
                        moves.Add(new MahjongMove(mtemp, state.AvailableTile, true));
                    }

                    // The last mahjong we need to check now is if we can form an eye and win
                    if (count > 0)
                    {
                        List <Card> cards = new List <Card>();

                        cards.Add(state.AvailableTile.Clone());
                        cards.Add(state.AvailableTile.Clone());

                        MahjongMeld m = new MahjongMeld(cards);

                        Hand h; List <MahjongMeld> melds;
                        CreateDuplicateMinusMeld(ai.CardsInHand, mp.Melds, m, state.AvailableTile, out h, out melds);

                        if (MahjongStaticFunctions.HasMahjong(h, melds))
                        {
                            moves.Add(new MahjongMove(m, state.AvailableTile, true));
                        }
                    }
                }

                // Lastly, add the simple pass option
                moves.Add(new MahjongMove());
            }

            return(moves.GetEnumerator());
        }
Exemplo n.º 6
0
        /// <summary>
        /// Determines the next move for this AI to make.
        /// </summary>
        /// <param name="state">The state of the game. This will be cloned so that the AI does not affect the actual game state.</param>
        /// <returns>Returns the next move to make based on the current game state.</returns>
        public MahjongMove GetNextMove(GameState <MahjongMove> state)
        {
            MahjongGameState s  = state as MahjongGameState;
            MahjongAIPlayer  ai = state.GetPlayer(Player) as MahjongAIPlayer;
            MahjongPlayer    mp = ai as MahjongPlayer;

            // Pass if it's not this AI's turn and it can't meld a pung or kong
            if (s.ActivePlayer != Player)
            {
                if (s.AvailableTile == null)
                {
                    return(new MahjongMove()); // The active player is going out, so there's nothing we can do
                }
                else
                {
                    switch (ai.CardsInHand.CountCard(s.AvailableTile))
                    {
                    case 1:
                        // If this is the last tile we needed to declare mahjong, go for it
                        if (ai.CardsInHand.CardsInHand == 1)
                        {
                            List <Card> eye = new List <Card>();
                            eye.Add(s.AvailableTile.Clone());
                            eye.Add(s.AvailableTile.Clone());

                            return(new MahjongMove(new MahjongMeld(eye, false), s.AvailableTile, true));
                        }

                        // Pass otherwise
                        return(new MahjongMove());

                    case 2:
                        List <Card> p2 = new List <Card>();
                        p2.Add(s.AvailableTile.Clone());
                        p2.Add(s.AvailableTile.Clone());
                        p2.Add(s.AvailableTile.Clone());

                        Hand htemp2 = new StandardHand(ai.CardsInHand.Cards);
                        htemp2.PlayCard(s.AvailableTile);
                        htemp2.PlayCard(s.AvailableTile);

                        MahjongMeld        m2     = new MahjongMeld(p2, false);
                        List <MahjongMeld> melds2 = new List <MahjongMeld>();

                        foreach (MahjongMeld m22 in mp.Melds)
                        {
                            melds2.Add(m22.Clone());
                        }

                        return(new MahjongMove(m2, s.AvailableTile, MahjongStaticFunctions.HasMahjong(htemp2, melds2)));

                    case 3:
                        List <Card> p3 = new List <Card>();
                        p3.Add(s.AvailableTile.Clone());
                        p3.Add(s.AvailableTile.Clone());
                        p3.Add(s.AvailableTile.Clone());
                        p3.Add(s.AvailableTile.Clone());

                        Hand htemp3 = new StandardHand(ai.CardsInHand.Cards);
                        htemp3.PlayCard(s.AvailableTile);
                        htemp3.PlayCard(s.AvailableTile);
                        htemp3.PlayCard(s.AvailableTile);

                        MahjongMeld        m3     = new MahjongMeld(p3, false);
                        List <MahjongMeld> melds3 = new List <MahjongMeld>();

                        foreach (MahjongMeld m33 in mp.Melds)
                        {
                            melds3.Add(m33.Clone());
                        }

                        return(new MahjongMove(m3, s.AvailableTile, MahjongStaticFunctions.HasMahjong(htemp3, melds3)));

                    default:
                        return(new MahjongMove());    // If we naturally drew a kong, it'll stick around until we're forced to discard one of the tiles or just lose
                    }
                }
            }

            // If it's our turn, check if we have mahjong
            if (MahjongStaticFunctions.HasMahjong(ai.CardsInHand, mp.Melds))
            {
                return(new MahjongMove(true));
            }

            // Find all the singletons
            List <int> singletons = new List <int>();

            for (int i = 0; i < ai.CardsInHand.CardsInHand; i++)
            {
                if (ai.CardsInHand.CountCard(ai.CardsInHand.Cards[i]) == 1)
                {
                    singletons.Add(i);
                }
            }

            // If we don't have a singleton tile, we'll have to part with another
            if (singletons.Count == 0)
            {
                singletons.Add(rand.Next(0, (int)ai.CardsInHand.CardsInHand));
                rc++;
            }

            // Pick a random singleton tile to discard
            int r = rand.Next(0, singletons.Count);

            rc++;

            return(new MahjongMove(ai.CardsInHand.Cards[r]));
        }