CISC124 Assignment 1: Translate a Python program into a Java program

Instructions

Download and extract the contents of this zip file of Python code into the assignment directory on your computer.

Download and save this eclipse project archive file but do not extract it. Copy or move the file into the assignment directory on your computer.

In eclipse, import the project archive using the following steps:

  1. Under the File menu choose Import…
  2. Under General choose Existing Projects into Workspace
  3. Click the radio button beside Select archive file and browse for the zip file A1_project.zip
  4. Make sure that the project A1 appears in the list of projects and has a check mark beside it.
  5. Click Finish to import the project.

Submitting your work

Submit your work on onq under Assignment 1. You should upload two files:

DO NOT upload a zip file containing your work. Doing so greatly slows down the processing of assignments, and you will receive a grade of zero on this assignment if you do so.

Your class should both be in the package soc (the same package that they appear in in the project file). DO NOT change the package name or remove the package name from your files.

When marking your work the teaching assistants will be looking for the following:

Brief background information: Cellular automaton and self-organized criticality

A cellular automaton consists of a regular grid of cells where each cell can be in one of a finite number of states. For example, in John Conway’s famous Conway’s Game of Life each cell is either on or off (alive or dead).

Over time, the state of a cell may change. The way that a cell changes is governed by a set of rules. Mathematicians and scientists have observed that very simple rules are capable of producing surprising behaviour. One example from the Game of Life is the Gosper gliding gun shown below (by Lucas Vieira, license):

In the above example, there are no rules that explicitly describe the pattern of cells that emerge. Instead the pattern is a result of the initial configuration of cells and the rules of The Game of Life.

Mathematicians and scientists have also observed that certain cellular automata exhibit self-organizing criticality. (Very) loosely speaking, self-organizing criticality in a cellular automaton is the phenomenon where an automaton tends to produce some sort of complex behaviour that is insensitive to the initial starting states of the cells. One example of such an automaton is the Forest-fire model.

The Forest-fire model

The Forest-fire model is a very simple model of the evolution of a forest over a long period of time. In the model, the forest is represented by a regular grid of cells where each cell is in one of three possible states:

The lifetime of a tree starts when it is born and the lifetime ends when the tree dies by burning in a forest fire. To update the state of a cell, we apply the following four rules to all cells simultaneously:

  1. a burning cell becomes empty (marking the death of a tree)
  2. a cell occupied by a living tree becomes burning if at least one nearby cell is also burning
  3. a cell occupied by a living tree is struck by lightning and becomes burning with some probability \(p_\text{ignition}\)
  4. a cell that is empty grows a tree with some probability \(p_\text{growth}\) (marking the birth of new tree)

An example of the patterns produced by the Forest-fire model are shown below:

In the animated GIF above, empty cells are shown in black, trees are shown in green, and burning cells are shown in red.

Moore neighbourhood

Rule #2 of the Forest-fire model requires examining the nearby cells of the cell being updated. There are many ways to define which cells are “near” to another cell. One of the simplest definitions is the Moore neighbourhood.

The Moore neighborhood of a cell is the \(3 \times 3\) region surrounding the cell (including the cell itself). The following image shows the Moore neighbourhood for the cell coloured blue:

For cells located on the edges of the automaton, some cells in the neighborhood are missing. In such cases, we assume the missing cells are empty (contain no tree and is not burning). In the two images below, cells colored red are cells of the neighborhood that are beyond the border of the forest and are assumed to be empty:

Representing the Forest-fire model

In Python, we can represent the state of a cell using something called an enumeration. An enumeration is a set of names that represent a set of related constants, for example:

The Forest-fire model has three states which we can implement as a Python enumeration named state like so:

# state.py
from enum import Enum

class state(Enum):
    EMPTY = 1
    OCCUPIED = 2
    BURNING = 3

Using a Python enumeration is simple: write the name of the enumeration (state) followed by a period (.) followed by the name of the constant. For example:

from state import state

s = state.EMPTY
t = state.BURNING
if s == t:
    print('equal states')
else:
    print('different states')

Each row of the Forest-fire model grid is simply a list of state values. For example:

cells = [state.EMPTY, state.EMPTY, state.EMPTY, state.OCCUPIED, state.EMPTY]

is a row of length \(5\) made up of cells that are empty, empty, empty, occupied by a tree, and empty. If there is more than one row, then we can use a list of lists, which is often called a two-dimensional list. For example:

cells = [[state.OCCUPIED, state.EMPTY,   state.OCCUPIED, state.OCCUPIED, state.OCCUPIED],
         [state.OCCUPIED, state.BURNING, state.OCCUPIED, state.OCCUPIED, state.EMPTY],
         [state.OCCUPIED, state.EMPTY,   state.OCCUPIED, state.OCCUPIED, state.OCCUPIED]]

cells refers to a list of \(3\) rows where each row has length \(5\). cells actually refers to a list where each element is a list of state values. For example, we can get the third row of cells like so:

a_row = cells[2]

To access the value of a cell, we use two indexes: A row index, r, and a column index, c:

r = 0
c = 2
val = cell[r][c]    # third element in first row
                    # cell[r] is the list representing the first row

The only burning cell in cells has a row index of 1 and a column index of 1.

Java also has enumerations, and you use them in a similar way as in Python. An enumeration in Java is a special kind of class so we name our Java enumeration State:

// State.java
package soc;

public enum State {
    EMPTY,
    OCCUPIED,
    BURNING
}
// StateDemo.java
package soc;

