|
- {-# LANGUAGE MultiParamTypeClasses #-}
- {-# LANGUAGE BlockArguments #-}
- {-# LANGUAGE TypeSynonymInstances #-}
- {-# LANGUAGE FlexibleInstances #-}
- {-# LANGUAGE FlexibleContexts #-}
- {-# LANGUAGE FunctionalDependencies #-}
- {-# LANGUAGE TupleSections #-}
- {-# LANGUAGE InstanceSigs #-}
- {-# LANGUAGE StandaloneDeriving #-}
- {-# LANGUAGE ImportQualifiedPost #-}
- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-
- module Skat.AI.TicTacToe where
-
- import Control.Monad.State
- import Control.Exception (assert)
- import Control.Monad.Fail
- import Data.Ord
- import Text.Read (readMaybe)
- import Data.List (maximumBy, sortBy)
- import Debug.Trace
- import Text.Printf
- import Data.Maybe
- import qualified System.Random as Rand
-
- import Skat.AI.Base
- import Skat.AI.MonteCarlo
- import Skat.Utils
- -- TIC TAC TOE implementation
-
- data TicTacToe = Tic | Tac | Toe
- deriving (Eq, Ord)
-
- instance Show TicTacToe where
- show Tic = "O"
- show Tac = "X"
- show Toe = "_"
-
- data WinLossTie = Loss | Tie | Win
- deriving (Eq, Show, Ord)
-
- instance Value WinLossTie where
- invert Win = Loss
- invert Loss = Win
- invert Tie = Tie
- win = Win
- loss = Loss
- tie = Tie
-
- data GameState = GameState { getBoard :: [TicTacToe]
- , getCurrent :: Bool }
- deriving Show
-
- instance HasGameState Int Bool WinLossTie GameState where
- execute turn state = execState (play turn) state
- moves state = evalState turns state
- monteevaluate s = let b = getBoard s
- w = fromMaybe Toe $ ticWinner b
- in case w of
- Tac -> Win
- Tic -> Loss
- Toe -> Tie
- current s = evalState currentPlayer s
-
- instance Player Bool where
- maxing = id
-
- instance Monad m => MonadGame Int [] WinLossTie Bool (StateT GameState m) where
- currentPlayer = gets getCurrent
- turns = do
- o <- over
- if o then return [] else do
- board <- gets getBoard
- let fields = zip [0..] board
- return $ map fst $ filter ((==Toe) . snd) fields
- play turn = do
- env <- get
- let value = if getCurrent env then Tic else Tac
- board' = updateAt turn (getBoard env) value
- current' = not $ getCurrent env
- put $ GameState board' current'
- simulate turn action = do
- backup <- get
- play turn
- res <- action
- put backup
- return $! res
- evaluate = do
- board <- gets getBoard
- current <- currentPlayer
- let mayWinner = ticWinner board
- case mayWinner of
- Just Tic -> return $ if current then Win else Loss
- Just Tac -> return $ if current then Loss else Win
- Just Toe -> return Tie
- Nothing -> return Tie
- over = do
- board <- gets getBoard
- case ticWinner board of
- Just _ -> return True
- _ -> return False
-
- ticWinner :: [TicTacToe] -> Maybe TicTacToe
- ticWinner board
- | ticWon = Just Tic
- | tacWon = Just Tac
- | over = Just Toe
- | otherwise = Nothing
- where ticWon = hasWon $ map (==Tic) board
- tacWon = hasWon $ map (==Tac) board
- hasWon (True:_:_:True:_:_:True:_:_:[]) = True
- hasWon (True:_:_:_:True:_:_:_:True:[]) = True
- hasWon (_:True:_:_:True:_:_:True:_:[]) = True
- hasWon (_:_:True:_:_:True:_:_:True:[]) = True
- hasWon (_:_:True:_:True:_:True:_:_:[]) = True
- hasWon (True:True:True:_:_:_:_:_:_:[]) = True
- hasWon (_:_:_:True:True:True:_:_:_:[]) = True
- hasWon (_:_:_:_:_:_:True:True:True:[]) = True
- hasWon _ = False
- over = (length $ filter (==Toe) board) == 0
-
- -- some consts
-
- emptyBoard :: [TicTacToe]
- emptyBoard = [Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe]
-
- otherBoard2 :: [TicTacToe]
- otherBoard2 = [Tic, Tac, Toe, Tac, Tac, Tic, Tic, Toe, Toe]
-
- otherBoard3 :: [TicTacToe]
- otherBoard3 = [Tic, Toe, Toe, Tac, Tic, Toe, Toe, Tac, Toe]
-
- tree2 = emptytree (initGameState { getBoard = otherBoard2
- , getCurrent = True })
-
- tree3 = emptytree (initGameState { getBoard = otherBoard3
- , getCurrent = False })
-
- initGameState :: GameState
- initGameState = GameState { getBoard = emptyBoard
- , getCurrent = False }
-
- tictree :: Tree Int GameState
- tictree = emptytree initGameState
-
- instance Draw GameState where
- draw s = let b = getBoard s
- in printf "%s %s %s\n%s %s %s\n%s %s %s"
- (show $ b !! 0)
- (show $ b !! 1)
- (show $ b !! 2)
- (show $ b !! 3)
- (show $ b !! 4)
- (show $ b !! 5)
- (show $ b !! 6)
- (show $ b !! 7)
- (show $ b !! 8)
-
- otherBoard :: [TicTacToe]
- otherBoard = [Tic, Tac, Tac, Tic, Tac, Tic, Toe, Tic, Toe]
-
- print9x9 :: (Int -> IO ()) -> IO ()
- print9x9 pr = pr 0 >> pr 1 >> pr 2 >> putStrLn ""
- >> pr 3 >> pr 4 >> pr 5 >> putStrLn ""
- >> pr 6 >> pr 7 >> pr 8 >> putStrLn ""
-
- printBoard :: [TicTacToe] -> IO ()
- printBoard board = print9x9 pr >> putStrLn ""
- where pr n = putStr (show $ board !! n) >> putStr " "
-
- printOptions :: [Int] -> IO ()
- printOptions opts = print9x9 pr
- where pr n
- | n `elem` opts = putStr (show n) >> putStr " "
- | otherwise = putStr " "
-
- instance MonadIO m => PlayableGame Int [] WinLossTie Bool (StateT GameState m) where
- showBoard = do
- board <- gets getBoard
- liftIO $ printBoard board
- showTurns = turns >>= liftIO . printOptions
- winner = do
- board <- gets getBoard
- let win = ticWinner board
- case win of
- Just Toe -> return Nothing
- Just Tic -> return $ Just True
- Just Tac -> return $ Just False
- Nothing -> return Nothing
- askTurn = readMaybe <$> liftIO getLine
- showTurn _ = return ()
-
- playTicTacToe :: Int -> IO ()
- playTicTacToe n = void $ (flip runStateT) (GameState emptyBoard False) (playCLI n)
-
- playoften :: Int -> IO ()
- playoften n = mapM_ playTicTacToe [1..n]
-
- {-
- newtype TicMCTS a = TicMCTS (StateT GameState (State Rand.StdGen) a)
- deriving (Functor, Applicative, Monad, MonadState GameState)
-
- instance Choose Int TicMCTS where
- choose = do
- s <- get
- -}
- playCLI :: Int -> StateT GameState IO ()
- playCLI n = do
- gameOver <- over
- if gameOver
- then announceWinner
- else do
- current <- currentPlayer
- --let current = False
- if not current then do
- s <- get
- let tree = Leaf s False (0, 0)
- t = bestmove $ runmonte n (foldM (\tree _ -> montecarlo tree) tree [1..5000])
- put t
- else do
- showBoard
- t <- readTurn
- play t
- showBoard
- {-
- liftIO $ getLine
- -}
- playCLI n
- where
- readTurn :: (MonadFail m, Read t, PlayableGame t l v p m) => m t
- readTurn = do
- options <- turns
- showTurns
- liftIO $ putStr "> "
- mayTurn <- askTurn
- case mayTurn of
- Just val -> if val `elem` options then return val else readTurn
- Nothing -> readTurn
- announceWinner = do
- showBoard
- win <- winner
- liftIO $ putStrLn $ show win ++ " wins the game!"
|