Module 11, Part 1: Lab Day#

This first part of Module 11 does not introduce new contents: instead, it provides a series of assessments to improve your Java programming skills. All the following assignments can be solved with the Java programming notions introduced in Modules 2–10.

Important

01 - Rowing Club, Revisited#

This assessment is a revised version of 02 - Rowing Club: your task is to help writing a program for the management of the boats of a rowing club, where a club member is represented as an object of the class Member.

The key difference with 02 - Rowing Club is that a boat must be now represented an object of the class Boat, that offers the constructor and methods discussed below. Overall, the idea is that the class Boat must provide methods to manage which Members (if any) are sitting in a boat object.

Your task is to edit the file Boat.java provided in the handout, and implement a class called Boat with the following constructor and methods. Besides, your definition of the class Boat can include all the fields you like, but they must be all private.

  1. public Boat(int seats)
    

    This constructor initialises a new boat with the given number of empty seats.

  2. public void print()
    

    This method prints the content of each seat of this boat, one seat per line.

    • When a seat is occupied by a member m, then this method must print the result of m.description();

    • Otherwise, when a seat is empty, this method must print: <Empty seat>

  3. public boolean isFull()
    

    This method returns true if all seats of this boat are occupied. Otherwise (i.e. if there is at least one empty seat) this method returns false.

  4. public boolean assignMember(Member m)
    

    This method tries to place the given member m in an empty seat of this boat. This method may either:

    • return true and modify this boat by placing m in the first available seat. This may only happen if there is an empty seat on the this boat, and a member equal to m is not already sitting on the boat. Or,

    • return false without modifying this boat. This can only happen if:

      • the boat is already full, or

      • there is already a member equal to m that is sitting on the boat.

The handout for this assessment contains the following files:

  • Member.java: definition of the class Member (do not modify!)

  • Boat.java: the class representing a boat

  • Test01.java, Test02.java, …: test programs (do not modify!)

  • BoatTestUtils.java: utility methods used by some tests (do not modify!)

When you are done, submit the modified file Boat.java on DTU Autolab.

Tip

  • When implementing the class Boat, you might consider defining a private field of type Member[] to keep track of who is sitting in each boat position, and which seats are available (if any).

  • You can approach this problem by taking the solution of 02 - Rowing Club (either your own solution, or the one provided by the teacher) and converting its static methods into (non-static) object methods (as shown in Example 40). (Note: you will also need to slightly change the name of some methods, according to the requirements above.)

02 - Maze#

You are helping develop a text-based video game where the player moves in a maze. The maze has a rectangular shape and is represented as a bidimensional array of characters, where each character depicts the content of a cell of the maze:

  • The character X depicts the player position.

  • The character # depicts a wall: the player cannot move there.

  • The character  (space) depicts an empty cell: the player can move there.

Your task is to edit the file Maze.java provided in the handout, and implement the following static methods:

  • public static char[][] createMaze(int rows, int cols)
    

    This static method creates a maze with the given number of rows and columns (cols). The maze must be empty, i.e. each cell must contain the character   (space).

  • public static boolean moveUp(char[][] maze)
    public static boolean moveDown(char[][] maze)
    public static boolean moveLeft(char[][] maze)
    public static boolean moveRight(char[][] maze)
    

    Each one of these static methods tries to move the player by one cell in the requested direction in the given maze. If the move is possible (i.e., if the destination cell is within the limits of the given maze, and the cell is empty), then the static method must update the maze (by moving the player as requested) and return true; otherwise, the static method must not modify the maze, and must just return false.

    Note

    When writing these methods, you can assume that the given maze contains exactly one cell with the player.

When you are done, submit the modified file Maze.java on DTU Autolab.

Warning

The automatic grading on DTU Autolab includes some additional secret checks that test your submission with more mazes. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.

Hint

  • Remember that a value of type char in Java is represented between single quotes ' — for instance, 'A', 'b', ' ' (space), …

  • The static methods moveUp(...), moveDown(...), moveLeft(...), and moveRight(...) must determine the position of the player in the given maze, before moving it. To do that, you could inspect each position in the maze, until you find the one that contains X.

  • When implementing moveUp(...), moveDown(...), moveLeft(...), and moveRight(...), you might be able to reduce the amount of code duplication by writing an auxiliary (and private) static method that can move the player in any direction…

03 - Arithmetic Expressions, Version 3#

This is a follow-up to the assessment 04 - Arithmetic Expressions, Version 2, and the starting point is its solution, i.e. the file Expression.java (you can use either your own file, or the one provided by the teacher as a solution to 04 - Arithmetic Expressions, Version 2).

