/// <summary>
        /// Finds the TileTypeId of the discard with highest UkeIre while maintaining shanten.
        /// </summary>
        public int GetHighestUkeIreDiscard()
        {
            Debug.Assert(TilesInHand() == 14, "Have to be able to discard a tile");

            // If we have a winning hand, all discards will lead to worse shanten
            var currentShanten = CalculateShanten(ArrangementValues);

            if (currentShanten == 0)
            {
                return(ConcealedTiles[0]);
            }

            var tileTypeId        = 0;
            var localArrangements = new[] { ArrangementValues[0], ArrangementValues[1], ArrangementValues[2], ArrangementValues[3] };

            var highestUkeIre        = -1;
            var highestUkeIreDiscard = -1;

            for (var suit = 0; suit < 3; ++suit)
            {
                for (var index = 0; index < 9; ++index)
                {
                    if (ConcealedTiles[tileTypeId] > 0)
                    {
                        var kyuuhaiValue = (0b100000001 >> index) & 1;
                        ConcealedTiles[tileTypeId] -= 1;
                        InHandByType[tileTypeId]   -= 1;
                        Kokushi.Discard(kyuuhaiValue, ConcealedTiles[tileTypeId]);
                        Chiitoi.Discard(ConcealedTiles[tileTypeId]);
                        Base5Hashes[suit] -= Base5.Table[index];

                        localArrangements[suit] = SuitClassifiers[suit].GetValue(ConcealedTiles, suit, Base5Hashes);
                        var newShanten = CalculateShanten(localArrangements);

                        if (newShanten == currentShanten)
                        {
                            var ukeIre = SumUkeIre(currentShanten, localArrangements, HonorClassifier);
                            if (ukeIre > highestUkeIre)
                            {
                                highestUkeIre        = ukeIre;
                                highestUkeIreDiscard = tileTypeId;
                            }
                        }

                        Base5Hashes[suit] += Base5.Table[index];
                        Kokushi.Draw(kyuuhaiValue, ConcealedTiles[tileTypeId]);
                        Chiitoi.Draw(ConcealedTiles[tileTypeId]);
                        ConcealedTiles[tileTypeId] += 1;
                        InHandByType[tileTypeId]   += 1;
                    }

                    tileTypeId += 1;
                }

                localArrangements[suit] = ArrangementValues[suit];
            }

            for (var index = 0; index < 7; ++index)
            {
                if (ConcealedTiles[tileTypeId] > 0)
                {
                    ConcealedTiles[tileTypeId] -= 1;
                    InHandByType[tileTypeId]   -= 1;
                    var tileCountAfterDiscard = ConcealedTiles[tileTypeId];
                    Kokushi.Discard(1, tileCountAfterDiscard);
                    Chiitoi.Discard(tileCountAfterDiscard);

                    var localHonorClassifier = HonorClassifier.Clone();
                    localArrangements[3] = localHonorClassifier.Discard(tileCountAfterDiscard, JihaiMeldBit >> index & 1);
                    var newShanten = CalculateShanten(localArrangements);

                    if (newShanten == currentShanten)
                    {
                        var ukeIre = SumUkeIre(currentShanten, localArrangements, localHonorClassifier);
                        if (ukeIre > highestUkeIre)
                        {
                            highestUkeIre        = ukeIre;
                            highestUkeIreDiscard = tileTypeId;
                        }
                    }

                    Chiitoi.Draw(tileCountAfterDiscard);
                    Kokushi.Draw(1, tileCountAfterDiscard);
                    ConcealedTiles[tileTypeId] += 1;
                    InHandByType[tileTypeId]   += 1;
                }

                tileTypeId += 1;
            }

            Debug.Assert(highestUkeIreDiscard != -1, "There should always be a tile to discard.");
            return(highestUkeIreDiscard);
        }
