restructure everything

This commit is contained in:
Christian Merten
2019-04-14 18:15:27 +02:00
parent 6578415e3c
commit 7c55e02cf6
6 changed files with 245 additions and 273 deletions
+44 -95
View File
@@ -1,8 +1,15 @@
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module Card where module Card where
import Data.List import Data.List
import System.Random (newStdGen)
import Utils import Utils
class Countable a b where
count :: a -> b
data Type = Seven data Type = Seven
| Eight | Eight
| Nine | Nine
@@ -13,13 +20,13 @@ data Type = Seven
| Jack | Jack
deriving (Eq, Ord, Show, Enum) deriving (Eq, Ord, Show, Enum)
countType :: Type -> Int instance Countable Type Int where
countType Ace = 11 count Ace = 11
countType Ten = 10 count Ten = 10
countType King = 4 count King = 4
countType Queen = 3 count Queen = 3
countType Jack = 2 count Jack = 2
countType _ = 0 count _ = 0
data Colour = Diamonds data Colour = Diamonds
| Hearts | Hearts
@@ -30,73 +37,14 @@ data Colour = Diamonds
data Card = Card Type Colour data Card = Card Type Colour
deriving (Eq, Show) deriving (Eq, Show)
countCard :: Card -> Int getColour :: Card -> Colour
countCard (Card t _) = countType t getColour (Card _ c) = c
count :: [Card] -> Int instance Countable Card Int where
count = sum . map countCard count (Card t _) = count t
data Team = Team | Single instance Countable [Card] Int where
deriving (Show, Eq, Ord, Enum) count = sum . map count
data Space = Table | Hand1 | Hand2 | Hand3 | WonTeam | WonSingle | SkatP
deriving (Show, Eq, Ord, Enum)
teamPile :: Team -> Space
teamPile Team = WonTeam
teamPile Single = WonSingle
playerHand :: Index -> Space
playerHand One = Hand1
playerHand Two = Hand2
playerHand Three = Hand3
playerOfHand :: Space -> Index
playerOfHand Hand1 = One
playerOfHand Hand2 = Two
playerOfHand Hand3 = Three
data CardS = CardS { getCard :: Card
, getSpace :: Space
, getOwner :: Space }
deriving (Show, Eq)
moveCard :: Card -> Space -> [CardS] -> [CardS]
moveCard card sp cards = map f cards
where f c = if card == getCard c then c { getSpace = sp } else c
findCards :: Space -> [CardS] -> [Card]
findCards sp cards = foldr f [] cards
where f (CardS c s _) cs
| s == sp = c : cs
| otherwise = cs
data Index = One | Two | Three
deriving (Show, Ord, Eq, Enum)
next :: Index -> Index
next One = Two
next Two = Three
next Three = One
prev :: Index -> Index
prev One = Three
prev Two = One
prev Three = Two
data Player = Player { team :: Team
, index :: Index }
deriving Show
data Players = Players Player Player Player
deriving Show
player :: Players -> Index -> Player
player (Players p _ _) One = p
player (Players _ p _) Two = p
player (Players _ _ p) Three = p
type Hand = [Card]
equals :: Colour -> Maybe Colour -> Bool equals :: Colour -> Maybe Colour -> Bool
equals col (Just x) = col == x equals col (Just x) = col == x
@@ -112,31 +60,37 @@ effectiveColour trumpCol card@(Card _ col) =
if trump then trumpCol else col if trump then trumpCol else col
where trump = isTrump trumpCol card where trump = isTrump trumpCol card
isAllowed :: Colour -> Maybe Colour -> Hand -> Card -> Bool isAllowed :: Colour -> Maybe Colour -> [Card] -> Card -> Bool
isAllowed trumpCol turnCol cs card = isAllowed trumpCol turnCol cs card =
if col `equals` turnCol if col `equals` turnCol
then True then True
else not $ any (\ca -> effectiveColour trumpCol ca `equals` turnCol && ca /= card) cs else not $ any (\ca -> effectiveColour trumpCol ca `equals` turnCol && ca /= card) cs
where col = effectiveColour trumpCol card where col = effectiveColour trumpCol card
putAt :: Space -> Card -> CardS compareCards :: Colour
putAt sp c = CardS c sp sp -> Maybe Colour
-> Card
-> Card
-> Ordering
compareCards _ _ (Card Jack col1) (Card Jack col2) = compare col1 col2
compareCards trumpCol turnCol c1@(Card tp1 col1) c2@(Card tp2 col2) =
case compare trp1 trp2 of
EQ ->
case compare (col1 `equals` turnCol)
(col2 `equals` turnCol) of
EQ -> compare tp1 tp2
v -> v
v -> v
where trp1 = isTrump trumpCol c1
trp2 = isTrump trumpCol c2
distribute :: [Card] -> [CardS] sortCards :: Colour -> Maybe Colour -> [Card] -> [Card]
distribute cards = map (putAt Hand1) hand1 sortCards trumpCol turnCol cs = sortBy (compareCards trumpCol turnCol) cs
++ map (putAt Hand2) hand2
++ map (putAt Hand3) hand3
++ map (putAt SkatP) skt
where round1 = chunksOf 3 (take 9 cards)
skt = take 2 $ drop 9 cards
round2 = chunksOf 4 (take 12 $ drop 11 cards)
round3 = chunksOf 3 (take 9 $ drop 23 cards)
hand1 = concatMap (!! 0) [round1, round2, round3]
hand2 = concatMap (!! 1) [round1, round2, round3]
hand3 = concatMap (!! 2) [round1, round2, round3]
playersFromTable :: Players -> [CardS] -> [Player] shuffleCards :: IO [Card]
playersFromTable ps = map (player ps . playerOfHand . getOwner) shuffleCards = do
gen <- newStdGen
return $ shuffle gen allCards
-- TESTING VARS -- TESTING VARS
@@ -155,15 +109,10 @@ c4 = Card Queen Hearts
c5 :: Card c5 :: Card
c5 = Card Jack Clubs c5 = Card Jack Clubs
h1 :: Hand h1 :: [Card]
h1 = [c1,c2,c3,c4,c5] h1 = [c1,c2,c3,c4,c5]
allCards :: [Card] allCards :: [Card]
allCards = [ Card t c | t <- tps, c <- cols ] allCards = [ Card t c | t <- tps, c <- cols ]
where tps = [Seven .. Jack] where tps = [Seven .. Jack]
cols = [Diamonds .. Clubs] cols = [Diamonds .. Clubs]
distributePutSkat :: [Card] -> [CardS]
distributePutSkat cards = foldr (\c m -> moveCard c WonSingle m) distributed skt
where distributed = distribute cards
skt = findCards SkatP distributed
+36 -159
View File
@@ -7,28 +7,10 @@ import Data.Ord
import Card import Card
import Skat import Skat
import Pile
import Player
import Utils (shuffle) import Utils (shuffle)
compareCards :: Colour
-> Maybe Colour
-> Card
-> Card
-> Ordering
compareCards _ _ (Card Jack col1) (Card Jack col2) = compare col1 col2
compareCards trumpCol turnCol c1@(Card tp1 col1) c2@(Card tp2 col2) =
case compare trp1 trp2 of
EQ ->
case compare (col1 `equals` turnCol)
(col2 `equals` turnCol) of
EQ -> compare tp1 tp2
v -> v
v -> v
where trp1 = isTrump trumpCol c1
trp2 = isTrump trumpCol c2
sortCards :: Colour -> Maybe Colour -> [Card] -> [Card]
sortCards trumpCol turnCol cs = sortBy (compareCards trumpCol turnCol) cs
compareRender :: Card -> Card -> Ordering compareRender :: Card -> Card -> Ordering
compareRender (Card t1 c1) (Card t2 c2) = case compare c1 c2 of compareRender (Card t1 c1) (Card t2 c2) = case compare c1 c2 of
EQ -> compare t1 t2 EQ -> compare t1 t2
@@ -37,167 +19,62 @@ compareRender (Card t1 c1) (Card t2 c2) = case compare c1 c2 of
sortRender :: [Card] -> [Card] sortRender :: [Card] -> [Card]
sortRender = sortBy compareRender sortRender = sortBy compareRender
-- | finishes the calculation of a match turn :: Hand -> Skat (Int, Int)
turning :: Index -> Skat (Int, Int) turn n = do
turning n = undefined table <- getp tableCards
turn2 :: Index -> Skat (Int, Int)
turn2 n = do
t <- table
ps <- gets players ps <- gets players
let p = player ps n let p = player ps n
hand <- cardsAt (playerHand $ index p) hand <- getp $ handCards n
if length hand == 0 case length table of
then countGame 0 -> play p >> turn (next n)
else case length t of 1 -> do
0 -> play p >> turn2 (next n) modify $ setTurnColour (Just $ getColour $ head table)
1 -> do play p
modify (setTurnColour . f . head $ t) turn (next n)
play p 2 -> play p >> turn (next n)
turn2 (next n) 3 -> do
2 -> play p >> evaluateTable >>= turn2 w <- evaluateTable
3 -> evaluateTable >>= turn2 if length hand == 0 then countGame else turn w
where f (Card _ col) = Just col
simulate :: Team -> Index -> Skat (Int, Int) evaluateTable :: Skat Hand
simulate team n = do
t <- table
ps <- gets players
let p = player ps n
hand <- cardsAt (playerHand $ index p)
if length hand == 0
then countGame
else case length t of
0 -> playOpen team p >> simulate team (next n)
1 -> do
modify (setTurnColour . f . head $ t)
playOpen team p
simulate team (next n)
2 -> playOpen team p >> evaluateTable >>= simulate team
3 -> evaluateTable >>= simulate team
where f (Card _ col) = Just col
evaluateTable :: Skat Index
evaluateTable = do evaluateTable = do
trumpCol <- gets trumpColour trumpCol <- gets trumpColour
turnCol <- gets turnColour turnCol <- gets turnColour
t <- table table <- getp tableCards
ts <- tableS
ps <- gets players ps <- gets players
let psOrdered = playersFromTable ps ts let winningCard = head $ sortCards trumpCol turnCol table
l = zip psOrdered t Just winnerHand <- getp $ originOfCard winningCard
g a b = compareCards trumpCol turnCol (snd a) (snd b) let winner = player ps winnerHand
(winner, _) = last (sortBy g l) modifyp $ cleanTable (team winner)
pile = teamPile $ team winner
forM t (\c -> move c pile)
modify $ setTurnColour Nothing modify $ setTurnColour Nothing
return $ index winner return $ hand winner
countGame :: Skat (Int, Int) countGame :: Skat (Int, Int)
countGame = do countGame = getp count
sgl <- count <$> cardsAt WonSingle
tm <- count <$> cardsAt WonTeam
return (sgl, tm)
turn :: Index -> Skat Index play :: Player p => p -> Skat Card
turn n = do
ps <- gets players
let p1 = player ps n
p2 = player ps (next n)
p3 = player ps (next $ next n)
c1@(Card _ col) <- play p1
modify $ setTurnColour (Just col)
c2 <- play p2
c3 <- play p3
trumpCol <- gets trumpColour
turnCol <- gets turnColour
let l = zip3 [p1, p2, p3] [c1, c2, c3] [n, next n, next $ next n]
g a b = compareCards trumpCol turnCol (f a) (f b)
(winner, _, idx) = last (sortBy g l)
pile = teamPile $ team winner
move c1 pile
move c2 pile
move c3 pile
modify $ setTurnColour Nothing
return idx
where f (_, x, _) = x
play :: Player -> Skat Card
play p = do play p = do
table <- table table <- getp tableCards
turnCol <- gets turnColour turnCol <- gets turnColour
trump <- gets trumpColour trump <- gets trumpColour
hand <- cardsAt (playerHand $ index p) hand <- getp $ handCards (hand p)
let card = playCard p table hand trump turnCol let card = chooseCard p trump turnCol hand
move card Table modifyp $ playCard card
return card return card
playOpen :: Team -> Player -> Skat Card ---- TESTING VARS
playOpen team p = do
card <- playCardOpenAI team p
move card Table
return card
-- | cheating AI that knows all cards (open play)
playCardOpenAI :: Team -> Player -> Skat Card
playCardOpenAI team p = do
table <- table
turnCol <- gets turnColour
trump <- gets trumpColour
hand <- cardsAt (playerHand $ index p)
let possible = filter (isAllowed trump turnCol hand) hand
ownResult = if team == Single then fst else snd
ownIdx = index p
results <- forM possible (\card -> do
move card Table
val <- ownResult <$> simulate team ownIdx
move card (playerHand $ index p)
return (val, card))
return $ snd $ maximumBy (comparing fst) results
playCard :: Player
-> [Card]
-> [Card]
-> Colour
-> Maybe Colour
-> Card
playCard p table hand trump turnCol = head possible
where possible = filter (isAllowed trump turnCol hand) hand
runGame :: Skat (Int, Int)
runGame = do
foldM_ (\i _ -> turn i) One [1..10]
sgl <- fmap count $ cardsAt WonSingle
tm <- fmap count $ cardsAt WonTeam
return (sgl, tm)
shuffleCards :: IO [Card]
shuffleCards = do
gen <- newStdGen
return $ shuffle gen allCards
-- TESTING VARS
env :: SkatEnv env :: SkatEnv
env = SkatEnv cards Nothing Spades playersExamp env = SkatEnv piles Nothing Spades playersExamp
where hand1 = take 10 allCards where piles = distribute allCards
hand2 = take 10 $ drop 10 allCards
hand3 = take 10 $ drop 20 allCards
skt = drop 30 allCards
cards = map (putAt Hand1) hand1
++ map (putAt Hand2) hand2
++ map (putAt Hand3) hand3
++ map (putAt WonSingle) skt
playersExamp :: Players playersExamp :: Players
playersExamp = Players (Player Team One) (Player Team Two) (Player Single Three) playersExamp = Players
(PL $ Stupid Team Hand1)
(PL $ Stupid Team Hand2)
(PL $ Stupid Single Hand3)
shuffledEnv :: IO SkatEnv shuffledEnv :: IO SkatEnv
shuffledEnv = do shuffledEnv = do
cards <- shuffleCards cards <- shuffleCards
return $ SkatEnv (distribute cards) Nothing Spades playersExamp return $ SkatEnv (distribute cards) Nothing Spades playersExamp
shuffledEnv2 :: IO SkatEnv
shuffledEnv2 = do
cards <- shuffleCards
return $ SkatEnv (distributePutSkat cards) Nothing Spades playersExamp
+103
View File
@@ -0,0 +1,103 @@
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module Pile where
import Data.List
import Card
import Utils
import Control.Exception
data Team = Team | Single
deriving (Show, Eq, Ord, Enum)
data CardS p = CardS { getCard :: Card
, getPile :: p }
deriving (Show, Eq)
instance Countable (CardS p) Int where
count = count . getCard
data Hand = Hand1 | Hand2 | Hand3
deriving (Show, Eq)
next :: Hand -> Hand
next Hand1 = Hand2
next Hand2 = Hand3
next Hand3 = Hand1
prev :: Hand -> Hand
prev Hand1 = Hand3
prev Hand2 = Hand1
prev Hand3 = Hand2
data Played = Table Hand
| Won Hand Team
deriving (Show, Eq)
data SkatP = SkatP
deriving (Show, Eq)
data Piles = Piles { hands :: [CardS Hand]
, played :: [CardS Played]
, skat :: [CardS SkatP] }
deriving (Show, Eq)
instance Countable Piles (Int, Int) where
count ps = (sgl, tm)
where sgl = count (skatCards ps) + count (wonCards Single ps)
tm = count (wonCards Team ps)
origin :: CardS Played -> Hand
origin (CardS _ (Table hand)) = hand
origin (CardS _ (Won hand _)) = hand
originOfCard :: Card -> Piles -> Maybe Hand
originOfCard card (Piles _ pld _) = origin <$> find ((==card) . getCard) pld
playCard :: Card -> Piles -> Piles
playCard card (Piles hs pld skt) = Piles hs' (ca : pld) skt
where (CardS _ hand, hs') = remove ((==card) . getCard) hs
ca = CardS card (Table hand)
winCard :: Team -> CardS Played -> CardS Played
winCard team (CardS card (Table hand)) = CardS card (Won hand team)
winCard team c = c
wonCards :: Team -> Piles -> [Card]
wonCards team (Piles _ pld _) = filterMap (f . getPile) getCard pld
where f (Won _ tm) = tm == team
f _ = False
cleanTable :: Team -> Piles -> Piles
cleanTable winner ps@(Piles hs pld skt) = Piles hs pld' skt
where table = tableCards ps
pld' = map (winCard winner) pld
tableCards :: Piles -> [Card]
tableCards (Piles _ pld _) = filterMap (f . getPile) getCard pld
where f (Table _) = True
f _ = False
handCards :: Hand -> Piles -> [Card]
handCards hand (Piles hs _ _) = filterMap ((==hand) . getPile) getCard hs
skatCards :: Piles -> [Card]
skatCards (Piles _ _ skat) = map getCard skat
putAt :: p -> Card -> CardS p
putAt = flip CardS
distribute :: [Card] -> Piles
distribute cards = Piles hands [] (map (putAt SkatP) skt)
where round1 = chunksOf 3 (take 9 cards)
skt = take 2 $ drop 9 cards
round2 = chunksOf 4 (take 12 $ drop 11 cards)
round3 = chunksOf 3 (take 9 $ drop 23 cards)
hand1 = concatMap (!! 0) [round1, round2, round3]
hand2 = concatMap (!! 1) [round1, round2, round3]
hand3 = concatMap (!! 2) [round1, round2, round3]
hands = map (putAt Hand1) hand1
++ map (putAt Hand2) hand2
++ map (putAt Hand3) hand3
+42
View File
@@ -0,0 +1,42 @@
{-# LANGUAGE ExistentialQuantification #-}
module Player where
import Card
import Pile
class Player p where
team :: p -> Team
hand :: p -> Hand
chooseCard :: p -> Colour -> Maybe Colour -> [Card] -> Card
data Stupid = Stupid { getTeam :: Team
, getHand :: Hand }
deriving Show
instance Player Stupid where
team = getTeam
hand = getHand
chooseCard p trumpCol turnCol hand = head possible
where possible = filter (isAllowed trumpCol turnCol hand) hand
data PL = forall p. (Show p, Player p) => PL p
instance Show PL where
show (PL p) = show p
instance Player PL where
team (PL p) = team p
hand (PL p) = hand p
chooseCard (PL p) = chooseCard p
data Players = Players PL PL PL
deriving Show
player :: Players -> Hand -> PL
player (Players p _ _) Hand1 = p
player (Players _ p _) Hand2 = p
player (Players _ _ p) Hand3 = p
--playersFromTable :: Players -> [CardS] -> [Player]
--playersFromTable ps = map (player ps . playerOfHand . getOwner)
+12 -19
View File
@@ -1,11 +1,16 @@
{-# LANGUAGE NamedFieldPuns #-}
module Skat where module Skat where
import Card
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import Data.List import Data.List
data SkatEnv = SkatEnv { cards :: [CardS] import Card
import Pile
import Player
data SkatEnv = SkatEnv { piles :: Piles
, turnColour :: Maybe Colour , turnColour :: Maybe Colour
, trumpColour :: Colour , trumpColour :: Colour
, players :: Players } , players :: Players }
@@ -13,24 +18,12 @@ data SkatEnv = SkatEnv { cards :: [CardS]
type Skat = StateT SkatEnv IO type Skat = StateT SkatEnv IO
table :: Skat [Card] modifyp :: (Piles -> Piles) -> Skat ()
table = gets cards >>= return . foldr f [] modifyp f = modify g
where f (CardS c Table _) cs = c : cs where g env@(SkatEnv {piles}) = env { piles = f piles}
f _ cs = cs
tableS :: Skat [CardS] getp :: (Piles -> a) -> Skat a
tableS = gets cards >>= return . foldr f [] getp f = gets piles >>= return . f
where f c@(CardS _ Table _) cs = c : cs
f _ cs = cs
move :: Card -> Space -> Skat ()
move card sp = do
cs <- gets cards
let cs' = moveCard card sp cs
modify (\env -> env { cards = cs' })
cardsAt :: Space -> Skat [Card]
cardsAt sp = gets cards >>= return . findCards sp
setTurnColour :: Maybe Colour -> SkatEnv -> SkatEnv setTurnColour :: Maybe Colour -> SkatEnv -> SkatEnv
setTurnColour col sk = sk { turnColour = col } setTurnColour col sk = sk { turnColour = col }
+8
View File
@@ -22,3 +22,11 @@ query s = do
case l of case l of
Just x -> return x Just x -> return x
Nothing -> query s Nothing -> query s
remove :: (a -> Bool) -> [a] -> (a, [a])
remove pred xs = foldr f (undefined, []) xs
where f c (old, cs) = if pred c then (c, cs) else (old, c : cs)
filterMap :: (a -> Bool) -> (a -> b) -> [a] -> [b]
filterMap pred f as = foldr g [] as
where g a bs = if pred a then f a : bs else bs