As usual, there are two components to this task. Both the Problem Set and Game Features - Learning Objective portions of this document MUST be completed to achieve the Learning Objective points and pass the course. The Game Features - Comprehensive Understanding is not needed for the Learning Objective requirement, but completing/not completing it will directly impact your grade through CU points. The Problem Set is worth 20 LO points, while the Game Engine Task is worth 30 LO points and 50 CU points.
You should complete the components below in the order they are listed; you will not be able to earn credit for the following component(s) until you complete the previous ones with all available points.
Coding Task 3 assesses Polymorphism and Binary Trees. Both the Problem Set and Game Engine Task will focus on these topics.
The video for this task can be found at https://www.youtube.com/watch?v=l3NGl37kjY4. This video gives an overview of the requirements for this task, namely the Game Features Learning Objective. As always, watching it is optional, but you will find it very useful.
Problem Set GitHub Repository link: https://github.com/CSE-116/ProblemSet-3
Once you have the project opened in IntelliJ, you'll see a src folder containing 2 Java packages named problem and tests.
    To submit your project, run problem.Zipper, which will create a zip file containing the
    problem set, and submit it to Autolab.
For this problem set, you will write the follow 3 static methods that are all related to binary trees (the Game Features will include Polymorphism). The signatures for these methods exist in the handout repo and the methods are all stubbed out.
problem.ProblemSet3 class, complete the maxBST method, which takes a
        BST of Integers as a parameter and returns an int. This method
        returns the maximum value of the BST according to its Comparator.
        Comparator. Maximum in this context can be thought of as the
                value that would come last if the values were fully sorted using this Comparator.
            BST is empty (ie. the root is null) this method should return 0.
            problem.ProblemSet3 class, complete the maxBinaryTree method, which
        takes a BinaryTreeNode of Integers and an Integer
        Comparator as parameters and returns an int. This method returns the maximum
        value of the binary tree, defined by the parameter node as the root of the tree, according to the
        provided comparator.
        maxBST, except the tree no longer has the structure of a
                BST. You should use the compartor to perform any comparisons that are needed to find the maximum
                value according to that comparator.
            problem.ProblemSet3 class, complete the isBST method that takes a
        BinaryTreeNode of Integers and an Integer Comparator
        as parameters and returns a boolean. This method returns true if the binary tree, defined
        by the parameter node as the root of the tree, is a valid BST according to the provided comparator, and
        false otherwise.
        
    Write JUnit tests for the following methods from the specification in the tests.TestProblemSet3
    class. These tests should be annotated with @Test.
maxBST
        maxBST method. Test that the proper value is returned in a
                variety of cases.
            Integer Comparators to create these BSTs.
            maxBinaryTree and isBST
        Implement the methods from the specification. You may wish to complete the Testing Requirements before you begin implementation, and it's suggested that you run these tests as you implement the methods.
Feedback from Autolab will be given in several phases. If you don't complete a phase, then feedback for the following phase(s) will not be given. You must complete all phases to earn the required score of 20 LOs. For the problem set, the phases will be as follows:
Once you have successfully completed all three phases, you will have completed this Problem Set and Autolab will confirm this with a score of 20 LOs.
You will continue to build functionality for the Game Engine in this component of the task. This will be done on top of the code written for the previous tasks; you should not reclone or remove said code for this task or any future Game Features components.
As with the previous tasks, there are two parts to the Game Features component: the Learning Objective and Comprehensive Understanding. The Learning Objective portion must be completed with a score of 30 LOs before the Comprehensive Understanding unlocks. Both portions of the Game Features will be submitted to the same assignment on Autolab.
    The content of the Learning Objective is primarily used in the Game Engine by Pacman. To play Pacman, during and
    after completing this Learning Objective, navigate to the class app.Configuration and change the
    GAME constant to "pacman".
