예제 #1
0
        public void TilesSetToTiles136Test()
        {
            var expected = new TileIds(new List <int>
            {
                0, 32, 33, 36, 37, 68, 69, 72, 73, 104, 105, 108, 109, 132
            });
            var t      = expected.ToTiles34();
            var actual = t.ToTileIds();

            for (var i = 0; i < expected.Count; i++)
            {
                AreEqual(expected[i].Value, actual[i].Value);
            }
        }
예제 #2
0
        /// <summary>
        /// シャンテン数を計算する 0:テンパイ, -1: あがり
        /// </summary>
        /// <param name="tiles"></param>
        /// <param name="openSets"></param>
        /// <param name="chiitoitsu"></param>
        /// <param name="kokushi"></param>
        /// <returns></returns>
        public static int CalculateShanten(TileIds tiles, IList <TileKinds> openSets = null,
                                           bool chiitoitsu = true, bool kokushi = true)
        {
            var tiles34 = tiles.ToTiles34();

            Init(tiles34);
            var countOfTiles = tiles34.Sum();

            if (countOfTiles > 14)
            {
                throw new ArgumentException("牌の数が14個より多いです。", nameof(countOfTiles));
            }

            if (!(openSets is null || openSets.Count == 0))
            {
                var isolatedTiles = tiles_.FindIsolatedTileIndices();
                foreach (var meld in openSets)
                {
                    if (isolatedTiles.Count == 0)
                    {
                        break;
                    }

                    var lastIndex    = isolatedTiles.Count - 1;
                    var isolatedTile = isolatedTiles[lastIndex];
                    isolatedTiles.RemoveAt(lastIndex);

                    tiles34[meld[0].Value]     -= 1;
                    tiles34[meld[1].Value]     -= 1;
                    tiles34[meld[2].Value]     -= 1;
                    tiles34[isolatedTile.Value] = 3;
                }
            }

            if (openSets is null || openSets.Count == 0)
            {
                minShanten_ = ScanChiitoitsuAndKokushi(chiitoitsu, kokushi);
            }

            RemoveCharacterTiles(countOfTiles);

            var initMentsu = (14 - countOfTiles) / 3;

            Scan(initMentsu);
            return(minShanten_);
        }
예제 #3
0
        public void Tiles136ToTilesSet()
        {
            var tiles = new TileIds(new List <int>
            {
                0, 34, 35, 36, 37, 70, 71, 72, 73, 106, 107, 108, 109, 134
            });

            var actual = tiles.ToTiles34();

            AreEqual(1, actual[0]);
            AreEqual(2, actual[8]);
            AreEqual(2, actual[9]);
            AreEqual(2, actual[17]);
            AreEqual(2, actual[18]);
            AreEqual(2, actual[26]);
            AreEqual(2, actual[27]);
            AreEqual(1, actual[33]);
            AreEqual(14, actual.Sum());
        }
