/// <summary> /// A player took an action! /// </summary> public void RecordPlayerTurn(Turn turn, int turnNumber) { // Increase the age of info foreach (var tile in m_infoLookup.Values) { tile.AgeInfo(); } switch (turn.Action.ActionType) { case PlayerAction.PlayerActionType.Info: { List <Guid> tiles = new List <Guid>(); // Go through the tiles included in the give and record what we now know about them foreach (Guid g in turn.TargetedTiles) { var lookup = Lookup(g); if (turn.Action.InfoType == PlayerAction.PlayerActionInfoType.Suit) { lookup.MustBe((Suit)turn.Action.Info, "Given Info"); lookup.GotInfo(); } else if (turn.Action.InfoType == PlayerAction.PlayerActionInfoType.Number) { lookup.MustBe(turn.Action.Info, "Given Info"); lookup.GotInfo(); } // If the targeted tile might be playable at all, remember it if (lookup.IsPossiblyPlayable(m_lastGameState)) { tiles.Add(g); } } if (m_allowFinesses) { if (turn.Action.TargetPlayer == PlayerIndex) { // Info was given to you, double check if it might be a finesse var finesseTargets = GetPossibleFinesseTargets(); // Ignore any finesse targets that are in the hand of the person giving the info, they can't see those finesseTargets = finesseTargets.Where(t => m_lastGameState.WhoHas(t.UniqueId) != turn.Action.ActingPlayer); // If there are possible finesses if (finesseTargets.Any()) { // Possible values our info could have if they're finessing each target var infoValueList = finesseTargets.Select(t => new TileValue(t.Suit, t.Number + 1)).ToList(); foreach (var infoTarget in turn.TargetedTiles) { foreach (var finesseTarget in finesseTargets) { var targetValueList = new TileValueList(); targetValueList.Add(new TileValue(finesseTarget.Suit, finesseTarget.Number)); var finesse = new Finesse(m_lastGameState, infoTarget, finesseTarget.UniqueId, infoValueList, targetValueList, turnNumber, turn.Action.ActingPlayer); m_activeFinesses.Add(finesse); } } } } else { // Info was given to someone else, see if you're being finessed var actualTiles = turn.TargetedTiles.Select(g => m_lastGameState.AllHands().First(t => t.UniqueId == g)); // If this info give is all unplayable tiles, it's gotta be a finesse if (!actualTiles.Any(t => m_lastGameState.IsPlayable(t))) { var finesseTargets = GetPossibleFinesseTargets(); var missingTargets = new List <Tile>(); foreach (var target in turn.TargetedTiles) { // Find this info tile var infoTile = m_lastGameState.AllHands().Where(t => t.UniqueId == target).First(); // See if the finesse targets list contains a corresponding tile var finesseTarget = finesseTargets.Where(t => t.Suit == infoTile.Suit && t.Number == infoTile.Number - 1).FirstOrDefault(); var infoValues = new TileValueList(); infoValues.Add(new TileValue(infoTile.Suit, infoTile.Number)); if (finesseTarget != null) { var targetValues = new TileValueList(); targetValues.Add(new TileValue(finesseTarget.Suit, finesseTarget.Number)); var finesse = new Finesse(m_lastGameState, infoTile.UniqueId, finesseTarget.UniqueId, infoValues, targetValues, turnNumber, turn.Action.ActingPlayer); m_activeFinesses.Add(finesse); } else { // We must have the target! var myNewest = m_lastGameState.YourHand.Last(); var targetValues = new TileValueList(); targetValues.Add(new TileValue(infoTile.Suit, infoTile.Number - 1)); var finesse = new Finesse(m_lastGameState, infoTile.UniqueId, myNewest, infoValues, targetValues, turnNumber, turn.Action.ActingPlayer); m_activeFinesses.Add(finesse); } } } } } if (tiles.Any()) { // Modify our strength value for all targeted tiles that could possibly be playable ModifyPlayStrength(turn.Action.TargetPlayer, tiles); } // Now go through all other tiles in that hand and mark the negative info we got foreach (Guid g in TilesInHand(turn.Action.TargetPlayer).Except(turn.TargetedTiles)) { var lookup = Lookup(g); if (turn.Action.InfoType == PlayerAction.PlayerActionInfoType.Suit) { lookup.CannotBe((Suit)turn.Action.Info, "Negative info"); } else if (turn.Action.InfoType == PlayerAction.PlayerActionInfoType.Number) { lookup.CannotBe(turn.Action.Info, "Negative info"); } } } break; } }
/// <summary> /// Execute the given player action /// </summary> private void ExecuteAction(PlayerAction action) { // If our bot did something invalid, bail on this game if (action.ActionType == PlayerAction.PlayerActionType.Invalid) { LogGameState(); if (LogToConsole) { Debug.Assert(false, "Tried to play a null action!"); } else { throw new InvalidOperationException("Tried to play a null action!"); } } IPlayer currPlayer = m_players[m_currPlayerIndex]; List <Tile> currHand = m_hands[m_currPlayerIndex]; // Construct a Turn to put in our history Turn thisTurn = new Turn(); thisTurn.Action = action; // If this is an Info action, go ahead and construct the list of tiles it targets if (action.ActionType == PlayerAction.PlayerActionType.Info) { var targetHand = m_hands[action.TargetPlayer]; thisTurn.TargetedTiles = new List <Guid>(); if (action.InfoType == PlayerAction.PlayerActionInfoType.Number) { thisTurn.TargetedTiles.AddRange(targetHand.Where(t => t.Number == action.Info).Select(t => t.UniqueId)); } else if (action.InfoType == PlayerAction.PlayerActionInfoType.Suit) { thisTurn.TargetedTiles.AddRange(targetHand.Where(t => t.Suit == (Suit)action.Info).Select(t => t.UniqueId)); } } // Log the player's action String tiles = ""; if (thisTurn.TargetedTiles != null && thisTurn.TargetedTiles.Any()) { tiles += " at " + thisTurn.TargetedTiles.Aggregate("", (s, g) => s += g + " "); } Log("\nPLAYER " + currPlayer.PlayerIndex + ": \"" + thisTurn.Action + tiles + ".\""); // Now process that action switch (action.ActionType) { case PlayerAction.PlayerActionType.Discard: { // If we try to discard with 8 tokens, bail on this game if (m_tokens == 8) { if (LogToConsole) { Debug.Assert(false, "Can't discard with 8 tokens"); } else { throw new InvalidOperationException("Can't discard with 8 tokens"); } } // Do it! Tile toDiscard = currHand.First(t => t.UniqueId == action.TileId); currHand.Remove(toDiscard); DiscardTile(toDiscard); DrawToHand(currHand); m_tokens++; } break; case PlayerAction.PlayerActionType.Play: { // Try to play the tile Tile toPlay = currHand.First(t => t.UniqueId == action.TileId); currHand.Remove(toPlay); AttemptPlayTile(toPlay); DrawToHand(currHand); } break; case PlayerAction.PlayerActionType.Info: { // If we try to give info when we can't, bail on this game if (m_tokens == 0) { if (LogToConsole) { Debug.Assert(false, "Can't discard with 8 tokens"); } else { throw new InvalidOperationException("Can't give info with 0 tokens"); } } // Use up a token m_tokens--; } break; } // Finally add this turn's data to the history m_history.Add(thisTurn); }