diff --git a/Controllers/MauController.cs b/Controllers/MauController.cs new file mode 100644 index 0000000..acfc789 --- /dev/null +++ b/Controllers/MauController.cs @@ -0,0 +1,21 @@ +using MauMau_Server.Mau; +using Microsoft.AspNetCore.Mvc; + +namespace MauMau_Server.Controllers; + +[ApiController] +[Route("[controller]")] +public class DeckController : ControllerBase +{ + [HttpGet("deck")] + public IActionResult GetDeck() + { + return Ok(new Deck().GetUnusedDeck().Select(card => card.ToString()).ToList()); + } + + [HttpGet("hand")] + public IActionResult GetHand() + { + return Ok(new Deck().DrawCards(8).Select(card => card.ToString()).ToList()); + } +} \ No newline at end of file diff --git a/Controllers/RoomController.cs b/Controllers/RoomController.cs index d687512..f9c281b 100644 --- a/Controllers/RoomController.cs +++ b/Controllers/RoomController.cs @@ -51,4 +51,11 @@ public class RoomController : ControllerBase var id = _roomManager.CreateRoom(); return Ok(id); } + + [HttpDelete] + public IActionResult Delete() + { + _roomManager.RemoveAllRooms(); + return NoContent(); + } } \ No newline at end of file diff --git a/Mau/Card.cs b/Mau/Card.cs new file mode 100644 index 0000000..7e5ad7e --- /dev/null +++ b/Mau/Card.cs @@ -0,0 +1,51 @@ +namespace MauMau_Server.Mau; + +public class Card +{ + public readonly CardType CardType; + public readonly CardValue CardValue; + + public Card(CardType cardType, CardValue cardValue) + { + CardType = cardType; + CardValue = cardValue; + } + + public override string ToString() + { + return $"{CardType} {CardValue}"; + } + + public Card parseCard(string card) + { + var cardType = card.Split(" ")[0]; + var cardValue = card.Split(" ")[1]; + return new Card((CardType)Enum.Parse(typeof(CardType), cardType), + (CardValue)Enum.Parse(typeof(CardValue), cardValue)); + } +} + +public enum CardType +{ + SPADES, + HEARTS, + DIAMONDS, + CLUBS +} + +public enum CardValue +{ + TWO, + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + TEN, + JACK, + QUEEN, + KING, + ACE +} \ No newline at end of file diff --git a/Mau/CardDTO.cs b/Mau/CardDTO.cs new file mode 100644 index 0000000..91334b7 --- /dev/null +++ b/Mau/CardDTO.cs @@ -0,0 +1,30 @@ +namespace MauMau_Server.Mau; + +public class CardDTO +{ + public string CardType { get; set; } + public string CardValue { get; set; } + + public CardDTO(Card card) + { + CardType = card.CardType.ToString(); + CardValue = card.CardValue.ToString(); + } + + public CardDTO(string cardType, string cardValue) + { + CardType = cardType; + CardValue = cardValue; + } + + public CardDTO() + { + + } + + public Card ToCard() + { + return new Card((CardType)Enum.Parse(typeof(CardType), CardType), + (CardValue)Enum.Parse(typeof(CardValue), CardValue)); + } +} \ No newline at end of file diff --git a/Mau/Deck.cs b/Mau/Deck.cs new file mode 100644 index 0000000..b2b6ff5 --- /dev/null +++ b/Mau/Deck.cs @@ -0,0 +1,59 @@ +namespace MauMau_Server.Mau; + +public class Deck +{ + public List UnusedDeck = new(); + public List UsedDeck = new(); + + public Deck() + { + foreach (CardType cardType in Enum.GetValues(typeof(CardType))) + { + foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue))) + { + UnusedDeck.Add(new Card(cardType, cardValue)); + } + } + ShuffleDeck(); + } + + public List GetUnusedDeck() + { + return UnusedDeck; + } + + public Card DrawCard() + { + if (UnusedDeck.Count == 0) ReshuffleDeck(); + var card = UnusedDeck[0]; + UnusedDeck.RemoveAt(0); + return card; + } + + public List DrawCards(int amount) + { + var cards = new List(); + for (var i = 0; i < amount; i++) + { + cards.Add(DrawCard()); + } + return cards; + } + + public void AddCardToUsedDeck(Card card) + { + UsedDeck.Add(card); + } + + private void ReshuffleDeck() + { + UnusedDeck.AddRange(UsedDeck); + UsedDeck.Clear(); + ShuffleDeck(); + } + + private void ShuffleDeck() + { + UnusedDeck = UnusedDeck.OrderBy(x => Guid.NewGuid()).ToList(); + } +} \ No newline at end of file diff --git a/Mau/Game.cs b/Mau/Game.cs new file mode 100644 index 0000000..1ba09f0 --- /dev/null +++ b/Mau/Game.cs @@ -0,0 +1,90 @@ +using System.Net.WebSockets; + +namespace MauMau_Server.Mau; + +public class Game +{ + public readonly Deck Deck = new(); + public Card CurrentCard; + public List Players = new(); + public Player CurrentPlayer; + public int TurnDirection = 1; + + public Game() + { + CurrentCard = Deck.DrawCard(); + Deck.AddCardToUsedDeck(CurrentCard); + } + + public void AddPlayerToGame(string playerId, WebSocket socket) + { + var player = new Player("Koetje " + playerId.Split('-')[0], playerId, socket) + { + Hand = Deck.DrawCards(8) + }; + Players.Add(player); + if (Players.Count == 1) CurrentPlayer = player; + } + + public void RemovePlayer(string playerId) + { + var player = GetPlayer(playerId); + Players.Remove(player); + } + + public void PlayCard(string playerId, Card card) + { + var player = GetPlayer(playerId); + if (CurrentPlayer != player) return; + var hand = player.Hand; + if (!IsCardInHand(hand, card) || !IsCardPlayable(CurrentCard, card)) return; + Deck.AddCardToUsedDeck(card); + hand.Remove(GetSameCardFromHand(hand, card)); + CurrentCard = card; + CurrentPlayer = GetNextPlayer(); + } + + public Player GetNextPlayer() + { + var index = Players.IndexOf(CurrentPlayer); + 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 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); + } + + private static bool IsCardInHand(IEnumerable 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; + } +} \ No newline at end of file diff --git a/Mau/GameState.cs b/Mau/GameState.cs new file mode 100644 index 0000000..2218875 --- /dev/null +++ b/Mau/GameState.cs @@ -0,0 +1,28 @@ +namespace MauMau_Server.Mau; + +public class GameState +{ + public string PlayerName { get; set; } + public List Hand { get; set; } = new(); + public string CurrentCard { get; set; } + public string CurrentPlayer { get; set; } + public List Players { get; set; } = new(); + + public GameState(Game game, string playerId) + { + var p = game.GetPlayer(playerId); + PlayerName = p.Name; + foreach (var card in p.Hand) + { + Hand.Add(card.ToString()); + } + + foreach (var player in game.Players) + { + Players.Add(player.Name); + } + + CurrentCard = game.CurrentCard.ToString(); + CurrentPlayer = game.CurrentPlayer.Name; + } +} \ No newline at end of file diff --git a/Mau/Player.cs b/Mau/Player.cs new file mode 100644 index 0000000..0a91b20 --- /dev/null +++ b/Mau/Player.cs @@ -0,0 +1,20 @@ +using System.Net.WebSockets; + +namespace MauMau_Server.Mau; + +public class Player +{ + public string Name { get; set; } + public string PlayerId { get; set; } + public WebSocket Socket { get; set; } + public List Hand { get; set; } = new(); + + public Player(string name, string playerId, WebSocket socket) + { + Name = name; + PlayerId = playerId; + Socket = socket; + } + + public bool IsMe(string playerId) => PlayerId == playerId; +} \ No newline at end of file diff --git a/Websockets/Room.cs b/Websockets/Room.cs index c511148..b3003fd 100644 --- a/Websockets/Room.cs +++ b/Websockets/Room.cs @@ -1,11 +1,14 @@ using System.Net.WebSockets; using System.Text; +using System.Text.Json; +using MauMau_Server.Mau; namespace MauMau_Server.Websockets; public class Room { private readonly Dictionary _connections = new(); + private readonly Game _game = new(); public async Task InstantiateConnection(WebSocket socket) { @@ -15,23 +18,28 @@ public class Room private async Task HandleConnection(WebSocket socket, string socketId) { + BroadcastGameState(); var buffer = EmptyBuffer(); var result = await ReceiveAsync(socket, buffer); while (!result.CloseStatus.HasValue) { - var message = $"{socketId}: {Encoding.Default.GetString(buffer)}"; - BroadcastAsync(message); + var slicedBuffer = buffer[0..result.Count]; + var playedCard = JsonSerializer.Deserialize(slicedBuffer).ToCard(); + _game.PlayCard(socketId, playedCard); + BroadcastGameState(); buffer = EmptyBuffer(); result = await ReceiveAsync(socket, buffer); } await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); RemoveConnection(socketId); + _game.RemovePlayer(socketId); } private string AddConnection(WebSocket socket) { var socketId = Guid.NewGuid().ToString(); _connections.Add(socketId, socket); + _game.AddPlayerToGame(socketId, socket); return socketId; } @@ -57,14 +65,29 @@ public class Room return result; } + private void BroadcastGameState() + { + foreach (var (id, socket) in GetAllConnections()) + { + var gameState = new GameState(_game, id); + var message = JsonSerializer.Serialize(gameState); + SendAsync(socket, message); + } + } + private void BroadcastAsync(string message) + { + foreach (var (id, socket) in GetAllConnections()) + { + SendAsync(socket, message); + } + } + + private void SendAsync(WebSocket socket, string message) { var bytes = Encoding.Default.GetBytes(message); var arraySegment = new ArraySegment(bytes); - foreach (var (id, socket) in GetAllConnections()) - { - socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); - } + socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); } private static byte[] EmptyBuffer() diff --git a/Websockets/RoomManager.cs b/Websockets/RoomManager.cs index 5fb699a..280a9c7 100644 --- a/Websockets/RoomManager.cs +++ b/Websockets/RoomManager.cs @@ -31,6 +31,11 @@ public class RoomManager : IRoomManager { return Rooms.ContainsKey(roomId); } + + public void RemoveAllRooms() + { + Rooms.Clear(); + } } public interface IRoomManager @@ -40,4 +45,5 @@ public interface IRoomManager public List GetAllRooms(); public void RemoveRoom(string roomId); public bool RoomExists(string roomId); + public void RemoveAllRooms(); } \ No newline at end of file diff --git a/mau.json b/mau.json new file mode 100644 index 0000000..60c5d52 --- /dev/null +++ b/mau.json @@ -0,0 +1 @@ +{"CardType":"SPADES","CardValue":"THREE"} \ No newline at end of file