Card as enum and packs with bit sets

This commit is contained in:
Sasha 2018-02-05 18:14:11 +01:00
parent e7619af640
commit 6dbd72cf36
3 changed files with 162 additions and 175 deletions

View file

@ -1,72 +0,0 @@
module CCard where
import Data.Word (Word64)
import Data.Bits (clearBit, testBit, setBit, Bits)
import System.Random (randomRIO)
data Flower =
Pine
| Plum
| Cherry
| Wisteria
| Iris
| Peony
| BushClover
| SusukiGrass
| Chrysanthemum
| Maple
| Willow
| Paulownia
deriving (Eq, Ord, Enum, Show)
data Card =
Pine0 | Pine1 | PinePoetry | Crane
| Plum0 | Plum1 | PlumPoetry | BushWarbler
| Cherry0 | Cherry1 | CherryPoetry | CampCurtain
| Wisteria0 | Wisteria1 | WisteriaRed | Cuckoo
| Iris0 | Iris1 | IrisRed | EightPlankBridge
| Peony0 | Peony1 | PeonyBlue | Butterflies
| BushClover0 | BushClover1 | BushCloverRed | Boar
| SusukiGrass0 | SusukiGrass1 | Geese | FullMoon
| Chrysanthemum0 | Chrysanthemum1 | ChrysanthemumBlue | SakeCup
| Maple0 | Maple1 | MapleBlue | Deer
| Lightning | WillowRed | Swallow | RainMan
| Paulownia0 | Paulownia1 | Sand | Phoenix
deriving (Eq, Ord, Enum, Show)
flower :: Card -> Flower
flower = toEnum . (`div` 4) . fromEnum
type Pack = Word64
packOfCards :: [Card] -> Pack
packOfCards = foldl setBit 0 . map fromEnum
port :: (Bits a, Enum e) => (a -> Int -> b) -> a -> e -> b
port f bits = f bits . fromEnum
contains :: Pack -> Card -> Bool
contains = port testBit
add :: Pack -> Card -> Pack
add = port setBit
remove :: Pack -> Card -> Pack
remove = port clearBit
pair :: Card -> Card -> Bool
pair card1 card2 = flower card1 == flower card2
cards :: [Card]
cards = [Pine0 .. Phoenix]
shuffle :: [a] -> IO [a]
shuffle l =
aux (length l) l
where
aux n [] = return []
aux n (h:t) = do
cut <- randomRIO (0, n-1)
shuffled <- shuffle t
let (top, bottom) = splitAt cut shuffled
return $ top ++ h : bottom

164
Card.hs
View file

