The Basic 10 Liner contest has been running for 11 years. This year (2021) the 10th competition, and the first time I've heard of it, thanks to Dean Belfield.
The competition is simple: write a game in some BASIC language in 10 lines of code. Like the arcade games for the micro:bit the fun comes from trying to squeeze as much of a game as possible out of impossible limits.
Since many BASIC dialects allow you to stack multiple statements onto the same line (SAM BASIC is no exception) there are additional category limits on characters per line, e.g.
- Category "PUR-80": Program a game in 10 lines (max 80 characters per logical line, abbreviations are allowed).
- Category "PUR-120": Program a game in 10 lines (max 120 characters per logical line, abbreviations are allowed)
I aimed for the PUR-80 category and got there with Snake.
This isn't a notable achievement, go see some of the great games from previous years like Sim City! But for a first attempt, I'm pretty chuffed.
You control the Snake with the keys Q, A, O & P for up, down, left, right. Eating food gains you a point and extends your Snake length by 1. If you chomp your own tail you die.
I mean, it's Snake, what do you expect?
You can download a MGT disk image containing 10 line Snake.
To play, you can use the SimCoupe emulator. Insert disk image with File>Open. Load & Run with
LOAD "snake80" LINE 1. Press
ESC to break to source.
The complete code is shown below with inline comments.
# vars x, y: current coords # xs, ys: current x/y speed (always -1, 0 or 1) # l: length, starts at 1 # fx, fy: x, y position of food # die: 1 if dead LET x=10, y=10, xs=1, ys=0, l=1, fx=0, fy=0, die=0 # Pre-fill the tail memory with the starting location. POKE &8000,x,y,x,y # Setup the flashing effect for the food. Alternates color 11 between palette 50 (pink) & 127 (white). # flashing every 5/50ths of a second. PALETTE 11, 50, 127: POKE &5a08,5 DO # Move the head by current xs & ys. LET x=x+xs, y=y+ys # Wrap around on screen edges. IF x<0: LET x=31: ELSE IF x>31: LET x=0: END IF IF y<0: LET y=18: ELSE IF y>18: LET y=0: END IF # Draw the head. Only the head is drawn. PEN 12: PRINT AT PEEK &8000, PEEK &8001;"█": # Blank out the square behind the tail. PRINT AT PEEK (&8000+l*2), PEEK (&8000+l*2+1);" " # If our head is at the same position as the food, eat it. Update score. # ZAP is a built-in SAM BASIC sound effect. fx is set to zero when no food. IF x=fx AND y=fy: ZAP : LET l=l+1: LET fx=0: PEN 15: PRINT AT 0,0; l: END IF # Move the tail memory down 2 bytes, making way for the new head position. POKE &8002, MEM$ (&8000 TO &8000+l*2) # Poke current head location to the top of the tail memory. # Poked as 2 bytes, y then x (cols by rows, because we're printing). POKE &8000, y, x # Collision check (using pixels) the position in front of the snake. If we # see ourselves, we're dead. IF POINT (x*8+4, (19-y)*9)=12: LET die=1: END IF # If fx is zero (no food) add some randomly on the map and print it. IF fx=0: LET fx= RND (25)+3: LET fy= RND (10)+4: PEN 11: PRINT AT fy,fx;"▟": END IF # Scan the keyboard for input & update directions. LET i$= INKEY$ : IF i$="q": LET xs=0, ys=-1: ELSE IF i$="a": LET xs=0, ys=1 ELSE IF i$="o": LET xs=-1, ys=0: ELSE IF i$="p": LET xs=1, ys=0: END IF # Exit when die=1. LOOP UNTIL die # POW is a built-in SAM BASIC sound effect. POW # Restart the game. RUN
How it works
Each tick the head of the Snake moves 1 block in whichever direction it is going, by incrementing the
To do this we
POKE our current x & y positions into memory using one byte for each for simplicity sakes. On each tick
we shift 2 x length bytes from
&8002 and poke the new y & x position into
By reading memory at
&8000+2*l+1 we can get the last segment of our current length tail and delete it
from the map by printing an empty space.
Tail is POKEd to memory, shifted right 2 bytes on each tick & the current x & y coords are POKEd to the head. The value shifted off the end is used to clear up after the tail.
Food is placed randomly on the map and we compare x & y positions of the food, to that of the Snakes head to register an "eat".
We're using two SAM-specific features here. First, is the use of the SAM palette flashing to flicker the food. By setting
PALETTE 11, 30, 127
we color palette 11 (which we are using for our food
PEN) to alternate between system color 30 & 27. The
POKE &5a08, 5 sets
the rate of alternating to every 5/50ths of a second to highlight the food. Second, is the use of the SAM BASIC built-in sound effects
which make pow and zap sounds, kind of. One negative is that doing this freezes execution of the BASIC program, so there is a small
freeze when picking up food. But it's not too bad.
Tiny games for your BBC micro:bit
Create arcade games in 5x5 pixels
A step by step guide to creating playable arcade games on the micro:bit
To support developers in [[ countryRegion ]] I give a [[ localizedDiscount[couponCode] ]]% discount on all books and courses.
[[ activeDiscount.description ]] I'm giving a [[ activeDiscount.discount ]]% discount on all books and courses.
To squeeze the game down to fit the 80 character line limit needed the following tricks.
- The &8000 address was stored in a variable
osaving 4 chars each, or 2 for
- Statements were folded back into lines using
:, including breaking
ELSEblocks across lines. SAM BASIC doesn't care about line grouping.
- Re-order statements to fit, e.g.
PEN 12is moved to the
I thought I'd need to drop the palette cycling effects at first, but by jiggling statements around it came in under. The final code is --
1LET x=10,y=10,xs=1,ys=0,l=1,fx=0,fy=0,o=&8000,d=0:PALETTE 11,50,127 2POKE o,x,y,x,y:POKE &5a08,5:DO:LET x=x+xs,y=y+ys:PEN 12:IF x<0:LET x=31 3ELSE IF x>31:LET x=0:END IF:IF y<0:LET y=18:ELSE IF y>18:LET y=0:END IF 4PRINT AT PEEK o,PEEK (o+1);"ď":PRINT AT PEEK (o+l*2),PEEK (o+l*2+1);" " 5IF x=fx AND y=fy:ZAP:LET l=l+1:LET fx=0:PEN 15:PRINT AT 0,0; l:END IF 6POKE o+2,MEM$(o TO o+l*2):POKE o,y,x:IF POINT(x*8+4,(19-y)*9)=12:LET d=1 7END IF:IF fx=0:LET fx=RND(25)+3:LET fy=RND(10)+4:PEN 11 8PRINT AT fy,fx;"č":END IF:LET i$=INKEY$:IF i$="q":LET xs=0,ys=-1 9ELSE IF i$="a":LET xs=0,ys=1:ELSE IF i$="o":LET xs=-1,ys=0:ELSE IF i$="p" 10LET xs=1,ys=0:END IF:LOOP UNTIL d:POW:RUN
I wasn't originally packing the BASIC as compact as it can go, and this still isn't
as small as it can go: you can even do
FOR a=1to10 for example. As a result the longest
line is line 9, with 74 chars.
The game is available on an disk image here.
When placing food there is no check to make sure it's not going placed in the tail. Since we only draw the food when first placed, this means it disappears once the tail moves over (the end-tail blanking removes it). You have to try and remember where it is.
If you're looking for a reference to SAM BASIC you can take a look at the original User Manual and the Complete Guide to SAM Basic by Graham Burtenshaw.
Continue reading the BASIC10 series with Remaking the classic Atari game ET in 10 lines of SAM Coupé BASIC