Beispiel #1
0
        /// <summary>
        /// Given a tile we want to give info on, evaluate the relative strengths of suit vs number info for that tile
        /// and return whichever is better
        /// </summary>
        private Tuple <int, PlayerAction> ChooseBestInfoForTile(int playerIndex, Tile tile, string log)
        {
            // Only give info if this tile hasn't had info given on it recently
            var  unknownTile  = Lookup(tile);
            bool noRecentInfo = (unknownTile.InfoAge == -1 || unknownTile.InfoAge > 20);

            // Only give this info if no other player has already been told to play their copy of this tile
            var  otherCopies            = m_lastGameState.AllHands().Where(t => t.Same(tile) && t.UniqueId != tile.UniqueId);
            bool otherCopiesInfoAlready = otherCopies.Any(t => Lookup(t).InfoAge >= 0);

            Tuple <int, PlayerAction> bestAction = null;

            if (noRecentInfo && !otherCopiesInfoAlready)
            {
                // Construct an action for giving number info for this tile
                var numAction   = PlayerAction.GiveInfo(PlayerIndex, PlayerAction.PlayerActionInfoType.Number, playerIndex, tile.Number, log);
                var numTiles    = m_lastGameState.Hands[playerIndex].Where(t => t.Number == tile.Number);
                var numPlayable = numTiles.Count(t => m_lastGameState.IsPlayable(t));
                var numBad      = numTiles.Count() - numPlayable;
                var numFitness  = (INFO_PLAYABLE_FITNESS * numPlayable + INFO_UNPLAYABLE_FITNESS * numBad);

                // Construct an action for giving suit info for this tile
                var suitAction   = PlayerAction.GiveInfo(PlayerIndex, PlayerAction.PlayerActionInfoType.Suit, playerIndex, (int)tile.Suit, log);
                var suitTiles    = m_lastGameState.Hands[playerIndex].Where(t => t.Suit == tile.Suit);
                var suitPlayable = suitTiles.Count(t => m_lastGameState.IsPlayable(t));
                var suitBad      = suitTiles.Count() - suitPlayable;
                var suitFitness  = (INFO_PLAYABLE_FITNESS * suitPlayable + INFO_UNPLAYABLE_FITNESS * suitBad);

                if (numFitness > suitFitness)
                {
                    bestAction = new Tuple <int, PlayerAction>(numFitness, numAction);
                }
                else
                {
                    bestAction = new Tuple <int, PlayerAction>(suitFitness, suitAction);
                }
            }

            return(bestAction);
        }
Beispiel #2
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));
            }
        }