Fixed some bugs and added new features that came up while testing:
All checks were successful
Build Mau & Deploy Mau / build (push) Successful in 1m29s
Build Mau & Deploy Mau / deploy (push) Has been skipped

- Check for allowed card type was before the check for the mau cards, which caused incorrect cards to be played and mau cards to be passed to wrong players
- Ending with a special card is now a faulty move, which grants the player 5 fault cards now
- Grabbed cards could be registered as playable as it didnt include next allowed card type
- If the first card is a joker, set the next allowed card type to a random other card type to prevent an instant softlock
- Other cleanup :)
This commit is contained in:
2024-06-12 15:57:09 +02:00
parent 2ef1de9d64
commit 0918b2b3cc
3 changed files with 126 additions and 78 deletions

View File

@@ -31,22 +31,37 @@ public static class CardExtensions
{ {
return card1.IsSameCardType(card2) && card1.IsSameCardValue(card2); return card1.IsSameCardType(card2) && card1.IsSameCardValue(card2);
} }
public static bool IsSameCardType(this Card card1, Card card2) public static bool IsSameCardType(this Card card1, Card card2)
{ {
return card1.CardType == card2.CardType; return card1.CardType == card2.CardType;
} }
public static bool IsSameCardValue(this Card card1, Card card2) public static bool IsSameCardValue(this Card card1, Card card2)
{ {
return card1.CardValue == card2.CardValue; return card1.CardValue == card2.CardValue;
} }
public static bool CanBePlayedOn(this Card playedCard, Card currentCard) public static bool CanBePlayedOn(this Card playedCard, Card currentCard)
{ {
return playedCard.IsSameCardType(currentCard) return playedCard.IsSameCardType(currentCard)
|| playedCard.IsSameCardValue(currentCard) || playedCard.IsSameCardValue(currentCard)
|| playedCard.CardType == CardType.JOKER; || 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;
} }
} }

View File

@@ -2,9 +2,9 @@
public class Deck public class Deck
{ {
private List<Card> _unusedDeck = new(); private readonly List<Card> _unusedDeck = new();
private List<Card> _usedDeck = new(); private readonly List<Card> _usedDeck = new();
public Card CurrentCard; public Card CurrentCard { get; private set; }
/** /**
* <summary> * <summary>
@@ -15,23 +15,20 @@ public class Deck
{ {
CreateSet(); CreateSet();
ShuffleDeck(); ShuffleDeck();
CurrentCard = DrawCard(); var initialCard = DrawCard();
_usedDeck.Add(CurrentCard); CurrentCard = initialCard;
if (CurrentCard.CardType != CardType.JOKER) return;
CurrentCard = DrawCard();
_usedDeck.Add(CurrentCard);
} }
/** /**
* <summary> * <summary>
* 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.
* </summary> * </summary>
* <param name="card">The card to add to the used cards deck.</param> * <param name="card">The card to add to the used cards deck.</param>
*/ */
public void AddCardToUsedDeck(Card card) public void AddCardToUsedDeck(Card card)
{ {
_usedDeck.Add(card); _usedDeck.Add(card);
CurrentCard = card;
} }
/** /**
@@ -44,29 +41,6 @@ public class Deck
{ {
_usedDeck.AddRange(cards); _usedDeck.AddRange(cards);
} }
/**
* <summary>
* Creates a new deck of cards and adds them to the unused deck.
* </summary>
*/
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));
}
}
}
/** /**
* <summary> * <summary>
@@ -97,6 +71,29 @@ public class Deck
} }
return cards; return cards;
} }
/**
* <summary>
* Creates a new deck of cards and adds them to the unused deck.
* </summary>
*/
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));
}
}
}
/** /**
* <summary> * <summary>
@@ -124,6 +121,9 @@ public class Deck
*/ */
private void ShuffleDeck() private void ShuffleDeck()
{ {
_unusedDeck = _unusedDeck.OrderBy(x => Guid.NewGuid()).ToList(); var unusedDeckCopy = new List<Card>(_unusedDeck);
_unusedDeck.Clear();
unusedDeckCopy = unusedDeckCopy.OrderBy(x => Guid.NewGuid()).ToList();
_unusedDeck.AddRange(unusedDeckCopy);
} }
} }

View File

