public void PlayToDeck_Player_can_play_any_card_on_empty_Discard_pile(CardSuit cardSuit, int cardValue)
        {
            // Arrange
            _game.Mode = ShitheadConstants.GameModes.NORMAL;
            var player1HandPile = _player1.GetHandPile();
            var card            = new Card(null, cardSuit, cardValue);

            player1HandPile.Cards.Add(card);

            ShitheadGameConfig config = ShitheadGameConfig.Default;

            _shitheadMoveValidator.CanPlayCardsOnPile(config, Arg.Any <Pile>(), Arg.Any <List <Card> >()).ReturnsNull();

            List <Card> cards = new List <Card>
            {
                card,
            };

            // Act
            Assert.Single(player1HandPile.Cards);
            Assert.Empty(_discardPile.Cards);
            _shitheadPileLogic.PlayToDeck(config, _player1, cards);

            // Assert
            Assert.Empty(player1HandPile.Cards);
            Assert.Single(_discardPile.Cards);
        }
        public void CanPlayCardsOnPile_Valid_when_card_can_be_played_on_anything(int cardToPlayValue)
        {
            // Arrange
            var config = new ShitheadGameConfig {
                Joker = 15, Reset = 2, Invisible = 3, Reverse = 7, Burn = 10, Skip = 15
            };
            var  shitheadMoveValidator = new ShitheadMoveValidator();
            Pile pile = new Pile(null, PileType.Deck, "")
            {
                Cards = new List <Card>
                {
                    new Card(null, CardSuit.Clubs, 14)
                },
            };
            var cardsToPlay = new List <Card>
            {
                new Card(null, CardSuit.Clubs, cardToPlayValue)
            };

            // Act
            var result = shitheadMoveValidator.CanPlayCardsOnPile(config, pile, cardsToPlay);

            // Assert
            Assert.Null(result);
        }
        public void CanPlayCardsOnPile_Invalid_when_card_is_lower_than_pile()
        {
            // Arrange
            var config = new ShitheadGameConfig {
                Joker = 15, Reset = 2, Invisible = 3, Reverse = 7, Burn = 10, Skip = 15
            };
            var  shitheadMoveValidator = new ShitheadMoveValidator();
            Pile pile = new Pile(null, PileType.Deck, "")
            {
                Cards = new List <Card>
                {
                    new Card(null, CardSuit.Clubs, 14),
                },
            };
            var cardsToPlay = new List <Card>
            {
                new Card(null, CardSuit.Clubs, 5)
            };

            // Act
            var result = shitheadMoveValidator.CanPlayCardsOnPile(config, pile, cardsToPlay);

            // Assert
            var expectedValidationResult = string.Format(Resources.Card__0__is_not_higher_or_equal_to_card__1_, cardsToPlay.First(), pile.Cards.First());

            Assert.Equal(expectedValidationResult, result);
        }
