As with the previous and future tasks, there are two components to this task.
To reiterate, 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. Each Problem Set will carry 20 LO points, while each Game Engine Task will carry 30 LO points and 50 CU points.
    Note: Although the CU content is not required, some of it may benefit you in later tasks. Specifically, writing the
    Agent.followPath method will make testing your Task 4 code in-game much easier.
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 2 assesses Linked Lists and Inheritance. Both the Problem Set and Game Engine Task will focus on these topics.
The video for this task can be found at https://youtu.be/a_fr2sE5GZ8. 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-2
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.
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.
For this Problem Set, you will implement the following functionality. Note that all of the methods are public and non-static.
Monster: Create an abstract class in the problem package
        named Monster. This class should implement the following methods:
        String and an int
                representing the name and maximum health of the monster. The current health of the monster should
                initially be set to the maximum amount.
                Monster is created with a max health of 10, the initial health
                        would also be 10. The max health should never change, even if the current health does.
                    getName: create a method named getName which takes no arguments and returns a
                String. This method should return the name of the Monster.
            setName: create a method named setName which takes a single
                String and returns void. This method should set the name of the
                Monster to the input value.
            getHP: create a method named getHP which takes no arguments and returns an
                int. This method should return the current health of the Monster.
            setHP: create a method named setHP which takes a single int and
                returns void. This method should set the current health of the Monster to the
                input value.
            getMaxHP: create a method named getMaxHP which takes no arguments and returns
                an int. This method should return the maximum health of the Monster.
            attack: create an abstract method named attack which takes a
                reference
                to a Monster object and returns void.
            Skeleton: Create a class in the problem package named Skeleton. This
        class should extend the Monster class described above. This class should implement the following
        methods:
        String two ints
                representing the name, maximum health, and damage of the skeleton, respectively.
            getDamage: create a method named getDamage which takes no arguments and
                returns an int. This method should return the damage value of the Skeleton.
            setDamage: create a method named setDamage which takes a single
                int and returns void. This method should set the damage value of the
                Skeleton to the input value.
            reanimate: create a method named reanimate which takes no arguments and
                returns void. If the Skeleton's health is less than or equal to 0, it should
                set it to the
                maximum health of that object. If its health is greater than 0, it should do nothing.
            attack: override the abstract method of the same name from the Monster class.
                It should take a reference to a Monster object and return void. This method
                should decrease
                the health of the Monster parameter by the damage of that Skeleton.
                Skeleton was created with a damage of 5, and a
                        Monster with a health of 8 was passed in, its health would be decreased to 3.
                    Skeleton was created with a damage of 5, and a Monster with a health
                        of 1 was passed in, its health would be decreased to -4.
                    StringList: Create a class in the problem package named StringList.
        This class will contain methods for using and manipulating LinkedListNodes of Strings.
        You must use the LinkedListNode class provided, which can be found in
        problem.datastructures. This class should implement the following methods:
        getList: create a method named getList which takes no arguments and returns a
                LinkedListNode of Strings. This method should return the entire linked list of
                strings.
            setList: create a method named setList which takes a
                LinkedListNode of Strings as an argument and returns void. This
                method should
                set the linked list of strings to the input value.
            addElement: create a method named addElement which takes a single
                String as an argument and returns void. This method should append the input
                String to the end of the linked list.
            getMidpoint: create a method named getMidpoint which takes no arguments and
                returns a String. This method should return the value at the middle index of the linked
                list.
                String.
                    deleteElement: create a method named deleteElement which takes a
                String as an argument and returns void. This method should remove the first
                occurrence of
                the input String from the list.
                
    Create a class in the tests package named TestUtils. In this class, create a
    static method named compareListsOfStrings that takes in two
    LinkedListNodes of Strings and returns void. This method should contain JUnit
    asserts to
    verify that the two linked lists are of the same length and contain all the same Strings. If either the
    length or content of the lists are not the same, this method should fail a JUnit assert.
    Note that this method is not a test, and thus should not have the @Test annotation. Rather, this is a
    static method intended to be used in your testing. You are encouraged to use this method to simplify your test
    cases.
    Write JUnit tests for the following methods from the specification in the tests.TestProblemSet2 class.
    These tests should be annotated with @Test.
