Battleships

This file is part of and SINCUS Exchange Tape 103 - Potpourri. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

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:

  1. Lines 49–88: Subroutines — display grid (50–54), validate/place a ship (70–79), and undo a partial placement (80–88).
  2. Lines 900–940: Initialization — dimension arrays, read direction vectors, define UDG, fill grids, read ship names.
  3. Lines 1000–1020: Computer ship placement using random positions and direction vectors.
  4. Lines 1050–1090: Human ship placement with coordinate and direction input.
  5. Lines 1400–1415: Game setup — difficulty selection.
  6. Lines 1500–1535: Human turn loop — input, hit/miss detection, ship sinking logic.
  7. Lines 2000–2210: Computer turn logic — AI targeting, hit/miss detection.
  8. 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

ArrayDimensionsPurpose
C$(10,10)10×10 stringComputer’s ship grid — each cell holds CHR$ of ship index or space
Y$(10,10)10×10 stringHuman’s ship grid — same encoding as C$
D$(10,10)10×10 stringDisplay grid for computer’s ocean (shown to human)
E$(10,10)10×10 stringDisplay grid for human’s ocean (shown to human)
Z$(10,10)10×10 stringTracks squares the computer has already guessed
D(8,2)8×2 numericDirection vectors: 8 compass directions as (ΔRow, ΔCol) pairs
A$(5,10)5×10 stringShip names (PT-Boat through Carrier)
L$(5,6)5×6 stringHit 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$ I used as a compact ship-index marker in grid arrays, keeping cell values in the range CHR$(1)–CHR$(5).
  • CODE C$(R,C)>5 at 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 FOR loops with IF RND>=.5 THEN FOR ... STEP -1 to 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 share V$ 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 variable I is reused here, clobbering the outer ship-loop counter — this is benign at this point since the code branches to line 1500 afterward.

Content

Appears On

A little of everything — play Battleship with adjustable AI, solve Klondike Solitaire with custom card graphics, guide Santa's reindeer into their pen, or track your stock portfolio on tape. The potpourri tape lives up to its name.

Related Products

Related Articles

Related Content

Image Gallery

Battleships

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.

People

No people associated with this content.

Scroll to Top