Project: Battleship CLI

Battleship CLI is an implementation of the classic game Battleship.

As part of a group project in the module CS2103 in NUS, my group had to morph an existing product, addressbook-level4, into a new product while keeping the original product’s distinctive feature of being operated through the command-line. My group chose to morph it into a command-line based Battleships application.

My role in the team was to design and implement the Battle feature, which:

  • Ensures that the human and computer players each take their turns in order,

  • Ensures that the game progresses in a fixed sequence of stages (from setting up the map, to placing ships, to attacking)

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.

Summary of contributions

  • Major enhancement: implemented the logic for the battle stage of the game
    The battle is one of the two important phases of Battleship, along with the placing of ships. Via the battle feature, the user will be able to launch attacks at the enemy, and the enemy launch attacks at the user. Crucially, it ensures that the user and enemy take their turns in the correct order.

  • Minor enhancement: added the ability to prevent commands from being executed at inappropriate times
    Preventing the user from executing certain commands at certain times (e.g. the user should not be placing ships when the battle is already in progress) will ensure the correct progression of the game, as well as prevent the game from entering an invalid state.

  • Code contributed: Here are my [code commits].

  • Other contributions:

    • Project management:

      • Removed elements from addressbook-level4 that were not used in our project (Pull requests #274, #336)

      • Refactored the Battle component’s package structure (Pull request #407)

    • Testing:

      • Wrote tests for that increased coverage by a total of 6.7% (Pull requests #231, #269, #404)

    • Documentation:

      • Wrote the sections of the User Guide and Developer guide for the Battle Component, as reproduced below

    • Community:

      • Made summaries of current issues in order to make it easier for the team to coordinate our efforts. (Issues #211, #275, #394)

      • Reported bugs for team W15-3.

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.

Battle phase

Beginning the battle: begin

Begins the battle against the computer enemy.
Format: begin
Alternative command: start

  • After this command is entered, the program’s AI will place its own ships.

  • After the AI completes its placing of ships, you can take your first turn.

Shooting at a coordinate: shoot

Launches an attack against given coordinate on the enemy’s map.
Format: shoot COORDINATES
Alternative commands: attack, fire, hit

Examples:

  • shoot a6

  • attack b5

  • The program will prevent you from attacking an invalid coordinate.

  • The program will prevent you from attacking a square that you have already attacked.

  • If you hit one of the opponent’s ships, you may make another attack. Otherwise, the enemy will take their turn(s), firing until they get a miss.

Contributions to the Developer Guide

Given below is an excerpt of my contributions to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. To see my full contribution, please refer to the full Developer Guide.

2. Design

Battle component

BattleClassDiagram
Figure 1. Structure of the Battle Component and interactions with Model and Logic

API : Battle.java

3. Implementation

Battle feature

The Battle feature handles the following:

  • keeping track of the stage of the battle, and ensuring that the player does not enter a command in the wrong stage of the game,

  • allowing the computer to place its ships at the correct time,

  • allowing the user and the computer enemy to attack each other and maintain proper turn-taking while doing so.

The Battle feature is split between several packages:

  • seedu.address.battle: the main Battle class is implemented here.

  • seedu.address.battle.state: the BattleState class is implemented here, and can be stored and retrieved via Model.

  • seedu.address.battle.result: the attack result classes are implemented here. These classes are returned by Battle#humanPerformAttack(Coordinates) and Battle#takeComputerTurn().

  • BeginCommand and AttackCommand handle the player’s interaction with this component.

In the following sections, we will explain the workings of each of these different packages.

Implementation of battle state

BattleStateDiagram
Figure 2. State diagram of the application

In this program, there are certain commands that may only be executed at specific phases of the game (e.g. attack must only be used when battling, not while placing ships). To implement this, the battle state must be tracked.

The battle state is implemented as a BattleState enumeration, and stored within the Model. When LogicManager executes a Command object, it checks whether that command is allowed to be executed in the current state using Command#canExecuteIn(BattleState). Only if the command is allowed to execute does LogicManager then call Command#execute - if it is not, then execution is prevented and the user is notified.

In addition, certain commands may change the battle state - for example, start changes the battle state from PLAYER_PUT_SHIP to ENEMY_PUT_SHIP then finally to PLAYER_ATTACK.

Implementation of the BattleManager class

List<AttackResult> takeComputerTurn()
TakeComputerTurnSequenceDiagram
Figure 3. Sequence diagram of the takeComputerTurn method

takeComputerTurn is called after the player makes an attack but misses (via AttackCommand, see below). As mentioned in the game rules, after the player misses the computer may begin to attack until it misses, and this method implements that functionality.

Implementation of attack result

The representation of the result of an attack is the class AttackResult and its subclasses. To help the receiver in deciphering the attack result without needing to resort to instanceof, methods are provided to test for attributes such as whether the attack is a hit.

The methods are:

  • isSuccessful: tests whether the attack actually completed

  • isHit: tests whether the attack damaged a ship

  • isDestroy: tests whether the attack destroyed the ship

  • isWin: tests whether the attack caused the attacker to win

A summary of each type of AttackResult can be seen from the following table:

Result type Scenario

AttackFailed

The Coordinates provided was out of bounds, or some error occurred during the attack.

AttackMissed

The attack did not hit an enemy ship.

AttackHit

The attack hit an enemy ship but did not sink it.

AttackDestroyedShip

The attack hit an enemy ship and sank it, but the enemy still has ships remaining.

AttackDefeatedEnemy

The attack hit the last of the enemy’s ships and sank it, resulting in a victory for the attacker.

Implementation of player interaction via commands

BattleUseCaseDiagram
Figure 4. Use case diagram of the Battle Component.
BattleActivityDiagram
Figure 5. Activity diagram of the process of battling.

The upper diagram is a use case diagram which shows briefly the possible interactions between the user and the application during the battling phase. Below is a more detailed activity diagram of what happens when the user does battle against the computer enemy. As shown, the player does battle by using the begin command to initiate the battle, then the attack command to attack the enemy.

The following sequence diagrams show what happens when the user enters the begin command, then the attack a1 command.

BeginSequenceDiagram
Figure 6. Sequence diagram of begin.
AttackSequenceDiagram
Figure 7. Sequence diagram of attack a1.

Design considerations

  • Current choice:
    BattleManager is stored under Model. Every time Attack or Begin commands are executed, they will use this BattleManager to actually perform the actions, with the logic in the Command-s mostly being error handling.
    When AI performs attacks, the BattleManager will call the AI to compute its attack and return it, then call an internal method to actually perform the attack.

    • Pros:

      • Command logic is abstracted into places where it can be reused by the AI.

      • Flow of program is clear - it always is a higher level component calling a lower-level component.
        (e.g. when the player types in an Attack command, user interacts with UI, which calls Logic, which calls Model and BattleManager, which call the lowest level classes Map and Player)

    • Cons:

      • The Model component now contains game logic (BattleManager) within it.

  • Alternative
    BattleManager is stored under Logic. Every time Attack or Begin commands are executed, they will perform the action using the logic coded within themselves, not interfacing with BattleManager.
    When AI performs attacks, the BattleManager will create these commands and execute them. In the Model, the current attacking player is kept track of, allowing the commands to be used for both a human and AI player.

    • Pros:

      • The game’s model and logic are kept separate from each other.

    • Cons:

      • AttackCommand is now state-dependent (the state being the current attacking player) which can more easily lead to bugs and race conditions.

    In the end, we decided to implement Option 1. Even though some team members preferred either option, we decided that Logic and Model not being kept separate was a worthy tradeoff for the advantages of Option 1.

Appendix C: Use Cases

Begin the battle

MSS

  1. User requests to begin the battle.

  2. Game instructs the enemy to place their ships.

  3. Game displays that the battle has begun.

    Use case ends.

Extensions

  • 2a. The user has not placed any ships.

    • 2a1. The game prevents the user from starting the battle, and informs them to place at least one ship.

Appendix F: Instructions for Manual Testing

Attacking

  1. To test attack, we will need to play through the game. Please follow the following steps in order.

    1. Start the application, then execute attack.
      Expected result: no attack is executed, and an error message is displayed.

    2. Execute init 6, and place your ships on the board.

    3. Execute begin.

    4. Make an attack on a square that is out of bounds, e.g. attack a9.
      Expected result: no attack is executed, and a prompt to select another cell is displayed.

    5. Make an attack on a valid square that misses.
      Expected result: the attacked square on the enemy map turns dark blue, and a "miss" message is displayed. The enemy also makes one or more moves.

    6. Make an attack on a square you have already attacked. Expected result: no attack is executed, and a prompt to select another cell is displayed.

    7. Make an attack on a valid square that hits.
      Expected result: the attacked square on the enemy map turns orange, and a "hit" message is displayed.

    8. Make an attack that destroys a ship.
      Expected result: the destroyed ship on the enemy map turns red, and a "destroy" message is displayed.

    9. Destroy all the enemy’s ships.
      Expected result: a "win" message is displayed.

    10. Start another battle, then have all your ships be destroyed by the enemy.
      Expected result: a "lose" message is displayed.