示例#1
0
        // Creates a panel for the specified combination.
        private StackPanel CreateCombinationPanel(int pIndex, TileComboPivot combo)
        {
            StackPanel panel = new StackPanel
            {
                Orientation = (pIndex == 0 || pIndex == 2 ? Orientation.Horizontal : Orientation.Vertical)
            };

            WindPivot pWind = _game.GetPlayerCurrentWind(pIndex);

            int i = 0;
            List <Tuple <TilePivot, bool> > tileTuples = combo.GetSortedTilesForDisplay(pWind);

            if (pIndex > 0 && pIndex < 3)
            {
                tileTuples.Reverse();
            }

            foreach (Tuple <TilePivot, bool> tileTuple in tileTuples)
            {
                panel.Children.Add(tileTuple.Item1.GenerateTileButton(null,
                                                                      (AnglePivot)(tileTuple.Item2 ? pIndex.RelativePlayerIndex(1) : pIndex),
                                                                      combo.IsConcealedDisplay(i)));
                i++;
            }

            return(panel);
        }
示例#2
0
        /// <summary>
        /// Gets the list of tiles from the combination, sorted by wind logic for display.
        /// </summary>
        /// <param name="ownerWind">The current wind of the owner.</param>
        /// <returns>List of tiles tuple; the second item is <c>True</c> when the tile is the opened one.</returns>
        public List <Tuple <TilePivot, bool> > GetSortedTilesForDisplay(WindPivot ownerWind)
        {
            if (!StolenFrom.HasValue)
            {
                return(Tiles.Select(t => new Tuple <TilePivot, bool>(t, false)).ToList());
            }

            var concealedOnly = new List <TilePivot>(_tiles);

            concealedOnly.Remove(OpenTile);

            int i = 0;

            var tiles = new List <Tuple <TilePivot, bool> >
            {
                GetTileForSortedListAtSpecifiedWind(ownerWind.Left(), concealedOnly, ref i),
                GetTileForSortedListAtSpecifiedWind(ownerWind.Opposite(), concealedOnly, ref i)
            };

            // For a square, the third tile is never from an opponent.
            if (IsSquare)
            {
                tiles.Add(new Tuple <TilePivot, bool>(concealedOnly[i], false));
                i++;
            }

            tiles.Add(GetTileForSortedListAtSpecifiedWind(ownerWind.Right(), concealedOnly, ref i));

            return(tiles);
        }
示例#3
0
 /// <summary>
 /// Checks if the hand contains a valuable pair (dragon, dominant wind, player wind).
 /// </summary>
 /// <param name="combinations">Lsit of combinations.</param>
 /// <param name="dominantWind">The dominant wind.</param>
 /// <param name="playerWind">The player wind.</param>
 /// <returns><c>True</c> if vluable pair in the hand; <c>False</c> otherwise.</returns>
 public static bool HandWithValuablePair(List <TileComboPivot> combinations, WindPivot dominantWind, WindPivot playerWind)
 {
     return(combinations != null && combinations.Any(c => c.IsPair && (
                                                         c.Family == FamilyPivot.Dragon ||
                                                         (c.Family == FamilyPivot.Wind && (c.Tiles.First().Wind == dominantWind || c.Tiles.First().Wind == playerWind))
                                                         )));
 }
示例#4
0
 // Constructor.
 private PlayerPivot(string name, WindPivot initialWind, InitialPointsRulePivot initialPointsRulePivot, bool isCpu)
 {
     Name        = name;
     InitialWind = initialWind;
     Points      = initialPointsRulePivot.GetInitialPointsFromRule();
     IsCpu       = isCpu;
 }
示例#5
0
        /// <summary>
        /// Declares a pon. Does not discard a tile.
        /// </summary>
        /// <param name="tile">The stolen tile.</param>
        /// <param name="stolenFrom">The wind which the tile has been stolen from.</param>
        /// <exception cref="ArgumentNullException"><paramref name="tile"/> is <c>Null</c>.</exception>
        /// <exception cref="InvalidOperationException"><see cref="Messages.InvalidCall"/></exception>
        internal void DeclarePon(TilePivot tile, WindPivot stolenFrom)
        {
            if (tile == null)
            {
                throw new ArgumentNullException(nameof(tile));
            }

            CheckTilesForCallAndExtractCombo(_concealedTiles.Where(t => t == tile), 2, tile, stolenFrom);
        }
