CIT 590 Assignment 6: Battleship
Spring 2013, David Matuszek

# Purposes of this assignment

• To give you some experience with classes and inheritance.
• To give you more experience with tuples, sets, and dictionaries.

# General idea of the assignment

This assignment is based on a game, since games are a good source of relatively simple problems.

The Fleet
One battleship
 S
 S
 S
 S
Two cruisers
 S
 S
 S

 S
 S
 S
Three destroyers
 S
 S

 S
 S

 S
 S
Four submarines
 S

 S

 S

 S

Battleship is usually a two-player game, where each player has a fleet and an ocean (hidden from the other player), and tries to be the first to sink the other player's fleet. We'll just do a solo version, where the computer places the ships, and the human attempts to sink them. (Unfortunately, this makes the game much less fun to play.)

Pay careful attention to the types of information (sets, dictionaries, tuples) used in this assignment; where specified, these are requirements.

# How to play

The computer places the ten ships on the ocean in such a way that no ships are immediately adjacent to each other, either horizontally, vertically, or diagonally. For example,

Legal arrangement
 S S S S S S S S S S S S S S S S S S S S
 S S S S S S S S S S S S S S S S S S S
 S S S S S S S S S S S S S S S S S S S

The human player does not know where the ships are. The initial display of the ocean shows a 10 by 10 array of locations, all the same.

The human player tries to hit the ships, by calling out a row and column number. The computer responds with one bit of information--hit" or "miss." When a ship is hit but not sunk, the program does not provide any information about what kind of a ship was hit. However, when a ship is hit and sinks, the program prints out a message `"You just sank a ship-type."` After each shot, the computer redisplays the ocean with the new information.

A ship is "sunk" when every square of the ship has been hit. Thus, it takes four hits (in four different places) to sink a battleship, three to sink a cruiser, two for a destroyer, and one for a submarine. The object is to sink the fleet with as few shots as possible; the best possible score would be 20. (Low scores are better.) When all ships have been sunk, the program prints out a message that the game is over, tells how many shots were required, and prints an evaluation (for example, "You did great!" for an exceptionally small number of shots).

# Details

Important note: In all of the above method descriptions I have omitted the initial `self` parameter. It's your job to remember that functions don't need it, methods do.

## The modules

Your program should have the following modules (files):

• `battleship_game.py `
• This is the "main" class. It should have a `main` method and it should end with the lines
``````if __name__ == '__main__':
main()``````
• `battleship_game_tests.py`
• This module runs all the unit tests.
• Other modules
• You may have as many modules as you like, in addition to the above two. (A common convention is to put each class in a separate file.) You can use `import` statements to access code in one module from another module, so having several files does not make the program more complex, and it may simplify working with a partner.
• Everything should be in the two files listed above. You can use other modules while you are developing the program, if you like, but before you turn in the program, combine the code into the two required files.

## The classes

Your program should have the following classes:

• `class BattleshipGame` -- This is the "main" class, containing the `main` method. Interaction with the user occurs in this class.
• `class Ocean` -- This contains a dictionary of `Ship`s, representing the "ocean," and some methods to manipulate it.
• `class Ship` -- This describes characteristics common to all the ships. It has subclasses:
• `class Battleship extends Ship` -- Describes a ship of length 4.
• `class Cruiser extends Ship` -- Describes a ship of length 3.
• `class Destroyer extends Ship` -- Describes a ship of length 2.
• `class Submarine extends Ship` -- Describes a ship of length 1.

## class BattleshipGame

The `BattleshipGame` class is the "main" class--that is, it contains a `main` method. In this class you will set up the game; accept "shots" from the user; display the results; print final scores; and ask the user if he/she wants to play again. All input/output is done here (although some of it is done by calling a` print `method in the `Ocean` class.) All significant computation will be done in the `Ocean` class and the various `Ship` classes.

Use methods. Don't cram everything into one or two methods, but try to divide up the work into sensible parts with reasonable names.

## class ShipTest

Test every method in the `Ship` class. TDD (Test-Driven Design is highly recommended.) Test the different types of ships.

Also test the methods in each subclass of `Ship`. You can do this here or in separate test classes, as you wish.

## class Ship

Since we don't really care which end of a ship is the bow and which the stern, we will consider all ships to be facing up or left. Other parts of the ship are in higher-numbered rows or columns.

In the following, `location` always means a 2-tuple, `(row, column)`, where `0 <= row <= 9` and `0 <= column <= 9`.