It may look like there are a lot of classes to create and a lot of code to write for this task, but don't let this intimiate you. While that is true, most of the individual pieces are not overly complex, and have a lot of similarity between them. The amount of unique code you must write, and problems you must solve, is roughly in line with earlier tasks.
    You will need to use various utility methods found in the app.gameengine.utils.PacmanUtils class for
    this task. However, one of these methods is missing from the handout. You can find it here, and should copy that method into your own class.
For this Learning Objective portion, you will be writing code in the following classes which already exist:
Agent, located in the app.gameengine.model.gameobjects package.Ghost, located in the app.games.pacman package.Collectible, located in the app.gameengine.model.gameobjects package.MagicPickup, AxePickup, and PotionPickup, located in the
        app.games.commonobjects package.
    Player, located in the app.gameengine.model.gameobjects package.LevelParser, located in the app.gameengine package.
    You should create the TestTask3 class in your app.tests package. We have provided some
    tests for the various Decision and Collectible subclasses, as well as the
    LevelParser updates. They can be accessed at this link: TestTask3.java. You can open this link in a new tab to copy
    and paste the content into your project, or right click and select "save link as", then download the file to the
    correct location within the project.
These tests are not overly thorough, but should validate some of the basic behavior of the features that they test, and let you know if you are on the right track.
You will also have to create many additional classes, which will be specified in the following instructions.
Many of the features of this task, primarily dealing with the player's inventory and collectibles, heavily rely on each other, so one feature being implemented incorrectly may cause tests for other features to fail. Feedback in Autolab will usually note when this applies, but keep this in mind when looking over your feedback.
You should read through all the following sections before you begin to implement the methods from the specification. Note that you will not receive feedback on the correctness of these methods until you've completed the testing component of this task (see the "Autolab Feedback" section for more details). However, you must at least create every class/method from this specification to receive feedback. You can "stub out" these methods by having them always return a fixed value, but they must exist so the grading code, and your tests, can compile and run.
    Some of the methods in this task will make use of the BinaryTreeNode class, found in the
    app.gameengine.model.datastructures package. You are required to use this class. It is identical to the
    BinaryTreeNode class used in the Problem Set. Note that this is not the BST class
    from the Problem Set.
For the Learning Objective portion of the Game Features component for this task, you will implement the following functionality:
        Create the package app.gameengine.model.ai. Remember that a package is just a folder containing
        Java files. This package will contain a number of files that allow the ghosts in Pacman to have dynamic behavior
        depending on the state of the game at any given time.
    
        You will be using a decision tree to determine behavior, which will be represented as a binary tree of
        Decision objects. These decisions will determine either which action to take, depending on the game
        state, or perform an action, like movement.
    
Decision: In the app.gameengine.model.ai package, create an
        abstract class named Decision. This represents a node in the decision
        tree that will either make a decision or take an action.
        Agent and a String as parameters, and
                store them as instance variables. These represent the Agent whose behavior is being is
                being controlled by this Decision, and the name of this node.
            getName and setName, which are a getter and setter for
                the String name from the constructor. The purpose of the name is primarly for testing, and
                for differentiating nodes of the same type within a single tree.
            getAgent which is a getter for the Agent from the
                constructor. You do not need to create a setter for this instance variable.
            decide which returns a boolean
                and takes in a double and a Level, representing the time since the last call
                and the current level, respectively.
                doAction which returns
                void and takes in a double and a Level, representing the time
                since the last call and the current level, respectively.
                Agent to achieve the desired behavior.
                        This method will only be called if it is determined that this is the action that should be
                        taken, according to the rest of the decision tree.
                    DecisionTree: In the app.gameengine.model.ai package, create a class named
        DecisionTree. This class will possess a binary tree of Decision objects, to determine
        what behavior an Agent should take, and accordingly take that action.
        BinaryTreeNode of Decisions, and store
                it as an instance variable.
            getTree and setTree, which are a getter and setter for
                the BinaryTreeNode from the constructor.
            traverse that returns a Decision and takes a
                BinaryTreeNode of Decisions, a double, and a Level
                as parameters. The double represents the amount of time passed since the last update, and
                the Level is the current level.
                decide on
                        the Decision stored in each tree node. A value of true indicates that you should
                        "travel" to the right child node, and a value of false means travel to the left child node.
                    Decision value at that node should be returned. Keep in mind that the node in the
                        parameter itself might be a leaf node.
                    decide, you should pass in the double and
                        Level from the parameters as arguments.
                    decide method returns true, you cannot traverse to the right, and should return
                        null. This is useful for handling poor tree structures, or having a state that performs no
                        action.
                    traverse method that returns void and takes a
                double and a Level, which represent the amount of time passed since the last
                update and the current level, respectively.
                traverse method, passing the root of the tree (the
                        instance variable) as the first argument, and the parameters as the remaining arguments. Then
                        call doAction on the returned Decision, again using the parameters of
                        this method as the arguments.
                    Decision is null, this method will do nothing.
                    
        Now you will create subclasses of the Decision class which implement behavior for the ghosts in
        Pacman. You should look at the app.gameengine.utils.PacmanUtils class, as you will need to use
        these methods within your Decision subclasses. You do not need to understand all of this code, and
        the specific methods to use will be mentioned each time. If you did not already, download the missing method
        canAct from here and add it to this class.
    
