Homework 2: Let the Forneymon battles commence!

Assigned: Tuesday, September 29, 2026

Due: Sunday, October 18, 2026 @ 11:59 PM

Forney Industries is continuing with its campaign to make Forneymon a household name. Despite challenges with its live action television series (in which negative press overwhelmed its debut following an unfortunate wardrobe malfunction from a Dampymon), they are moving forward with its digital presence, and like every other company in the domain, wish to create a new variant of an “Auto-Chess” game.

If you’re unfamiliar with Auto Chess games, the basic idea is that the game is played in 2 phases: (1) you recruit an army to face off against others, and then (2) the army you’ve recruited fights automatically against another player’s. These types of games have all the charm of recruiting the perfect fighting force with none of the effort from having to micro-manage the fight.

Examples of this genre include Super Auto Pets, Clash Royale, and Teamfight Tactics.

Your Task

Implement a Forneymon-themed Auto Chess game by defining two important pieces:

  1. Forneymonagerie: (a portmanteau of “Forneymon” and “menagerie”) Forneymonagerie are ordered collections of Forneymon that hold one unique instance of each Forneymon species that are collected. The order of Forneymon stored within decides the order in which they battle their opponents.
  2. ForneymonArena: a battleground in which two players’ ForneymonagArray face off!

Example

Here is an example playthrough that the Forney Industries concept artists charged us $10k for. It features 2 ForneymonagArrays, fm1, fm2 each containing different Forneymon in different orders.

example

Notes on the above:

  • ForneymonagArray in the arena can have different number of contained Forneymon, and each collection may contain different Forneymon species (though may have only one of each species, more on that later).
  • ForneymonagArray fm1 has 3 Forneymon in order: Dampymon, Burnymon, and the newest Zappymon. However, fm2 has only a Burnymon and Dampymon, in order.
  • In the ForneymonArena, each Forneymon between the two battling ForneymonagArray pair up and attack one another in order. In the above illustration, the first 4 pairs are depicted (though there may be more in the sequence), in which the arrow indicates that the two Forneymon deal their type of damage to the other:
    1. Pair 0 [Blue]: The Dampymon in fm1 and the Burnymon in fm2 attack one another.
    2. Pair 1 [Green]: The Burnymon in fm1 and the Dampymon in fm2 attack one another.
    3. Pair 2 [Red]: The Zappymon in fm1 and (circling back around) the Burnymon in fm2 attack one another.
  • After each attack, if a Forneymon’s health falls to or below 0, they are removed from their associated ForneymonagArray.
  • The above continues until at least 1 of the battling ForneymonagArray have no Forneymon remaining!

Preliminaries


Info

In preparation of launching Forneymon Battlegrounds, some changes to the Forneymon classes have been made, alongside some new programmatic tools that we’ll review in the coming section.

Changes to Forneymon

Tldr

The “base game” of Battlegrounds comes with 4 Forneymon species: Dampymon, Burnymon, Leafymon, and Zappymon, each with their own strengths and weaknesses.

Forneymon now have 3 important fields for the arena:

  • int health; same as before, the amount of remaining health. Each species has a different starting health that is specified as a constant in their class file.
  • int level; strength level of the Forneymon that affects how much damage it deals; levels are assumed to be only positive ints greater than 1.
  • DamageType damageType; the type of damage that this Forneymon deals; DamageType is a new enum type, which are basically just constants that belong to a class for ease of comparison. See the DamageType enum in the skeleton that follows (examples of which include DamageType.DAMPY for the damage type dealt by the Dampymon).

The full set of methods and fields in the skeleton’s few Forneymon can be found in the following Javadocs, or you can consult each source file individually.

Some additional notes on the above:

  • You may add any additional Forneymon types or DamageTypes that you desire to adequately test or play with your submission, but you must leave the original Forneymon and DamageTypes untouched from the skeleton.
  • Note the new equals method that checks if two Forneymon are of the same species and level.
  • Additionally, Forneymon can now be cloned, which produces a copy of them.

Polymorphism

In order for our ForneymonagArrays to hold a diverse set of different Forneymon species, and still maintain their different behaviors, we’ll exploit a tool called polymorphism.

Polymorphism

(in this context) defines the mechanics by which objects belonging to a subtype (e.g., species: Dampymon, Burnymon, etc.) can be referred to via a supertype reference but use any overrided versions of methods.

Example