示例#6
0
 // Gets the tile corresponding to the specified wind in the purpose to create a sorted list for display.
 private Tuple <TilePivot, bool> GetTileForSortedListAtSpecifiedWind(WindPivot wind, List <TilePivot> concealedOnly, ref int i)
 {
     if (wind == StolenFrom.Value)
     {
         return(new Tuple <TilePivot, bool>(OpenTile, true));
     }
     else
     {
         i++;
         return(new Tuple <TilePivot, bool>(concealedOnly[i - 1], false));
     }
 }
示例#7
0
        /// <summary>
        /// Extension; gets the wind to the left of the specified wind.
        /// </summary>
        /// <param name="origin">The wind.</param>
        /// <returns>The left wind.</returns>
        public static WindPivot Left(this WindPivot origin)
        {
            switch (origin)
            {
            case WindPivot.East:
                return(WindPivot.North);

            case WindPivot.South:
                return(WindPivot.East);

            case WindPivot.West:
                return(WindPivot.South);

            default:
                return(WindPivot.West);
            }
        }
示例#8
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));
        }
示例#9
0
        /// <summary>
        /// Gets a japanese caracter which represents the specified wind.
        /// </summary>
        /// <param name="wind">The wind to display.</param>
        /// <returns>The associated japanese caracter.</returns>
        /// <exception cref="NotImplementedException">The wind is not implemented.</exception>
        internal static string ToWindDisplay(this WindPivot wind)
        {
            switch (wind)
            {
            case WindPivot.East:
                return("東");

            case WindPivot.South:
                return("南");

            case WindPivot.West:
                return("西");

            case WindPivot.North:
                return("北");

            default:
                throw new NotImplementedException();
            }
        }
示例#10
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="latestTile">The <see cref="LatestTile"/> value.</param>
        /// <param name="drawType">The <see cref="DrawType"/> value.</param>
        /// <param name="dominantWind">The <see cref="DominantWind"/> value.</param>
        /// <param name="playerWind">The <see cref="PlayerWind"/> value.</param>
        /// <param name="isFirstOrLast">Optionnal; indicates a win at the first turn without any call made (<c>True</c>) or at the last tile of the round (<c>Null</c>); default value is <c>False</c>.</param>
        /// <param name="isRiichi">Optionnal; indicates if riichi (<c>True</c>) or riichi at first turn without any call made (<c>Null</c>); default value is <c>False</c>.</param>
        /// <param name="isIppatsu">Optionnal; indicates if it's a win by ippatsu (<paramref name="isRiichi"/> can't be <c>False</c> in such case); default value is <c>False</c>.</param>
        /// <param name="useRenhou">Optionnal; the <see cref="_useRenhou"/> value; default value is <c>False</c>.</param>
        /// <exception cref="ArgumentNullException"><paramref name="latestTile"/> is <c>Null</c>.</exception>
        /// <exception cref="ArgumentException"><see cref="Messages.InvalidContextIppatsuValue"/></exception>
        public WinContextPivot(TilePivot latestTile, DrawTypePivot drawType, WindPivot dominantWind, WindPivot playerWind,
                               bool?isFirstOrLast = false, bool?isRiichi = false, bool isIppatsu = false, bool useRenhou = false)
        {
            if (isRiichi == false && isIppatsu)
            {
                throw new ArgumentException(Messages.InvalidContextIppatsuValue, nameof(isIppatsu));
            }

            LatestTile        = latestTile ?? throw new ArgumentNullException(nameof(latestTile));
            IsRoundLastTile   = isFirstOrLast == null;
            IsRiichi          = isRiichi != false;
            IsFirstTurnRiichi = isRiichi == null;
            IsIppatsu         = isIppatsu;
            DominantWind      = dominantWind;
            PlayerWind        = playerWind;
            IsFirstTurnDraw   = isFirstOrLast == true;
            DrawType          = drawType;
            _useRenhou        = useRenhou;
            IsNagashiMangan   = false;
        }
