Compare commits
9 commits
main
...
stateless-
Author | SHA1 | Date | |
---|---|---|---|
7a324ee7ef | |||
ce0f51720a | |||
31ad94e5af | |||
3440c84543 | |||
e0003c5906 | |||
0b6fd62255 | |||
e12f8e1f0d | |||
d2918c5256 | |||
ff1a31a105 |
6 changed files with 95 additions and 57 deletions
|
@ -1,10 +1,38 @@
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
module Hanafuda.ID (
|
module Hanafuda.ID (
|
||||||
ID(..)
|
ID(..)
|
||||||
, getID
|
, IDType(..)
|
||||||
|
, Prefix(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
newtype ID a = ID Int deriving (Eq, Ord, Enum, Read, Show)
|
import Data.Char (isDigit)
|
||||||
|
import System.Random (Random(..))
|
||||||
|
import Text.ParserCombinators.ReadP (char, munch, string)
|
||||||
|
import Text.ParserCombinators.ReadPrec (lift)
|
||||||
|
import Text.Read (readPrec)
|
||||||
|
|
||||||
getID :: ID a -> String
|
newtype ID a = ID {
|
||||||
getID (ID n) = show n
|
getID :: Int
|
||||||
|
} deriving (Eq, Ord)
|
||||||
|
|
||||||
|
newtype Prefix a = Prefix String
|
||||||
|
|
||||||
|
class IDType a where
|
||||||
|
prefix :: Prefix a
|
||||||
|
|
||||||
|
instance IDType a => Show (ID a) where
|
||||||
|
show (ID someID) = p ++ ('#' : show someID)
|
||||||
|
where Prefix p = (prefix :: Prefix a)
|
||||||
|
|
||||||
|
instance IDType a => Read (ID a) where
|
||||||
|
readPrec = fmap (ID . read) . lift $
|
||||||
|
string p >> char '#' >> munch (isDigit)
|
||||||
|
where Prefix p = (prefix :: Prefix a)
|
||||||
|
|
||||||
|
rIntToRID :: (Int, g) -> (ID a, g)
|
||||||
|
rIntToRID (someID, g) = (ID someID, g)
|
||||||
|
|
||||||
|
instance Random (ID a) where
|
||||||
|
random = rIntToRID . randomR (0, maxBound)
|
||||||
|
randomR (ID idA, ID idB) = rIntToRID . randomR (idA, idB)
|
||||||
|
|
|
@ -4,13 +4,16 @@ module Hanafuda.KoiKoi (
|
||||||
Action(..)
|
Action(..)
|
||||||
, Card(..)
|
, Card(..)
|
||||||
, Environment
|
, Environment
|
||||||
, Game
|
, Game(..)
|
||||||
, GameBlueprint(..)
|
|
||||||
, GameID
|
, GameID
|
||||||
, Mode(..)
|
, Mode(..)
|
||||||
, Move(..)
|
, Move(..)
|
||||||
|
, Player
|
||||||
, PlayerID
|
, PlayerID
|
||||||
|
, PlayerTurn
|
||||||
|
, Players
|
||||||
, Score
|
, Score
|
||||||
|
, Scores
|
||||||
, Source(..)
|
, Source(..)
|
||||||
, Step(..)
|
, Step(..)
|
||||||
, Yaku(..)
|
, Yaku(..)
|
||||||
|
@ -18,17 +21,19 @@ module Hanafuda.KoiKoi (
|
||||||
, play
|
, play
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import qualified Data.Map as Map (fromList)
|
||||||
import Hanafuda (Card(..), Flower(Pine), contains, flower, match, remove)
|
import Hanafuda (Card(..), Flower(Pine), contains, flower, match, remove)
|
||||||
import qualified Hanafuda.Player as Player (players, random, scores)
|
import qualified Hanafuda.Player as Player (players, random, scores)
|
||||||
import Hanafuda.KoiKoi.Yaku (Yaku(..), Score)
|
import Hanafuda.KoiKoi.Yaku (Yaku(..), Score)
|
||||||
import Hanafuda.KoiKoi.Game (
|
import Hanafuda.KoiKoi.Game (
|
||||||
Action(..), Environment, Game, GameBlueprint(..), GameID, Mode(..), Move(..), PlayerID
|
Action(..), Environment, Game(..), GameID, Mode(..), Move(..), Player
|
||||||
, Source(..), Step(..)
|
, PlayerID, PlayerTurn, Players, Scores, Source(..), Step(..)
|
||||||
)
|
)
|
||||||
import qualified Hanafuda.KoiKoi.Round as Round (deal, next)
|
import qualified Hanafuda.KoiKoi.Round as Round (deal, next)
|
||||||
import qualified Hanafuda.KoiKoi.Turn as Turn (catch, end, next)
|
import qualified Hanafuda.KoiKoi.Turn as Turn (catch, end, next)
|
||||||
import Control.Monad.Except (MonadError(..))
|
import Control.Monad.Except (MonadError(..))
|
||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
import System.Random (randomIO)
|
||||||
|
|
||||||
play :: Environment m => Move -> Game -> m Game
|
play :: Environment m => Move -> Game -> m Game
|
||||||
play move game@(Game {river, step}) =
|
play move game@(Game {river, step}) =
|
||||||
|
@ -52,11 +57,14 @@ play move game@(Game {river, step}) =
|
||||||
new :: MonadIO m => (PlayerID, PlayerID) -> Mode -> m Game
|
new :: MonadIO m => (PlayerID, PlayerID) -> Mode -> m Game
|
||||||
new (playerA, playerB) mode = do
|
new (playerA, playerB) mode = do
|
||||||
playing <- Player.random players
|
playing <- Player.random players
|
||||||
|
gameID <- liftIO $ randomIO
|
||||||
Round.deal $ Game {
|
Round.deal $ Game {
|
||||||
mode
|
gameID
|
||||||
|
, mode
|
||||||
, scores = Player.scores players [0, 0]
|
, scores = Player.scores players [0, 0]
|
||||||
, month = Pine
|
, month = Pine
|
||||||
, players
|
, players
|
||||||
|
, nextPlayer
|
||||||
, playing
|
, playing
|
||||||
, winning = playing
|
, winning = playing
|
||||||
, oyake = playing
|
, oyake = playing
|
||||||
|
@ -68,3 +76,4 @@ new (playerA, playerB) mode = do
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
players = Player.players [playerA, playerB]
|
players = Player.players [playerA, playerB]
|
||||||
|
nextPlayer = Map.fromList $ [(playerA, playerB), (playerB, playerA)]
|
||||||
|
|
|
@ -4,13 +4,16 @@
|
||||||
module Hanafuda.KoiKoi.Game (
|
module Hanafuda.KoiKoi.Game (
|
||||||
Action(..)
|
Action(..)
|
||||||
, Environment
|
, Environment
|
||||||
, Game
|
, Game(..)
|
||||||
, GameBlueprint(..)
|
|
||||||
, GameID
|
, GameID
|
||||||
, ID
|
, ID
|
||||||
, Mode(..)
|
, Mode(..)
|
||||||
, Move(..)
|
, Move(..)
|
||||||
|
, Player
|
||||||
, PlayerID
|
, PlayerID
|
||||||
|
, PlayerTurn
|
||||||
|
, Players
|
||||||
|
, Scores
|
||||||
, Source(..)
|
, Source(..)
|
||||||
, Step(..)
|
, Step(..)
|
||||||
, end
|
, end
|
||||||
|
@ -20,14 +23,14 @@ module Hanafuda.KoiKoi.Game (
|
||||||
import Control.Monad.Except (MonadError)
|
import Control.Monad.Except (MonadError)
|
||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
import Control.Monad.Writer (MonadWriter)
|
import Control.Monad.Writer (MonadWriter)
|
||||||
|
import Data.Map (Map)
|
||||||
import Hanafuda (Card, Flower, Pack)
|
import Hanafuda (Card, Flower, Pack)
|
||||||
import Hanafuda.ID (ID)
|
import Hanafuda.ID (ID, IDType(..), Prefix(..))
|
||||||
import qualified Hanafuda.KoiKoi.Yaku as KoiKoi (Score)
|
import qualified Hanafuda.KoiKoi.Yaku as KoiKoi (Score)
|
||||||
import Hanafuda.Player (Players, Player, Scores, set)
|
import qualified Hanafuda.Player as Player (ID, Players, Player, Scores, set)
|
||||||
import qualified Hanafuda.Player as Player (ID)
|
|
||||||
|
|
||||||
data Mode = FirstAt Int | WholeYear deriving (Show)
|
data Mode = FirstAt Int | WholeYear deriving (Show)
|
||||||
data Move = Play Card | Capture (Card, Card) | Choose Card | KoiKoi Bool
|
data Move = Play Card | Capture (Card, Card) | Choose Card | KoiKoi Bool deriving (Show)
|
||||||
data Step = ToPlay | Turned Card | Scored | Over deriving (Show)
|
data Step = ToPlay | Turned Card | Scored | Over deriving (Show)
|
||||||
data Source = Hand | Deck deriving (Show)
|
data Source = Hand | Deck deriving (Show)
|
||||||
data Action = Action {
|
data Action = Action {
|
||||||
|
@ -37,27 +40,36 @@ data Action = Action {
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
type Environment m = (MonadIO m, MonadError String m, MonadWriter [Action] m)
|
type Environment m = (MonadIO m, MonadError String m, MonadWriter [Action] m)
|
||||||
|
type Player = Player.Player KoiKoi.Score
|
||||||
type PlayerID = Player.ID KoiKoi.Score
|
type PlayerID = Player.ID KoiKoi.Score
|
||||||
|
type Players = Player.Players KoiKoi.Score
|
||||||
|
type Scores = Player.Scores KoiKoi.Score
|
||||||
type GameID = ID Game
|
type GameID = ID Game
|
||||||
|
instance IDType Game where
|
||||||
|
prefix = Prefix "Game"
|
||||||
|
type PlayerTurn = Map PlayerID PlayerID
|
||||||
|
|
||||||
data GameBlueprint deckType = Game {
|
data Game = Game {
|
||||||
mode :: Mode
|
gameID :: GameID
|
||||||
, scores :: Scores KoiKoi.Score
|
, mode :: Mode
|
||||||
|
, scores :: Scores
|
||||||
, month :: Flower
|
, month :: Flower
|
||||||
, players :: Players KoiKoi.Score
|
, nextPlayer :: PlayerTurn
|
||||||
|
, players :: Players
|
||||||
, playing :: PlayerID
|
, playing :: PlayerID
|
||||||
, winning :: PlayerID
|
, winning :: PlayerID
|
||||||
, oyake :: PlayerID
|
, oyake :: PlayerID
|
||||||
, deck :: deckType
|
, deck :: [Card]
|
||||||
, river :: Pack
|
, river :: Pack
|
||||||
, step :: Step
|
, step :: Step
|
||||||
, trick :: [Card]
|
, trick :: [Card]
|
||||||
, rounds :: [(PlayerID, KoiKoi.Score)]
|
, rounds :: [(PlayerID, KoiKoi.Score)]
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
type Game = GameBlueprint [Card]
|
|
||||||
|
|
||||||
setPlayer :: Game -> Player KoiKoi.Score -> Game
|
setPlayer :: Game -> Player -> Game
|
||||||
setPlayer game@(Game {players, playing}) player = game {players = set playing player players}
|
setPlayer game@(Game {players, playing}) player = game {
|
||||||
|
players = Player.set playing player players
|
||||||
|
}
|
||||||
|
|
||||||
end :: Monad m => Game -> m Game
|
end :: Monad m => Game -> m Game
|
||||||
end game = return $ game {step = Over}
|
end game = return $ game {step = Over}
|
||||||
|
|
|
@ -6,8 +6,8 @@ module Hanafuda.KoiKoi.Round (
|
||||||
|
|
||||||
import Hanafuda (Flower(Paulownia), cards, shuffle, packOfCards)
|
import Hanafuda (Flower(Paulownia), cards, shuffle, packOfCards)
|
||||||
import Hanafuda.KoiKoi.Yaku (sumYakus)
|
import Hanafuda.KoiKoi.Yaku (sumYakus)
|
||||||
import Hanafuda.KoiKoi.Game (Game, GameBlueprint(..), Mode(..), Step(..), end)
|
import Hanafuda.KoiKoi.Game (Game(..), Mode(..), Step(..), end)
|
||||||
import qualified Hanafuda.Player as Player (deal, get, next, score, yakus)
|
import qualified Hanafuda.Player as Player (deal, get, score, yakus)
|
||||||
import Data.Map ((!), insert)
|
import Data.Map ((!), insert)
|
||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
import Control.Monad.State (runState, state)
|
import Control.Monad.State (runState, state)
|
||||||
|
@ -25,14 +25,14 @@ deal game@(Game {players}) = do
|
||||||
getTriple = (,,) <$> take8 <*> take8 <*> take8
|
getTriple = (,,) <$> take8 <*> take8 <*> take8
|
||||||
|
|
||||||
next :: MonadIO m => Game -> m Game
|
next :: MonadIO m => Game -> m Game
|
||||||
next game@(Game {mode, scores, month, players, oyake, winning, rounds}) =
|
next game@(Game {mode, scores, month, players, nextPlayer, oyake, winning, rounds}) =
|
||||||
case mode of
|
case mode of
|
||||||
FirstAt n | n <= newScore -> end scored
|
FirstAt n | n <= newScore -> end scored
|
||||||
FirstAt _ -> continue
|
FirstAt _ -> continue
|
||||||
WholeYear | month == Paulownia -> end scored
|
WholeYear | month == Paulownia -> end scored
|
||||||
WholeYear -> continue
|
WholeYear -> continue
|
||||||
where
|
where
|
||||||
playing = Player.next players oyake
|
playing = nextPlayer ! oyake
|
||||||
winner = Player.get winning players
|
winner = Player.get winning players
|
||||||
newScore = (scores ! winning) + Player.score sumYakus winner
|
newScore = (scores ! winning) + Player.score sumYakus winner
|
||||||
scored = game {scores = insert winning newScore scores, rounds = (winning, Player.yakus winner): rounds}
|
scored = game {scores = insert winning newScore scores, rounds = (winning, Player.yakus winner): rounds}
|
||||||
|
|
|
@ -7,11 +7,12 @@ module Hanafuda.KoiKoi.Turn (
|
||||||
, popNextCard
|
, popNextCard
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.Map ((!))
|
||||||
import Hanafuda (Card, Pack, empty, match)
|
import Hanafuda (Card, Pack, empty, match)
|
||||||
import Hanafuda.Player (Player(..), plays)
|
import Hanafuda.Player (Player(..), plays)
|
||||||
import qualified Hanafuda.Player as Player (get, next)
|
import qualified Hanafuda.Player as Player (get)
|
||||||
import Hanafuda.KoiKoi.Yaku (meldInto)
|
import Hanafuda.KoiKoi.Yaku (meldInto)
|
||||||
import Hanafuda.KoiKoi.Game (Action(..), Environment, Game, GameBlueprint(..), Source(..), Step(..), setPlayer)
|
import Hanafuda.KoiKoi.Game (Action(..), Environment, Game(..), Source(..), Step(..), setPlayer)
|
||||||
import qualified Hanafuda.KoiKoi.Round as Round (next)
|
import qualified Hanafuda.KoiKoi.Round as Round (next)
|
||||||
import Control.Monad.Except (MonadError(..))
|
import Control.Monad.Except (MonadError(..))
|
||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
|
@ -56,8 +57,8 @@ end game@(Game {month, trick, playing, players}) card (river, newCaptured) = do
|
||||||
updatedPlayer = player {meld = newMeld, yakus = scored `mappend` (yakus player)}
|
updatedPlayer = player {meld = newMeld, yakus = scored `mappend` (yakus player)}
|
||||||
|
|
||||||
next :: MonadIO m => Game -> m Game
|
next :: MonadIO m => Game -> m Game
|
||||||
next game@(Game {players, playing}) =
|
next game@(Game {players, nextPlayer, playing}) =
|
||||||
let newPlaying = Player.next players playing in
|
let newPlaying = nextPlayer ! playing in
|
||||||
if hand (Player.get newPlaying players) == empty
|
if hand (Player.get newPlaying players) == empty
|
||||||
then Round.next $ game
|
then Round.next $ game
|
||||||
else return $ game {playing = newPlaying, step = ToPlay}
|
else return $ game {playing = newPlaying, step = ToPlay}
|
||||||
|
|
|
@ -3,42 +3,33 @@
|
||||||
module Hanafuda.Player where
|
module Hanafuda.Player where
|
||||||
|
|
||||||
import Hanafuda (Card, Pack, contains, packOfCards, remove)
|
import Hanafuda (Card, Pack, contains, packOfCards, remove)
|
||||||
import qualified Hanafuda.ID as Hanafuda (ID)
|
import qualified Hanafuda.ID as Hanafuda (ID, IDType(..), Prefix(..))
|
||||||
import Data.Map ((!), Map, adjust, elemAt, empty, findMin, fromList, insert, keys, singleton, size)
|
import Data.Map ((!), Map, elemAt, insert, keys, size)
|
||||||
import qualified Data.Map as Map (filter)
|
import qualified Data.Map as Map (fromList, keys)
|
||||||
import Control.Monad.IO.Class (MonadIO(..))
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
import Control.Monad.Except (MonadError(..))
|
import Control.Monad.Except (MonadError(..))
|
||||||
import System.Random (Random(..))
|
import System.Random (Random(..))
|
||||||
|
|
||||||
type ID yakus = Hanafuda.ID (Player yakus)
|
type ID yakus = Hanafuda.ID (Player yakus)
|
||||||
|
instance Hanafuda.IDType (Player yakus) where
|
||||||
|
prefix = Hanafuda.Prefix "Player"
|
||||||
|
|
||||||
data Player yakus = Player {
|
data Player yakus = Player {
|
||||||
hand :: Pack
|
hand :: Pack
|
||||||
, meld :: Pack
|
, meld :: Pack
|
||||||
, nextPlayer :: ID yakus
|
|
||||||
, yakus :: yakus
|
, yakus :: yakus
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
newtype Players yakus = Players (Map (ID yakus) (Player yakus)) deriving (Show)
|
newtype Players yakus = Players (Map (ID yakus) (Player yakus)) deriving (Show)
|
||||||
|
|
||||||
new :: Monoid yakus => (ID yakus) -> Player yakus
|
new :: Monoid yakus => [Card] -> Player yakus
|
||||||
new nextPlayer = Player {
|
new cards = Player {
|
||||||
hand = packOfCards []
|
hand = packOfCards cards
|
||||||
, meld = packOfCards []
|
, meld = packOfCards []
|
||||||
, nextPlayer
|
|
||||||
, yakus = mempty
|
, yakus = mempty
|
||||||
}
|
}
|
||||||
|
|
||||||
players :: Monoid yakus => [ID yakus] -> Players yakus
|
players :: Monoid yakus => [ID yakus] -> Players yakus
|
||||||
players [] = Players empty
|
players = Players . Map.fromList . fmap (\playerID -> (playerID, new []))
|
||||||
players [player] = Players $ singleton player $ new player
|
|
||||||
players (alice:others@(bob:_)) =
|
|
||||||
let Players playersByID = players others in
|
|
||||||
let (before, _) = findMin $ Map.filter ((== bob) . nextPlayer) playersByID in
|
|
||||||
Players $ insert alice (new bob) $ adjust (setNextPlayer alice) before playersByID
|
|
||||||
where
|
|
||||||
setNextPlayer nextPlayer player = player {nextPlayer}
|
|
||||||
|
|
||||||
next :: Players yakus -> (ID yakus) -> (ID yakus)
|
|
||||||
next (Players playersByID) = nextPlayer . (playersByID !)
|
|
||||||
|
|
||||||
random :: MonadIO m => Players yakus -> m (ID yakus)
|
random :: MonadIO m => Players yakus -> m (ID yakus)
|
||||||
random (Players playersByID) =
|
random (Players playersByID) =
|
||||||
|
@ -54,12 +45,9 @@ set playerID player (Players playersByID) = Players $ insert playerID player pla
|
||||||
|
|
||||||
deal :: Monoid yakus => Players yakus -> [[Card]] -> Players yakus
|
deal :: Monoid yakus => Players yakus -> [[Card]] -> Players yakus
|
||||||
deal (Players playersByID) hands =
|
deal (Players playersByID) hands =
|
||||||
Players $ snd $ foldl dealTo (fst $ findMin playersByID, playersByID) hands
|
Players . Map.fromList . zipWith setHand hands $ Map.keys playersByID
|
||||||
where
|
where
|
||||||
setHand cards (Player {nextPlayer}) =
|
setHand cards playerID = (playerID, new cards)
|
||||||
(new nextPlayer) {hand = packOfCards cards}
|
|
||||||
dealTo (playerID, m) hand =
|
|
||||||
(nextPlayer $ m ! playerID, adjust (setHand hand) playerID m)
|
|
||||||
|
|
||||||
plays :: MonadError String m => Player yakus -> Card -> m (Player yakus)
|
plays :: MonadError String m => Player yakus -> Card -> m (Player yakus)
|
||||||
plays player@(Player {hand}) card =
|
plays player@(Player {hand}) card =
|
||||||
|
@ -74,4 +62,4 @@ score :: (yakus -> Points) -> Player yakus -> Points
|
||||||
score rater = rater . yakus
|
score rater = rater . yakus
|
||||||
|
|
||||||
scores :: Players yakus -> [Points] -> Scores yakus
|
scores :: Players yakus -> [Points] -> Scores yakus
|
||||||
scores (Players playersByKey) = fromList . zip (keys playersByKey)
|
scores (Players playersByKey) = Map.fromList . zip (keys playersByKey)
|
||||||
|
|
Loading…
Reference in a new issue