Though you do not need to understand the implementation of these methods, you should understand what they do. They each include a Javadoc comment that explains the purpose of the method, the parameters, and the return value. In IntelliJ, you can hover over the name of a method to read the Javadocs.
        All of these classes will exist in the app.gameengine.model.ai.pacman package, which you should
        create.
    
        Remember that the Decision class has two main functionalities, the decide and
        doAction methods. For some subclasses, only one of these will be implemented, and such classes will
        only make sense in certain parts of the tree. For example, a subclass which stubs out the decide
        method and actually implements the doAction method would only make sense as a leaf node.
    
Other decisions and actions are so closely related that it makes sense to include them within the same class. These classes could be placed anywhere within the tree, and in fact there will likely be duplicates, where one is used as the decision, and the other is used as the action.
IsActive: In the app.gameengine.model.ai.pacman package, create a class named
        IsActive which extends Decision. This class will represent a decision on whether a
        ghost is in an active state, which is either chasing the player or retreating to one corner of the level
        (scattering).
        Ghost and a String, which are passed to
                the super constructor as the Agent and name. This is possible due to polymorphism, since
                Ghost is a subclass of Agent. You should also store the ghost as an instance
                variable.
            decide method, which should return true if the Ghost
                from the constructor is in a chasing or scattering state, and false otherwise.
                getState method on a Ghost to access its state. This
                        method returns a String representing which of several states the ghost is in.
                    "Chase" or "Scatter", then the ghost is
                        considered to be in an active state, and this method should return true.
                    doAction method to do nothing.
            Idle: In the app.gameengine.model.ai.pacman package, create a class named
        Idle which extends Decision. This class will represent an action of bouncing up and
        down while the ghost is at its home location in the center of the level.
        Ghost and a String, which are passed to
                the super constructor as the Agent and name. You should also store the ghost as an instance
                variable.
            decide method to always return false.
            doAction method to do the following: If the magnitude of the ghost's velocity
                is greater than 0, this method should do nothing. Otherwise, you should negate both the x and y
                components of the ghost's
                orientation. Afterwards, you should call the followPath method on the Ghost
                object, passing in the double from the parameter.
                magnitude method within the Vector2D
                        class.
                    setOrientation method. Although
                        there exists a negate method in the Vector2D class, which negates both
                        the x and y components of a Vector2D, the setOrientation method is
                        needed to update the sprite of the ghost.
                    followPath method is the same as the one from the Task 2 CU, but is
                        implemented differently within the Ghost class. So, even if you did not complete
                        that CU, this will still function as intended.
                    Chase: In the app.gameengine.model.ai.pacman package, create a class named
        Chase which extends Decision. This class will represent both a decision of whether a
        ghost is actively chasing the player, as well as the action of chasing.
        Ghost, a PacmanGame, and a
                String. You should pass the Ghost and String to the super
                constructor, and store the Ghost and PacmanGame as instance variables. You
                should create an additional Vector2D instance variable to store the last whole-numbered
                location of the ghost, which will be used by several of the utility methods provided. The initial value
                of this vector should be null.
            decide method to return whether the ghost is in the chasing state.
                getState method on a Ghost to access its state. This
                        method returns a String representing which of several states the ghost is in.
                    "Chase", then the ghost is chasing the player, and this
                        method should return true.
                    doAction method. This method should use the methods in the
                PacmanUtils class to do the following sequence of actions. Only take these actions if the
                PacmanUtils.canAct method returns true, passing in the Ghost and
                Vector2D instance variables and the double parameter.
                PacmanUtils.getChaseTarget
                        method. Note that you must pass in a PacmanLevel, so you cannot use the
                        Level parameter. Instead you should call getCurrentLevel on the
                        PacmanGame instance variable.
                    PacmanUtils.getValidDirs method, again with the game's current level and the ghost.
                    PacmanUtils.getBestDirection
                        method, passing in the valid directions, the location of the ghost, and the target
                        Vector2D.
                    Vector2D returned by getBestDirection may be null. If so, skip
                        this step. If it is not null, set the orientation of the ghost to the x and y components of that
                        Vector2D. Again, you should use the setOrientation method to do so.
                    Vector2D instance variable equal to the rounded value of the ghost's
                        location. You may call Math.round on the individual components, or use the
                        Vector2D.round method.
                    followPath method on the ghost object, passing in the double
                parameter. You should do this even if the canAct method returns false.
            Scatter: In the app.gameengine.model.ai.pacman package, create a class named
        Scatter which extends Decision. This class will represent the action of scattering,
        which is when the ghosts retreat to the corners of the level to give the player some breathing room.
        Ghost, a PacmanGame, and a
                String. You should pass the Ghost and String to the super
                constructor, and store the Ghost and PacmanGame as instance variables. You
                should create an additional Vector2D instance variable to store the last whole-numbered
                location of the ghost, which will be used by several of the utility methods provided. The initial value
                of this vector should be null.
            decide method to always return false.
            doAction method. The behavior of this method should be identical to that of
                the Chase class, except using the PacmanUtils.getScatterTarget method instead
                of the PacmanUtils.getChaseTarget method.
            Dead: In the app.gameengine.model.ai.pacman package, create a class named
        Dead which extends Decision. This class will represent the decision of whether a ghost
        has been eaten and should return to the spawn area, as well as the action of returning home.
        Ghost, a PacmanGame, and a
                String. You should pass the Ghost and String to the super
                constructor, and store the Ghost and PacmanGame as instance variables. You
                should create an additional Vector2D instance variable to store the last whole-numbered
                location of the ghost, which will be used by several of the utility methods provided. The initial value
                of this vector should be null.
            decide method to return whether the ghost is in the dead state.
                getState method on a Ghost to access its state. This
                        method returns a String representing which of several states the ghost is in.
                    "Dead", then the ghost is dead, and this method should
                        return true.
                    doAction method. The behavior of this method should be identical to that of
                the Chase class, except using the PacmanUtils.getHomeTarget method instead
                of the PacmanUtils.getChaseTarget method. Note that this method only takes one argument,
                and does not need the ghost itself to be passed in.
            Flee: In the app.gameengine.model.ai.pacman package, create a class named
        Flee which extends Decision. This class will represent the decision of whether a ghost
        has is fleeing from the player, which happens when a power pellet has been eaten, as well as the action of
        running from the player, which involves moving in a random direction at each intersection.
        Ghost, a PacmanGame, and a
                String. You should pass the Ghost and String to the super
                constructor, and store the Ghost and PacmanGame as instance variables. You
                should create an additional Vector2D instance variable to store the last whole-numbered
                location of the ghost, which will be used by several of the utility methods provided. The initial value
                of this vector should be null.
            decide method to return whether the ghost is in the frightened state.
                getState method on a Ghost to access its state. This
                        method returns a String representing which of several states the ghost is in.
                    "Frightened", then the ghost is fleeing, and this method
                        should return true.
                    doAction method. The behavior of this method is similar to but not exactly
                the same as that of the Chase class. Take the following actions if the
                PacmanUtils.canAct method returns true, passing in the Ghost and
                Vector2D instance variables and the double parameter.
                PacmanUtils.getValidDirs method, again with the game's current level and the ghost.
                    PacmanUtils.getRandomDirection
                        method, passing in the valid directions.
                    Vector2D returned by getRandomDirection may be null. If so, skip
                        this step. If it is not null, set the orientation of the ghost to the x and y components of that
                        Vector2D. Again, you should use the setOrientation method to do so.
                    Vector2D instance variable equal to the rounded value of the ghost's
                        location. You may call Math.round on the individual components, or use the
                        Vector2D.round method.
                    followPath method on the ghost object, passing in the double
                parameter. You should do this even if the canAct method returns false.
            Agent: Navigate the to app.gameengine.model.gameobjects.Agent class. Each
        Agent will possess a DecisionTree to determine and follow their behavior.
        DecisionTree within this class. It will initially be
                null.
            DecisionTree, named getDecisionTree and
                setDecisionTree respectively.
            update method from the super class, which takes a double and a
                Level as parameters and returns void.
                DecisionTree instance variable is not null, call traverse on
                        it. You should call the version of the method which does not take in a
                        BinaryTreeNode. If the DecisionTree is null, you do not need to do
                        anything.
                    Agent paths, you may keep that
                        here. You can place it before or after the new code.
                    Ghost: Navigate to the app.games.pacman.Ghost class. You will modify the constructor
        to give ghosts a decision tree matching classic pacman behavior. You can add this anywhere in the constructor.
        Ghost extends Enemy, which extends Agent, so
                Ghost has access to all of the methods in the Agent class.
            IsActive node.
                Chase node, acting as a decision;
                        if the ghost is in the chase state (to the right), there is another Chase node,
                        this time acting as an action; if the ghost is not in the chase state (to the left), there is a
                        Scatter node.
                    Dead node, acting as a
                        decision; if the ghost is dead, there is another Dead node, this time acting as an
                        action; if the ghost is not dead, there is a Flee node, acting as a decision; from
                        here, if the ghost is fleeing, there is another Flee node, this time acting as an
                        action; if the ghost is not fleeing, there is an Idle node.
                    DecisionTree structure: 
            app.Configuration and change the GAME constant to "sample game".
    Player class that are referenced here do not exist yet. You may want to
    implement those methods first, or along with these classes and methods.
    Collectible: Navigate to the app.gameengine.model.gameobjects.Collectible class, which
        is an abstract class representing an object that can be picked up and used by the player. Currently this class
        contains very little, but you will add extra functionality by adding methods and extending this class.
        getItemID (note the capital "ID"), which is a getter for the itemID
                instance variable. It should take no parameters and return a String.
            use, which takes a
                Level as a parameter and returns void. This represents what occurs when the
                Collectible is used in game by the player.
            collideWithDynamicObject method, which takes one
                DynamicGameObject parameter and returns void. If the dynamic object is the
                player, add this object to the player's inventory, and destroy this object.
                GameObject is a Player
                        object by calling the isPlayer method on it.
                    addInventoryItem
                        method. This method likely does not exist yet. Read ahead to see the specification for that
                        method.
                    Player class instead of the
                        DynamicGameObject class, you cannot call it on the parameter. Instead, call
                        getPlayer on the game that you stored as an instance variable.
                    destroy method on it. This will remove
                        it from the level, so that it is not rendered on the screen anymore and will not continue
                        colliding with the player.
                    AxePickup: navigate to the app.games.commonobjects.AxePickup class, which is a pickup
        that should allow the player to throw axe projectiles at enemies. Notice that this class extends
        Collectible, and that the name passed to the super constructor is "Axe". Add the following
        functionality.
        Timer. Use the app.gameengine.utils.Timer
                class, not one of the built-in Java timers. In the constructor, assign this instance variable to a new
                Timer with a cooldown of 0.25.
                Timer class allows objects to limit how frequently they can use used. In this
                        case, you will use the timer to limit how frequently axes can be thrown. You should look through
                        the methods in this class, but do not need to fully understand them.
                    Timer instance variable called getTimer, which takes
                no parameters and returns a Timer.
            use method from the Collectible class. If the
                cooldown period on the timer is up, this method should make the player fire an axe projectile.
                Timer.check method on your
                        instance variable.
                    Level parameter and call getPlayer to
                        get the Player object. On this Player, call
                        fireProjectile, which takes three arguments. The first argument is the projectile
                        to fire, which should be a new PlayerAxeProjectile object. The location of this
                        projectile does not matter, as it will be set within the fireProjectile method. The
                        second argument is the speed the projectile will be fired at, which should be set to exactly
                        5. The third argument should be the Level parameter.
                    update method, which has parameters of type double and
                Level, representing the time since the last update and the current level, respectively, and
                returns void.
                Timer.advance on the Timer instance variable, passing in the
                        double parameter dt. This will ensure that the cooldown actually
                        decreases and the pickup can be used.
                    MagicPickup: Navigate to the app.games.commonobjects.MagicPickup class, which is a
        pickup that should allow the player to fire magic projectiles at enemies. Notice that this class extends
        Collectible, and that the name passed to the super constructor is "Magic". You should create the
        same instance variables and methods as in the AxePickup class. The only difference should be that
        in the use method, when calling fireProjectile, you should pass in a
        MagicProjectile object, and a speed of 10.
    PotionPickup: Navigate to the app.games.commonobjects.PotionPickup class, which is a
        pickup that should allow the player to regain some health when used. Notice that this class extends
        Collectible, and that the name passed to the super constructor is "Health Potion". Add the
        following functionality.
        int, and in the constructor, assign this instance
                variable to the constructor parameter "heal". This is the amount that the potion will heal the player
                by.
            getHealAmount, which takes no
                parameters and returns an int.
            use method from the Collectible class. Increase the
                health of the player by the heal instance variable, and remove the potion from the player's inventory.
                getPlayer method on the Level
                        parameter.
                    getHealth and
                        setHealth methods respectively.
                    removeActiveItem method. This method likely does not exist yet. Read ahead to see
                        the specification for that method.
                    app.gameengine.model.gameobjects package, make the following additions to the
    Player class to implement a functioning inventory system. Some of these methods will already exist, and
    you should just modify them. Others you will have to create.
    addInventoryItem: Create a method named addInventoryItem that takes in a
        Collectible and returns void. This method should add the object from the parameter to
        the Player's inventory.
        removeActiveItem: Add a method named removeActiveItem which takes no parameters and
        returns void. This method should remove whatever the currently active item is from the
        Player's inventory.
        getActiveItem: Add a method named getActiveItem that takes no parameters and returns a
        Collectible. This method should return whichever Collectible in the inventory is
        active.
        getActiveItemID: Add a method called getActiveItemID (note the capital "ID") that
        takes in no parameters and returns a String.
        Collectible from the inventory, by calling
                the Collectible.getItemID method on it.
            String "No item equipped".
            cycleInventory: Add a method called cycleInventory that takes no parameters and
        returns void. This method will cycle through the inventory, changing the item currently marked as
        active.
        clearInventory: Add a method called clearInventory that takes no parameters and
        returns void. This method will reset the inventory to its initial state, which should be empty.
    update: The update method already exists in this class. Add code which iterates over every item in
        the inventory and calls the update method on it, passing in both parameters.
        Level.actionButtonPressed method, which will call the getActiveItem method, and will
        call use on the returned object. This means that whichever object is currently active will be used,
        allowing you to fire projectiles or use potions.
    LevelParser class so that the kinds of
    collectibles can be parsed. The sample game already contains an AxePickup and a
    MagicPickup in a couple of the levels. If you want to use the PotionPickup in game, you
    can add it to one of the levels. An example of this would be adding this line to one of the level csv files:
    "StaticGameObject,PotionPickup,13,13,50".
    readStaticObject: Modify this method so that it can recognize and return the following objects.
        AxePickup objects. If the string being checked is "AxePickup", a new
                AxePickup object should be returned. The AxePickup constructor
                takes two doubles and a Game. The doubles should be the variables
                x and y, and the Game should be the parameter game,
                all of which already exist in this method.
            MagicPickup objects. If the string being checked is "MagicPickup", a new
                MagicPickup object should be returned. The MagicPickup constructor
                takes two doubles and a Game. The doubles should be the variables
                x and y, and the Game should be the parameter game,
                all of which already exist in this method.
            PotionPickup objects. If the string being checked is "PotionPickup", a new
                PotionPickup object should be returned. The PotionPickup constructor
                takes two doubles, an int, and a Game. The doubles
                should be the variables x and y, and the Game should be the
                parameter game, all of which already exist in this method. The int should be
                the amount to heal, which is the fifth value in the line. Refer to the example above, where the amount
                to heal would be 50.
            
    In the app.tests package, create a class called TestDecision, which extends the
    Decision class. This class will be used to help you test by simplifying the behavior of the
    decide and doAction methods. Specifically, we want the behavior of these methods to not
    depend on the actual state of the game, so that you can test the DecisionTree methods without setting
    complex properties of the game, player, and ghosts.
    The constuctor should take an Agent, a String, and a boolean. The first two
    parameters can be passed to the super constuctor, and the last should be stored as an instance variable. This
    boolean will be the value returned by decide, so it will decide which direction the traversal
    progresses.
    You should create another boolean instance variable representing whether the TestDecision
    has been used. It should initially be false.
    Create a method called isUsed, which takes no parameters and returns a boolean. It should return the
    instance variable described above.