StringList
        getMidpoint method. Test that the proper value is returned in a variety
                of cases.
            deleteElement method. Tests for this method are provided for
                you in the TestProblemSet2 class. They are likely commented out, and must be uncommented
                for you to use them. You can uncomment multiple lines by selecting the lines and pressing ctrl+/
                (command+/ on mac). You are encouraged to use these tests to help you write the methods they are
                testing.
            Monster and Skeleton
        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 Mario. To play Mario, during and after
    completing this Learning Objective, navigate to the class app.Configuration and change the
    GAME constant to "mario".
For this Learning Objective portion, you will be writing code in the following classes which already exist:
LinearGame, located in the app.gameengine package.
    PhysicsEngineWithGravity, located in the app.gameengine.model.physics package.
    LevelParser, located in the app.gameengine package.
    TestUtils, located in the app.tests package.
    
    You should create the TestTask2 class in your app.tests package. We have provided some
    tests for the PhysicsEngineWithGravity and LevelParser classes for you, which can be
    accessed at this link: TestTask2.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.
One of the tests relies on testing files, which should all be placed in the directory "data/levels/testing". They are: mario1.csv, mario2.csv, and medium.csv
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.
    Many of the methods in this task will make use of the LinkedListNode class, found at
    app.gameengine.model.datastructures. You are required to use this class. It is identical to the
    LinkedListNode class used in the Problem Set.
