Пример #1
0
        /// <summary>
        /// Handles all the logic necessary for a declaration of mahjong, including scoring and setting up the next round, and, if necessary, delcaring the game finished.
        /// </summary>
        protected void Mahjong(int player_index, bool stolen_from_kong = false)
        {
            MahjongPlayer[] mp = new MahjongPlayer[4];

            for (int i = 0; i < 4; i++)
            {
                mp[i] = players[i] as MahjongPlayer;
            }

            // Score the hand
            int fan         = MahjongStaticFunctions.HandFan(mp[player_index].Melds, mp[player_index].SeatWind, PrevailingWind, player_moves[player_index].SelfPick, Deck.CountDrawPile == 0, OnReplacement > 0, OnReplacement > 1, stolen_from_kong, Heavenly, Earthly);
            int base_points = MahjongStaticFunctions.FanToBasePoints(fan);

            // Payout the score
            for (int i = 0; i < 4; i++)
            {
                if (i != player_index)
                {
                    int factor = payment_factor(player_index, i);

                    mp[i].Score            -= factor * base_points;
                    mp[player_index].Score += factor * base_points;
                }
            }

            // Go to the next hand
            if (mp[player_index].SeatWind == SuitIdentifier.EAST_WIND)
            {
                // East won, so we have a bonus hand
                BonusHand++;

                if (BonusHand > MaxBonusHand)
                {
                    BonusHand = 0;
                    Hand++;
                }
            }
            else
            {
                // East didn't win, so no bonus hand
                BonusHand = 0;
                Hand++;
            }

            if (!GameFinished)
            {
                InitHand();
            }

            return;
        }
Пример #2
0
        /// <summary>
        /// Copies the data in hand and melds into new objects assigned to h and m. It then adds meld to m, add to h, and discards from h every tile in meld.
        /// Add can be null.
        /// </summary>
        private void CreateDuplicateMinusMeld(Hand hand, List <MahjongMeld> melds, MahjongMeld meld, Card add, out Hand h, out List <MahjongMeld> m)
        {
            MahjongStaticFunctions.CopyData(hand, melds, out h, out m);
            m.Add(meld);

            if (add != null)
            {
                h.DrawCard(add.Clone());
            }

            foreach (Card c in meld.Cards)
            {
                h.PlayCard(c);
            }

            return;
        }
Пример #3
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]));
        }
Пример #4
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());
        }
Пример #5
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]));
        }
Пример #6
0
        /// <summary>
        /// Given a move encoded in m, will perform all the necessary work to make the player reflect the move occuring.
        /// </summary>
        /// <param name="m">The move to make. It is assumed that the move is valid (and not null).</param>
        /// <returns>Returns true if the move is valid and false otherwise.</returns>
        public override bool MakePlay(MahjongMove m)
        {
            // We assume that the move we're given is valid, so let's jump right in
            if (m.Pass)
            {
                return(true);
            }

            // Discarding is simple to deal with
            if (m.Discard)
            {
                CardsInHand.PlayCard(m.DiscardedTile);
                return(true);
            }

            // Melding takes a bit of work
            // Note that if we're melding to declare mahjong, the meld is fixed and must happen, so we do it here always
            if (m.Meld)
            {
                // First decide if we're upgrading a pung
                bool upgrade = false;

                if (m.MeldTiles.Kong)
                {
                    foreach (MahjongMeld meld in Melds)
                    {
                        if (meld.Pung && meld.Cards.Contains(m.DiscardedTile))
                        {
                            upgrade = true;
                            break;
                        }
                    }
                }

                if (upgrade)
                {
                    // If we're adding to a pung to make a kong, we only need to play the tile we drew (and we HAVE to have drawn the tile from the wall)
                    CardsInHand.PlayCard(m.DiscardedTile);

                    // We do also have to update the meld we already have
                    foreach (MahjongMeld meld in Melds)
                    {
                        if (meld.ConvertToKong(m.DiscardedTile))                        // This will eventually work and fail all other times
                        {
                            break;
                        }
                    }
                }
                else
                {
                    // Add the meld
                    Melds.Add(m.MeldTiles.Clone());

                    // We're not expanding a pung, so we don't have to worry about that case now
                    List <Card> meld = new List <Card>(m.MeldTiles.Cards);

                    if (!m.ConcealedMeld)                    // If this is a concealed meld, then we drew the tile we're currently melding
                    {
                        meld.Remove(m.DiscardedTile);        // We don't need to play the tile we don't technically have yet
                    }
                    foreach (Card c in meld)
                    {
                        CardsInHand.PlayCard(c);
                    }
                }
            }

            // If we have Mahjong, we need to put whatever is left of the player's hand into melds
            // This can occur as a meld is declared, so we don't return early before to catch that case here
            if (m.Mahjong)
            {
                List <MahjongMeld> mm = MahjongStaticFunctions.BestMahjong(CardsInHand, Melds, SeatWind, PrevailingWind, m.SelfPick);

                if (mm != null)                // This should always be the case, but let's be careful
                {
                    Melds = mm;
                }
            }

            return(true);
        }