Override the decide method to return the instance variable for the traversal direction.
Override the doAction method to set the instance variable for whether it has been used to true.
    Write JUnit tests for the following methods from the specification in the TestTask3 class. These tests
    should be annotated with @Test.
DecisionTree:
        Decision objects to populate the
                DecisionTree. You are highly encouraged to use your TestDecision class.
            traverse methods in this class.
            BinaryTreeNode returns the proper
                Decision, or null, with a variety of trees.
            doAction on the correct Decision node, and
                only on that node.
            Player:
        addInventoryItem and cycleInventory:
                cycleInventory will go through every added
                        Collectible and wrap around to the beginning.
                    removeActiveItem:
                removeActiveItem properly removes the active
                        item from the inventory and sets the new active item.
                    Implement the methods from the specification. You may wish to complete the Testing Requirements before you begin implementation, and it's suggested that you run these tests as you implement the methods.
Feedback for the Learning Objective component of the Game Features will be given in several phases. If you don't complete a phase, then feedback for the following phase(s) will not be given. You must complete all the following phases to earn the required score of 30 LOs to complete this task. The phases for the Learning Objective are as follows:
Once you have successfully passed all four phases, you will have completed the Learning Objective component of the Game Features, and Autolab will confirm this with a score of 30 LOs.
    For the Comprehensive Understanding portion of this task, you will implement functionality in the
    app.gameengine.statistics.Scoreboard class, and several classes that you will create in the
    app.gameengine.model.datastructures package, in order to create a functional scoreboard for several of
    the games. At the moment, the only games with scoreboards are Minesweeper and Pacman. Each of these games adds a
    score to the scoreboard whenever you win a game.
