Skat Engine und AI auf Haskell Basis
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

243 рядки
7.3KB

  1. {-# LANGUAGE MultiParamTypeClasses #-}
  2. {-# LANGUAGE BlockArguments #-}
  3. {-# LANGUAGE TypeSynonymInstances #-}
  4. {-# LANGUAGE FlexibleInstances #-}
  5. {-# LANGUAGE FlexibleContexts #-}
  6. {-# LANGUAGE FunctionalDependencies #-}
  7. {-# LANGUAGE TupleSections #-}
  8. {-# LANGUAGE InstanceSigs #-}
  9. {-# LANGUAGE StandaloneDeriving #-}
  10. {-# LANGUAGE ImportQualifiedPost #-}
  11. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  12. module Skat.AI.TicTacToe where
  13. import Control.Monad.State
  14. import Control.Exception (assert)
  15. import Control.Monad.Fail
  16. import Data.Ord
  17. import Text.Read (readMaybe)
  18. import Data.List (maximumBy, sortBy)
  19. import Debug.Trace
  20. import Text.Printf
  21. import Data.Maybe
  22. import qualified System.Random as Rand
  23. import Skat.AI.Base
  24. import Skat.AI.MonteCarlo
  25. import Skat.Utils
  26. -- TIC TAC TOE implementation
  27. data TicTacToe = Tic | Tac | Toe
  28. deriving (Eq, Ord)
  29. instance Show TicTacToe where
  30. show Tic = "O"
  31. show Tac = "X"
  32. show Toe = "_"
  33. data WinLossTie = Loss | Tie | Win
  34. deriving (Eq, Show, Ord)
  35. instance Value WinLossTie where
  36. invert Win = Loss
  37. invert Loss = Win
  38. invert Tie = Tie
  39. win = Win
  40. loss = Loss
  41. tie = Tie
  42. data GameState = GameState { getBoard :: [TicTacToe]
  43. , getCurrent :: Bool }
  44. deriving Show
  45. instance HasGameState Int Bool WinLossTie GameState where
  46. execute turn state = execState (play turn) state
  47. moves state = evalState turns state
  48. monteevaluate s = let b = getBoard s
  49. w = fromMaybe Toe $ ticWinner b
  50. in case w of
  51. Tac -> Win
  52. Tic -> Loss
  53. Toe -> Tie
  54. current s = evalState currentPlayer s
  55. instance Player Bool where
  56. maxing = id
  57. instance Monad m => MonadGame Int [] WinLossTie Bool (StateT GameState m) where
  58. currentPlayer = gets getCurrent
  59. turns = do
  60. o <- over
  61. if o then return [] else do
  62. board <- gets getBoard
  63. let fields = zip [0..] board
  64. return $ map fst $ filter ((==Toe) . snd) fields
  65. play turn = do
  66. env <- get
  67. let value = if getCurrent env then Tic else Tac
  68. board' = updateAt turn (getBoard env) value
  69. current' = not $ getCurrent env
  70. put $ GameState board' current'
  71. simulate turn action = do
  72. backup <- get
  73. play turn
  74. res <- action
  75. put backup
  76. return $! res
  77. evaluate = do
  78. board <- gets getBoard
  79. current <- currentPlayer
  80. let mayWinner = ticWinner board
  81. case mayWinner of
  82. Just Tic -> return $ if current then Win else Loss
  83. Just Tac -> return $ if current then Loss else Win
  84. Just Toe -> return Tie
  85. Nothing -> return Tie
  86. over = do
  87. board <- gets getBoard
  88. case ticWinner board of
  89. Just _ -> return True
  90. _ -> return False
  91. ticWinner :: [TicTacToe] -> Maybe TicTacToe
  92. ticWinner board
  93. | ticWon = Just Tic
  94. | tacWon = Just Tac
  95. | over = Just Toe
  96. | otherwise = Nothing
  97. where ticWon = hasWon $ map (==Tic) board
  98. tacWon = hasWon $ map (==Tac) board
  99. hasWon (True:_:_:True:_:_:True:_:_:[]) = True
  100. hasWon (True:_:_:_:True:_:_:_:True:[]) = True
  101. hasWon (_:True:_:_:True:_:_:True:_:[]) = True
  102. hasWon (_:_:True:_:_:True:_:_:True:[]) = True
  103. hasWon (_:_:True:_:True:_:True:_:_:[]) = True
  104. hasWon (True:True:True:_:_:_:_:_:_:[]) = True
  105. hasWon (_:_:_:True:True:True:_:_:_:[]) = True
  106. hasWon (_:_:_:_:_:_:True:True:True:[]) = True
  107. hasWon _ = False
  108. over = (length $ filter (==Toe) board) == 0
  109. -- some consts
  110. emptyBoard :: [TicTacToe]
  111. emptyBoard = [Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe]
  112. otherBoard2 :: [TicTacToe]
  113. otherBoard2 = [Tic, Tac, Toe, Tac, Tac, Tic, Tic, Toe, Toe]
  114. otherBoard3 :: [TicTacToe]
  115. otherBoard3 = [Tic, Toe, Toe, Tac, Tic, Toe, Toe, Tac, Toe]
  116. tree2 = emptytree (initGameState { getBoard = otherBoard2
  117. , getCurrent = True })
  118. tree3 = emptytree (initGameState { getBoard = otherBoard3
  119. , getCurrent = False })
  120. initGameState :: GameState
  121. initGameState = GameState { getBoard = emptyBoard
  122. , getCurrent = False }
  123. tictree :: Tree Int GameState
  124. tictree = emptytree initGameState
  125. instance Draw GameState where
  126. draw s = let b = getBoard s
  127. in printf "%s %s %s\n%s %s %s\n%s %s %s"
  128. (show $ b !! 0)
  129. (show $ b !! 1)
  130. (show $ b !! 2)
  131. (show $ b !! 3)
  132. (show $ b !! 4)
  133. (show $ b !! 5)
  134. (show $ b !! 6)
  135. (show $ b !! 7)
  136. (show $ b !! 8)
  137. otherBoard :: [TicTacToe]
  138. otherBoard = [Tic, Tac, Tac, Tic, Tac, Tic, Toe, Tic, Toe]
  139. print9x9 :: (Int -> IO ()) -> IO ()
  140. print9x9 pr = pr 0 >> pr 1 >> pr 2 >> putStrLn ""
  141. >> pr 3 >> pr 4 >> pr 5 >> putStrLn ""
  142. >> pr 6 >> pr 7 >> pr 8 >> putStrLn ""
  143. printBoard :: [TicTacToe] -> IO ()
  144. printBoard board = print9x9 pr >> putStrLn ""
  145. where pr n = putStr (show $ board !! n) >> putStr " "
  146. printOptions :: [Int] -> IO ()
  147. printOptions opts = print9x9 pr
  148. where pr n
  149. | n `elem` opts = putStr (show n) >> putStr " "
  150. | otherwise = putStr " "
  151. instance MonadIO m => PlayableGame Int [] WinLossTie Bool (StateT GameState m) where
  152. showBoard = do
  153. board <- gets getBoard
  154. liftIO $ printBoard board
  155. showTurns = turns >>= liftIO . printOptions
  156. winner = do
  157. board <- gets getBoard
  158. let win = ticWinner board
  159. case win of
  160. Just Toe -> return Nothing
  161. Just Tic -> return $ Just True
  162. Just Tac -> return $ Just False
  163. Nothing -> return Nothing
  164. askTurn = readMaybe <$> liftIO getLine
  165. showTurn _ = return ()
  166. playTicTacToe :: Int -> IO ()
  167. playTicTacToe n = void $ (flip runStateT) (GameState emptyBoard False) (playCLI n)
  168. playoften :: Int -> IO ()
  169. playoften n = mapM_ playTicTacToe [1..n]
  170. {-
  171. newtype TicMCTS a = TicMCTS (StateT GameState (State Rand.StdGen) a)
  172. deriving (Functor, Applicative, Monad, MonadState GameState)
  173. instance Choose Int TicMCTS where
  174. choose = do
  175. s <- get
  176. -}
  177. playCLI :: Int -> StateT GameState IO ()
  178. playCLI n = do
  179. gameOver <- over
  180. if gameOver
  181. then announceWinner
  182. else do
  183. current <- currentPlayer
  184. --let current = False
  185. if not current then do
  186. s <- get
  187. let tree = Leaf s False (0, 0)
  188. t = bestmove $ runmonte n (foldM (\tree _ -> montecarlo tree) tree [1..5000])
  189. put t
  190. else do
  191. showBoard
  192. t <- readTurn
  193. play t
  194. showBoard
  195. {-
  196. liftIO $ getLine
  197. -}
  198. playCLI n
  199. where
  200. readTurn :: (MonadFail m, Read t, PlayableGame t l v p m) => m t
  201. readTurn = do
  202. options <- turns
  203. showTurns
  204. liftIO $ putStr "> "
  205. mayTurn <- askTurn
  206. case mayTurn of
  207. Just val -> if val `elem` options then return val else readTurn
  208. Nothing -> readTurn
  209. announceWinner = do
  210. showBoard
  211. win <- winner
  212. liftIO $ putStrLn $ show win ++ " wins the game!"