Suppose we have a Pet superclass, both Dog and Fish subclasses of Pet, and wish for the “default” speak behavior to be silence, but for Dogs to differ from that and bark.

public abstract class Pet {
 
    public void speak () {
        System.out.println("*silence*");
    }
 
}
public class Dog extends Pet {
 
    @Override
    public void speak () {
        System.out.println("Woof!");
    }
 
}
public class Fish extends Pet {} // lol nothing here

If we wished to make an array of Pets, but wanted it to hold many different species of pets we could simply do the following:

public static void main (String[] args) {
    // Array holds superclass references...
    Pet[] petArray = new Pet[2];
    // ...to subclass objects! Groovy!
    petArray[0] = new Dog();
    petArray[1] = new Fish();
    
    for (Pet p : petArray) {
        p.speak();
    }
}

The above will print out Woof! then *silence* in order.

Note

Remember how we overloaded the equals method that accepted an Object other parameter? Similar idea: we used a reference of the supertype (Object) to accept any sort of species argument.

Warning

Now, remember the above for managing our ForneymonagArray in which we’ll have a collection of Forneymon!

Solution Skeleton

Info

Start with the solution skeleton in-hand! The following will also serve as your submission mechanism (see submission instructions below).

ForneymonagArray Specifications

Info

The following section instructs you on how to implement the ForneymonagArray class, an Array List implementation of a Forneymonagerie, as well as expectations for each method. CAREFULLY read through this section and the “Restrictions” section that follow before you begin any coding!

Basics


In this assignment, we’re going to develop a new data structure called a “ForneymonagArray”, which is a happy medium between ArrayLists and Multi-sets used to order our Forneymon. Similar to our IntArrayList from class, ForneymonagArray maintain 2 fields:

  • Forneymon[] collection; the underlying array storage structure for the Forneymon stored in the monagerie.
  • int size; the number of unique Forneymon stored in the ForneymonagArray.

The basic mechanics of a ForneymonagArray support the ordered addition and retrieval of Forneymon from the collection, with a couple of quirks as follows:

  • Adding a Forneymon to a ForneymonagArray of a type that doesn’t currently exist within adds it to the last open index.

Attention

ForneymonagArray should accommodate an arbitrary number of Forneymon species (i.e., Burnymon, Dampymon, etc.) even though only 4 are included in the base game (skeleton). You should add additional types to test that you can accommodate more than 4!

  • Trying to add a Forneymon to the ForneymonagArray that is already stored within (i.e., by identity equivalence) will do nothing.
  • Trying to add a Forneymon of the same species as a Forneymon already in the ForneymonagArray will instead keep the higher-leveled one (replacing the lower-leveled one at the same index).

Example

Consider the following example of a ForneymonagArray storing several Forneymon and representing the rules above!

// Creates a new, empty ForneymonagArray
ForneymonagArray fm = new ForneymonagArray();
 
// Creates a new level-1 Burnymon...
Burnymon b1 = new Burnymon(1);
// ...and adds it to the ForneymonagArray
fm.collect(b1);
// Since fm is emtpy, b1 gets added at index 0
// fm = [Burnymon(lvl 1)]
 
// ...but if we tried to add it again, nothing
// will happen
fm.collect(b1);
// fm = [Burnymon(lvl 1)]
// (still)
 
// Creates a new level-2 Dampymon
Dampymon d1 = new Dampymon(2);
fm.collect(d1);
// Since fm doesn't yet contain a Dampymon, it gets
// added to the end:
// fm = [Burnymon(lvl 1), Dampymon(lvl 2)]
 
// Creates a new level-4 Burnymon
Burnymon b2 = new Burnymon(4);
fm.collect(b2);
// Since fm already contains a Burnymon, but not the
// same one as we added, we instead keep the one with
// a higher level
// fm = [Burnymon(lvl 4), Dampymon(lvl 2)]

Problem 1: Constructor


Info

You will define one constructor for the ForneymonagArray class: A default constructor that merely reserves START_SIZE amount of space for the collection, and instantiates the size to 0.

If you have any additional fields that you have defined, you should initialize them in the above as well.

Problem 2: Methods


Your ForneymonagArray class will implement the following interface, with individual method descriptions to follow.

Remember

This is an Interface, which means it defines the contract of minimum requirements of any classes that implement it!

public interface Forneymonagerie {
 
