Purposes of this assignment:
- To give you more experience with collections (tuples, sets, ...)
- To give you experience of writing tests and TDD-style development
- To give you experience of working with output and graphics
This assignment is based on the well-known game. 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.
The Ocean is a field of 10 x 10 squares. The squares are numbered from 0 to 9 in each dimension with numbers increasing from top to bottom and from left to right. ![ocean]
The fleet consists of 10 ships. The fleet is made up of 4 different types of ships, each of different size as follows:
- One battleship, occupying 4 squares
- Two cruisers, each occupying 3 squares
- Three destroyers, each occupying 2 squares
- Four submarines, each occupying 1 square
To begin the game, the computer places all the 10 ships of the fleet in the ocean randomly. Each ship can be placed either horizontally (as shown in the figure above) or vertically. Moreover, no ships may be immediately adjacent to each other, either horizontally, vertically, or diagonally.
The human player does not know where the ships are. The human player tries to hit the ships, by calling out a row and column number. The computer responds with one bit of information--"You have a hit!" or "You missed!" (Note that the human player can call out the same location more than once, even though it does not make sense. If that happens, 2nd, 3rd, ... calls of the same location are misses.) 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 sank a _ship-type_!"
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 objective 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 and tells how many shots were required.
We represent a ship by means of tuples (row, column, horizontal, length, hits) where:
row
andcolumn
are integers between 0 and 9 identifying, respectively, the row and column of the square of the top-left corner of the shiphorizontal
is a Boolean value equal toTrue
if the ship is placed horizontally andFalse
if placed verticallylength
is an integer between 1 and 4 representing the length of the shiphits
is a set of tuples of the form(row, column)
containing all the squares occupied by the ship that were hit
We represent a fleet by means of a list
[ship1, ship2, ....]
of ships. Note that during the game, a fleet will contain 10 ships (which may be intact, hit, or sunk), however, when the computer places the ships randomly, it is convenient to start with an empty list and iteratively expand it by adding ships.
is_sunk(ship)
-- returns Boolean value, which isTrue
ifship
is sunk andFalse
otherwiseship_type(ship)
-- returns one of the strings"battleship"
,"cruiser"
,"destroyer"
, or"submarine"
identifying the type ofship
is_open_sea(row, column, fleet)
-- checks if the square given byrow
andcolumn
neither contains nor is adjacent (horizontally, vertically, or diagonally) to some ship infleet
. Returns BooleanTrue
if so andFalse
otherwiseok_to_place_ship_at(row, column, horizontal, length, fleet)
-- checks if addition of a ship, specified byrow, column, horizontal
, andlength
as inship
representation above, to thefleet
results in a legal arrangement (see the figure above). If so, the function returns BooleanTrue
and it returnsFalse
otherwise. This function makes use of the functionis_open_sea
place_ship_at(row, column, horizontal, length, fleet)
-- returns a new fleet that is the result of adding a ship, specified byrow, column, horizontal
, andlength
as inship
representation above, tofleet
. It may be assumed that the resulting arrangement of the new fleet is legalrandomly_place_all_ships()
-- returns a fleet that is a result of a random legal arrangement of the 10 ships in the ocean. This function makes use of the functionsok_to_place_ship_at
andplace_ship_at
check_if_hits(row, column, fleet)
-- returns Boolean value, which isTrue
if the shot of the human player at the square represented byrow
andcolumn
hits any of the ships offleet
, andFalse
otherwisehit(row, column, fleet)
-- returns a tuple(fleet1, ship)
whereship
is the ship from the fleetfleet
that receives a hit by the shot at the square represented byrow
andcolumn
, andfleet1
is the fleet resulting from this hit. It may be assumed that shooting at the squarerow, column
results in hitting of some ship infleet
are_unsunk_ships_left(fleet)
-- returns Boolean value, which isTrue
if there are ships in the fleet that are still not sunk, andFalse
otherwisemain()
-- returns nothing. It prompts the user to call out rows and columns of shots and outputs the responses of the computer (see General Idea of Assignment) iteratively until the game stops. Our expectations from this function: (a) there must be an option for the human player to quit the game at any time, (b) the program must never crash (i.e., no termination with Python error messages), whatever the human player does. Note that there is an indicative implementation ofmain()
to help you start working, but it does not satisfy the expectations above and you should improve or entirely redo it.
Notes:
ship
,row
,column
,fleet
,fleet1
,horizontal
must be of the corresponding data types (see Data Structures)
You can add any additional functions or classes if you need. We recommend also testing your new functions and classes, if they are easily testable, but doing that would not affect your mark.
In addition, you can implement the visualisation of the game. Our expectations of the high-quality visualisation are as follows:
- The new state of the ocean and fleet are presented to the human player each time after he/she shoots
- The rows and columns of the ocean are numbered
- The squares that have never been shot at are clearly indicated (i)
- The squares that have been shot at but not resulted in a hit (nothing was there) are clearly indicated (ii)
- The squares containing a ship (of unknown type) that has been hit but not yet sunk are clearly indicated (iii)
- The ships that were sunk are clearly indicated, as well as their type (iv)