2018-03-05 16:29:10 +01:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
2018-03-10 23:25:44 +01:00
|
|
|
module Hanafuda.KoiKoi.Yaku where
|
2018-03-05 16:29:10 +01:00
|
|
|
|
2018-03-10 23:25:44 +01:00
|
|
|
import Hanafuda (Card(..), Monthly, Pack, add, contains, intersection, packOfCards, size)
|
2018-05-12 11:17:37 +02:00
|
|
|
import Hanafuda.Player (Points)
|
2018-03-19 12:28:15 +01:00
|
|
|
import qualified Data.Map as M (Map, empty, insert, unionWith, (!))
|
2018-03-07 23:22:45 +01:00
|
|
|
import qualified Data.Set as S (Set, empty, singleton, union)
|
2018-03-05 16:29:10 +01:00
|
|
|
import Control.Monad.Reader (reader)
|
|
|
|
|
|
|
|
data Yaku =
|
|
|
|
Lights
|
|
|
|
| InoShikaCho
|
|
|
|
| Tane
|
|
|
|
| Akatan
|
|
|
|
| Aotan
|
|
|
|
| Tan
|
|
|
|
| Kasu
|
|
|
|
| TsukimiZake
|
|
|
|
| HanamiZake
|
|
|
|
| TsukiFuda
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
type YakuRater = Pack -> Maybe Points
|
|
|
|
type Score = M.Map Yaku Points
|
|
|
|
|
|
|
|
data YakuFinder = YakuFinder {
|
|
|
|
yaku :: Yaku
|
|
|
|
, rater :: YakuRater
|
|
|
|
}
|
|
|
|
|
|
|
|
instance Eq YakuFinder where
|
|
|
|
a == b = yaku a == yaku b
|
|
|
|
|
|
|
|
instance Ord YakuFinder where
|
|
|
|
compare a b = compare (yaku a) (yaku b)
|
|
|
|
|
2018-03-19 12:28:15 +01:00
|
|
|
type YakusByCard = M.Map Card (S.Set YakuFinder)
|
2018-03-05 16:29:10 +01:00
|
|
|
|
|
|
|
lights :: Pack -> YakuRater
|
|
|
|
lights _ pack = rate (size pack) (pack `contains` RainMan)
|
|
|
|
where
|
|
|
|
goko = 10
|
|
|
|
shiko = 8
|
|
|
|
ameshiko = 7
|
|
|
|
sanko = 5
|
|
|
|
rate 5 _ = Just goko
|
|
|
|
rate 4 hasRainMan = if hasRainMan then Just ameshiko else Just shiko
|
|
|
|
rate n hasRainMan = if not hasRainMan && n > 2 then Just sanko else Nothing
|
|
|
|
|
|
|
|
fixed :: Points -> Pack -> YakuRater
|
|
|
|
fixed points indicatorSet pack = if pack == indicatorSet then Just points else Nothing
|
|
|
|
|
|
|
|
moreThan :: Int -> (Pack -> YakuRater)
|
|
|
|
moreThan count _ pack =
|
|
|
|
let n = size pack - count in
|
|
|
|
if n > 0 then Just n else Nothing
|
|
|
|
|
2018-03-19 12:28:15 +01:00
|
|
|
index :: (Yaku, [Card], (Pack -> YakuRater)) -> YakusByCard
|
2018-03-05 16:29:10 +01:00
|
|
|
index (yaku, cards, scorer) =
|
|
|
|
let pack = packOfCards cards in
|
|
|
|
let yakuFinder = YakuFinder {yaku, rater = scorer pack . intersection pack} in
|
2018-03-19 12:28:15 +01:00
|
|
|
foldl (\yakusByCard card -> M.insert card (S.singleton yakuFinder) yakusByCard) M.empty cards
|
2018-03-05 16:29:10 +01:00
|
|
|
|
2018-03-19 12:28:15 +01:00
|
|
|
finders :: Monthly YakusByCard
|
2018-03-05 16:29:10 +01:00
|
|
|
finders = do
|
|
|
|
monthCardPlus <- reader $ (+) . (4*) . fromEnum
|
2018-03-19 12:28:15 +01:00
|
|
|
return $ foldl (\yakusByCard -> M.unionWith S.union yakusByCard . index) M.empty [
|
2018-03-05 16:29:10 +01:00
|
|
|
(Lights, [Crane, CampCurtain, FullMoon, RainMan, Phoenix], lights)
|
|
|
|
, (InoShikaCho, inoshikacho, fixed 5)
|
|
|
|
, (Tane, [BushWarbler, Cuckoo, EightPlankBridge, Geese, SakeCup, Swallow] ++ inoshikacho, moreThan 4)
|
|
|
|
, (Akatan, akatan, fixed 5)
|
|
|
|
, (Aotan, aotan, fixed 5)
|
|
|
|
, (Tan, [WisteriaRed, IrisRed, BushCloverRed, WillowRed] ++ aotan ++ akatan, moreThan 4)
|
2018-03-07 23:22:45 +01:00
|
|
|
, (Kasu, plains, moreThan 9)
|
2018-03-05 16:29:10 +01:00
|
|
|
, (TsukimiZake, [SakeCup, FullMoon], fixed 3)
|
|
|
|
, (HanamiZake, [SakeCup, CampCurtain], fixed 3)
|
|
|
|
, (TsukiFuda, map (toEnum . monthCardPlus) [0..3], fixed 5)
|
|
|
|
]
|
|
|
|
where
|
|
|
|
inoshikacho = [Butterflies, Boar, Deer]
|
|
|
|
aotan = [PeonyBlue, ChrysanthemumBlue, MapleBlue]
|
|
|
|
akatan = [PinePoetry, PlumPoetry, CherryPoetry]
|
2018-03-07 23:22:45 +01:00
|
|
|
plains = (foldl (++) [] [map toEnum [4*i, 4*i+1] | i <- [0..10]]) ++ Lightning : [Paulownia0 .. Sand]
|
2018-03-05 16:29:10 +01:00
|
|
|
|
2018-03-10 23:25:44 +01:00
|
|
|
meldInto :: [Card] -> Pack -> Monthly (Score, Pack)
|
|
|
|
meldInto cards pack = do
|
2018-03-05 16:29:10 +01:00
|
|
|
yakusToCheck <- fmap toCheck finders
|
|
|
|
return (foldl scored M.empty yakusToCheck, newPack)
|
|
|
|
where
|
|
|
|
newPack = foldl add pack cards
|
2018-03-19 12:28:15 +01:00
|
|
|
toCheck yakusByCard = foldl (\set key -> S.union set (yakusByCard M.! key)) S.empty cards
|
2018-03-05 16:29:10 +01:00
|
|
|
scored score (YakuFinder {yaku, rater}) = foldr (M.insert yaku) score $ rater newPack
|
2018-03-10 23:25:44 +01:00
|
|
|
|
|
|
|
sumYakus :: Score -> Points
|
2018-03-15 22:32:24 +01:00
|
|
|
sumYakus s
|
|
|
|
| null s = 6
|
|
|
|
| otherwise = foldl (+) 0 s
|