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
For each assessment, you can download the corresponding handout and submit your solution on DTU Autolab: https://autolab.compute.dtu.dk/courses/02312-E24.
For details on how to use Autolab and the assessment handouts, and how to submit your solutions, please read these instructions.
If you have troubles, you can get help from the teacher and TAs.
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
orO
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 String
s,
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 givengrid
is filled with the same mark"X"
or"O"
. Otherwise, this static method must returnfalse
.
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.Start with a simplified task: check whether the given
grid
contains a winning row only containing the mark"X"
.Then, you could adapt the code to also check whether there is a winning row with the mark
"O"
.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 classMember
(do not modify!)BoatUtils.java
: utility methods for handling a boatTest01.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:
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 ofseats
, all empty (i.e. the array must only containnull
s).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 ofm.description()
;Otherwise, when a seat is empty, this method must print:
<Empty seat>
public static boolean isBoatFull(Member[] boat)
This static method returns
true
if all seats of the givenboat
are occupied. Otherwise (i.e. if there is at least one empty seat) this static method returnsfalse
.public static boolean assignMember(Member[] boat, Member m)
This method tries to place the given member
m
in an empty seat of the givenboat
. This static method may either:return
true
and modify theboat
by placingm
in the first available seat. This may only happen if there is an empty seat on theboat
, and a member equal tom
is not already sitting on the boat. Or,return
false
without modifying theboat
. This can only happen if:the boat is already full, or
there is already a member equal to
m
that is sitting on theboat
.
When you are done, submit the modified file BoatUtils.java
on DTU Autolab.
Hint
To compare two objects
m1
andm2
of the classMember
, you must callm1.equals(m2)
(see how to check whether two objects are equal and the fileMember.java
).If
boat
is an array ofMember
objects, you can check whether the element at indexi
isnull
by checking the result of the boolean expressionboat[i] == null
(as mentioned in Checking Whether a Variable, Array Position, or Object Field Contains null). For example:if (boat[0] == null) { System.out.println("null!"); } else { System.out.println("A member is sitting in the boat: " + boat[0].name); }
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
representingthis
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 beprivate
). Then, ifc
is an object of the classConstant
, it must behave as follows:the method
c.result()
must return thevalue
used to createthis
object;the method
c.format()
must return a string containing thevalue
used to createthis
object.Hint
To convert a value
d
of typedouble
into aString
, 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, whilec.format()
must return the string42.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
ande2
in some fields of your choice (which must beprivate
). Then, ifa
is an object of the classAddition
created using the sub-expressionse1
ande2
, thena
must behave as follows:the method
a.result()
must compute and return the addition between the result ofe1
and the result ofe2
;the method
a.format()
must return a string containing, between parentheses, the strings returned bye1.format()
ande2.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, whilea.format()
must return the string(1.0 + 2.0)
.The classes
Subtraction
,Multiplication
, andDivision
are similar toAddition
— except that they implement different arithmetic operations, and their methodformat()
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 allprivate
.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 ofint
egers that specifies how many seats are placed in each row; different rows may have a different number of seats. For instance, if aCinema
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
ofthis
cinema. If the givenrow
is invalid (i.e. negative, or greater than the last row number forthis
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
ofthis
cinema. If the givenrow
is invalid (i.e. negative, or greater than the last row number forthis
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
andcolumn
) ofthis
cinema. After this method is called, the selected seat is not vacant any more. If the givenrow
andcolumn
denote an invalid position inthis
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
andcolumn
) ofthis
cinema: i.e. if the seat was booked, it becomes vacant again. If the givenrow
andcolumn
denote an invalid position inthis
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 ofthis
cinema:each row of seats must be represented by a sequence of characters — either
.
(for a vacant seat) orX
(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(...)
), thenc.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
Monster
s) with the given number ofrows
andcolumns
(where each coordinate represents a playground cell). Each cell in the playground must be empty (i.e. containnull
).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 methodgetDescription()
). 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 ofGameUtils.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 callGameUtils.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 callGameUtils.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(...)
andGameUtils.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…