@ -1,5 +1,20 @@
module Card where module Card where
import Data.Word (Word64)
import Data.Bits (
clearBit
, popCount
, setBit
, shift
, testBit
, xor
, Bits
, (.&.)
, (.|.)
, countTrailingZeros
)
import System.Random (randomRIO)
data Flower = data Flower =
Pine Pine
| Plum | Plum
@ -13,106 +28,85 @@ data Flower =
| Maple | Maple
| Willow | Willow
| Paulownia | Paulownia
deriving (Eq) deriving (Eq, Ord, Enum, Show)
data Ribbon = data Card =
Red Pine0 | Pine1 | PinePoetry | Crane
| Blue | Plum0 | Plum1 | PlumPoetry | BushWarbler
| Poetry | Cherry0 | Cherry1 | CherryPoetry | CampCurtain
| Wisteria0 | Wisteria1 | WisteriaRed | Cuckoo
| Iris0 | Iris1 | IrisRed | EightPlankBridge
| Peony0 | Peony1 | PeonyBlue | Butterflies
| BushClover0 | BushClover1 | BushCloverRed | Boar
| SusukiGrass0 | SusukiGrass1 | Geese | FullMoon
| Chrysanthemum0 | Chrysanthemum1 | ChrysanthemumBlue | SakeCup
| Maple0 | Maple1 | MapleBlue | Deer
| Lightning | WillowRed | Swallow | RainMan
| Paulownia0 | Paulownia1 | Sand | Phoenix
deriving (Eq, Ord, Enum, Show)
data Animal = flower :: Card -> Flower
BushWarbler flower = toEnum . (`div` 4) . fromEnum
| Cuckoo
| EightPlankBridge
| Butterflies
| Boar
| Geese
| SakeCup
| Deer
| Swallow
data Light = type Pack = Word64
Crane
| CampCurtain
| FullMoon
| RainMan
| ChinesePhoenix
data Value = empty :: Pack
Plain empty = 0
| Sand
| Lightning
| Ribbon Ribbon
| Animal Animal
| Light Light
data Card = Card { packOfCards :: [Card] -> Pack
flower::Flower packOfCards = foldl setBit 0 . map fromEnum
, value::Value
}
deck :: [ Card ] smallest :: Pack -> Card
deck = [ smallest = toEnum . countTrailingZeros
Card Pine Plain
, Card Pine Plain
, Card Pine (Ribbon Poetry)
, Card Pine (Light Crane)
, Card Plum Plain cardsOfPack :: Pack -> [Card]
, Card Plum Plain cardsOfPack 0 = []
, Card Plum (Ribbon Poetry) cardsOfPack pack =
, Card Plum (Animal BushWarbler) let n = countTrailingZeros pack in
toEnum n : cardsOfPack (clearBit pack n)
, Card Cherry Plain port :: (Bits a, Enum e) => (a -> Int -> b) -> a -> e -> b
, Card Cherry Plain port f bits = f bits . fromEnum
, Card Cherry (Ribbon Poetry)
, Card Cherry (Light CampCurtain)
, Card Wisteria Plain contains :: Pack -> Card -> Bool
, Card Wisteria Plain contains = port testBit
, Card Wisteria (Ribbon Red)
, Card Wisteria (Animal Cuckoo)
, Card Iris Plain add :: Pack -> Card -> Pack
, Card Iris Plain add = port setBit
, Card Iris (Ribbon Red)
, Card Iris (Animal EightPlankBridge)
, Card Peony Plain remove :: Pack -> Card -> Pack
, Card Peony Plain remove = port clearBit
, Card Peony (Ribbon Blue)
, Card Peony (Animal Butterflies)
, Card BushClover Plain union :: Pack -> Pack -> Pack
, Card BushClover Plain union = (.|.)
, Card BushClover (Ribbon Red)
, Card BushClover (Animal Boar)
, Card SusukiGrass Plain intersection :: Pack -> Pack -> Pack
, Card SusukiGrass Plain intersection = (.&.)
, Card SusukiGrass (Animal Geese)
, Card SusukiGrass (Light FullMoon)
, Card Chrysanthemum Plain difference :: Pack -> Pack -> Pack
, Card Chrysanthemum Plain difference a b = a `xor` (a .&. b)
, Card Chrysanthemum (Ribbon Blue)
, Card Chrysanthemum (Animal SakeCup)
, Card Maple Plain sameMonth :: Card -> Pack
, Card Maple Plain sameMonth card = 0xf `shift` (fromEnum card .&. 0xfc)
, Card Maple (Ribbon Blue)
, Card Maple (Animal Deer)
, Card Willow Lightning cards :: [Card]
, Card Willow (Ribbon Red) cards = [Pine0 .. Phoenix]
, Card Willow (Animal Swallow)
, Card Willow (Light RainMan)
, Card Paulownia Plain shuffle :: [a] -> IO [a]
, Card Paulownia Plain shuffle l =
, Card Paulownia Sand aux (length l) l
, Card Paulownia (Light ChinesePhoenix) where
] aux n [] = return []
aux n (h:t) = do
cut <- randomRIO (0, n-1)
shuffled <- shuffle t
let (top, bottom) = splitAt cut shuffled
return $ top ++ h : bottom
pair :: Card -> Card -> Bool pair :: Card -> Pack -> Maybe (Pack, Pack)
pair card1 card2 = flower card1 == flower card2 pair card pack =
let sameMonthCards = sameMonth card `intersection` pack in
case popCount sameMonthCards of
0 -> Just (add pack card, empty)
1 -> Just (difference pack sameMonthCards, add sameMonthCards card)
_ -> Nothing

101
KoiKoi.hs
View file

