Assignment 8: Zombies: The Final OBJECTive¶
Assignment Setup¶
To create your repository go here. Then follow the same accept/import process described in the setup instructions.
Zombies… The Final OBJECTive¶
In Assignment 5 we created a simulation using Methods. Since we had boolean
state (isZombie) and double
state (x and y coordinates), we kept track of our entities in separate arrays. Further, since ,methods can only return a single value, we (somewhat awkwardly) were forced to create arrays and pass them into readEntities(in, areZombies, positions)
. Now that we’ve seen the concept of objects and inheritance, it makes more sense to store the individual data in an Entity
base class. We can then forgo the arrays for a single array of entities stored in our ZombieSimulator.
In this installment of the zombie saga we will:
Refactor our code to use an
Entity
base class withZombie
andNonzombie
subclasses.Use a collection of
Entity
objects - these can be eitherZombies
orNonzombie
objects, since both areEntity
objects.Rather than having all entities move randomly, we’ll make our
Zombies
andNonzombies
behave somewhat intelligently.When a
Nonzombie
is touching aZombie
there’s a chance it will be consumed rather than always turn into aZombie
.
Questions to ask if you get stuck¶
Like all problems, this one can be tricky. Here are some common questions that we get from students regarding this assignment. Use these questions to gauge your own understanding of what we are asking you to do. Please ask these questions to a TA or an instructor if you are feeling stuck on a certain part of the assignment.
What is inheritance? What parts of a class are inherited?
How do we inherit from one class to another class in Java?
What does it mean to override a method? Which method will be used if a method is overriden?
How do constructors work when using inheritance?
What is
super
?What is polymorphism? How does it help us?
Files¶
Your project includes a number of source files in the src
folder:
assignment8/DrawEntitiesDebugApp.java
: Rudimentary check on your Entity drawing.assignment8/Entity.java
: TheEntity
class. You will need to complete this fileassignment8/EntityAndZombieSimulatorTestSuite.java
: Incomplete testing of your Entity and ZombieSimulator classesassignment8/Nonzombie.java
: TheNonzombie
class. You will need to complete this fileassignment8/Zombie.java
: TheZombie
class. You will need to complete this fileassignment8/ZombieSimulator.java
: TheZombieSimulator
class. You will need to complete this file
Procedure & Recommended Workflow¶
Below is a recommended work-flow. You should expect to have to revisit the implementation of some methods as you experiment with different strategies.
Entity¶
Constructor and Instance Variables¶
Your Entity constructor is passed the initial values of for the zombie state (isZombie
), the speed, and the x and y coordinates. You should store these values in instance variables for later use. Some of these instance variables have already been created for you, you should create any additional instance variables that you need. You should also keep track of a radius for each entity. This is not specified as a parameter to the constructor. The initial radius value is left for you to choose. An Entity also needs to keep track of whether it is alive or not. Make sure that there is an instance variable for this purpose and also ensure that all Entities start out as alive when they are first constructed.
Is Zombie, X, and Y Accessors¶
Complete the
isZombie()
,getX()
, andgetY()
methods.
Radius Getter and Setter¶
Complete
getRadius()
andsetRadius(double d)
methods.
Note: if you are looking for a default radius value to start with, Assignment 5 used 0.008
.
isAlive and wasConsumed¶
isAlive()
is a getter that should return whether or not the current entity is alivewasConsumed()
is a method that will be used to indicate that anEntity
has been caught and eaten by aZombie
. It should make it so that theEntity
being consumed is no longer alive.
distanceCenterToPoint(xOther, yOther)¶
Note: the method
distanceCenterToCenter(other)
has been provided to you. It simply callsdistanceCenterToPoint(xOther, yOther)
with the center of the other Entity.
distanceEdgeToEdge(xOther, yOther, radiusOther)¶
Question: How can you leverage your implementation of
distanceCenterToPoint(xOther, yOther)
for this method?Question: How should you use the two radii (the this instance’s radius and the otherRadius) to calculate the edge-to-edge distance?
Note: the method
distanceEdgeToEdge(other)
has been provided to you. It simply callsdistanceEdgeToEdge(xOther, yOther, radiusOther)
with the center and radius of the other Entity.
isTouching(xOther, yOther, radiusOther)¶
Question: How can you leverage your existing code to calculate if the circle centered at (
xOther
,yOther
) ofradius
overlaps with the bounding circle of this Entity?Note: the method
isTouching(other)
has been provided to you. It simply callsisTouching(xOther, yOther, radiusOther)
with the center and radius of the other Entity.
moveAwayFrom(x, y)¶
Question: How can you implement this method leveraging
moveToward(x, y)
? Recall that each entity has a speed instance variable that can be used to represent the amount of distance an entity should move in a given frame.
Investigate Find Closest¶
A few convenience methods have been provided to you for finding the closest entity (which is not this itself) to this Entity. Variations include finding the closest zombie, the closest nonzombie, and the closest entity (independent of its state of undeadedness). Investigate these so that you know how to utilize them in your
update(entities)
method.
Warning: each of the find closest methods will return null if no Entity meets the specified constriaints. For example, if there are no remaining nonzombies and findClosestNonzombie(entities) is called null
will be returned. You will need to handle this case gracefully since if you try to call a method on null
a NullPointerException
will be thrown.
checkBounds()¶
As we have seen in previous simulations, it is possible for entities to go off the edge of the screen. This method should check the x and y position of the entity and move them back onto the screen if they have gone off of the edge. You likely have code from a previous assignment that you can use for this part.
update(entities), draw()¶
Notice that these methods don’t do much - they aren’t intended to be used, since these behaviors are different depending on whether the entity performing them is a Zombie
or a Nonzombie
. Instead of using these methods, you will implement the specific behavior for Zombies and Nonzombies in their respective classes by overriding this method. In practice, since these methods are never used we would say that they are in fact abstract, a concept that will be covered in future CS courses (but not this one).
Zombie¶
Constructor and Instance Variables¶
Since a Zombie
is an Entity
, our main task here is to call the constructor of the base class using super()
. The Entity
constructor has four parameters even though the Zombie
constructor only has two - you will have to supply values for the missing two parameters. For the speed, you can use the given ZOMBIE_SPEED
constant (which can be easily changed to adjust the speed of your nonzombies).
consumeNonzombie()¶
This method will be used when this
zombie has consumed an unfortunate human. The zombie’s radius should increase to show that it is becoming bigger and stronger. Increase the zombie’s radius by 20% every time this method is called, unless the zombie hits a maximum radius of 0.02.
draw()¶
This is the first method that you will overwrite from the Entity
class. Instead of displaying everything in pink (as the base class specifies) include code here to display your zombies in a unique way so that you can distinguish them once the simulation is running.
ALERT: You should not use StdDraw.show()
or StdDraw.clear()`` in this method, those methods are used to manage the overall animation in the ZombieSimulator
class. This method is responsible for drawing a single zombie, and will likely involve setting the pen color and drawing a shape in the correct position.
update()¶
Repeated Warning: each of the find closest methods will return null
if no Entity meets the specified constriaints. For example, if there are no remaining nonzombies and findClosestNonzombie(entities) is called null
will be returned. You will need to handle this case gracefully since if you try to call a method on null
a NullPointerException
will be thrown.
The method will be passed a list of all current entities (this allows an entity to try to make a rational decision based on all other entities, like running away from a zombie).
Zombies are hungry. They should intentionally (not randomly) move toward nonzombies.
Make sure your zombies stay in bounds by using the
checkBounds()
method from theEntity
class.
Remember to focus on the simulation of the this
instance of Entity
. You should not concern yourself here with updating all of the entities
. You will undoubtedly inspect them via the findClosest methods, of course. However, keep in mind that it will be ZombieSimulator
’s responsibility to call update for each of its non-consumed entities.
Nonzombie¶
Constructor and Instance Variables¶
Similar to what you did for Zombie
, call the super constructor with the appropriate parameters.
convert()¶
When a nonzombie gets eaten, sometimes it may turn into a Zombie. This method should create and return a new Zombie
object at the same position as this
nonzombie to represent this conversion.
draw()¶
Place code here to specify how you want your nonzombies to be drawn.
update()¶
Repeated Warning: each of the find closest methods will return null
if no Entity meets the specified constriaints. For example, if there are no remaining nonzombies and findClosestNonzombie(entities) is called null
will be returned. You will need to handle this case gracefully since if you try to call a method on null
a NullPointerException
will be thrown.
Nonzombies don’t want to be infected. They should move in a way that reduces their chance of becoming a zombie. They could move such that they try to avoid populated areas, or avoid zombies, or they could believe there’s safety in numbers and try to approach other nonzombies.
When a nonzombie touches a zombie it should there is a random chance that it will be consumed by the zombie.
80% of the time the nonzombie should change its state to zombie at the same location and with the same size (recall the convert() method).
20% of the time it is consumed by the zombie that is touching it (the closest if many are). The zombie that consumes it will increase in radius by 20% of the nonzombie’s radius up to a reasonable maximum size (e.g. 0.02). You should use the
consume()
method for this.
The
Entity
that gets returned depends on the outcome: if the nonzombie remains untouched by zombies then you’ll likely just returnthis
however, in some cases you will need to return aZombie
instead.
Zombie Simulator¶
Constructor and Instance Variable(s)¶
The ZombieSimulator’s constructor should initialize an array of
Entities
for the given size.
getEntities()¶
Returns the current array of entities.
readEntities(ap)¶
reads a complete zombie simulation file as described in Assignment 4. Each read Entity should be added to this instance’s List of entities.
getZombieCount()¶
Returns the number of Entities in the current list of entities which are zombies.
getNonzombieCount()¶
Question: How can you implement this method leveraging
getZombieCount()
?
draw()¶
A bare bones implementation has been provided to you. Feel free to come up with more creative presentations if you choose.
update()¶
Updating a ZombieSimulator largely defers to its Entities to each update. Put another way, each of the entities should have its update method called. Each Entity will need the complete list of active entities passed to it, so that it can properly simulate its update.
Calling
update(entities)
on an Entity will return anEntity
(which could be either a Zombie or Nonzombie) as a result of the update. Make sure that thisEntity
is properly stored in the array so that the simulation can continue.
main(args)¶
A bare bones implementation of a real-time simulation has been provided to you. Investigate this method and make any changes you need to improve your ZombieSimulator.
Assignment Requirements¶
Partial credit is possible and will be based on the number of unit tests that are passed and the degree to which you complete the required update strategies. For full credit:
All unit tests should pass
The
ZombieSimulator
should run.It should repeatedly update entities
Your zombies should move, try to consume nonzombies, and occasionally increase in size after consuming a nonzombie up to a maximum size.
Nonzombies should be consumed by zombies after about 20% of run-ins and turn into zombies the other 80% of the time.
Your nonzombies should move and have some reasonable approach to survival (not just random motion all the time)
Animation should be reasonably smooth (nothing should jump to new locations on the screen)
As before, all entities must stay within the visible window (the unit square from (0,0) to (1,1))
Submitting your work¶
Get your assignment graded by bringing it to lab on Wednesday/Thursday or going to office hours and signing up for a demo via wustl-cse.help.
Confirm that your score is recorded in Canvas. Mistakes can happen and you should always confirm credit is recorded before leaving class!