A downloadable game

FISHIE - KEEP THE SEA PLASTIC FREE!

BASIC 10 Liner action game for the Sinclair ZX Spectrum

by Marco Varesio (Marco's Retrobits): https://retrobits.itch.io

English language blog: https://retrobits.altervista.org

Italian language blog: https://somebitsofme.altervista.org

YouTube channel: https://www.youtube.com/channel/UCWTxG8paNsOUEU5mPcNklXA

Download/play online: https://retrobits.itch.io/fishie

Gameplay video:

"Fishie - Keep the sea plastic free!" is a small game with a big ecological message.

Help Fishie keep the sea clean of plastic waste, by catching the oxygen bubbles that emerge from the water before they pop.

Whenever you catch a bubble, the garbage on the same line will disappear and Fishie will score a point.

Contact with plastic waste is harmful and will make you lose one life.

"Fishie - Keep the sea plastic free!" has been coded in BASIC language for the Sinclair ZX Spectrum homecomputer

and is my (second) entry to the 11th edition (2022) of the BASIC 10 Liner Contest (http://basic10liner.com/), PUR-80 category.


GAME DESCRIPTION

When the program starts, the title screen is shown.

You can start the game by pressing any key.

The little yellow fish on the left side of the screen is Fishie, our hero.

You can make Fishie swim up, down, left or right by pressing the e, d, o or p key respectively.

The plastic waste emerges from the sea, on the right side of the screen, and is pushed leftwards by the waves.

Contact with garbage will make it disappear, but Fishie will lose a life, so lingering on the left side of the screen is particularly dangerous.

Luckily, oxygen bubbles emerge from the water, too. 

Whenever Fishie catches a bubble, the garbage on the same line will disappear and you will score a point.

You have to reach the bubbles as fast as possible, because the movement of the waves will make them pop.

As the game progresses, oxygen bubbles will appear less and less frequently, making Fishie's task more difficult.

The lower part of the screen displays useful information, such as your current score, the highest score, the number of lives left and a recap of the game controls.


PROGRAM DESCRIPTION

Variables

h: High score

s: Current score 

l$(20,32): Array of 20 strings; each string is 32 characters long and represents a line in the game area

e$: "Empty" line, i.e. a string with 32 white spaces

i: Loop iterator

a: Used both as temporary variable and as flag indicating a collision between Fishie and an oxygen bubble

b: Flag indicating a collision between Fishie and a plastic waste

x: Fishie horizontal position (column number)

y: Fishie vertical position (line number)

f: Lives left

p: Oxygen bubble appearance probability

l: Plastic waste appearance line

c: Plastic waste appearance column

k: Code of the pressed key

s$: Character on the screen in Fishie's position (used for collision detection)


Program listing

1 BORDER 5:PAPER 1:INK 5:LET h=0:LET s=0:DIM l$(20,32):LET e$="":RESTORE 9:FOR i=65368TO65375:READ a:POKE i,a:LET e$=e$+"  ":NEXT i
2 CLS:LET x=5:LET y=11:LET f=3:LET p=.9:FOR i=1TO20:LET l$(i)=e$:NEXT i:PRINT AT 10,13;"FISHIE":GO SUB 10:PAUSE 0:LET s=0:CLS:GO SUB 10
3 LET l=1+INT(RND*20):LET c=23+INT(RND*10):LET c$=CHR$(35-(l=y)*(c=x)*3)
4 LET l$(l)=l$(l,2 TO c)+c$+l$(l,c+1 TO):LET k=CODE(INKEY$):IF k=0 THEN GO TO 6
5 PRINT AT y,x;" ":LET y=y-(k=101)*(y>1)+(k=100)*(y<20):LET x=x+(k=112)*(x<31)-(k=111)*(x>0)
6 PRINT INK 3;AT l,0;l$(l):IF RND<p THEN PRINT AT 1+INT(RND*20),1+INT(RND*30);"o"
7 LET s$=SCREEN$(y,x):LET a=(s$="o"):LET b=(s$="#"):LET s=s+a:LET f=f-b:IF a THEN LET p=p-.01+.01*(p<.15)
8 IF a+b THEN LET l$(y)=e$:PRINT AT y,0;l$(y):GO SUB 10:BORDER (a*4+b*2)*(f>0):BEEP .02+.4*b,15-30*b:BORDER 5
9 PRINT AT y,x;INK 6;CHR$(32+112*(f<>0)):GO TO 3-(f=0):DATA 32,28,190,251,255,190,28,32
10 LET h=s*(s>=h)+h*(s<h):PRINT #1;AT 0,0;"Score:";s,"Hi:";h,"{A}:";f,"Keys:e,d,o,p.":RETURN

Please note that this program listing is formatted in order to be used with the bas2tap(https://worldofspectrum.net/utilities/) utility, 

which converts a BASIC listing in an ASCII text file to a .TAP emulator tape image.

In particular, "{A}" represents the first user defined graphic charcter, with code 144 (0x90), which has been defined to the fish symbol.


Source code explained


Program initialization

Line 1 is executed only once when the program is run and performs screen, variables and user defined graphic characters initializations.

The FOR loop initializes the first UDG graphic character to the image of a fish, using DATA at line 9.

1 BORDER 5:PAPER 1:INK 5:LET h=0:LET s=0:DIM l$(20,32):LET e$="":RESTORE 9:FOR i=65368TO65375:READ a:POKE i,a:LET e$=e$+"  ":NEXT i


Game initialization

Line 2 is executed every time a new game starts. It clears the screen, performs some other initializations, displays the title screen and waits for a key press.

In particular, the 20 lines in l$ are initialized to white spaces using e$.

2 CLS:LET x=5:LET y=11:LET f=3:LET p=.9:FOR i=1TO20:LET l$(i)=e$:NEXT i:PRINT AT 10,13;"FISHIE":GO SUB 10:PAUSE 0:LET s=0:CLS:GO SUB 10

Hacking tip: you can make the game harder by setting p (the probability with which oxygen bubbles are generated) to a lower value. 

You can also change the number of available lives, by initializing f to a different value.


Game loop

Line 3 randomly chooses the position (line l, column c) where the plastic waste could appear. 

c$ stores the character that will be displayed in the chosen position: if the chosen position overlaps with Fishie's position, c$ will be set to a white space (code 32), otherwise it will be set to the character used for the waste ("#", code 35). 

I would have preferred to use a graphic character for the waste, but this causes problems with the SCREEN$ function, used in line 7 for collision detection, so I turned to the "#" character.

3 LET l=1+INT(RND*20):LET c=23+INT(RND*10):LET c$=CHR$(35-(l=y)*(c=x)*3)

Line 4 performs some string manipulation in order to simulate a wave at line l: the line is partially shifted to the left and the c$ character is inserted at column c.

Then user input is tested and if no key is pressed, the program jumps to line 6.

4 LET l$(l)=l$(l,2 TO c)+c$+l$(l,c+1 TO):LET k=CODE(INKEY$):IF k=0 THEN GO TO 6

Line 5 is executed only if a key is pressed. Fishie is deleted from the screen by printing a white space and our hero's new position (x, y) is calculated based on the code of the pressed key.

5 PRINT AT y,x;" ":LET y=y-(k=101)*(y>1)+(k=100)*(y<20):LET x=x+(k=112)*(x<31)-(k=111)*(x>0)

Hacking tip: the controls (up: key "e", character code: 101; down: key "d", character code: 100; right: key "o", character code 112; left: key "p", character code: 111) have chosen so that they are in the same position on the most common keyboard layouts (QWERTY, AZERTY, QWERTZ).

You can configure different controls by changing the [character codes](https://worldofspectrum.org/ZXBasicManual/zxmanappa.html) in line 5.

Line 6, prints line l, modified in line 4. Then, with a probability of p, an oxygen bubble (the character "o") is printed at a random position.

6 PRINT INK 3;AT l,0;l$(l):IF RND<p THEN PRINT AT 1+INT(RND*20),1+INT(RND*30);"o"

Lines 7 and 8 handle collision detection. First, s$ is set to the character currently at Fishie's position on the screen. 

If Fishie hits an oxygen bubble "o", a is set to 1, b is set to 0, the score s is incremented and the oxygen probability p is decremented (within a limit).

Otherwise, if Fishie hits a plastic waste "#", a is set to 0, b is set to 1 and the lives count f is decremented.

7 LET s$=SCREEN$(y,x):LET a=(s$="o"):LET b=(s$="#"):LET s=s+a:LET f=f-b:IF a THEN LET p=p-.01+.01*(p<.15)

If a collision was detected, the corresponding line on the screen is cleared (set to white spaces) and the game information displayed on screen is updated. The BORDER colour and a BEEP will notify whether the player scored a point or lost a life.

8 IF a+b THEN LET l$(y)=e$:PRINT AT y,0;l$(y):GO SUB 10:BORDER (a*4+b*2)*(f>0):BEEP .02+.4*b,15-30*b:BORDER 5

In line 9, if the game is not over (lives count f is still greater than 0), Fishie is printed at the previously calculated position (x, y) and then the programs jumps to the beginning of the game loop at line 3 for the next iteration; otherwise the program jumps to line 2.

Line 9 also contains DATA for the fish image.

9 PRINT AT y,x;INK 6;CHR$(32+112*(f<>0)):GO TO 3-(f=0):DATA 32,28,190,251,255,190,28,32


Print game status subroutine

The routine at line 10 checks if current score s is the new high score and updates h accordingly. 

Then, it prints score, high score, lives count and a recap of the game controls at the bottom of the screen.

10 LET h=s*(s>=h)+h*(s<h):PRINT #1;AT 0,0;"Score:";s,"Hi:";h,"{A}:";f,"Keys:e,d,o,p.":RETURN


Program lines length proof

By replacing each BASIC token with a single character and removing redundant blank spaces, each line in the resulting source code does not exceed the 80 characters limit:

1b5:C1:X5:lh=0:ls=0:dl$(20,32):le$="":S9:fi=65368F65375:Aa:oi,a:le$=e$+"  ":ni

2v:lx=5:ly=11:lf=3:lp=.9:fi=1F20:ll$(i)=e$:ni:pI10,13;"FISHIE":h10:m0:ls=0:v:h10

3ll=1+R(T*20):lc=23+R(T*10):lc$=U(35-(l=y)*(c=x)*3)

4ll$(l)=l$(l,2 Fc)+c$+l$(l,c+1 F):lk=I(XEY$):uk=0 Gg6

5pIy,x;" ":ly=y-(k=101)*(y>1)+(k=100)*(y<20):lx=x+(k=112)*(x<31)-(k=111)*(x>0)

6pX3;Il,0;l$(l):uT<p GpI1+R(T*20),1+R(T*30);"o"

7ls$=K(y,x):la=(s$="o"):lb=(s$="#"):ls=s+a:lf=f-b:ua Glp=p-.01+.01*(p<.15)

8ua+b Gll$(y)=e$:pIy,0;l$(y):h10:b(a*4+b*2)*(f>0):Z.02+.4*b,15-30*b:b5

9pIy,x;X6;U(32+112*(f<>0)):g3-(f=0):D32,28,190,251,255,190,28,32

10lh=s*(s>=h)+h*(s<h):p#1;I0,0;"Score:";s,"Hi:";h,"{A}:";f,"Keys:e,d,o,p.":y

Therefore, "Fishie - Keep the sea plastic free!" is a suitable entry for the PUR-80 category of the BASIC 10 Liner Contest (https://basic10liner.com).



Download

Download
fishie.bas 1,001 bytes
Download
fishie.pdf 191 kB
Download
fishie.tap 1.1 kB
Download
fishie.txt 11 kB

Install instructions

LOADING INSTRUCTIONS

"Fishie - Keep the sea plastic free!" is provided in TAP tape image format, which can be easily loaded in most ZX Spectrum emulators and on the real machines, either equipped with devices such as the DivMMC or by playing it through the MIC port using tools like PlayTZX or WinTZX.

The following instructions apply to the Fuse (http://fuse-emulator.sourceforge.net/) open source emulator, which is available for Unix, Linux, Windows, macOS and many other platforms.

For other emulators or devices, please refer to their specific documentation for loading TAP files.

Start the Fuse emulator and select the Spectrum 48K model in "Machine" -> "Select..."

Make sure that automatic loading of tape image files is enabled, by checking the corresponding options in "Options" -> "Media..."

Open the "fishie.tap" file, either by selecting it in "File"->"Open..." or by dragging and dropping it on the emulator window.

To see the program listing, BREAK the program by pressing SHIFT + SPACE BAR  (the CAPS SHIFT ZX Spectrum key is usually mapped to SHIFT).

The "L BREAK into program" message will be displayed. Then press the K key, followed by the ENTER key. Now you will see the first page of the program listing; to scroll to the next page, press any key except SPACE BAR.

If your emulator of choice does not support automatic tape loading, after mounting the tape image you must manually issue the tape loading command, by pressing the J key, followed by CTRL + P twice (the SYMBOL SHIFT ZX Spectrum key is usually mapped to CTRL) and then by ENTER.

If you are emulating a 128K ZX Spectrum model, to start loading simply select "Tape Loader" using the cursor keys in the main system menu and press ENTER.

Leave a comment

Log in with itch.io to leave a comment.