Martin Fitzpatrick

# SAM Coupé Snake Simple Snake game in 10 lines of BASIC

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.

note: 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.

## Playing

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?

tip: 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
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

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
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 `x` & `y` variables. We `PRINT` the head to the screen on each move. Since things remain where they are printed, we don't need to re-print the tail. But we do need to keep track of where it is both for collisions and to clear up after the tail.

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 `&8000` to `&8002` and poke the new y & x position into `&8000` and `&8001` respectively. By reading memory at `&8000+2*l` and `&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 `POW` and `ZAP` 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.

## PUR 80

To squeeze the game down to fit the 80 character line limit needed the following tricks.

1. The &8000 address was stored in a variable `o` saving 4 chars each, or 2 for `o+1`, `o+2`.
2. Statements were folded back into lines using `:`, including breaking `IF` and `ELSE` blocks across lines. SAM BASIC doesn't care about line grouping.
3. Re-order statements to fit, e.g. `PEN 12` is moved to the `DO..` line.

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.

## Bugs

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.