Instructions
Requirements and Specifications
Source Code
BATTLESHIP GAME
/**
* A simple implementation of the game Battleship. There are two players, each with 5 ships as in
* the real game. This version uses the extends relation to create 5 ship types: Carrier
* Battleship
* Destroyer
* Submarine
* PTBoat
*
*
* The ships, one each of the above types, are placed on the board at random but do not overlap.
* Boards are displayed in ASCII characters. One player wins win he/she sinks all of the other
* player's ships.
*
*
*
* @author David Mathias
* @version November 12, 2013
*/
import java.util.Scanner;
class BattleshipGame
{
// This is the method that begins and controls game play.
public static void main(String[] args)
{
// Scanner for getting player moves
Scanner scan = new Scanner(System.in);
// There are two players, each with their own board.
// Player 0 plays first.
Board player0 = new Board(scan);
Board player1 = new Board(scan);
int current_player = 0;
// Play until someone wins
while(!player0.allSunk() && !player1.allSunk())
{
if(current_player == 0)
{
System.out.printf("Player 0, it is your turn. Here is the board:%n%n");
player1.printBoard();
player1.makeGuess();
player1.printBoard();
current_player = 1;
}
else if(current_player == 1)
{
System.out.printf("Player 1, it is your turn. Here is the board:%n%n");
player0.printBoard();
player0.makeGuess();
player0.printBoard();
current_player = 0;
}
System.out.printf("%n%n");
}
if(player0.allSunk())
{
System.out.printf("Player 1, you win!%n%n");
}
else
{
System.out.printf("Player 0, you win!%n%n");
}
scan.close();
}
}
BOARD
/**
* Board class for the Battleship game.
*
* A board is an nxn array containing one each of: Carrier
* Battleship
* Destroyer
* Submarine
* PTBoat
*
* @author David Mathias
* @version November 12, 2013
*/
import java.util.Scanner;
public class Board
{
// class variables
// If the number of ships is changed, the constructor must be
// updated as well
// When debug is true, positions of ships will be shown when the
// board is displayed. This is helpful when testing the game since
// you won't have to guess the ships' locations.
final static boolean debug = true;
final static int board_size = 10;
final static int num_ships = 5;
// Characters printed when the board is displayed. Not all are used.
final static char hit = 'H';
final static char miss = 'M';
final static char used = 'S';
final static char blank = ' ';
final static char sunk = 'X';
// instance variables - replace the example below with your own
private char[][] board = new char[board_size][board_size];
private int num_sunk;
private Ship[] ships = new Ship[num_ships];
private Scanner scanIn;
/**
* Default constructor for objects of class Board
*
* @param s
*/
public Board(Scanner s)
{
// initialise instance variables
scanIn = s;
num_sunk = 0;
initializeBoard();
// create the ships
ships[0] = new PTBoat();
hideShip(0);
ships[1] = new Submarine();
hideShip(1);
ships[2] = new Destroyer();
hideShip(2);
ships[3] = new Battleship();
hideShip(3);
ships[4] = new Carrier();
hideShip(4);
}
/**
* Set every board position to blank
*/
private void initializeBoard()
{
for(int i=0; i < board_size; i++)
{
for(int j=0; j < board_size; j++)
{
board[i][j] = blank;
}
}
}
/**
* Place a ship on the board at a random but valid location.
* In other words, a ship can't go beyond the bounds of the board
* or intersect another ship.
*
* @param s - index into the array of ships
*/
private void hideShip(int s)
{
boolean valid = false;
// keep looking until a randomly generated position is valid
while(!valid)
{
// choose a direction and a starting row or column
int start = (int)(Math.random() * (board_size - ships[s].getSize()));
int dir = (int)(Math.random() * 2);
if(dir == 0) // horizontal
{
ships[s].setDir(0);
ships[s].setCol(start);
int row = (int)(Math.random() * 32767) % board_size;
ships[s].setRow(row);
valid = true;
for(int i=0; i < ships[s].getSize(); i++)
{
valid = (valid && (board[row][start + i] != used));
}
if(valid)
{
for(int i=0; i < ships[s].getSize(); i++)
{
board[row][start + i] = used;
}
}
}
else // dir == 1, vertical
{
ships[s].setDir(1);
ships[s].setRow(start);
int col = (int)(Math.random() * 32767) % board_size;
ships[s].setCol(col);
valid = true;
for(int i=0; i < ships[s].getSize(); i++)
{
valid = (valid && (board[start + i][col] != used));
}
if(valid)
{
for(int i=0; i < ships[s].getSize(); i++)
{
board[start + i][col] = used;
}
}
}
}
}
// method allSunk
/**
* Return true if all of the ships on the board have been sunk
*
* @return boolean
*/
public boolean allSunk() {
for (Ship ship : ships) {
if (!ship.isSunk()) {
return false;
}
}
return true;
}
/**
* Output a representation of the board
*/
public void printBoard()
{
System.out.printf(" ");
for(int i=0; i < board_size; i++)
{
System.out.printf("%4d", i);
}
System.out.printf("%n%n");
// for each board position, print blank, hit or miss
for(int i=0; i < board_size; i++)
{
System.out.printf("%d ", i);
for(int j=0; j < board_size; j++)
{
// don't want to reveal position of ships when printing
// the board so print a blank instead
if(board[i][j] == used && !debug)
{
System.out.printf("%4c", blank);
}
else
{
System.out.printf("%4c", board[i][j]);
}
}
System.out.printf("%n%n");
}
}
// makeGuess method
/**
* Get a guess from the user. Make sure it hasn't been guessed
* previously. Call evaluateGuess to see if it is a hit or a miss.
* Assume that the values entered are integers in the correct range.
*/
public void makeGuess() {
System.out.print("Enter a row (0--9): ");
int row = Integer.parseInt(scanIn.nextLine());
System.out.print("Enter a column (0--9): ");
int column = Integer.parseInt(scanIn.nextLine());
if (board[row][column] == blank || board[row][column] == used) {
evaluateGuess(row, column);
}
else {
System.out.printf("%nThat's a miss.%n");
}
}
/**
* Determine if a guess is a hit or a miss
*
* @param r - row of guess
* @param c - column of guess
*/
private void evaluateGuess(int r, int c)
{
// assume the guess is a miss until we see otherwise
int i = 0;
boolean is_hit = false;
// check each ship by calling the Ship isAHit method
while((i < num_ships) && !is_hit)
{
// if it's a hit, update that board position to hit
if(ships[i].isAHit(r, c))
{
System.out.printf("That's a Hit! %n");
board[r][c] = hit;
is_hit = true;
if(ships[i].isSunk())
{
num_sunk++;
markSunk(ships[i]);
System.out.printf("You sunk my %s!%n%n", ships[i].getName());
}
}
i++;
}
// if it's a miss, update board position to miss
if(!is_hit)
{
board[r][c] = miss;
System.out.printf("%nThat's a miss.%n");
}
}
// method markSunk
/**
* When a ship is sunk, change the board markers
* for that ship to 'X'. Note that this is defined
* as a final static char at the top of this file.
*
* @param s - the ship that has just been sunk
*/
public void markSunk(Ship s) {
if (s.isSunk()) {
for (int i = 0; i<s.getSize(); i++) {
int row = s.getDirection() == 0 ? s.getStartRow() : s.getStartRow() + i;
int col = s.getDirection() == 0 ? s.getStartCol() + i : s.getStartCol();
board[row][col] = sunk;
}
}
}
}
CARRIER
/**
* Class for the Aircraft Carrier of the Battleship Game.
*
* Superclass: Ship
* Subclasses: none
*
* Attributes: String name
* int aircraft
*
* name = "Aircraft Carrier"
* size = 5
* aircraft = number of aircraft on this aircraft carrier
*
* You must declare the attributes, complete the constructors and
* write any necessary getters and setters
*
* @author
* @version
*/
public class Carrier extends Ship
{
private static final int CARRIER_SIZE = 5;
// instance variables
/**
* Constructors for objects of class Carrier
*/
public Carrier()
{
// initialize name, size and aircraft and
// invoke the Ship constructor
super();
setSize(CARRIER_SIZE);
}
public Carrier(int row, int col, int dir)
{
super(row, col, dir);
setSize(CARRIER_SIZE);
}
/**
* This method exists solely so that it can be overriden in
* the subclasses of Ship
*/
@Override
public String getName() {
return "Aircraft Carrier";
}
}
DESTROYER
/**
* Class for the Destroyer of the Battleship Game.
*
* Superclass: Ship
* Subclasses: none
*
* Attributes: String name
* int guns
*
* name = "Destroyer"
* size = 3
* guns = number of guns on this Destroyer
*
* You must declare the attributes, complete the constructors and
* write any necessary getters and setters
*
* @author
* @version
*/
public class Destroyer extends Ship
{
private static final int DESTROYER_SIZE = 3;
// instance variables
/**
* Constructors for objects of class Carrier
*/
public Destroyer()
{
super();
setSize(DESTROYER_SIZE);
}
public Destroyer(int row, int col, int dir)
{
super(row, col, dir);
setSize(DESTROYER_SIZE);
}
/**
* This method exists solely so that it can be overriden in
* the subclasses of Ship
*/
@Override
public String getName() {
return "Destroyer";
}
}
PT BOAT
/**
* Class for the PT Boat of the Battleship Game.
*
* Superclass: Ship
* Subclasses: none
*
* Attributes: String name
* int charges;
*
* name = "PT Boat"
* size = 2
* charges - number of depth charges on this PT Boat
*
* You must declare the attributes, complete the constructors and
* write any necessary getters and setters
*
* @author
* @version
*/
public class PTBoat extends Ship
{
private static final int PTBOAT_SIZE = 2;
// instance variables
/**
* Constructors for objects of class Carrier
*/
public PTBoat()
{
super();
setSize(PTBOAT_SIZE);
}
public PTBoat(int row, int col, int dir)
{
super(row, col, dir);
setSize(PTBOAT_SIZE);
}
/**
* This method exists solely so that it can be overriden in
* the subclasses of Ship
*/
@Override
public String getName() {
return "PT Boat";
}
}
SHIP
/**
* Ship class for the Battleship game.
*
* Superclass: none (Object)
* Subclasses: Carrier
* Battleship
* Destroyer
* Submarine
* PTBoat
*
* Attributes: int size - the number of board positions for this ship
* note that size is intitalized in the subclasses
* int start_row - the first row on which this ship appears
* int start_col - the first columb on which this ship appears
* int direction - is this ship horizontal or vertical on the board
* int hits - number of times this ship has been hit
* boolean sunk - if this ship has been sunk or not
*
* You must declare the attributes, complete the body of the isAHit method
* as well as all of the getters and setters. isAHit is a complicated
* operation -- reason it through before you start coding.
*
* @author
* @version
*/
public abstract class Ship
{
// class variables
// Constants used to encode direction
// What does the static keyword mean in this context?
protected static final int horizontal = 0;
protected static final int vertical = 1;
// instance variables (attributes) go here
private int size;
private int start_row;
private int start_col;
private int direction;
private int hits;
private boolean sunk;
/*
*********************************************************
*** Students to provide the attributes (described above)
*********************************************************
*/
// Are any objects of type Ship ever declared?
// Think about what this means for when these
// constructors are invoked.
/**
* Default constructor for objects of class Ship
*/
public Ship()
{
// initialise instance variables
hits = 0;
sunk = false;
}
/**
* Constructor for objects of class Ship
*/
public Ship(int row, int col, int dir)
{
// initialise instance variables
hits = 0;
sunk = false;
start_row = row;
start_col = col;
direction = dir;
}
/**
* Given a guess (row, col) determine if it is a hit for this ship
* Update hits and sunk attributes of ship as appropriate
*
* Parameters: int row -- row of the guess
* int col -- column of the guess
*
* Return: true: if the guess hits this ship
* false: otherwise
*
* Note: you do not have to determine if this position has been guessed
* previously -- that has already been done in the Board class.
* Also - in this method, you are evaluating one guess for one ship.
* That is very important -- the guess you are provided as a parameter
* is being evaluated by this method with respect to one ship, the
* ship to which this method is being applied. In the Board class,
* this method is called for each ship -- in that way we check all of
* the ships.
*/
public boolean isAHit(int row, int col)
{
boolean result;
if (direction == horizontal) {
int diff = col - start_col;
result = row == start_row && (diff >= 0) && (diff < size);
}
else {
int diff = row - start_row;
result = col == start_col && (diff >= 0) && (diff < size);
}
if (result) {
hits++;
if (hits == size) {
sunk = true;
}
}
return result;
}
// getters and setters
/**
* Indicate if the ship has been sunk
*/
public boolean isSunk()
{
return sunk;
}
/**
* Set value of start_row
*/
public void setRow(int row)
{
start_row = row;
}
/**
* Set value of start_col
*/
public void setCol(int col)
{
start_col = col;
}
/**
* Set value of size
*/
public void setSize(int size)
{
this.size = size;
}
/**
* Set value of direction
*/
public void setDir(int dir)
{
direction = dir;
}
/**
* Return value of size
*/
public int getSize()
{
return size;
}
/**
* Return value of start_row
*/
public int getStartRow()
{
return start_row;
}
/**
* Return value of start_col
*/
public int getStartCol()
{
return start_col;
}
/**
* Return value of direction
*/
public int getDirection()
{
return direction;
}
/**
* This method exists solely so that it can be overriden in
* the subclasses of Ship
*/
public abstract String getName();
}
SUBMARINE
/**
* Class for the Submarine of the Battleship Game.
*
* Superclass: Ship
* Subclasses: none
*
* Attributes: String name
* int torpedos
*
* name = "Submarine"
* size = 3
* torpedos = number of torpedos on this Submarine
*
* You must declare the attributes, complete the constructors and
* write any necessary getters and setters
*
* @author
* @version
*/
public class Submarine extends Ship
{
private static final int SUBMARINE_SIZE = 3;
// instance variables
/**
* Constructors for objects of class Carrier
*/
public Submarine()
{
super();
setSize(SUBMARINE_SIZE);
}
public Submarine(int row, int col, int dir)
{
super(row, col, dir);
setSize(SUBMARINE_SIZE);
}
/**
* This method exists solely so that it can be overriden in
* the subclasses of Ship
*/
@Override
public String getName() {
return "Submarine";
}
}