示例#11
0
        /// <summary>
        /// Wind family constructor.
        /// </summary>
        /// <param name="wind"><see cref="WindPivot"/>.</param>
        public TilePivot(WindPivot wind)
        {
            Wind   = wind;
            Family = FamilyPivot.Wind;
            switch (wind)
            {
            case WindPivot.East:
                Graphic = DllRsc.vent_est;
                break;

            case WindPivot.South:
                Graphic = DllRsc.vent_sud;
                break;

            case WindPivot.West:
                Graphic = DllRsc.vent_ouest;
                break;

            case WindPivot.North:
                Graphic = DllRsc.vent_nord;
                break;
            }
        }
示例#12
0
        /// <summary>
        /// Declares a chii. Does not discard a tile.
        /// </summary>
        /// <param name="tile">The stolen tile.</param>
        /// <param name="stolenFrom">The wind which the tile has been stolen from.</param>
        /// <param name="startNumber">The sequence first number.</param>
        /// <exception cref="ArgumentNullException"><paramref name="tile"/> is <c>Null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="startNumber"/> is out of range.</exception>
        /// <exception cref="InvalidOperationException"><see cref="Messages.InvalidCall"/></exception>
        internal void DeclareChii(TilePivot tile, WindPivot stolenFrom, int startNumber)
        {
            if (tile == null)
            {
                throw new ArgumentNullException(nameof(tile));
            }

            if (startNumber < 1 || startNumber > 7)
            {
                throw new ArgumentOutOfRangeException(nameof(startNumber));
            }

            if (tile.Number < startNumber || tile.Number > startNumber + 2)
            {
                throw new InvalidOperationException(Messages.InvalidCall);
            }

            CheckTilesForCallAndExtractCombo(Enumerable
                                             .Range(startNumber, 3)
                                             .Where(i => i != tile.Number)
                                             .Select(i => _concealedTiles.FirstOrDefault(t => t.Family == tile.Family && t.Number == i))
                                             .Where(t => t != null), 2, tile, stolenFrom);
        }
示例#13
0
        /// <summary>
        /// Computes the number of points for a winner, without honba and riichi pending count.
        /// </summary>
        /// <param name="fanCount">Fan count.</param>
        /// <param name="fuCount">Fu count.</param>
        /// <param name="isTsumo"><c>True</c> if win by tsumo; <c>False</c> otherwise.</param>
        /// <param name="playerWind">The current player wind.</param>
        /// <returns>
        /// - Number of points lost by east players (or one of the three remaining if the winner is east; or the specific loser if ron).
        /// - Number of points lost by the two other players.
        /// </returns>
        public static Tuple <int, int> GetPoints(int fanCount, int fuCount, bool isTsumo, WindPivot playerWind)
        {
            int v1 = 0;
            int v2 = 0;

            bool east = playerWind == WindPivot.East;

            if ((fanCount == 4 && fuCount >= 40) || (fanCount == 3 && fuCount >= 70))
            {
                fanCount = 5;
            }

            if (fanCount > 4)
            {
                int basePoints = OVER_FOUR_FAN.Last(k => k.Key <= fanCount).Value *(east ? 2 : 1);
                // in case of several yakumans.
                if (fanCount > 13)
                {
                    basePoints += (basePoints * ((fanCount - 13) / 13));
                }
                if (isTsumo)
                {
                    v1 = basePoints * (east ? 1 : 2);
                    v2 = basePoints;
                }
                else
                {
                    v1 = basePoints * (east ? 3 : 4);
                    v2 = 0;
                }
            }
            else if (east)
            {
                var basePts = CHART_EAST.Last(k => k.Item1 <= fanCount && k.Item2 <= fuCount);
                if (isTsumo)
                {
                    v1 = basePts.Item4;
                    v2 = basePts.Item4;
                }
                else
                {
                    v1 = basePts.Item3;
                    v2 = 0;
                }
            }
            else
            {
                var basePts = CHART_OTHER.Last(k => k.Item1 <= fanCount && k.Item2 <= fuCount);
                if (isTsumo)
                {
                    v1 = basePts.Item4;
                    v2 = basePts.Item5;
                }
                else
                {
                    v1 = basePts.Item3;
                    v2 = 0;
                }
            }

            return(new Tuple <int, int>(v1, v2));
        }
