# My first monoid (or: Tic Tac Toe in 99 lines of Haskell)

This topic is 3123 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Recently I was pondering how to detect if a player owns a line on a Tic Tac Toe board in a high-level, functional way -- so no conditionals or boolean operators allowed, just for fun:
import Data.Monoid
import Data.Array
import Data.List (intercalate)
import Data.Char (isDigit, digitToInt)

data Player = None | One | Two | Unknown
deriving Eq

instance Show Player where
show None = " "
show One  = "X"
show Two  = "O"

data Board = Board (Array Int Player)

emptyBoard = Board (listArray (1, 9) (repeat None))

isFree (Board a) pos =
a ! pos == None

isFull (Board a) =
all (/= None) $elems a makeMove (Board a) player pos = Board (a // [(pos, player)]) instance Show Board where show (Board a) = embed' "+---+---+---+\n" (map line [7, 4, 1]) where line y = embed "| " " | " " |\n" (map col [0, 1, 2]) where col x = show$ a!(y+x)

embed front middle back xs =
front ++ intercalate middle xs ++ back

embed' sep = embed sep sep sep

winners board =
[(player, line) | line <- boardLines,
let player = owner board line,
player /= None]

boardLines = [

[1, 2, 3], [4, 5, 6], [7, 8, 9],   -- horizontal

[1, 4, 7], [2, 5, 8], [3, 6, 9],   -- vertical

[3, 5, 7], [1, 5, 9] ]             -- diagonal

instance Monoid Player where
mempty                = Unknown

x   mappend Unknown = x
One mappend One     = One
Two mappend Two     = Two
_   mappend _       = None

owner :: Board -> [Int] -> Player
owner (Board a) = mconcat . map (a !)

hasWinner :: Board -> Bool
hasWinner = not . null . winners

main = do
print emptyBoard
gameloop emptyBoard One Two

gameloop board player enemy = do
pos <- getFreePosition board
if (pos == 0)
then putStrLn "goodbye!"
else evaluate (makeMove board player pos) player enemy

evaluate board player enemy = do
print board
if (hasWinner board)
then putStrLn ("player " ++ show player ++ " wins!")
else if (isFull board)
then putStrLn "It's a tie!"
else gameloop board enemy player

getFreePosition board = do
pos <- getPosition
if (pos == 0 || isFree board pos)
then return pos
else getFreePosition board

getPosition = do
c <- getCharacter
if (isDigit c)
then return (digitToInt c)
else getPosition

getCharacter = do
putStr "> "
line <- getLine
if (null line)
then getCharacter

What do you think of the owner function? Horrible? Beautiful? An abomination?

1. 1
2. 2
3. 3
Rutin
18
4. 4
5. 5
JoeJ
13

• 14
• 10
• 25
• 9
• 57
• ### Forum Statistics

• Total Topics
632640
• Total Posts
3007618
• ### Who's Online (See full list)

There are no registered users currently online

×