Ejemplo n.º 4
0
        public void AddDeckCardsToPile(ShitheadGameConfig config, Pile pile, CreateDeckOptions options)
        {
            var cards = new List <Card>();

            for (var i = 1; i <= 13; i++)
            {
                cards.Add(new Card(pile, CardSuit.Clubs, i));
                cards.Add(new Card(pile, CardSuit.Diamonds, i));
                cards.Add(new Card(pile, CardSuit.Hearts, i));
                cards.Add(new Card(pile, CardSuit.Spades, i));
            }

            if (options.IncludeJokers)
            {
                for (var i = 0; i < 2; i++)
                {
                    cards.Add(new Card(pile, CardSuit.Joker, config.Joker));
                }
            }

            if (options.Shuffled)
            {
                _shuffler.Shuffle(cards);
            }

            foreach (var card in cards)
            {
                pile.Cards.Add(card);
            }
        }
        private static bool ShouldBurnPile(ShitheadGameConfig config, Pile discardPile)
        {
            var lastDiscardedCard = discardPile.Cards.Last();

            if (lastDiscardedCard.Value == config.Burn)
            {
                return(true);
            }

            return(discardPile.Cards.Reverse().TakeWhile(c => c.Value == lastDiscardedCard.Value).Count() >= 4);
        }
        public void PlayToDeck_Fail_if_zero_cards_to_play()
        {
            // Arrange
            ShitheadGameConfig config = ShitheadGameConfig.Default;
            List <Card>        cards  = new List <Card>();

            // Act
            var exception = Assert.Throws <DomainException>(() => _shitheadPileLogic.PlayToDeck(config, _player1, cards));

            // Assert
            Assert.Equal(Resources.At_least_one_card_must_be_specified_to_play_to_deck, exception.Message);
        }
        private static void OnAfterMove(
            ShitheadGameConfig config,
            Game game,
            Player player)
        {
            var deckPile       = game.GetDeckPile();
            var playerHandPile = player.GetHandPile();

            while (!deckPile.IsEmpty() && playerHandPile.Cards.Count < config.HandCount)
            {
                var firstDeckCard = deckPile.Cards.First();
                deckPile.Cards.Remove(firstDeckCard);
                playerHandPile.Cards.Add(firstDeckCard);
            }
        }
        public void PlayToDeck_Fail_if_another_players_turn()
        {
            // Arrange
            _game.State = "";
            ShitheadGameConfig config = ShitheadGameConfig.Default;
            List <Card>        cards  = new List <Card>
            {
                new Card(null, CardSuit.Clubs, 1)
            };

            // Act
            var exception = Assert.Throws <DomainException>(() => _shitheadPileLogic.PlayToDeck(config, _player1, cards));

            // Assert
            Assert.Equal(string.Format(Resources.It_is_another_player_s_turn__not_player__0_, _player1.Id), exception.Message);
        }
        public void PickUpDiscardPile(ShitheadGameConfig config, Player player, Pile playerHandPile)
        {
            var game = player.Game;

            var discardPile      = game.GetDiscardPile(ShitheadConstants.PileIdentifiers.DISCARD);
            var discardPileCards = discardPile.Cards.DefaultCardOrdering().ToList();

            discardPile.Cards.Clear();

            foreach (var discardPileCard in discardPileCards)
            {
                playerHandPile.Cards.Add(discardPileCard);
            }

            game.State = _shitheadPlayerLogic.CalculatePreviousPlayer(game, player);

            OnAfterMove(config, game, player);
        }
        public void PlayToDeck_Fail_if_cards_to_play_have_different_values()
        {
            // Arrange
            ShitheadGameConfig config = ShitheadGameConfig.Default;
            List <Card>        cards  = new List <Card>
            {
                new Card(null, CardSuit.Clubs, 1),
                new Card(null, CardSuit.Diamonds, 1),
                new Card(null, CardSuit.Clubs, 2),
                new Card(null, CardSuit.Clubs, 2),
            };

            // Act
            var exception = Assert.Throws <DomainException>(() => _shitheadPileLogic.PlayToDeck(config, _player1, cards));

            // Assert
            Assert.Equal(Resources.Cannot_play_cards_with_different_values, exception.Message);
        }
Ejemplo n.º 11
0
        public async Task TakeDiscardPile(ShitheadGameConfig config, Guid gameId, Guid playerId)
        {
            var game = await GetGameOrThrow(gameId);

            var player = game.Players.SingleOrDefault(p => p.Id == playerId);

            if (player == null)
            {
                throw new DomainException(DomainErrorCode.EntityMissing, $"Player with id {playerId} is not in the game with id {gameId}");
            }

            ValidateIsPlayerTurn(game, player);

            var playerHandPile = player.GetHandPile();

            _shitheadPileLogic.PickUpDiscardPile(config, player, playerHandPile);
            await _dbContext.SaveChangesAsync();

            await _realtimeService.OnGameMove(game.Id.ToString(), player.Id.ToString());
        }
