public DiscardResponse OnDiscard(VisibleBoard board, Tile tile, int who, DiscardActions suggestedActions) { if ((suggestedActions & DiscardActions.Ron) != 0) { return(DiscardResponse.Ron()); } if ((suggestedActions & DiscardActions.Kan) != 0) { return(DiscardResponse.Daiminkan()); } if ((suggestedActions & DiscardActions.Pon) != 0) { var impossibleToKan = ImpossibleToKan(board); if (!impossibleToKan.Contains(tile.TileType)) { var tiles = board.Watashi.ConcealedTiles.Where(t => t.TileType == tile.TileType).Take(2).ToList(); var discard = FindDiscard(board); return(tiles.Contains(discard) ? DiscardResponse.Pass() : DiscardResponse.Pon(tiles[0], tiles[1], discard)); } } return(DiscardResponse.Pass()); }
public DrawResponse OnDraw(VisibleBoard board, Tile tile, DrawActions suggestedActions) { if (suggestedActions.HasFlag(DrawActions.Tsumo)) { return(DrawResponse.Tsumo()); } if (suggestedActions.HasFlag(DrawActions.Riichi)) { var tileTypeId = board.Watashi.Hand.GetHighestUkeIreDiscard(); var discard = board.Watashi.ConcealedTiles.First(i => i.TileType.TileTypeId == tileTypeId); return(DrawResponse.Riichi(discard)); } if (suggestedActions.HasFlag(DrawActions.KyuushuKyuuhai) && (board.Watashi.Hand.Shanten > 2 || board.Seats.Any(s => s.DeclaredRiichi))) { return(DrawResponse.KyuushuKyuuhai()); } { var tileTypeId = board.Watashi.Hand.GetHighestUkeIreDiscard(); // Prefer tsumogiri if (tile.TileType.TileTypeId == tileTypeId) { return(DrawResponse.Discard(tile)); } var discard = board.Watashi.ConcealedTiles.First(i => i.TileType.TileTypeId == tileTypeId); return(DrawResponse.Discard(discard)); } }
public DiscardResponse OnDiscard(VisibleBoard board, Tile tile, int who, DiscardActions suggestedActions) { if (suggestedActions.HasFlag(DiscardActions.Ron)) { return(DiscardResponse.Ron()); } // Call value honors if it improves shanten. But don't call if already tenpai (all discards would be furiten) var shanten = board.Watashi.Hand.Shanten; if (shanten > 0 && suggestedActions.HasFlag(DiscardActions.Pon) && !suggestedActions.HasFlag(DiscardActions.Kan)) { var tileType = tile.TileType; if (tileType.TileTypeId >= 31 || tileType == board.RoundWind || tileType == board.Watashi.SeatWind) { var t = board.Watashi.Hand.WithPon(tileType); if (t.Shanten < shanten) { var tilesInHand = board.Watashi.ConcealedTiles.Where(i => i.TileType.TileTypeId == tileType.TileTypeId).ToList(); var tileTypeId = t.GetHighestUkeIreDiscard(); var discard = board.Watashi.ConcealedTiles.First(i => i.TileType.TileTypeId == tileTypeId); return(DiscardResponse.Pon(tilesInHand[0], tilesInHand[1], discard)); } } } return(DiscardResponse.Pass()); }
public DrawResponse OnDraw(VisibleBoard board, Tile tile, DrawActions suggestedActions) { if ((suggestedActions & DrawActions.Tsumo) != 0) { return(DrawResponse.Tsumo()); } if ((suggestedActions & DrawActions.KyuushuKyuuhai) != 0) { return(DrawResponse.KyuushuKyuuhai()); } if ((suggestedActions & DrawActions.Kan) != 0) { var ankanTileType = board.Watashi.ConcealedTiles.GroupBy(t => t.TileType).FirstOrDefault(t => t.Count() == 4)?.Key; if (ankanTileType != null) { return(DrawResponse.Ankan(ankanTileType)); } var shouminkanTile = board.Watashi.ConcealedTiles.FirstOrDefault(t => board.Watashi.Melds.Any(m => m.MeldType == MeldType.Koutsu && m.LowestTile.TileType == t.TileType)); if (shouminkanTile != null) { return(DrawResponse.Shouminkan(tile)); } } return(DrawResponse.Discard(FindDiscard(board))); }
internal override bool CanExecute(VisibleBoard board, DrawActions possibleActions) { return(possibleActions.HasFlag(DrawActions.Kan) && !board.Watashi.DeclaredRiichi && HasTile(board, _tile) && board.Watashi.Melds.Any(m => m.MeldType == MeldType.Koutsu && m.LowestTile.TileType == _tile.TileType)); }
internal override bool CanExecute(VisibleBoard board, DrawActions possibleActions) { if (!possibleActions.HasFlag(DrawActions.Kan) || board.Watashi.ConcealedTiles.Count(t => t.TileType == _tileType) != 4 || board.Watashi.CurrentDraw == null) { return(false); } return(!board.Watashi.DeclaredRiichi || !board.Watashi.Hand.IsUkeIreChangedByAnkan(board.Watashi.CurrentDraw.TileType, _tileType)); }
private Tile FindDiscard(VisibleBoard board) { var impossibleToKan = ImpossibleToKan(board); var grouped = board.Watashi.ConcealedTiles.GroupBy(t => t.TileType).OrderBy(g => g.Count()).ToList(); var toDiscard = grouped.FirstOrDefault(g => impossibleToKan.Contains(g.Key)) ?? grouped.First(); return(toDiscard.First()); }
private static HashSet <TileType> ImpossibleToKan(VisibleBoard board) { var visibleDiscards = board.Seats.SelectMany(s => s.Discards); var visibleMelded = board.Seats.Skip(1).SelectMany(s => s.Melds.SelectMany(m => m.Tiles)); var visibleIndicators = board.DoraIndicators; var impossibleToKan = visibleDiscards.Concat(visibleMelded).Concat(visibleIndicators).Select(t => t.TileType).Distinct().ToHashSet(); return(impossibleToKan); }
public Player() { Name = "Player"; Score = 0; HiddenBoard = new HiddenBoard(); VisibleBoard = new VisibleBoard(); armada = new ShipFleet(); rowSelection = 0; columnSelection = 0; }
private static async Task Start(Decider decider, Board board, Wall wall, ISpectator spectator) { State state = new Start(); var spectatorBoard = new VisibleBoard(board); while (!state.IsFinal) { state.Update(board, wall); spectator.Updated(spectatorBoard); await state.Decide(board, decider); state = state.Advance(); } spectator.Updated(spectatorBoard); }
internal override bool CanExecute(VisibleBoard board, DiscardActions possibleActions) { if (board.CurrentDiscard == null) { return(false); } var tileType = board.CurrentDiscard.TileType; return(possibleActions.HasFlag(DiscardActions.Pon) && _tile0.TileType == tileType && _tile1.TileType == tileType && _discardAfterCall.TileType != tileType && // kuikae HasTile(board, _tile0) && HasTile(board, _tile1) && HasTile(board, _discardAfterCall)); }
public TenhouClient(IPlayer player, ISpectator spectator) { _player = player; _spectator = spectator; _lobby = _player.Lobby; _playerId = _player.Id; var folderName = MakeFolderName(_playerId); _directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TenhouAi", folderName); if (!Directory.Exists(_directory)) { Directory.CreateDirectory(_directory); } _sessionLogFileName = Path.Combine(_directory, Path.GetRandomFileName() + ".txt"); _wall = new FakeWall(); _board = new Board(_wall); _visibleBoard = new VisibleBoard(_board); }
internal override bool CanExecute(VisibleBoard board, DiscardActions possibleActions) { if (board.CurrentDiscard == null) { return(false); } var tileType = board.CurrentDiscard.TileType; var tileTypeIds = new[] { _tile0.TileType.TileTypeId, _tile1.TileType.TileTypeId, tileType.TileTypeId }.OrderBy(x => x).ToList(); var isValidDiscardForKanchanCase = tileTypeIds[1] == tileType.TileTypeId && _discardAfterCall.TileType != tileType; var isValidDiscard = isValidDiscardForKanchanCase || Kuikae.IsValidDiscardForNonKanchanChii(tileType, _discardAfterCall.TileType); return(possibleActions.HasFlag(DiscardActions.Chii) && tileTypeIds[0] + 1 == tileTypeIds[1] && tileTypeIds[1] + 1 == tileTypeIds[2] && isValidDiscard && HasTile(board, _tile0) && HasTile(board, _tile1) && HasTile(board, _discardAfterCall)); }
protected static bool HasTile(VisibleBoard board, Tile tile) { return(board.Watashi.ConcealedTiles.Contains(tile)); }
public void Updated(VisibleBoard board) { }
public bool Chankan(VisibleBoard board, Tile tile, int who) { Delay(50); return(_player.Chankan(board, tile, who)); }
public DiscardResponse OnDiscard(VisibleBoard board, Tile tile, int who, DiscardActions suggestedActions) { Delay(50); return(_player.OnDiscard(board, tile, who, suggestedActions)); }
public DrawResponse OnDraw(VisibleBoard board, Tile tile, DrawActions suggestedActions) { Delay(50); return(_player.OnDraw(board, tile, suggestedActions)); }
public bool Chankan(VisibleBoard board, Tile tile, int who) { return(true); }
internal override bool CanExecute(VisibleBoard board, DrawActions possibleActions) { return(possibleActions.HasFlag(DrawActions.KyuushuKyuuhai)); }
internal override bool CanExecute(VisibleBoard board, DrawActions possibleActions) { return(HasTile(board, _tile)); }
internal override bool CanExecute(VisibleBoard board, DiscardActions possibleActions) { return(true); }
public void Updated(VisibleBoard board) { Broadcast(s => s.Updated(board)); }
internal override bool CanExecute(VisibleBoard board, DiscardActions possibleActions) { return(possibleActions.HasFlag(DiscardActions.Kan)); }
internal override bool CanExecute(VisibleBoard board, DrawActions possibleActions) { return(possibleActions.HasFlag(DrawActions.Riichi) && HasTile(board, _tile) && board.Watashi.Hand.ShantenAfterDiscard(_tile.TileType) == 0); }
internal abstract bool CanExecute(VisibleBoard board, DiscardActions possibleActions);