Instance variables
` locations ` -- A set of locations occupied by the ship. Battleships occupy four locations, Cruisers 3, etc.
` ``hits` -- A set of locations on this ship that have been "hit;" `hits` is always a subset of `locations`.
Methods:
` ok_to_place_ship_at(row, column, horizontal, length, ocean)`
Returns `True` if it is okay to put a ship of this length with its bow in this row and column, with the given orientation, and returns `False` otherwise. The ship must not overlap another ship, or touch another ship (vertically, horizontally, or diagonally), and it must not "stick out" beyond the array. Does not actually change either the ship or the Ocean, just says whether it is legal to do so. Note: `row`, `column`, and `length` should be integers; `horizontal` should be a boolean, and `ocean` should be an Ocean object, not just the map contained in the Ocean.
` place_ship_at(row, column, horizontal, length, ocean)`
"Puts" the ship in the ocean. This involves giving values to the `locations` instance variable in the ship, and it also involves putting a reference to the ship in each of 1 or more locations (up to 4) in the `ships` dictionary in the `Ocean` object. (Note: This will be as many as four identical references; you can't refer to a "part" of a ship, only to the whole ship.) Note: `row`, `column`, and `length` should be integers; `horizontal` should be a boolean, and `ocean` should be an Ocean object, not just the map contained in the Ocean.
` shoot_at(location)`
If a part of this ship occupies the given row and column, and the ship hasn't been sunk, mark that part of the ship as "hit" (in the `hits` set) and return `True`, otherwise return `False`. Notice that this method has the same name as a method in the `Ocean` class, and will be used by that method.
Programming note: This method expects a 2-tuple as a parameter. The call` shoot_at(r, c) `has two parameters, not a tuple parameter; but the call` shoot_at((r, c)) ` works correctly. The same is true for other methods that expect a location as a parameter.
`is_sunk()`
Returns `True` if all locations of this Ship have been hit, and `False` otherwise. (Hint: It's easiest to compute this each time the method is called.)

## class Battleship extends Ship class Cruiser extends Ship class Destroyer extends Ship class Submarine extends Ship

Each of these classes has the following instance variables methods:

`ship_type`
Instance variable that holdsone of the strings `"battleship"`, `"cruiser"`, `"destroyer"`, or `"submarine"`, as appropriate.
`__init__()`
Sets the ship type, sets `hits` to an empty dictionary set, and does any other necessary initialization.
`get_ship_type()`
Returns one of the strings `"battleship"`, `"cruiser"`, `"destroyer"`, or `"submarine"`, as appropriate.
`__str__()`
Returns a single-character String to use in the `Ocean`'s `print `method (see below).

## class OceanTest

This is a JUnit test class for `Ocean`. Test every required method for `Ocean`, including the constructor, but not including the` print()` method. If you create additonal methods in the `Ocean` class, write tests for them. Test methods do not need comments, unless they do something non-obvious.

Experience has shown that it is a bad idea for one partner to write the tests and the other partner to write the code. It works much better if you write tests for your own code.

## class Ocean

Instance variables
`ships`-- A dictionary whose keys are locations and whose values are Ships. Used to quickly determine which ship is in any given location.
`shots_fired` -- The total number of shots fired by the user.
`hit_count` -- The number of times a shot hit a ship. If the user shoots the same part of a ship more than once, every hit is counted, even though the additional "hits" don't do the user any good.
`number_of_ships_sunk` -- The number of ships sunk (there are 10 ships in all).
``` __init__()```
The constructor. Initializes the ocean to contain all the ships (use the `place_all_ships_randomly` method).

`place_all_ships_randomly()`
Create all ten ships and place them randomly on the ocean. Place larger ships before smaller ones, or you may end up with no legal place to put a large ship. (Hint: use `random.randint`).
`is_occupied(location)`
Returns `True` if the given location contains a ship, `False` if it does not.
`is_open_sea(location)`
Returns `True` if the given location neither contains nor is adjacent to some ship, `False` otherwise.
`shoot_at(location)`
Returns `True` if the given location contains a ship that is still afloat (before this shot), `False` if it does not. In addition, this method updates the number of shots that have been fired, and the number of hits.
Note: If a location contains a "real" ship, `shoot_at` should return `True` every time the user shoots at that same location. Once a ship has been "sunk", additional shots at its location should return `False`.
`get_shots_fired()`
Returns the number of shots fired (in this game).
`get_hit_count()`
Returns the number of hits recorded (in this game). All hits are counted, not just the first time a given square is hit.
`get_ships_sunk()`
Returns the number of ships sunk (in this game).
`game_is_over()`
Returns `True` if all ten ships have been sunk, otherwise `False`.
`get_ocean()`
Returns the dictionary of ships (not everything about this ocean). This is the methods in the `Ship` class that take an `Ocean` parameter really need to be able to look at the contents of this array; the `place_ship_at` method even needs to modify it. While it is undesirable to allow methods in one class to directly access instance variables in another class, sometimes there is just no good alternative.
` print()`
Prints the ocean. To aid the user, row numbers should be displayed along the left edge of the array, and column numbers should be displayed along the top. Numbers should be` 0 `to` 9`, not` 1 `to` 10`. The top left corner square should be` 0`, `0`. Use the following codes:
• `. `(period) to indicate a location that has never been shot at.
• `- `(hyphen) to indicate a location that has been shot at, but nothing was there.
• `* `to indicate a location containing a ship (of unknown type) that has been hit but not yet sunk, and
• `B`, `C`, `D`, or `S` to idicate a ship that has been sunk; the letter indicates the kind of ship it was.
Example:
```      0 1 2 3 4 5 6 7 8 9
---------------------
0 | . . . . . . . . . .
1 | . C C C . * - . . *
2 | . . . . . . . . . .
3 | . * . . * - . . . .
4 | . - . . . . . S . .
5 | . * . . . . . . . -
6 | . . - . . - . . . *
7 | . . . - . - . . . *
8 | . - . . . . . . . .
9 | . . . . . . . . . .```
This is the only method in the `Ocean` class that does any input/output, and it is never called from within the `Ocean` class (except possibly during debugging), only from the `BattleshipGame` class.

You are welcome to write additional methods of your own. All methods that do computation should be tested.

Decide which of you will submit the assignment (only one assignment per team, please!) and submit it by 6am Friday February 22. Zip (don't use `.rar`) your `.py` files and submit the zipped file to Canvas. No other form of submission will be accepted.