3
\$\begingroup\$

I want to learn Haskell and figured the best way to do it is to write a simple TicTacToe implementation. However the result feels very inelegant and I think it might be the best playground for your rants and give me an opportunity to learn some lessons.

{-|
TODO: -filter symmetric states from game tree
|-}
module Tictactoe where
import Data.List
initialBoardStr = " " 
data Tree a = Node a [Tree a] deriving (Show)
type Board = String
type Points = Int
type Pos = Int
expandGameTree :: Board -> Tree Board
expandGameTree state = Node state $ succesors state
 where succesors cur_state = [Node s (succesors s) 
 | s <- indicateAllowedMoves cur_state, s /= []]
evalBoard :: Board -> Points
evalBoard board
 | win && nextSymb board == "X" = 100
 | win && nextSymb board == "O" = (-100)
 | otherwise = 0
 where win = winingBoard board
evalState :: Tree Board -> Points
evalState (Node a []) = evalBoard a
evalState (Node a (x:xs)) = (minimax a) [evalState b | b <- (x:xs)]
minimax board
 | nextSymb board == "X" = minimum
 | nextSymb board == "O" = maximum
nextMove = (makeNextMove . expandGameTree)
numerate :: Board -> Board
numerate board
 | length (findIndices (==' ') board) == 0 = board
 | otherwise = numerate $ replace board (idx board) (show (idx board)) 
 where idx board = (findIndices (==' ') board) !! 0
makeNextMove (Node a (x:xs)) = getBoardFromTree ((x:xs) !! (maxIndex (rates (x:xs))))
 where maxIndex xs = head $ filter ((== maximum xs) . (xs !!)) [0..]
 rates (x:xs) = [evalState c | c <- (x:xs)]
getBoardFromTree (Node a (x:xs)) = a
getBoardFromTree (Node a []) = a
showBoard :: Board -> String
showBoard board = "|" ++ (row 0) ++ "|\n|" ++ (row 1) ++ "|\n|" ++ (row 2) ++ "|\n"
 where numeratedBoard = numerate board
 row i = takeRange numeratedBoard [3*i..(3*i)+2]
indicateAllowedMoves :: Board -> [Board]
indicateAllowedMoves board
 | winingBoard board = [] 
 | otherwise = [replace board i symb | i <- [0..8], board !! i == ' '] 
 where symb = nextSymb board
replace :: String -> Pos -> String -> String
replace str idx char = take idx str ++ char ++ drop (idx+1) str
isAllowedMove :: Board -> Pos -> Bool 
isAllowedMove board pos = elem next allowed
 where next = insertInBoardStr board pos
 allowed = indicateAllowedMoves board
winingRow :: String -> Bool
winingRow row 
 | row == "XXX" || row == "OOO" = True
 | otherwise = False
rows :: Board -> [String]
rows board = row ++ cols ++ diag
 where row = [takeRange board [i..(i+2)] | i <- [0,3,6]]
 cols = [takeRange board $ map (+i) [0,3,6] | i <- [0..2]]
 diag = [takeRange board [0,4,8]] ++ [takeRange board [2,4,6]]
winingBoard :: Board -> Bool
winingBoard board = any winingRow $ rows board
--takeRange :: String -> [Int] -> String
takeRange str range = [str !! i | i <- range]
insertInBoardStr :: Board -> Pos -> Board
insertInBoardStr board pos = replace board pos symb
 where symb = nextSymb board
nextSymb :: Board -> String
nextSymb board 
 | countLetters board 'X' == countLetters board 'O' = "X"
 | otherwise = "O"
 where countLetters str c = length $ filter (== c) str
curSymb :: Board -> String
curSymb board
 | nextSymb board == "X" = "O"
 | otherwise = "X"
isEquivivalent :: String -> String -> Bool
isEquivivalent row1 row2
 | row1 == row2 = True
 | row1 == reverse row2 = True
 | otherwise = False
play board = do
 putStr "\ESC[2J"
 putStrLn $ showBoard board
 if (winingBoard board) == True then
 putStrLn ("Player: " ++ (curSymb board) ++ " wins")
 else
 putStrLn "Make your move: "
 input <- getLine
 let pos = (read input :: Int)
 if (isAllowedMove board pos) == True then
 play (nextMove (insertInBoardStr board pos))
 else
 play board
main = play initialBoardStr
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Nov 5, 2014 at 8:33
\$\endgroup\$
3
  • 3
    \$\begingroup\$ a different implementation of the same "problem": codereview.stackexchange.com/questions/68345/… \$\endgroup\$ Commented Nov 5, 2014 at 8:50
  • \$\begingroup\$ @Vogel612 The question which you linked to is mine, and I implemented a Tic Tac Toe game. On the other hand, OP implemented a Tic Tac Toe AI. Both are related but isn't really the same. \$\endgroup\$ Commented Nov 5, 2014 at 14:15
  • \$\begingroup\$ @wei2912 ohh carp.. I don't speak haskell... but the title and text didn't make it apparent your and his "problem" are different :( That's why I prefer to add a "What does my code do?" to questions I ask ;) Thank you for clarifying \$\endgroup\$ Commented Nov 5, 2014 at 14:18

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.