/// <summary> /// Checks if <see cref="Yakus"/> and <see cref="YakusCombinations"/> have to be cancelled because of the temporary furiten rule. /// </summary> /// <param name="currentRound">The current round</param> /// <param name="playerIndex">The player index of the hand.</param> /// <returns><c>True</c> if temporary furiten; <c>False</c> otherwise.</returns> internal bool CancelYakusIfTemporaryFuriten(RoundPivot currentRound, int playerIndex) { if (currentRound == null) { return(false); } int i = 0; while (currentRound.PlayerIndexHistory.Count < i && currentRound.PlayerIndexHistory.ElementAt(i) == playerIndex.RelativePlayerIndex(-(i + 1)) && playerIndex.RelativePlayerIndex(-(i + 1)) != playerIndex) { // The tile discarded by the latest player is the tile we ron ! if (i > 0) { TilePivot lastFromDiscard = currentRound.GetDiscard(currentRound.PlayerIndexHistory.ElementAt(i)).LastOrDefault(); if (lastFromDiscard != null && IsCompleteFull(new List <TilePivot>(ConcealedTiles) { lastFromDiscard }, DeclaredCombinations.ToList())) { Yakus = null; YakusCombinations = null; return(true); } } i++; } return(false); }
/// <summary> /// Checks if the hand has finished on a closed wait. /// </summary> /// <returns><c>True</c> if contains a closed wait; <c>False</c> otherwise.</returns> public bool HandWithClosedWait() { if (YakusCombinations == null) { return(false); } // The combination with the last pick. TileComboPivot combo = YakusCombinations.FirstOrDefault(c => c.Tiles.Any(t => ReferenceEquals(t, LatestPick))); if (combo == null || combo.IsBrelanOrSquare) { return(false); } // Other concealed (and not declared) combinations with the same tile. List <TileComboPivot> otherCombos = YakusCombinations .Where(c => c != combo && !DeclaredCombinations.Contains(c) && c.Tiles.Contains(LatestPick)) .ToList(); // The real "LatestPick" is closed... bool isClosed = combo.IsPair || LatestPick.TileIsMiddleWait(combo) || LatestPick.TileIsEdgeWait(combo); // .. but there might be not-closed alternatives with the same tile as "LatestPick" in other combination. bool alternative1 = otherCombos.Any(c => c.IsBrelanOrSquare); bool alternative2 = otherCombos.Any(c => c.IsSequence && !LatestPick.TileIsMiddleWait(c) && !LatestPick.TileIsEdgeWait(c)); return(isClosed && !(alternative1 || alternative2)); }
/// <summary> /// Checks if <see cref="Yakus"/> and <see cref="YakusCombinations"/> have to be cancelled because of the furiten rule. /// </summary> /// <param name="discard">The discard of the current player.</param> /// <param name="opponentDiscards"> /// Aggregation of discards from opponents since the riichi call; includes tiles stolen by another opponent and tiles used to call opened kan. /// </param> /// <returns><c>True</c> if furiten; <c>False</c> otherwise.</returns> internal bool CancelYakusIfFuriten(IEnumerable <TilePivot> discard, IEnumerable <TilePivot> opponentDiscards) { if (discard?.Any(t => IsCompleteFull(new List <TilePivot>(ConcealedTiles) { t }, DeclaredCombinations.ToList())) == true) { Yakus = null; YakusCombinations = null; return(true); } if (opponentDiscards?.Any(t => IsCompleteFull(new List <TilePivot>(ConcealedTiles) { t }, DeclaredCombinations.ToList())) == true) { Yakus = null; YakusCombinations = null; return(true); } return(false); }