Beispiel #1
0
        public override State Handle(PlayerAction action, IPlayerContext playerContext)
        {
            // [_, ход пятёркой] -> "конечное состояние. Подсказка есть."

            if (IsPlayFiveRankedAction(action) || IsDiscardNoNeedCard(action))
            {
                return(new FinalWithClueState(this));
            }

            // [A, ход картой] -> A
            // ["возможна подсказка", сброс некритичной карты] -> "возможна подсказка"
            if (IsPlayCardAction(action) || IsWhateverToPlayCardAction(action))
            {
                return(this);
            }

            // ["возможна подсказка", взрыв] -> "требуется подсказка" или "безвыходная ситуация"
            // ["возможна подсказка", сброс критичной карты] -> "требуется подсказка" или "безвыходная ситуация"
            if (action.IsActionToAvoid)
            {
                ClueAndAction          clueAndAction = action.CreateClueToAvoid(BoardContext, playerContext);
                IClueSituationStrategy solution;

                if (clueAndAction == null)
                {
                    // придумаем подсказку про запас

                    var possibleClue = CreateClue(playerContext, action.Cards);
                    if (possibleClue != null)
                    {
                        solution = new OnlyClueExistsSituation(playerContext, possibleClue);
                        return(new RequiredClue(this, solution));
                    }
                    else
                    {
                        return(this);
                    }
                }

                // TODO выяснить, что будет делать игрок после этой подсказки
                // Если действие порождает подсказку, то можно уходить в конечной состояние.

                solution = new OnlyClueExistsSituation(playerContext, clueAndAction.Clue);
                return(new RequiredClue(this, solution));
            }

            throw new ArgumentException(action.ToString());
        }
Beispiel #2
0
        protected virtual ClueAndAction ChooseClue(IPlayerContext playerContext,
                                                   IEnumerable <ClueAndAction> possibleCluesAndActions)
        {
            if (playerContext == null)
            {
                throw new ArgumentNullException(nameof(playerContext));
            }
            if (possibleCluesAndActions == null)
            {
                throw new ArgumentNullException(nameof(possibleCluesAndActions));
            }

            if (possibleCluesAndActions.Count() <= 1)
            {
                return(possibleCluesAndActions.FirstOrDefault());
            }

            int           max    = 0;
            ClueAndAction result = null;

            foreach (var clueAndAction in possibleCluesAndActions)
            {
                int affectedCards = GetAffectedCardsCount(playerContext, clueAndAction.Clue);

                if (affectedCards > max)
                {
                    max    = affectedCards;
                    result = clueAndAction;
                }
            }

            return(result);

            int GetAffectedCardsCount(IPlayerContext context, ClueType clue)
            {
                // возможно, стоит исключить карты, о которых игрок и так знает...

                return(context.Hand
                       .Select(cardInHand => cardInHand.Card)
                       .Select(card => new ClueAndCardMatcher(card))
                       .Count(clueAndCardMatcher => clue.Accept(clueAndCardMatcher)));
            }
        }
Beispiel #3
0
        public ClueAndAction Find()
        {
            var expectedCards = _boardContext.GetExpectedCards();

            var expectedThatCanPlay =
                _playerContext.Hand
                .Where(cih => expectedCards.Contains(cih.Card))
                .ToList();

            var cardsToPlay =
                expectedThatCanPlay.Where(cih => cih.Card.Rank == Rank.Five)
                .Concat(
                    expectedThatCanPlay
                    .Where(cih => cih.Card.Rank != Rank.Five)
                    .OrderBy(cih => cih.Card.Rank)
                    )
                .ToList();


            List <ClueAndAction> possibleClues = new List <ClueAndAction>();

            if (cardsToPlay.Any())
            {
                var clues =
                    cardsToPlay
                    .Select(card => ClueDetailInfo.CreateClues(card, _playerContext))
                    .Aggregate((acc, list) => acc.Concat(list).ToList())
                    .Distinct();

                foreach (var clue in clues)
                {
                    var playerContext = _playerContext.Clone();
                    playerContext.PossibleClue = clue;

                    PlayerActionPredictor predictor    = new PlayerActionPredictor(_boardContext, playerContext);
                    IPlayCardStrategy     playStrategy =
                        PlayStrategyFabric.Create(playerContext.Player.GameProvider, playerContext);

                    IDiscardStrategy discardStrategy =
                        DiscardStrategyFabric.Create(playerContext.Player.GameProvider, playerContext);

                    var action = predictor.Predict(playStrategy, discardStrategy);

                    if ((action.Outcome & OutcomeFlags.Play) > 0)
                    {
                        possibleClues.Add(new ClueAndAction {
                            Action = action, Clue = clue
                        });
                    }
                }
            }

            if (possibleClues.Count <= 1)
            {
                return(possibleClues.FirstOrDefault());
            }

            // раз возможных подсказок больше одной, то выберем ту, которая затрагивает большее число карт
            // из тех, которыми можно сыграть
            int           max           = 0;
            ClueAndAction clueAndAction = null;

            foreach (var clue in possibleClues)
            {
                int cardsAffected = CardsToClue(clue.Clue);
                if (cardsAffected > max)
                {
                    max           = cardsAffected;
                    clueAndAction = clue;
                }
            }

            return(clueAndAction);
        }
