Project Overview
The purpose of this document is to showcase the work that I have done for the project named "Battleship". The project was done as a requirement for the module CS2103T Software Engineering in National University of Singapore.
My team and I were presented with an existing code base for a command line interface (CLI) address book application. We were given about 8 weeks to either enhance or morph the address book application to a product of our own choice. We chose to morph it into Battleship.
Battleship is a desktop game application based on the classic 2 player guessing game of the same name. The objective of the game is to sink all the opposing player’s ships. The player interacts with the game using a command-line interface (CLI) and feedback is given to the player through the graphical user interface (GUI). The game is written in Java, with the GUI created using JavaFX and has about 10 kLoC.
My role was to design and write the code for the map feature of the game. The following sections illustrate the feature in more detail, as well as the relevant sections I have added to the user and developer guides in relation to this feature.
Relevant links:
Project Respository: Battleship
Project Website: Battleship @ github.io
The full User Guide and Developer Guide can be viewed in the project website.
Note the following formatting used in this document:
|
A grey highlight indicates that this is either a command that can be inputted into the command line or a class name or method name that can be found in the code. |
This symbol indicates important information. |
Summary of contributions
This section provides the summary of the contributions that I have made to the project.
-
Major enhancement: Added the map feature of the game. This includes the internal data structure of the map, the cells that make up the map and how they are displayed in the UI.
-
What it does: The player can initialise the maps to the size they specified using the command
init
. The players can place ships on their own map and shoot the opposing players map. The maps are displayed in the UI and each cell will be represented with different colours depending on its status. Theinit
command can be used at any point in time during the game and will reset the board to a clean state. -
Highlights: The other features (e.g placing ships) rely heavily on the map. It requires careful planning on the design as the other features must be built on top of it and any changes made later on will affect the other features.
-
Justification: The maps are crucial to the gameplay as all of the other features are built on top of it.
-
-
Code contributed:
-
Other contributions:
-
Project management:
-
Managed project release in GitHub: v1.2
-
Made changes to the project that do not fall under a specific feature (Pull Requests "Change output jar name" #352, "Create Model component diagram" #240, "Renaming the app header title and icon" #176)
-
Reported bugs not related to my own feature. (Issues #339, #105, #61)
-
-
Enhancements to existing features:
-
Overhauled UI to display the maps. (Pull request #87)
-
-
Documentation:
-
Restructured UG to make it easier to follow when playing the game. (Pull Request #256)
-
-
Community:
-
Tools:
-
Integrated Netlify to the team repo.
-
Setup auto publishing of documentation.
-
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. Parts of the User Guide that belong to different sections are separated by a horizontal line. |
Preparation phase
This section describes the commands used in the preparation phase.
Initialising the maps : init
The init
command initialises both your map and the enemy AI’s map to the size that you specify. The size of the maps must be between 6 and 10, inclusive.
Format: init MAP_SIZE
Example: init 10
Figure 1 below shows how the maps will be displayed in the UI.
Throughout the course of the game, the cells in the map will change colour based on their status.
Cell statuses:
|
Figure 2 below shows which colour is used for each status.
Features Coming in v2.0
This section describes features that have not been implemented yet and are scheduled to be released in version 2.0.
Special map shapes
You will be allowed to use maps that are randomised in shape. This adds an extra layer of challenge and fun.
Both yourself and the enemy AI will not be able to place ships on the grey tiles. The enemy AI will also have a randomised map that will not be the same as your map. You will not be able to see the shape of the enemy map and can shoot grey cells which will be counted as a miss.
The image below shows an example of a randomised map.
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. Parts of the Developer Guide that belong to different sections are separated by a horizontal line. |
Co-authored the Model diagram as shown below.
Map feature
The map feature handles the interactions of the game in the map level. The map feature does the following:
-
Initialise both players' maps.
-
Allow placing of ships in the cells.
-
Allow attacking of cells.
Implementation of map initialisation
The map feature is facilitated by MapGrid
and Cell
.
The map grid is stored internally in MapGrid
as a 2D array of Cell
objects.
MapGrid
implements the following method to initialise the map:
-
MapGrid#initialise(Cell[][] map)
— initialises the map using the givenCell
2D array.
Below is the code snippet for the initialise
method. cellGrid
is the internal 2D array comprising of Cell
objects.
The method copy2dArray
copies the map
parameter passed in to the internal cellGrid
public void initialise(Cell[][] map) {
this.size = map.length;
cellGrid = new Cell[size][size];
copy2dArray(cellGrid, map);
updateUi();
}
Below is the code snippet for the copy2dArray
method. The copy2dArray
method creates a new Cell
object for each of the input Cell
objects.
The copying is done using a constructor in Cell
that takes in a parameter Cell
.
This constructor copies the private attributes of the given Cell
parameter.
private void copy2dArray(Cell[][] output, Cell[][] toBeCopied) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
output[i][j] = new Cell(toBeCopied[i][j]);
}
}
}
The following sequence diagram shows what happens when the "initialise map" command is used.:
The following activity diagram shows when the "initialise map" command can be used by the user:
Implementation of the Cell
class
The cells are the lowest-level feature of the game and is represented by the Cell
class. Each Cell
allows one ship to be placed on it but the same ship can be referenced from multiple Cell
objects. Cell
also allows receiving of an attack and will propagate the attack call to the Battleship
in it. To support the above, the following methods are available:
-
void putShip(Battleship ship)
- places a ship in theCell
. -
boolean receiveAttack()
- receives an attack and returns true if it’s a hit, false otherwise.
Also, each Cell
has a Status
attribute which is an enum
of the following:
Status | Description |
---|---|
HIDDEN |
This cell has not been hit. |
EMPTY |
This cell is empty and has not been hit. |
EMPTYHIT |
This cell has been hit before, and is empty. |
SHIP |
This cell has a ship in it. |
SHIPHIT |
This cell has been hit before, and there is a damaged ship here. |
DESTROYED |
This cell has been hit before, and the ship here has been destroyed. |
The Status
of a Cell
can be checked through the following method:
-
Status getStatus()
- returns theStatus
of theCell
.
The Status
is used by several higher level functionality such as:
-
the UI to know what colour should a
Cell
be displayed as. -
placing ships to check whether a ship has already been placed.
Design considerations
Aspect: How the maps are initialised
-
Alternative 1 (current choice):
MapGrid
will have an initialise method that takes in a 2D array ofCell
objects. The initialise method inMapGrid
will then do a deep copy of the passed in 2D array to initialise the internal 2D array ofCell
.-
Pros: The underlying 2D array in
MapGrid
is better protected from modification as it can only be modified through the initialise method. -
Cons: Might have more overhead due to deep copying.
-
-
Alternative 2:
MapGrid
will have a getter method that returns the 2D array ofCell
.InitialiseMapCommand
will then use this method to get the internal 2D array and populate it from outside of theMapGrid
class.-
Pros: Easy to implement.
-
Cons: The 2D array within
MapGrid
is unprotected and open for modification.
-
Alternative 1 was chosen as the overhead is negligible and a defensive approach to the design is preferable.
Aspect: Data structure to support the map
-
Alternative 1 (current choice): 2D array of
Cell
objects.-
Pros: Resulting code is simple and readable.
-
Cons: More changes to be done from the original AB4 codebase.
-
-
Alternative 2: List of lists of
Cell
objects.-
Pros: Easier to implement from the original AB4 codebase.
-
Cons: Worse readability and more complicated compared to using a 2D array.
-
Alternative 1 was chosen because of good code readability and it being the simpler implementation. Good code readability is important for new developers taking on the project. Simpler implementation means the likelihood of bugs being introduced is less when changes are made.
Initialise Maps
MSS
-
User requests to initialise maps
-
Battleship creates and displays both player’s maps
Use case ends.
Extensions
-
2a. The map size is invalid.
-
2a1. Battleship shows an error message
Use case resumes at step 1.
-
Initialise the maps
-
Initialising the maps after launching the game
-
Test case:
init 7
Expected: Displays two empty map grids of size 7 in the GUI. Both maps will have the appropriate alphanumeric labels on the first row and column. -
Test case:
init 0
Expected: If this is the first command ran, no map will be displayed. Otherwise, no changes will be made to the displayed maps. Error message will be displayed in the result display box. -
Other incorrect initialise commands to try:
init
,init x
(where x is smaller or larger than the specified minimum or maximum map size respectively).
Expected: No map will be displayed if this is the first command ran. Otherwise, no changes will be made to the displayed map.
-