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
else return ( head line)
aW1wb3J0IERhdGEuTW9ub2lkCmltcG9ydCBEYXRhLkFycmF5CmltcG9ydCBEYXRhLkxpc3QgKGludGVyY2FsYXRlKQppbXBvcnQgRGF0YS5DaGFyIChpc0RpZ2l0LCBkaWdpdFRvSW50KQoKZGF0YSBQbGF5ZXIgPSBOb25lIHwgT25lIHwgVHdvIHwgVW5rbm93bgogICAgIGRlcml2aW5nIEVxCgppbnN0YW5jZSBTaG93IFBsYXllciB3aGVyZQogICAgc2hvdyBOb25lID0gIiAiCiAgICBzaG93IE9uZSAgPSAiWCIKICAgIHNob3cgVHdvICA9ICJPIgoKZGF0YSBCb2FyZCA9IEJvYXJkIChBcnJheSBJbnQgUGxheWVyKQoKZW1wdHlCb2FyZCA9IEJvYXJkIChsaXN0QXJyYXkgKDEsIDkpIChyZXBlYXQgTm9uZSkpCgppc0ZyZWUgKEJvYXJkIGEpIHBvcyA9CiAgICBhICEgcG9zID09IE5vbmUKCmlzRnVsbCAoQm9hcmQgYSkgPQogICAgYWxsICgvPSBOb25lKSAkIGVsZW1zIGEKCm1ha2VNb3ZlIChCb2FyZCBhKSBwbGF5ZXIgcG9zID0KICAgIEJvYXJkIChhIC8vIFsocG9zLCBwbGF5ZXIpXSkKCmluc3RhbmNlIFNob3cgQm9hcmQgd2hlcmUKICAgIHNob3cgKEJvYXJkIGEpID0gZW1iZWQnICIrLS0tKy0tLSstLS0rXG4iIChtYXAgbGluZSBbNywgNCwgMV0pIHdoZXJlCiAgICAgICAgICAgIGxpbmUgeSA9IGVtYmVkICAifCAiICIgfCAiICIgfFxuIiAobWFwIGNvbCAgWzAsIDEsIDJdKSB3aGVyZQogICAgICAgICAgICAgY29sIHggPSBzaG93ICQgYSEoeSt4KQoKZW1iZWQgZnJvbnQgbWlkZGxlIGJhY2sgeHMgPQogICAgZnJvbnQgKysgaW50ZXJjYWxhdGUgbWlkZGxlIHhzICsrIGJhY2sKCmVtYmVkJyBzZXAgPSBlbWJlZCBzZXAgc2VwIHNlcAoKd2lubmVycyBib2FyZCA9CiAgICBbKHBsYXllciwgbGluZSkgfCBsaW5lIDwtIGJvYXJkTGluZXMsCiAgICAgICAgICAgICAgICAgICAgICBsZXQgcGxheWVyID0gb3duZXIgYm9hcmQgbGluZSwKICAgICAgICAgICAgICAgICAgICAgIHBsYXllciAvPSBOb25lXQoKYm9hcmRMaW5lcyA9IFsKCiAgICBbMSwgMiwgM10sIFs0LCA1LCA2XSwgWzcsIDgsIDldLCAgIC0tIGhvcml6b250YWwKCiAgICBbMSwgNCwgN10sIFsyLCA1LCA4XSwgWzMsIDYsIDldLCAgIC0tIHZlcnRpY2FsCgogICAgWzMsIDUsIDddLCBbMSwgNSwgOV0gXSAgICAgICAgICAgICAtLSBkaWFnb25hbAoKaW5zdGFuY2UgTW9ub2lkIFBsYXllciB3aGVyZQogICAgbWVtcHR5ICAgICAgICAgICAgICAgID0gVW5rbm93bgoKICAgIHggICBgbWFwcGVuZGAgVW5rbm93biA9IHgKICAgIE9uZSBgbWFwcGVuZGAgT25lICAgICA9IE9uZQogICAgVHdvIGBtYXBwZW5kYCBUd28gICAgID0gVHdvCiAgICBfICAgYG1hcHBlbmRgIF8gICAgICAgPSBOb25lCgpvd25lciA6OiBCb2FyZCAtPiBbSW50XSAtPiBQbGF5ZXIKb3duZXIgKEJvYXJkIGEpID0gbWNvbmNhdCAuIG1hcCAoYSAhKQoKaGFzV2lubmVyIDo6IEJvYXJkIC0+IEJvb2wKaGFzV2lubmVyID0gbm90IC4gbnVsbCAuIHdpbm5lcnMKCm1haW4gPSBkbwogICAgcHJpbnQgZW1wdHlCb2FyZAogICAgZ2FtZWxvb3AgZW1wdHlCb2FyZCBPbmUgVHdvCgpnYW1lbG9vcCBib2FyZCBwbGF5ZXIgZW5lbXkgPSBkbwogICAgcG9zIDwtIGdldEZyZWVQb3NpdGlvbiBib2FyZAogICAgaWYgKHBvcyA9PSAwKQogICAgICAgIHRoZW4gcHV0U3RyTG4gImdvb2RieWUhIgogICAgICAgIGVsc2UgZXZhbHVhdGUgKG1ha2VNb3ZlIGJvYXJkIHBsYXllciBwb3MpIHBsYXllciBlbmVteQoKZXZhbHVhdGUgYm9hcmQgcGxheWVyIGVuZW15ID0gZG8KICAgIHByaW50IGJvYXJkCiAgICBpZiAoaGFzV2lubmVyIGJvYXJkKQogICAgICAgIHRoZW4gcHV0U3RyTG4gKCJwbGF5ZXIgIiArKyBzaG93IHBsYXllciArKyAiIHdpbnMhIikKICAgICAgICBlbHNlIGlmIChpc0Z1bGwgYm9hcmQpCiAgICAgICAgICAgICB0aGVuIHB1dFN0ckxuICJJdCdzIGEgdGllISIKICAgICAgICAgICAgIGVsc2UgZ2FtZWxvb3AgYm9hcmQgZW5lbXkgcGxheWVyCgpnZXRGcmVlUG9zaXRpb24gYm9hcmQgPSBkbwogICAgcG9zIDwtIGdldFBvc2l0aW9uCiAgICBpZiAocG9zID09IDAgfHwgaXNGcmVlIGJvYXJkIHBvcykKICAgICAgICB0aGVuIHJldHVybiBwb3MKICAgICAgICBlbHNlIGdldEZyZWVQb3NpdGlvbiBib2FyZAoKZ2V0UG9zaXRpb24gPSBkbwogICAgYyA8LSBnZXRDaGFyYWN0ZXIKICAgIGlmIChpc0RpZ2l0IGMpCiAgICAgICAgdGhlbiByZXR1cm4gKGRpZ2l0VG9JbnQgYykKICAgICAgICBlbHNlIGdldFBvc2l0aW9uCgpnZXRDaGFyYWN0ZXIgPSBkbwogICAgcHV0U3RyICI+ICIKICAgIGxpbmUgPC0gZ2V0TGluZQogICAgaWYgKG51bGwgbGluZSkKICAgICAgICB0aGVuIGdldENoYXJhY3RlcgogICAgICAgIGVsc2UgcmV0dXJuIChoZWFkIGxpbmUp