This program implements a two-player Battleship game where a human competes against a computer opponent on two 10×10 grids. Ships range from PT-Boat to Carrier and are placed using alphanumeric coordinates (e.g., “A5” for row, column). The computer opponent features an adjustable intelligence level from 0 to 10, controlled by variable CINT, which influences whether the CPU uses a memory cell CREM to target squares adjacent to a previous hit rather than guessing randomly. A custom UDG character (“\a”) is defined via POKEs to USR to render a bordered ship icon on the grid display. Scoring is time-based, rewarding faster hits with higher point values derived from a turn counter T.
Program Analysis
Program Structure
The program is organized into several functional regions separated by line number ranges:
- Lines 49–88: Subroutines — display grid (
50–54), validate/place a ship (70–79), and undo a partial placement (80–88). - Lines 900–940: Initialization — dimension arrays, read direction vectors, define UDG, fill grids, read ship names.
- Lines 1000–1020: Computer ship placement using random positions and direction vectors.
- Lines 1050–1090: Human ship placement with coordinate and direction input.
- Lines 1400–1415: Game setup — difficulty selection.
- Lines 1500–1535: Human turn loop — input, hit/miss detection, ship sinking logic.
- Lines 2000–2210: Computer turn logic — AI targeting, hit/miss detection.
- Lines 2400–2500: End-game display, score output, and
STOP.
Entry is via line 49 which immediately jumps to line 900 for initialization, then falls through to placement and gameplay.
Array Usage
| Array | Dimensions | Purpose |
|---|---|---|
C$(10,10) | 10×10 string | Computer’s ship grid — each cell holds CHR$ of ship index or space |
Y$(10,10) | 10×10 string | Human’s ship grid — same encoding as C$ |
D$(10,10) | 10×10 string | Display grid for computer’s ocean (shown to human) |
E$(10,10) | 10×10 string | Display grid for human’s ocean (shown to human) |
Z$(10,10) | 10×10 string | Tracks squares the computer has already guessed |
D(8,2) | 8×2 numeric | Direction vectors: 8 compass directions as (ΔRow, ΔCol) pairs |
A$(5,10) | 5×10 string | Ship names (PT-Boat through Carrier) |
L$(5,6) | 5×6 string | Hit tracking per ship: L$(i,1) = hit count, subsequent bytes = cell indices |
Direction Vector System
Eight compass directions are encoded as (ΔRow, ΔCol) pairs and stored in numeric array D(8,2) via READ/DATA at lines 905–910. The DATA values are stored as quoted strings and converted with VAL V$ — this is a common technique to store signed integers in DATA statements since bare negative numbers in DATA can be misread. The ship placement subroutine at line 70 uses DR=D(DS,1) and DC=D(DS,2) to step along the chosen direction for I cells.
Ship Placement Validation (Lines 70–88)
The subroutine at line 70 validates and places a ship of length I starting at (R,C) in direction DS. It checks bounds at line 72, then iterates cell by cell. A shared string variable V$ is reused as a mode selector: the substring V$(2) is checked for "C" (computer grid) or "Y" (human grid) to decide which array to update. This dual-mode dispatch avoids duplicating the placement loop. The rollback subroutine at lines 80–88 undoes any partial placement by stepping backwards along the same direction vector.
UDG Definition
Lines 915–920 define UDG \a (character 144) as a hollow rectangle — the DATA bytes 255,129,129,129,129,129,129,255 produce a bordered 8×8 block. This is used to fill empty ocean squares in both display grids, giving a distinctive visual appearance for unguessed cells. The POKEs target USR "\a" through USR "\a"+7, the standard eight-byte UDG definition block.
Computer AI (Lines 2000–2210)
The computer’s targeting logic is the most complex section. After a hit, the cell index CREM is stored. On the next turn, if RND < CINT and CREM <> 0, the CPU uses CREM as a seed to search adjacent cells using the 8-direction vectors, looking for an unguessed, unoccupied-by-miss square. The difficulty parameter CINT (0.0–1.0, set at line 1415) gates both the probability of using the remembered hit and the probability of skipping over cells during random scanning (line 2016). If no adjacent smart target is found, the CPU falls back to a random cell. This creates a graduated difficulty from pure random to systematic adjacent searching.
Scoring System
A turn counter T is incremented each time the computer takes a turn (line 2250). Both players’ scores accumulate as 100 - T per hit (lines 1520, 2210), so early hits score higher. The displayed running score at line 1501 divides by T to show average score per turn. The end-game display at line 2405 adds a bonus of T*(RHY=15) or T*(RHC=15) — a Boolean-as-integer idiom that adds T to the winner’s final average when all 15 ship cells (5 ships × 3 cells average) are sunk.
Coordinate Encoding
Human grid rows are encoded as letters A–J: the input character is decoded with CODE V$(1)-64 for placement (line 1065) but CODE V$(1)-CODE "J" during the guessing phase (line 1510), which maps K–T to 1–10. This means the human places ships using A–J row labels but guesses on the computer’s grid using K–T labels — a clean visual separation of the two 10×10 grids on a shared display. Columns are always numeric 1–10.
Notable Idioms and Techniques
VAL V$(2 TO )— open-ended slice to parse multi-digit column numbers from input strings.CHR$ Iused as a compact ship-index marker in grid arrays, keeping cell values in the range CHR$(1)–CHR$(5).CODE C$(R,C)>5at line 1515 tests for a miss marker rather than a ship, since valid ship indices are 1–5.L$(L,M+1)=CHR$ ((R-1)*10+C)packs a 2D coordinate into a single byte using a 1-based linear index, requiring only a 100-value range which fits in a single character code.- Line 2005–2010 use nested conditional
FORloops withIF RND>=.5 THEN FOR ... STEP -1to randomize scan direction, a compact way to vary traversal order without extra variables.
Potential Bugs and Anomalies
- Ship length 1 (PT-Boat, index 1) is handled as a special case at lines 1010 and 1065 with direct placement bypassing the direction subroutine — ships of length 1 need no direction, but this also means
L$(1)hit-count tracking is initialized without a direction vector entry, which could cause issues if the sinking logic at line 1530 tries to read hit positions for a 1-cell ship. - The variable
V$is heavily overloaded: it serves as a mode string ("CODE C$(R,C)"), a player input buffer, a DATA-reading temporary, and a direction input. This creates implicit coupling between subroutines that shareV$state. - Line 2200’s loop self-jumps (
GO TO 2200) to retry if the selected cell is already guessed, with no guard against infinite loops when the grid is nearly full. - At line 1530, the sinking display sets
D$(R,C)=A$(L,1)(the first letter of the ship name) for all hit cells, correctly marking a sunk ship. However, the loop variableIis reused here, clobbering the outer ship-loop counter — this is benign at this point since the code branches to line 1500 afterward.
Content
Source Code
49 GO TO 900
50 LET B=100/8: LET B=INT (T/B): BORDER B: CLS : PRINT " YOU CPU": PRINT " 1 1": PRINT " 1234567890 1234567890"
52 FOR K=1 TO 10: PRINT CHR$ (K+64);E$(K,1 TO 10);" ";CHR$ (K+10+64); INK 1;D$(K,1 TO 10)
54 NEXT K: RETURN
70 LET V=1: LET DR=D(DS,1): LET DC=D(DS,2)
72 IF R+DR*(I-1)>10 OR R+DR*(I-1)<1 OR C+DC*(I-1)>10 OR C+DC*(I-1)<1 THEN LET V=0: RETURN
74 FOR J=1 TO I: IF VAL V$<>32 THEN LET V=0: GO SUB 80: RETURN
76 IF V$(2)="C" THEN LET C$(R,C)=CHR$ I: LET L$(I,1)=CHR$ 0: LET L$(I,J+1)=CHR$ 0
78 IF V$(2)="Y" THEN LET Y$(R,C)=CHR$ I: LET E$(R,C)=A$(I,1)
79 LET R=R+DR: LET C=C+DC: NEXT J: RETURN
80 IF J=1 THEN RETURN
82 FOR K=J-1 TO 1 STEP -1: LET R=R-DR: LET C=C-DC
84 IF V$(2)="C" THEN LET C$(R,C)=" ": LET L$(I,1)=CHR$ 0: LET L$(I,K+1)=CHR$ 0
86 IF V$(2)="Y" THEN LET Y$(R,C)=" ": LET E$(R,C)="\a"
88 NEXT K: RETURN
900 DIM C$(10,10): DIM Y$(10,10): DIM D$(10,10): DIM E$(10,10): DIM Z$(10,10): DIM D(8,2)
905 DATA "0","1","0","-1","1","0","-1","0","+1","-1","-1","1","+1","+1","-1","-1"
910 FOR I=1 TO 8: READ V$: LET D(I,1)=VAL V$: READ V$: LET D(I,2)=VAL V$: NEXT I
915 DATA "255","129","129","129","129","129","129","255"
920 FOR I=0 TO 7: READ V$: POKE USR "\a"+I,VAL V$: NEXT I
925 FOR I=1 TO 10: FOR J=1 TO 10: LET D$(I,J)="\a": LET E$(I,J)="\a": NEXT J: NEXT I
930 DIM A$(5,10): FOR I=1 TO 5: READ A$(I): NEXT I
935 DATA "PT-BOAT","SUBMARINE","DESTROYER","BATTLESHIP","CARRIER"
940 DIM L$(5,6)
1000 LET T=1: LET V$="CODE C$(R,C)": FOR I=1 TO 5
1005 LET DS=INT (RND*8)+1
1010 LET S=INT (RND*100)+1: LET R=INT ((S-1)/10)+1: LET C=S-(R-1)*10: IF I=1 THEN LET C$(R,C)=CHR$ I: LET L$(I,1)=CHR$ 0: LET L$(I,2)=CHR$ 0: GO TO 1020
1015 GO SUB 70: IF NOT V THEN GO TO 1005
1020 NEXT I
1050 FOR I=1 TO 5
1055 GO SUB 50: PRINT AT 13,0;"ENTER POSITION OF ";A$(I): INPUT V$
1060 IF NOT LEN V$ THEN GO TO 1055
1062 IF LEN V$<2 OR LEN V$>3 THEN GO TO 1055
1065 LET R=CODE V$(1)-64: LET C=VAL V$(2 TO ): IF I=1 THEN LET Y$(R,C)=CHR$ I: LET E$(R,C)=A$(I,1): GO TO 1090
1066 PRINT AT R+2,C; FLASH 1;"\a";
1070 PRINT AT 14,0;"ENTER DIRECTION FOR ";TAB 0;A$(I): PRINT "846";TAB 0;"2*1";TAB 0;"537": INPUT V$
1075 IF NOT LEN V$ THEN GO TO 1070
1077 PRINT V$: IF LEN V$>1 OR V$>"8" OR V$<"1" THEN GO TO 1070
1080 LET DS=VAL V$
1085 LET V$="CODE Y$(R,C)": GO SUB 70: IF NOT V THEN BEEP 2,-5: GO TO 1055
1090 NEXT I
1400 LET M$="START THE GAME"
1405 LET RHC=0: LET RHY=0: LET SC=0: LET SY=0
1410 PRINT "HOW SMART IS MR. COMPUTER?";TAB 0;"0=IGNORANT, 10=GENIUS": INPUT CINT: IF CINT<0 OR CINT>10 THEN CLS : GO TO 1410
1415 LET CINT=CINT/10: LET CREM=0
1500 IF RHC=15 OR RHY=15 THEN GO TO 2400
1501 GO SUB 50: PRINT "ENTER YOUR GUESS": PRINT AT 20,0;M$: PRINT TAB 4;SY/T;TAB 17;SC/T: LET M$=" ": INPUT V$
1505 IF NOT LEN V$ THEN GO TO 1500
1507 IF V$(1)<"K" OR V$(1)>"T" OR LEN V$<2 THEN GO TO 1500
1509 IF VAL V$(2 TO )<1 OR VAL V$(2 TO )>10 THEN GO TO 1500
1510 LET R=CODE V$(1)-CODE "J": LET C=VAL V$(2 TO )
1515 IF CODE C$(R,C)>5 THEN LET D$(R,C)="\::": GO TO 2000
1516 IF D$(R,C)>="A" AND D$(R,C)<="Z" THEN GO TO 2000
1520 LET RHY=RHY+1: BEEP .5,43: LET D$(R,C)="X": LET M$="YOU GOT 'EM; TRY AGAIN": LET SY=SY+100-T
1525 LET L=CODE C$(R,C): LET M=CODE (L$(L,1))+1: LET L$(L,1)=CHR$ M: LET L$(L,M+1)=CHR$ ((R-1)*10+C)
1530 IF M=L THEN FOR I=1 TO M: LET V=CODE (L$(L,I+1)): LET R=INT ((V-1)/10)+1: LET C=V-(R-1)*10: LET D$(R,C)=A$(L,1): NEXT I
1535 GO TO 1500
2000 LET K=INT (RND*100)+1: IF RND<CINT AND CREM<>0 THEN LET K=CREM: LET R=INT ((K-1)/10)+1: LET C=K-(R-1)*10: LET R=R-1*(R>1): LET C=C-1*(C>1): FOR I=R TO 10: FOR J=C TO 10: GO TO 2015
2005 FOR I=INT (RND*10)+1 TO 10: IF RND>=.5 THEN FOR I=10 TO INT (RND*10)+1 STEP -1
2010 FOR J=INT (RND*10)+1 TO 10: IF RND>=.5 THEN FOR J=10 TO INT (RND*10)+1 STEP -1
2015 IF Z$(I,J)=" " THEN GO TO 2100
2016 IF RND>CINT THEN GO TO 2100
2020 IF CODE Y$(I,J)>5 THEN GO TO 2100
2025 FOR L=1 TO 8
2026 LET RR=I+D(L,1): LET CC=J+D(L,2): IF RR>10 OR RR<1 OR CC<1 OR CC>10 THEN GO TO 2050
2030 LET K=(RR-1)*10+CC
2035 IF Z$(RR,CC)=" " THEN GO TO 2200
2050 NEXT L
2055 LET K=INT (RND*100)+1
2100 NEXT J
2150 NEXT I
2200 LET R=INT ((K-1)/10)+1: LET C=K-(R-1)*10: IF Z$(R,C)<>" " THEN LET CREM=0: LET K=INT (RND*100)+1: GO TO 2200
2205 IF CODE Y$(R,C)>5 THEN LET E$(R,C)="\::": LET Z$(R,C)="\::": GO TO 2250
2210 LET Z$(R,C)="\::": LET CREM=K: LET RHC=RHC+1: LET E$(R,C)=CHR$ (CODE Y$(R,C)+47): LET SC=SC+100-T: BEEP .5,10: LET M$="HE GOTCHA!!!": GO TO 2000
2250 LET T=T+1: GO TO 1500
2400 FOR I=1 TO 10: FOR J=1 TO 10
2401 IF C$(I,J)<>" " AND D$(I,J)=" " THEN LET D$(I,J)=CHR$ (CODE (A$(CODE C$(I,J),1))+32)
2402 NEXT J: NEXT I
2405 GO SUB 50: PRINT AT 19,0;"YOU=";SY/T+T*(RHY=15);"(";SY;")";TAB 0;"CPU=";SC/T+T*(RHC=15);"(";SC;")": IF RHC=15 THEN PRINT "MR. COMPUTER SANK YOUR NAVY!": COPY : STOP
2500 PRINT "YOU SANK THE COMPUTER.": COPY : STOP
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
