module Haverer.Game (
Game,
Outcome,
finalScores,
makeGame,
newRound,
newRound',
players,
playersWon,
roundsPlayed,
scores,
winners
) where
import BasicPrelude
import Control.Monad.Random (MonadRandom)
import Haverer.Deck (FullDeck, newDeck)
import Haverer.PlayerSet (
PlayerSet,
rotate,
toPlayers,
)
import Haverer.Round (Round)
import qualified Haverer.Round as Round
import qualified Haverer.Internal.Counter as Counter
type PlayerScores a = Counter.Counter a Int
data Game playerId = Game {
_winningScore :: Int,
_players :: PlayerScores playerId,
_playerSet :: PlayerSet playerId,
_roundsPlayed :: Int
} deriving Show
data Outcome playerId = Outcome { _unoutcome :: PlayerScores playerId } deriving Show
makeGame :: Ord playerId => PlayerSet playerId -> Game playerId
makeGame ps = Game {
_winningScore = 4,
_players = Counter.initialize $ toPlayers ps,
_playerSet = ps,
_roundsPlayed = 0
}
newRound' :: (Ord playerId, Show playerId) => Game playerId -> FullDeck -> Round playerId
newRound' game deck = Round.makeRound deck (_playerSet game)
newRound :: (Functor m, MonadRandom m, Ord playerId, Show playerId) => Game playerId -> m (Round playerId)
newRound game = newRound' game <$> newDeck
playersWon :: Ord playerId => Game playerId -> [playerId] -> Either (Outcome playerId) (Game playerId)
playersWon game ps =
rotatePlayers . bumpRoundsPlayed <$> onCounter game (`Counter.incrementMany` ps)
where
bumpRoundsPlayed g = g { _roundsPlayed = _roundsPlayed g + 1 }
rotatePlayers g = g { _playerSet = rotate (_playerSet g) }
roundsPlayed :: Game playerId -> Int
roundsPlayed = _roundsPlayed
scores :: Ord playerId => Game playerId -> [(playerId, Int)]
scores = Counter.toList . _players
players :: Game playerId -> PlayerSet playerId
players = _playerSet
winners :: Ord playerId => Outcome playerId -> [playerId]
winners = snd . Counter.top . _unoutcome
finalScores :: Ord playerId => Outcome playerId -> [(playerId, Int)]
finalScores = Counter.toList . _unoutcome
onCounter :: Ord playerId => Game playerId -> (PlayerScores playerId -> PlayerScores playerId) -> Either (Outcome playerId) (Game playerId)
onCounter game f =
let players' = f $ _players game in
if Counter.topValue players' >= _winningScore game
then Left $ Outcome players'
else Right $ game { _players = players' }