@@ -9,19 +9,38 @@ namespace MauMau_Server.Mau;
public class Game : RoomType public class Game : RoomType
{ {
// Helpers
private readonly Deck _deck = new(); private readonly Deck _deck = new();
private readonly TurnManager _turnManager = new(); private readonly TurnManager _turnManager = new();
public readonly List<Card> MauCardBuffer = new();
public CardType? NextAllowedCardType { get; set; } // Game state
private readonly List<Card> _mauCardBuffer = new();
private CardType? NextAllowedCardType { get; set; }
// Variables
private const int NumberOfFaultcards = 5;
private const int NumberOfStartCards = 8;
public Game(Room.Room room, IEnumerable<ConnectionInstance> connections) : base(room) public Game(Room.Room room, IEnumerable<ConnectionInstance> 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 // Convert all the connections to players
List<Player> players = new(); List<Player> players = new();
foreach (var player in connections.Select(connection => new Player(connection))) foreach (var player in connections.Select(connection => new Player(connection)))
{ {
// Give the new player a hand of cards // Give the new player a hand of cards
var initialHand = _deck.DrawCards(8); var initialHand = _deck.DrawCards(NumberOfStartCards);
player.GiveCards(initialHand); player.GiveCards(initialHand);
players.Add(player); 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 // If there are cards in the MauCardBuffer, this means there are multiple cards that need to be drawn
// Otherwise, just draw a single card // Otherwise, just draw a single card
if (MauCardBuffer.Count > 0) if (_mauCardBuffer.Count > 0)
{ {
// Count the amount of cards that need to be drawn // Count the amount of cards that need to be drawn
var totalCards = CountMauCardBuffer(); var totalCards = CountMauCardBuffer();
@@ -178,7 +197,7 @@ public class Game : RoomType
player.GiveCard(drawnCard); player.GiveCard(drawnCard);
// Change the player if the drawn card cannot be played // Change the player if the drawn card cannot be played
if (!drawnCard.CanBePlayedOn(_deck.CurrentCard)) if (!CardCanBePlayed(drawnCard))
{ {
_turnManager.ChangeTurnTo(); _turnManager.ChangeTurnTo();
} }
@@ -217,43 +236,26 @@ public class Game : RoomType
{ {
return; return;
} }
// Check if there is a specific card type that is allowed to be played if (!CardCanBePlayed(playerCard)) return;
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;
}
}
// Remove the card from the player's hand // Remove the card from the player's hand
player.Hand.Remove(playerCard); 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 // Add the card to the used deck
_deck.AddCardToUsedDeck(playerCard); _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 // If the player has no cards left, end the game with player as winner
_deck.CurrentCard = playerCard;
if (player.Hand.Count == 0) if (player.Hand.Count == 0)
{ {
EndGame(player); EndGame(player);
@@ -280,13 +282,13 @@ public class Game : RoomType
case CardValue.RED: case CardValue.RED:
case CardValue.BLACK: case CardValue.BLACK:
{ {
MauCardBuffer.Add(card); _mauCardBuffer.Add(card);
_turnManager.CurrentPlayer.State = PlayerState.CHOOSE; _turnManager.CurrentPlayer.State = PlayerState.CHOOSE;
break; break;
} }
case CardValue.TWO: case CardValue.TWO:
{ {
MauCardBuffer.Add(card); _mauCardBuffer.Add(card);
_turnManager.ChangeTurnTo(); _turnManager.ChangeTurnTo();
break; break;
} }
@@ -319,6 +321,37 @@ public class Game : RoomType
} }
} }
/**
* <summary>
* Check if the card can be played with the current game state.
* <list type="bullet">
* <item>The player can only play a mau card if there are pending mau cards (or draw)</item>
* <item>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</item>
* <item>Otherwise, the player can play a card that has the same type, same value or is a joker</item>
* </list>
* </summary>
* <returns>True if the given card could be played</returns>
*/
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);
}
/** /**
* <summary> * <summary>
* Count the amount of cards that need to be drawn from the MauCardBuffer. This method also clears the buffer. * 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() private int CountMauCardBuffer()
{ {
var totalCards = 0; var totalCards = 0;
foreach (var card in MauCardBuffer) foreach (var card in _mauCardBuffer)
{ {
if (card.CardType == CardType.JOKER) if (card.CardType == CardType.JOKER)
{ {
@@ -342,7 +375,7 @@ public class Game : RoomType
continue; continue;
} }
} }
MauCardBuffer.Clear(); _mauCardBuffer.Clear();
return totalCards; return totalCards;
} }