Homework 3: Forneymonagerie II - The Missing Link
Assigned: Thursday, October 15, 2026
Due: Tuesday, November 3, 2026 @ 11:59 PM
Following the smashing success of Forneymon Battlegrounds (I know, we were as surprised as you were), you notice that your server hosting the game is going through a lot of extra computation compared to what it should be, which is costing Forney Industries some serious cash.
Even slight inefficiencies can add up over time, especially when you’re paying for the compute use of a dedicated server! As such, you investigate your present implementation and realize the following:
Warning
Every time a Forneymon faints mid-battle, you had to spend a lot of extra time just shifting all of the remaining ones over to maintain the proper indexing, and relative order of those that were still alive.
Noting the above, you realize the following remedy…
Info
It’s time to re-do the last HW… but with LinkedForneymonageries! That’s right, time to make a LinkedList implementation of the Forneymonagerie.
That means that, if it isn’t still fresh in your mind, you should probably take a second look through the last HW spec here:
Example
The mechanics of the ForneymonArena will be the same, except that we will be pitting 2 LinkedForneymonageries against each other whereas previously our implementation of Forneymonagerie was based on an ArrayList.

LinkedForneymonagerie
Info
Slightly different from the lectures, though with the same underlying concepts, we will be implementing a Circular, Doubly-Linked Link List to model our LinkedForneymonagerie.
This means the following:
- Circular: The LinkedList will “wrap around” at each end, allowing for our ForneymonArena mechanics to quickly find the next Forneymon to pair in battle after we’ve gone through the List at least once.
- Doubly-Linked: List structure will be preserved in linked Nodes that remember the Node both before and after them in the sequence.
- LinkedForneymonagerie.Iterator: Easy iteration through the LinkedForneymonagerie will be accomplished by maintaining an Iterator that can both traverse (in either direction) and modify (through removal) the underlying LinkedList for when Forneymon faint during battle.
- Sentinel Node: To make all of the above simpler, we will be implementing a dummy “Sentinel Node” that will replace our in-class diagrams of LinkedLists having a
headandtailreference. This implementation is a bit trickier to visualize, but will simplify many of your methods such that you won’t have to check a variety of edge cases related to the “special” head and tail references, but rather, can keep every operation in terms of Nodes. The Sentinel has the following properties:- A reference to the Sentinel is maintained as a field in the LinkedList, replacing the previous head and tail. Since the Sentinel is a Node like any other, the new conception of a head can be thought of as
sentinel.nextand the tail assentinel.prev. - The Sentinel will always be present in the LinkedList, even if the List itself contains no Forneymon / other Nodes.
- The Sentinel will be the “alpha and omega” Node such that the last Node in the list will have a
nextreference that points to it, and the first Node in the list will have aprevreference that points to it. - In an empty list, the Sentinel’s
nextandprevreferences will refer to itself (Sentinel hugging itself because it’s lonely!). - Though other Nodes in the LinkedForneymonagerie will have a reference to the Forneymon they store (in a field called
fm), the Sentinel’sfm = null, always.
- A reference to the Sentinel is maintained as a field in the LinkedList, replacing the previous head and tail. Since the Sentinel is a Node like any other, the new conception of a head can be thought of as
Example
Consider the following LinkedForneymonagerie with a Sentinel node and 3 Forneymon stored in other nodes. Not shown are the other LinkedForneymonagerie fields and the other fields in each Node.

