Your program will simulate the functioning of an early agricultural society. It is based on the ancient computer game Hammurabi, named after a Babylonian king (See en.wikipedia.org/wiki/Hammurabi) famous for his laws. He also ran a very authoritarian society in which the peasants were allocated fixed rations of food by the state.
Goal. The user, who has just become the ruler of a kingdom, wants to make a place in history by having having the largest population of peasants. The simulation will last five years or until everyone has starved.
Grain is the basic resource. Each year, ask the ruler how to use the grain in storage.
The remaining grain, if any, is saved for the next year in case of a bad harvest.
Your program should create a Kingdom with the following values: an area of 1500 acres, a population of 100, and with 4000 bushels of grain from the previous harvest.
Each person needs a minimum of 20 bushels of grain per year to survive.
Starvation. If the ruler doesn't allocate enough food for everyone, some will starve. The population is then reduced by the number of people who starved.
Immigrants if lots of food. If people receive more than 20 bushels per person, immigrants from neighboring kingdoms will be attracted, resulting in a population increase.
Formula. This simplistic idea of the size of the population can be computed by simply dividing the total amount of food by the amount needed per person.
myPopulation = food / FOOD_REQUIRED_PER_PERSON
This is inside the simulateOneYear method,
where myPopulation is an instance variable representing
the Kingdom's current population, and FOOD_REQUIRED_PER_PERSON
is a constant predefined to be 20.
For example, if the ruler allocates 2400 bushels, this will support a population of 2400 / 20, which is 120. This would become the new population.
Seed for Planting. Not all grain can be used for feeding the people. Some must be used to plant next year's crop. It takes two bushels of grain to plant an acre. To plant everything therefore requires 2 * area bushels of grain.
Harvest. There are variations in the weather each year. The yield varies from 2 to 6 bushels per planted acre. This number is randomly generated each year.
One of the main purposes of separating a program into different classes is to group data and methods that belong together and not mix the different responsibilities of the classes.
This contains the main program, which creates a new Kingdom, gets information from the user/ruler on how to allocate the grain each year, simulates one year, and display the results for that year. It should continue in a loop until the end of the simulation time period or everyone has starved. It should know nothing about how the Kingdom class functions internally; it's communication is only by means of calling a Kingdom object's methods.
There is a private utility method, displayStatus,
which is used to display the year, population, and grain of the
Kingdom that is passed to it.
The Kingdom class represents the information about a kingdom, and provides methods for getting this information. In addition, it provides a method that simulates one year given the amount of grain to be used for food and seed. The simulation updates instance variables to reflect what happened during that year. This class must not contain user interface operations -- no input / output. All interaction with the user must be in the calling program in the Hammurabi class.
This class runs the simulation and provides the user interface. It contains the main method. It creates a Kingdom object called samaria, but you can change that.
This class uses the default constructor. When you write assignments to instance variables, these will be used to initialize any objects which are created. Or these assignments could have been put in a constructor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// File : hammurabi0/Hammurabi.java
// Purpose: Starting point for working on the Hammurabi program.
// Author : Fred Swartz
// Date : 07 Oct 2005
// TODO : * Prompt for amount to feed peasants.
// * Check that there is enough grain to meet requests.
// * Also stop simulation if population is 0 (while condition)
import javax.swing.*;
public class Hammurabi {
//============================================================= main
public static void main(String[] args) {
//... Initialization
Kingdom samaria = new Kingdom(); // Create a new Kingdom
//... Run the simulation for 5 years or until everyone starves.
while (samaria.getYear() <= 5) {
//... Display state of the kingdom at beginning of each year.
JOptionPane.showMessageDialog(null, samaria.toString());
//TODO: Ask the ruler how much to feed the people.
int food = 0; // Temporary substitute for asking for input.
//.. Ask the ruler how much grain should be used for seed.
String plantStr = JOptionPane.showInputDialog(null,
"Exalted Ruler, how much of the remaining "
+ (samaria.getGrain()-food) + " bushels should be planted?");
int seeds = Integer.parseInt(plantStr);
//... TODO: Check if not enough grain for this request, Reprompt.
//... Update the food and population of this kingdom.
samaria.simulateOneYear(food, seeds);
}
//... Show final state.
JOptionPane.showMessageDialog(null, "At end " + samaria.toString());
}
}
|
There are two portions to a class: data (instance variables) and methods.
This class also has "getters" to get instance variable values.
The simulateOneYear method, which takes parameters for
how much grain to use to plant the next crop, and how much grain to feed
the population. Extend it to add population, including a getter method,
and extend the simulateOneYear method
to update the population.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// File : hammurabi0/Kingdom.java
// Purpose: Represents a "kingdom". Starting version.
// Author : Fred Swartz
// Date : 07 Oct 2005
// TODO : * Add population instance variable. Default value 100.
// * Add a getter method for population.
// * Compute the new population based on food in simulation..
class Kingdom {
//========================================================== constants
private final static int MIN_GRAIN_TO_SURVIVE = 20;
private final static int MAX_LAND_FARMABLE_PER_PERSON = 15;
private final static int SEED_REQUIRED_PER_ACRE = 2;
//================================================= instance variables
private int myGrain = 4000; // Bushels of grain in storage.
private int myArea = 1500; // Area of kingdom.in acres. Note that
// myArea isn't used yet, but will be if
// you add a check for the total amount
// of land that can be planted.
private int myYear = 0; // Years since founding of kingdom.
private int myHarvest = 0; // Last harvest in bushels.
//=========================================================== getGrain
public int getGrain() {
return myGrain;
}
//============================================================ getYear
public int getYear() {
return myYear;
}
//=========================================================== toString
public String toString() {
// TODO: Don't forget to add population here too.
return "Kingdom status at year " + myYear
+ ", last harvest = " + myHarvest
+ ", total grain = " + myGrain;
}
//==================================================== simulateOneYear
public void simulateOneYear(int food, int seed) {
//TODO: Need to calculate new population.based on food.
//... Reduce grain stockpile by amount used for food and seed
myGrain = myGrain - food - seed;
//... Calculate new harvest
// 1. How many acres can be planted with seed.
// 2. The yield per acre is random (2-6)
// 3. Harvest is yield * area planted.
int acresPlanted = seed / SEED_REQUIRED_PER_ACRE;
// TODO: Check that there are enough people and there is
// enough land to actually plant that number of
// acres.
int yieldPerAcre = 2 + (int)(5 * Math.random());
myHarvest = yieldPerAcre * acresPlanted;
//... Compute new amount of grain in storage.
myGrain += myHarvest; // New amount of grain in storage.
myYear++; // Another year has passed.
}
}
|
This above game omits some interesting aspects. After you have the simple version of the program running as described above, you might want to make it more "realistic" by adding some of the following features.