Each of the classes and methods you implement will have an associated CU count that you will earn upon passing our tests for that method. Partially implemented methods may result in some partial credit being earned, depending on the method.
app.gameengine.statistics.GameState
                class. It is a small, simple class, so you should look at and understand this class.
            app.gameengine.model.datastructures.Comparator class. Currently, this is a
                concrete class with a stubbed out method called compare. Modify this class such that it is
                an interface instead of a class, and make this method abstract. Do not modify the rest of the method
                signature. This is not worth any CUs on its own, but is required for the rest of the comparator classes.
                Scoreboard class, which tries to
                        instantiate a Comparator. You can fix this by changing this to be any of the
                        classes which implement the Comparator below, or by removing this constructor
                        entirely. If you remove the constructor, you should call the other constructor from within the
                        Game class when creating the scoreboard instance variable. Any of these solutions
                        are fine, and we won't test this constructor, or the default comparator.
                    LevelNameComparator: In the app.gameengine.model.datastructures package,
                create a class named LevelNameComparator. This should implement the
                Comparator<GameStat>.
                compare method to compare two GameStat objects based on
                        their entry names. If the first name is lexicographically less than the second, this method
                        should return true, and it should return false in all other cases.
                    String.compareTo method useful for performing this comparison.
                    false, since a
                        String cannot be lexicographically less than itself.
                    PlaytimeComparator: In the app.gameengine.model.datastructures package, create
                a class named PlaytimeComparator. This should implement the
                Comparator<GameStat>.
                compare method to compare two GameStat objects based on
                        their playtimes. If the first playtime is less than the second, this method should return
                        true, and it should return false in all other cases.
                    false.ScoreComparator: In the app.gameengine.model.datastructures package, create a
                class named ScoreComparator. This should implement the
                Comparator<GameStat>.
                compare method to compare two GameStat objects based on
                        their scores. If the first score is greater than the second, this method should return
                        true, and it should return false in all other cases.
                    false.app.gameengine.statistics.Scoreboard class. Modify the constructor which
                takes two parameters so that the Comparator is stored as an instance variable.
            getComparator and
                setComparator respectively.
            BinaryTreeNode of GameStats. This will
                act as a BST to store and sort the scores in a game according to the comparator.
            getScoreTree and
                setScoreTree, respectively.
            addScore method to add the input GameStat to the correct
                location in the tree, according to the comparator.
                setComparator method will only ever be called before any scores are added.
                    loadStats method (5 CUs)
        loadStats method to load the existing stats from previous games. This
                will ensure that high scores carry carry over when the game is closed and restarted. Note that this
                method requires a functioning implementation of the addScore method.
            statsPath instance variable will be the location of the csv file which stores
                the stats. Each csv file will have one score per line, as "entryName,playTime,score". You should read
                each line in this file, parse it to create a GameStat object corresponding to that line,
                and add it to the tree using the addScore method.
            getScoreList methods (25 CUs)
        getScoreList method to return a LinkedListNode that is
                the head of a sorted list containing every element in the tree, according to the comparator.
            SnakeGame constructor, you
        should set the comparator to the desired metric. You must then choose a place to add a score to the scoreboard.
        It would likely make the most sense to add this in both the advanceLevel and
        resetCurrentLevel methods, so that both winning and losing the game save the score. In either case,
        you can use the getScoreboard method to get the scoreboard, and then add a new
        GameStat entry. You can use the getScore and getPlaytime methods of the
        Level class to create the GameState.
    The Comprehensive Understanding tests will unlock once you have earned 30 LOs on the assignment. These will be run in a single phase for correctness. For each test you successfully pass, you will earn a varying amount of CUs with a total maximum score of 50 CUs.
There is no required testing component for the Comprehensive Understanding; however, you are expected to test your code nonetheless as the Autolab feedback is unlikely to be sufficient for debugging.