Module 9, Part 1: Lab Day#

This first part of Module 9 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–8.

Important

01 - Super Tic-Tac-Toe#

You are helping writing a program that implements a variant of the game tic-tac-toe, where:

  • players place the marks X or O on a grid of size \(n \times n\) (with \(n \ge 1\));

  • a player wins if a whole row or column of the grid is filled with the same mark (i.e. the game does not take into account the diagonals).

The plan is to represent the playing grid as a bidimensional array of Strings, where each coordinate may contain the null value (if the grid position is empty) or a mark string "X" or "O".

Your task is to edit the file SuperTicTacToe.java provided in the handout, and implement a class named SuperTicTacToe with the following static methods:

  • public static String[][] createGrid(int n)
    

    which creates and returns an empty playing grid (i.e. all grid coordinates must contain null).

  • public static boolean hasWinner(String[][] grid)
    

    which returns true if a whole row or column of the given grid is filled with the same mark "X" or "O". Otherwise, this static method must return false.

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

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

Hint

  • You might want to revise how to check whether an array element is null.

  • To implement hasWinner(grid), you may proceed gradually.

    1. Start with a simplified task: check whether the given grid contains a winning row only containing the mark "X".

    2. Then, you could adapt the code to also check whether there is a winning row with the mark "O".

    3. Finally, you could extend your code to also check whether there is any winning column.

02 - Rowing Club#

This assessment models the management of the boats of a rowing club: a club member is represented as an object of the class Member (with fields for the member’s name and age), and a boat is represented as an array of Member objects; each position in the array corresponds to a seat of the boat, which may be either occupied (if it contains a Member object) or empty (if it contains null).

The handout for this assessment contains the following files:

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

  • BoatUtils.java: utility methods for handling a boat

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

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

Your task is to edit the file BoatUtils.java provided in the handout, and implement a class called BoatUtils with the following static methods:

  1. public static Member[] createBoat(int seats)
    

    This static method creates and returns a new boat (i.e. an array of Member objects) with the given number of seats, all empty (i.e. the array must only contain nulls).

  2. public static void printBoat(Member[] boat)
    

    This static method prints the content of each seat of the given 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 static boolean isBoatFull(Member[] boat)
    

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

  4. public static boolean assignMember(Member[] boat, Member m)
    

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

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

    • return false without modifying the 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.

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

Hint

Note

The test programs that use the class BoatUtils.java might not compile or work correctly until you complete the implementation of class BoatUtils in the file BoatUtils.java.

03 - Arithmetic Expressions#

Your task is to implement an interface and some classes that can be used represent an arithmetic expression and compute its result. An arithmetic expression may be either a constant number, or an arithmetic operator (addition, subtraction, multiplication, or division) with two sub-expressions for the left and right operands.

Edit the file Expression.java provided in the handout, and write an interface called Expression with two abstract methods:

  • public double result();
    

    When implemented in a class, this method is expected to compute and return the result of this expression.

  • public String format();
    

    When implemented in a class, this method is expected to return a String representing this expression in a readable way.

Then, you should implement the classes called Constant, Addition, Subtraction, Multiplication, and Division, as described below. Importantly, all the classes described below must be written in the file Expression.java, and all of them must implement the interface Expression described above. The goal is that, after all the classes described below are implemented, then we can create an “expression object” by combining a series of objects for each sub-expression, e.g.:

var e = new Subtraction(new Addition(new Constant(1.0),
                                     new Constant(2.0)),
                        new Constant(3.0));

