Java Project: Mice Fill Central Park

Tips :: JavaJava program that calculates the time it would take mice to multiply and cover every inch of Central Park.
This project was created as part of a Java programming 101 course in the fall semester of 2012. The project and tutorial can help Java newbies get a sense of some of the basic conditions, loops, and mathematical functions.

 

Copying this project in full or in part is plagiarism. I share this code as a sample, to help you understand and learn. If you use any of this code, you should maintain the attribution to the original author (me!) along with a link to this page.

The Exercise

The original project exercise requested to create a program that calculates the time it would take to have enough mice cover every corner of central park. It allows for the existence of cats, who, of course, eat mice at a certain rate.

Algorithm Description

As with most codes, this too can be done in many different ways. I chose to use ‘BigFloat’ (which is a java library) to have the ability to present large numbers with many decimal points out of my love for accurate pure math. However, this should work just as well using floats. It won’t be as delightfully accurate to the umpteenth floating point, though.

The program is divided into two files:

  • SpeciesGrowth.java Class Defines the species objects and the method to define growth per a certain length of time.
  • MiceFillCentralPark.java Sets up the program, requests input from the user, and refers to the SpeciesGrowth class to calculate the desired result.

Algorithm Flowchart

This flowchart represents the conceptual operation of the program:

Algorithm Flowchart, Click to Enlarge

Algorithm Flowchart, Click to Enlarge

The SpeciesGrowth Class

This class defines the objects that will create the Mice and Cats, and holds methods for growth factors per certain length of time.

Object Methods

  • growPopulation (int)
    • Description: Calculates the new population number based on the growth factor and number of iterations. If “saveTheValue” is set to false, the value will not be saved in the population property. Otherwise, the value is automatically set into population property for the object.
    • Expected Input: numWeeks (int); saveTheValue
    • Output: New population number (int).
  • setPopulation (void)
    • Description: sets the population number for the object. Reverts to 0 on negative values.
    • Expected Input: popAmount (BigDecimal)
    • Output: void.
  • setSpecies
    • Description: sets the species name for the object.
    • Expected Input: specName (String)
    • Output: void.
  • setGrowthRate
    • Description: sets the growth rate for the object.
    • Expected Input: rate (double)
    • Output: void.
  • getSpeciesName
    • Description: retrieves the species name set for this object.
    • Expected Input: none.
    • Output: String.
  • getPopulation
    • Description: retrieves the current population number for the object.
    • Expected Input: none.
    • Output: BigDecimal.
  • getGrowthRate
    • Description: retrieves the growth rate that is set for this object.
    • Expected Input: none.
    • Output: double.

MiceFillCentralPark

This program reads user input for the initial number of mice and cats, their growth rates, and how many mice each cat eats per week. Then, it calculates the number of mice and number of cats per week, taking into account the number of eaten mice. Every year, it calculates the new population based on the growth rate. The program then displays the result to the user.

Class Methods

I later created a standalone user validation class for Java programs that contains the validation and user input request loops below. You can go over that class here.
  • askForInputDouble (double)
    • Description: Repeatedly asks the user for input, verifies the input is valid, and only returns the value when it is valid.
    • Expected Input: inputQuestion (String)
    • Output: The inserted valid value is returned as a double.
  • askForInputInt (int)
    • Description: Repeatedly asks the user for input, verifies the input is valid, and only returns the value when it is valid.
    • Expected Input: inputQuestion (String)
    • Output: The inserted valid value is returned as integer.
  • processValue (Boolean)
    • Description: retrieves the species name set for this object.
    • Expected Input: value (String); type (String).
    • Output: Boolean.

The Program Itself

Now that we have our object classes up and running, it’s time to work on the actual program. We start by presenting the user an introduction:

/** Print intro **/
System.out.println("Welcome to 'Mice Fill Central Park'.");
System.out.println("====================================");
System.out.println();

And then set up the species objects for both cats and mice:

/** Set up objects **/
SpeciesGrowth Mice = new SpeciesGrowth("Mice");
SpeciesGrowth Cats = new SpeciesGrowth("Cats");

The program proceeds to request an input for three variables: initial number of mice, growth rate for mice per year, and initial number of cats. If the user chose to have some number of cats that is not zero, the program will ask for the growth rate of cats per year and the number of mice each cat eats per week.

