I have written a tic-tac-toe game in Haskell and was wondering if anyone could provide any hints on how it could be improved
import Data.Bits import System.IO import Data.List type Board = (Int, Int) -- the first Int is X's board, the second Int is O's board data Player = X | O deriving (Eq, Show) emptyBoard :: Board emptyBoard = (0, 0) place :: Board -> Player -> Int -> Maybe Board place (xb, ob) player pos | testBit xb pos || testBit ob pos = Nothing -- position is already occupied | player == X = Just (setBit xb pos, ob) | player == O = Just (xb, setBit ob pos) winningMasks :: [Int] winningMasks = [0o7, 0o70, 0o700, 0o111, 0o222, 0o444, 0o421, 0o124] winner :: Board -> Maybe Player winner (xb, ob) | any (\mask -> (mask .&. xb) == mask) winningMasks = Just X -- X wins | any (\mask -> (mask .&. ob) == mask) winningMasks = Just O -- O wins | otherwise = Nothing -- the game is not over yet data Score = OWins | Tie | XWins deriving (Eq, Ord, Show) minimax :: Board -> Player -> (Score, Maybe Board) minimax board player | winner board == Just X = (XWins, Nothing) | winner board == Just O = (OWins, Nothing) | popCount (fst board .|. snd board) == 9 = (Tie, Nothing) | player == X = maximum [(fst (minimax newBoard O), Just newBoard) | i <- [0..8], Just newBoard <- [place board X i]] | player == O = minimum [(fst (minimax newBoard X), Just newBoard) | i <- [0..8], Just newBoard <- [place board O i]] -- Utility functions readInt :: String -> Maybe Int readInt s = case reads s of [(x,"")] -> Just x _ -> Nothing readMove :: IO Int readMove = do putStr "Enter move (0-8): " hFlush stdout input <- getLine case readInt input of Just move | move `elem` [0..8] -> return move _ -> readMove printBoard :: Board -> IO () printBoard (xb, ob) = putStrLn $ unlines (intersperse divider [intersperse '|' [symbol (mask .&. (xb .|. ob)) | mask <- masks j] | j <- [0, 3, 6]]) where symbol 0 = ' ' symbol m = if m .&. xb /= 0 then 'X' else 'O' masks j = [1 `shiftL` (j + i) | i <- [0..2]] divider = replicate 6 '-' -- Main game loop main :: IO () main = do putStrLn "Welcome to tic-tac-toe!" putStrLn "You are X, and the computer is O." putStrLn "Enter a number between 0 and 8 to make your move." playGame emptyBoard X playGame :: Board -> Player -> IO () playGame board player = do printBoard board case winner board of Just X -> putStrLn "You win!" Just O -> putStrLn "Computer wins!" Nothing -> case player of X -> do move <- readMove case place board X move of Just newBoard -> playGame newBoard O Nothing -> do putStrLn "That square is already occupied." playGame board X O -> do case minimax board O of (score, Just newBoard) -> playGame newBoard X (score, Nothing) -> putStrLn "Game ends in a tie."