This is a follow-up question of this one: Tic Tac Toe game in Haskell
I have revised my code. How does it look now?
import System.Random (randomRIO)
import System.IO (hFlush, stdout, getLine)
import Data.List (intercalate)
import Data.Array
data Tile = EmptyTile | X | O deriving (Eq)
data Player = Player1 | Player2
showTile :: Tile -> String
showTile EmptyTile = " "
showTile X = "X"
showTile O = "O"
showBoard :: Board -> String
showBoard b = let bstr = fmap showTile b
blist = boardAsList bstr
in unlines [intercalate "|" row | row <- blist]
where
boardAsList b = [[b!(x,y) | y <- [1,2,3]] | x <- [1,2,3]]
type Board = Array (Int,Int) Tile
emptyBoard :: Board
emptyBoard = array ((1,1),(3,3)) [((x,y), EmptyTile) | x <- [1,2,3], y <- [1,2,3]]
put :: Board -> Tile -> Int -> Int -> Maybe Board
put b t x y = case b!(x,y) of
EmptyTile -> Just (b // [((x,y), t)])
_ -> Nothing
p1wins, p2wins :: Board -> Bool
p1wins b = tileWins b X
p2wins b = tileWins b O
tileWins :: Board -> Tile -> Bool
tileWins b t =
any (\row -> all (\col -> b!(row,col) == t) [1..3]) [1..3] ||
any (\col -> all (\row -> b!(row,col) == t) [1..3]) [1..3] ||
all (\rc -> b!(rc,rc) == t) [1..3] ||
all (\rc -> b!(rc,4-rc) == t) [1..3]
checkFull :: Board -> Bool
checkFull b = all (\row -> all (\col -> b!(row, col) /= EmptyTile) [1..3]) [1..3]
compMove :: Board -> IO (Board)
compMove b = do
(row, col) <- getRandomEmpty b
let (Just b') = put b O row col
return b'
getRandomEmpty :: Board -> IO (Int, Int)
getRandomEmpty b = do
col <- randomRIO (1,3)
row <- randomRIO (1,3)
case b!(row, col) of
EmptyTile -> return (row, col)
_ -> getRandomEmpty b
prompt :: String -> IO String
prompt s = do
putStr s
hFlush stdout
getLine
showTileNumbers :: String
showTileNumbers = (unlines
[intercalate "|" ["(" ++ show x ++ "," ++ show y ++ ")" |
y <- [1,2,3]] | x <- [1,2,3]])
main :: IO ()
main = do
putStrLn "This is classic tic tac toe game."
putStrLn "In order to play, you need to put a number between 0 and 8"
putStrLn "This table shows tile numbers"
putStrLn showTileNumbers
putStrLn $ showBoard emptyBoard
playTurn emptyBoard Player1
where
playTurn b Player1 = do
row <- prompt "Row: "
col <- prompt "Col: "
let newboard = put b X (read row) (read col)
case newboard of
Nothing -> do
putStrLn "Invalid move."
playTurn b Player1
Just b' -> if p1wins b' then
putStrLn "You win"
else
playTurnIfNotTie b' Player2
playTurn b Player2 = do
b' <- compMove b
putStrLn $ showBoard b'
if p2wins b' then
putStrLn "You Lose!"
else
playTurnIfNotTie b' Player1
playTurnIfNotTie b p = if checkFull b then
putStrLn "Tie."
else
playTurn b p