{-# LANGUAGE NamedFieldPuns #-} module AI ( move ) where import Data.List (sortOn) import Data.Map ((!), delete, findMin) import Data.Ord (Down(..)) import Data.Set (member) import qualified Data.Set as Set (fromList) import Hanafuda ( Card(..), Flower(..), Pack , cardsOf, cardsOfPack, contains, empty,flower, packOfCards, sameMonth, size, union ) import Hanafuda.KoiKoi (Move(..), PlayerID, Step(..)) import Hanafuda.Message (PublicGame(..), PublicState(..), PublicPlayer) import qualified Hanafuda.Message as Message (Coordinates(..)) move :: PlayerID -> PublicGame -> Move move me (PublicGame {playerHand, public = PublicState {coordinates, step = ToPlay, players, river}}) = case getAvailableCards hand (cardsOfPack river) of [] -> Play $ worstFor month opponent hand available -> let riverCard = bestFor month (players ! me) available in let matchingCards = cardsOfPack . sameMonth riverCard $ playerHand in capture (bestFor month (players ! me) matchingCards) riverCard river where month = Message.month $ coordinates hand = cardsOfPack playerHand opponent = snd . findMin $ delete me players move me (PublicGame {public = PublicState {coordinates, step = Turned card, players, river}}) = Choose . bestFor month (players ! me) . cardsOfPack $ sameMonth card river where month = Message.month $ coordinates move _ (PublicGame {playerHand, public = PublicState {step = Scored}}) = KoiKoi $ playerHand /= empty move _ _ = error "Nothing to play on ended game" capture :: Card -> Card -> Pack -> Move capture card caught river = if size (sameMonth card river) == 1 then Play card else Capture (card, caught) getAvailableCards :: [Card] -> [Card] -> [Card] getAvailableCards hand = filter ((`member` flowersInHand) . flower) where flowersInHand = Set.fromList $ flower <$> hand choose :: Ord a => (Card -> a) -> [Card] -> Card choose sortCriterion = head . sortOn sortCriterion bestFor :: Flower -> PublicPlayer -> [Card] -> Card bestFor monthFlower player = choose (Down . rank monthFlower player) worstFor :: Flower -> PublicPlayer -> [Card] -> Card worstFor monthFlower player = choose (rank monthFlower player) rank :: Flower -> PublicPlayer -> Card -> Int rank monthFlower _ card | isTrueLight card = 5 | card == RainMan = 2 | isSpecialRibbon card = 4 | isRibbon card = 3 | isSpecialAnimal card = 4 | isAnimal card = 3 | isMonthly monthFlower card = 4 | otherwise = 1 isTrueLight :: Card -> Bool isTrueLight = (packOfCards [Crane, CampCurtain, FullMoon, Phoenix] `contains`) isSpecialRibbon :: Card -> Bool isSpecialRibbon = ((aotan `union` akatan) `contains`) where aotan = packOfCards [PeonyBlue, ChrysanthemumBlue, MapleBlue] akatan = packOfCards [PinePoetry, PlumPoetry, CherryPoetry] isRibbon :: Card -> Bool isRibbon card = isSpecialRibbon card || packOfCards other `contains` card where other = [WisteriaRed, IrisRed, BushCloverRed, WillowRed] isSpecialAnimal :: Card -> Bool isSpecialAnimal = (packOfCards [Butterflies, Boar, SakeCup, Deer] `contains`) isAnimal :: Card -> Bool isAnimal card = (isSpecialAnimal card) || (packOfCards other `contains` card) where other = [BushWarbler, Cuckoo, EightPlankBridge, Geese, Swallow] isMonthly :: Flower -> Card -> Bool isMonthly monthFlower = (cardsOf monthFlower `contains`)