Ejemplo n.º 1
0
 /// <summary>
 /// Constructor when winning.
 /// </summary>
 /// <param name="index">The <see cref="Index"/> value.</param>
 /// <param name="fanCount">The <see cref="FanCount"/> value.</param>
 /// <param name="fuCount">The <see cref="FuCount"/> value.</param>
 /// <param name="hand">The <see cref="_hand"/> value.</param>
 /// <param name="pointsGain">The <see cref="PointsGain"/> value.</param>
 /// <param name="doraCount">The <see cref="DoraCount"/> value.</param>
 /// <param name="uraDoraCount">The <see cref="UraDoraCount"/> value.</param>
 /// <param name="redDoraCount">The <see cref="RedDoraCount"/> value.</param>
 /// <param name="handPointsGain">The <see cref="HandPointsGain"/> value.</param>
 internal PlayerInformationsPivot(int index, int fanCount, int fuCount, HandPivot hand,
                                  int pointsGain, int doraCount, int uraDoraCount, int redDoraCount, int handPointsGain)
 {
     Index          = index;
     FanCount       = fanCount;
     FuCount        = fuCount;
     PointsGain     = pointsGain;
     DoraCount      = doraCount;
     UraDoraCount   = uraDoraCount;
     RedDoraCount   = redDoraCount;
     HandPointsGain = handPointsGain;
     _hand          = hand;
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Computes the fu count.
        /// </summary>
        /// <param name="hand">The hand.</param>
        /// <param name="isTsumo"><c>True</c> if the winning tile is concealed; <c>False</c> otherwise.</param>
        /// <param name="dominantWind">The dominant wind;</param>
        /// <param name="playerWind">The player wind.</param>
        /// <exception cref="ArgumentNullException"><paramref name="hand"/> is <c>Null</c>.</exception>
        public static int GetFuCount(HandPivot hand, bool isTsumo, WindPivot dominantWind, WindPivot playerWind)
        {
            if (hand == null)
            {
                throw new ArgumentNullException(nameof(hand));
            }

            if (hand.Yakus.Any(y => y == YakuPivot.Chiitoitsu))
            {
                return(CHIITOI_FU);
            }

            int fuCount =
                hand.YakusCombinations.Count(c => c.IsSquare && c.HasTerminalOrHonor) * HONOR_KAN_FU
                + hand.YakusCombinations.Count(c => c.IsSquare && !c.HasTerminalOrHonor) * REGULAR_KAN_FU
                + hand.YakusCombinations.Count(c => c.IsBrelan && c.HasTerminalOrHonor) * HONOR_PON_FU
                + hand.YakusCombinations.Count(c => c.IsBrelan && !c.HasTerminalOrHonor) * REGULAR_PON_FU;

            if (isTsumo && !hand.Yakus.Any(y => y == YakuPivot.Pinfu))
            {
                fuCount += TSUMO_FU;
            }

            if (HandPivot.HandWithValuablePair(hand.YakusCombinations, dominantWind, playerWind))
            {
                fuCount += VALUABLE_PAIR_FU;
            }

            if (hand.HandWithClosedWait())
            {
                fuCount += CLOSED_WAIT_FU;
            }

            if (fuCount == 0 && !hand.Yakus.Any(y => y == YakuPivot.Pinfu))
            {
                fuCount += OPEN_PINFU_FU;
            }

            int baseFu = (hand.IsConcealed && !isTsumo ? BASE_CONCEALED_RON_FU : BASE_FU) + fuCount;

            return(Convert.ToInt32(Math.Ceiling(baseFu / (decimal)10) * 10));
        }
Ejemplo n.º 3
0
        private bool HandCanBeOpened(int playerId, TilePivot tile, HandPivot hand)
        {
            var valuableWinds = new[] { _round.Game.GetPlayerCurrentWind(playerId), _round.Game.DominantWind };

            var canPonForYakuhai = IsDragonOrValuableWind(tile, valuableWinds);

            // >= 75% of the tile family
            var closeToChinitsu = hand.ConcealedTiles.Count(_ => _.Family == tile.Family) >= 11;

            // how much pair (or better) of valuable honors ?
            var valuableHonorPairs = hand.ConcealedTiles.GroupBy(_ => _)
                                     .Count(_ => _.Key.IsHonor && _.Count() >= 2 && IsDragonOrValuableWind(_.Key, valuableWinds));

            if (!canPonForYakuhai && valuableHonorPairs < 2 && !closeToChinitsu)
            {
                return(false);
            }

            int dorasCount = hand.ConcealedTiles
                             .Sum(t => _round.DoraIndicatorTiles
                                  .Take(_round.VisibleDorasCount)
                                  .Count(d => t.IsDoraNext(d)));
            int redDorasCount = hand.ConcealedTiles.Count(t => t.IsRedDora);

            var hasValuablePair = hand.ConcealedTiles.GroupBy(_ => _)
                                  .Any(_ => _.Count() >= 2 && _.Key != tile && IsDragonOrValuableWind(_.Key, valuableWinds));

            // >= 66% of one family or honor
            var closeToHonitsu = new[] { FamilyPivot.Bamboo, FamilyPivot.Caracter, FamilyPivot.Circle }
            .Any(f => hand.ConcealedTiles.Count(t => t.Family == f || t.IsHonor) > 9);

            return(hasValuablePair ||
                   (dorasCount + redDorasCount) > 0 ||
                   closeToHonitsu ||
                   valuableWinds[0] == WindPivot.East);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Computes the list of yakus available with this sequence of combinations.
        /// If the list contains one or several yakumans, it can't contain any regular yakus.
        /// Two <see cref="YakuPivot"/> are not computed :
        /// <list type="bullet">
        /// <item><see cref="KokushiMusou"/></item>
        /// <item><see cref="NagashiMangan"/></item>
        /// </list>
        /// </summary>
        /// <param name="combinationsSequence">Sequence of combinations.</param>
        /// <param name="context">Context.</param>
        /// <returns>List of yakus with this sequence of combinations.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="combinationsSequence"/> is <c>Null</c>.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="context"/> is <c>Null</c>.</exception>
        /// <exception cref="NotImplementedException">The <see cref="YakuPivot"/> to check is not implemented.</exception>
        public static List <YakuPivot> GetYakus(List <TileComboPivot> combinationsSequence, WinContextPivot context)
        {
            if (combinationsSequence == null)
            {
                throw new ArgumentNullException(nameof(combinationsSequence));
            }

            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var yakus = new List <YakuPivot>();

            foreach (YakuPivot yaku in Yakus.Where(y => y.IsYakuman))
            {
                bool addYaku = false;
                if (yaku == Daisangen)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare && c.Family == FamilyPivot.Dragon) == 3;
                }
                else if (yaku == Suuankou)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare && c.IsConcealed && (!c.Tiles.Contains(context.LatestTile) || context.DrawType.IsSelfDraw())) == 4;
                }
                else if (yaku == Shousuushii)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare && c.Family == FamilyPivot.Wind) == 3 &&
                              combinationsSequence.Any(c => c.IsPair && c.Family == FamilyPivot.Wind);
                }
                else if (yaku == Daisuushii)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare && c.Family == FamilyPivot.Wind) == 4;
                }
                else if (yaku == Tsuuiisou)
                {
                    addYaku = combinationsSequence.All(c => c.IsHonor);
                }
                else if (yaku == Ryuuiisou)
                {
                    addYaku = combinationsSequence.All(c =>
                                                       (c.Family == FamilyPivot.Bamboo && c.Tiles.All(t => new[] { 2, 3, 4, 6, 8 }.Contains(t.Number))) ||
                                                       (c.Family == FamilyPivot.Dragon && c.Tiles.First().Dragon == DragonPivot.Green)
                                                       );
                }
                else if (yaku == Chinroutou)
                {
                    addYaku = combinationsSequence.All(c => c.IsTerminal);
                }
                else if (yaku == ChuurenPoutou)
                {
                    if (combinationsSequence.All(c => c.IsConcealed) &&
                        combinationsSequence.Select(c => c.Family).Distinct().Count() == 1)
                    {
                        string numberPattern = string.Join(string.Empty, combinationsSequence.SelectMany(c => c.Tiles).Select(t => t.Number).OrderBy(i => i));
                        addYaku = new[]
                        {
                            "11112345678999", "11122345678999", "11123345678999",
                            "11123445678999", "11123455678999", "11123456678999",
                            "11123456778999", "11123456788999", "11123456789999"
                        }.Contains(numberPattern);
                    }
                }
                else if (yaku == Suukantsu)
                {
                    addYaku = combinationsSequence.Count(c => c.IsSquare) == 4;
                }
                else if (yaku == Tenhou)
                {
                    addYaku = context.IsTenhou();
                }
                else if (yaku == Chiihou)
                {
                    addYaku = context.IsChiihou();
                }
                else if (yaku == Renhou)
                {
                    addYaku = context.IsRenhou();
                }
                else if (yaku == KokushiMusou)
                {
                    // Do nothing here, but prevents the exception below.
                }
                else
                {
                    throw new NotImplementedException();
                }

                if (addYaku)
                {
                    yakus.Add(yaku);
                }
            }

            // Remove yakumans with existant upgrade (it's an overkill as the only known case is "Shousuushii" vs. "Daisuushii").
            yakus.RemoveAll(y => y.Upgrades.Any(yu => yakus.Contains(yu)));

            // Only return yakumans if any.
            if (yakus.Count >= 1)
            {
                return(yakus);
            }

            foreach (YakuPivot yaku in Yakus.Where(y => !y.IsYakuman))
            {
                bool addYaku    = false;
                int  occurences = 1;
                if (yaku == Chiniisou)
                {
                    addYaku = combinationsSequence.Select(c => c.Family).Distinct().Count() == 1 &&
                              !combinationsSequence.Any(c => c.IsHonor);
                }
                else if (yaku == Haitei)
                {
                    addYaku = context.IsRoundLastTile;
                }
                else if (yaku == RinshanKaihou)
                {
                    addYaku = context.DrawType == DrawTypePivot.Compensation;
                }
                else if (yaku == Chankan)
                {
                    addYaku = context.DrawType == DrawTypePivot.OpponentKanCallOpen;
                }
                else if (yaku == Tanyao)
                {
                    addYaku = combinationsSequence.All(c => !c.HasTerminalOrHonor);
                }
                else if (yaku == Yakuhai)
                {
                    occurences = combinationsSequence.Count(c =>
                                                            c.IsBrelanOrSquare && (
                                                                c.Family == FamilyPivot.Dragon || (
                                                                    c.Family == FamilyPivot.Wind && (
                                                                        c.Tiles.First().Wind == context.DominantWind || c.Tiles.First().Wind == context.PlayerWind
                                                                        )
                                                                    )
                                                                )
                                                            );
                    addYaku = occurences > 0;
                }
                else if (yaku == Riichi)
                {
                    addYaku = context.IsRiichi;
                }
                else if (yaku == Ippatsu)
                {
                    addYaku = context.IsIppatsu;
                }
                else if (yaku == MenzenTsumo)
                {
                    addYaku = context.DrawType.IsSelfDraw() && combinationsSequence.All(c => c.IsConcealed);
                }
                else if (yaku == Honiisou)
                {
                    addYaku = combinationsSequence.Where(c => !c.IsHonor).Select(c => c.Family).Distinct().Count() == 1;
                }
                else if (yaku == Pinfu)
                {
                    addYaku = combinationsSequence.Count(c => c.IsSequence && c.IsConcealed) == 4 &&
                              !HandPivot.HandWithValuablePair(combinationsSequence, context.DominantWind, context.PlayerWind) &&
                              combinationsSequence.Any(c => c.IsSequence && c.Tiles.Contains(context.LatestTile) &&
                                                       !context.LatestTile.TileIsEdgeWait(c) && !context.LatestTile.TileIsMiddleWait(c));
                }
                else if (yaku == Iipeikou)
                {
                    int sequencesCount = combinationsSequence.Count(c => c.IsSequence);
                    addYaku = combinationsSequence.All(c => c.IsConcealed) && sequencesCount >= 2 &&
                              combinationsSequence.Where(c => c.IsSequence).Distinct().Count() < sequencesCount;
                }
                else if (yaku == Shousangen)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare && c.Family == FamilyPivot.Dragon) == 2 &&
                              combinationsSequence.Any(c => c.IsPair && c.Family == FamilyPivot.Dragon);
                }
                else if (yaku == Honroutou)
                {
                    addYaku = combinationsSequence.All(c => c.IsTerminal || c.IsHonor);
                }
                else if (yaku == Chiitoitsu)
                {
                    addYaku = combinationsSequence.All(c => c.IsPair);
                }
                else if (yaku == Sankantsu)
                {
                    addYaku = combinationsSequence.Count(c => c.IsSquare) == 3;
                }
                else if (yaku == SanshokuDoukou)
                {
                    addYaku = combinationsSequence
                              .Where(c => c.IsBrelanOrSquare && !c.IsHonor)
                              .GroupBy(c => c.Tiles.First().Number)
                              .FirstOrDefault(b => b.Count() >= 3)?
                              .Select(b => b.Family)?
                              .Distinct()?
                              .Count() == 3;
                }
                else if (yaku == Sanankou)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare && c.IsConcealed && (!c.Tiles.Contains(context.LatestTile) || context.DrawType.IsSelfDraw())) == 3;
                }
                else if (yaku == Toitoi)
                {
                    addYaku = combinationsSequence.Count(c => c.IsBrelanOrSquare) == 4;
                }
                else if (yaku == Ittsu)
                {
                    List <TileComboPivot> ittsuFamilyCombos =
                        combinationsSequence
                        .Where(c => c.IsSequence)
                        .GroupBy(c => c.Family)
                        .FirstOrDefault(b => b.Count() >= 3)?
                        .ToList();

                    addYaku = ittsuFamilyCombos != null &&
                              ittsuFamilyCombos.Any(c => c.SequenceFirstNumber == 1) &&
                              ittsuFamilyCombos.Any(c => c.SequenceFirstNumber == 4) &&
                              ittsuFamilyCombos.Any(c => c.SequenceFirstNumber == 7);
                }
                else if (yaku == SanshokuDoujun)
                {
                    addYaku = combinationsSequence
                              .Where(c => c.IsSequence)
                              .GroupBy(c => c.SequenceFirstNumber)
                              .FirstOrDefault(b => b.Count() >= 3)?
                              .Select(b => b.Family)?
                              .Distinct()?
                              .Count() == 3;
                }
                else if (yaku == Chanta)
                {
                    addYaku = combinationsSequence.All(c => c.HasTerminalOrHonor);
                }
                else if (yaku == DaburuRiichi)
                {
                    addYaku = context.IsRiichi && context.IsFirstTurnRiichi;
                }
                else if (yaku == Ryanpeikou)
                {
                    addYaku = combinationsSequence.All(c => c.IsConcealed) &&
                              combinationsSequence.Count(c => c.IsSequence) == 4 &&
                              combinationsSequence.Where(c => c.IsSequence).Distinct().Count() <= 2;
                }
                else if (yaku == Junchan)
                {
                    addYaku = combinationsSequence.All(c => c.HasTerminal);
                }
                else if (yaku == NagashiMangan)
                {
                    // Do nothing here, but prevents the exception below.
                }
                else
                {
                    throw new NotImplementedException();
                }

                if (addYaku)
                {
                    for (int i = 0; i < occurences; i++)
                    {
                        yakus.Add(yaku);
                    }
                }
            }

            // Remove yakus with existant upgrade.
            // It works because Upgrades is not recursive.
            yakus.RemoveAll(y => y.Upgrades.Any(yu => yakus.Contains(yu)));

            // On a concealed chanka, only Kokushi is allowed.
            if (context.DrawType == DrawTypePivot.OpponentKanCallConcealed && !yakus.Contains(KokushiMusou))
            {
                yakus.Clear();
            }

            return(yakus);
        }