Esempio n. 1
0
        /// <summary>
        /// The recursive version of the GetAllMahjongs function.
        /// This algorithm does not clobber data.
        /// </summary>
        /// <param name="hand">The set of tiles free to make melds as desired.</param>
        /// <param name="melds">The set of melds that cannot be changed.</param>
        /// <param name="ret">The set to add new winning hands to.</param>
        private static void GetAllMahjongs(Hand hand, List <MahjongMeld> melds, List <List <MahjongMeld> > ret)
        {
            // If we have less than 2 tiles left, something has gone horribly wrong
            if (hand.CardsInHand < 2)
            {
                return;
            }

            // If we have a limit hand, then get it out of the way right away
            if (hand.CardsInHand == 14)            // Note that this can only happen if we have no fixed melds
            {
                MahjongMeld meld = new MahjongMeld(hand.Cards, true);

                if (meld.Valid)
                {
                    List <MahjongMeld> temp = new List <MahjongMeld>();
                    temp.Add(meld);

                    ret.Add(temp);
                }
            }

            // If we have two tiles left, then we need them to be an eye
            if (hand.CardsInHand == 2)
            {
                MahjongMeld meld = new MahjongMeld(hand.Cards, true);

                // If we don't have an eye, this won't form a winning hand
                if (!meld.Eye)
                {
                    return;
                }

                // If we have an eye, then we need to make sure what we have is valid
                // Copying the data into a new list here is very important, because we're going to return a whole bunch of lists that need to all be distinct
                List <MahjongMeld> melds2 = new List <MahjongMeld>();

                foreach (MahjongMeld m in melds)
                {
                    melds2.Add(m.Clone());
                }

                melds2.Add(meld);

                if (IsMahjong(melds2))                // It should be, but it's possible we were originally passed weird melds
                {
                    ret.Add(melds2);
                }

                return;                 // We have nothing further to do, so return
            }

            // A simple check for failure
            // We must have an eye and melds of size 3 as we don't have extra tiles to make kongs
            if ((hand.CardsInHand - 2) % 3 != 0)
            {
                return;
            }

            // This call for BRUTE FORCE
            for (int i = 0; i < hand.CardsInHand - 2; i++)
            {
                for (int j = i + 1; j < hand.CardsInHand - 1; j++)
                {
                    for (int k = j + 1; k < hand.CardsInHand; k++)
                    {
                        // Check if (i,j,k) forms a meld
                        List <Card> l = new List <Card>();

                        l.Add(hand.Cards[i]);
                        l.Add(hand.Cards[j]);
                        l.Add(hand.Cards[k]);

                        MahjongMeld meld = new MahjongMeld(l, true);

                        // If we do have a meld, meld it and go deeper
                        if (meld.Valid)
                        {
                            // Add the meld
                            melds.Add(meld);

                            // We want to preserve card order in the hand, and the easiest way to do that is to just make a new hand
                            Hand temp = new StandardHand(hand.Cards);

                            // i < j < k, so this is fine
                            temp.PlayCard(k);
                            temp.PlayCard(j);
                            temp.PlayCard(i);

                            // Go deeper
                            GetAllMahjongs(temp, melds, ret);

                            // Remove the meld
                            melds.RemoveAt(melds.Count - 1);
                        }
                    }
                }
            }

            return;
        }
Esempio n. 2
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]));
        }
