It's been a challenge, and I haven't gotten as far as I would have expected. I wish the Haskell documentation, whether online or provided with the installation, was put together better. I'd like to have ready access to a language reference, not just the auto-generated library documentation; I'd like to have lots of sample code snippets; and I'd like for it to be fully searchable.
For instance: in BASIC you can write INPUT A to read an integer from the keyboard. The closest equivalent in Haskell is read but it aborts the program with an exception if the user does not type in something that can be parsed correctly. Since I was attempting to match BASIC's behavior I needed it to be able to deal more gracefully with bad input. With Google I eventually found a thread somewhere addressing this problem.
So far my program is not ending up any smaller than the original BASIC version, which surprises me a bit. I'm having difficulty deciding the best way to structure the program. I've discovered a method that emulates BASIC's goto style pretty closely, but it doesn't seem like that's necessarily a step toward readability, because the flow of control is embedded throughout the program:
main = buyLand
[choose how much land to buy with stored food]
if bought land:
[choose how much land to sell for food]
[choose how much to feed your people]
[choose how many acres to sow with seed]
[show what happened as a result of player's choices]
if game not over:
I've omitted several additional control branches: If the user inputs nonsensical numbers the original program sometimes prints a huffy message and quits.
There is a basic game state that I pass through all the functions; it contains information that needs to persist from one year of game time to the next. There are additional bits of information that flow between some stages as well. For instance the choice of how many acres to sow with seed is needed in displayYearResults but not needed after that, so I've left it out of the main game state.
Simple as Hamurabi is, I found myself needing to step back and do an even simpler game first. Here's one where the computer picks a number and the player tries to guess it:
-- Computer chooses a number; player guesses what it is
-- Example of basic I/O
minNum = 1
maxNum = 1000
main = do
targetNum <- randomRIO (minNum, maxNum)
putStrLn ("I'm thinking of a number between " ++ (show minNum) ++
" and " ++ (show maxNum) ++ ". Can you guess it?")
guess 1 targetNum
guess :: Int -> Int -> IO ()
guess totalGuesses targetNum = do
putStr ("Guess " ++ (show totalGuesses) ++ ": ")
line <- getLine
case (maybeRead line) of
Nothing -> putStrLn ("Give up? The number was " ++ (show targetNum) ++ ".")
Just guessedNum ->
if targetNum == guessedNum then
putStrLn ("You guessed it in " ++ (show totalGuesses) ++ " tries!")
guess (totalGuesses + 1) targetNum
hint = "My number is " ++ lessOrGreater ++ " than " ++ (show guessedNum) ++ "."
lessOrGreater = if targetNum < guessedNum then "less" else "greater"
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x, str)] | all isSpace str -> Just x
_ -> Nothing
This should give an idea of the level of verbosity that I'm contending with right now. Hopefully I can improve on this and come up with a clean way to structure the more complex program.