University of Calgary

Battleship - HELP!

Battleship - HELP!
Katrin Becker, 2001
Contents: Sample Main (C, and A-versions), 2-D Arrays, Printing Menus,
Testing for Placement of Ships, Representing the Board, Dealing with a Covered Board

(*****************************************************************
* Random Number function
*
* **************************************************************)
function DrawNum(low, high : integer) : integer;
var
range,num : integer;

begin
range := high - low + 1;
num := Random(range);
DrawNum := num + low;
end; (*Draw*)

Sample main 'C' version:
begin (* MAIN *)
  StartGame;
   request := ' ';
   while (not done) do
   begin
      DisplayBoard(player1, debug);
      DisplayMenu;
      request := GetRequest;
      case (request) of
          'h','H': DisplayHelp; (* display help *)
          's','S': begin (* take a shot *)
                     PlayUser(player1,debug);
                   end;
          'p','P': begin (* start a new game *)
                      StartGame;
                   end;
          'q','Q': begin (* end play *)
                      done := true;
                   end;
          else ; (* continue *)
      end;
   end;
   writeln('DONE.');
end. (* BATTLESHIP *)

Sample MAIN, 'A' version:
begin (* MAIN *)

Randomize;
StartGame;
request := ' ';
while (not done) do
begin
(* User's Turn *)
writeln('User''s Turn...... <hit return to continue>');
readln;
DisplayBoard('COMPUTER',player1, cover1, debug);
DisplayStats(p1Stats,debug,win);
DisplayMenu;
request := GetRequest;
case (request) of
'h','H': DisplayHelp; (* display help *)
's','S': begin (* take a shot *)
PlayUser(player1,cover1,p1Stats,done);
end;
'p','P': begin (* start a new game *)
StartGame;
end;
'q','Q': begin (* end play *)
done := true;
end;
else ; (* continue *)
end; (* case *)
DisplayBoard('COMPUTER',player1, cover1, debug);
DisplayStats(p1Stats,debug,win);
if (win) then
begin
writeln('CONGRATULATIONS! You WIN!!');
done := true;
end;

if (not done) then
begin (* Computer's Turn *)
writeln('Computer''s Turn.... <hit return to continue>');
readln;
PlayRand(player2,cover2,p2Stats);
DisplayBoard('USER',player2, cover2, SHOW);
DisplayStats(p2Stats,debug,win);
if (win) then
begin
writeln('TOO BAD. The Computer Beat You.');
done := true;
end;
end;
end;
writeln;
writeln(' End Game. ');
writeln('DONE.');

end. (* BATTLESHIP *)

2-D Arrays: declaration, initialization, printing
Declaration: define a new type that is a 2-D array:

const MAX = 10;
type MatrixT = array [1..MAX] of array [1..MAX] of integer;
var box : MatrixT;
     i,j : integer;

(* Initialization: *)
for i := 1 to MAX do
   for j := 1 to MAX do
      box[i][j] := 0;

(* printing *)
(* here we want to print all the elements of one 'row' in an inner loop, followed by a linefeed afterwards *)
(* headers go outside the loop *)
for i := 1 to MAX do
begin
   for j := 1 to MAX do
      write( box[i][j], ' ');
   writeln;
end;

(* suppose we want to label all the columns and rows. Here's how: *)
(* headers go here *)
(* do header *)
writeln;
write(' ');
for i := 1 to MAXBOARD do
   write(i:2); (* numbers across the top *)
writeln; (* top border *)
writeln(' ---------------------------------');

for i := 1 to MAXBOARD do
begin
   write(i:2,'| '); (* left-hand numbers & border *)
   for j := 1 to MAXBOARD do
   begin
     case (board[i][j]) of
                     (* see later for an explanation of this *)
        HIT : write('X ');
        MISS : write('* ');
        CLEAR : write(' ');
         1..5 : write(shipName[board[i][j]],' ');
     end;
   end; (* for j *)
   writeln('|');
end; (* for i *)
   (* bottom border *)
writeln(' ---------------------------------');

If we want to print two 'boards' side by side, we will have to print a row from each before we output a linefeed.

Printing Menus:
- First, draw what you want the output to look like.
- Hint: keep it simple by having each request be a single character
- once the request has been read and recognized, we can ask for any other necessary input.
Example:

procedure DisplayMenu;
begin
   writeln;
   writeln('Options:');
   writeln;
   writeln(' h : display help screen');
   writeln(' s : take a shot');
   writeln(' p : play a new game');
   writeln(' q : QUIT');
   writeln(' any key : continue game');
   writeln;
   write ('Enter Request: ');
end; (* DisplayMenu *)

function GetRequest : char;
   var
      ans : char;
begin
   ans := ' ';
   readln(ans);
   GetRequest := ans;
end; (* GetRequest *)

Testing for Placement of Ships:
- HINT: start with ships that are 1 square big.
- find a random location (row and column)
- check to see if there's a ship there already. If so, simply try again. We can't really use a for-loop here because we don't know how many times we will end up choosing a location. If we never choose the same location twice we will need to do this 5 times because there are 5 ships. Since we cannot guarantee we won't choose one location twice, a better approach might be to write this as a while-loop; count each successful ship placement and stop (leave the loop) when we have successfully placed 5 ships.
- once we have this working we can worry about ships of different sizes. Again make each step (refinement) relatively small and we have a very good chance of staying on top of any errors we introduce. Once we can place 5 single-square ships correctly (we have tested this), Let's try making all ships 3 squares long. [We can easily make ships of different sizes later]. If the user is allowed to specify one location (a row and column), or if the computer is allowed to choose one square at random, then we can do the rest. We can assume that the point we have is the top-most of left-most point of the ship. If the ship is oriented east-west, and if the given point is at board[r][c], then the end of the ship can be found at board[r][c+2]. If the ship is oriented north-south, then the other end can be found at board[r+2][c]. If we want to deal with ships of different sizes then the '2' will be replaced with the ship's size.
The two things we need to check are that the ship is completely on the board and that there are no other ships in any of the squares to be occupied by this one. The first can easily be checked by making sure that the other end ( either board[r][c+size-1] or board[r+size-1][c] depending on orientation) is still on the board. The second can be determined by examining each square that the ship will occupy to make sure it is clear. If either of these requirements do not check out we cannot place the ship there and will have to try again.

Representing The Board:
There are, as always, a number of ways of coping with this. Here's ONE way:
Represent the board internally as a 2-D array of integers. Values mean:
0 ::= CLEAR {no-one there}
1 ::= a ship {later we can represent different ships as 1-5}
-1 ::= a shot was fired here but it missed
-2 ::= a shot was fired here and it hit a ship

Now, when we go to print the board we can do it like this:
if the value is 0 print a blank ' '
if the value is 1 print 'S', meaning ship
if the value is -1 print '*' meaning a torpedo was fired here
if the value -s -2 priint 'X' meaning s piece of a ship was hit here
     case (board[i][j]) of
               (* see later for an explanation of this *)
          HIT : write('X ');
          MISS : write('* ');
          CLEAR : write(' ');
          1..5 : write(shipName[board[i][j]],' ');
     end;

Where:
(* board values *)
const
    A = 1;
    B = 2;
    C = 3;
    S = 4;
    D  = 5;

    MISS   = -1;
    CLEAR = 0;
    HIT   = -2; (* annonymous hit *)


Dealing with a Covered Board:
Once again there are a number of ways to accomplish this and this is just one of them....
Create a 2-D array of booleans that compliments the board (which is integer). If a square is 'true' that means that square should be covered. If it's false then the square is uncovered. Done this way, an appropriate name for this array might be "covered".
Every time a shot is fired, the corresponding square in this 'covered' array is set to false. Before we go to print the board, we simply need to check this array:
if covered[i][j] then
   write ('  ')
else begin
    case (board[i][j]) of
          (* see later for an explanation of this *)
          HIT   : write('X ');
          MISS  : write('* ');
          CLEAR : write('  ');
          1..5  : write(shipName[board[i][j]],' ');
     end;
end;



Updated: August 31, 2005 03:22 PM