Esempio n. 3
0
        /// <summary>
        /// Creates a new meld from the given tiles.
        /// </summary>
        /// <param name="meld">The tiles to meld.</param>
        /// <param name="concealed">If true, the meld will be treated as concealed.</param>
        public MahjongMeld(IEnumerable <Card> meld, bool concealed = false) : base(meld)
        {
            Chow = false;
            Pung = false;
            Kong = false;
            Eye  = false;

            ThirteenOrphans = false;
            NineGates       = false;
            SevenPair       = false;

            Concealed          = concealed;
            ExposedFromExposed = false;

            // There are two special cases we need to handle
            // There's thirteen orphans and nine gates (the latter is a special case because it must be all concealed and has meaning beyond the melds it makes)
            if (CardsInHand == 14)
            {
                // Thirteen orphans is easier to check, so do that first
                Card[] orphans = { new StandardCard(new SimpleSuit(SuitIdentifier.BAMBOO),       new ValueN(1, "One")), new StandardCard(new SimpleSuit(SuitIdentifier.BAMBOO),    new ValueN(9,                                               "Nine")),
                                   new StandardCard(new SimpleSuit(SuitIdentifier.CHARACTER),    new ValueN(1, "One")), new StandardCard(new SimpleSuit(SuitIdentifier.CHARACTER), new ValueN(9,                                               "Nine")),
                                   new StandardCard(new SimpleSuit(SuitIdentifier.DOT),          new ValueN(1, "One")), new StandardCard(new SimpleSuit(SuitIdentifier.DOT),       new ValueN(9,                                               "Nine")),
                                   new StandardCard(new HonourSuit(SuitIdentifier.GREEN_DRAGON), new ValueN(0, null),   "Green Dragon"),                                           new StandardCard(new HonourSuit(SuitIdentifier.RED_DRAGON), new ValueN(0,null),  "Red Dragon"), new StandardCard(new HonourSuit(SuitIdentifier.WHITE_DRAGON), new ValueN(0, null), "White Dragon"),
                                   new StandardCard(new HonourSuit(SuitIdentifier.EAST_WIND),    new ValueN(0, null),   "East Wind"),                                              new StandardCard(new HonourSuit(SuitIdentifier.SOUTH_WIND), new ValueN(0,null),  "South Wind"), new StandardCard(new HonourSuit(SuitIdentifier.WEST_WIND),    new ValueN(0, null), "West Wind"),   new StandardCard(new HonourSuit(SuitIdentifier.NORTH_WIND),new ValueN(0, null), "North Wind") };

                for (int i = 0; i < 13; i++)
                {
                    if (!Cards.Contains(orphans[i]))
                    {
                        break;
                    }
                    else if (i == 12)
                    {
                        ThirteenOrphans = true;
                        return;
                    }
                }

                // We don't have thirteen orphans, so check for seven pairs
                Hand temp = new StandardHand(Cards);

                // Check for pairs by sheer brute force
                for (int i = 0; i < 7; i++)
                {
                    Card c = temp.PlayCard(0);

                    if (!temp.Cards.Contains(c))
                    {
                        break;
                    }

                    temp.PlayCard(c);

                    if (i == 6)
                    {
                        SevenPair = true;
                        return;
                    }
                }

                // We don't have thirteen orphans or seven pairs, so check for nine gates
                CardSuit suit = Cards[0].Suit;

                // For optimisation, we check this here rather than letting it be implicitly check later by asking for values
                if (suit.Color != SuitColor.SIMPLE)
                {
                    return;
                }

                // Each card must be the same suit
                for (int i = 1; i < 14; i++)
                {
                    if (!suit.Equals(Cards[i].Suit))
                    {
                        return;
                    }
                }

                // Now we need the tiles 1112345678999 and one extra
                temp = new StandardHand(Cards);
                string[] value_names = { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" };

                // Check if all the cards are there by brute force
                for (int i = 1; i < 10; i++)
                {
                    for (int j = (i == 1 || i == 9 ? 0 : 2); j < 3; j++)                  // Ones and nines need to be played thrice
                    {
                        Card c = new StandardCard(suit, new ValueN(i, value_names[i - 1]));

                        if (!temp.Cards.Contains(c))
                        {
                            return;
                        }

                        temp.PlayCard(c);
                    }
                }

                // The last card doesn't matter as we know it's the right suit, so we're done
                NineGates = true;
                return;
            }

            if (CardsInHand < 2 || CardsInHand > 4)
            {
                return;
            }

            if (CardsInHand == 2)
            {
                if (Cards[0].Equals(Cards[1]))
                {
                    Eye = true;
                }

                return;
            }

            // If we have four tiles, our only option is a kong
            if (CardsInHand == 4)
            {
                // A kong must have four equal tiles
                for (int i = 0; i < 3; i++)
                {
                    if (!Cards[i].Equals(Cards[i + 1]))
                    {
                        return;
                    }
                }

                Kong = true;
                return;
            }

            // If we have three tiles and the first two are equal, we either have a pung or nothing
            if (Cards[0].Equals(Cards[1]))
            {
                if (!Cards[1].Equals(Cards[2]))
                {
                    return;
                }
                else
                {
                    Pung = true;
                    return;
                }
            }

            // We now have a chow or nothing, so make sure the tiles are of the same suit and are sequential
            if (!Cards[0].Suit.Equals(Cards[1].Suit) || !Cards[1].Suit.Equals(Cards[2].Suit))
            {
                return;
            }

            SortByValue();

            if (!ApproxEqual(Cards[0].Value.MaxValue + 1.0, Cards[1].Value.MaxValue) || !ApproxEqual(Cards[1].Value.MaxValue + 1.0, Cards[2].Value.MaxValue))
            {
                return;
            }

            Chow = true;
            return;
        }