CSC 213 — Programming Fundamentals

Lab 10 - Game of Life

A relatively famous computer science simluation is a cellular automation program called The Game of Life.

In this assignment, you will use Java interfaces to implement a modified version of this simulator.

Assignment

Part I - Become Familiar With the Code:

  1. Download the existing source code for this project.

  2. Make sure you are able to compile and run the existing program.

    When you first open the program in IntelliJ, if you can't see the code, click on the Project tab on the right side of the screen, then expand the src and prove02 folders.

  3. Examine the .java files to make sure you understand the purpose of each:

    • Game.java - This file contains the main() method. It initializes the List of Creature objects, and then creates the World.

    • World.java - This class creates the GUI, handles GUI events such as resizing and repaiting, and kicks off the background thread for updating the state of the Creature instances.

    • Creature.java - Contains the abstract base class from which all Creature subclasses must inherit.

    • Movable.java - Contains the definition of the Movable interface. This interface should be implemented by all subclasses of Creature that can move.

    • Aggressor.java - Contains the definition of the Aggressor interface. This interface should be implemented by all subclasses of Creature that can attack other creatures.

    • Aware.java - Contains the definition of the Aware interface. This interface should be implemented by all subclasses of Creature that can sense and react to other creatures around them.

    • Plant.java - A subclass of Creature that does nothing but provide food for other creatures. Instances of the Plant class are represented in the game by green circles. Plants start with one health point.

    • Animal.java - A subclass of Creature that implements the Movable and Aggressor interfaces. Instances of the Animal class are represented in the game by red squares. They move in random directions and if they land on a plant, they will eat it. Animal instances start with one health point and gain a health point for every plant they eat. Animals inflict one point of damage when they attack.

    • Shape.java - An enum that contains possible creature shapes.

    • CreatureHandler.java - This class coordinates the behavior of each Creature instance. The key method in this class is the updateCreatures() method, which is called by the World class every time the update loop runs. This method will iterate through a List of Creature instances, determine which behaviors they implement, and signal the Creature instance to carry out those behaviors.

Part II - Create a Zombie:

Your first task is to create a new Zombie creature based on existing behaviors.

It will start out by moving from left to right across the screen, devouring anything it comes in contact with, except for plants.

  1. Create a new file called Zombie.java. That file should contain a public class definition for the Zombie class.

  2. The Zombie class should be a subclass of Creature and should implement the Movable and Aggressor interfaces.

  3. Instances of the Zombie class should always move from left to right.

  4. Instances of the Zombie class should attack any creature they land on, as long as it isn't an instance of the Plant class. They should inflict 10 points of damage when they attack.

  5. Instances of the Zombie class should be represented as blue squares.

  6. Modify the Game class to add 10 Zombie instances to the List of Creature objects created at the start of the simulation.

Part III - Create a Wolf:

Your next task is to create a Wolf creature based on existing behaviors. Wolves start out by moving in a random direction, searching for something to eat.

If a wolf senses an animal nearby, it will decide to move in that direction as soon as possible. If it lands on an animal, it will eat it. Wolves will not eat or purposefully move towards zombies or plants.

  1. Create a new file called Wolf.java. That file should contain a public class definition for the Wolf class.

  2. The Wolf class should be a subclass of Creature and should implement the Movable, Aware, and Aggressor interfaces.

  3. When the move() function is called on a Wolf instance, it should move in its preferred direction. When it is first created, it's preferred direction should be random.

  4. When the senseNeighbors() function is called, the Wolf instance should change its preferred direction to be in the direction of the first Animal instance it sees. When checking for nearby animals, it should first check in the direction it's already moving. If no Animal instance is there, it should search in a clockwise pattern starting at the top.

    So, if the Wolf is moving left, it should first check the creature provided in the left parameter of senseNeighbors() , followed by above, then right, then below.

  5. Instances of the Wolf class should attack any Animal instances they land on, but should not attack Plant or Zombie instances. Wolves should inflict 5 points of damage when they attack.

  6. Instances of the Wolf class should be represented as gray squares.

  7. Modify the Game class to add 10 Wolf instances to the List of Creature objects created at the start of the simulation.

Part IV Give Wolves a New Behavior:

Your next task is to allow wolves the ability to spawn baby wolves.

Every time a wolf eats another animal, it should gain the ability to spawn a new wolf on its next turn.

The newly spawned wolf should be created in the square directly to the left of its parent. After spawning a new wolf, the parent should lose the ability to spawn new wolves until the next time it eats an animal.

  1. Create a new interface called Spawner. This interface should define a single method:

    public Creature spawnNewCreature();
    
  2. Update the Wolf class so that it implements this new behavior.

  3. Modify the CreatureHandler class so that it handles the Spawner behavior. It should handle this behavior in a similar way to how it handles the other behaviors. Spawner behaviors should be handled after Aggressor behaviors.

    This part will be tricky, and will require you to do some research on your own, a pattern that will continue throughout the course.

    Remember, wolves can only spawn once they've eaten something, and once they've spawned a new creature they lose their spawning ability until they eat again.

Bonus Points

Invent a new creature that implements a mix of existing or new behaviors.

Some possibilities include: a flying creature that swoops down on zombies, a zombie-eating plant, or a wolf-zombie that hunts down other wolves and turns them into zombies.

Regardless of what you do, make sure you follow these design guidelines:

  1. Each distinct behavior (flying, hunting, etc...) should be in its own interface.

  2. Make sure you create separate files for any new interfaces you create, as well as any new creature subclasses you create.

  3. Make sure you modify the CreatureHandler class to incorporate any new behaviors in the update loop.

  4. Make sure you modify the Game class to instantiate some of your new creatures at the start of the simulation.

  5. If you want to use a shape other than a solid circle or square, add the shape option to the Shape enum, and add the necessary drawing code to the paint() method of the World class. You can find additional drawing methods in the official Java docs for the Graphics class.

Submission

To submit your assignment, follow the instructions for code submission. Make sure you submit your entire project so that both Java files are included.