Coding Task 2


Time Estimate:

Jump to current week
Requirements

Project Structure


You will continue to add functionality to your existing project from the previous task. There is no new repository to clone.

For this task, you will be writing code in the following classes:

  • SampleTopDownGame, located in the package app.games
  • Enemy, located in the package app.games.topdownobjects

You will also need to create new classes in these exact locations:

  • Pathfinding, in the package app.gameengine.model.ai
  • TestTask2, in the package tests

You will also be using the provided LinkedListNode class from the app.gameengine.model.datastructures package. You may find it helpful to add to this class, however you cannot modify or remove any of the given code. You can use these additions in your implementation, but not in your tests (the TestTask2 class).



Specification


In this task, you will test and implement the following specs:

  • SampleTopDownGame - Linked List Operations
    • Currently, the way the game handles loading levels is poorly implemented. The first part of this task will be to improve it using Linked Lists. The following methods that will be implemented should interact with a LinkedListNode of Level instance variable you will add to the SampleTopDownGame class.
    • Add a getter called getLevelList. This method should return the LinkedListNode of type Level representing the head of the list of Levels.
      • If no Levels have been added, this method should return null.
    • Add a setter called setLevelList that takes in a LinkedListNode of Levels and returns void.
      • This method should replace the instance variable representing the head of the list with the LinkedListNode in the parameter.
    • Add a method called addLevel that takes in a Level as a parameter and returns void.
      • This method should append the Level specified in the parameter to the Linked List of levels.
    • Add a method called removeLevelByName that takes in a String and returns void.
      • This method should remove the level with the specified name String from the Linked List of levels.
      • If multiple Levels exist with this name, it should only remove the first one.
  • SampleTopDownGame - Modifications
    • Now that the Linked List operations have been implemented, you will now make the following modifications to the given methods:
      • Replace the body of the advanceLevel method to use the Linked List operations. When this method is called, it should advance the head of the Level list to the next node, then call loadLevel on the level contained at the (new) head of the Linked List.
        • If the path is null, this method should do nothing.
      • Modify the init method to add the three given levels to the Linked List before the given loadLevel call.
    • With these changes, you should be able to play through the game as before.
  • Enemy - Path Operations
    • You will now implement some path operations to the Enemy class. Once this is complete, Enemies will be able to create and follow paths in game, which will be represented by a LinkedListNode of Vector2D objects.
    • Add a method called setPath that takes in a LinkedListNode of Vector2D objects. This method should set an instance variable representing the Enemy's path being followed.
    • Add a method called getPath that returns a LinkedListNode of Vector2D objects. This method should return the instance variable representing the Enemy's path.
  • Pathfinding - findPath method
    • In the Pathfinding class you created, write a static method called findPath that takes in two Vector2D objects as parameters and returns a LinkedListNode of Vector2Ds.
    • This method should return the shortest valid path that takes it from the tile containing the first Vector2D object to the tile containing the location of the second Vector2D object.
      • A path is valid if each 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).
        • [(3.0, 4.0), (3.0, 5.0), (4.0, 5.0)] is valid.
        • [(3.0, 4.0), (3.0, 5.0), (5.0, 6.0)] is not valid as the third Vector2D is two tiles right of the second.
        • [(3.0, 4.0), (4.0, 5.0), (4.0, 6.0)] is not valid as the second Vector2D is diagonal to the first one.
        • [(3.1, 4.9), (3.1, 5.9), (4.1, 5.9)] is not valid as it is not aligned to a tile.
      • This is NOT the same as starting from the location of the first Vector2D and ending at the location of the second Vector2D.
      • You may find it helpful to review the overview of the coordinate system in the task 1 handout.
  • Enemy - Path Functionality
    • Finally, modify the given method update so that it has the following behavior:
      • If the Enemy's path LinkedListNode is null, it should use the findPath method from the Pathfinding class to generate a path from the Enemy's location to the Level's Player's location. This path should then be assigned to the enemy.
      • If the Enemy is within a small amount of the location at the path's head, the Enemy's position should be set to exactly that location. The head of the list should then be moved one node further in the list.
        • This is necessary to prevent enemies from getting caught at the edge of a hitbox.
      • Otherwise, the Enemy's velocity should be set to move towards the tile at the head of the Enemy's path.
        • The Enemy's velocity should have a magnitude (speed) of 2.0.
        • Since the Enemy can only move in four directions, only one component of a moving Enemy's velocity will be non-zero at any point.
        • If the enemy is already at that tile, it should move the path's head node up one further in the list.

Note: You can see feedback in Autolab for your testing utilities and tests without completing the programming portion of this task, but you must at least create the classes and methods that you test. 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.



Testing Utilities


Create a class named TestTask2 in the tests package and write the following methods in that class (Note: Do not add the @Test annotation to these methods since they are not tests):

  • compareListsOfLevels - Write a method named compareListsOfLevels in the tests.TestDataStructures1 class that:
    • Takes [references to] 2 LinkedListNode<Level> objects as parameters and returns void
    • This method checks if the names of all Levels in both lists are the same and are in the same order. The method fails a JUnit assert if the lists do not contain all the same names of their Level objects in the same order
    • Note that you cannot use == or .equals to compare 2 Levels. You must specifically check the name values.
  • validatePath - Write a method named validatePath that:
    • takes in a LinkedListNode of Vector2D objects and returns void.
    • This method should fail a JUnit assert if the path from the parameter is invalid.
    • This should use the same criteria as the findPath method uses.


Testing Requirements


Add test methods to the tests.TestTask2 class, using the @Test annotation, that tests the following methods from the specification:

  • SampleTopDownGame class:
    • addLevel
    • removeLevelByName
    • advanceLevel
    • For these tests, you should call the TopDownLevel constructor whenever a Level object needs to be initialized.
    • Also, make sure your tests account for the behavior of the given SampleTopDownGame constructor.
  • Pathfinding class:
    • findPath

You should call your testing utility methods to save time while writing these tests.



Programming Requirements


Implement the functionality from the specification.



Autolab Feedback


The feedback in Autolab will be given in 4 phases. If you don't complete a phase, then feedback for the following phase(s) will not be provided.

  1. Testing your testing utility methods
    • Each of your testing utility methods will be checked with a variety of test cases to ensure that they make all the required checks. This phase will ensure that your utility methods are accurate before you start using them in your tests
  2. Running your tests on a correct solution
    • Your tests will be run against a solution that is known to be correct. If your tests do not pass this correct solution, there is an error somewhere in your tests that must be fixed before you can move on with the assignment. If your tests don't get past this check, you should re-read this document and make sure you implemented your tests and code according the specification. You should also make sure that if there are multiple correct outputs to the input in your tests cases that you accept any of the outputs as correct
  3. Checking your tests for feature coverage
    • The next phase is to check if your tests check for a variety of features defined by different inputs. You should write at least one test case for each feature to pass this phase
    • Passing this phase does not necessarily mean that your testing is completely thorough. Satisfying Autolab is the bare minimum testing requirement. Not all possible inputs are checked, and it is sometimes possible to pass this phase with weak testing. If you are struggling to earn credit for code that you believe is correct, you should write more than the required tests
  4. Running my tests on your solution
    • Once Autolab is happy with your tests, it will run my tests against your code to check it for correctness. If your testing is thorough, and your code passes your tests, then you should pass this phase. If you pass your tests, but fail one of mine, it is an indicator that you should write more tests to help expose your bug

Once you complete all 4 phases, you will have completed this Task and Autolab will confirm this with a score of 1.0 for complete.