It’s worth noting that there is a difference in timing between the number of cats eaten (per week) and the growth rate of both populations (per year). This made the calculations a bit more elaborate (though much more fun, too, as you’ll see later in the code.)

initMice = askForInputInt("How many mice would you like to start with?");
miceGrowthRate = askForInputDouble("What is the percentage of growth of mice per year?");
initCats = askForInputInt("How many cats would you like to start with?");

if (initCats > 0) {
	catsGrowthRate = askForInputDouble("What is the percentage of growth of cats per year?");
	miceEatenPerWeek = askForInputInt("How many mice would each cat eat per week?");
} else {
	System.out.println("There are no cats.");
}

The inputs are requested by invoking the askForInputInt and askForInputDouble methods, which verify the validity of the input and return its numerical value. They will continue asking for input from the user until the input is found to be valid. (For an updated version of these inputs as a standalone validation class, check out my Java User Input Validation tutorial)

Setting up the Calculation (or: Math! AAAA!)

If you dislike math, and the ‘or’ applies to you, the first step to take here is taking a breath and following the good ol’ concept: Don’t Panic.

Whatever initial values were given, the final result will involve large numbers that cannot be stored in an Integer type. As a result, the code will deal with BigDecimals to make sure the mathematical calculations can be processed. (As mentioned above, you should also be able to achieve this by using floats, but I chose to be messier for the sake of better accuracy)

initMice and initCats are transformed into BigDecimal values, and stored in the objects:

Mice.setPopulation(iMice);
Mice.setGrowthRate(miceGrowthRate);
Cats.setPopulation(iCats);
Cats.setGrowthRate(catsGrowthRate);

After collecting the inputs the program proceeds in a ‘do’ loop to calculate the eaten mice per week, and growth of the populations of mice and cats per year.

The loop is fairly simple. In each iteration, the area made by the number of mice is compared to the area of Central Park. The loop will proceed as long as the area of the mice is smaller than that of Central Park, or until all mice were eaten by cats, and there are no more mice left:

do {
	...
	/* code */
	...
} while ((miceArea.compareTo(BigDecimal.valueOf(AREA_OF_CENTRAL_PARK)) < 0 && (micePopulation.compareTo(BigDecimal.ZERO) > 0)));

The Calculation Loop

Since each cat eats certain amount of mice per week, and yet both population growth happen per year, it’s more than reasonable that there will be no more mice left to increase after 52 weeks of being eaten by cats. The code must continuously make sure there are mice left, and also that the number of mice do not go over the area allowed.

Each loop iteration represents one week. It begins by retrieving the current number of mice and cats, and uses that data to calculate how many mice were eaten this week:

micePopulation = Mice.getPopulation();
catsPopulation = Cats.getPopulation();
BigDecimal miceDeaths = catsPopulation.multiply(miceEatenPerCat);
micePopulation = micePopulation.subtract(miceDeaths);

Mice.setPopulation(micePopulation);

Then, it calculates the current area made by the new population of mice:

miceArea = BigDecimal.valueOf(AREA_OF_ONE_MOUSE).multiply(micePopulation);

This miceArea variable is the one used by the loop to decide if another iteration should happen.

Now we must account for population growth. The above code is run every week, but we must also make sure that the populations of mice and cats grow exponentially once a year, based on their prospective growth factors.

We do this by checking if the number of weeks is divisible by 52, as there are 52 weeks per year:

if (weeks % 52 == 0) { 
	/* code */		
}

Early in the programming process I realized another potential issue: since the population growth is exponential, and my program checks this growth once a year, there may be a good chance that the area created by the mice will suddenly grow beyond that of Central Park.

There are two ways of solving this issue. First, you can redo the calculation loop to calculate the relative growth of mice and cats per week (and that is definitely possible, but requires another calculation) or, you can do what I did, which was first check if the next year’s population is acceptable, and if it is not, ignore it and count up to the current year. It might be a cop-out, but considering I went above and beyond the requirements of the initial project, I deemed it acceptable. If you want to practice your skills, you can try adjusting the loop to do the first suggested solution.

So, back to the calculation of species growth. The cat population will grow regardless (they don’t have natural predators in this program) but the mice population will first be tested, so we insert it to temporary variables called “testPopulation” and “testPopArea“:

Cats.growPopulation(1, true);
BigDecimal testPopulation, testPopArea;
testPopulation = Mice.growPopulation(1, false);
testPopArea = testPopulation.multiply(BigDecimal.valueOf(AREA_OF_ONE_MOUSE));

If the mice population after the yearly increase is larger than the area of central park, the code will ignore the new increase and exit to show the output. If it’s smaller than central park, the code will save the new population into the Mice object and continue counting.

/* Test to see if population is too big */
if (testPopArea.compareTo(BigDecimal.valueOf(AREA_OF_CENTRAL_PARK)) > 0) {
	//too big. break!
	break;
} else {
	//good. Set it:
	Mice.setPopulation(testPopulation);
}

See? Problem solved. Not ideal, but hey – it works.

The loop will now check the value of miceArea  and micePopulation. If miceArea is bigger or equals to the area of Central Park, or if micePopulation is zero (no more mice) the loop will end.

Printing the Result

There are two options for a result – either there were enough mice left to cover Central Park, or all mice were eaten. The final section of the code checks for exactly that, and returns the proper response:

BigDecimal newPop = Mice.getPopulation();
System.out.println();
System.out.println("Result:");
/** Display output **/
if (newPop.compareTo(BigDecimal.ZERO) > 0) {
	//BigInteger BigMiceArea = miceArea.toBigInteger();
	System.out.println("After " + weeks + " weeks (about " + years + " years), there will be ");
	System.out.println(Mice.getPopulation().toBigInteger() + " mice that will fill up central park.");
	System.out.println("The mice take an area of " + miceArea + " sq feet, out of " + AREA_OF_CENTRAL_PARK + " sq feet of Central Park.");
	System.out.println("There are " + Cats.getPopulation().toBigInteger() + " cats.");
} else {
	//mice were eaten by cats!
	System.out.println("Could not fill up Central Park, because");
	System.out.println("all mice were eaten within " + weeks + " weeks.");
	System.out.println("At that time, there were "+ Cats.getPopulation().toBigInteger() + " cats.");
}

And there you have it. Either mice covered Central Park, or there were lots of really fat cats.

Output

Welcome to ‘Mice Fill Central Park’.

====================================

How many mice would you like to start with?

1000

What is the percentage of growth of mice per year?

50

How many cats would you like to start with?

1

What is the percentage of growth of cats per year?

100

How many mice would each cat eat per week?

1

Calculating…

Result:

Could not fill up Central Park, because all mice were eaten within 319 weeks. At that time, there were 128 cats.

Done.

Or, another option:

Welcome to ‘Mice Fill Central Park’.

====================================

How many mice would you like to start with?

10000

What is the percentage of growth of mice per year?

50

How many cats would you like to start with?

5

What is the percentage of growth of cats per year?

10

How many mice would each cat eat per week?

1

Calculating…

Result:

After 1924 weeks (about 37 years), there will be 30403592887 mice that will fill up central park.

The mice take an area of 30403592.887sq feet of Central Park. There are 187 cats.

The Full Code

Please note: You are free to use this class in your code, as long as you keep the attribution and copyright notice. If you improve it, send it back to me and I’ll make sure to credit your improvements in the live version!

SpeciesGrowth.java

package MiceInCentralPark;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**  
 * Class defining species and their growth and decay over period of time.
 * @author Moriel Schottlender
 * @version 1.0
 * 
 */
public class SpeciesGrowth {

	private static Logger log;

	private String speciesName = "";
	private BigDecimal population = BigDecimal.ZERO;
	private double growthRate = 0;
	private static BigDecimal eatenMice = BigDecimal.ZERO;

	/** The Constructor will create the species with its designated name.
	 * 
	 * @param name	The name of the species.
	 * 
	 */
	public SpeciesGrowth(String name) {
		this.speciesName = name;
	}