예제 #4
0
        public static HandResponse EstimateHandValue(TileIds tiles,
                                                     TileId winTile,
                                                     List <Meld> melds      = null,
                                                     TileIds doraIndicators = null,
                                                     HandConfig config      = null)
        {
            if (melds is null)
            {
                melds = new List <Meld>();
            }
            if (doraIndicators is null)
            {
                doraIndicators = new TileIds();
            }
            config_ = config ?? new HandConfig();

            var handYaku    = new List <Yaku>();
            var tiles34     = tiles.ToTiles34();
            var openedMelds = melds.Where(x => x.Opened)
                              .Select(x => x.TileKinds)
                              .ToList();
            var allMelds   = melds.Select(x => x.TileKinds).ToList();
            var isOpenHand = openedMelds.Count() > 0;

            if (config_.IsNagashiMangan)
            {
                handYaku.Add(new NagashiMangan());
                var fu   = 30;
                var han  = new NagashiMangan().HanClosed;
                var cost = CalculateScores(han, fu, config_, false);
                return(new HandResponse(cost, han, fu, handYaku));
            }

            if (!IsAgari(tiles, allMelds))
            {
                return(new HandResponse(error: "Hand is not winning"));
            }

            if (!tiles.Contains(winTile))
            {
                return(new HandResponse(error: "Win tile not in the hand"));
            }

            if (config_.IsRiichi && isOpenHand)
            {
                return(new HandResponse(error: "Riichi can't be declared with open hand"));
            }
            if (config_.IsDaburuRiichi && isOpenHand)
            {
                return(new HandResponse(error: "Daburu Riichi can't be declared with open hand"));
            }
            if (config_.IsIppatsu && isOpenHand)
            {
                return(new HandResponse(error: "Ippatsu can't be declared with open hand"));
            }

            if (config_.IsIppatsu && !config_.IsRiichi && !config_.IsDaburuRiichi)
            {
                return(new HandResponse(error: "Ippatsu can't be declared without riichi"));
            }

            var handOptions     = DivideHand(tiles34, melds);
            var calculatedHands = new List <HandResponse>();

            foreach (var hand in handOptions)
            {
                var isChiitoitsu = new Chiitoitsu().IsConditionMet(hand);
                var valuedTiles  = new List <int>
                {
                    HAKU, HATSU, CHUN,
                    config_.PlayerWind, config_.RoundWind,
                };
                var winGroups = FindWinGroups(winTile, hand, openedMelds);
                foreach (var winGroup in winGroups)
                {
                    Cost   cost  = null;
                    string error = null;
                    handYaku = new List <Yaku>();
                    var han = 0;
                    var(fuDetails, fu) = CalculateFu(
                        hand, winTile, winGroup, config_, valuedTiles, melds);
                    var isPinfu = fuDetails.Count == 1 && !isChiitoitsu && !isOpenHand;
                    var ponSets = hand.Where(x => x.IsPon);
                    var chiSets = hand.Where(x => x.IsChi);
                    if (config_.IsTsumo)
                    {
                        if (!isOpenHand)
                        {
                            handYaku.Add(new Tsumo());
                        }
                    }
                    if (isPinfu)
                    {
                        handYaku.Add(new Pinfu());
                    }
                    if (isChiitoitsu && isOpenHand)
                    {
                        continue;
                    }
                    if (isChiitoitsu)
                    {
                        handYaku.Add(new Chiitoitsu());
                    }
                    var isDaisharin = new Daisharin().IsConditionMet(hand);
                    if (config_.Options.HasDaisharin && isDaisharin)
                    {
                        handYaku.Add(new Daisharin());
                    }
                    if (config_.IsRiichi && !config_.IsDaburuRiichi)
                    {
                        handYaku.Add(new Riichi());
                    }
                    if (config_.IsDaburuRiichi)
                    {
                        handYaku.Add(new DaburuRiichi());
                    }
                    var isTanyao = new Tanyao().IsConditionMet(hand);
                    if (isOpenHand && !config_.Options.HasOpenTanyao)
                    {
                        isTanyao = false;
                    }
                    if (isTanyao)
                    {
                        handYaku.Add(new Tanyao());
                    }
                    if (config_.IsIppatsu)
                    {
                        handYaku.Add(new Ippatsu());
                    }
                    if (config_.IsRinshan)
                    {
                        handYaku.Add(new Rinshan());
                    }
                    if (config_.IsChankan)
                    {
                        handYaku.Add(new Chankan());
                    }
                    if (config_.IsHaitei)
                    {
                        handYaku.Add(new Haitei());
                    }
                    if (config_.IsHoutei)
                    {
                        handYaku.Add(new Houtei());
                    }
                    if (config_.IsRenhou)
                    {
                        if (config_.Options.RenhouAsYakuman)
                        {
                            handYaku.Add(new RenhouYakuman());
                        }
                        else
                        {
                            handYaku.Add(new Renhou());
                        }
                    }
                    if (config_.IsTenhou)
                    {
                        handYaku.Add(new Tenhou());
                    }
                    if (config_.IsChiihou)
                    {
                        handYaku.Add(new Chiihou());
                    }
                    if (new Honitsu().IsConditionMet(hand))
                    {
                        handYaku.Add(new Honitsu());
                    }
                    if (new Chinitsu().IsConditionMet(hand))
                    {
                        handYaku.Add(new Chinitsu());
                    }
                    if (new Tsuuiisou().IsConditionMet(hand))
                    {
                        handYaku.Add(new Tsuuiisou());
                    }
                    if (new Honroto().IsConditionMet(hand))
                    {
                        handYaku.Add(new Honroto());
                    }
                    if (new Chinroutou().IsConditionMet(hand))
                    {
                        handYaku.Add(new Chinroutou());
                    }
                    if (new Ryuuiisou().IsConditionMet(hand))
                    {
                        handYaku.Add(new Ryuuiisou());
                    }

                    //順子が必要な役
                    if (chiSets.Count() != 0)
                    {
                        if (new Chanta().IsConditionMet(hand))
                        {
                            handYaku.Add(new Chanta());
                        }
                        if (new Junchan().IsConditionMet(hand))
                        {
                            handYaku.Add(new Junchan());
                        }
                        if (new Ittsu().IsConditionMet(hand))
                        {
                            handYaku.Add(new Ittsu());
                        }
                        if (!isOpenHand)
                        {
                            if (new Ryanpeikou().IsConditionMet(hand))
                            {
                                handYaku.Add(new Ryanpeikou());
                            }
                            else if (new Iipeiko().IsConditionMet(hand))
                            {
                                handYaku.Add(new Iipeiko());
                            }
                        }
                        if (new Sanshoku().IsConditionMet(hand))
                        {
                            handYaku.Add(new Sanshoku());
                        }
                    }

                    //刻子が必要な役
                    if (ponSets.Count() != 0)
                    {
                        if (new Toitoi().IsConditionMet(hand))
                        {
                            handYaku.Add(new Toitoi());
                        }
                        if (new Sanankou().IsConditionMet(hand, new object[]
                        {
                            winTile, melds, config_.IsTsumo
                        }))
                        {
                            handYaku.Add(new Sanankou());
                        }
                        if (new SanshokuDoukou().IsConditionMet(hand))
                        {
                            handYaku.Add(new SanshokuDoukou());
                        }
                        if (new Shosangen().IsConditionMet(hand))
                        {
                            handYaku.Add(new Shosangen());
                        }
                        if (new Haku().IsConditionMet(hand))
                        {
                            handYaku.Add(new Haku());
                        }
                        if (new Hatsu().IsConditionMet(hand))
                        {
                            handYaku.Add(new Hatsu());
                        }
                        if (new Chun().IsConditionMet(hand))
                        {
                            handYaku.Add(new Chun());
                        }
                        if (new YakuhaiEast().IsConditionMet(hand, new object[]
                        {
                            config_.PlayerWind, config_.RoundWind
                        }))
                        {
                            if (config_.PlayerWind == EAST)
                            {
                                handYaku.Add(new YakuhaiOfPlace());
                            }
                            if (config_.RoundWind == EAST)
                            {
                                handYaku.Add(new YakuhaiOfRound());
                            }
                        }
                        if (new YakuhaiSouth().IsConditionMet(hand, new object[]
                        {
                            config_.PlayerWind, config_.RoundWind
                        }))
                        {
                            if (config_.PlayerWind == SOUTH)
                            {
                                handYaku.Add(new YakuhaiOfPlace());
                            }
                            if (config_.RoundWind == SOUTH)
                            {
                                handYaku.Add(new YakuhaiOfRound());
                            }
                        }
                        if (new YakuhaiWest().IsConditionMet(hand, new object[]
                        {
                            config_.PlayerWind, config_.RoundWind
                        }))
                        {
                            if (config_.PlayerWind == WEST)
                            {
                                handYaku.Add(new YakuhaiOfPlace());
                            }
                            if (config_.RoundWind == WEST)
                            {
                                handYaku.Add(new YakuhaiOfRound());
                            }
                        }
                        if (new YakuhaiNorth().IsConditionMet(hand, new object[]
                        {
                            config_.PlayerWind, config_.RoundWind
                        }))
                        {
                            if (config_.PlayerWind == NORTH)
                            {
                                handYaku.Add(new YakuhaiOfPlace());
                            }
                            if (config_.RoundWind == NORTH)
                            {
                                handYaku.Add(new YakuhaiOfRound());
                            }
                        }
                        if (new Daisangen().IsConditionMet(hand))
                        {
                            handYaku.Add(new Daisangen());
                        }
                        if (new Shousuushii().IsConditionMet(hand))
                        {
                            handYaku.Add(new Shousuushii());
                        }
                        if (new DaiSuushi().IsConditionMet(hand))
                        {
                            if (config_.Options.HasDoubleYakuman)
                            {
                                handYaku.Add(new DaiSuushi());
                            }
                            else
                            {
                                handYaku.Add(new DaiSuushi {
                                    HanOpen = 13, HanClosed = 13
                                });
                            }
                        }
                        if (melds.Count() == 0 && new ChuurenPoutou().IsConditionMet(hand))
                        {
                            if (tiles34[winTile.Value / 4] == 2 ||
                                tiles34[winTile.Value / 4] == 4)
                            {
                                if (config_.Options.HasDoubleYakuman)
                                {
                                    handYaku.Add(new DaburuChuurenPoutou());
                                }
                                else
                                {
                                    handYaku.Add(new DaburuChuurenPoutou {
                                        HanClosed = 13
                                    });
                                }
                            }
                            else
                            {
                                handYaku.Add(new ChuurenPoutou());
                            }
                        }
                        if (!isOpenHand && new Suuankou().IsConditionMet(hand, new object[]
                        {
                            winTile, config_.IsTsumo
                        }))
                        {
                            if (tiles34[winTile.Value / 4] == 2)
                            {
                                if (config_.Options.HasDoubleYakuman)
                                {
                                    handYaku.Add(new SuuankouTanki());
                                }
                                else
                                {
                                    handYaku.Add(new SuuankouTanki {
                                        HanClosed = 13
                                    });
                                }
                            }
                            else
                            {
                                handYaku.Add(new Suuankou());
                            }
                        }
                        if (new SanKantsu().IsConditionMet(hand, new object[]
                        {
                            melds
                        }))
                        {
                            handYaku.Add(new SanKantsu());
                        }
                        if (new Suukantsu().IsConditionMet(hand, new object[]
                        {
                            melds
                        }))
                        {
                            handYaku.Add(new Suukantsu());
                        }
                    }

                    //役満に役満以外の役は付かない
                    var yakumanList = handYaku.Where(x => x.IsYakuman)
                                      .ToList();
                    if (yakumanList.Count != 0)
                    {
                        handYaku = yakumanList;
                    }

                    //翻を計算する
                    foreach (var item in handYaku)
                    {
                        if (isOpenHand && item.HanOpen != 0)
                        {
                            han += item.HanOpen;
                        }
                        else
                        {
                            han += item.HanClosed;
                        }
                    }
                    if (han == 0)
                    {
                        error = "There are no yaku in the hand.";
                        cost  = null;
                    }

                    //役満にドラは付かない
                    if (yakumanList.Count == 0)
                    {
                        var tilesForDora = tiles.ToList();
                        foreach (var meld in melds)
                        {
                            if (meld.Type == MeldType.KAN || meld.Type == MeldType.CHANKAN)
                            {
                                tilesForDora.Add(meld.Tiles[3]);
                            }
                        }
                        var countOfDora    = 0;
                        var countOfAkaDora = 0;
                        foreach (var tile in tilesForDora)
                        {
                            countOfDora += PlusDora(tile, doraIndicators);
                        }
                        foreach (var tile in tilesForDora)
                        {
                            if (IsAkaDora(tile, config_.Options.HasAkaDora))
                            {
                                countOfAkaDora++;
                            }
                        }
                        if (countOfDora != 0)
                        {
                            handYaku.Add(new Dora {
                                HanOpen = countOfDora, HanClosed = countOfDora
                            });
                            han += countOfDora;
                        }
                        if (countOfAkaDora != 0)
                        {
                            handYaku.Add(new Akadora {
                                HanOpen = countOfAkaDora, HanClosed = countOfAkaDora
                            });
                            han += countOfAkaDora;
                        }
                    }
                    if (string.IsNullOrEmpty(error))
                    {
                        cost = CalculateScores(han, fu, config_, yakumanList.Count > 0);
                    }
                    calculatedHands.Add(new HandResponse(
                                            cost, han, fu, handYaku, error, fuDetails));
                }
            }
            if (!isOpenHand && new KokushiMusou().IsConditionMet(null, new object[] { tiles34 }))
            {
                if (tiles34[winTile.Value / 4] == 2)
                {
                    if (config_.Options.HasDoubleYakuman)
                    {
                        handYaku.Add(new DaburuKokushiMusou());
                    }
                    else
                    {
                        handYaku.Add(new DaburuKokushiMusou {
                            HanClosed = 13
                        });
                    }
                }
                else
                {
                    handYaku.Add(new KokushiMusou());
                }
                if (config_.IsRenhou && config_.Options.RenhouAsYakuman)
                {
                    handYaku.Add(new RenhouYakuman());
                }
                if (config_.IsTenhou)
                {
                    handYaku.Add(new Tenhou());
                }
                if (config_.IsChiihou)
                {
                    handYaku.Add(new Chiihou());
                }
                var han = 0;
                foreach (var item in handYaku)
                {
                    if (isOpenHand && item.HanOpen != 0)
                    {
                        han += item.HanOpen;
                    }
                    else
                    {
                        han += item.HanClosed;
                    }
                }
                var fu   = 0;
                var cost = CalculateScores(han, fu, config_, handYaku.Count > 0);
                handYaku.Sort((x, y) => x.YakuId.CompareTo(y.YakuId));
                calculatedHands.Add(new HandResponse(
                                        cost, han, fu, handYaku, null, new List <FuDetail>()));
            }
            calculatedHands.Sort((x, y) =>
                                 x.Han <y.Han ? 1 : x.Han> y.Han ? -1
                : x.Fu <y.Fu ? 1 : x.Fu> y.Fu ? -1 : 0);
            var resultHand = calculatedHands[0];

            resultHand.Yaku.Sort((x, y) => x.YakuId.CompareTo(y.YakuId));
            return(resultHand);
        }
