Battleship - HELP!
Katrin Becker, 2001
- (*****************************************************************
* 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;