Your task is to improve the solution to 04 - Arithmetic Expressions, Version 2 by adding support for some unary operations, i.e. operations that take only one argument. The goal is to introduce 3 new classes with the following constructors:

  • Minus(e), whose result is the result of the given expression e with its sign flipped (from positive to negative, or vice versa). For instance, if we have var m = new Minus(Constant(42.0)), then:

    • m.result() must return the value -42.0;

    • m.format() must return the string "-(42.0)".

  • AbsoluteValue(e), whose result is the absolute value of the result of the given expression e. For instance, if we have var a = new AbsoluteValue(Constant(-42.0)), then:

    • a.result() must return the value 42.0;

    • a.format() must return the string "abs(42.0)".

    Hint

    The Java API method Math.abs(…) can be useful.

  • SquareRoot(e), whose result is the square root of the result of the given expression e. For instance, if we have var s = new SquareRoot(Constant(9.0)), then:

    • s.result() must return the value 3.0;

    • s.format() must return the string "sqrt(9.0)".

All the new classes Minus, AbsoluteValue, and SquareRoot must extend an abstract class called UnaryOperation which implements the interface Expression.

Your task is to edit the file Expression.java and implement the classes described above, matching the UML diagram in Fig. 32 below — where the difference with 04 - Arithmetic Expressions, Version 2 are the abstract class UnaryOperation and its subclasses.

UML class diagram for "Arithmetic expressions, version 3" assessment

Fig. 32 UML class diagram for 03 - Arithmetic Expressions, Version 3.#

When you are done, submit the modified file Expression.java on DTU Autolab.

The handout includes some Java files called Test01.java, Test02.java, etc.: they are test programs that use the code you should write in Expression.java, and they might not compile or work correctly until you complete your work. You should read those test programs, try to run them, and also run ./grade to see their expected outputs — but you must not modify those files.

Note

This assessment has several potential solutions. In fact, the UML diagram in Fig. 32 omits various details in the classes BinaryOperation and UnaryOperation — e.g. what is its constructor (if any), what methods it implements, and whether it has non-private fields: you are free to choose those details as you like, trying to minimise code duplication as much as possible. After the submission deadline expires, please compare your solution with the one provided by the teacher.

Warning

The automatic grading on DTU Autolab includes some additional secret checks that test your submission with more expressions. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.

04 - Video Game Monsters, Part 5#

Important

For this assessment you must submit two files: Monster.java and GameUtils.java. For the submission instructions, see the note at the end of this assessment.

This is a follow-up to the assessment 06 - Video Game Monsters, Part 4, and the starting point is its solution, i.e. the files Monster.java and GameUtils.java (you can use either your own files, or the ones provided by the teacher as a solution to 06 - Video Game Monsters, Part 4).

The development of the video game has two new requirements:

  • there is a new species of monster called the “thing”;

  • sometimes, the game may need to create some monsters that do not fit one of the 4 main species (wumpus, owlbear, demogorgon, thing).

Therefore, the development team decides to revise the structure of the Java code of the game:

  • Monster will become a class (instead of an interface) allowing the creation of various kinds of monsters having different species, names, maximum health points, and damage from being hit or burnt;

  • Wumpus, Owlbear, and Demogorgon should become sub-classes of the class Monster, each one with with its predetermined characteristics (listed in 06 - Video Game Monsters, Part 2).

You will need to edit the file Monster.java to turn the interface Monster into a class Monster in the file Monster.java. The class Monster must implement all the methods that were specified for the Monster interface (as described in 06 - Video Game Monsters, Part 2), plus the one listed below. Besides, feel free to define all the fields you need — but they must be all private.

  • The class Monster must provide the constructor:

    public Monster(String species, String name, int maxHealth,
                   double hitDamageFactor, double burnDamageFactor)
    

    This constructor initialises a new object of the class Monster with the given species, name, maximum health (maxHealth), and hitDamageFactor and burnDamageFactor, which modify the damage the monster takes when hit or burnt, respectively (for example: if burnDamageFactor is 0.5, then the monster takes half the damage when burnt; instead, if burnDamageFactor is 2.0, the monster takes twice the damage when burnt). When created, the health points of the monster object correspond to its maximum health points.

    For example, the constructor can be used as follows:

    var monster = new Monster("skeleton", "Toothless", 42, 2.0, 1.0);
    

    This creates a new monster of the species “skeleton” that is called Toothless, has 42 maximum health points, takes twice the damage when hit, and normal damage when burnt.

  • The class Monster must provide the following method:

    public char getSymbol()
    

    This method returns a character representing the monster, used to show the monster on the playground. The character must be the first letter of the monster species, uppercase. For example, for the variable monster defined above, monster.getSymbol() must return the character S (i.e. the first letter of "skeleton", uppercase).

After you implement the class Monster, you will need to revise the classes Wumpus, Owlbear, and Demogorgon to make them extend the class Monster.

Finally, you will need to implement the new class Thing for the new species of monster, which has a maximum of 30 health points, and takes double damage when burned.

Note

For this assessment you need to prepare and submit a ZIP file containing your modified versions of Monster.java and GameUtils.java. To prepare that ZIP file, you can simply execute from the terminal (inside the handout directory):

./grade -z

This command will grade your work and prepare a ZIP file that you can then submit on DTU Autolab.

