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()); }
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))); } }
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); }
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()}"); }