    public boolean empty ();
    public int size ();
    public boolean collect (Forneymon toAdd);
    public Forneymon get (int index);
    public Forneymon getMVP ();
    public Forneymon remove (int index);
    public boolean releaseSpecies (String fmSpecies);
    public int getSpeciesIndex (String fmSpecies);
    public boolean containsSpecies (String fmSpecies);
    public void rearrange (String fmSpecies, int index);
    public Forneymonagerie clone ();
    public boolean equals (Object other);
    public String toString ();
 
}

Methods

boolean empty();

Returns true if the ForneymonagArray has no Forneymon inside, false otherwise.

int size();

Returns the current size of the ForneymonagArray (i.e., the number of Forneymon in the collection, not the collection’s length).

boolean collect (Forneymon toAdd);

Attempts to add a reference to the given Forneymon to the ForneymonagArray’s collection, as decided by the following rules (repeated from above):

  1. Adding a Forneymon to a ForneymonagArray of a species that doesn’t currently exist within appends it to the last open index.

Danger

ForneymonagArray should accommodate an arbitrary number of Forneymon species (i.e., Burnymon, Dampymon, etc.) even though only 4 are included in the base game (skeleton). You should add additional types to test that you can accommodate more than 4! Note that you can just copy and rename existing Forneymon species to test.

  1. Trying to add a Forneymon to the ForneymonagArray that is already stored within will do nothing (i.e., if toAdd is an alias of a Forneymon already stored inside; can be checked by identity equivalence ==).
  2. Trying to add a Forneymon of the same species as a Forneymon already in the ForneymonagArray will instead keep the Forneymon with the higher level. In the event that the two possess the same level, the Forneymon already stored within is kept.

Returns true if toAdd was a newly added species to the ForneymonagArray (i.e., case 1 above), false otherwise.

Forneymon get (int index);

Returns the Forneymon at the requested index in the collection, if valid.

If the given index is invalid, throw a new IllegalArgumentException().

int getSpeciesIndex (String fmSpecies);

Returns the index of a Forneymon with the given fmSpecies in the collection, or -1 if that type is not found within.

boolean containsSpecies (String fmSpecies);

Returns true if the given fmSpecies is found within the ForneymonagArray’s collection.

Forneymon remove (int index);

Removes and returns the Forneymon at the given index, if valid, and maintains the relative order of remaining Forneymon in the collection.

If the given index is invalid, throw a new IllegalArgumentException().

boolean releaseSpecies (String fmSpecies);

Removes the Forneymon of the given species fmSpecies from the ForneymonagArray, maintaining the relative order of remaining Forneymon in the collection, and returning true.

For example, if we call fm.releaseSpecies("Dampymon") and a Dampymon was at index 1, then all Forneymon in indexes 2+ are shifted one left.

If the given fmSpecies does not exist in the ForneymonagArray, do nothing, and return false.

Forneymon getMVP ();

Returns the “best” Forneymon currently in the collection, or null if the collection is empty.

Here, “best” is defined as:

  • The highest level Forneymon in the collection, with ties broken by:
  • The highest health of those with the max level, with ties broken by:
  • The earlier index of those tied in the collection.
void rearrange (String fmSpecies, int index);

Moves the Forneymon of the given fmSpecies from its current position in the ForneymonagArray to the one specified by the index, shifting any existing Forneymon around the requested index so that the relative indexing is preserved.

The given index is defined on the range [0, size-1], inclusive, and all other indexes provided should throw new IllegalArgumentException();

void trade (Forneymonagerie other);

Swaps the contents of the calling ForneymonagArray and the other specified.

Note

Although the parameter is of the interface type, you may assume for this problem that other can be downcast to a ForneymonagArray.

Restriction

You may NOT use iteration/recursion to solve this problem! Doing so will lose you all points on this method. Hint: exploit the fact that some fields are references!  

@Override ForneymonagArray clone ();

Returns a deep copy of this ForneymonagArray, which is a new ForneymonagArray object with the same Forneymon species, and in the same collection order, but with new (cloned) instances of each stored Forneymon and it’s own collection.

A deep-copy means that changes to one ForneymonagArray (e.g., collecting, releasing, damaging, etc. any contained Forneymon) should NOT affect the other.

Put differently, clone should produce a copy of the calling ForneymonagArray that operates independently from the original.

HINT

Draw out the references of your implementation to make sure that there is nothing shared between the original and the copy.

@Override boolean equals (Object other);

Returns whether or not the given Object other is a property-equivalent ForneymonagArray to this one, which we define as meaning that it contains equal (i.e., property-equivalence) Forneymon in the same order in the collection as this one.

