Thanks to Julio Marchi for this space in MSX All
Also known as Sea Battle, it is a strategy game, where one player must hit all ships from the opponent before the opponent hit your ships.
There are some variations of this game. From the board size to the number of ships and their arrangement.
In this version, the board grid size is 10x10, where the horizontal coordinates are denoted by letters, while the vertical coordinates are represented by numbers. An example of a coordinate is "B7". Thus, this game has 10 ships, configured as follows:
Type | Format | Total -----------------+---------+----------- Aircraft Carrier | AAAA | 1 Cruiser | CCC | 2 Battleship | BB | 3 Submarine | S | 4
Initially, each player places his/her ships on the board without exceeding the board dimensions.
Valid Invalid ..... ..... .BB.. ....B
The ship overlapping is not allowed. Also, the ships must be separated by at least one board cell from each other.
Valid Invalid Invalid ..... ..... ..
The goal is to find the opponent's ships before hi/she finds yours.
There are two boards on the game: one for writing the player's own ship positioning, and the other to write the opponent's game, as soon as the player finds out the ships.
Each player informs a coordinate to the opponent and he/she answer "water" if a cell contains no ship or "hit a ...", if a part of a ship is found.
The game will take place on screen 1 for a better legibility.
5 ' Rem MarMSX 2018 10 COLOR 15,0,0:SCREEN 1:WIDTH 32:KEY OFF ... 200 ' 201 ' Draw game 202 ' 210 DIM PX(2):PX(1)=0:PX(2)=18 220 FOR I=1 TO 2 230 LOCATE PX(I)+2,0:?"ABCDEFGHIJ" 240 NEXT I 250 FOR Y=1 TO 10:FOR I=1 TO 2 260 LOCATE PX(I),Y:PRINT RIGHT$(" "+STR$(Y),2);".........." 270 NEXT I,Y 275 LOCATE 4,12:PRINT"You":LOCATE 24,12:PRINT"Me" 280 LOCATE 0,17 290 FOR I=1 TO 5 300 LOCATE 0:READ A$:PRINT A$; 310 LOCATE 18:READ A$:PRINT A$; 320 LOCATE 26:READ A$:PRINT A$ 325 IF I=1 THEN PRINT 330 NEXT I 340 RETURN ... 2000 ' 2001 ' Game data 2002 ' ... 2020 DATA Ship, Format, Total, Aircraft Carrier, AAAA, 1, Cruiser, CCC, 2, Battleship, BB, 3, Submarine, S, 4
Ship positioning routine
There are two ways to position the ships on the board: the first one is creating a routine where the player himself position the ships; the second one is creating a routine that places all ships automatically.
In this game version, we will place the ships automatically for both player and computer. So, we can take advantage of the same rotine to place all ships.
We will create a three dimensional matrix to store both boards together. In that case, "TJ" has the dimensions of 10x10x2.
20 BT=10:DIM TJ(BT,BT,2):DIM N$(4)Obs: the size of the board is indicated by the variable "BT", which gives more flexibility to the positioning routine.
For example, if we would like to access the player's position 5,4, we should write TJ(5,4,1). For the same computer's coordinate, TJ(5,4,2). So, the last coordinate controls the player's or the computer's board.
The array "N$" will store the symbol of four ship types:
2010 DATA A,4,1,C,3,2,B,2,3,S,1,4Obs: as these data will be read before than the drawing routine data, they must appear first on the program listing.
After creating all necessary informations, we are ready to place the ships on the boards.
Basic strategy for ships positioning:
var TJ : array[1..10, 1..10, 1..2] of integer; N : string; C,T,X,Y : integer; i, it, p : integer; for i ← 1 to 4 do read(N, C, T); for it ← 1 to T do for p ← 1 to 2 do X ← random(10-C+1); Y ← random(10); insert_ship(i, X, Y); end_for end_for end_for
How to ensure that the random value do not place the ship outside the board limits?
If the object size is 1, the problem is solved by creating a random number between 1 and 10. Nevertheless, some objects have their length greater than 1 along the X axis.
To solve this issue, we will create a pivot on the least X coordinate of the object and delimit the X maximum value according to the object length "C".
Ship pivot in green
For a 10x10 board, the random values are calculated as follows:
Y: min=1, max=10 X: min=1, max=10-C+1For example, the cruiser with length "C" equal to three, the variable X ranges from 1 to 8, and the variable Y ranges from 1 to 10, which corresponds to the red rectangle on the last figure.
After calculating X and Y, we must write the ship code in the "TJ" matrix. Thus, we must write it "C" times on consecutive cells. So:
for ic ← 0 to C-1 do TJ[X+ic,Y,P] ← i; end_forObs: once the "TJ" matrix uses integer values, we will store the ship code and not the ship symbol.
The code in Basic is:
400 ' 401 ' Place ships 402 ' 410 FOR I=1 TO 4 413 LOCATE 0,2:PRINT"Placing ships:";INT(100*(I-1)/4);"%" 415 READ N$(I),C,T 420 FOR IT=1 TO T 425 FOR P=1 TO 2 430 X=INT((BT-C+1)*RND(-TIME))+1 435 Y=INT(BT*RND(-TIME))+1 440 IF I>1 THEN GOSUB 500 ELSE 460 445 IF FV=1 THEN 460 450 X=X+1:IF X+C-1>BT THEN X=1:Y=Y+1 453 IF Y>BT THEN Y=1 455 GOTO 440 460 FOR IC=0 TO C-1:TJ(X+IC,Y,P)=I:NEXT 465 NEXT P,IT,I 470 RETURN
The lines 440-455 handle the objects overlapping. Nevertheless, it is not necessary to check overlapping for the first object (Carrier), once the board is empty.
The routine in line 500 is responsible for checking the overlapping. It returns "FV=0", if the current object overlaps another object.
If overlapping is true, we can adopt one of these two strategies:
Overlaping free routine
Once calculated the ship pivot coordinates X,Y, we must check if the ship overlaps another object.
Do not forget that objects must be far aways at least 1 cell from each other. In order to ensure that, we will create a bounding box enlarged by 1 cell and check if any object lies inside that area.
The example on the figure above shows a verification area (green) for a cruiser.
500 ' 501 ' Check for overlapping 502 ' 510 FV=1 520 FOR VY=Y-1 TO Y+C:FOR VX=X-1 TO X+C 530 IF VX<1 OR VX>BT OR VY<1 OR VY>BT THEN 550 540 IF TJ(VX,VY,P)<>0 THEN FV=0:RETURN 550 NEXT VX,VY 560 RETURN
Display player's game
This routine displays the player's game.
1000 ' 1001 ' Display player's game 1002 ' 1010 FOR Y=1 TO 10:FOR X=1 TO 10 1020 LOCATE X+1,Y:IF TJ(X,Y,1)<>0 THEN PRINT N$(TJ(X,Y,1)) ELSE PRINT " " 1025 'LOCATE X+19,Y:IF TJ(X,Y,2)<>0 THEN PRINT N$(TJ(X,Y,2)) ELSE PRINT "X" 1030 NEXT X,Y 1040 RETURN
After positioning the ships and drawing the screen, we create the main loop which controls the actions from both player and computer.
30 PRINT "Battleship - MarMSX 2018" 40 GOSUB 400:CLS:GOSUB 200:GOSUB 1000 50 CX=20:CY=1:PJ=0:PC=0 100 ' 101 ' Main loop 102 ' 110 LOCATE CX,CY:A$=INPUT$(1):A=ASC(A$) 120 IF A=29 THEN CX=CX-1:IF CX<20 THEN CX=20 130 IF A=28 THEN CX=CX+1:IF CX>29 THEN CX=29 140 IF A=30 THEN CY=CY-1:IF CY<1 THEN CY=1 150 IF A=31 THEN CY=CY+1:IF CY>10 THEN CY=10 160 IF A=32 THEN GOSUB 600:IF V=1 THEN GOSUB 650 170 GOTO 110
This code controls the player's moves on the opponent's map, this one used to save the opponent's ships locations.
The space bar marks the current location as visited (hits) and reveals to the player what is under that cell.
The routine called (line 600) returns a flag "V" indicating if the requested cell is valid. If true, it passes the control to the computer's routine (line 650).
This routine first checks if the required cell was not opened before by reading the screen coordinate and checking if the current chracter is a dot ".".
If the cell is unvisited, it marks as visited with a blank character " " if the "TJ(X,Y)" value 0, or with the ship corresponding symbol(N$) otherwise.
By hitting a ship, the "PJ" variable is incremented informing that an object was found. When "PJ" reaches 20, the player wins.
600 ' 601 ' Player move 602 ' 610 V=1:IF VPEEK(6144+CX+CY*32)<>46 THEN V=0:RETURN 620 LOCATE CX,CY:IF TJ(CX-19,CY,2)<>0 THEN PRINT N$(TJ(CX-19,CY,2)):PJ=PJ+1 ELSE PRINT " " 630 IF PJ=20 THEN LOCATE 0,14:PRINT"** You win the game !! **":END 640 RETURN
First we calculate the coordinate randomly. Then, we check if the X,Y position is already visited. If true, we shift X and Y on the board until an unvisited place is found.
The visited cells are marked with an "X".
If an object is hit, we increment PC, the computer score.
650 ' 651 ' Computer move 652 ' 660 X=INT(BT*RND(-TIME))+1 665 Y=INT(BT*RND(-TIME))+1 670 IF VPEEK(6144+(X+1)+Y*32)<>88 THEN 685 675 X=X+1:IF X>10 THEN Y=Y+1:X=1:IF Y>10 THEN Y=1 680 GOTO 670 685 LOCATE X+1,Y:PRINT"X":IF TJ(X,Y,1)<>0 THEN PC=PC+1 690 IF PC=20 THEN LOCATE 0,14:PRINT"** I win the game !! **":END 695 RETURN
Important: the access to the opponent's board is only allowed at the player's or computer's play. Any other access out of a play is like "cheating" the opponent. Because of that, lines 670 (computer routine) and 610 (player routine) check only the screen information.
The computer move routine (lines 650-695) is quite simple and always performs a "shot in the dark", once the computer's behaviour is not changed after identifying a ship. The routine always calculates X and Y randomly.
We may improve this by making the computer search for the adjacent cells everytime it hits an object with the size greater than 1.
The first step is to create another array "N" to store each ship type size, once now this information it is useful.
20 BT=10:DIM TJ(BT,BT,2):DIM N$(4)
By this time, there will be two options for computer to calculate X and Y: the first is randomly; the second, we will perform an object searching along the X axis.
We will create a flag to indicate the which strategy the computer will adopt: random or search.
The resulting code is:
655 IF OE=1 THEN 690 656 ' Random routine 660 X=INT(BT*RND(-TIME))+1 665 Y=INT(BT*RND(-TIME))+1 670 IF VPEEK(6144+(X+1)+Y*32)<>88 THEN 685 675 X=X+1:IF X>10 THEN Y=Y+1:X=1:IF Y>10 THEN Y=1 680 GOTO 670 685 IF TJ(X,Y,1)<>0 THEN OE=1:CR=N(TJ(X,Y,1)):C=CR:DX=1 690 LOCATE X+1,Y:PRINT"X":IF TJ(X,Y,1)<>0 THEN PC=PC+1 695 IF PC=20 THEN LOCATE 0,14:PRINT"** I win the game !! **":END 700 IF OE=0 THEN RETURN 705 ' Search routine 710 IF TJ(X,Y,1)=0 THEN DX=-DX:GOTO 760 720 CR=CR-1:IF CR=0 THEN OE=0:RETURN 730 X=X+DX 740 IF X<1 OR X>10 THEN DX=-DX:GOTO 760 750 IF VPEEK(6144+(X+1)+Y*32)<>88 THEN 770 ELSE DX=-DX 760 X=X+DX:IF VPEEK(6144+(X+1)+Y*32)=88 THEN 760 770 RETURN
The line 685 checks if the random X,Y is a ship. If true, sets the "OE" flag, reads the ship length in "CR" (remaining length) and sets the initial shift "DX" as positive.
In the next rounds, the search routine (line 705) will take place until CR=0.
If the target is "water", change the "DX" signal. Move X until an unvisited cell is found (line 760).
We calculate the next X value now. If the next position is a visited place, change the "DX" signal and move X until an unvisited cell is found (line 760).
Obs: the green rectangle is the current X,Y coordinate, while the light gray rectangle is the future X,Y.
The line 690 represents the computer's play for both random and seach strategies.
We also can mark the object surroundings in order to minimize the efforts from the computer.
720 CR=CR-1:IF CR=0 THEN OE=0:
battle.zip - both Basic and ASCII Basic.
Marcelo Teixeira Silveira
|© MarMSX 1999-2018|