Hint

  • When solving this assessment, you should not need to change the file GameUtils.java developed for 06 - Video Game Monsters, Part 4 (i.e. you should be able to just copy&paste it, and submit it as it is).

  • As you revise the classes Wumpus, Owlbear, and Demogorgon to make them extend the class Monster, you should be able to remove almost all their code, and reuse the methods already provided by Monster. You should just need to call the constructor of the Monster superclass using super (as shown in Example 56 and Example 60).

  • Similarly, your implementation of the class Thing that derives from Monster, you should be able to reuse all the methods provided by Monster.

  • When updating a monster’s health after the monster is hit or burnt, you will need to multiply the damage by the hitDamageFactor or burnDamageFactor (which have type double). To this end, you might need convert the result of an expression of type double to type int: to achieve this you can use a narrowing primitive cast by writing (int) before the expression of type double, e.g.

    var x = (int)expression_of_type_double;
    

    This way, the variable x has type int and has the result of expression_of_type_double with its decimal digits truncated.

05 - Particle#

Your task is to edit the file Particle.java provided in the handout, and implement a class named Particle that simulates a particle moving through a rectangular grid. The particle has a position represented by non-negative row and column numbers. The position corresponding to row 0 and column 0 is located in the top-left corner of the grid. The particle moves in one of the following directions:

  • N for north;

  • NE for north-east;

  • E for east;

  • SE for south-east;

  • S for south;

  • SW for south-west;

  • W for west;

  • NW for north-west.

The definition of the class Particle must satisfy the following requirements; besides, feel free to define all the fields you need — but they must be all private.

  • The class Particle must have the public constructor:

    public Particle(int row, int column, int gridRows, int gridColumns,
                    String direction)
    

    which constructs a Particle object located in a grid at the position row, column; the grid has a total number of rows and columns given by the arguments gridRows and gridColumns, respectively; moreover, the particle moves in the given direction.

    You can assume that row and column are non-negative and smaller than gridRows and gridColumns, respectively; moreover, you can assume that the argument direction is one of the directions listed above.

  • Objects of the class Particle must provide the methods:

    public int getRow()
    
    public int getColumn()
    
    public String getDirection() 
    

    which return the current row, column, and direction of this particle, respectively.

  • Objects of the class Particle must provide the method:

    public void move()
    

    This method must change the current row and/or column of this particle by moving it by one cell along its current direction, in a grid having the number of rows and columns specified in the constructor above.

    For instance, if p is a particle located at row 2 and column 3 of a 10x10 grid, and p is moving in the direction SW, then the method call p.move() must change the position of p to row 3 and column 2.

    Importantly, the particle must bounce and change direction if it tries to move beyond the limits of the grid.

    For instance, if p is a particle located at row 5 and column 3 of a 6x4 grid, and p moving in the direction SW, then the method call p.move() must change the position of p to row 4 and column 2, and the direction of p must change to NW — i.e., p cannot keep moving south-west after row 5 (because it is the last row of the 6x4 grid), so it bounces and starts moving north-west. (See the animation at the end of this assessment for a visual representation of a bouncing particle.)

    Tip

    Remember that the grid position at row 0 and column 0 is located in the top-left corner of the grid. Therefore, a particle moving south will increase its row number (unless it has already reached the last row of the grid), and a particle moving east will decrease its column number (unless it has already reached column 0).

  • Objects of the class Particle must provide the method:

    public String display()
    

    This method must return a string that, when printed, visualises this particle as an asterisk * inside a rectangle having the number of rows and columns specified by the arguments gridRows and gridColumns of the constructor above. Moreover, the returned string must include the particle position and direction.

    For instance, if p is a particle located at row 2 and column 3 in a 5x5 grid, and p is moving in the direction SW, then the method call p.display() must return the following string: (remember that the coordinate with row 0 and column 0 is in the top-left corner of the grid)

    +-----+
    |     |
    |     |
    |   * |
    |     |
    |     |
    +-----+
    Position: (2,3); direction: SW
    

    Tip

    To create the string returned by the method display(...), you will need to use System.lineSeparator(), as explained in the hints of 06 - Square 2. Notice that the returned string must include a line separator at the end of the last line.

When you are done, submit the modified file Particle.java on DTU Autolab.

The handout includes some Java files called Test01.java, Test02.java, etc.: they are test programs that use the code you should write in Particle.java, and they might not compile or work correctly until you complete your work. You should read those test programs, try to run them, and also run ./grade to see their expected outputs — but you must not modify those files.

Tip

The test Test13.java can show an animation based on your implementation of the class Particle. To see the animation, you will need to open a terminal in the directory of the handout and compile all Java files:

javac *.java

Then you can execute Test13 by providing the command-line argument animate, e.g.:

java Test13 animate

The animation should look as follows:

Note

For Windows users: if the animation is not displayed correctly, make sure that the console program you are using is Git Bash (as recommended) or Powershell.

Warning

The automatic grading on DTU Autolab includes some additional secret checks. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.