anteat - 10-line "Anteater" game in TurboBASIC XL for the Atari 8-bit

Bill Kendrick, February 2016

For the NOMAM 2016 contest

Purpose

This was an attempt to write a working game in 10 lines (or fewer) of BASIC, without using any machine language routines, for the NOMAM 2016 ten-liner contest.

I chose TurboBASIC XL on the Atari 8-bit (my two favorites), and used an ASCII-to-ATASCII conversion tool I had written for Linux in 2014. Despite that, I limited lines to 120 characters, the physical limit for editing "logical lines" of text on the Atari itself.

Therefore, this counts as a "PUR-120" entry (TurboBASIC XL is actually capable of reading source from a file with lines up to 256 characters). That said, heavy use of abbreviations, whitespace removal, and crazy logic shorthands were utilized to get it to fit within the limits — 1200 characters total; that's less than 10 tweets on Twitter! Below, I've broken down the code, and explained what each line does, and why.

Objective

This game is a simplified clone of the arcade game Anteater (1982, Stern Electronics), and its numerous clones over the years.

Maneuver your tongue through a maze of underground corridors, in an attempt to eat all of the ants on the screen (200 of them). Avoid having your tongue bitten by the bug, or you'll loose. Eat the bug for bonus points.

Requirements

Atari XL or XE computer (or 800 with Incognito board, I guess?), capable of running TurboBASIC XL — or an emulator — and a joystick.

Download

Controls

Use a joystick in port 1. Press up/down/left/right to move your tongue. You cannot cross your own path! To retract your tongue, and backtrack towards the surface, press the fire button.

Scoring

Your score is shown at the top left. (The number of ants remaining on the screen is shown at the top right.)

The point value of each ant depends on the length of your tongue (and you'll notice the pitch of the tone that's play when you eat them is lower, the longer your tongue is). While risky (since a bug might bite you, and end your game), twisting throughout the corridors will result in higher points.

If you touch the bug with the tip of your tongue, you'll eat it, and receive 1000 points. (If the bug touches you, it's game over!)

Game Over

If you've eaten all 200 bugs, or you're bitten by a bug, the game ends. Your final score is shown. Press any key on the keyboard to start a new game.

Code Breakdown

For each of the 10 lines of source code, the abbreviated version (which fits within 120 characters) is shown, immediately followed by an expanded version that's slightly more readable. Then, each individual command is shown, and its purpose is explained, using nested bullet lists (to show the code's structure within IF/ENDIF, FOR/NEXT, etc. blocks).

10 GR.0:DPOK.82,9984:DPOK.709,15:POK.752,1:POK.755,3:?"{clear}";:SC=DPEEK(88):DIM A$(40),B$(40):A$(1)="{^c}":A$(40)="{^c}"
10 GRAPHICS 0:DPOKE 82,9984:DPOKE 709,15:POKE 752,1:POKE 755,3:? "{clear}";:SC=DPEEK(88):DIM A$(40),B$(40):A$(1)="{^c}":A$(40)="{^c}"

20 A$(2)=A$:B$="{^n}{^,}":B$(39)="{^n}{^,}":B$(3)=B$:F.Y=3TO23STEP2:SCC=SC+Y*40:MOV.ADR(A$),SCC,40:IF Y<23:POK.SCC+11+RAND(9),0
20 A$(2)=A$:B$="{^n}{^,}":B$(39)="{^n}{^,}":B$(3)=B$:FOR Y=3 TO 23 STEP 2:SCC=SC+Y*40:MOVE ADR(A$),SCC,40:IF Y<23:POKE SCC+11+RAND(9),0

30 POK.SCC+22+RAND(9),0:MOV.ADR(B$),SCC+40,40:R=R+20:END.:N.Y:X=19:Y=2:REP.:S=STICK(0):OX=X:OY=Y:X=X+(S=7)-(S=11)
30 POKE SCC+22+RAND(9),0:MOVE ADR(B$),SCC+40,40:R=R+20:ENDIF:NEXT Y:X=19:Y=2:REPEAT:S=STICK(0):OX=X:OY=Y:X=X+(S=7)-(S=11)

40 Y=Y+(S=13)-(S=14):POS.0,0:?Z:IF X<0 OR X>39 OR Y<2 OR Y>23:X=OX:Y=OY:END.:IF X<>OX OR Y<>OY:K=SC+Y*40+X:V=PEEK(K)
40 Y=Y+(S=13)-(S=14):POSITON 0,0:? Z:IF X<0 OR X>39 OR Y<2 OR Y>23:X=OX:Y=OY:ENDIF:IF X<>OX OR Y<>OY:K=SC+Y*40+X:V=PEEK(K)