@ -2,8 +2,8 @@
module KoiKoi where module KoiKoi where
import CCard import CCard
import Data.Bits (popCount, (.|.), (.&.), shift, xor) import Data.Bits (popCount, (.|.), (.&.))
import Data.Map (Map, empty, insert, unionWith, (!)) import Data.Map (Map, adjust, empty, fromList, insert, unionWith, (!))
data Yaku = data Yaku =
Goko Goko
@ -28,21 +28,28 @@ data Player = Player {
hand :: Pack hand :: Pack
, captured :: Pack , captured :: Pack
, scored :: Score , scored :: Score
} } deriving (Show)
data Turn = Player1 | Player2 data Turn = Player1 | Player2 deriving (Eq, Ord, Show)
data Step = PlayACard | ChooseWhichCard | Scored
switch :: Turn -> Turn
switch Player1 = Player2
switch _ = Player1
data Step = PlayACard | ChooseWhichCard Card | Scored deriving (Show)
data Game = Game { data Game = Game {
players :: (Player, Player) players :: Map Turn Player
, river :: Pack , river :: Pack
, deck :: [ Card ] , deck :: [ Card ]
, player :: Turn , turn :: Turn
, month :: Flower , month :: Flower
, step :: Step , step :: Step
} } | Over {
winner :: Turn
} deriving (Show)
data Move = Take (Turn, Card, Card) | Choose (Turn, Card) | KoiKoi Bool data Move = Drop Card | Take (Card, Card) | Choose Card | KoiKoi Bool
fixed :: (Yaku, Points) -> Pack -> YakuFinder fixed :: (Yaku, Points) -> Pack -> YakuFinder
fixed points indicatorSet pack = if pack == indicatorSet then Just points else Nothing fixed points indicatorSet pack = if pack == indicatorSet then Just points else Nothing
@ -99,11 +106,12 @@ yakus = foldl (\map -> unionWith (++) map . index) empty [
, ([SakeCup, CampCurtain], fixed (HanamiZake, 5)) , ([SakeCup, CampCurtain], fixed (HanamiZake, 5))
] ]
capture :: Game -> Card -> Pack -> (Pack, Score) capture :: Flower -> Pack -> [Card] -> (Score, Pack)
capture (Game {month}) card pack = capture month pack cards =
let newPack = add pack card in let newPack = foldl add pack cards in
let yakuFinders = (unionWith (++) yakus . index $ tsukiFuda month) ! card in let monthYakus = (unionWith (++) yakus . index $ tsukiFuda month) in
(newPack, foldl (\score -> foldr (uncurry insert) score . ($newPack)) empty yakuFinders) let yakuFinders = foldl (\finders card -> monthYakus ! card ++ finders) [] cards in
(foldl (\score -> foldr (uncurry insert) score . ($newPack)) empty yakuFinders, newPack)
foldApply :: [[a] -> ([a], [a])] -> [[a]] -> [[a]] foldApply :: [[a] -> ([a], [a])] -> [[a]] -> [[a]]
foldApply [] init = init foldApply [] init = init
@ -114,13 +122,70 @@ deal :: IO Game
deal = do deal = do
shuffled <- shuffle cards shuffled <- shuffle cards
let [hand1,hand2,river,deck] = foldApply (take 3 . repeat $ splitAt 8) [shuffled] let [hand1,hand2,river,deck] = foldApply (take 3 . repeat $ splitAt 8) [shuffled]
let p1 = Player {hand = packOfCards hand1, captured = packOfCards [], scored = empty} let players = fromList [
let p2 = Player {hand = packOfCards hand2, captured = packOfCards [], scored = empty} (Player1, Player {hand = packOfCards hand1, captured = packOfCards [], scored = empty})
, (Player2, Player {hand = packOfCards hand2, captured = packOfCards [], scored = empty})
]
return $ Game { return $ Game {
players = (p1, p2) players
, river = packOfCards river , river = packOfCards river
, deck , deck
, player = Player1 , turn = Player1
, month = Pine , month = Pine
, step = PlayACard , step = PlayACard
} }
makeSure :: Bool -> String -> Either String ()
makeSure check message = if check then return () else fail message
{-
playACard :: Game -> Card -> Card -> Either String Game
playACard (Game {players, river, month}) c1 c2 = do
makeSure (hand `contains` c1) "You don't have that card"
capture
-}
turnOver :: Game -> Either String Game
turnOver game@(Game { players, river, deck, turn, month, step }) =
case deck of
[] -> fail "Deck got empty"
next : cards ->
let game = game { deck = cards } in
let canTake = sameMonth next .&. river in
Right $ case popCount canTake of
0 -> game { river = add river next }
1 ->
let current@(Player { captured, scored }) = players ! turn in
let (score, newPack) = capture month captured (next:cardsOfPack canTake) in
let game = game { players = insert turn (current { captured = newPack, scored = unionWith max scored score }) players } in
if null score
then game { step = PlayACard, turn = switch turn }
else game { step = Scored }
_ -> game { step = ChooseWhichCard next }
{-
play :: Game -> Move -> Either String Game
play game@(Game { players, river, deck, turn, month, step }) = playFrom step
where
playFrom PlayACard (Drop card) = do
makeSure (month card .&. river == 0) "This card takes another one in the river"
return $ game {
player =
river = add card river
}
playFrom PlayACard (Take (card1, card2)) = do
makeSure (card1 `pair` card2) "Cards aren't from the same month"
makeSure (river `contains` card2) "That card isn't in the river"
let player = case turn of
Player1 -> fst players
_ -> snd players
playACard player c1 c2
playFrom (ChooseWhichCard card1) (Choose card2) =
makeSure (card1 `pair` card2) "Cards aren't from the same month"
makeSure (river `contains` card2) "That card isn't in the river"
playFrom Scored (KoiKoi yes) =
playFrom _ _ = fail "Invalid move"
-}