# Novice implementation of TicTacToe in Haskell

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