50 IF V<>0AND V<>14AND V<>10:X=OX:Y=OY:EL.:POK.SC+Y*40+X,128+S:L=L+1:IF V=14 OR V=10:SO.0,L/2,10,15:IF V=14:Z=Z+L:R=R-1
50 IF V<>0 AND V<>14 AND V<>10:X=OX:Y=OY:ELSE:POKE SC+Y*40+X,128+S:L=L+1:IF V=14 OR V=10:SOUND 0,L/2,10,15:IF V=14:Z=Z+L:R=R-1

60 POS.35,0:?R;" ":EL.:Z=Z+1000:B=0:END.:END.:END.:EL.:IF (X<>OX)+(Y<>OY)+STRIG(0)=0:K=SC+Y*40+X:S=PEEK(K)&15:POK.K,0
60 POSITION 35,0:? R;" ":ELSE:Z=Z+1000:B=0:ENDIF:ENDIF:ENDIF:ELSE:IF (X<>OX)+(Y<>OY)+STRIG(0)=0:K=SC+Y*40+X:S=PEEK(K)&15:POKE K,0

70 X=X-(S=7)+(S=11):Y=Y-(S=13)+(S=14):L=L-(L>0):END.:END.:IF ABS(B)+RAND(10)=0:BY=4+RAND(10)*2:B=RAND(2)*2-1:BX=19-B*19
70 X=X-(S=7)+(S=11):Y=Y-(S=13)+(S=14):L=L-(L>0):ENDIF:ENDIF:IF ABS(B)+RAND(10)=0:BY=4+RAND(10)*2:B=RAND(2)*2-1:BX=19-B*19

80 IF PEEK(SC+BY*40+BX)<>0:B=0:END.:END.:IF B:POK.SC+BY*40+BX,0:IF M=0:BX=BX+B:IF BX<0OR BX>39:B=0:END.:END.:IF B
80 IF PEEK(SC+BY*40+BX)<>0:B=0:ENDIF:ENDIF:IF B:POKE SC+BY*40+BX,0:IF M=0:BX=BX+B:IF BX<0 OR BX>39:B=0:ENDIF:ENDIF:IF B

90 K=SC+BY*40+BX:V=PEEK(K):IF V=14:B=-B:BX=BX+B:EL.:POK.K,10:END.:IF V&128:SO.0,200,12,15:PA.100:R=0:END.:END.:END.
90 K=SC+BY*40+BX:V=PEEK(K):IF V=14:B=-B:BX=BX+B:ELSE:POKE K,10:ENDIF:IF V&128:SOUND 0,200,12,15:PAUSE 100:R=0:ENDIF:ENDIF:ENDIF

100 M=(M+1)MOD10:POK.712,M+16*ABS(B):SO.0,0,0,0:UNTIL R=0:GR.18:?#6;"FINAL SCORE:";Z:?#6;"press a key":GET A$:RUN
100 M=(M+1) MOD 10:POKE 712,M+16*ABS(B):SOUND 0,0,0,0:UNTIL R=0:GRAPHICS 18:? #6;"FINAL SCORE:";Z:? #6;"press a key":GET A$:RUN

Footnotes

  1. As the tongue is drawn on the screen, the values read from the joystick (7=right, 11=left, 13=down, 14=up) are actually drawn onto the screen as breadcrumbs. In other words, data on the screen is used to track the tongue's previous positions (without needing separate arrays of X & Y positions plus an index; only the current X/Y position needs to be tracked). By utilizing ANTIC's ability to adjust how characters are rendered when the high bit is set — in this case, invisible character, but keeping the inverse-video effect — a solid shape appears for the tongue, even though four different characters are being placed on the screen.
  2. Setting the first and last characters of a string, and then setting the second character to the string itself, is a well-known trick that can be used to initialize a string in Atari BASIC.
  3. Rather than using an IF/ELSE to test the value of S, and then change X by +1 or -1, the true/false results of the logical operations ("S=7" results in "1" if S is 7, "0" otherwise) are applied to the variable mathematically (+1 if S equals 7, -1 if S equals 11, 0 otherwise).
  4. Rather than using ANDs, the true/false results of logical operations (X is not equal to OX results in "1"), together with the firebutton's state (0=pressed, 1=released), are combined mathematically. This ends up being shorthand (if hard-to-read) for "IF X=OX AND Y=OY AND STRIG(0)=0".
  5. The joystick-direction 'breadcrumb' we left on the screen when we passed this position earlier is now being used to move the tip of the tongue back in the opposite direction. (And again, logical results are being applied as math, to be more concise.)
  6. The bug's activity is recorded as it's direction (-1 for left, +1 for right), or 0 if it's not currently on screen. Logically, any non-zero number comes out as true, so there's no need to say, e.g., "IF B=1 OR B=-1", or even "IF B<>0"
  7. The variable M increases each iteration of the main REPEAT/UNTIL loop. Once it hits 10, though, it resets back to 0, via the MOD (modulus) operator (which is known as % in C and some other languages). It's used to determine when the bug should move; it only moves once every 10 iterations (so at 1/10th of the tongue's speed).

Contact