Preliminaries
Info
The revisions to the Forneymon class and subtypes of Forneymon are the same as in the previous HW — you are free to use any additional Forneymon subtypes that you made to test your last submission, or from the grading tests I provide, once posted.
Forneymon
Info
The “base game” of Battlegrounds comes with 4 Forneymon subtypes: Dampymon, Burnymon, Leafymon, and Zappymon, each with their own strengths and weaknesses. Note: These are the same from the previous assignment!
Recall that Forneymon have 3 important fields for the arena:
int health;same as before, the amount of remaining health. Each subtype 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 newenumtype, 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 includeDamageType.DAMPYfor 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.
Hint
Make sure you read through the method-level comments for
Forneymonand its subclasses to have an understanding of the tools available to you for this assignment!
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
equalsmethod that checks if two Forneymon are of the same subtype, health, and level. - Additionally, Forneymon can now be
cloned, which produces a copy of them.
Solution Skeleton
Info
Start with the solution skeleton in-hand! The following will also serve as your submission mechanism (see submission instructions below).
LinkedForneymonagerie Specifications
Info
The following section instructs you on how to implement the LinkedForneymonagerie class, as well as expectations for each method. 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 “LinkedForneymonagerie”, which is a happy medium between LinkedLists and Multi-sets used to order our Forneymon.
Similar to our IntLinkedList from class, Forneymonagerie maintain 3 fields:
Node sentinel;a reference to the Sentinel Node described above. This Node has been constructed for you in the LinkedForneymonagerie constructor.int size;the number of unique Forneymon stored in the LinkedForneymonagerie.int modCount;the number of modifications made to this LinkedForneymonagerie, which is used to determine if one of the Iterators constructed upon it is still valid or not.
The basic mechanics of a LinkedForneymonagerie support the ordered addition and retrieval of Forneymon from the collection, with a couple of quirks as follows:
- Adding a Forneymon to a LinkedForneymonagerie of a type that doesn’t currently exist within adds it to the last open index.
Danger
LinkedForneymonagerie 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 LinkedForneymonagerie 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 LinkedForneymonagerie will instead keep the higher-leveled one (replacing the lower-leveled one at the same index).
Example
Consider the following example of a LinkedForneymonagerie storing several Forneymon and representing the rules above!
// Creates a new, empty LinkedForneymonagerie
LinkedForneymonagerie fm = new LinkedForneymonagerie();
// Creates a new level-1 Burnymon...
Burnymon b1 = new Burnymon(1);
// ...and adds it to the LinkedForneymonagerie
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)]Note
The interface of our Forneymonagerie has not changed, allowing us to create a new underlying data structure (the LinkedForneymonagerie) without having to modify any of the above method calls from the previous assignment!
Info
That said, we have added some methods to our LinkedForneymonagerie that were not included in the interface.
Constructor
Tldr
The default constructor has been done for you, which constructs the Sentinel Node as described above.
Warning
You should not add any fields nor modify the constructor as-given — all you need is already there!
Methods
Your LinkedForneymonagerie class will implement the following interface, with individual method descriptions to follow.
Note: this is the same interface as in the previous HW!
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 ();
}boolean empty();
Returns true if the LinkedForneymonagerie has no Forneymon inside, false otherwise.
int size();
Returns the current size of the LinkedForneymonagerie (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 LinkedForneymonagerie’s collection, as decided by the following rules (repeated from above):
- Adding a Forneymon to a LinkedForneymonagerie of a species that doesn’t currently exist within appends it to the last open index.
Danger
LinkedForneymonagerie 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.
- Trying to add a Forneymon to the LinkedForneymonagerie 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
==). - Trying to add a Forneymon of the same species as a Forneymon already in the LinkedForneymonagerie 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 newly added to the LinkedForneymonagerie (i.e., case 1 above), false otherwise.
Hint
Calling this method should increment the calling LinkedForneymonagerie’s
modCountfield IF cases 1 (when a new species is added) or 3 (when a replacement is made) are met above! We’ll get to the purpose of thismodCountvariable later in the instructions!
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 LinkedForneymonagerie’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().
Hint
Calling this method should increment the calling LinkedForneymonagerie’s
modCountfield!
boolean releaseSpecies (String fmSpecies);
Removes the Forneymon of the given species fmSpecies from the LinkedForneymonagerie, 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 LinkedForneymonagerie, do nothing, and return false.
Hint
Calling this method should increment the calling LinkedForneymonagerie’s
modCountfield IF a Forneymon was released!
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 earliest index in the collection.
void rearrange (String fmSpecies, int index);
Moves the Forneymon of the given fmSpecies from its current position in the LinkedForneymonagerie 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();
Hint
Calling this method should increment the calling LinkedForneymonagerie’s
modCountfield IF a Forneymon was rearranged (i.e., only if the givenfmSpecieswas found AND it moved from its original position)!!
void trade (Forneymonagerie other);
Swaps the contents of the calling LinkedForneymonagerie 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 LinkedForneymonagerie.
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!
Hint
Calling this method should increment BOTH the calling LinkedForneymonagerie’s AND the other LinkedForneymonagerie’s
modCountfields!
LinkedForneymonagerie.Iterator getIterator ()
Returns a new Iterator on the calling LinkedForneymonagerie that begins on the first Node in the sequence.
See the spec details below for more info on LinkedForneymonagerie.Iterator.
Danger
Iterators cannot be created on empty LinkedForneymonagerie, and attempting to do so with this method should
throw new IllegalStateException();
NOTE
Iterators are used by the users of the class to iterate over its stored Forneymon; YOU should not employ one to iterate over the Nodes in your Linked List because you have access to the Nodes directly (and so can simply use a for-loop like what we saw in class).
@Override LinkedForneymonagerie clone ();
Returns a deep copy of this LinkedForneymonagerie, which is a new LinkedForneymonagerie 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 LinkedForneymonagerie (e.g., collecting, releasing, damaging, etc. any contained Forneymon) should NOT affect the other.
Put differently, clone should produce a copy of the calling LinkedForneymonagerie 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 LinkedForneymonagerie 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 LinkedForneymonagerie (useful for debugging too!).
This one’s on the house (you don’t need to do anything else), and displays LinkedForneymonagerie via the pattern: ForneymonSpecies [Level]: HPRemaining
For example:
LinkedForneymonagerie fm1 = new LinkedForneymonagerie();
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 ]LinkedForneymonagerie.Iterator
Info
The
LinkedForneymonagerie.Iteratorclass provides users with a means of iterating over the contents, and removing fainted Forneymon, without having to start each retrieval operation at the head!
Hint
Because our list is doubly linked, our Iterators will be capable of traveling forward and backward in the list.
Note
We will be designing a fast fail iterator for our LinkedForneymonageries, which means that our Iterators will be considered invalid (and therefore unusable) if any modifications (i.e., insertion, deletion, etc.) are done to the host LinkedForneymonagerie that were not done by the Iterator itself. In practice, this is done to ensure that the Iterator never ends up in a state where continuing its iteration could cause errors due to a desynchronized state with the underlying LinkedList.
We’ll track this rather cleverly:
- Our LinkedForneymonagerie will increment a counter (
modCount) every time they are modified. - New Iterators created on each LinkedForneymonagerie will have their modification counter (
itModCount) set to the same value. - If the Iterator itself ever modifies the LinkedForneymonagerie, then both the LinkedForneymonagerie’s
modCountand the Iterator’sitModCountare incremented, indicating that the two are still in sync. - If those values ever disagree, then it means that the LinkedForneymonagerie was modified outside of the Iterator’s influence, and so that Iterator will be considered invalid.
As such, we’ll design our iterators so that we will never unsafely use them to access or modify any element of the LinkedForneymonagerie.
Iterator Fields
private Node current;a reference to the Node that the Iterator is currently “pointing at” in the LinkedForneymonagerie.private LinkedForneymonagerie host;a reference to the LinkedForneymonagerie on which the Iterator was created.private int itModCount;the modification count for this Iterator. Valid iterators have the same modCount as their host.
Iterator Constructor
Define one parameterized constructor Iterator (LinkedForneymonagerie host); that instantiates the Iterator at the head of the given LinkedForneymonagerie (i.e., the Sentinel’s next), making sure to set its itModCounter to that of its host’s modCount.
Iterator Methods
boolean isValid ();
Returns true if this Iterator’s itModCount agrees with that of its host’s modCount AND if the host LinkedForneymonagerie has at least one element, false otherwise.
boolean atEnd ();
Returns true if the Iterator is valid and its current.next is the host’s Sentinel node, false otherwise.
boolean atStart ();
Returns true if the Iterator is valid and its current.prev is the host’s Sentinel node, false otherwise.
Forneymon getCurrent ();
Returns the Forneymon stored in the Node that the Iterator is currently pointing at (i.e., the fm field of the Node it is referring to).
If the Iterator is invalid, throw new IllegalStateException(); instead.
void next ();
Advances the Iterator’s current reference to point to the next Node in the sequence. If this next Node is the Sentinel, continue next a second time (i.e., if the Iterator points at the Sentinel after moving forward once, have it point to the one after the Sentinel instead).
If the Iterator is invalid, throw new IllegalStateException(); instead.
void prev ();
Advances the Iterator’s current reference to point to the previous Node in the sequence. If this previous Node is the Sentinel, continue previously a second time (i.e., if the Iterator points at the Sentinel after moving back once, have it point to the one before the Sentinel instead).
If the Iterator is invalid, throw new IllegalStateException(); instead.
Forneymon removeCurrent ();
Removes the Node that this Iterator references from the LinkedForneymonagerie, and then moves the iterator to the Node preceding / previous to the one deleted. The relative order of Nodes remaining in the list should be maintained. Finally, returns a reference to the Forneymon of the removed Node.
Warning
Just like
prev(), this move skips the Sentinel: if the Node preceding the deleted one is the Sentinel, the Iterator should continue on to the Node before that (i.e., wrap around to the last Node in the list). So removing the first Node leaves the Iterator pointing at the last.
Note that this Iterator may make itself invalid if it removes the final Node from the list.
If the Iterator is invalid, throw new IllegalStateException(); instead.
Danger
In your implementation of this method, you MAY NOT call the
releaseSpeciesorremovemethods from the LinkedForneymonagerie, as that will defeat the purpose of using an Iterator (since they both search from the front of the list all over).
Hint
Increments this Iterator’s
itModCountand itshost’smodCount(since this Iterator performed the modification, and so is still in sync with the host LinkedForneymonagerie).
BIG SCARY RED NOTE
The Iterator is meant to enable users of your LinkedForneymonagerie to iterate over the Nodes without knowing about / being able to manipulate the Nodes themselves. You should not use an Iterator in your other methods’ implementations, since you have direct access to the Nodes!
If you find yourself wanting to use some behavior that is both common to the iterator and another LinkedForneymonagerie method, consider making a helper method in LinkedForneymonagerie that both can call to keep code DRY.
Let’s make sure we understand how our Iterators are meant to behave (the following tests are also in the sample unit tests given):
@Test
public void testIterator_t0() {
// fm1 contains 3 Forneymon
fm1.collect(new Dampymon(1));
fm1.collect(new Burnymon(1));
fm1.collect(new Leafymon(1));
// The Iterator begins at the first Node: the Dampymon
LinkedForneymonagerie.Iterator it = fm1.getIterator();
assertTrue(it.isValid());
assertTrue(it.atStart());
assertEquals("Dampymon", it.getCurrent().getFMType());
it.next();
assertEquals("Burnymon", it.getCurrent().getFMType());
it.next();
assertEquals("Leafymon", it.getCurrent().getFMType());
assertTrue(it.atEnd());
// Note that calling next here will cause the Iterator to
// "wrap around" back to the first Node
it.next();
assertEquals("Dampymon", it.getCurrent().getFMType());
it.prev();
assertEquals("Leafymon", it.getCurrent().getFMType());
it.prev();
assertEquals("Burnymon", it.getCurrent().getFMType());
it.prev();
assertEquals("Dampymon", it.getCurrent().getFMType());
// By removing a Node outside of the Iterator's influence,
// the Forneymonagerie and Iterator are out of sync, so the
// Iterator should no longer be valid
fm1.remove(0);
assertFalse(it.isValid());
}
@Test
public void testIterator_t1() {
fm1.collect(new Dampymon(1));
fm1.collect(new Burnymon(1));
fm1.collect(new Leafymon(1));
LinkedForneymonagerie.Iterator it = fm1.getIterator();
it.next();
assertEquals("Burnymon", it.getCurrent().getFMType());
// Note that calling the removeCurrent will regress the Iterator
// to point to the Node preceding the one deleted
it.removeCurrent();
assertEquals("Dampymon", it.getCurrent().getFMType());
assertEquals(2, fm1.size());
assertFalse(fm1.containsType("Burnymon"));
// ...TODO: Test that the relative ordering of Forneymon remaining in
// the list are preserved after removing with the Iterator!
}ForneymonArena Specification
Info
Contains the logic for conducting the AutoChess battle between two Forneymonagerie!
Each fight between Forneymonagerie consists of the following steps:
- Pairs of fighting Forneymon from each Forneymonagerie are formed in sequence based on the order in which they appear in each collection. For example, for Forneymonagerie
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 Forneymonagerie. For instance, if the next index in fm2 were 3, but it contains only 3 Forneymon, then the next index will instead be 0. - 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 subtype’s takeDamage method and is based on the type of damage that the attacker deals.
- If a Forneymon is knocked out (health less than or equal to 0), it is removed from its Forneymonagerie (note that this mutates the input Forneymonagerie).
- Pairs continue to form and fight until either one (or both) Forneymonagerie are out of Forneymon!
Example
Consider again the Forneymonagerie 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.