For the Learning Objective portion of the Game Features component for this task, you will implement the following functionality:
LinearGame class. This class represents a Game
        with a linear sequence of levels that are typically only traveled through in order from start to finish. For
        example, the MarioGame class extends the LinearGame class, and uses these methods.
        getLevelList
                getLevelList which takes no parameters and returns a
                        LinkedListNode of Levels. It should return the head of the game's list
                        of levels.
                    null.
                    setLevelList
                setLevelList which takes a LinkedListNode of
                        Levels as an argument and returns void. It should replace this game's
                        list of
                        levels with the input value.
                    addLevel
                addLevel which takes a Level object as an
                        argument and returns void. This method should append the input Level
                        to the end of
                        this game's list of levels.
                    advanceLevel
                advanceLevel which takes no parameters and returns
                        void. When
                        this method is called, it should find the current level within the list of levels, and call
                        this.loadLevel on the next level in the list.
                    getCurrentLevel method to access the current level. If the
                        current level is not in the level list, including when the list is empty, this method should
                        not do anything.
                    getName method on each level object to determine if it is the
                        current level (ie. if the names are equal). This method may assume that there will not be
                        multiple levels with the same name in the list.
                    loadLevel with an argument of null.
                    removeLevelByName
                removeLevelByName which takes a String and
                        returns void. It should remove the first level in the list which has the same name
                        as the input
                        String.
                    Levels in the list exist with this name, only the first should be
                        removed.
                    getName method on each level object to determine if its name is
                        the same as the input String.
                    reset
                reset method on it.
                    this.currentLevel directly, and set it to the desired value. Note that this
                        instance variable is of type Level, not LinkedListNode.
                    reset called on it.
                        Otherwise, the player will start at the wrong location.
                    PhysicsEngineWithGravity class. Notice that this class
        extends the PhysicsEngine class, and as such has access to all of the public methods from that
        class, including those that you wrote in the previous task.
        double as a parameter and represents the amount of gravity.
                        You should create an instance variable to store the gravity, and assign it in the constructor.
                    getGravity
                getGravity that takes no parameters and returns a
                        double. This method should return the gravity of this physics engine. Note that
                        this is different from the static variable "DEFAULT_GRAVITY". You should return the instance
                        variable that was described above.
                    setGravity
                setGravity that takes a double and returns
                        void.
                        This method should set the gravity instance variable to the input value.
                    updateObject
                updateObject which takes a double and a
                        DynamicGameObject and returns void. This method should override the
                        method of the
                        same name in the PhysicsEngine class.
                    DynamicGameObject's
                        y-velocity (note that change in velocity is equal to [acceleration from] gravity times the
                        change in time) (note also that the positive y direction is down).
                    updateObject method and pass in the parameters. Be sure to
                        call this after applying gravity, to make sure that velocity and position are
                        appropriately adjusted.
                    isOnGround method on the DynamciGameObject from the parameter,
                        which returns true if the object is on the ground, and false otherwise.
                    isPlayer
                        method on the DynamicGameObject from the parameter, which returns true if the
                        object is the player, and false otherwise.
                    PhysicsEngineWithGravity object instead of a basic PhysicsEngine will
                now have gravity applied automatically. You can play Mario, as described above, to see the effects (as
                long as you have also made the required changes to the LevelParser).
            LevelParser class. These changes will allow the game engine
        to parse levels which contain objects for Mario.
        readDynamicObject
                Goomba objects. If the string being checked is exactly "Goomba", a
                        new Goomba object should be returned. The Goomba constructor takes
                        only two doubles, which should be the variables x and y
                        which already exist in this method.
                    Koopa objects. If the string being checked is exactly "Koopa", a new
                        Koopa object should be returned. The Koopa constructor takes only two
                        doubles, which should be the variables x and y
                        which already exist in this method.
                    readStaticObject
                readDynamicObject, this method uses a switch-case statement.
                    Block objects. If the string being checked is "Block", "Bricks", or
                        "Ground", a new Block object should be returned. The Block constructor
                        takes two doubles and a String. The doubles should be the
                        variables x and y which already exist in this method. The
                        String should be the same as the String being checked. For example, if
                        the String is "Bricks", "Bricks" should be passed into the constructor.
                    QuestionBlock, HiddenBlock,
                        PipeEnd, PipeStem. For each of these objects, the String
                        should exactly match the class name (eg. "QuestionBlock" or "PipeStem"), and the only
                        constructor parameters are two doubles, which should be the variables
                        x and y which already exist in this method.
                    Flag objects. If the string being checked is exactly "Flag", a new
                        Flag object should be returned. The Flag constructor takes two
                        doubles and a Game, which should be the variables x,
                        y, and game which already exist in this method.
                    parseLevel
                TopDownLevel or a MarioLevel
                        depending on the content of the csv file being read.
                    TopDownLevel object. If that field is "MarioLevel", it should be a
                        MarioLevel object instead.
                    LevelParser should still be capable of reading and parsing all previously assessed
                levels. However, now it can also create levels for Mario with the appropriate objects.
            
    In the app.tests.TestUtils class, create a static method named
    compareListsOfLevels that takes in (references to) two LinkedListNodes of
    Levels and returns void. This method should contain JUnit asserts to verify that the two
    linked lists
    are of the same length and contain all the same levels. Two levels can be considered the same if they have the same
    name. Recall that you can access the name of a Level object by calling the getName method
    on it. If either the length or content of the lists are not the same, this method should fail a JUnit assert.
    Note that this method is not a test, and thus should not have the @Test annotation. Rather, this is a
    static method intended to be used in your testing. You are encouraged to use this method to simplify your test
    cases.
    Write JUnit tests for the following methods from the specification in the TestTask2 class. These tests
    should be annotated with @Test:
    Many of these methods require you to make Level objects to test. However, the Level class
    is abstract, so you cannot directly instantiate Levels. You should instead create
    TopDownLevels. You will still be able to pass these objects in to methods which take a
    Level as an argument due to polymorphism, which will be taught in greater detail later.
LinearGame
        addLevel
                advanceLevel
                getCurrentLevel method to access the current level of the game.
                    advanceLevel, you should call loadLevel on the game
                        to make sure there is a level loaded.
                    getLevelList method.
                    removeLevelByName
                PhysicsEngineWithGravity
        getGravity,
                setGravity, and updateObject. Tests are provided for you as described in the
                Overview section above, which you may use to help you write the methods themselves.
            LevelParser
        readDynamicObject,
                readStaticObject, and parseLevel. Tests are provided for you as described in
                the Overview section above, which you may use to help you write the methods themselves.
            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 be implementing functionality in the
    Agent class (from the app.gameengine.model.gameobjects package) and the
    PathfindingUtils class (that you should create in the app.gameengine.utils package) such
    that enemies have somewhat functional pathfinding. This will be most obvious in the Sample Game, which you can
    access by changing the GAME constant in app.Configuration to
    "Sample Game".