Ejemplo n.º 12
0
        public async Task PlayCards(
            ShitheadGameConfig config,
            Guid gameId,
            Guid playerId,
            PlayShitheadCardsRequest request)
        {
            var game = await GetGameOrThrow(gameId);

            var player = game.Players.SingleOrDefault(p => p.Id == playerId);

            if (player == null)
            {
                throw new DomainException(DomainErrorCode.EntityMissing, $"Player with {gameId} is not in the game");
            }

            ValidateIsPlayerTurn(game, player);

            var playerPile    = player.GetFirstNonEmptyPile();
            var matchingCards = request.CardIds.Select(id => new
            {
                CardId = id,
                Card   = playerPile.Cards.SingleOrDefault(c => c.Id == id),
            })
                                .ToList();

            var missingCards = matchingCards.Where(m => m.Card == null).ToList();

            if (missingCards.Any())
            {
                var joinedIds = string.Join(", ", missingCards.Select(m => m.CardId));
                throw new DomainException(DomainErrorCode.BadRequest, $"Cards (ids {joinedIds}) do not exist or cannot be found in the pile of player {player.Id} (game {game.Id})");
            }

            var cardsToPlay = matchingCards.Select(m => m.Card).ToList();

            _shitheadPileLogic.PlayToDeck(config, player, cardsToPlay);
            await _dbContext.SaveChangesAsync();

            await _realtimeService.OnGameMove(game.Id.ToString(), player.Id.ToString());
        }
        public void CanPlayCardsOnPile_Valid_when_pile_has_only_NonValue_cards(int[] nonValueCards, int cardToPlayValue)
        {
            // Arrange
            var config = new ShitheadGameConfig {
                Joker = 15, Reset = 2, Invisible = 3, Reverse = 7, Burn = 10, Skip = 15
            };
            var  shitheadMoveValidator = new ShitheadMoveValidator();
            Pile pile = new Pile(null, PileType.Deck, "")
            {
                Cards = nonValueCards.Select(v => new Card(null, CardSuit.Clubs, v)).ToList(),
            };
            var cardsToPlay = new List <Card>
            {
                new Card(null, CardSuit.Clubs, cardToPlayValue)
            };

            // Act
            var result = shitheadMoveValidator.CanPlayCardsOnPile(config, pile, cardsToPlay);

            // Assert
            Assert.Null(result);
        }
        public void PlayToDeck_Fail_if_first_non_empty_player_pile_does_not_contains_cards()
        {
            // Arrange
            var player1HandPile = _player1.GetHandPile();
            var card1           = new Card(null, CardSuit.Clubs, 1)
            {
                Id = 1
            };
            var card2 = new Card(null, CardSuit.Diamonds, 1)
            {
                Id = 2
            };

            player1HandPile.Cards.Add(card1);
            player1HandPile.Cards.Add(card2);

            ShitheadGameConfig config = ShitheadGameConfig.Default;
            List <Card>        cards  = new List <Card>
            {
                card1,
                new Card(null, CardSuit.Spades, 1)
                {
                    Id = 3
                },
            };

            // Act
            var exception = Assert.Throws <DomainException>(() => _shitheadPileLogic.PlayToDeck(config, _player1, cards));

            // Assert
            var expectedExceptionMessage = string.Format(Resources.Cards__with_ids__0___cannot_be_played_because_they_are_not_in_the_first_non_empty_pile__1__of_player__2_,
                                                         "3",
                                                         player1HandPile.Type.ToString(),
                                                         _player1.Id);

            Assert.Equal(expectedExceptionMessage, exception.Message);
        }
        public string CanPlayCardsOnPile(ShitheadGameConfig config, Pile pile, List <Card> cardsToPlay)
        {
            var pileCards = pile.Cards;

            if (pileCards.Count == 0)
            {
                return(null);
            }

            var cardToPlay  = cardsToPlay.First();
            var valueToPlay = cardToPlay.Value;

            if (valueToPlay == 1)
            {
                // Ace
                valueToPlay = 14;
            }

            if (valueToPlay == config.Reset ||
                valueToPlay == config.Invisible ||
                valueToPlay == config.Burn ||
                valueToPlay == config.Skip)
            {
                return(null);
            }

            var pileCardsWithValues = pileCards
                                      .Where(c =>
                                             c.Value != config.Invisible &&
                                             c.Value != config.Skip)
                                      .DefaultCardOrdering()
                                      .ToList();

            if (pileCardsWithValues.Count == 0)
            {
                // Pile ends with "invisible" or "no value" cards, so we can play on it
                return(null);
            }

            var lastPileCard = pileCardsWithValues.Last();

            var lastPileCardValue = lastPileCard.Value;

            if (lastPileCardValue == 1)
            {
                // Ace
                lastPileCardValue = 14;
            }

            if (lastPileCardValue == config.Reverse &&
                valueToPlay > lastPileCardValue)
            {
                return(string.Format(Resources.Card__0__is_not_lower_or_equal_to_the_reverse_card__1_, cardToPlay, lastPileCard));
            }

            if (lastPileCardValue != config.Reverse &&
                valueToPlay < lastPileCardValue)
            {
                return(string.Format(Resources.Card__0__is_not_higher_or_equal_to_card__1_, cardToPlay, lastPileCard));
            }

            return(null);
        }