	/** A method to return the expected population after a certain amount of weeks
	 * based on the initial population, the number of weeks and the growth rate.
	 * The method either saves the value in the population number of the object, 
	 * or simply returns it as a means of testing.
	 * 
	 * @param numWeeks		The number of weeks the population growth is factored for.
	 * @param saveTheValue	Determines whether or not the new population value is saved
	 * 						in the population count of the object. 'True' will save the
	 * 						new value in the object's population, and 'False' will just
	 * 						return the value of the projected population without saving
	 * 						it.
	 * @return				The number of projected population, in BigDecimal format.
	 * 						The population is preserved as BigDecimal to allow for incremental
	 * 						addition of members. The final result for the amount of individuals
	 * 						should be converted to integers to present real-life whole value.
	 * 
	 */
	public BigDecimal growPopulation(int numWeeks, Boolean saveTheValue) {
		int weekcounter = 0;
		BigDecimal newPopulation = this.getPopulation();
		BigDecimal grate = new BigDecimal(this.growthRate/100);
		for (int i=1; i <= numWeeks; i++) {
			newPopulation = newPopulation.multiply(grate);
			newPopulation.add(this.population);
			weekcounter++;
		}
		if (saveTheValue == true) {
			this.setPopulation(this.population.add(newPopulation));
		} 
		return newPopulation.add(this.population);
	}

	/** Defines the number of individuals in the species' population.
	 * This method will reject negative values.
	 * 
	 * @param popAmount	The current number of individuals in the population.
	 */
	public void setPopulation(BigDecimal popAmount) {
		if (popAmount.compareTo(BigDecimal.ZERO) >= 0) { //bigger or equal to zero
			this.population = popAmount;
		} else {
			this.population = BigDecimal.ZERO;
			log.log(Level.WARNING, this.speciesName + " population cannot be negative.");
		}
	}

	/** Sets the value for the species name.
	 * @param specName	The species name.
	 */
	public void setSpecies(String specName) {
		this.speciesName = specName;
	}

	/** Sets the value for the growth rate per interval.
	 * @param rate	The growth rate (double).
	 */
	public void setGrowthRate(double rate) {
		this.growthRate = rate;
	}

	/** Retrieves the species name.
	 * @return The name that was declared for this object's species.
	 */
	public String getSpeciesName() {
		return this.speciesName;
	}

	/** Retrieves the species' population at the given moment.
	 * @return The number of individuals that belong to this species.
	 */
	public BigDecimal getPopulation() {
		return this.population;
	}

	/** Retrieves the species' growth rate.
	 * @return The growth rate that was declared for this object's species.
	 */
	public double getGrowthRate() {
		return this.growthRate;
	}

}

 

MiceFillCentralPark.java

package MiceInCentralPark;

import java.math.BigDecimal;
import java.util.Scanner;

public class MiceFillCentralPark {

	public static final int AREA_OF_CENTRAL_PARK = 36715900; // square feet
	public static final double AREA_OF_ONE_MOUSE = 0.001; // cubic feet
	public static final Scanner keyboard = new Scanner (System.in);

