1 module TicTacToe (
    2         Player(..),
    3         Fin3(..),
    4         Position(..),
    5         Move (..),
    6         Game,
    7         isFreePosition,
    8         isValidMove,
    9         isValidGame,
   10         emptyGame,
   11         addMove,
   12         moveCount
   13 )
   14         where
   15 
   16 import Control.Exception (assert)
   17 
   18 data Player = X | O
   19   deriving (Eq, Show, Bounded, Enum)
   20 data Fin3 = I1 | I2 | I3
   21   deriving (Eq, Show, Bounded, Enum)
   22 newtype Position = Position { unPosition :: (Fin3, Fin3) }
   23   deriving (Eq, Show)
   24 data Move = Move { movePosition :: Position, movePlayer :: Player }
   25   deriving (Eq, Show)
   26 newtype Game = Game { unGame :: [Move] }
   27   deriving (Eq, Show)
   28 
   29 isFreePosition :: Game -> Position -> Bool
   30 isFreePosition g p = p `notElem` map movePosition (unGame g)
   31 
   32 isValidMove :: Game -> Move -> Bool
   33 isValidMove g m = isFreePosition g (movePosition m)
   34 
   35 isValidGame :: Game -> Bool
   36 isValidGame (Game []) = True
   37 isValidGame (Game (m:ms)) = isValidMove g m && isValidGame g
   38   where g = Game ms
   39 
   40 emptyGame :: Game
   41 emptyGame = Game []
   42 
   43 addMove :: Move -> Game -> Game
   44 addMove m g = assert (isValidGame g && isValidMove g m) $ Game (m : unGame g)
   45 
   46 moveCount :: Game -> Int
   47 moveCount = length . unGame