Beispiel #4
0
        public override State Handle(PlayerAction action, IPlayerContext playerContext)
        {
            // [_, ход пятёркой] -> "конечное состояние без подсказки"

            if (IsPlayFiveRankedAction(action))
            {
                return(new FinalWithoutClueState(this, playerContext.Player));
            }

            // [_, сброс ненужной карты] -> "конечное состояние без подсказки"
            if (IsDiscardNoNeedCard(action))
            {
                // пробуем найти альтернативу.
                var alternativeAction = action.CreateClueToAvoid(BoardContext, playerContext);
                var defaultState      = new FinalWithoutClueState(this, playerContext.Player);

                if (alternativeAction != null)
                {
                    BoardContext.AddToFirework(action.Cards.First());
                    var situation = new OnlyClueExistsSituation(playerContext, alternativeAction.Clue);

                    var optionalState = new OptionalClueState(this, situation);

                    return(new MixedState(optionalState, defaultState));
                }
                else
                {
                    return(defaultState);
                }
            }

            // [A, ход картой] -> A
            if (IsPlayCardAction(action))
            {
                BoardContext.AddToFirework(action.Cards.First());
                return(this);
            }

            // ["никаких подсказок не требуется", сброс некритичной карты] -> "возможна подсказка" или "плюс подсказка"
            if (IsWhateverToPlayCardAction(action))
            {
                var clueAndAction =
                    action.CreateClueToAvoid(BoardContext, playerContext);

                if (clueAndAction != null)
                {
                    // добавить в сброшенные карты карту, которая полетит?
                    return(new OptionalClueState(this, new OnlyClueExistsSituation(playerContext, clueAndAction.Clue)));
                }
                else
                {
                    return(new FinalWithoutClueState(this, playerContext.Player));
                }
            }

            // ["никаких подсказок не требуется", взрыв] -> "требуется подсказка" или безвыходная ситуация
            // ["никаких подсказок не требуется", сброс критичной карты] -> "требуется подсказка" или безвыходная ситуация
            if (action.IsActionToAvoid)
            {
                ClueAndAction clueAndAction = action.CreateClueToAvoid(BoardContext, playerContext);

                if (clueAndAction == null)
                {
                    return(new NoExitState(this));
                }

                // TODO выяснить, что будет делать игрок после этой подсказки
                // Если действие порождает подсказку, то можно уходить в конечной состояние.
                var correctedAction = clueAndAction.Action;

                var solution          = new OnlyClueExistsSituation(playerContext, clueAndAction.Clue);
                var requiredClueState = new RequiredClue(this, solution);
                if ((correctedAction.Outcome & OutcomeFlags.Play) > 0)
                {
                    return(requiredClueState);
                }
                else if ((correctedAction.Outcome &
                          (OutcomeFlags.DiscardNoNeedCard |
                           OutcomeFlags.DiscardWhateverToPlayCard |
                           OutcomeFlags.PlayFiveRankCard)) > 0)
                {
                    return(new FinalWithClueState(requiredClueState));
                }
                throw new InvalidOperationException($"{action.GetType()}");
            }

            throw new InvalidOperationException($"Неизвестный тип {action.GetType()}");
        }