	public static void main(String[] args) {
		/** Print intro **/
		System.out.println("Welcome to 'Mice Fill Central Park'.");
		System.out.println("====================================");
		System.out.println();

		/** Set up objects **/
		SpeciesGrowth Mice = new SpeciesGrowth("Mice");
		SpeciesGrowth Cats = new SpeciesGrowth("Cats");

		/** Ask for input **/
		int initMice = 0, initCats = 0;
		double miceGrowthRate = 0;
		double catsGrowthRate = 0;
		int miceEatenPerWeek = 0;
		String strInitMice ="0", strMiceGRate ="0", strInitCats="0", strCatsGRate="0", strMiceEaten="0";

		initMice = askForInputInt("How many mice would you like to start with?");
		miceGrowthRate = askForInputDouble("What is the percentage of growth of mice per year?");
		initCats = askForInputInt("How many cats would you like to start with?");

		if (initCats > 0) {
			catsGrowthRate = askForInputDouble("What is the percentage of growth of cats per year?");
			miceEatenPerWeek = askForInputInt("How many mice would each cat eat per week?");

		} else {
			System.out.println("There are no cats.");
		}

		System.out.println();
		System.out.println("Calculating...");

		/** Update Objects **/
		BigDecimal iMice = BigDecimal.valueOf(initMice);
		BigDecimal iCats = BigDecimal.valueOf(initCats);

		Mice.setPopulation(iMice);
		Mice.setGrowthRate(miceGrowthRate);

		Cats.setPopulation(iCats);
		Cats.setGrowthRate(catsGrowthRate);

		/** Calculate output **/
		BigDecimal micePopulation; 
		BigDecimal catsPopulation;
		BigDecimal miceArea; 

		int weeks = 0, years = 0, weeksadded = 0;
		BigDecimal miceEatenPerCat = BigDecimal.valueOf(miceEatenPerWeek); 

		do {
			micePopulation = Mice.getPopulation();
			catsPopulation = Cats.getPopulation();
			BigDecimal miceDeaths = catsPopulation.multiply(miceEatenPerCat);
			micePopulation = micePopulation.subtract(miceDeaths);

			Mice.setPopulation(micePopulation);

			miceArea = BigDecimal.valueOf(AREA_OF_ONE_MOUSE).multiply(micePopulation);

			/* Only grow the population every year */
			if (weeks % 52 == 0) { 
				//A year passed!
				Cats.growPopulation(1, true);
				BigDecimal testPopulation, testPopArea;
				testPopulation = Mice.growPopulation(1, false);
				testPopArea = testPopulation.multiply(BigDecimal.valueOf(AREA_OF_ONE_MOUSE));

				/* Test to see if population is too big */
				if (testPopArea.compareTo(BigDecimal.valueOf(AREA_OF_CENTRAL_PARK)) > 0) {
					//too big. break!
					break;
				} else {
					//good. Set it:
					Mice.setPopulation(testPopulation);
				}
				years++;

			}

			weeks++;
			/* code */
		} while ((miceArea.compareTo(BigDecimal.valueOf(AREA_OF_CENTRAL_PARK)) < 0 && (micePopulation.compareTo(BigDecimal.ZERO) > 0)));

		BigDecimal newPop = Mice.getPopulation();
		System.out.println();
		System.out.println("Result:");
		/** Display output **/
		if (newPop.compareTo(BigDecimal.ZERO) > 0) {
			//BigInteger BigMiceArea = miceArea.toBigInteger();
			System.out.println("After " + weeks + " weeks (about " + years + " years), there will be ");
			System.out.println(Mice.getPopulation().toBigInteger() + " mice that will fill up central park.");
			System.out.println("The mice take an area of " + miceArea + " sq feet, out of " + AREA_OF_CENTRAL_PARK + " sq feet of Central Park.");
			System.out.println("There are " + Cats.getPopulation().toBigInteger() + " cats.");
		} else {
			//mice were eaten by cats!
			System.out.println("Could not fill up Central Park, because");
			System.out.println("all mice were eaten within " + weeks + " weeks.");
			System.out.println("At that time, there were "+ Cats.getPopulation().toBigInteger() + " cats.");
		}

		System.out.println("Done.");

		keyboard.close();
	}

	/** Ask the user for input. Repeat asking until input is valid.
	 * 
	 */
	public static double askForInputDouble(String inputQuestion) {
		String strInitInput = "";
		Boolean error = false;
		do {
			System.out.println(inputQuestion);
			strInitInput = keyboard.nextLine();
			error = processValue(strInitInput, "double");
		} while (error == true);
		if (strInitInput.length() > 0) {
			return Double.parseDouble(strInitInput);
		} else {
			return 0;
		}
	}

	public static int askForInputInt(String inputQuestion) {
		String strInitInput = "";
		Boolean error = false;
		do {
			System.out.println(inputQuestion);
			strInitInput = keyboard.nextLine();
			error = processValue(strInitInput, "int");
		} while (error == true);

		if (strInitInput.length() > 0) {
			return Integer.parseInt(strInitInput);
		} else {
			return 0;
		}
	}

	/** Process input value and check if it returns a valid value.
	 * 
	 * @param value 	The literal input from the user.
	 * @param type 		The type of value to test against: int or double
	 * @return boolean 	Returns error status. False if the value is valid (no errors) 
	 * 					and true if there were errors found.
	 */
	public static Boolean processValue(String value, String type) {
		if (type.equalsIgnoreCase("int")) {
			try {
				int intVal = Integer.parseInt(value);
				if (intVal < 0) {
					System.err.println("ERROR: Value must be a positive whole number.");
					return true;
				}
				return false;
			} catch (Exception x) {
				System.err.println("ERROR: Value must be a positive whole number.");
				return true;
			}
		} else if (type.equalsIgnoreCase("double")) {
				try {
					double intVal = Double.parseDouble(value);
					if (intVal < 0) {
						System.err.println("ERROR: Value must be a positive decimal.");
						return true;
					}
					return false;
				} catch (Exception x) {
					System.err.println("ERROR: Value must be a positive whole number.");
					return true;
				}
		}
		return true;
	}
}

 

Tags: ,

Trackback from your site.