Ejemplo n.º 16
0
        public async Task <CreateGameResponse> CreateGame(ShitheadGameConfig config, CreateShitheadGameRequest request)
        {
            await using (var transaction = await _dbContext.Database.BeginTransactionAsync())
            {
                var game = new Game(ShitheadConstants.GAME_TYPE, ShitheadConstants.GameModes.NORMAL);
                await _dbContext.Games.AddAsync(game);

                await _dbContext.SaveChangesAsync();

                var deckCount = 1 + request.NumberPlayers / 5;
                var deckPile  = new Pile(game, PileType.Deck, ShitheadConstants.PileIdentifiers.DECK);
                for (var i = 0; i < deckCount; i++)
                {
                    _deckFactory.AddDeckCardsToPile(config, deckPile, new CreateDeckOptions
                    {
                        IncludeJokers = true,
                        Shuffled      = true,
                    });
                }

                game.Piles.Add(deckPile);

                var playerFaceDownPiles = new List <Pile>();
                var playerFaceUpPiles   = new List <Pile>();
                var playerHandPiles     = new List <Pile>();
                for (var i = 0; i < request.NumberPlayers; i++)
                {
                    var player = new Player(game);
                    game.Players.Add(player);
                    await _dbContext.SaveChangesAsync();

                    var faceDownPile = game.CreatePlayerPile(player, PlayerHandType.FaceDown);
                    game.Piles.Add(faceDownPile);
                    playerFaceDownPiles.Add(faceDownPile);

                    var faceUpPile = game.CreatePlayerPile(player, PlayerHandType.FaceUp);
                    game.Piles.Add(faceUpPile);
                    playerFaceUpPiles.Add(faceUpPile);

                    var handPile = game.CreatePlayerPile(player, PlayerHandType.Hand);
                    game.Piles.Add(handPile);
                    playerHandPiles.Add(handPile);
                }

                for (var i = 1; i <= config.FaceDownCount; i++)
                {
                    foreach (var faceDownPile in playerFaceDownPiles)
                    {
                        var firstDeckCard = deckPile.Cards.First();
                        deckPile.Cards.Remove(firstDeckCard);
                        faceDownPile.Cards.Add(firstDeckCard);
                    }
                }

                for (var i = 1; i <= config.FaceUpCount; i++)
                {
                    foreach (var faceUpPile in playerFaceUpPiles)
                    {
                        var firstDeckCard = deckPile.Cards.First();
                        deckPile.Cards.Remove(firstDeckCard);
                        faceUpPile.Cards.Add(firstDeckCard);
                    }
                }

                for (var i = 1; i <= config.HandCount; i++)
                {
                    foreach (var handPile in playerHandPiles)
                    {
                        var firstDeckCard = deckPile.Cards.First();
                        deckPile.Cards.Remove(firstDeckCard);
                        handPile.Cards.Add(firstDeckCard);
                    }
                }

                var discardPile = new Pile(game, PileType.Discard, ShitheadConstants.PileIdentifiers.DISCARD);
                game.Piles.Add(discardPile);

                var burnPile = new Pile(game, PileType.Discard, ShitheadConstants.PileIdentifiers.BURN);
                game.Piles.Add(burnPile);

                game.State = game.Players.First().Id.ToString();

                await _dbContext.SaveChangesAsync();

                await transaction.CommitAsync();

                return(new CreateGameResponse(game));
            }
        }
        public void PlayToDeck(ShitheadGameConfig config, Player player, List <Card> cardsToPlay)
        {
            var game = player.Game;

            if (cardsToPlay.Count == 0)
            {
                throw new DomainException(DomainErrorCode.BadRequest, Resources.At_least_one_card_must_be_specified_to_play_to_deck);
            }

            if (game.State != player.Id.ToString())
            {
                throw new DomainException(DomainErrorCode.BadRequest, string.Format(Resources.It_is_another_player_s_turn__not_player__0_, player.Id));
            }

            var firstCardValue      = cardsToPlay.First().Value;
            var cardsWithOtherValue = cardsToPlay.Where(c => c.Value != firstCardValue).ToList();

            if (cardsWithOtherValue.Any())
            {
                throw new DomainException(DomainErrorCode.BadRequest, Resources.Cannot_play_cards_with_different_values);
            }

            var firstNonEmptyPile = player.GetFirstNonEmptyPile();

            if (!firstNonEmptyPile.ContainsAllCards(cardsToPlay, out var cardsMissingFromPile))
            {
                var msg = string.Format(Resources.Cards__with_ids__0___cannot_be_played_because_they_are_not_in_the_first_non_empty_pile__1__of_player__2_,
                                        string.Join(", ", cardsMissingFromPile.Select(c => c.Id)),
                                        firstNonEmptyPile.Type.ToString(),
                                        player.Id);
                throw new DomainException(DomainErrorCode.BadRequest, msg);
            }

            var discardPile       = game.GetDiscardPile(ShitheadConstants.PileIdentifiers.DISCARD);
            var canPlayValidation = _shitheadMoveValidator.CanPlayCardsOnPile(config, discardPile, cardsToPlay);

            if (canPlayValidation != null)
            {
                throw new DomainException(DomainErrorCode.BadRequest, canPlayValidation);
            }

            foreach (var card in cardsToPlay)
            {
                firstNonEmptyPile.Cards.Remove(card);
                discardPile.Cards.Add(card);
            }

            var pileGotBurnt = ShouldBurnPile(config, discardPile);

            if (pileGotBurnt)
            {
                var burnPile       = game.GetDiscardPile(ShitheadConstants.PileIdentifiers.BURN);
                var discardedCards = discardPile.Cards.ToList();
                foreach (var discardedCard in discardedCards)
                {
                    discardPile.Cards.Remove(discardedCard);
                    burnPile.Cards.Add(discardedCard);
                }
            }

            var playedCardIsReverse = firstCardValue == config.Reverse;
            var playedCardIsSkip    = firstCardValue == config.Skip;

            var flags = (PileGotBurnt : pileGotBurnt, PlayedCardIsReverse : playedCardIsReverse, PlayedCardIsSkip : playedCardIsSkip);

            game.State = _shitheadPlayerLogic.CalculateNextPlayer(game, player, flags);

            OnAfterMove(config, game, player);
        }