示例#14
0
        private static Tuple <List <SubstitutionGroup>, List <Substitution> > BackgroundHandlerForOneTileAway(WindPivot dominantWind, WindPivot seatWind,
                                                                                                              List <TilePivot> handTileWithLast, List <TilePivot> availableTiles, List <TilePivot> forbiddenTiles)
        {
            var substitutionsAttemps = new List <Substitution>();
            var rawResults           = new List <SubstitutionGroup>();

            foreach (TilePivot subbedTile in handTileWithLast.Distinct())
            {
                foreach (TilePivot subTile in availableTiles.Distinct())
                {
                    if (subbedTile.Equals(subTile) || forbiddenTiles.Contains(subTile))
                    {
                        continue;
                    }

                    substitutionsAttemps.Add(new Substitution(subbedTile, subTile, availableTiles));

                    List <TilePivot> handTilesWithSub = new List <TilePivot>(handTileWithLast);
                    handTilesWithSub.Remove(subbedTile);

                    var hand1 = new FullHandPivot(handTilesWithSub, dominantWind, seatWind, subTile);

                    HandYakuListPivot tmpResults = hand1.ComputeHandYakus()?.FirstOrDefault();
                    if (tmpResults != null)
                    {
                        var subSeq = new SubstitutionSequence();
                        subSeq.AddSubstitution(new Substitution(subbedTile, subTile, availableTiles));

                        var finded = rawResults.FirstOrDefault(x => x.Yakus.Equals(tmpResults));
                        if (finded == null)
                        {
                            rawResults.Add(new SubstitutionGroup(tmpResults));
                            finded = rawResults.Last();
                        }
                        finded.AddSubstitutionSequence(subSeq);
                    }
                }
            }

            return(new Tuple <List <SubstitutionGroup>, List <Substitution> >(rawResults, substitutionsAttemps));
        }
示例#15
0
        private bool ExtractFromForm(out List <TilePivot> tiles, out TilePivot latestPickTile, out WindPivot dominantWind, out WindPivot seatWind)
        {
            tiles          = new List <TilePivot>();
            latestPickTile = null;
            dominantWind   = WindPivot.East;
            seatWind       = WindPivot.East;

            for (int i = 1; i <= 14; i++)
            {
                if (GetCombo(i).SelectedItem == null)
                {
                    MessageBox.Show("Some slots are empty.", "LionelRiichiStats - Error");
                    return(false);
                }
                TilePivot tile = GetCombo(i).SelectedItem as TilePivot;
                if (GetRadio(i).IsChecked == true && latestPickTile == null)
                {
                    latestPickTile = tile;
                }
                else
                {
                    tiles.Add(tile);
                }
            }
            if (latestPickTile == null)
            {
                MessageBox.Show("The latest tile has not been selected.", "LionelRiichiStats - Error");
                return(false);
            }
            if (tiles.Concat(new List <TilePivot> {
                latestPickTile
            }).GroupBy(t => t).Any(tg => tg.Count() > 4))
            {
                MessageBox.Show("One tile is selected more than four times.", "LionelRiichiStats - Error");
                return(false);
            }
            if (CbbDominantWind.SelectedIndex == -1)
            {
                MessageBox.Show("The dominant wind has not been selected.", "LionelRiichiStats - Error");
                return(false);
            }
            if (CbbSeatWind.SelectedIndex == -1)
            {
                MessageBox.Show("The seat wind has not been selected.", "LionelRiichiStats - Error");
                return(false);
            }
            dominantWind = (WindPivot)CbbDominantWind.SelectedItem;
            seatWind     = (WindPivot)CbbSeatWind.SelectedItem;

            return(true);
        }
