Partial rewrite, missing:
Some checks failed
Build Mau & Deploy Mau / build (push) Failing after 1m15s
Build Mau & Deploy Mau / deploy (push) Has been skipped

- Correctly parsing incoming messages
- Sending the gamestate after relevant actions
This commit is contained in:
DTieman
2024-05-04 19:55:11 +02:00
parent 0c2cb8272d
commit 694bc6147a
20 changed files with 612 additions and 294 deletions

View File

@@ -1,92 +1,207 @@
using System.Text.Json;
using MauMau_Server.Mau.GameMessages;
using MauMau_Server.Mau.Managers;
using MauMau_Server.Websockets;
using MauMau_Server.Room;
using Newtonsoft.Json;
namespace MauMau_Server.Mau;
public class Game
public class Game : RoomType
{
public readonly Deck Deck = new();
private readonly Deck _deck = new();
public Card CurrentCard;
public List<Player> Players = new();
public Player CurrentPlayer;
public int TurnDirection = 1;
private readonly Room _room;
private readonly TurnManager _turnManager = new();
public Game(Room room)
public Game(Room.Room room, IEnumerable<ConnectionInstance> connections) : base(room)
{
_room = room;
CurrentCard = Deck.DrawCard();
Deck.AddCardToUsedDeck(CurrentCard);
}
CurrentCard = _deck.DrawCard();
_deck.AddCardToUsedDeck(CurrentCard);
public void AddPlayerToGame(ConnectionInstance connection)
{
var player = new Player(connection)
List<Player> players = new();
foreach (var player in connections.Select(connection => new Player(connection)))
{
Hand = Deck.DrawCards(8)
};
Players.Add(player);
if (Players.Count > 1) return;
CurrentPlayer = player;
CurrentPlayer.State = PlayerState.TURN;
}
public void RemovePlayer(string playerId)
{
var player = GetPlayer(playerId);
Players.Remove(player);
var initialHand = _deck.DrawCards(8);
player.GiveCards(initialHand);
players.Add(player);
}
_turnManager.Initialize(players);
}
public void HandleAction(string playerId, ActionDTO action)
/**
* <inheritdoc cref="RoomType.OnMessage"/>
*/
public override void OnMessage(ConnectionInstance sender, string message)
{
var player = GetPlayer(playerId);
if (CurrentPlayer != player) return;
switch (action.Action)
// Get the player that sent the message
var player = _turnManager.Players.FirstOrDefault(x => x.IsMe(sender.Id));
// If the player is not the player that is currently playing, ignore the message
if (_turnManager.CurrentPlayer != player) return;
// Deserialize the message
var gameMessage = JsonConvert.DeserializeObject<GameMessage>(message);
// Based on the message intent, handle the message
switch (gameMessage.Intent)
{
case "PLAYCARD":
{
if (player.State != PlayerState.TURN)
{
break;
}
var card = JsonSerializer.Deserialize<CardDTO>(action.Data).ToCard();
PlayCard(player, card);
case GameIntent.CHOOSE:
Choose(player, gameMessage.Data);
break;
}
case "CHOOSE":
var choice = action.Data;
if (!Enum.TryParse(choice, out CardType cardType))
{
break;
}
CurrentPlayer.State = PlayerState.WAIT;
CurrentPlayer = CurrentCard.CardType == CardType.JOKER ? GetNextPlayer(2) : GetNextPlayer();
CurrentPlayer.State = PlayerState.TURN;
CurrentCard = new Card(cardType, CardValue.JACK);
case GameIntent.DRAW:
default:
Draw(player, gameMessage.Data);
break;
case "DRAW":
if (player.State != PlayerState.TURN)
{
break;
}
DrawCard(player);
case GameIntent.PLAY:
Play(player, gameMessage.Data);
break;
}
}
private void PlayCard(Player player, Card card)
/**
* <inheritdoc cref="RoomType.OnConnect"/>
*/
public override void OnConnect(ConnectionInstance connection)
{
var hand = player.Hand;
if (!IsCardInHand(hand, card) || !IsCardPlayable(CurrentCard, card)) return;
Deck.AddCardToUsedDeck(card);
hand.Remove(GetSameCardFromHand(hand, card));
CurrentCard = card;
if (hand.Count == 0)
var player = new Player(connection);
var initialHand = _deck.DrawCards(8);
player.GiveCards(initialHand);
_turnManager.Players.Add(player);
}
/**
* <inheritdoc cref="RoomType.OnDisconnect"/>
*/
public override void OnDisconnect(ConnectionInstance connection)
{
var player = _turnManager.Players.FirstOrDefault(x => x.IsMe(connection.Id));
if (player is null) return;
var playerHand = player.Hand;
_deck.AddCardsToUsedDeck(playerHand);
if (player == _turnManager.CurrentPlayer)
{
_turnManager.ChangeTurn();
}
_turnManager.Players.Remove(player);
}
/**
* <summary>
* The player had to choose a new card type
* </summary>
* <param name="player">The player that chose a new card type</param>
* <param name="data">A string that represents a CardType</param>
*/
private void Choose(Player player, string data)
{
// Convert the data to a CardType, if it fails, ignore the message
if (!Enum.TryParse(data, out CardType cardType))
{
_room.EndGame(player);
return;
}
HandleNextPlayer(card);
// Does the current card require the next player to draw cards?
var isCardGivingCard = CurrentCard.CardType == CardType.JOKER || CurrentCard.CardValue == CardValue.TWO;
// Can the player that received a card giving card counter the card?
var skippedPlayerCanPlay = player.CanPlayCard(CurrentCard);
// If the card is a card giving card and the player cannot counter it, skip the next player
var shouldSkipPlayer = isCardGivingCard && !skippedPlayerCanPlay;
var nextPlayer = shouldSkipPlayer
? _turnManager.GetNextPlayer(2)
: _turnManager.GetNextPlayer();
// TODO: Not make it a jack, as it would not work when a Joker is played
// Set the new current card
CurrentCard = new Card(cardType, CardValue.JACK);
// Change the turns
_turnManager.ChangeTurn(nextPlayer);
}
/**
* <summary>
* The player either:
* <list type="bullet">
* <item>Could not play a card and had to draw a card</item>
* <item>Chose to draw a card as a strategic move</item>
* </list>
* </summary>
* <param name="player">The player that drew a card</param>
* <param name="data">A string that can be serialized to a drawcard instance</param>
*/
private void Draw(Player player, string data)
{
// TODO: data will contain the amount of cards to draw in the future, for now, just draw 1 card
// Draw a card from the deck
var drawnCard = _deck.DrawCard();
// Give the card to the player
player.GiveCard(drawnCard);
// If the player can play the card, do not change the player
if (drawnCard.CanBePlayedOn(CurrentCard)) return;
// Change the turn to the next player
_turnManager.ChangeTurn();
}
/**
* <summary>
* The player plays a card, this will only be possible if:
* <list type="bullet">
* <item>The player has the correct state</item>
* <item>The player has the card in their hand</item>
* <item>The card is playable on the current card</item>
* </list>
* </summary>
* <param name="player">The player that played</param>
* <param name="data">A string that can be serialized to a playcard instance</param>
*/
private void Play(Player player, string data)
{
// Check if the player has the correct state to play a card
if (player.State != PlayerState.TURN)
{
return;
}
// Convert the data to a Card instance
var cardData = JsonConvert.DeserializeObject<PlayCard>(data).ToCard();
// Check if the player indeed has the card they claim to have
var playerCard = player.TakeCardFromHand(cardData);
if (playerCard is null)
{
return;
}
// Check if the played card is compatible with the current card
// If not, ignore the play
if (!playerCard.CanBePlayedOn(CurrentCard))
{
return;
}
// Remove the card from the player's hand
player.Hand.Remove(playerCard);
// Add the card to the used deck
_deck.AddCardToUsedDeck(playerCard);
// Set the new current card
CurrentCard = playerCard;
if (player.Hand.Count == 0)
{
EndGame(player);
return;
}
HandleNextPlayer(playerCard);
}
private void HandleNextPlayer(Card card)
@@ -96,41 +211,31 @@ public class Game
case CardValue.RED:
case CardValue.BLACK:
{
var nextPlayer = GetNextPlayer();
var cardsToDraw = Deck.DrawCards(5);
foreach (var drawnCard in cardsToDraw)
{
nextPlayer.Hand.Add(drawnCard);
}
CurrentPlayer.State = PlayerState.CHOOSE;
_turnManager.GetNextPlayer().GiveCards(_deck.DrawCards(5));
_turnManager.CurrentPlayer.State = PlayerState.CHOOSE;
break;
}
case CardValue.TWO:
{
var nextPlayer = GetNextPlayer();
var cardsToDraw = Deck.DrawCards(2);
foreach (var drawnCard in cardsToDraw)
{
nextPlayer.Hand.Add(drawnCard);
}
HandleNextPlayer(CurrentPlayer, GetNextPlayer(2));
_turnManager.GetNextPlayer().GiveCards(_deck.DrawCards(2));
_turnManager.ChangeTurn(_turnManager.GetNextPlayer(2));
break;
}
case CardValue.SEVEN:
case CardValue.KING:
break;
case CardValue.EIGHT:
HandleNextPlayer(CurrentPlayer, GetNextPlayer(2));
_turnManager.ChangeTurn(_turnManager.GetNextPlayer(2));
break;
case CardValue.ACE:
if (Players.Count > 2)
if (_turnManager.Players.Count > 2)
{
TurnDirection *= -1;
HandleNextPlayer(CurrentPlayer, GetNextPlayer());
_turnManager.ChangeDirection();
_turnManager.ChangeTurn();
}
break;
case CardValue.JACK:
CurrentPlayer.State = PlayerState.CHOOSE;
_turnManager.CurrentPlayer.State = PlayerState.CHOOSE;
break;
case CardValue.THREE:
case CardValue.FOUR:
@@ -140,69 +245,18 @@ public class Game
case CardValue.TEN:
case CardValue.QUEEN:
default:
HandleNextPlayer(CurrentPlayer, GetNextPlayer());
_turnManager.ChangeTurn();
break;
}
}
private void HandleNextPlayer(Player current, Player next)
private void EndGame(Player winner)
{
current.State = PlayerState.WAIT;
next.State = PlayerState.TURN;
CurrentPlayer = next;
_room._roomType = new Lobby(_room, winner.Connection);
}
private void DrawCard(Player player)
public Player? GetPlayer(string playerId)
{
player.Hand.Add(Deck.DrawCard());
HandleNextPlayer(player, GetNextPlayer());
}
private Player GetNextPlayer(int numberOfPlayers = 1)
{
var index = Players.IndexOf(CurrentPlayer);
for (var i = 0; i < numberOfPlayers; i++)
{
index += TurnDirection;
if (index >= Players.Count) index = 0;
if (index < 0) index = Players.Count - 1;
}
return Players[index];
}
public Player GetPlayer(string playerId)
{
return Players.FirstOrDefault(p => p.IsMe(playerId));
}
private static Card GetSameCardFromHand(IEnumerable<Card> hand, Card card)
{
return hand.FirstOrDefault(handCard => IsSameCard(handCard, card));
}
private static bool IsCardPlayable(Card currentCard, Card playedCard)
{
return IsSameCardType(currentCard, playedCard) || IsSameCardValue(currentCard, playedCard) || playedCard.CardType == CardType.JOKER;
}
private static bool IsCardInHand(IEnumerable<Card> hand, Card card)
{
return hand.Any(handCard => IsSameCard(handCard, card));
}
private static bool IsSameCard(Card card1, Card card2)
{
return IsSameCardType(card1, card2) && IsSameCardValue(card1, card2);
}
private static bool IsSameCardType(Card card1, Card card2)
{
return card1.CardType == card2.CardType;
}
private static bool IsSameCardValue(Card card1, Card card2)
{
return card1.CardValue == card2.CardValue;
return _turnManager.Players.FirstOrDefault(p => p.IsMe(playerId));
}
}