2018-02-03 18:23:22 +01:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
2018-01-30 17:34:28 +01:00
|
|
|
module KoiKoi where
|
|
|
|
|
|
|
|
import CCard
|
2018-02-03 18:23:22 +01:00
|
|
|
import Data.Bits (popCount, (.|.), (.&.), shift, xor)
|
|
|
|
import Data.Map (Map, empty, insert, unionWith, (!))
|
2018-02-01 12:24:07 +01:00
|
|
|
|
2018-02-02 17:44:35 +01:00
|
|
|
data Yaku =
|
|
|
|
Goko
|
|
|
|
| Shiko
|
|
|
|
| AmeShiko
|
|
|
|
| Sanko
|
|
|
|
| InoShikaCho
|
|
|
|
| Tane
|
|
|
|
| Akatan
|
|
|
|
| Aotan
|
|
|
|
| Tan
|
|
|
|
| Kasu
|
|
|
|
| TsukimiZake
|
|
|
|
| HanamiZake
|
|
|
|
| TsukiFuda
|
2018-02-03 18:23:22 +01:00
|
|
|
deriving (Eq, Ord, Show)
|
2018-02-01 12:24:07 +01:00
|
|
|
type Points = Int
|
2018-02-03 18:23:22 +01:00
|
|
|
type YakuFinder = Pack -> Maybe (Yaku, Points)
|
|
|
|
type Score = Map Yaku Points
|
2018-01-30 17:34:28 +01:00
|
|
|
|
|
|
|
data Player = Player {
|
|
|
|
hand :: Pack
|
|
|
|
, captured :: Pack
|
2018-02-03 18:23:22 +01:00
|
|
|
, scored :: Score
|
2018-01-30 17:34:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
data State = State {
|
|
|
|
players :: (Player, Player)
|
|
|
|
, river :: Pack
|
|
|
|
, deck :: [ Card ]
|
2018-02-01 12:24:07 +01:00
|
|
|
, month :: Flower
|
2018-01-30 17:34:28 +01:00
|
|
|
, turn :: Bool
|
|
|
|
}
|
2018-02-01 12:24:07 +01:00
|
|
|
|
2018-02-03 18:23:22 +01:00
|
|
|
fixed :: (Yaku, Points) -> Pack -> YakuFinder
|
|
|
|
fixed points indicatorSet pack = if pack == indicatorSet then Just points else Nothing
|
2018-02-01 12:24:07 +01:00
|
|
|
|
2018-02-03 18:23:22 +01:00
|
|
|
moreThan :: Int -> Yaku -> (Pack -> YakuFinder)
|
|
|
|
moreThan count yaku _ =
|
|
|
|
(\n -> if n > 0 then Just (yaku, n) else Nothing) . ($count) . (-) . popCount
|
2018-02-02 17:44:35 +01:00
|
|
|
|
2018-02-03 18:23:22 +01:00
|
|
|
lights :: [Card]
|
|
|
|
lights = [Crane, CampCurtain, FullMoon, RainMan, Phoenix]
|
|
|
|
|
|
|
|
hikari :: Pack -> YakuFinder
|
|
|
|
hikari _ pack = rate (popCount pack) (pack `contains` RainMan)
|
2018-02-02 17:44:35 +01:00
|
|
|
where
|
|
|
|
rate 5 _ = Just (Goko, 10)
|
|
|
|
rate 4 hasRainMan = if hasRainMan then Just (AmeShiko, 7) else Just (Shiko, 8)
|
|
|
|
rate n hasRainMan = if not hasRainMan && n > 2 then Just (Sanko, 5) else Nothing
|
|
|
|
|
2018-02-03 18:23:22 +01:00
|
|
|
tsukiFuda :: Flower -> ([Card], Pack -> YakuFinder)
|
|
|
|
tsukiFuda flower = (map toEnum $ map (fromEnum flower * 4 +) [0..3], fixed (TsukiFuda, 8))
|
|
|
|
|
|
|
|
index :: ([Card], Pack -> YakuFinder) -> Map Card [YakuFinder]
|
|
|
|
index (cards, scorer) =
|
|
|
|
let pack = packOfCards cards in
|
|
|
|
foldl (\map card -> insert card [scorer pack . (.&.) pack] map) empty cards
|
|
|
|
|
|
|
|
inoshikacho :: [Card]
|
|
|
|
inoshikacho = [Butterflies, Boar, Deer]
|
|
|
|
|
|
|
|
animals :: [Card]
|
|
|
|
animals = [BushWarbler, Cuckoo, EightPlankBridge, Geese, SakeCup, Swallow] ++ inoshikacho
|
|
|
|
|
|
|
|
blue :: [Card]
|
|
|
|
blue = [PeonyBlue, ChrysanthemumBlue, MapleBlue]
|
|
|
|
|
|
|
|
poetry :: [Card]
|
|
|
|
poetry = [PinePoetry, PlumPoetry, CherryPoetry]
|
|
|
|
|
|
|
|
ribbons = [WisteriaRed, IrisRed, BushCloverRed, WillowRed] ++ blue ++ poetry
|
2018-02-02 17:44:35 +01:00
|
|
|
|
2018-02-03 18:23:22 +01:00
|
|
|
plain :: [Card]
|
|
|
|
plain = (foldl (++) [] [map toEnum [4*i, 4*i+1] | i <- [0..10]]) ++ Lightning : [Paulownia0 .. Sand]
|
2018-02-02 17:44:35 +01:00
|
|
|
|
2018-02-03 18:23:22 +01:00
|
|
|
yakus :: Map Card [YakuFinder]
|
2018-02-02 17:44:35 +01:00
|
|
|
yakus = foldl (\map -> unionWith (++) map . index) empty [
|
2018-02-03 18:23:22 +01:00
|
|
|
(lights, hikari)
|
|
|
|
, (inoshikacho, fixed (InoShikaCho, 5))
|
|
|
|
, (animals, moreThan 4 Tane)
|
|
|
|
, (poetry, fixed (Akatan, 5))
|
|
|
|
, (blue, fixed (Aotan, 5))
|
|
|
|
, (ribbons, moreThan 4 Tan)
|
|
|
|
, (plain, moreThan 9 Kasu)
|
|
|
|
, ([SakeCup, FullMoon], fixed (TsukimiZake, 5))
|
|
|
|
, ([SakeCup, CampCurtain], fixed (HanamiZake, 5))
|
2018-02-01 12:24:07 +01:00
|
|
|
]
|
2018-02-03 18:25:11 +01:00
|
|
|
|
|
|
|
capture :: State -> Card -> Pack -> (Pack, Score)
|
|
|
|
capture (State {month}) card pack =
|
|
|
|
let newPack = add pack card in
|
|
|
|
let yakuFinders = (unionWith (++) yakus . index $ tsukiFuda month) ! card in
|
|
|
|
(newPack, foldl (\score -> foldr (uncurry insert) score . ($newPack)) empty yakuFinders)
|
2018-02-04 22:06:07 +01:00
|
|
|
|
|
|
|
foldApply :: [[a] -> ([a], [a])] -> [[a]] -> [[a]]
|
|
|
|
foldApply [] init = init
|
|
|
|
foldApply (f:fs) [x] =
|
|
|
|
let (a,b) = f x in a : foldApply fs [b]
|
|
|
|
|
|
|
|
deal :: IO State
|
|
|
|
deal = do
|
|
|
|
shuffled <- shuffle cards
|
|
|
|
let [hand1,hand2,river,deck] = foldApply (take 3 . repeat $ splitAt 8) [shuffled]
|
|
|
|
let p1 = Player {hand = packOfCards hand1, captured = packOfCards [], scored = empty}
|
|
|
|
let p2 = Player {hand = packOfCards hand2, captured = packOfCards [], scored = empty}
|
|
|
|
return $ State {
|
|
|
|
players = (p1, p2)
|
|
|
|
, river = packOfCards river
|
|
|
|
, deck
|
|
|
|
, month = Pine
|
|
|
|
, turn = False
|
|
|
|
}
|