// The above, specified programmatically
LinkedForneymonagerie fm1 = new LinkedForneymonagerie();
fm1.collect(new Dampymon(1));
fm1.collect(new Burnymon(1));
fm1.collect(new Zappymon(1));
LinkedForneymonagerie fm2 = new LinkedForneymonagerie();
fm2.collect(new Burnymon(5));
fm2.collect(new Dampymon(5));
LinkedForneymonArena.fight(fm1, fm2);
// 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: LinkedForneymonagerie 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.
Some additional notes:
- This method will indeed mutate (i.e., modify) both LinkedForneymonagerie 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 LinkedForneymonagerie before calling the fight method.
- Don’t worry about the edge case of a single Forneymon object belonging to multiple LinkedForneymonagerie — we’ll consider this user error and need not check nor test for it.
- The 3rd parameter to the
fightmethod determines whether or not it prints out the fight, so you can double check the individual steps by making thistrueif you’d like. - Although you are not required to do anything for the
ForneymonArenaclass, you’ll use it to validate that your LinkedForneymonagerie 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: Java Style Guide
Note
You are totally allowed to copy your JavaDocs from the previous HW on most of these methods, and use any of the previous grading tests to validate your proper functionality!
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 and LinkedLists).
- You may NOT add any methods or fields to the Forneymonagerie class’ public interface, nor may you add any private fields. You MAY (read: should) create any private helper 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).
- Due to the polymorphic collection of Forneymon, and the overridden takeDamage methods in each subtype, you should never have to (read: should not) downcast a Forneymon to one of its suclasses for this assignment!
- Draw, draw, then draw some more. I cannot stress how important it is to draw all of the references out for your Nodes in this assignment — VITAL!
- 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.
- Save the rearrange method for last — it’s particularly difficult when dealing with this LinkedList structure.
- Use helper methods that operate on Nodes, e.g., for appending and removing Nodes and repairing references around them. This will simplify many of your other methods.
- Use the style feedback from your previous Forneymonagerie to refine your work on this submission
- 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 (all required work is in LinkedForneymonagerie.java), in the project structure given in the skeleton above.
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.mdfile.