patternMinor
Tic Tac Toe game in Haskell
Viewed 0 times
toetictacgamehaskell
Problem
```
import System.Random (randomRIO)
import System.IO (hFlush, stdout, getLine)
data Tile = EmptyTile | X | O
data Player = Player1 | Player2
instance Show Tile where
show EmptyTile = " "
show X = "X"
show O = "O"
type Board = (Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile)
emptyBoard :: Board
emptyBoard = (EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile)
put :: Board -> Tile -> Int -> Maybe Board
put (EmptyTile,b,c,d,e,f,g,h,i) t 0 = Just (t,b,c,d,e,f,g,h,i)
put (a,EmptyTile,c,d,e,f,g,h,i) t 1 = Just (a,t,c,d,e,f,g,h,i)
put (a,b,EmptyTile,d,e,f,g,h,i) t 2 = Just (a,b,t,d,e,f,g,h,i)
put (a,b,c,EmptyTile,e,f,g,h,i) t 3 = Just (a,b,c,t,e,f,g,h,i)
put (a,b,c,d,EmptyTile,f,g,h,i) t 4 = Just (a,b,c,d,t,f,g,h,i)
put (a,b,c,d,e,EmptyTile,g,h,i) t 5 = Just (a,b,c,d,e,t,g,h,i)
put (a,b,c,d,e,f,EmptyTile,h,i) t 6 = Just (a,b,c,d,e,f,t,h,i)
put (a,b,c,d,e,f,g,EmptyTile,i) t 7 = Just (a,b,c,d,e,f,g,t,i)
put (a,b,c,d,e,f,g,h,EmptyTile) t 8 = Just (a,b,c,d,e,f,g,h,t)
put _ _ _ = Nothing
checkWinner :: Board -> Maybe Player
checkWinner (X,X,X,_,_,_,_,_,_) = Just Player1
checkWinner (_,_,_,X,X,X,_,_,_) = Just Player1
checkWinner (_,_,_,_,_,_,X,X,X) = Just Player1
checkWinner (X,_,_,X,_,_,X,_,_) = Just Player1
checkWinner (_,X,_,_,X,_,_,X,_) = Just Player1
checkWinner (_,_,X,_,_,X,_,_,X) = Just Player1
checkWinner (X,_,_,_,X,_,_,_,X) = Just Player1
checkWinner (_,_,X,_,X,_,X,_,_) = Just Player1
checkWinner (O,O,O,_,_,_,_,_,_) = Just Player2
checkWinner (_,_,_,O,O,O,_,_,_) = Just Player2
checkWinner (O,_,_,O,_,_,O,_,_) = Just Player2
checkWinner (_,O,_,_,O,_,_,O,_) = Just Player2
checkWinner (_,_,O,_,_,O,_,_,O) = Just Player2
checkWinner (_,_,_,_,_,_,O,O,O) = Just Player2
checkWinner (O,_,_,_,O,_,_,_,O) = Just Player2
checkWinner (_,_,O,_,O,_,O,_,_) = Just Player2
checkWinner _ = Nothing
checkFull :: Board -> Bool
checkFull (EmptyTile,_,_,_,_,_,_,_,_) = False
checkFull (_,EmptyTile,_,_,_,_,_,_,_)
import System.Random (randomRIO)
import System.IO (hFlush, stdout, getLine)
data Tile = EmptyTile | X | O
data Player = Player1 | Player2
instance Show Tile where
show EmptyTile = " "
show X = "X"
show O = "O"
type Board = (Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile)
emptyBoard :: Board
emptyBoard = (EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile,EmptyTile)
put :: Board -> Tile -> Int -> Maybe Board
put (EmptyTile,b,c,d,e,f,g,h,i) t 0 = Just (t,b,c,d,e,f,g,h,i)
put (a,EmptyTile,c,d,e,f,g,h,i) t 1 = Just (a,t,c,d,e,f,g,h,i)
put (a,b,EmptyTile,d,e,f,g,h,i) t 2 = Just (a,b,t,d,e,f,g,h,i)
put (a,b,c,EmptyTile,e,f,g,h,i) t 3 = Just (a,b,c,t,e,f,g,h,i)
put (a,b,c,d,EmptyTile,f,g,h,i) t 4 = Just (a,b,c,d,t,f,g,h,i)
put (a,b,c,d,e,EmptyTile,g,h,i) t 5 = Just (a,b,c,d,e,t,g,h,i)
put (a,b,c,d,e,f,EmptyTile,h,i) t 6 = Just (a,b,c,d,e,f,t,h,i)
put (a,b,c,d,e,f,g,EmptyTile,i) t 7 = Just (a,b,c,d,e,f,g,t,i)
put (a,b,c,d,e,f,g,h,EmptyTile) t 8 = Just (a,b,c,d,e,f,g,h,t)
put _ _ _ = Nothing
checkWinner :: Board -> Maybe Player
checkWinner (X,X,X,_,_,_,_,_,_) = Just Player1
checkWinner (_,_,_,X,X,X,_,_,_) = Just Player1
checkWinner (_,_,_,_,_,_,X,X,X) = Just Player1
checkWinner (X,_,_,X,_,_,X,_,_) = Just Player1
checkWinner (_,X,_,_,X,_,_,X,_) = Just Player1
checkWinner (_,_,X,_,_,X,_,_,X) = Just Player1
checkWinner (X,_,_,_,X,_,_,_,X) = Just Player1
checkWinner (_,_,X,_,X,_,X,_,_) = Just Player1
checkWinner (O,O,O,_,_,_,_,_,_) = Just Player2
checkWinner (_,_,_,O,O,O,_,_,_) = Just Player2
checkWinner (O,_,_,O,_,_,O,_,_) = Just Player2
checkWinner (_,O,_,_,O,_,_,O,_) = Just Player2
checkWinner (_,_,O,_,_,O,_,_,O) = Just Player2
checkWinner (_,_,_,_,_,_,O,O,O) = Just Player2
checkWinner (O,_,_,_,O,_,_,_,O) = Just Player2
checkWinner (_,_,O,_,O,_,O,_,_) = Just Player2
checkWinner _ = Nothing
checkFull :: Board -> Bool
checkFull (EmptyTile,_,_,_,_,_,_,_,_) = False
checkFull (_,EmptyTile,_,_,_,_,_,_,_)
Solution
I have only written Haskell once before, and then I did not write anything nearly as complicated as Tic Tac Toe. I can only give you certain suggestions, I am not able to tell you exactly how to do it in Haskell.
The type
In most of the code I write, I try to write it as flexible as possible. Here you have hard-coded nine tiles. What if you wanted to make a 4x4 Tic Tac Toe game instead? Or a 5x5? Or a NxN and let the user decide the size?
Use a 2-dimensional array instead.
put
Again, if using a 2D-array, you should be able to loop through all the tiles here and check the put for each position. Or better yet, as the
checkWinner
First of all, this contains duplicated logic. Once for
Additionally, there's a little trick that can be used when checking for winners in Tic-Tac-Toe. The idea is to start at a certain position, and then loop and change the row and column indices by 1 and check the next position, repeatedly until you go outside of the board range.
checkFull
Again, use a for-loop.
getTile
Again, use an array and grab a specific index.
The last part of your code
That's a lot of indentation! A looooot of indentation! Try to find a way to reduce that. Put more if-statements on the same indentation level? Switch if's with else's? Sorry I can't provide detailed help here. All I can say is: Yes, that is a code smell.
The type
type Board = (Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile)In most of the code I write, I try to write it as flexible as possible. Here you have hard-coded nine tiles. What if you wanted to make a 4x4 Tic Tac Toe game instead? Or a 5x5? Or a NxN and let the user decide the size?
Use a 2-dimensional array instead.
put
put :: Board -> Tile -> Int -> Maybe Board
put (EmptyTile,b,c,d,e,f,g,h,i) t 0 = Just (t,b,c,d,e,f,g,h,i)
put (a,EmptyTile,c,d,e,f,g,h,i) t 1 = Just (a,t,c,d,e,f,g,h,i)
put (a,b,EmptyTile,d,e,f,g,h,i) t 2 = Just (a,b,t,d,e,f,g,h,i)
...
put (a,b,c,d,e,f,g,EmptyTile,i) t 7 = Just (a,b,c,d,e,f,g,t,i)
put (a,b,c,d,e,f,g,h,EmptyTile) t 8 = Just (a,b,c,d,e,f,g,h,t)
put _ _ _ = NothingAgain, if using a 2D-array, you should be able to loop through all the tiles here and check the put for each position. Or better yet, as the
Int tells you exactly what index to change, you should only change the value on that specific index. Study up on arrays in Haskell and you should be able to make it better by only changing one index in the array.checkWinner
First of all, this contains duplicated logic. Once for
X and once for Y, that part can be extracted.Additionally, there's a little trick that can be used when checking for winners in Tic-Tac-Toe. The idea is to start at a certain position, and then loop and change the row and column indices by 1 and check the next position, repeatedly until you go outside of the board range.
checkFull
Again, use a for-loop.
getTile
Again, use an array and grab a specific index.
The last part of your code
That's a lot of indentation! A looooot of indentation! Try to find a way to reduce that. Put more if-statements on the same indentation level? Switch if's with else's? Sorry I can't provide detailed help here. All I can say is: Yes, that is a code smell.
Code Snippets
type Board = (Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile, Tile)put :: Board -> Tile -> Int -> Maybe Board
put (EmptyTile,b,c,d,e,f,g,h,i) t 0 = Just (t,b,c,d,e,f,g,h,i)
put (a,EmptyTile,c,d,e,f,g,h,i) t 1 = Just (a,t,c,d,e,f,g,h,i)
put (a,b,EmptyTile,d,e,f,g,h,i) t 2 = Just (a,b,t,d,e,f,g,h,i)
...
put (a,b,c,d,e,f,g,EmptyTile,i) t 7 = Just (a,b,c,d,e,f,g,t,i)
put (a,b,c,d,e,f,g,h,EmptyTile) t 8 = Just (a,b,c,d,e,f,g,h,t)
put _ _ _ = NothingContext
StackExchange Code Review Q#88497, answer score: 5
Revisions (0)
No revisions yet.