Returns false in all other cases.

@Override String toString ();

Returns a String representation of the calling ForneymonagArray (useful for debugging too!).

This one’s on the house (you don’t need to do anything else), and displays ForneymonagArray via the pattern: ForneymonSpecies [Level]: HPRemaining

For example:

ForneymonagArray fm1 = new ForneymonagArray();
Dampymon d1 = new Dampymon(1);
Burnymon b1 = new Burnymon(2);
Leafymon e1 = new Leafymon(4);
fm1.collect(d1);
fm1.collect(b1);
fm1.collect(e1);
 
System.out.println(fm1);
// Prints:
// [ Dampymon [1]: 25HP, Burnymon [2]: 15HP, Leafymon [4]: 20HP ]

ForneymonArena Specification

Info

ForneymonArenas contain the logic for conducting the AutoChess battle between two ForneymonagArray!

Each fight between ForneymonagArray consists of the following steps:

  1. Pairs of fighting Forneymon from each ForneymonagArray are formed in sequence based on the order in which they appear in each collection. For example, for ForneymonagArray fm1, fm2, then the first pair would be fm1 at index 0 with fm2 at index 0, then both at index 1 etc. The indexes thus wrap-around if they go over the current size of the ForneymonagArray. For instance, if the next index in fm2 were 3, but it contains only 3 Forneymon, then the next index will instead be 0.
  2. The pair of Forneymon then fight, dealing the following attack damage to one another: damage = BASE_DAMAGE + ATTACKER_LEVEL + DAMAGE_MODIFIER
    • The BASE_DAMAGE is set in the ForneymonArena class as a constant.
    • The ATTACKER_LEVEL is decided by the level of the attacking Forneymon. For example, a Level 3 Forneymon will deal 3 bonus damage here.
    • The DAMAGE_MODIFIER logic is handled by the individual Forneymon species’ takeDamage method and is based on the type of damage that the attacker deals.
  3. If a Forneymon is knocked out (health less than or equal to 0), it is removed from its ForneymonagArray (note that this mutates the input ForneymonagArray).
  4. Pairs continue to form and fight until either one (or both) ForneymonagArray are out of Forneymon!

Example

Consider again the ForneymonagArray from the example in the intro, but suppose also that the Forneymon in fm1 are all level 1, and the two in fm2 are both level 5.

example

# The above, specified programmatically
fm1 = ForneymonagArray()
fm1.collect(Dampymon(1))
fm1.collect(Burnymon(1))
fm1.collect(Zappymon(1))

fm2 = ForneymonagArray()
fm2.collect(Burnymon(5))
fm2.collect(Dampymon(5))

fight(fm1, fm2, true)

// Now, some detailed comments on what happens above:

  [!] Combat Starting!
    [VS] New Round: Dampymon [1]: 25HP vs Burnymon [5]: 15HP
      [>] Combat Results: Dampymon [1]: 10HP vs Burnymon [5]: 9HP
      
    # Note how in the above first round, fm1's Dampymon took 15 damage:
    # 5 (Base) + 5 (Opponent's Level) + 5 (From a Dampymon taking BURNY damage)
    
    [VS] New Round: Burnymon [1]: 15HP vs Dampymon [5]: 25HP
      [>] Combat Results: Burnymon [1]: 5HP vs Dampymon [5]: 14HP
      
    [VS] New Round: Zappymon [1]: 20HP vs Burnymon [5]: 9HP
      [>] Combat Results: Zappymon [1]: 10HP vs Burnymon [5]: 3HP
      
    [VS] New Round: Dampymon [1]: 10HP vs Dampymon [5]: 14HP
      [>] Combat Results: Dampymon [1]: 0HP vs Dampymon [5]: 8HP
      
    [VS] New Round: Burnymon [1]: 5HP vs Burnymon [5]: 3HP
      [>] Combat Results: Burnymon [1]: -5HP vs Burnymon [5]: -3HP
      
    [VS] New Round: Zappymon [1]: 10HP vs Dampymon [5]: 8HP
      [>] Combat Results: Zappymon [1]: -3HP vs Dampymon [5]: 2HP
      
  [!] Combat Finished! Victor: ForneymonagArray 2
  # fm2 will have a single, beat up Dampymon with 2HP at this point, and
  # fm1 will be wiped out (no Forneymon left within)

Info

On this assignment, this portion of the ForneymonArena is given! You do not need to worry about making any changes to ForneymonArena, however you must ensure that its mechanics operate properly like in the above!

