CIT 590/591 Assignment 1: Hammurabi
Fall 2012, David Matuszek

Purposes of this assignment

General idea of the assignment

Hammurabi is a very old computer game, in which you are the ruler of ancient Samaria, trying to increase the wealth of your country. Your job is to bring this program into the 21st century by writing it in Python.

Step by step instructions

Since some of you have never programmed before, this assignment is in the form of a step-by-step tutorial. Later assignments will leave more up to you.

  1. Write and test a print statement to print out the following introductory message:
    Congratulations, you are the newest ruler of ancient Samaria, elected
    for a ten year term of office. Your duties are to dispense food, direct
    farming, and buy and sell land as needed to support your people. Watch
    out for rat infestations and the plague! Grain is the general currency,
    measured in bushels. The following will help you in your decisions:
    
      * Each person needs at least 20 bushels of grain per year to survive.
    
      * Each person can farm at most 10 acres of land.
    
      * It takes 2 bushels of grain to farm an acre of land.
    
      * The market price for land fluctuates yearly.
    
    Rule wisely and you will be showered with appreciation at the end of
    your term. Rule poorly and you will be kicked out of office!
    Hint: You can put this all into a single print statement by using a so-called “triply-quoted string”; that is, a string that begins and ends with either three double quote marks or three single quote marks. Such a string can hold more than one line.
  2. Put the print statement from above into a function called print_introductory_message(). Test your function by calling it from the REPL.

    Hint: You can use Format → Indent Region to indent a block of lines.
  3. Write a function called hammurabi(). Inside this function put a call to print_introductory_message. Test your hammurabi function.
  4. Declare the following variables, which you will need in the rest of the program. Put them as the first thing inside your hammurabi function, before the call to print_introductory_message
        starved = 0
        immigrants = 5
        population = 100
        harvest = 3000          # total bushels harvested
        bushels_per_acre = 3    # amount harvested for each acre planted
        rats_ate = 200          # bushels destroyed by rats
        bushels_in_storage = 2800
        acres_owned = 1000
        cost_per_acre = 19      # each acre costs this many bushels
        plague_deaths = 0
  5. Next inside hammurabi, write a loop to print the following report ten times, with the variable year set to 1, 2, 3, ..., 10. Each of the numbers in the report (shown in red) should be the value of the corresponding variable.
    O great Hammurabi!
    You are in year 1 of your ten year rule.
    In the previous year 0 people starved to death.
    In the previous year 5 people entered the kingdom.
    The population is now 100.
    We harvested 3000 bushels at 3 bushels per acre.
    Rats destroyed 200 bushels, leaving 2800 bushels in storage.
    The city owns 1000 acres of land.
    Land is currently worth 19 bushels per acre.
    There were 0 deaths from the plague.

    The above summary represents the initial state, at the beginning of the first year--that is, when you first take office, and before you do any of the computations below). So, for example, the previous year (under a different ruler) must have started with 95 people; none starved, and 5 entered the kingdom, so as you enter office you rule 100 people.

    Since you are not yet changing the values of any of the variables, when you test the hammurabi function you should get the same report (except for the year number) printed out ten times.

