HugLife: An exercise in unit testing creatures inhabiting a 2D universe

Overview

In this assignment, students are given a framework for simulating creatures in a 2D space. Students are given a default Creature class, and each Creature class must provide the following methods (in addition to other methods not listed below that are inherited by all Creatures):

The methods are intended to make changes only to the internal state of each creature, and not the outside world. For example, the move method for the "Plip" creature, a mostly sessile photosynthetic creature, might look something like the method below. This code indicates that if a Plip stays, it gains 0.2 units of energy (from photosynthesis), up to a maximum energy level of 2.

    public void stay() {
        if (energy + 0.2 > 2) {
            energy = 2;
        } else {
            energy += 0.2;
        }
    }

An example of what the system looks like when it is running correctly can be found at this youtube link.

Beyond implentation of the creature classes above, there is a significant emphasis on writing unit tests for the creatures. In my version of the assignment, we used JUnit, though a more ad hoc testing approach would be reasonable as well. An example of such a test is given below. In this test, a carnivorous Clorus is created and then told that there is a Plip to the right, and the test ensures that the Clorus correctly wants to attack the Plip.

    @Test
    public void testChooseAttack() {        
        HashMap worldWithPlipToTheRight = new HashMap();
        worldWithPlipToTheRight.put(Direction.TOP, new Empty());
        worldWithPlipToTheRight.put(Direction.BOTTOM, new Impassible());
        worldWithPlipToTheRight.put(Direction.LEFT, new Impassible());
        worldWithPlipToTheRight.put(Direction.RIGHT, new Plip());

        Clorus c = new Clorus(2);
        Action expected = new Action(Action.ActionType.ATTACK, Direction.RIGHT);
        Action actual = c.chooseAction(worldWithPlipToTheRight);
        assertEquals(expected, actual);        
    }

Goals

My version of the assignment covers the following goals, in decreasing order of importance:

While vaguely similar to the AP CS Grid World case study, the scope is considerably restricted, the aesthetics are cleaner, and the focus is on testing rather than the implementation of the simple functionality of the creatures.

There are two aspects to this assignment that make it particularly well suited to testing. First is that the system as a whole is highly complex, with hundreds of interacting creatures, each with their own state. Combined with the fact that the visualization is coarse (i.e. each creature has hidden state and the simulation moves quite quickly), it is next to impossible to tell whether or not your creatures are correctly implemented just by watching the creatures run around. Secondly, the API of each creature is very crisp, allowing unit tests to fully and precisely exercise the behavior of the creatures (including statistical tests of random behavior for advanced students).

This assignment can be successfully repurposed towards goals other than testing. The most obvious idea is to use it as a simple OOP exercise (as I did during a Summer course in 2014). See the enrichment section of the assignment spec for more ideas.

Materials

Specification for this assignment: Link
Starter files (Java): Link
Demo video: Link
Starter files (Python): Starter file for creatures and simulation client

The Python version of this assignment was used during a Summer course in 2014. It's in a more raw state than the Java version, which has been battle tested over two semesters.

Metadata

Summary Students create and test creatures that compete for resources in a 2 dimensional grid-based world.
Topics
Unit testing, java packages, callbacks, debugging a large system, subtype polymorphism, serialization.
Audience
Late CS1, CS2
Difficulty
Easy to moderate. This was used as a 2 hour lab (but runs over time).
Strengths

  • Testing: Students get a chance to see how easy it is to write tests even in the context of a complex software system.
  • Natural Use of Inheritance: Students see a nice clean example of subtype polymorphism.
  • Entertaining: It is addictive to run the simulation over and over to see what happens.
  • Creative Testing Strategies Required: Writing a test to ensure that random behavior of the creatures obeys the expected statistics was very appealing for some students.
Weaknesses

  • Idiosyncratic: This mini-assignment may be hard to fit into an existing curriculum without some modifications, as it assumes a very specific set of prerequisite coverage.
  • Focus on Testing / Lack of Ownership: Though it is fun to watch, students may not feel as accomplished when completing the tasks. The code for the creatures is shorter than the tests.
Library Dependencies
Requires Princeton's StdDraw.java file.
Variants

Students can create other creatures than those suggested, allowing observation of novel dynamics.

Students can simply tweak parameters to attempt to push the system into greater ecological stability.

Assignment can be adapted to focus on other features (e.g. inheritance, serialization) instead of its present focus as a testing assignment.

Students can share creature definitions and world files, allowing for a contest for survival-of-the-fittest.