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