and then, e.format() should return the string ((1.0 + 2.0) - 3.0), and e.result() should return the value 0.0.

  • The class Constant represents a constant number. It must provide the following constructor:

    public Constant(double value)
    

    which stores the given value in a field of your choice (which must be private). Then, if c is an object of the class Constant, it must behave as follows:

    • the method c.result() must return the value used to create this object;

    • the method c.format() must return a string containing the value used to create this object.

      Hint

      To convert a value d of type double into a String, you can use e.g. Double.toString(d).

    For example, if we have:

    var c = new Constant(42.3)
    

    then c.result() must return the value 42.3, while c.format() must return the string 42.3.

  • The class Addition represents the addition between two sub-expressions. It must provide the following constructor:

    public Addition(Expression e1, Expression e2);
    

    which stores the given sub-expressions e1 and e2 in some fields of your choice (which must be private). Then, if a is an object of the class Addition created using the sub-expressions e1 and e2, then a must behave as follows:

    • the method a.result() must compute and return the addition between the result of e1 and the result of e2;

    • the method a.format() must return a string containing, between parentheses, the strings returned by e1.format() and e2.format(), separated by the symbol + between spaces.

    For example, if we have:

    var a = new Addition(new Constant(1.0), new Constant(2.0));
    

    then a.result() must return the value 3.0, while a.format() must return the string (1.0 + 2.0).

  • The classes Subtraction, Multiplication, and Division are similar to Addition — except that they implement different arithmetic operations, and their method format() returns a representation using the symbol -, *, or / (instead of +).

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

The handout includes some Java files called ClassTestUtils.java and 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.

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.

Note

When you write an expression like (1 + 2) - 3 in a Java program or on the Java shell, the Java compiler transforms it into an internal representation that is very similar to the solution to this assessment. That internal representation is called Abstract Syntax Tree.

Moreover, the execution of the method result() shows how an arithmetic expression can be computed, step by step, by computing the results of all its sub-expressions.

04 - Cinema#

You are helping develop a program that manages cinema bookings. The plan is to develop a class called Cinema, whose objects represent instances of a cinema with a screening room having a certain arrangement of seats, organised in rows. Different rows may have a different number of seats. Each seat may be either vacant or booked.

Your task is to edit the file Cinema.java provided in the handout, and implement a class named Cinema with the following requirements.

  • The class Cinema can define any number of fields, but they must be all private.

  • The class Cinema must have a constructor:

    public Cinema(int[] seatsOnRows) 
    

    This constructor initialises an object of the class Cinema with the given seats arrangement: seatsOnRows is an array of integers that specifies how many seats are placed in each row; different rows may have a different number of seats. For instance, if a Cinema object is created as follows:

    var c = new Cinema(new int[] {9, 8, 5});
    

    then c has 3 rows of seats, with:

    • 9 seats on row 0;

    • 8 seats on row 1;

    • 5 seats on row 2.

    All the seats in a new Cinema object must be vacant.

  • Objects of the class Cinema must provide the method:

    public int rows()
    

    This method returns the number of rows of seats in this cinema.

  • Objects of the class Cinema must provide the method:

    public int seatsOnRow(int row)
    

    This method returns the number of seats in the given row of this cinema. If the given row is invalid (i.e. negative, or greater than the last row number for this cinema), then this method must return 0.

  • Objects of the class Cinema must provide the method:

    public int seats()
    

    This method returns the total number of seats in this cinema.

  • Objects of the class Cinema must provide the method:

    public int rowVacancies(int row)
    

    This method returns the number of vacant seats in the given row of this cinema. If the given row is invalid (i.e. negative, or greater than the last row number for this cinema), the method must return 0.

  • Objects of the class Cinema must provide the method:

    public int vacancies()
    

    This method returns the total number of vacant seats in this cinema.

  • Objects of the class Cinema must provide the method:

    public void book(int row, int column)
    

    This method books a seat in the given position (row and column) of this cinema. After this method is called, the selected seat is not vacant any more. If the given row and column denote an invalid position in this cinema, the method must just return without doing anything.

  • Objects of the class Cinema must provide the method:

    public void release(int row, int column)
    

    This method releases a seat in the given position (row and column) of this cinema: i.e. if the seat was booked, it becomes vacant again. If the given row and column denote an invalid position in this cinema, the method must just return without doing anything.

  • Objects of the class Cinema must provide the method:

    public String seatMap()
    

    This method returns a String representing the seat map of this cinema:

    • each row of seats must be represented by a sequence of characters — either . (for a vacant seat) or X (for a booked seat);

    • each row (including the last one) must be ended by a line separator character (that you can obtain by calling System.lineSeparator()).

    For instance, if a Cinema object is created as:

    var c = new Cinema(new int[] {9, 8, 9, 8, 9, 8});
    

    Then c.seatMap() must return the string corresponding to:

    .........
    ........
    .........
    ........
    .........
    ........
    

    and after some seats are booked (using c.book(...)), then c.seatMap() may return a string corresponding to:

    ..XXX....
    ....XX..
    ..XXX.XX.
    ...X....
    ....XXX..
    ........
    

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

