The Typing Of The Spec by Marco's Retrobits
A downloadable game
The Typing of the Spec
BASIC 10 Liner videogame for the Sinclair ZX Spectrum
Play online link: https://retrobits.itch.io/typingspec
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
The Typing of the Spec is an edutainment videogame for the Sinclair ZX Spectrum home computer.
The goal is to quickly type the words as they are displayed on the screen, before the time runs out.
On successful typing of a word, the remaining time is added to current score.
The player loses a life whenever he types a wrong character or the time runs out.
The Typing of the Spec is my third entry to the 2021 edition of the BASIC 10Liner Contest, PUR-80 category.
When the program starts, it prompts you about the desired level of difficulty, by asking you to input a value between 1 (easiest) and 5 (hardest).
This value, together with the length of each word, affects the available time you have to type.
After difficulty selection, the game begins.
The top line displays your current score S, the high score H and the remaining lives L. You start with 5 lives.
Below the high score, you will see the countdown timer. The word to type is displayed below, in yellow.
As long as you type some characters, they will be displayed under the requested word.
The game is case insensitive, so you can type lower case letters; they will be displayed upper case.
A bright beep and the green border will indicate that you have correctly typed the requested word.
In this case, the remaining time will be multiplied by 10 and added to your score.
If your score is better than the highest score, it will be displayed in cyan.
Then, the game will continue with another random word.
Otherwise, if either the time runs out or you type a wrong character, the system beeps a sad tone and the border colour becomes red.
In this case you will lose a life and then the game will continue with another random word.
When you run out of lives, a new game will automatically start.
The game requires some precision when pressing the keys, since a short tap will not be recorded by the system. On the contrary, if you hold the key for too much time, multiple keystrokes will be recorded. With a bit of practice, you will find the correct timing.
For a full retro experience, it is recommended to play on a real rubber keyboard Speccy :)
The program randomly choses the word to type among the BASIC keywords, which actually are characters whose codes range from 165 to 255, so there is no need to store a words database.
In order to obtain a string that can be compared to the user input, the keyword character is first invisibly printed on the screen.
Then the characters composing the keyword are read back into a string, character by character, using the SCREEN$ function.
I'm sure there is a better way to convert the keyword to a string, maybe calling the tokenization routines in the ZX Spectrum ROM, but this method just works.
The 2 least significant bytes of the FRAMES counter system variable are used for the countdown.
L : Lives left
S : Score
H : High score
D : Difficulty level (1..5)
E : Difficulty coefficient, used to calculate the available time, based on the difficulty level D
M : Initial available time (in frames, i.e. 1/50 sec.), calculated using E and A
T : Time left (in frames, i.e. 1/50 sec.), initialized to M and decremented at each game loop
T$: String representation of time left
C : Random character code of the (key)word to type
C$: Temporary character variable, used to read back the word from the screen to the string A
A$: String corresponding to the (key)word to type
A : Length of the word A$
R : Row offset based on A; used to horizontally center the word when printing it
I : Loop iterator
B$: String made up of blank (space) characters, used to clear portions of the screen
K : Temporary character code associated to the key pressed by the player
K$: String input by the player
Program listing 1 LET H=0:LET B$=" ":POKE 23674,0:INPUT "LEVEL (1-5): ";D:LET E=.25*(5-D):GO TO 1+(D>0)*(D<6) 2 RANDOMIZE:LET L=4:LET S=0:BORDER 0:PAPER 0:INK 7:CLS:PRINT "S:0";B$;"H:";H;AT 0,29;"L:";L 3 PRINT AT 0,31;L:BORDER 0:INK 0:LET A$="":LET K$="":LET C=165+INT(RND*91):GO TO 4-(C>198 AND C<202) 4 PRINT AT 6,10;B$;AT 7,10;B$;AT 21,0;CHR$ C;B$:FOR I=0TO9:LET C$=SCREEN$(21,I):IF C$<>" " THEN LET A$=A$+C$:LET A=LEN A$ 5 NEXT I:INK 6:LET R=11+(9-A)/2:PRINT AT 6,R;A$:POKE 23672,0:POKE 23673,0:LET M=50*(E+1.4/A+A/4):INK 7 6 LET T=INT((M-PEEK 23672-256*PEEK (23673))/5)/10:LET T$=STR$(T*(T>0)):PRINT AT 2,14;T$;:IF LEN T$=1 THEN PRINT ".0" 7 IF T<=0 OR A$(TO LEN K$)<>K$ THEN BORDER 2:LET L=L-1:BEEP .5,-23:LET H=(S>H)*S+(H>=S)*H:GO TO 3*(L>=0)+2*(L<0) 8 LET K=CODE INKEY$:IF K>0 THEN LET K=K-32*(K>96)*(K<123):LET K$=K$+CHR$ K:PRINT AT 7,R;K$:BEEP 0.009,0 9 IF A$=K$ THEN BORDER 4:BEEP .02,23:LET S=S+T*10:INK 7-2*(S>H):PRINT AT 0,2;S:INK 7:GO TO 3 10 GO TO 6: REM *** Marco's Retrobits 2021 https://retrobits.itch.io ***
Source code explained
1 LET H=0:LET B$=" ":POKE 23674,0:INPUT "LEVEL (1-5): ";D:LET E=.25*(5-D):GO TO 1+(D>0)*(D<6)
Line 1 is executed only once when the program starts.
Set high score H to 0; reset most significant byte of the FRAMES counter (address 23674); prompt the user for the desired difficulty level D and initialize the difficulty coefficient E accordingly.
If the input value is not in the expected range (1..5) go back to line 1; otherwise continue to line 2.
2 RANDOMIZE:LET L=4:LET S=0:BORDER 0:PAPER 0:INK 7:CLS:PRINT "S:0";B$;"H:";H;AT 0,29;"L:";L
Line 2 is executed whenever a new game starts.
Initialize the random number generator; set the remaining lives counter L to 4 and the current score S to 0; clear the screen to black BORDER, black PAPER and white INK.
Print the top line with score, high score and lives info.
3 PRINT AT 0,31;L:BORDER 0:INK 0:LET A$="":LET K$="":LET C=165+INT(RND*91):GO TO 4-(C>198 AND C<202)
Line 3 is where the game loop starts and where a new (key)word is picked.
Print current lives counter L. The border colour is reset to black. So is the ink colour, so that the next printed will be invisible, since also the paper colour is black.
A$ and K$ are initialized to empty string. Then C, the character code of the keyword to be displayed, is assigned a random integer value between 165 ("RND") and 255 ("COPY").
Characters with codes 199 ("<="), 200 (">=") and 201 ("<>") are not words and so are not considered.
4 PRINT AT 6,10;B$;AT 7,10;B$;AT 21,0;CHR$ C;B$:FOR I=0TO9:LET C$=SCREEN$(21,I):IF C$<>" " THEN LET A$=A$+C$:LET A=LEN A$
Line 4 constructs the word A$ from the keyword character code C.
First, delete words already on screen by overwriting them with B$, then invisibly print the keyword corresponding to character code C.
Create the A$ string by retrieving each single character composing the keyword (excluding spaces), using the SCREEN$ function in a FOR loop. Store the word length in A.
5 NEXT I:INK 6:LET R=11+(9-A)/2:PRINT AT 6,R;A$:POKE 23672,0:POKE 23673,0:LET M=50*(E+1.4/A+A/4):INK 7
Line 5 prints the word A$ in yellow and initializes the countdown.
The NEXT statement completes the FOR loop started in line 5. Set INK colour to yellow (6) and calculate the horizontal position R of the word A$ so that it will be printed in the middle of the line.
Print the word A$. Reset the (2 least significant bytes of the) FRAMES counter. The most significant byte was already reset at the beginning of the program. Two bytes are enough for the countdown.
Initialize the countdown timer M based on word length A and difficulty coefficient E and finally set the INK colour to white (7).
6 LET T=INT((M-PEEK 23672-256*PEEK (23673))/5)/10:LET T$=STR$(T*(T>0)):PRINT AT 2,14;T$;:IF LEN T$=1 THEN PRINT ".0"
Line 6 is the first statement of an inner loop, in which the program calculates and updates the available time left and checks if the string typed by the player matches the word.
Calculate the time left T, by subtracting the current value of the (least 2 significant bytes of the) FRAMES counter from the initial available time M and print its value on screen.
7 IF T<=0 OR A$(TO LEN K$)<>K$ THEN BORDER 2:LET L=L-1:BEEP .5,-23:LET H=(S>H)*S+(H>=S)*H:GO TO 3*(L>=0)+2*(L<0)
Line 7 checks if the player has failed at typing the word.
The player fails at typing the word if either the timer expires (available time T is <= 0), or if the string typed so far by the player K$ is not part of the word A$.
If one of these conditions occur, the player loses a life (L is decremented by one), the BORDER colour is set to red (2) and a long low tone note is BEEPed. If current score S is greater than the high score H, the high score is updated.
Finally, if the player has still some lives left, the program jumps to the beginning of the game loop at line 3; otherwise if there are no more lives left, a new game starts by jumping to line 2.
Please note that if the player has not yet typed anything, K$ would contain an empty string, which is considered a valid substring of the word in A$.
8 LET K=CODE INKEY$:IF K>0 THEN LET K=K-32*(K>96)*(K<123):LET K$=K$+CHR$ K:PRINT AT 7,R;K$:BEEP 0.009,0
Line 8 handles player input.
If a key is pressed, the corresponding character is converted to upper case and appended to the K$ string. The updated K$ string is printed to the screen, below the word A$.
Finally, a short tone is BEEPed to give a feedback that the key press has been recorded.
9 IF A$=K$ THEN BORDER 4:BEEP .02,23:LET S=S+T*10:INK 7-2*(S>H):PRINT AT 0,2;S:INK 7:GO TO 3
Line 9 handles successful typing of the word.
If the typed string K$ matches the requested word A$, the player has successfully typed the word within the timeout.
The BORDER is set to green (4), a bright tone is BEEPed and the remaining time is multiplied by 10 and added to the score S.
If the score S is greater than the high score, it is printed in cyan INK (5), otherwise in white (7). Finally the program jumps to the beginning of the game loop at line 3.
10 GO TO 6: REM *** Marco's Retrobits 2021 https://retrobits.itch.io ***
Line 10 is the end of the inner game loop.
If the program flow reaches line 10, it means that the word has not been completely typed by the player, but the characters typed so far (if any) are correct and the available time has not yet expired.
So, simply jump back to the beginning of the inner loop at line 6.
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.
1lH=0:lB$=" ":o23674,0:i"LEVEL (1-5):";D:lE=.25*(5-D):g1+(D>0)*(D<6)
3pI0,31;L:b0:X0:lA$="":lK$="":lC=165+INT(T*91):g4-(C>198 AND C<202)
4pI6,10;B$;I7,10;B$;I21,0;U C;B$:fI=0F9:lC$=K(21,I):uC$<>" " GlA$=A$+C$:lA=KA$
7uT<=0 OR A$(FKK$)<>K$ Gb2:lL=L-1:Z.5,-23:lH=(S>H)*S+(H>=S)*H:g3*(L>=0)+2*(L<0)
8lK=IXEY$:uK>0 GlK=K-32*(K>96)*(K<123):lK$=K$+U K:pI7,R;K$:Z0.009,0
10g6:e*** Marco's Retrobits 2021 https://retrobits.itch.io ***
Therefore, The Typing of the Spec is a suitable entry for the PUR-80 category of the BASIC 10 Liner Contest.
The Typing of the Spec 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 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 typing.tap file, either by selecting it in "File"->"Open..." or by dragging and dropping it on the emulator window.
To see the program listing, as soon as the program starts and prompts for the difficulty level, tap the A key and then tap ENTER.
The "2 variable not found" 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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.