Exemplo n.º 2
0
        public YakuConfig()
        {
            var id = 0;

            aka_dora = new AkaDora(++id);
            tenhou   = new Tenhou(++id);
            // Yaku situations
            tsumo              = new Tsumo(++id);
            riichi             = new Riichi(++id);
            open_riichi        = new OpenRiichi(++id);
            ippatsu            = new Ippatsu(++id);
            chankan            = new Chankan(++id);
            rinshan            = new Rinshan(++id);
            haitei             = new Haitei(++id);
            houtei             = new Houtei(++id);
            daburu_riichi      = new DaburuRiichi(++id);
            daburu_open_riichi = new DaburuOpenRiichi(++id);
            nagashi_mangan     = new NagashiMangan(++id);
            renhou             = new Renhou(++id);
            // Yaku 1 Han
            pinfu         = new Pinfu(++id);
            tanyao        = new Tanyao(++id);
            iipeiko       = new Iipeikou(++id);
            haku          = new Haku(++id);
            hatsu         = new Hatsu(++id);
            chun          = new Chun(++id);
            east          = new East(++id);
            south         = new South(++id);
            west          = new West(++id);
            north         = new North(++id);
            yakuhai_place = new YakuhaiPlace(++id);
            yakuhai_round = new YakuhaiRound(++id);
            // Yaku 2 Hans
            sanshoku       = new Sanshoku(++id);
            ittsu          = new Ittsu(++id);
            chantai        = new Chanta(++id);
            honroto        = new Honroutou(++id);
            toitoi         = new Toitoi(++id);
            sanankou       = new Sanankou(++id);
            sankantsu      = new Sankantsu(++id);
            sanshoku_douko = new SanshokuDoukou(++id);
            chiitoitsu     = new Chiitoitsu(++id);
            shosangen      = new Shousangen(++id);
            // Yaku 3 Hans
            honitsu   = new Honitsu(++id);
            junchan   = new Junchan(++id);
            ryanpeiko = new Ryanpeikou(++id);
            // Yaku 6 Hans
            chinitsu = new Chinitsu(++id);
            // Yakuman list
            kokushi        = new Kokushi(++id);
            chuuren_poutou = new ChuurenPoutou(++id);
            suuankou       = new Suuankou(++id);
            daisangen      = new Daisangen(++id);
            shosuushi      = new Shousuushii(++id);
            ryuisou        = new Ryuuiisou(++id);
            suukantsu      = new Suukantsu(++id);
            tsuisou        = new Tsuisou(++id);
            chinroto       = new Chinroutou(++id);
            daisharin      = new Daisharin(++id);
            daichisei      = new Daichisei(++id);
            // Double yakuman
            daisuushi             = new Daisuushii(++id);
            daburu_kokushi        = new DaburuKokushi(++id);
            suuankou_tanki        = new SuuankouTanki(++id);
            daburu_chuuren_poutou = new DaburuChuurenPoutou(++id);
            // Yakuman situations
            tenhou         = new Tenhou(id++);
            chiihou        = new Chiihou(++id);
            renhou_yakuman = new RenhouYakuman(++id);
            sashikomi      = new Sashikomi(++id);
            paarenchan     = new Paarenchan(++id);
            // Other
            dora     = new Dora(++id);
            aka_dora = new AkaDora(++id);
        }
        private int SumUkeIre(int currentShanten, int[] arrangements, ProgressiveHonorClassifier localHonorClassifier)
        {
            var ukeIre            = 0;
            var tileTypeId        = 0;
            var localArrangements = new[] { arrangements[0], arrangements[1], arrangements[2], arrangements[3] };

            for (var suit = 0; suit < 3; ++suit)
            {
                for (var index = 0; index < 9; ++index)
                {
                    if (InHandByType[tileTypeId] != 4)
                    {
                        var kyuuhaiValue = (0b100000001 >> index) & 1;
                        Kokushi.Draw(kyuuhaiValue, ConcealedTiles[tileTypeId]);
                        Chiitoi.Draw(ConcealedTiles[tileTypeId]);
                        ConcealedTiles[tileTypeId] += 1;
                        Base5Hashes[suit]          += Base5.Table[index];

                        localArrangements[suit] = SuitClassifiers[suit].GetValue(ConcealedTiles, suit, Base5Hashes);
                        var newShanten = CalculateShanten(localArrangements);
                        Debug.Assert(currentShanten >= newShanten);

                        var a = currentShanten - newShanten;
                        var t = (4 - InHandByType[tileTypeId]) * a;
                        ukeIre += t;

                        ConcealedTiles[tileTypeId] -= 1;
                        Base5Hashes[suit]          -= Base5.Table[index];
                        Kokushi.Discard(kyuuhaiValue, ConcealedTiles[tileTypeId]);
                        Chiitoi.Discard(ConcealedTiles[tileTypeId]);
                    }

                    tileTypeId += 1;
                }

                localArrangements[suit] = arrangements[suit];
            }

            for (var index = 0; index < 7; ++index)
            {
                if (InHandByType[tileTypeId] != 4)
                {
                    var previousTileCount = ConcealedTiles[tileTypeId];
                    Kokushi.Draw(1, previousTileCount);
                    Chiitoi.Draw(previousTileCount);

                    localArrangements[3] = localHonorClassifier.Clone().Draw(previousTileCount, JihaiMeldBit >> index & 1);
                    var newShanten = CalculateShanten(localArrangements);
                    Debug.Assert(currentShanten >= newShanten);

                    var a = currentShanten - newShanten;
                    var t = (4 - InHandByType[tileTypeId]) * a;
                    ukeIre += t;

                    Kokushi.Discard(1, previousTileCount);
                    Chiitoi.Discard(previousTileCount);
                }

                tileTypeId += 1;
            }

            return(ukeIre);
        }