Purify types into newtypes to ease writing instances
This commit is contained in:
parent
dfccff2915
commit
95b2132a10
4 changed files with 62 additions and 50 deletions
|
@ -51,49 +51,52 @@ flower = toEnum . (`div` 4) . fromEnum
|
||||||
|
|
||||||
type Monthly a = Reader Flower a
|
type Monthly a = Reader Flower a
|
||||||
|
|
||||||
type Pack = Word64
|
newtype Pack = Pack { unpack :: Word64 } deriving (Eq)
|
||||||
|
|
||||||
empty :: Pack
|
empty :: Pack
|
||||||
empty = 0
|
empty = Pack 0
|
||||||
|
|
||||||
packOfCards :: [Card] -> Pack
|
packOfCards :: [Card] -> Pack
|
||||||
packOfCards = foldl add 0
|
packOfCards = foldl add empty
|
||||||
|
|
||||||
smallest :: Pack -> Card
|
smallest :: Pack -> Card
|
||||||
smallest = toEnum . countTrailingZeros
|
smallest = toEnum . countTrailingZeros . unpack
|
||||||
|
|
||||||
cardsOfPack :: Pack -> [Card]
|
cardsOfPack :: Pack -> [Card]
|
||||||
cardsOfPack 0 = []
|
cardsOfPack (Pack 0) = []
|
||||||
cardsOfPack pack =
|
cardsOfPack p =
|
||||||
let n = countTrailingZeros pack in
|
let c = smallest p in
|
||||||
toEnum n : cardsOfPack (clearBit pack n)
|
c : cardsOfPack (remove p c)
|
||||||
|
|
||||||
port :: (Bits a, Enum e) => (a -> Int -> b) -> a -> e -> b
|
portEnum :: Enum e => (Word64 -> Int -> b) -> Pack -> e -> b
|
||||||
port f bits = f bits . fromEnum
|
portEnum f (Pack p) = f p . fromEnum
|
||||||
|
|
||||||
contains :: Pack -> Card -> Bool
|
contains :: Pack -> Card -> Bool
|
||||||
contains = port testBit
|
contains = portEnum testBit
|
||||||
|
|
||||||
size :: Pack -> Int
|
size :: Pack -> Int
|
||||||
size = popCount
|
size (Pack p) = popCount p
|
||||||
|
|
||||||
add :: Pack -> Card -> Pack
|
add :: Pack -> Card -> Pack
|
||||||
add = port setBit
|
add p = Pack . portEnum setBit p
|
||||||
|
|
||||||
remove :: Pack -> Card -> Pack
|
remove :: Pack -> Card -> Pack
|
||||||
remove = port clearBit
|
remove p = Pack . portEnum clearBit p
|
||||||
|
|
||||||
|
portBinary :: (Word64 -> Word64 -> Word64) -> Pack -> Pack -> Pack
|
||||||
|
portBinary operator (Pack a) (Pack b) = Pack $ operator a b
|
||||||
|
|
||||||
union :: Pack -> Pack -> Pack
|
union :: Pack -> Pack -> Pack
|
||||||
union = (.|.)
|
union = portBinary (.|.)
|
||||||
|
|
||||||
intersection :: Pack -> Pack -> Pack
|
intersection :: Pack -> Pack -> Pack
|
||||||
intersection = (.&.)
|
intersection = portBinary (.&.)
|
||||||
|
|
||||||
difference :: Pack -> Pack -> Pack
|
difference :: Pack -> Pack -> Pack
|
||||||
difference a b = a `xor` (a .&. b)
|
difference = portBinary (\a b -> a `xor` (a .&. b))
|
||||||
|
|
||||||
sameMonth :: Card -> Pack
|
sameMonth :: Card -> Pack
|
||||||
sameMonth card = 0xf `shift` (fromEnum card .&. 0xfc)
|
sameMonth card = Pack $ 0xf `shift` (fromEnum card .&. 0xfc)
|
||||||
|
|
||||||
cards :: [Card]
|
cards :: [Card]
|
||||||
cards = [Pine0 .. Phoenix]
|
cards = [Pine0 .. Phoenix]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
module Hanafuda.Game where
|
module Hanafuda.Game where
|
||||||
|
|
||||||
import Data.Map (Map, empty, fromList)
|
import Data.Map (Map, empty, fromList, (!))
|
||||||
import Hanafuda.Card (Card, Pack, packOfCards)
|
import Hanafuda.Card (Card, Pack, packOfCards)
|
||||||
import Hanafuda.Yaku (Score, Points)
|
import Hanafuda.Yaku (Score, Points)
|
||||||
|
|
||||||
|
@ -21,9 +21,15 @@ data PlayerState = PlayerState {
|
||||||
}
|
}
|
||||||
type Players = Map Player PlayerState
|
type Players = Map Player PlayerState
|
||||||
|
|
||||||
initPlayers :: [Card] -> [Card] -> Players
|
players :: [Player]
|
||||||
initPlayers hand1 hand2 =
|
players = [Player1, Player2]
|
||||||
fromList [(Player1, player hand1), (Player2, player hand2)]
|
|
||||||
|
deal :: [a] -> Map Player a
|
||||||
|
deal = fromList . zip players
|
||||||
|
|
||||||
|
initPlayers :: [[Card]] -> Players
|
||||||
|
initPlayers =
|
||||||
|
deal . map player
|
||||||
where
|
where
|
||||||
player cards = PlayerState {
|
player cards = PlayerState {
|
||||||
hand = packOfCards cards
|
hand = packOfCards cards
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE TypeSynonymInstances #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
module Hanafuda.Month where
|
module Hanafuda.Month where
|
||||||
|
|
||||||
import Hanafuda.Card (Card, Flower(Pine), cards, shuffle, packOfCards)
|
import Hanafuda.Card (Card, Flower(Pine), cards, shuffle, packOfCards)
|
||||||
|
@ -26,7 +24,10 @@ data Over = Over {
|
||||||
, score :: Int
|
, score :: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Month = Either Over On
|
newtype Month = Month (Either Over On)
|
||||||
|
|
||||||
|
go :: On -> Month
|
||||||
|
go = Month . Right
|
||||||
|
|
||||||
new :: Player -> [Card] -> On
|
new :: Player -> [Card] -> On
|
||||||
new playing shuffled =
|
new playing shuffled =
|
||||||
|
@ -42,25 +43,25 @@ new playing shuffled =
|
||||||
where
|
where
|
||||||
take8 = state $ splitAt 8
|
take8 = state $ splitAt 8
|
||||||
([hand1, hand2, river], next:stock) = runState (replicateM 3 take8) shuffled
|
([hand1, hand2, river], next:stock) = runState (replicateM 3 take8) shuffled
|
||||||
players = initPlayers hand1 hand2
|
players = initPlayers [hand1, hand2]
|
||||||
|
|
||||||
next :: On -> IO On
|
next :: On -> IO On
|
||||||
next month@(On {flower, oyake}) = do
|
next (On {flower, oyake}) = do
|
||||||
shuffled <- shuffle cards
|
shuffled <- shuffle cards
|
||||||
return $ (new (Game.next oyake) cards) {flower = succ flower}
|
return $ (new (Game.next oyake) shuffled) {flower = succ flower}
|
||||||
|
|
||||||
instance Game On Month where
|
instance Game On Month where
|
||||||
play month@(On {flower, day, playing, players, stock = next : moreStock}) move =
|
play on@(On {flower, day, playing, players, stock = next : moreStock}) move =
|
||||||
fmap after $ play day move
|
fmap after $ play day move
|
||||||
where
|
where
|
||||||
after (Day {step = Day.Over True, player = PlayerState {yakus}}) =
|
after (Day {step = Day.Over True, player = PlayerState {yakus}}) =
|
||||||
Left $ Over {winner = playing, score = foldl (+) 0 yakus}
|
Month . Left $ Over {winner = playing, score = foldl (+) 0 yakus}
|
||||||
after (Day {step = Day.Over False, player, river}) =
|
after (Day {step = Day.Over False, player, river}) =
|
||||||
let otherPlayer = Game.next playing in
|
let otherPlayer = Game.next playing in
|
||||||
Right $ month {
|
go $ on {
|
||||||
players = insert playing player players
|
players = insert playing player players
|
||||||
, playing = otherPlayer
|
, playing = otherPlayer
|
||||||
, day = (Day.new river (players ! otherPlayer) next) { month = flower }
|
, day = (Day.new river (players ! otherPlayer) next) { month = flower }
|
||||||
, stock = moreStock
|
, stock = moreStock
|
||||||
}
|
}
|
||||||
after newDay = Right $ month {day = newDay}
|
after newDay = go $ on {day = newDay}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE TypeSynonymInstances #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
module Hanafuda.Year where
|
module Hanafuda.Year where
|
||||||
|
|
||||||
import Hanafuda.Card (Flower(Paulownia), cards, shuffle)
|
import Hanafuda.Card (Flower(Paulownia), cards, shuffle)
|
||||||
import Hanafuda.Month (flower, next, score, winner)
|
import Hanafuda.Month (Month(..), flower, next, score, winner)
|
||||||
import qualified Hanafuda.Month as Month (On(..), Over(..), new)
|
import qualified Hanafuda.Month as Month (On(..), Over(..), new)
|
||||||
import Hanafuda.Game (Game(..), Player(Player1), Scores)
|
import Hanafuda.Game (Game(..), Player(Player1), Scores, deal)
|
||||||
import Data.Map (Map, empty, insert, (!))
|
import Data.Map (Map, insert, (!))
|
||||||
import System.Random (StdGen)
|
import System.Random (StdGen)
|
||||||
|
|
||||||
data Mode = FirstAt Int | WholeYear
|
data Mode = FirstAt Int | WholeYear
|
||||||
|
@ -22,35 +21,38 @@ data Over = Over {
|
||||||
finalScores :: Scores
|
finalScores :: Scores
|
||||||
}
|
}
|
||||||
|
|
||||||
type Year = IO (Either Over On)
|
newtype Year = Year (Either Over On)
|
||||||
|
|
||||||
new :: Mode -> Year
|
go :: On -> IO Year
|
||||||
|
go = return . Year . Right
|
||||||
|
|
||||||
|
new :: Mode -> IO Year
|
||||||
new mode = do
|
new mode = do
|
||||||
shuffled <- shuffle cards
|
shuffled <- shuffle cards
|
||||||
return . Right $ On {
|
go $ On {
|
||||||
mode
|
mode
|
||||||
, month = Month.new Player1 shuffled
|
, month = Month.new Player1 shuffled
|
||||||
, scores = empty
|
, scores = deal $ cycle [0]
|
||||||
}
|
}
|
||||||
|
|
||||||
consolidate :: On -> Player -> Int -> Year
|
consolidate :: On -> Player -> Int -> IO Year
|
||||||
consolidate year@(On {mode, month, scores}) winner score =
|
consolidate on@(On {mode, month, scores}) winner score =
|
||||||
case mode of
|
case mode of
|
||||||
FirstAt n | n <= newScore -> over
|
FirstAt n | n <= newScore -> stop
|
||||||
FirstAt n -> continue
|
FirstAt n -> continue
|
||||||
WholeYear | flower month == Paulownia -> over
|
WholeYear | flower month == Paulownia -> stop
|
||||||
WholeYear -> continue
|
WholeYear -> continue
|
||||||
where
|
where
|
||||||
newScore = scores ! winner + score
|
newScore = scores ! winner + score
|
||||||
newScores = insert winner newScore scores
|
newScores = insert winner newScore scores
|
||||||
over = return . Left $ Over {finalScores = newScores}
|
stop = return . Year . Left $ Over {finalScores = newScores}
|
||||||
continue = do
|
continue = do
|
||||||
nextMonth <- next month
|
nextMonth <- next month
|
||||||
return . Right $ year {scores = newScores, month = nextMonth}
|
go $ on {scores = newScores, month = nextMonth}
|
||||||
|
|
||||||
instance Game On Year where
|
instance Game On (IO Year) where
|
||||||
play year@(On {mode, month}) move =
|
play on@(On {mode, month}) move =
|
||||||
fmap after $ play month move
|
fmap after $ play month move
|
||||||
where
|
where
|
||||||
after (Left (Month.Over {winner, score})) = consolidate year winner score
|
after (Month (Left (Month.Over {winner, score}))) = consolidate on winner score
|
||||||
after (Right newMonth) = return . Right $ year {month = newMonth}
|
after (Month (Right newMonth)) = go $ on {month = newMonth}
|
||||||
|
|
Loading…
Reference in a new issue