-
Notifications
You must be signed in to change notification settings - Fork 0
/
Skull.hs
82 lines (67 loc) · 3.47 KB
/
Skull.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
-- Author: Naitik Mundra (c) 2024
-- This is pretty much all the functions you need on the types to create a fully functional game of Skulls and Roses. The only thing missing really is a gameplay loop that reveals cards and updates wins, but that is not a part of the "backend" functionality.
module Main where
import Data.Data
import GHC.TypeLits
-- | Type for cards, which can either be Skull or Rose.
data Card = Skull | Rose
deriving (Show, Eq)
-- | Type for player with a unique ID and their hand.
data Player = Player {playerId :: Int, hand :: [Card], wins :: Int}
deriving (Show, Eq)
-- | Type for cards on the table, associated with a player.
data TableCard = TableCard {ownerId :: Int, card :: Card}
deriving (Show, Eq)
-- | A game state parameterised by the number of players.
data GameState (n :: Nat) where
GameState :: {players :: [Player], table :: [TableCard]} -> GameState n
-- | Create a player with a given ID and an empty hand.
createPlayer :: Int -> Player
createPlayer pid = Player{playerId = pid, hand = [Rose, Rose, Skull], wins = 0}
-- | Create a new game state with a specific number of players.
createGameState :: forall n. (KnownNat n) => Proxy n -> GameState n
createGameState _ =
GameState
{ players =
map
createPlayer
[1 .. fromIntegral (natVal (Proxy :: Proxy n))]
, table = []
}
-- | Function to place one card from each player onto the table.
placeCards :: [Card] -> GameState n -> GameState n
placeCards cards (GameState players table) =
GameState
{ players = zipWith removeCardFromPlayer players cards
, table = table ++ zipWith createTableCard players cards
}
where
removeCardFromPlayer :: Player -> Card -> Player
removeCardFromPlayer player card = player{hand = removeCard card (hand player)}
createTableCard :: Player -> Card -> TableCard
createTableCard player card = TableCard{ownerId = playerId player, card = card}
-- | Helper function to remove a card from a player's hand.
removeCard :: Card -> [Card] -> [Card]
removeCard card hand = case span (/= card) hand of
(before, _ : after) -> before ++ after
(before, []) -> before -- If the card isn't found, return the hand unchanged.
-- Making a bid is a game play thng, but we need a helper function to make sure it is done in order, and that the maximum number of cards requirement is met. So, the bid has the properties: the player who made it must have kept that many cards on the table and it must be strictly higher than the last bid. Since this returns only the new bid, we must make sure that gameplay loop handles showing error condition to players if new bid is not higher than last bid.
validBid :: Int -> [TableCard] -> Player -> Bool
validBid lastBid table player = lastBid + 1 <= countOwnerId table (playerId player)
makeBid :: Int -> [TableCard] -> Player -> Int
makeBid lastBid table player = lastBid + fromEnum (validBid lastBid table player)
countOwnerId :: [TableCard] -> Int -> Int
countOwnerId table playerId = length (filter (\tc -> ownerId tc == playerId) table)
-- | Main module to demonstrate the game state updates.
main :: IO ()
main = do
let game = createGameState (Proxy :: Proxy 3)
putStrLn "Game state start:"
print (players game)
let updatedGame = placeCards [Rose, Skull, Rose] game
putStrLn "Game state after placing one card each:"
print (players updatedGame)
putStrLn "Cards on the table:"
print (table updatedGame)
print $ countOwnerId (table updatedGame) 1
print $ makeBid 1 (table updatedGame) (players game !! 1)