That's it for most of the printing. Next, inside the "year loop," you will

  1. Use functions to ask the Great Hammurabi (the user) to make some decisions. Check each decision to see whether it is possible (give enough parameters to the function to be able to decide this). If it is not possible, ask the Great Hammurabi very, very politely (as befits his high status) for a different answer, and repeat if necessary until a legal answer is obtained.

    At the beginning of each year, ask the player for:

    Questions should be asked in this order, and the player does not get a chance to go back and change an earlier answer. A player who is buying land is not allowed to sell land during the same turn; so if the user asks to buy land, do not call the function that asks how much land to sell.

    As an example, the first function has been written for you.

    def ask_to_buy_land(bushels, cost):
        "Ask user how many bushels to spend buying land."
        acres = get_int("How many acres will you buy? ")
        while acres * cost > bushels:
            print "O great Hammurabi, we have but", bushels, "bushels of grain!"
            acres = get_int("How much land will you buy? ")
        return acres
    Each function should be given just enough information to test whether the user's answer is legal, and to return that legal answer. That answer should then be used (within the hammurabi function) to adjust the values of the variables declared in #4 above. Don't forget to test every function you write!
  2. After you collect the answers to the above questions, you need to determine the consequences, in the order specified below. Again, for each of the following, call an appropriately-named function, and use the number returned by the function to update the variables in the hammurabi function. Test each function as you go.

    Many of the functions use "random" numbers; see below for information on how to get these numbers.
    1. If there is a plague
      • Each year, there is a 15% chance of a horrible plague. When this happens, half your people die.
      • There are a couple of ways you could write this function. You could just return True 15% of the time, and False the other 85% of the time. Or, you could pass in the number of people you have, and return the number who died (or the number you have left).
      • Similar comments apply to the rest of the functions in this list.
    2. How many people starved
      • Each person needs 20 bushels of grain to survive. If you feed them more than this, the grain has been wasted.
      • If more than 45% of the people starve, you will be immediately thrown out of office (in this case, you should print a suitably nasty message), and the game ends.
    3. How many people came to the city
      • Nobody will come to the city if people are starving. If everyone is well fed, compute how many people come to the city as:
        (20 * number of acres + amount of grain in storage) / (100 * population) + 1.
    4. How good the harvest is
      • Choose a random number between 1 and 8, inclusive. Each acre that was planted with seed will yield this many bushels of grain. (Example: if you planted 50 acres, and the random number is 3, you harvest 150 bushels of grain).
    5. If you have a problem with rats
      • There is a 40% chance that you will have a rat infestation. When this happens, rats will eat somewhere between 1/10 and 3/10 of your grain.
    6. How much land will cost next year
      • The price of land is random, and ranges from 17 to 23 bushels per acre. The player will need this information in order to buy or sell land.
  3. At the end of the game (inside the hammurabi function but outside the "year loop"), use a function to print out a final summary, and to tell the player how good a job he/she did. I'll leave the details up to you, but the usual evaluation is based on how many people starved, and how many acres you end up with.

Additional information

Arithmetic

All the required arithmetic in this program can be integer. You do not need floats; an error of one or two percentage points is acceptable.

Random numbers

To get a random number generator, you first need to import random. Do this once, at the top of your program.

To get a new random number in the range a to b, including both endpoints, call random.randint(a, b). For example, to simulate the roll of a die, you could use random.randint(1, 6). To do something that happens p percent of the time, use

    if random.randint(0, 99) < p:
        # do something

Entering integers

Here's a function you can use to safely read an integer, so that the program doesn't crash if the user types in something else by mistake:

def get_int(message):
    "Print the message as prompt, and require the user to enter an integer."
    answer = None
    while answer == None:
        try:
            answer = int(input(message))
        except:
            print "Please enter a number."
    return answer

To use this code, you need to import string at the top of your program.

Program structure

The "main program" should be written as a function, and put first. Like this:

import random, string

def hammurabi():
    # This is the "main program"

# other functions go here

This structure makes it easy to test the functions as you write them. Just hit F5, then (in the shell window) try out various calls to the function you have just written. To test the entire program, enter hammurabi().

Also: If you know about "global" variables in Python, do not use them.

Grading

It would be a good idea to read How Assignments Are Graded, but here's a summary: Do exactly what the assignment says to do, use good programming style, test your program carefully, zip up your program (if it's more than a single file), and turn it in via Blackboard. Late programs will lose points.

Due date

Your program is due before .

Zipping files

"Zipping" files is a way to compress them (make them smaller). More important for this class, it is also a way to combine multiple files into a single file that can be easily uploaded and downloaded.

For this assignment (and many early assignments), your program will consist of only a single file. It is easier for everybody if you don't zip it first. But if your program consists of multiple files, Zip up the entire directory for that program, and submit the zipped file.

Important: "Zip" means the universally understood .zip format. Submissions using a proprietary format such as Microsoft's .rar will be penalized according to how much trouble they cause us.

Blackboard

All assignments are to be submitted via Blackboard.

Late penalties

Assignments will be due at 6am precisely. There will be a 10 point penalty (out of 100 points) for programs between one minute and one week late, and a 25 point penalty for programs later than one week. At some unspecified time after one week we will stop accepting assignments.