A variant of game of name, played on a board comprising five numbered rows of stars initially set up as follows:
- 1:✦✦✦✦✦
- 2:✦✦✦✦
- 3:✦✦✦
- 4:✦✦
- 4:✦
Two player in bottom-up manner, two players taking turns to remove one or more stars form the end of single row. The winner is who removes the final star.
import System.IO
import Data.List
import Data.Char
For simplicity players will shown as 1 2 integers using the following function
next :: Int -> Int
next 1 = 2
next 2 = 1
Show each row as a list.
type Board = [Int]
initial :: Board
initial = [5, 4, 3, 2, 1]
finished :: Board -> Bool
finished = all (== 0)
A move in the game is specified by a row number and the number of stars to be removed, and is valid if the row contains at this many stars.
valid :: Board -> Int -> Int -> Bool
valid board row num = board !! (row-1) >=num
If valid then do the move.
move :: Board -> Int -> Int -> Board
move board row num = [update r n | (r,n) <- zip [1..] board]
where update r n = if r == row then n-num else n
-- update is a small function that takes two args "r" and "n"
-- it looks for the row number within the board
-- then subtract the number of stars given by the player
-- otherwise it will return the row value as it is "n".
We start with a function that displays a row of the board on the screen, given the row number and the number of stars remaining.
putRow :: Int -> Int -> IO ()
putRow row num = do putStr (show row)
putStr ": "
putStrLn (concat (replicate num "✦ "))
We can use `putRow` to show the board.
putBoard :: Board -> IO ()
putBoard [a,b,c,d,e] = do putRow 1 a
putRow 2 b
putRow 3 c
putRow 4 d
putRow 5 e
A function that displays a prompt and reads single character from the keyboard, if the character is digit, the corresponding integer is returned as the result value, otherwise an error message is displayed and the user prompted to enter a digit again.
getDigit :: String -> IO Int
getDigit prompt = do putStr prompt
x <- getChar
newline
if isDigit x then
return (digitToInt x)
else
do putStrLn "ERROR: Invalid digit"
getDigit prompt
Move onto new line.
newline :: IO ()
newline = putChar '\n'
Using above utilities, we can implement the main game loop.
play :: Board -> Int -> IO ()
play board player =
do newline
putBoard board
if finished board then
do newline
putStr "Player "
putStr (show (next player))
putStrLn " wins!!"
else
do newline
putStr "Player "
putStrLn (show player)
row <- getDigit "Enter a row number: "
num <- getDigit "Stars to remove : "
if valid board row num then
play (move board row num) (next player)
else
do newline
putStrLn "ERROR: Invalid move"
play board player
nim :: IO ()
nim = play initial 1