public class StateDemo {
    public static void main(String[] args) {
        State s = State.EMPTY;
        State t = State.BURNING;
        // print states
        System.out.println(s);      // prints EMPTY
        System.out.println(t);      // prints BURNING

        // test if states are equal
        if (s == t) {
            System.out.println("equal states");
        }
        else {
            System.out.println("different states");
        }
    }
}

In Java, we can use a two-dimensional array of State values to represent a Forest-fire grid. For example:

State[][] cells = {
    {State.OCCUPIED, State.EMPTY,   State.OCCUPIED, State.OCCUPIED, State.OCCUPIED},
    {State.OCCUPIED, State.BURNING, State.OCCUPIED, State.OCCUPIED, State.EMPTY},
    {State.OCCUPIED, State.EMPTY,   State.OCCUPIED, State.OCCUPIED, State.OCCUPIED}
};

cells refers to a two-dimensional array having \(3\) rows and \(5\) columns. cells actually refers to an array where each element is an array of State values. For example, we can get the third row of cells like so:

State[] aRow = cells[2];

To access the value of a cell, we use two indexes: A row index, r, and a column index, c:

int r = 0;
int c = 2;
boolean val = cells[r][c];    // third element in first row
                              // cells[r] is the array representing the first row

The only burning cell in cells has a row index of 1 and a column index of 1.

In Java, we can make a new two-dimensional array in the same way that we make a new one-dimensional array:

int numRows = 10;
int numCols = 8;
State[][] cells = new State[numRows][numCols];

In the above example, all of the array elements will be set to null which means that each element refers to no object. It is up to the programmer to replace each element with a State object.

We can loop over all of the elements of the array using a pair of nested loops. For example, we can set each element of cells to State.EMPTY like so:

int numRows = 10;
int numCols = 8;
State[][] cells = new State[numRows][numCols];
for (int r = 0; r < numRows; r++) {
    for (int c = 0; c < numCols; c++) {
        cells[r][c] = State.EMPTY;
    }
}

Problem statement

In the A1_python_code.zip file, you will find three files that are already complete (you should not edit these in this assignment):

state.py is a module that defines the state enumeration.

forest_fire.py is a module that contains functions that are fundamental to the Forest-fire model.

demo.py is a small Python program that calls the functions in the forest_fire module and prints the output to the terminal.

Your task is to translate the functions in forest_fire.py to their corresponding Java methods in the class ForestFire.java, and to translate demo.py into a main method in the class Demo.java.

In the Java project file that you have imported into eclipse, you will find five files:

State.java is the already completed implementation of the State enumeration.

StateDemo.java is the already completed implementation of a main method that uses the State enumeration. You might want to refer to this program for examples of how to use the State enumeration.

ForestFireIndexException.java is the already completed implementation of a class that represents an exception (see below).

ForestFire.java is the partially completed implementation of the ForestFire class.

Demo.java is the partially completed implementation of the Demo class.

Your task is to:

  1. translate the functions from forest_fire.py into the methods in ForestFire.java
  2. translate the program demo.py into the main method of Demo.java

The Python program is written in such a way that you can almost translate each line of Python into the equivalent line of Java.

Notice that the Python function names are not conventional Java method names. When you translate a function to a method, you must make sure that you use the usual Java convention for naming. For the example, the Python function:

def is_burning(cells, row, col)

must be be translated to the Java method:

public static boolean isBurning(State[][] cells, int row, int col)

(observe that the name of the method is in lower camelCase and has no underscores in it).

When you translate a function, you must not change the order of the parameters. Whoever grades your assignment will be using an already created test program that assumes you have correctly translated each Python function so that it has the expected Java method name and the parameters in the same order.

Exceptions

Many functions have certain conditions on the inputs to the function. For example, the Python function math.sqrt requires that the input to the function be a non-negative numeric value. When writing a function that has conditions on its inputs, it is often good programming practice to test if the conditions are satisfied before proceeding. If the conditions are not satisfied, then the programmer may choose to raise an exception.

In Python, raising an exception involves creating an exception object and then using the raise keyword to pass the exception object to the Python interpreter. In the module forest_fire.py you will find a few instances that resemble the following:

if some_condition:
    raise ValueError()

In the example above, a ValueError exception object is created and passed to the interpreter. There are many choices for the type of exception object, but only ValueError is used in this assignment.

A function stops running when an exception is raised. Unless the caller handles the exception, the program will stop running and an error message will be printed.

Exceptions are similar in Java except that Java uses the terminology “to throw an exception”. Where a Python function raises a ValueError, your Java method should throw a ForestFireIndexException:

if (some_condition) {
    throw new ForestFireIndexException();
}

Demo.java

After implementing ForestFire.java, you should complete the implementation of the main method in Demo.java. It should repeat all of the same steps as the Python program demo.py and should produce the following output:

number of rows = 3
number of columns = 5

index ok? = true
index ok? = false
index ok? = false

copy = [[OCCUPIED, EMPTY, OCCUPIED, OCCUPIED, OCCUPIED], [OCCUPIED, BURNING, OCCUPIED, OCCUPIED, EMPTY], [OCCUPIED, EMPTY, OCCUPIED, OCCUPIED, OCCUPIED]]

T-TTT
T#TT-
T-TTT

T-T
T#T
T-T

T--
TT-
---

is burning = false
is burning = false

T#
#-
num burning = 2

When printing a two-dimensional array, you can use the method Arrays.toDeepString to the get the string representation of the array (Arrays.toString works only for one-dimensional arrays).

You do not need to document your code for this assignment. Later assignments will have you writing proper Java documentation.

In Assignment 2, we will complete the implementation of the Forest-fire model.