Each of the 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.
findPath (30 CUs)
        app.gameengine.utils package named PathfindingUtils. In
                this class, implement the following static method:
            PathfindingUtils class, write a static method named
                findPath that takes in two Vector2D objects as parameters and returns a
                LinkedListNode of Vector2Ds.
            Vector2D object to the tile containing the second
                Vector2D object.
                Vector2Ds themselves should not be modified. You should instead
                        create copies of them to modify. There are several ways to do this, such as: create a
                        new Vector2D using the constructor, with the desired x and y components;
                        use the Vector2D.copy method, and modify the returned vector; use the
                        static Vector2D.floor method, which returns a new vector with
                        floored components.
                    Vector2D object after the head of the list is one tile above,
                below, to the left, or to the right of the previous one AND if each Vector2D object in the list is
                aligned to a tile (i.e. no decimals). Considered the following paths attempting to travel from eg. (3.9,
                4.6) to (5.2, 5.999)
                followPath (20 CUs)
        Agent class, there exists a method named followPath that takes a
                double and returns void. The input double represents the change in time since
                the last
                time this method was called. Complete this method so that the agent along its path if it has one.
            path, as well as a getter and setter for that instance variable, named getPath
                and setPath respectively. You will need to use these to complete this method.
            Agent's path instance variable is null, their
                        velocity in the x and y directions should be set to 0, and nothing else should be done.
                    Agent is directly on the tile
                        at the path's head, the Agent's position is manually set to the location of that
                        tile. The head of the list is then removed from the list so the agent will travel to the next
                        node the next time this method is called.
                        this.getMovementSpeed, or with the
                                movementSpeed instance variable, both of which already exist in this class.
                                The change in time is the parameter dt.
                            √((x1-x2)2 + (y1-y2)2).
                                You may use the Vector2D.euclideanDistance method, which calculates this
                                for you.
                            Agent's velocity should be set to move towards the tile at the head of the
                        Agent's path.
                        Agent's velocity must have a magnitude (speed) equal to its movement
                                speed. The movement speed can be accessed by calling this.getMovementSpeed,
                                or with the movementSpeed instance variable, both of which already exist in
                                this class.
                            Agent's orientation should be set in the same direction as its
                                velocity, but always with a magnitude of 1.0.
                            Agent can only move in four directions, only one component (x or
                                y) of a moving Agent's velocity or orientation will be non-zero at any
                                point. This means there are only 4 possible values for the Agent's
                                orientation, (1.0, 0.0), (0.0, 1.0), (-1.0, 0.0), or (0.0, -1.0). Velocity is much the
                                same, except that the magnitude will depend on the movement speed.
                            Agent is located at (2.5, 2), has a movement speed of 4,
                                and its path is [(3, 2), (4, 2), (4, 3)], it must first target the point (3, 2). To
                                reach that point, it must travel in the positive x direction, so its velocity would be
                                set to (4, 0), and its orientation would be set to (1, 0).
                            Agent class named update, which takes a
                double and a Level object as parameters, and returns void. These paremeters
                represent the change in time since the last update call, and the current level. This will override a
                method from the parent class.
            Agent.update method, call super.update, passing in both parameters.
            Agent.update method, check if the path instance variable is null. If
                so, call the findPath method, where the start is the location of the Agent,
                and the end is the location of the Player (obtained with
                level.getPlayer().getLocation()), and call setPath with the returned path.
            Agent.update method, if the path is not null, call the
                followPath method, passing in dt as the change in time.
            Agent.update method in a different way.
                Those changes should replace these ones.
            Agent class given is a minimal example that provides only the additional
                imports/instance variables/code that you do not already have or are required to write. We recommend that
                you copy and paste these sections into your existing file.
            PathTile class given is complete. You can either copy and paste it, or right click and
                select "save file as", and download it to the correct location. It should be placed in the package
                app.games.roguelikeobjects.
            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.