Ejemplo n.º 1
0
        /// <summary>
        /// Get all the reasonable actions along with their "strength"
        /// </summary>
        private IEnumerable <Tuple <int, PlayerAction> > GetAllActions()
        {
            ///////////////////////////////////////////////////
            // PLAY ACTIONS
            ///////////////////////////////////////////////////
            foreach (Guid g in TilesInHand(PlayerIndex))
            {
                if (m_allowFinesses)
                {
                    // Finesses
                    var finesseTargetsInOurHand = m_activeFinesses.Where(f => m_lastGameState.YourHand.Contains(f.TargetTileId));

                    if (finesseTargetsInOurHand.Any())
                    {
                        var finesse = finesseTargetsInOurHand.First();

                        if (finesse.PossibleTargetValues.All(v => m_lastGameState.IsPlayable(v.Item1, v.Item2)))
                        {
                            var infoString = finesse.PossibleInfoValues.Aggregate("", (s, t) => s += t.Item1 + " " + t.Item2 + ", ");
                            var playAction = PlayerAction.PlayTile(PlayerIndex, finesse.TargetTileId, String.Format("Finessed by {0}", infoString));
                            yield return(new Tuple <int, PlayerAction>(80, playAction));
                        }
                    }
                }
                // Other Info

                var lookup = Lookup(g);
                // If this tile has been strongly messages OR we know it's definitely playable
                bool playStrength = lookup.PlayStrength >= GetCurrentPlayStrength();
                bool isUnplayable = lookup.IsUnplayable(m_lastGameState);
                bool isPlayable   = lookup.IsDefinitelyPlayable(m_lastGameState);
                if (isPlayable || (playStrength && !isUnplayable))
                {
                    string log = "";
                    if (isPlayable)
                    {
                        log = String.Format("This tile should be playable ({0})", lookup.ToString());
                    }
                    else
                    {
                        log = String.Format("PlayStrength is {0}", lookup.PlayStrength);
                    }

                    if (m_activeFinesses.Where(f => f.InfoTileId == g).Any())
                    {
                        // Check to see if this has play strength because it's the info part of a finesse
                        Finesse finesse  = m_activeFinesses.Where(f => f.InfoTileId == g).First();
                        int     currTurn = m_lastGameState.History.Count() + 1;

                        // If the target has had a chance to act
                        if (currTurn > finesse.TurnTargetShouldAct)
                        {
                            var target = finesse.PossibleTargetValues.First();
                            // If the target tile of this finesse was played
                            if (m_lastGameState.Play.Where(t => t.UniqueId == finesse.TargetTileId).Any())
                            {
                                // Awesome! This is probably playable!
                                log = String.Format("Was used to finesse the {0} {1} which was played!", target.Item1, target.Item2);
                            }
                            else
                            {
                                // There's been enough time but the finessed person didn't play
                                // Therefore this is probably not a finesse
                                // Remove the finesse from our list and keep assuming this tile is playable
                                m_activeFinesses.Remove(finesse);
                                log = String.Format("Thought this was finessing the {0} {1} but apparently not!", target.Item1, target.Item2);
                            }
                        }
                        else
                        {
                            // Hasn't been long enough for the finesse target to act yet
                            // So delay - skip considering this tile playable for now, but don't change any bookkeeping
                            continue;
                        }
                    }

                    var playAction = PlayerAction.PlayTile(PlayerIndex, g, log);
                    yield return(new Tuple <int, PlayerAction>(lookup.PlayStrength, playAction));
                }
            }

            ///////////////////////////////////////////////////
            // PURPOSEFUL DISCARD ACTIONS
            ///////////////////////////////////////////////////

            // Next see if we need to discard
            // TODO

            ///////////////////////////////////////////////////
            // INFO ACTIONS
            ///////////////////////////////////////////////////

            // Only give info if we have a token
            // TODO: don't wait until 0 tokens, maybe
            if (m_lastGameState.Tokens > 0)
            {
                if (m_allowFinesses)
                {
                    var finesses = GetSimpleFinesses().OrderBy(t => t.Item2.Number);

                    foreach (var combo in finesses)
                    {
                        var info = ChooseBestInfoForTile(combo.Item1, combo.Item2, "Finesse!");
                        if (info != null)
                        {
                            // Add bonus strength for this being a finesse
                            var newInfo = new Tuple <int, PlayerAction>(info.Item1 + 20, info.Item2);
                            yield return(newInfo);
                        }
                    }
                }

                // Next see if there's valid info to give
                // This is pretty naive
                var playable = GetAllPlayableTiles().OrderBy(t => t.Item2.Number);

                foreach (var combo in playable)
                {
                    var info = ChooseBestInfoForTile(combo.Item1, combo.Item2, "Playable");
                    if (info != null)
                    {
                        yield return(info);
                    }
                }
            }

            ///////////////////////////////////////////////////
            // NOTHING BETTER TO DO DISCARD
            ///////////////////////////////////////////////////

            if (m_lastGameState.Tokens < 8)
            {
                // TODO: account for don't-discard info
                var discardOldestAction = PlayerAction.DiscardTile(PlayerIndex, m_lastGameState.YourHand.First(), "Discard oldest");
                yield return(new Tuple <int, PlayerAction>(1, discardOldestAction));
            }
            else
            {
                // Give totally arbitrary info
                // This should maybe try to at least indicate a definitely-unplayable tile or something
                // This logic is currently responsible for most of our 3-Fuse endgames

                var leastPlayable = m_lastGameState.AllHands().OrderBy(t => t.Number).Last();
                var owner         = m_lastGameState.WhoHas(leastPlayable.UniqueId);

                var arbitraryInfoAction = PlayerAction.GiveInfo(PlayerIndex, PlayerAction.PlayerActionInfoType.Number, owner, leastPlayable.Number, "Least playable thing I could find");
                yield return(new Tuple <int, PlayerAction>(0, arbitraryInfoAction));
            }
        }
Ejemplo n.º 2
0
        /// <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;
            }
        }