{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE PartialTypeSignatures #-}
module Boardgame (
Player(..)
, Position(..)
, Outcome(..)
, PositionalGame(..)
, nextPlayer
, mapPosition
, isOccupied
, isEmpty
, mapOutcome
, isWin
, isDraw
, play
, playerToInt
, playIO
, takeEmptyMakeMove
, patternMatchingGameOver
, drawIf
, player1WinsIf
, player2WinsIf
, player1LosesIf
, player2LosesIf
, drawWhen
, player1WinsWhen
, player2WinsWhen
, player1LosesWhen
, player2LosesWhen
, criteria
, symmetric
, unless
, ifNotThen
, makerBreakerGameOver
) where
import Data.Functor ((<&>))
import Data.List (find, intercalate, minimumBy, intersect)
import Data.Maybe (isJust, fromJust)
import System.IO (hFlush, stdout)
import Text.Read (readMaybe)
import Control.Monad (join, foldM)
import Control.Applicative ((<|>))
import Data.Bifunctor (first, Bifunctor (second))
#ifdef WASM
import Data.Aeson (ToJSON(toJSON), Value(Number, Null))
import Data.Scientific (fromFloatDigits)
#endif
data Player = Player1 | Player2
deriving (Int -> Player -> ShowS
[Player] -> ShowS
Player -> String
(Int -> Player -> ShowS)
-> (Player -> String) -> ([Player] -> ShowS) -> Show Player
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Player] -> ShowS
$cshowList :: [Player] -> ShowS
show :: Player -> String
$cshow :: Player -> String
showsPrec :: Int -> Player -> ShowS
$cshowsPrec :: Int -> Player -> ShowS
Show, Player -> Player -> Bool
(Player -> Player -> Bool)
-> (Player -> Player -> Bool) -> Eq Player
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Player -> Player -> Bool
$c/= :: Player -> Player -> Bool
== :: Player -> Player -> Bool
$c== :: Player -> Player -> Bool
Eq)
nextPlayer :: Player -> Player
nextPlayer :: Player -> Player
nextPlayer Player
Player1 = Player
Player2
nextPlayer Player
Player2 = Player
Player1
playerToInt :: Player -> Int
playerToInt :: Player -> Int
playerToInt Player
Player1 = Int
1
playerToInt Player
Player2 = Int
2
#ifdef WASM
instance ToJSON Player where
toJSON = Number . fromFloatDigits . fromIntegral . playerToInt
#endif
data Position = Occupied Player | Empty
deriving (Position -> Position -> Bool
(Position -> Position -> Bool)
-> (Position -> Position -> Bool) -> Eq Position
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Position -> Position -> Bool
$c/= :: Position -> Position -> Bool
== :: Position -> Position -> Bool
$c== :: Position -> Position -> Bool
Eq, Int -> Position -> ShowS
[Position] -> ShowS
Position -> String
(Int -> Position -> ShowS)
-> (Position -> String) -> ([Position] -> ShowS) -> Show Position
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Position] -> ShowS
$cshowList :: [Position] -> ShowS
show :: Position -> String
$cshow :: Position -> String
showsPrec :: Int -> Position -> ShowS
$cshowsPrec :: Int -> Position -> ShowS
Show)
#ifdef WASM
instance ToJSON Position where
toJSON (Occupied p) = toJSON p
toJSON Empty = Null
#endif
mapPosition :: (Player -> Player) -> Position -> Position
mapPosition :: (Player -> Player) -> Position -> Position
mapPosition Player -> Player
f (Occupied Player
p) = Player -> Position
Occupied (Player -> Position) -> Player -> Position
forall a b. (a -> b) -> a -> b
$ Player -> Player
f Player
p
mapPosition Player -> Player
_ Position
Empty = Position
Empty
isOccupied :: Position -> Bool
isOccupied :: Position -> Bool
isOccupied (Occupied Player
_) = Bool
True
isOccupied Position
Empty = Bool
False
isEmpty :: Position -> Bool
isEmpty :: Position -> Bool
isEmpty (Occupied Player
_) = Bool
False
isEmpty Position
Empty = Bool
True
data Outcome = Win Player | Draw
deriving (Outcome -> Outcome -> Bool
(Outcome -> Outcome -> Bool)
-> (Outcome -> Outcome -> Bool) -> Eq Outcome
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Outcome -> Outcome -> Bool
$c/= :: Outcome -> Outcome -> Bool
== :: Outcome -> Outcome -> Bool
$c== :: Outcome -> Outcome -> Bool
Eq, Int -> Outcome -> ShowS
[Outcome] -> ShowS
Outcome -> String
(Int -> Outcome -> ShowS)
-> (Outcome -> String) -> ([Outcome] -> ShowS) -> Show Outcome
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Outcome] -> ShowS
$cshowList :: [Outcome] -> ShowS
show :: Outcome -> String
$cshow :: Outcome -> String
showsPrec :: Int -> Outcome -> ShowS
$cshowsPrec :: Int -> Outcome -> ShowS
Show)
#ifdef WASM
instance ToJSON Outcome where
toJSON (Win p) = toJSON p
toJSON Draw = Null
#endif
mapOutcome :: (Player -> Player) -> Outcome -> Outcome
mapOutcome :: (Player -> Player) -> Outcome -> Outcome
mapOutcome Player -> Player
f (Win Player
p) = Player -> Outcome
Win (Player -> Outcome) -> Player -> Outcome
forall a b. (a -> b) -> a -> b
$ Player -> Player
f Player
p
mapOutcome Player -> Player
_ Outcome
Draw = Outcome
Draw
isWin :: Outcome -> Bool
isWin :: Outcome -> Bool
isWin (Win Player
_) = Bool
True
isWin Outcome
Draw = Bool
False
isDraw :: Outcome -> Bool
isDraw :: Outcome -> Bool
isDraw (Win Player
_) = Bool
False
isDraw Outcome
Draw = Bool
True
class PositionalGame a c | a -> c where
makeMove :: a -> Player -> c -> Maybe a
makeMove = a -> Player -> c -> Maybe a
forall a c. PositionalGame a c => a -> Player -> c -> Maybe a
takeEmptyMakeMove
gameOver :: a -> Maybe (Outcome, [c])
positions :: a -> [Position]
getPosition :: a -> c -> Maybe Position
setPosition :: a -> c -> Position -> Maybe a
takeEmptyMakeMove :: PositionalGame a c => a -> Player -> c -> Maybe a
takeEmptyMakeMove :: a -> Player -> c -> Maybe a
takeEmptyMakeMove a
a Player
p c
coord = case a -> c -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition a
a c
coord of
Just Position
Empty -> a -> c -> Position -> Maybe a
forall a c. PositionalGame a c => a -> c -> Position -> Maybe a
setPosition a
a c
coord (Player -> Position
Occupied Player
p)
Maybe Position
_ -> Maybe a
forall a. Maybe a
Nothing
patternMatchingGameOver :: (Eq c, PositionalGame a c) => [[c]] -> a -> Maybe (Outcome, [c])
patternMatchingGameOver :: [[c]] -> a -> Maybe (Outcome, [c])
patternMatchingGameOver [[c]]
patterns a
a = case ((Position, [c]) -> Bool)
-> [(Position, [c])] -> Maybe (Position, [c])
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Position -> Bool
isOccupied (Position -> Bool)
-> ((Position, [c]) -> Position) -> (Position, [c]) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Position, [c]) -> Position
forall a b. (a, b) -> a
fst) ([(Position, [c])] -> Maybe (Position, [c]))
-> [(Position, [c])] -> Maybe (Position, [c])
forall a b. (a -> b) -> a -> b
$ (\[c]
pat -> (, [c]
pat) (Position -> (Position, [c])) -> Position -> (Position, [c])
forall a b. (a -> b) -> a -> b
$ [Position] -> Position
reduceHomogeneousList (Maybe Position -> Position
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe Position -> Position)
-> (c -> Maybe Position) -> c -> Position
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> c -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition a
a (c -> Position) -> [c] -> [Position]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [c]
pat)) ([c] -> (Position, [c])) -> [[c]] -> [(Position, [c])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [[c]]
patterns of
Maybe (Position, [c])
Nothing -> if (Position -> Bool) -> [Position] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Position -> Bool
isOccupied (a -> [Position]
forall a c. PositionalGame a c => a -> [Position]
positions a
a) then (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Outcome
Draw, []) else Maybe (Outcome, [c])
forall a. Maybe a
Nothing
Just (Occupied Player
winner, [c]
coords) -> (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Player -> Outcome
Win Player
winner, [c]
coords)
Just (Position
Empty, [c]
coords) -> (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Outcome
Draw, [c]
coords)
where
reduceHomogeneousList :: [Position] -> Position
reduceHomogeneousList :: [Position] -> Position
reduceHomogeneousList [] = Position
Empty
reduceHomogeneousList (Position
x:[Position]
xs) = if (Position -> Bool) -> [Position] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Position -> Position -> Bool
forall a. Eq a => a -> a -> Bool
== Position
x) [Position]
xs then Position
x else Position
Empty
makerBreakerGameOver :: (Eq c, PositionalGame a c) => [[c]] -> a -> Maybe (Outcome, [c])
makerBreakerGameOver :: [[c]] -> a -> Maybe (Outcome, [c])
makerBreakerGameOver [[c]]
patterns a
a
| Just [c]
coords <- Maybe [c]
player1won = (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Player -> Outcome
Win Player
Player1, [c]
coords)
| Bool
player2won = (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Player -> Outcome
Win Player
Player2, [c]
player2Coords)
| Bool
otherwise = Maybe (Outcome, [c])
forall a. Maybe a
Nothing
where
player1won :: Maybe [c]
player1won = ([c] -> Bool) -> [[c]] -> Maybe [c]
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((c -> Bool) -> [c] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ((c -> Bool) -> [c] -> Bool) -> (c -> Bool) -> [c] -> Bool
forall a b. (a -> b) -> a -> b
$ (Position -> Position -> Bool
forall a. Eq a => a -> a -> Bool
== Player -> Position
Occupied Player
Player1) (Position -> Bool) -> (c -> Position) -> c -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Position -> Position
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe Position -> Position)
-> (c -> Maybe Position) -> c -> Position
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> c -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition a
a) [[c]]
patterns
player2won :: Bool
player2won = ([c] -> Bool) -> [[c]] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ((c -> Bool) -> [c] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((c -> Bool) -> [c] -> Bool) -> (c -> Bool) -> [c] -> Bool
forall a b. (a -> b) -> a -> b
$ (Position -> Position -> Bool
forall a. Eq a => a -> a -> Bool
== Player -> Position
Occupied Player
Player2) (Position -> Bool) -> (c -> Position) -> c -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Position -> Position
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe Position -> Position)
-> (c -> Maybe Position) -> c -> Position
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> c -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition a
a) [[c]]
patterns
player2Coords :: [c]
player2Coords = ([c] -> [c] -> Ordering) -> [[c]] -> [c]
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> Ordering) -> t a -> a
minimumBy [c] -> [c] -> Ordering
forall a b. [a] -> [b] -> Ordering
compareLength ([[c]] -> [c]) -> [[c]] -> [c]
forall a b. (a -> b) -> a -> b
$ [[c]] -> [[c]]
forall c. Eq c => [[c]] -> [[c]]
assignments ([[c]] -> [[c]]) -> [[c]] -> [[c]]
forall a b. (a -> b) -> a -> b
$ (c -> Bool) -> [c] -> [c]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Position -> Position -> Bool
forall a. Eq a => a -> a -> Bool
== Player -> Position
Occupied Player
Player2) (Position -> Bool) -> (c -> Position) -> c -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Position -> Position
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe Position -> Position)
-> (c -> Maybe Position) -> c -> Position
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> c -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition a
a) ([c] -> [c]) -> [[c]] -> [[c]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [[c]]
patterns
compareLength :: [a] -> [b] -> Ordering
compareLength :: [a] -> [b] -> Ordering
compareLength [] [] = Ordering
EQ
compareLength (a
_:[a]
_) [] = Ordering
GT
compareLength [] (b
_:[b]
_) = Ordering
LT
compareLength (a
_:[a]
xs) (b
_:[b]
ys) = [a] -> [b] -> Ordering
forall a b. [a] -> [b] -> Ordering
compareLength [a]
xs [b]
ys
assignments :: Eq c => [[c]] -> [[c]]
assignments :: [[c]] -> [[c]]
assignments = [c] -> [[c]] -> [[c]]
forall a. Eq a => [a] -> [[a]] -> [[a]]
assignments' []
where
assignments' :: [a] -> [[a]] -> [[a]]
assignments' [a]
set [] = [[a]
set]
assignments' [a]
set ([a]
claus:[[a]]
clauses) = if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([a] -> Bool) -> [a] -> Bool
forall a b. (a -> b) -> a -> b
$ [a] -> [a] -> [a]
forall a. Eq a => [a] -> [a] -> [a]
intersect [a]
set [a]
claus
then [a] -> [[a]] -> [[a]]
assignments' [a]
set [[a]]
clauses
else [[[a]]] -> [[a]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[[a]]] -> [[a]]) -> [[[a]]] -> [[a]]
forall a b. (a -> b) -> a -> b
$ (\a
c -> [a] -> [[a]] -> [[a]]
assignments' (a
ca -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
set) [[a]]
clauses) (a -> [[a]]) -> [a] -> [[[a]]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [a]
claus
avoiderEnforcerGameOver :: (Eq c, PositionalGame a c) => [[c]] -> a -> Maybe (Outcome, [c])
avoiderEnforcerGameOver :: [[c]] -> a -> Maybe (Outcome, [c])
avoiderEnforcerGameOver [[c]]
patterns a
a = (Outcome -> Outcome) -> (Outcome, [c]) -> (Outcome, [c])
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ((Player -> Player) -> Outcome -> Outcome
mapOutcome Player -> Player
nextPlayer) ((Outcome, [c]) -> (Outcome, [c]))
-> Maybe (Outcome, [c]) -> Maybe (Outcome, [c])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [[c]] -> a -> Maybe (Outcome, [c])
forall c a.
(Eq c, PositionalGame a c) =>
[[c]] -> a -> Maybe (Outcome, [c])
makerBreakerGameOver [[c]]
patterns a
a
play :: (Monad m, PositionalGame a c) =>
(a -> m ())
-> (Player -> m ())
-> m c
-> m ()
-> ((Outcome, [c]) -> m ())
-> a
-> m ()
play :: (a -> m ())
-> (Player -> m ())
-> m c
-> m ()
-> ((Outcome, [c]) -> m ())
-> a
-> m ()
play a -> m ()
putState Player -> m ()
putTurn m c
getMove m ()
putInvalidMove (Outcome, [c]) -> m ()
putGameOver a
startingState = a -> m ()
putState a
startingState m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Player -> m ()
putTurn Player
Player1 m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> Player -> m ()
play' a
startingState Player
Player1
where
play' :: a -> Player -> m ()
play' a
s Player
p = m c
getMove m c -> (c -> Maybe a) -> m (Maybe a)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> a -> Player -> c -> Maybe a
forall a c. PositionalGame a c => a -> Player -> c -> Maybe a
makeMove a
s Player
p m (Maybe a) -> (Maybe a -> m ()) -> m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just a
s' -> a -> m ()
putState a
s' m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> case a -> Maybe (Outcome, [c])
forall a c. PositionalGame a c => a -> Maybe (Outcome, [c])
gameOver a
s' of
Just (Outcome, [c])
v -> (Outcome, [c]) -> m ()
putGameOver (Outcome, [c])
v
Maybe (Outcome, [c])
Nothing -> (\Player
p' -> Player -> m ()
putTurn Player
p' m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> Player -> m ()
play' a
s' Player
p') (Player -> m ()) -> Player -> m ()
forall a b. (a -> b) -> a -> b
$ Player -> Player
nextPlayer Player
p
Maybe a
Nothing -> m ()
putInvalidMove m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> Player -> m ()
play' a
s Player
p
playIO :: (Show a, Show c, Read c, PositionalGame a c) => a -> IO ()
playIO :: a -> IO ()
playIO = (a -> IO ())
-> (Player -> IO ())
-> IO c
-> IO ()
-> ((Outcome, [c]) -> IO ())
-> a
-> IO ()
forall (m :: * -> *) a c.
(Monad m, PositionalGame a c) =>
(a -> m ())
-> (Player -> m ())
-> m c
-> m ()
-> ((Outcome, [c]) -> m ())
-> a
-> m ()
play a -> IO ()
forall a. Show a => a -> IO ()
putState Player -> IO ()
putTurn IO c
getMove IO ()
putInvalidMove (Outcome, [c]) -> IO ()
putGameOver
where
putState :: a -> IO ()
putState a
s = String -> IO ()
putStr String
"\ESC[s\ESC[0;0H" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> IO ()
forall a. Show a => a -> IO ()
print a
s IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> IO ()
putStr String
"\ESC[u" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout
putTurn :: Player -> IO ()
putTurn Player
p = String -> IO ()
putStr (String
"Move for " String -> ShowS
forall a. [a] -> [a] -> [a]
++ (case Player
p of
Player
Player1 -> String
"player 1"
Player
Player2 -> String
"player 2") String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
": ") IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout
getMove :: IO c
getMove = IO String
getLine IO String -> (String -> Maybe c) -> IO (Maybe c)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> String -> Maybe c
forall a. Read a => String -> Maybe a
readMaybe IO (Maybe c) -> (Maybe c -> IO c) -> IO c
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just c
c -> c -> IO c
forall (m :: * -> *) a. Monad m => a -> m a
return c
c
Maybe c
Nothing -> String -> IO ()
putStr String
"Invalid input, try again: " IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout IO () -> IO c -> IO c
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO c
getMove
putInvalidMove :: IO ()
putInvalidMove = String -> IO ()
putStr String
"Invalid move, try again: " IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout
putGameOver :: (Outcome, [c]) -> IO ()
putGameOver = \case
(Win Player
Player1, [c]
p) -> String -> IO ()
putStrLn String
"Player 1 won!" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> [c] -> IO ()
forall a. Show a => a -> IO ()
print [c]
p IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout
(Win Player
Player2, [c]
p) -> String -> IO ()
putStrLn String
"Player 2 won!" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> [c] -> IO ()
forall a. Show a => a -> IO ()
print [c]
p IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout
(Outcome
Draw, [c]
_) -> String -> IO ()
putStrLn String
"It's a draw!" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
stdout
data CombinedPositionalGames a b i j = CombinedPositionalGames a b
instance (PositionalGame a i, PositionalGame b j) => PositionalGame (CombinedPositionalGames a b i j) (Either i j) where
makeMove :: CombinedPositionalGames a b i j
-> Player -> Either i j -> Maybe (CombinedPositionalGames a b i j)
makeMove (CombinedPositionalGames a
x b
y) Player
player Either i j
index = case Either i j
index of
Left i
i -> (a -> b -> CombinedPositionalGames a b i j)
-> b -> a -> CombinedPositionalGames a b i j
forall a b c. (a -> b -> c) -> b -> a -> c
flip a -> b -> CombinedPositionalGames a b i j
forall a b i j. a -> b -> CombinedPositionalGames a b i j
CombinedPositionalGames b
y (a -> CombinedPositionalGames a b i j)
-> Maybe a -> Maybe (CombinedPositionalGames a b i j)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> Player -> i -> Maybe a
forall a c. PositionalGame a c => a -> Player -> c -> Maybe a
makeMove a
x Player
player i
i
Right j
i -> a -> b -> CombinedPositionalGames a b i j
forall a b i j. a -> b -> CombinedPositionalGames a b i j
CombinedPositionalGames a
x (b -> CombinedPositionalGames a b i j)
-> Maybe b -> Maybe (CombinedPositionalGames a b i j)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> b -> Player -> j -> Maybe b
forall a c. PositionalGame a c => a -> Player -> c -> Maybe a
makeMove b
y Player
player j
i
gameOver :: CombinedPositionalGames a b i j -> Maybe (Outcome, [Either i j])
gameOver (CombinedPositionalGames a
x b
y) = (([i] -> [Either i j]) -> (Outcome, [i]) -> (Outcome, [Either i j])
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second ((i -> Either i j) -> [i] -> [Either i j]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap i -> Either i j
forall a b. a -> Either a b
Left) ((Outcome, [i]) -> (Outcome, [Either i j]))
-> Maybe (Outcome, [i]) -> Maybe (Outcome, [Either i j])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> Maybe (Outcome, [i])
forall a c. PositionalGame a c => a -> Maybe (Outcome, [c])
gameOver a
x)
Maybe (Outcome, [Either i j])
-> Maybe (Outcome, [Either i j]) -> Maybe (Outcome, [Either i j])
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (([j] -> [Either i j]) -> (Outcome, [j]) -> (Outcome, [Either i j])
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second ((j -> Either i j) -> [j] -> [Either i j]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap j -> Either i j
forall a b. b -> Either a b
Right) ((Outcome, [j]) -> (Outcome, [Either i j]))
-> Maybe (Outcome, [j]) -> Maybe (Outcome, [Either i j])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> b -> Maybe (Outcome, [j])
forall a c. PositionalGame a c => a -> Maybe (Outcome, [c])
gameOver b
y)
positions :: CombinedPositionalGames a b i j -> [Position]
positions (CombinedPositionalGames a
x b
y) = a -> [Position]
forall a c. PositionalGame a c => a -> [Position]
positions a
x [Position] -> [Position] -> [Position]
forall a. [a] -> [a] -> [a]
++ b -> [Position]
forall a c. PositionalGame a c => a -> [Position]
positions b
y
getPosition :: CombinedPositionalGames a b i j -> Either i j -> Maybe Position
getPosition (CombinedPositionalGames a
x b
y) = (i -> Maybe Position)
-> (j -> Maybe Position) -> Either i j -> Maybe Position
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (a -> i -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition a
x) (b -> j -> Maybe Position
forall a c. PositionalGame a c => a -> c -> Maybe Position
getPosition b
y)
setPosition :: CombinedPositionalGames a b i j
-> Either i j
-> Position
-> Maybe (CombinedPositionalGames a b i j)
setPosition (CombinedPositionalGames a
x b
y) Either i j
ij Position
p = case Either i j
ij of
Left i
i -> (a -> b -> CombinedPositionalGames a b i j)
-> b -> a -> CombinedPositionalGames a b i j
forall a b c. (a -> b -> c) -> b -> a -> c
flip a -> b -> CombinedPositionalGames a b i j
forall a b i j. a -> b -> CombinedPositionalGames a b i j
CombinedPositionalGames b
y (a -> CombinedPositionalGames a b i j)
-> Maybe a -> Maybe (CombinedPositionalGames a b i j)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> i -> Position -> Maybe a
forall a c. PositionalGame a c => a -> c -> Position -> Maybe a
setPosition a
x i
i Position
p
Right j
j -> a -> b -> CombinedPositionalGames a b i j
forall a b i j. a -> b -> CombinedPositionalGames a b i j
CombinedPositionalGames a
x (b -> CombinedPositionalGames a b i j)
-> Maybe b -> Maybe (CombinedPositionalGames a b i j)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> b -> j -> Position -> Maybe b
forall a c. PositionalGame a c => a -> c -> Position -> Maybe a
setPosition b
y j
j Position
p
player1WinsIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player1WinsIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player1WinsIf a -> Bool
pred a
x = if a -> Bool
pred a
x
then (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Player -> Outcome
Win Player
Player1, [])
else Maybe (Outcome, [c])
forall a. Maybe a
Nothing
player2LosesIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player2LosesIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player2LosesIf = (a -> Bool) -> a -> Maybe (Outcome, [c])
forall a c. (a -> Bool) -> a -> Maybe (Outcome, [c])
player1WinsIf
player2WinsIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player2WinsIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player2WinsIf a -> Bool
pred a
x = if a -> Bool
pred a
x
then (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Player -> Outcome
Win Player
Player2, [])
else Maybe (Outcome, [c])
forall a. Maybe a
Nothing
player1LosesIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player1LosesIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
player1LosesIf = (a -> Bool) -> a -> Maybe (Outcome, [c])
forall a c. (a -> Bool) -> a -> Maybe (Outcome, [c])
player2WinsIf
drawIf :: (a -> Bool) -> (a -> Maybe (Outcome, [c]))
drawIf :: (a -> Bool) -> a -> Maybe (Outcome, [c])
drawIf a -> Bool
pred a
x = if a -> Bool
pred a
x
then (Outcome, [c]) -> Maybe (Outcome, [c])
forall a. a -> Maybe a
Just (Outcome
Draw, [])
else Maybe (Outcome, [c])
forall a. Maybe a
Nothing
player1WinsWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player1WinsWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player1WinsWhen a -> Maybe [c]
pred a
x = (Player -> Outcome
Win Player
Player1, ) ([c] -> (Outcome, [c])) -> Maybe [c] -> Maybe (Outcome, [c])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> Maybe [c]
pred a
x
player2LosesWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player2LosesWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player2LosesWhen = (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
forall a c. (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player1WinsWhen
player2WinsWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player2WinsWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player2WinsWhen a -> Maybe [c]
pred a
x = (Player -> Outcome
Win Player
Player2, ) ([c] -> (Outcome, [c])) -> Maybe [c] -> Maybe (Outcome, [c])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> Maybe [c]
pred a
x
player1LosesWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player1LosesWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player1LosesWhen = (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
forall a c. (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
player2WinsWhen
drawWhen :: (a -> Maybe [c]) -> (a -> Maybe (Outcome, [c]))
drawWhen :: (a -> Maybe [c]) -> a -> Maybe (Outcome, [c])
drawWhen a -> Maybe [c]
pred a
x = (Outcome
Draw, ) ([c] -> (Outcome, [c])) -> Maybe [c] -> Maybe (Outcome, [c])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> Maybe [c]
pred a
x
ifNotThen :: (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c]))
ifNotThen :: (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
ifNotThen a -> Maybe (Outcome, [c])
crit1 a -> Maybe (Outcome, [c])
crit2 a
x = a -> Maybe (Outcome, [c])
crit1 a
x Maybe (Outcome, [c])
-> Maybe (Outcome, [c]) -> Maybe (Outcome, [c])
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> a -> Maybe (Outcome, [c])
crit2 a
x
infixl 8 `unless`
unless :: (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c]))
unless :: (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
unless = ((a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c]))
-> a
-> Maybe (Outcome, [c])
forall a b c. (a -> b -> c) -> b -> a -> c
flip (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
forall a c.
(a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
ifNotThen
criteria :: [a -> Maybe (Outcome, [c])] -> a -> Maybe (Outcome, [c])
criteria :: [a -> Maybe (Outcome, [c])] -> a -> Maybe (Outcome, [c])
criteria = ((a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c]))
-> [a -> Maybe (Outcome, [c])] -> a -> Maybe (Outcome, [c])
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldl1 (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
forall a c.
(a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
ifNotThen
symmetric :: (a -> a) -> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
symmetric :: (a -> a)
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
symmetric a -> a
flipState a -> Maybe (Outcome, [c])
criterion = a -> Maybe (Outcome, [c])
criterion (a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
forall a c.
(a -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
`ifNotThen` (((Outcome, [c]) -> (Outcome, [c]))
-> Maybe (Outcome, [c]) -> Maybe (Outcome, [c])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Outcome -> Outcome) -> (Outcome, [c]) -> (Outcome, [c])
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ((Outcome -> Outcome) -> (Outcome, [c]) -> (Outcome, [c]))
-> (Outcome -> Outcome) -> (Outcome, [c]) -> (Outcome, [c])
forall a b. (a -> b) -> a -> b
$ (Player -> Player) -> Outcome -> Outcome
mapOutcome Player -> Player
nextPlayer) (Maybe (Outcome, [c]) -> Maybe (Outcome, [c]))
-> (a -> Maybe (Outcome, [c])) -> a -> Maybe (Outcome, [c])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Maybe (Outcome, [c])
criterion (a -> Maybe (Outcome, [c]))
-> (a -> a) -> a -> Maybe (Outcome, [c])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> a
flipState)