The handout includes some Java files called ClassTestUtils.java and Test01.java, Test02.java, etc.: they are test programs that use the code you should write in Cinema.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.

Hint

Feel free to define and use the private fields of Cinema the way you like. Various different approaches are possible. However, a jagged array of boolean values may be enough — and to create this jagged array, the hint provided in 08 - Array Deep-Copy may be useful…

05 - Video Game Monsters, Part 3#

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 06 - Video Game Monsters, Part 2, and the starting point is the updated file Monster.java (either your own version, or the solution provided by the teacher).

The development of the video game has reached the stage of testing the monsters in a playground. A playground is a rectangle of \(m \times n\) cells, where each cell can be empty, or contain a monster. When the player is at a given position on the playground, the player can hit or burn the monsters nearby.

First, you will need to add a new method to the interface Monster (and implement it in all monster classes):

public char getSymbol()

This method returns a character representing the monster (used below to show the monster on the playground). The returned character must be:

  • W if the monster is a wumpus;

  • O if the monster is an owlbear;

  • D if the monster is a demogorgon.

Then, you will need to implement a class called GameUtils in the file GameUtils.java (provided in the handout), with the following requirements:

  • The class GameUtils must provide the static method:

    public static Monster[][] createPlayground(int rows, int columns)
    

    This static method returns a new playground (i.e. a bidimensional array of Monsters) with the given number of rows and columns (where each coordinate represents a playground cell). Each cell in the playground must be empty (i.e. contain null).

  • The class GameUtils must provide the static method:

    public static void displayPlayground(Monster[][] playground)
    

    This static method must print on screen the contents of each cell of the given playground, arranged in a rectangle:

    • if a cell is empty, it must be shown as . (a dot);

    • if a cell contains a monster, it must be shown with the monster’s symbol obtained by calling the monster’s method getSymbol() (described above).

    For example, a playground with 4 rows and 6 columns containing an owlbear and a wumpus may look like:

    ......
    .O....
    ...W..
    ......
    
  • The class GameUtils must provide the static method:

    public static void displayMonsters(Monster[][] playground)
    

    This static method must print on screen the position and description of each monster in the playground (using the Monster’s method getDescription()). The order in which the descriptions are printed follows the monsters’ positions on the displayed playground, from top to bottom, and from left to right. The cell positions start from row 0 and column 0.

    For instance, for the 4 \(\times\) 6 playground shown in GameUtils.displayPlayground(...) above, the output of GameUtils.displayMonsters(...) should look like:

    Row 1, column 1: Thunderscream (owlbear; health: 10)
    Row 2, column 3: Horrorface (wumpus; health: 33)
    
  • The class GameUtils must provide the static method:

    public static void hit(Monster[][] playground, int pRow, int pCol, int damage)
    

    This static method applies the given damage by hitting all the monsters that are at most 1 cell away (horizontally, vertically, or diagonally) from the given player coordinates (pRow, pCol).

    For instance, consider the 4 \(\times\) 6 playground shown in GameUtils.displayPlayground(...) above. To cause 10 points of hit damage with the player at row 2 and column 1, we can call GameUtils.hit(playground, 2, 1, 10) — and the method should apply 10 points of hit damage to the owlbear (which is 1 cell away from the player’s coordinates), but not to the wumpus (which is too far from the player’s coordinates).

  • The class GameUtils must provide the static method:

    public static void burn(Monster[][] playground, int pRow, int pCol, int damage)
    

    This static method applies the given damage by burning all the monsters that are at most 2 cells away (horizontally, vertically, or diagonally) from the given player coordinates (pRow, pCol).

    For instance, consider the 4 \(\times\) 6 playground shown in GameUtils.displayPlayground(...) above. To cause 20 points of burning damage with the player at row 2 and column 1, we can call GameUtils.burn(playground, 2, 1, 20) — and the method should apply 20 points of burning damage to both the owlbear and the wumpus (since they are both within 2 cells from the player’s coordinates).

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

  • Since GameUtils.hit(...) and GameUtils.burn(...) are very similar, you might save some coding by writing and using a (private) static method to check whether two pairs of coordinates are within a given range…