예제 #5
0
파일: Agari.cs 프로젝트: tkitada/mahjong
        internal static bool IsAgari(TileIds tiles, IList <TileKinds> openSets = null)
        {
            var tiles_ = tiles.ToTiles34();

            if (!(openSets is null) && openSets.Count != 0)
            {
                var isolatedTiles = tiles_.FindIsolatedTileIndices();
                foreach (var meld in openSets)
                {
                    if (isolatedTiles.Count == 0)
                    {
                        break;
                    }

                    var lastIndex    = isolatedTiles.Count - 1;
                    var isolatedTile = isolatedTiles[lastIndex];
                    isolatedTiles.RemoveAt(lastIndex);

                    tiles_[meld[0].Value]     -= 1;
                    tiles_[meld[1].Value]     -= 1;
                    tiles_[meld[2].Value]     -= 1;
                    tiles_[isolatedTile.Value] = 3;
                }
            }

            //jは4桁のバイナリ
            var j = (1 << tiles_[27]) | (1 << tiles_[28]) | (1 << tiles_[29]) | (1 << tiles_[30]) |
                    (1 << tiles_[31]) | (1 << tiles_[32]) | (1 << tiles_[33]);

            //jに5桁目はない
            if (j >= 0x10)
            {
                return(false);
            }

            //国士無双判定
            //1桁目:0-すべての字牌は1つ以上存在する
            //2桁目:1-すべての字牌が2つ以上であってはならない
            //么九牌のうち2つの牌が1種類、それ以外は1つ
            if ((j & 3) == 2 &&
                tiles_[0] * tiles_[8] * tiles_[9] * tiles_[17] * tiles_[18] * tiles_[26] *
                tiles_[27] * tiles_[28] * tiles_[29] * tiles_[30] * tiles_[31] * tiles_[32] * tiles_[33] == 2)
            {
                return(true);
            }

            //七対子判定
            //2,4桁目:0-すべての牌が0,2,4つのいずれか
            //2つの牌が7つ存在する
            if ((j & 10) == 0 && Range(0, 34).Count(i => tiles_[i] == 2) == 7)
            {
                return(true);
            }

            //2桁目:0-すべての字牌は2つ以上か0つである
            if ((j & 2) != 0)
            {
                return(false);
            }

            //(1,4,7),(2,5,8),(3,6,9)それぞれの個数を集めたもの
            //萬子
            var n00 = tiles_[0] + tiles_[3] + tiles_[6];
            var n01 = tiles_[1] + tiles_[4] + tiles_[7];
            var n02 = tiles_[2] + tiles_[5] + tiles_[8];
            //索子
            var n10 = tiles_[9] + tiles_[12] + tiles_[15];
            var n11 = tiles_[10] + tiles_[13] + tiles_[16];
            var n12 = tiles_[11] + tiles_[14] + tiles_[17];
            //筒子
            var n20 = tiles_[18] + tiles_[21] + tiles_[24];
            var n21 = tiles_[19] + tiles_[22] + tiles_[25];
            var n22 = tiles_[20] + tiles_[23] + tiles_[26];

            //対子があれば2, 面子のみなら0
            var n0 = (n00 + n01 + n02) % 3;
            var n1 = (n10 + n11 + n12) % 3;
            var n2 = (n20 + n21 + n22) % 3;

            if (n0 == 1 || n1 == 1 || n2 == 1)
            {
                return(false);
            }

            //雀頭は1つだけ
            if (new List <int> {
                n0, n1, n2,
                tiles_[27], tiles_[28], tiles_[29], tiles_[30],
                tiles_[31], tiles_[32], tiles_[33]
            }.Count(n => n == 2) != 1)
            {
                return(false);
            }

            //面子を消す
            //(1,4,7)%3=1,(2,5,8)%3=2, (3,6,9)%3=0
            //[123]=>(1*1+2*1=3)=0, [444]=>(1*3)%3=0
            //0:(3,6,9)に,1;(2,5,8)に,2:(3,6,9)に雀頭がある
            //[77]=>(1*2)%3=2, [88]=>(2*2)%3=1,[99]=>(0*2)%3=0
            var nn0 = (n00 * 1 + n01 * 2) % 3;
            var nn1 = (n10 * 1 + n11 * 2) % 3;
            var nn2 = (n20 * 1 + n21 * 2) % 3;

            var m0 = ToMeld(tiles_, 0);
            var m1 = ToMeld(tiles_, 9);
            var m2 = ToMeld(tiles_, 18);

            //3桁目:1-字牌に対子がある
            if ((j & 4) != 0)
            {
                //字牌以外面子しかもたない 以下も同様
                return((n0 | nn0 | n1 | nn1 | n2 | nn2) == 0 &&
                       IsMentsu(m0) &&
                       IsMentsu(m1) &&
                       IsMentsu(m2));
            }
            //萬子に対子がある
            if (n0 == 2)
            {
                return((n1 | nn1 | n2 | nn2) == 0 &&
                       IsMentsu(m1) &&
                       IsMentsu(m2) &&
                       IsAtamaMentsu(nn0, m0));
            }
            //筒子に対子がある
            if (n1 == 2)
            {
                return((n2 | nn2 | n0 | nn0) == 0 &&
                       IsMentsu(m2) &&
                       IsMentsu(m0) &&
                       IsAtamaMentsu(nn1, m1));
            }
            //索子に対子がある
            if (n2 == 2)
            {
                return((n0 | nn0 | n1 | nn1) == 0 &&
                       IsMentsu(m0) &&
                       IsMentsu(m1) &&
                       IsAtamaMentsu(nn2, m2));
            }
            return(false);
        }