Warning

Comparable to the unit tests you write for each method, the ForneymonArena tests serve as integration testing in which various packages are tested in concert with one another.

This is useful for making sure that classes not only work internally to their data structures, but also that they play together correctly.

In a future assignment, you will implement this method as well.

Some additional notes:

  • This method will indeed mutate (i.e., modify) both ForneymonagArray and their contained Forneymon. Worry not about this, because if the user of our class didn’t want those mutations, they could easily clone the ForneymonagArray before calling the fight method.
  • Don’t worry about the edge case of a single Forneymon object belonging to multiple ForneymonagArray — we’ll consider this user error and need not check nor test for it.
  • The 3rd parameter to the fight method determines whether or not it prints out the fight, so you can double check the individual steps by making this true if you’d like.
  • Although you are not required to do anything for the ForneymonArena class, you’ll use it to validate that your ForneymonagArray are working properly because many of the methods are called within.

Testing and Documentation

Warning

Although it won’t be graded, you should almost certainly add unit tests to the bare-bone skeleton ones that I provide to you! You are responsible for verifying the proper functionality of your submissions!

Disclaimer: the unit tests I provide to you in the skeleton are a mere tip of the iceberg for the grading tests; this is a big assignment and there are lots of places where things can go wrong. As such, ensure that you test as many edge cases as possible to give yourself confidence of a higher grade, and more importantly, to gain the invaluable practice of writing good QA tests!

Additionally, you should provide JavaDoc-level comments for each implemented method to receive full style credit.

Finally, remember to practice good programmatic design: keep code DRY, use helper methods for separation of concerns, name variables and helpers well, etc., as all aspects of your submission will be graded (as these stylistic choices may influence job interviewers significantly in the “real world!”)

Reminder that the Java Style guide can be accessed here:

Solution Restrictions

Bug

Read the following list of submission restrictions carefully! Violating any restriction will net you a 0 on this homework!

  • You may NOT use ANY data structure from the Java collections framework in your solution (that includes ArrayLists).
  • You may NOT add any methods or fields to the ForneymonagArray class’ public interface. You may, however, add any private fields or methods that you like.
  • Your classes and therefore source files must be named exactly as intimated above (as is in the Solution Skeleton), and you MUST submit your classes under the same package structure as the one given.
  • RECALL: As a homework assignment, you are free to discuss approaches at the high-level in groups so long as no code is shared between individuals. The same is true of code written by ChatGPT, Copilot, or any other AI model. We use sophisticated similarity checking software to detect copying, so just do your own work!

Warning

Be Wary! Even sharing any code snippets will cause red flags — consider this fair warning, do not share any code! If you feel tempted to do so, contact me or a TA instead — we’re happy to help, so long as you give yourself enough time to work with us (so start early!).

Hints and Tips

The implementation of this assignment requires you to make some design decisions. However, here are some hints for how you might structure your own.

  • Read the spec then re-read it — make sure you have a solid grasp on both the big and little picture before writing any code!
  • Consider making helper methods that are private to the class — these can reduce complex code to more readable segments that better organize your thoughts, and can be used to keep your code DRY (in the case of behavior that is repeated).
  • Note that to avoid repetition, it may make sense for some of your methods to call one another, especially if their behavior is very similar.
  • Due to the polymorphic collection of Forneymon, and the overridden takeDamage methods in each species, you should never have to (read: should not) downcast a Forneymon to one of its suclasses for this assignment!
  • Develop incrementally. I cannot impress this enough. Implement one method, ensure that it’s working with unit tests, and then move on to the next… don’t code a bunch at once and then try to disentangle the errors after hours of work!
  • Stuck on a bug? Draw things out, and remember that there are debuggers available to help you assess your program’s state! Recall our debugger example from the IntList lecture.
  • Do. Not. Wait. Give yourself ample time to complete this assignment — it is nontrivial in size and may require you to spend lots of time debugging!

Submission

Info

You will be submitting your assignments through GitHub Classroom!

What

Complete the source files that accomplishes the specification above in the project structure given in the skeleton.

How

To clone this assignment (if you need a refresher), consult the guide here: GitHub Classroom Tutorial

To submit this assignment:

  • Simply push your final, submission copy to the GitHub Classroom repository associated with your GitHub classroom account.
  • Make sure that your name is commented as the author at the top of each submitted file AND in the accompanying README.md file.