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:

MapGrid

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.

important

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. The init 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:

    • 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:

      • Reviewed PRs (with non-trivial review comments). (Pull Requests #53, #334)

      • Reported bugs and suggestions for other teams in the class. (Issues #672, #148)

    • 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

  • The init command can be used at any point in time during gameplay. When used in the middle of a game, the init command functions like a "new game" command and will reset the board to a clean state.

  • Each cell is represented by its coordinates e.g "b1". The coordinates will be used by the other commands to refer to a cell.

Figure 1 below shows how the maps will be displayed in the UI.

initmapresult
Figure 1. The maps as displayed in the UI


Throughout the course of the game, the cells in the map will change colour based on their status.

Cell statuses:
  • Hidden - Enemy map cell that has not been hit

  • Water - Empty (i.e cell with no ship) player cell that has not been hit

  • Water Hit - Empty cell that has been hit

  • Ship - Player cell that has a ship and has not been hit

  • Ship Hit - Ship cell that has been hit but not yet destroyed

  • Ship Destroyed - Ship cell that has been destroyed


Figure 2 below shows which colour is used for each status.

maplegend
Figure 2. Cell colours



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.

randommap
Figure 3. Example of a random map shape

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.

ModelClassDiagram
Figure 4. Model Diagram

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 given Cell 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.:

InitialiseMapCommandSequenceDiagram
Figure 5. Sequence diagram of initialising the maps

The following activity diagram shows when the "initialise map" command can be used by the user:

InitActivityDiagram
Figure 6. Activity diagram displaying when the user can initialise the maps


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 the Cell.

  • 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 the Status of the Cell.

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 of Cell objects. The initialise method in MapGrid will then do a deep copy of the passed in 2D array to initialise the internal 2D array of Cell.

    • 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 of Cell. InitialiseMapCommand will then use this method to get the internal 2D array and populate it from outside of the MapGrid 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

  1. User requests to initialise maps

  2. 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

  1. Initialising the maps after launching the game

    1. 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.

    2. 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.

    3. 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.