示例#16
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="tiles"><see cref="ConcealedTiles"/>.</param>
        /// <param name="dominantWind"><see cref="DominantWind"/>.</param>
        /// <param name="seatWind"><see cref="SeatWind"/>.</param>
        /// <param name="latestTile"><see cref="LatestTile"/>.</param>
        /// <param name="isRon">Optionnal; <see cref="IsRon"/>.</param>
        /// <param name="openedSets">Optionnal; <see cref="OpenedSets"/>.</param>
        /// <param name="concealedKans">Optionnal; <see cref="ConcealedKans"/>.</param>
        /// <param name="isRiichi">Optionnal; <see cref="IsRiichi"/>.</param>
        /// <param name="isDoubleRiichi">Optionnal; <see cref="IsDoubleRiichi"/>.</param>
        /// <param name="isIppatsu">Optionnal; <see cref="IsIppatsu"/>.</param>
        /// <param name="isHaitei">Optionnal; <see cref="IsHaitei"/>.</param>
        /// <param name="isRinshankaihou">Optionnal ; <see cref="IsRinshankaihou"/>.</param>
        /// <param name="isChankan">Optionnal; <see cref="IsChankan"/>.</param>
        /// <param name="isInitialDraw">Optionnal; <see cref="IsInitialDraw"/>.</param>
        /// <exception cref="ArgumentException"><see cref="Messages.InvalidTilesCountInHandError"/>.</exception>
        /// <exception cref="ArgumentException"><see cref="Messages.InvalidHandArgumentsConsistencyError"/>.</exception>
        public FullHandPivot(List <TilePivot> tiles, WindPivot dominantWind, WindPivot seatWind, TilePivot latestTile, bool isRon = false,
                             List <SetPivot> openedSets = null, List <SetPivot> concealedKans = null,
                             bool isRiichi        = false, bool isDoubleRiichi = false, bool isIppatsu     = false, bool isHaitei = false,
                             bool isRinshankaihou = false, bool isChankan      = false, bool isInitialDraw = false)
        {
            tiles         = tiles ?? new List <TilePivot>();
            openedSets    = openedSets ?? new List <SetPivot>();
            concealedKans = concealedKans ?? new List <SetPivot>();

            if ((openedSets.Count * 3 + concealedKans.Count * 3 + 1 + tiles.Count) != 14)
            {
                throw new ArgumentException(Messages.InvalidTilesCountInHandError);
            }

            if (openedSets.Count > 0 && (isRiichi || isDoubleRiichi || isIppatsu || openedSets.Any(set => set.IsPair)))
            {
                throw new ArgumentException(Messages.InvalidHandArgumentsConsistencyError, nameof(openedSets));
            }

            if (concealedKans.Count > 0 && concealedKans.Any(set => !set.IsKan))
            {
                throw new ArgumentException(Messages.InvalidHandArgumentsConsistencyError, nameof(concealedKans));
            }

            if (concealedKans.Count == 0 && !openedSets.Any(set => set.IsKan) && isRinshankaihou)
            {
                throw new ArgumentException(Messages.InvalidHandArgumentsConsistencyError, nameof(isRinshankaihou));
            }

            if (IsHaitei && isRinshankaihou)
            {
                throw new ArgumentException(Messages.InvalidHandArgumentsConsistencyError, nameof(IsHaitei));
            }

            if (isChankan && (isRinshankaihou || !isRon))
            {
                throw new ArgumentException(Messages.InvalidHandArgumentsConsistencyError, nameof(isChankan));
            }

            if (isIppatsu && (isRinshankaihou || (IsRon && isHaitei)))
            {
                throw new ArgumentException(Messages.InvalidHandArgumentsConsistencyError, nameof(isIppatsu));
            }

            // TODO : implement controls to prevent buggy initial draw

            ConcealedTiles  = tiles.OrderBy(t => t).ToList();
            DominantWind    = dominantWind;
            SeatWind        = seatWind;
            LatestTile      = latestTile ?? throw new ArgumentNullException(nameof(latestTile));
            IsRon           = isRon;
            OpenedSets      = openedSets;
            ConcealedKans   = concealedKans;
            IsRiichi        = isRiichi || isDoubleRiichi;
            IsIppatsu       = isIppatsu;
            IsDoubleRiichi  = isDoubleRiichi;
            IsChankan       = isChankan;
            IsHaitei        = isHaitei;
            IsRinshankaihou = isRinshankaihou;
            IsInitialDraw   = IsInitialDraw;
        }
示例#17
0
 // Constructor for wind.
 private TilePivot(WindPivot wind)
 {
     Family = FamilyPivot.Wind;
     Wind   = wind;
 }
示例#18
0
 /// <summary>
 /// Gets the player index for the specified wind.
 /// </summary>
 /// <param name="wind">The wind.</param>
 /// <returns>The player index.</returns>
 public int GetPlayerIndexByCurrentWind(WindPivot wind)
 {
     return(Enumerable.Range(0, 4).First(i => GetPlayerCurrentWind(i) == wind));
 }