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.
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.
Atari XL or XE computer (or 800 with Incognito board, I guess?), capable of running TurboBASIC XL — or an emulator — and a joystick.
anteat.atr
- ATR disk image
(for emulators, or real Ataris with SIO2PC, SIO2SD, etc.)
anteat.txt
- ASCII text file
of the source (as seen on this web page)
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.
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!)
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.
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}"
GRAPHICS 0
— switch to 40x24 text mode
DPOKE 82,9984
— set the margins: left (82/LMARGN=0), right (83/RMARGN=39)
DPOKE 709,15
— set the colors: brightest text (709/COLOR1=15), black background (710/COLOR2=0)
POKE 752,1
— disable the cursor (752/CRSINH=1)
POKE 755,3
— set character controls such that inverse characters appear as a solid inverse space (755/CHACT=3) 1
? "{clear}";
— clear the screen
SC=DPEEK(88)
— grab the start of screen memory (88,89/SAVMSC)
DIM A$(40),B$(40)
— declare, & reserve space for, some strings
A$(1)="{^c}"
— fill a string with character-3 (control+C), step 1/3 2
A$(40)="{^c}"
— ...step 2/3
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
A$(2)=A$
— ...step 3/3
B$="{^n}{^,}"
— fill another string with character 14 (control+N) and character 0 (control+,), step 1/3 2
B$(39)="{^n}{^,}"
— ...step 2/3
B$(3)=B$
— ...step 3/3
FOR Y=3 TO 23 STEP 2
— start a loop over every other line of the screen, starting near (not at) the top
SCC=SC+Y*40
— determine starting address of that row of screen
MOVE ADR(A$),SCC,40
— copy the solid string of char-3 (they'll appear as "#" – dirt)
IF Y<23
— if not at the very bottom row...
POKE SCC+11+RAND(9),0
— erase a random "#" near the center, to its left
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)
POKE SCC+22+RAND(9),0
— erase another random "#" near the center, this time to its right
MOVE ADR(B$),SCC+40,40
— copy the string of char-14, char-0 pattern (they'll appear as "." – ants – & spaces)
R=R+20
— keep track of how many "."'s we've drawn (we'll count them down as the player eats them)
ENDIF
NEXT Y
X=19
— set the player's X (at the center)
Y=2
— set the player's Y (near the top, above the first row of "#" dirt)
REPEAT
— start the main game loop
S=STICK(0)
— grab the state of the joystick (aka 632/STICK0)
OX=X
— record current X pos.
OY=Y
— record current Y pos.
X=X+(S=7)-(S=11)
— adjust X pos. based on joystick direction (7=right, 11=left) 3
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)
40 Y=Y+(S=13)-(S=14)
— adjust Y pos. based on joystick direction (13=down, 14=up) 3
POSITON 0,0
— move cursor to top left
? Z
— draw score
IF X<0 OR X>39 OR Y<2 OR Y>23
— if player is off any edge...
X=OX
— move back to where it was...
Y=OY
ENDIF
IF X<>OX OR Y<>OY
— if we moved this time around...
K=SC+Y*40+X
— calculate memory address of our new position
V=PEEK(K)
— grab what's at that spot on the screen
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
IF V<>0 AND V<>14 AND V<>10
— if it's not a blank (0), not an ant (14), and not a bug (10)...
X=OX
— move back to where it was...
Y=OY
ELSE
POKE SC+Y*40+X,128+S
— draw more tongue at the new location 1
L=L+1
— increase tongue-length counter
IF V=14 OR V=10
— if it was an ant (14) or a bug(10)...
SOUND 0,L/2,10,15
— play a sound (longer the tongue, the lower the pitch)
IF V=14
— if it was an ant...
Z=Z+L
— add to score, based on tongue length
R=R-1
— reduce the count of ants left
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
POSITION 35,0
— move cursor top the top, near the right
? R;" "
— show how many ants are left
ELSE
— it was a bug!
Z=Z+1000
— add 1000 points!
B=0
— disable the bug
ENDIF
ENDIF
ENDIF
ELSE
IF (X<>OX)+(Y<>OY)+STRIG(0)=0
— if we didn't move, and firebutton (aka 644/STRIG0) is pressed 4
K=SC+Y*40+X
— calculate memory address of our current position
S=PEEK(K)&15
— get the value of the character at that spot (disregarding the high nybble, so 0-15)
POKE K,0
— erase what was there (the tip of our tongue)
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
X=X-(S=7)+(S=11)
— adjust X pos. based the value (this time, 7=left, 11=right) 5
Y=Y-(S=13)+(S=14)
— adjust X pos. based the value (this time, 13=up, 14=down) 5
L=L-(L>0)
— reduce tongue-length counter
ENDIF
ENDIF
IF ABS(B)+RAND(10)=0
— if the bug is not enabled, there's a 1-in-10 chance we will...
BY=4+RAND(10)*2
— pick a random row for the bug
B=RAND(2)*2-1
— pick a random direction we want the bug to go (-1, or +1)
BX=19-B*19
— give it an X position based on the direction it's going (left side, if moving right, & vice-versa)
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
IF PEEK(SC+BY*40+BX)<>0
— if there's something already there...
B=0
— disable the bug (abort!)
ENDIF
ENDIF
IF B
— if the bug is enabled... 6
POKE SC+BY*40+BX,0
— erase the bug's current location
IF M=0
— if the "0 to 9" cyclic counter is at 0 again...
BX=BX+B
— move the bug in the direction it wants to go
IF BX<0 OR BX>39
— if the bug will go off the screen...
B=0
— disable the bug
ENDIF
ENDIF
IF B
— if the bug is still enabled... 6
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
K=SC+BY*40+BX
— calculate memory address of the bug's position
V=PEEK(K)
— grab what's at that spot on the screen
IF V=14
— if it was an ant...
B=-B
— turn around
BX=BX+B
— go back
ELSE
— it wasn't an ant...
POKE K,10
— draw the bug ("*")
ENDIF
IF V&128
— if what was on the screen was any part of the player's tongue...
SOUND 0,200,12,15
— play a sad sound
PAUSE 100
— wait a few seconds
R=0
— pretend there are no more ants left (so the game ends)
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
M=(M+1) MOD 10
— cycle the "0 to 9" cycling counter 7
POKE 712,M+16*ABS(B)
— flash the border, based on the cycling counter (greys), and whether a bug is on the screen (yellows) (712/COLOR4)
SOUND 0,0,0,0
— silence any currently-playing sound
UNTIL R=0
— keep running the main loop until the count of ants remaining hits 0
GRAPHICS 18
— switch to full-screen 20x12 text mode
? #6;"FINAL SCORE:";Z
— display the final score
? #6;"press a key"
— prompt for a keypress
GET A$
— wait for a keypress
RUN
— start over from scratch (new game)!
IF X=OX AND Y=OY AND STRIG(0)=0
".
IF B=1 OR B=-1
",
or even "IF B<>0
"