diff --git a/Mau/Card.cs b/Mau/Card.cs index 204a1ae..45e384b 100644 --- a/Mau/Card.cs +++ b/Mau/Card.cs @@ -31,22 +31,37 @@ public static class CardExtensions { return card1.IsSameCardType(card2) && card1.IsSameCardValue(card2); } - + public static bool IsSameCardType(this Card card1, Card card2) { return card1.CardType == card2.CardType; } - + public static bool IsSameCardValue(this Card card1, Card card2) { return card1.CardValue == card2.CardValue; } - + public static bool CanBePlayedOn(this Card playedCard, Card currentCard) { return playedCard.IsSameCardType(currentCard) - || playedCard.IsSameCardValue(currentCard) - || playedCard.CardType == CardType.JOKER; + || playedCard.IsSameCardValue(currentCard) + || playedCard.CardType == CardType.JOKER; + } + + public static bool IsSpecialCard(this Card card) + { + return card.IsMauCard() || card.CardValue is + CardValue.SEVEN or + CardValue.EIGHT or + CardValue.JACK or + CardValue.KING or + CardValue.ACE; + } + + public static bool IsMauCard(this Card card) + { + return card.CardType == CardType.JOKER || card.CardValue is CardValue.TWO; } } diff --git a/Mau/Deck.cs b/Mau/Deck.cs index e5129e8..a45b1ec 100644 --- a/Mau/Deck.cs +++ b/Mau/Deck.cs @@ -2,9 +2,9 @@ public class Deck { - private List _unusedDeck = new(); - private List _usedDeck = new(); - public Card CurrentCard; + private readonly List _unusedDeck = new(); + private readonly List _usedDeck = new(); + public Card CurrentCard { get; private set; } /** * @@ -15,23 +15,20 @@ public class Deck { CreateSet(); ShuffleDeck(); - CurrentCard = DrawCard(); - _usedDeck.Add(CurrentCard); - - if (CurrentCard.CardType != CardType.JOKER) return; - CurrentCard = DrawCard(); - _usedDeck.Add(CurrentCard); + var initialCard = DrawCard(); + CurrentCard = initialCard; } /** * - * Adds the given card to the used cards deck. + * Adds the given card to the used cards deck and sets the current card to the given card. * * The card to add to the used cards deck. */ public void AddCardToUsedDeck(Card card) { _usedDeck.Add(card); + CurrentCard = card; } /** @@ -44,29 +41,6 @@ public class Deck { _usedDeck.AddRange(cards); } - - /** - * - * Creates a new deck of cards and adds them to the unused deck. - * - */ - private void CreateSet() - { - foreach (CardType cardType in Enum.GetValues(typeof(CardType))) - { - if (cardType == CardType.JOKER) - { - _unusedDeck.Add(new Card(cardType, CardValue.RED)); - _unusedDeck.Add(new Card(cardType, CardValue.BLACK)); - continue; - } - foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue))) - { - if (cardValue is CardValue.RED or CardValue.BLACK) continue; - _unusedDeck.Add(new Card(cardType, cardValue)); - } - } - } /** * @@ -97,6 +71,29 @@ public class Deck } return cards; } + + /** + * + * Creates a new deck of cards and adds them to the unused deck. + * + */ + private void CreateSet() + { + foreach (CardType cardType in Enum.GetValues(typeof(CardType))) + { + if (cardType == CardType.JOKER) + { + _unusedDeck.Add(new Card(cardType, CardValue.RED)); + _unusedDeck.Add(new Card(cardType, CardValue.BLACK)); + continue; + } + foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue))) + { + if (cardValue is CardValue.RED or CardValue.BLACK) continue; + _unusedDeck.Add(new Card(cardType, cardValue)); + } + } + } /** * @@ -124,6 +121,9 @@ public class Deck */ private void ShuffleDeck() { - _unusedDeck = _unusedDeck.OrderBy(x => Guid.NewGuid()).ToList(); + var unusedDeckCopy = new List(_unusedDeck); + _unusedDeck.Clear(); + unusedDeckCopy = unusedDeckCopy.OrderBy(x => Guid.NewGuid()).ToList(); + _unusedDeck.AddRange(unusedDeckCopy); } } \ No newline at end of file diff --git a/Mau/Game.cs b/Mau/Game.cs index 8bd9804..01e36b0 100644 --- a/Mau/Game.cs +++ b/Mau/Game.cs @@ -9,19 +9,38 @@ namespace MauMau_Server.Mau; public class Game : RoomType { + // Helpers private readonly Deck _deck = new(); private readonly TurnManager _turnManager = new(); - public readonly List MauCardBuffer = new(); - public CardType? NextAllowedCardType { get; set; } + + // Game state + private readonly List _mauCardBuffer = new(); + private CardType? NextAllowedCardType { get; set; } + + // Variables + private const int NumberOfFaultcards = 5; + private const int NumberOfStartCards = 8; public Game(Room.Room room, IEnumerable connections) : base(room) { + // If the current card is a joker, set the next allowed card type to a random card type + if (_deck.CurrentCard.CardType == CardType.JOKER) + { + // In the case the random card type is a joker, try again until it is not + do + { + var cardTypes = Enum.GetValues(typeof(CardType)); + var randomIndex = new Random().Next(cardTypes.Length); + NextAllowedCardType = (CardType?)cardTypes.GetValue(randomIndex) ?? CardType.SPADES; + } while (NextAllowedCardType == CardType.JOKER); + } + // Convert all the connections to players List players = new(); foreach (var player in connections.Select(connection => new Player(connection))) { // Give the new player a hand of cards - var initialHand = _deck.DrawCards(8); + var initialHand = _deck.DrawCards(NumberOfStartCards); player.GiveCards(initialHand); players.Add(player); } @@ -155,7 +174,7 @@ public class Game : RoomType { // If there are cards in the MauCardBuffer, this means there are multiple cards that need to be drawn // Otherwise, just draw a single card - if (MauCardBuffer.Count > 0) + if (_mauCardBuffer.Count > 0) { // Count the amount of cards that need to be drawn var totalCards = CountMauCardBuffer(); @@ -178,7 +197,7 @@ public class Game : RoomType player.GiveCard(drawnCard); // Change the player if the drawn card cannot be played - if (!drawnCard.CanBePlayedOn(_deck.CurrentCard)) + if (!CardCanBePlayed(drawnCard)) { _turnManager.ChangeTurnTo(); } @@ -217,43 +236,26 @@ public class Game : RoomType { return; } - - // Check if there is a specific card type that is allowed to be played - if (NextAllowedCardType != null) - { - // If the card is not the allowed card type, not the same value or a joker, ignore the message - if (playerCard.CardType != NextAllowedCardType && playerCard.CardType != CardType.JOKER && playerCard.CardValue != _deck.CurrentCard.CardValue) - { - return; - } - // Reset the allowed card type, so the next player has the normal same type and same value rules - NextAllowedCardType = null; - } else if (MauCardBuffer.Count > 0) - { - // If there are queued mau cards, the player can only another mau card (or draw) - if (playerCard.CardType != CardType.JOKER && playerCard.CardValue != CardValue.TWO) - { - return; - } - } - else - { - // Check if the card can be played on the current card, if not, ignore the message - if (!playerCard.CanBePlayedOn(_deck.CurrentCard)) - { - return; - } - } + + if (!CardCanBePlayed(playerCard)) return; // Remove the card from the player's hand player.Hand.Remove(playerCard); + // If the player's last played card is a special card, give the player 5 fault cards + if (player.Hand.Count < 1 && playerCard.IsSpecialCard()) + { + var faultCards = _deck.DrawCards(NumberOfFaultcards); + player.GiveCards(faultCards); + } + // Add the card to the used deck _deck.AddCardToUsedDeck(playerCard); + + // Reset the allowed card type, so the next player has the normal same type and same value rules + NextAllowedCardType = null; - // Set the new current card - _deck.CurrentCard = playerCard; - + // If the player has no cards left, end the game with player as winner if (player.Hand.Count == 0) { EndGame(player); @@ -280,13 +282,13 @@ public class Game : RoomType case CardValue.RED: case CardValue.BLACK: { - MauCardBuffer.Add(card); + _mauCardBuffer.Add(card); _turnManager.CurrentPlayer.State = PlayerState.CHOOSE; break; } case CardValue.TWO: { - MauCardBuffer.Add(card); + _mauCardBuffer.Add(card); _turnManager.ChangeTurnTo(); break; } @@ -319,6 +321,37 @@ public class Game : RoomType } } + /** + * + * Check if the card can be played with the current game state. + * + * The player can only play a mau card if there are pending mau cards (or draw) + * If there is a next allowed card type, the player can only play that card type, a joker or a card with the same value as the current card + * Otherwise, the player can play a card that has the same type, same value or is a joker + * + * + * True if the given card could be played + */ + private bool CardCanBePlayed(Card card) + { + // Check if there are pending mau cards played + if (_mauCardBuffer.Count > 0) + { + // If so, the card must be a mau card + return card.IsMauCard(); + } + + // Check if there is a next allowed card type + if (NextAllowedCardType != null) + { + // If so, the card must be the allowed card type, a joker or the same value as the current card + return card.CardType == NextAllowedCardType || card.CardType == CardType.JOKER || card.CardValue == _deck.CurrentCard.CardValue; + } + + // Otherwise, use the normal rules + return card.CanBePlayedOn(_deck.CurrentCard); + } + /** * * Count the amount of cards that need to be drawn from the MauCardBuffer. This method also clears the buffer. @@ -328,7 +361,7 @@ public class Game : RoomType private int CountMauCardBuffer() { var totalCards = 0; - foreach (var card in MauCardBuffer) + foreach (var card in _mauCardBuffer) { if (card.CardType == CardType.JOKER) { @@ -342,7 +375,7 @@ public class Game : RoomType continue; } } - MauCardBuffer.Clear(); + _mauCardBuffer.Clear(); return totalCards; }