In February, I stumbled upon the BASIC 10Liner Contest, an extreme challenge of creating an entire retro video game in 10 lines of BASIC code.
BASIC was the de-facto language of the home computing revolution in the late 1970s through the 1980s. The first Apple, Atari, and Commodore (and other) computers shipped with BASIC as the primary system interface for the user to both run the computer and also to program their own software.
BASIC was created as a computer language that was easy to learn and fun to use. The home computer makers included extra features in their variants of the BASIC language to encourage average users to create their own video games. The BASIC 10Liner contest recreates that exciting moment in time where many fun video games could be, and in fact were, created by amateurs at home!
Of course this intrigued me. With the ridiculous constraint of only 10 lines of code on an old 8-bit machine, perhaps even I could create something that could be considered a video game! I thought, “Let me go ahead and make the world’s worst video game and enter this contest. Just for fun.”
That line of thinking was not only liberating but downright encouraging to take some tiny steps and see what I could create. If you have read my posts, you will know that I am a huge enthusiast of the Commodore 64 computer. So that’s the 8-bit machine that I chose. I even had a book that would be perfect for this!
In order to create a game in 10 lines, multiple BASIC statements must be included on each line, separated by the “:” character. According to the contest rules, each logical line can have a max of 80 characters to be allowed in the basic PUR-80 category. (This also is the line length limit on the C64.) Loading of any data or program parts from mass storage is not allowed. The 10 lines also cannot contain any machine code—it must simply use the factory built-in BASIC for an 8-bit machine, in this case BASIC V2 on the Commodore 64.
With only 10 lines to work with, the concept for the game had to be very simple. What kind of game should I make? Pondering this, I was inspired by my daughter who loves pandas and has often imagined having one as a companion, sharing in her pursuits and desires. Desires such as treats, and the best treat of all (in her opinion) is a delicious donut!
That was it. I would make a game where a panda attempts to eat a donut! I thought that was possible within the constraint of 10 lines. I turned to my daughter again to name the game, and Yum Yum Donut was born!
After several weeks, Yum Yum Donut slowly transformed from concept into my first video game creation.
You are a panda with a hankering for a scrumptious donut. When you try to take a bite, you are surprised to see the donut running away! It is so tasty, you must pursue it and take as many yummy bites as possible.
Use the joystick to move the panda in pursuit of the donut. Each successful bite increases your experience of Yum! Each bite also makes the donut harder to catch.
How many bites can you take in 90 seconds before the game is over?
These screenshots give a glimpse of the final game creation. In its entirety, Yum Yum Donut consists of two graphics sprites, a panda and a donut. The panda can be controlled with the joystick in order to chase after the moving donut. There is a scoreboard that shows how many yummy bites the panda has achieved, and each bite provide a nice arcade-like “Boop!” sound effect. Each game lasts 90 seconds, with the pitch of the sound effect and the color of the scoreboard changes as the game nears the end of the time limit, giving a greater sense of urgency to the user controlling the panda. The donut also becomes progressively more difficult to catch as the score increases. The 90-second limit combined with the score provides some additional fun in such a simple game, allowing the players to compete against each other for a high score!
0 s=12288:v=53248:poke2040,192:poke2041,193:t$="{clr}{home}donut! yum":ti$="000000"
1 pokev,50:pokev+1,80:pokev+21,3:pokev+28,1:pokev+39,1:q=54272:y=0:gosub7:gosub7
2 onkgosub9,9,9,9:j=z*2+2:f=peek(v+30)and1:onf+1gosub9,8:f=peek(56320)and15
3 k=f-int(f/5)*5:d=(2*(int(k/2)-int(k/4)*2)-1)*z*33:b=h*z:z=rnd(1):ife>90thenend
4 data3,234,240,3,170,176,2,170,160,2,251,224,2,251,224,2,234,224,2,238,224,2
5 data174,160,2,191,160,2,170,160,2,170,160,0,0,0,0,0,0,12,0,0:ife>70thena$="{yel}"
6 data63,0,0,51,0,0,97,128,0,97,128,0,51,0,0,63,0,0,12,0,0,0,0,0:j=-(k>2):goto2
7 z=y+32:forx=ytoz:reada:pokes+x,a:next:forx=z+1toz+31:pokes+x,0:next:y=x:a$="{wht}"
8 g=24+e/5:printt$a$h:h=h+1:pokeq+4,0:pokeq+4,17:pokeq+24,15:pokeq+5,6:pokeq+1,g
9 c=-(j>1):x=peek(v+(j-c*2))+d-(c*b):x=x-int(x/239)*239:pokev+j,x:e=ti/60:return
These program lines shown above are the entire 10-line BASIC program listing for Yum Yum Donut.
Thanks to all of my kids for providing great ideas and feedback during the game’s development!
Below is a video of the final creation. I submitted it to the 2023 BASIC 10Liner Contest on March 22!
You can play the game yourself, by downloading it from GitHub or running it in the Internet Archive’s online emulator:
If you are curious how this game can function with only these 10 lines, read the next section below for a brief explanation on how it works.
BASIC Code Explanation
- Line 0 : Set Memory Location Constants, Sprite Pointers, Scoreboard Message, and Reset System Timer
- s : Sprite Data Memory Location at 12288
- v : Sprite Control at 53248
- poke2040,192:poke2041,193 : Sprite Data Memory Pointers at 192 * 64 = 12288 and 193 * 64 = 12352
- t$ : Scoreboard Message
- ti$=”000000″ : Reset System Timer
- Line 1 : Set Sprite Properties, SID Address, and Draw Sprite Data
- pokev,50:pokev+1,80 : Set X/Y Coordinates of Panda Sprite
- pokev+21,3 : Enable Sprites 0 and 1 (Panda and Donut)
- pokev+28,1 : Enable Multi-Color Mode on Sprite 0
- pokev+39,1 : Set Main Sprite Color to White
- q : SID Chip Control at 54272
- y : Initial Sprite Memory Offset to 0
- gosub7:gosub7 : Call SUB 7 Twice to Draw Both Sprites
- Line 2 : Start of Main Loop – Call Sprite Movement Subroutines, Check for Sprite Collision, and Read Joystick Position
- on k gosub 9,9,9,9 : On Joystick Push (k is 1, 2, 3, or 4) Then Call Sprite Movement Subroutine 9
- j=z*2+2 : Set Random Donut Movement Axis
- f=peek(v+30) and 1 : Check for Sprite Collision
- on f+1 gosub 9,8 : If No Collision Then Call Sprite Movement Subroutine 9 ; If Collision then Call Scoreboard Subroutine 8
- f=peek(56320) and 15 : Check for Joystick Push
- Line 3 : Calculate Sprite Movement Values and Check Game Timer
- k=f-int(f/5) * 5 : Determine Joystick Direction
- d=(2 * (int(k/2) – int(k/4) * 2) – 1) * z * 33 : Calculate Sprite Movement Direction and Amount
- b=h*z : Additional Donut Jump As Score Increases
- z=rnd(1) : Set Random Number for Next Loop Iteration
- if e>90 then end : If Game Timer > 90 Seconds Then Game Over
- Line 4 : Sprite Shape Data
- Sprite Data Bytes
- Line 5 : More Sprite Shape Data and Scoreboard Color for Game End
- More Sprite Data Bytes
- if e>70 then a$=”{yel}” : If Game Timer > 70 Seconds Then Set Scoreboard to Yellow
- Line 6 : More Sprite Shape Data, Calculate Panda Movement Axis, and Keep Looping
- More Sprite Data Bytes
- j=-(k>2) : Set Panda Movement Axis Based on Joystick Direction
- goto 2 : Loop Back to Line 2
- Line 7 : SUBROUTINE – Read Sprite Data and Draw (Poke) Sprites Into Memory
- z=y+32:for x=y to z:read a:pokes+x,a:next : Read 33 Bytes of Sprite Data and Poke into Memory
- for x=z+1 to z+31:pokes+x,0:next : Poke Zero into Remaining Sprite Memory
- y=x : Set Sprite Memory Offset for Next Run
- a$=”{wht}” : Set Initial Scoreboard Color to White
- Line 8 : SUBROUTINE – Increment Scoreboard and Play Sound Effects
- g=24+e/5 : Set Sound Frequency to Increase as Game Timer Increases
- print t$ a$ h : Print Scoreboard Color, Message, and Score
- h=h+1 : Increment Score
- pokeq+4,0 : Reset Sound Chip
- pokeq+4,17 : Set Sound Waveform to Triangle
- pokeq+24,15 : Set Sound Volume to Max
- pokeq+5,6 : Set Sound Decay to 6
- pokeq+1,g : Set Sound Frequency for Boop! Sound Effect
- Line 9 : SUBROUTINE – Move A Sprite Based on Current Sprite Position and Movement Amount
- c=-(j>1) : Set Additional Jump Flag (0 for Panda, 1 for Donut)
- x=peek(v + (j – c * 2)) + d – (c * b) : Calculate New Sprite Coordinate Based on Sprite Selector (j), Additional Jump Flag (c), Direction and Amount (d), and Additional Jump Value (b)
- x=x-int(x/239) * 239 : Make Sure New Coordinate is Within Screen Dimensions
- pokev+j,x : Move the Sprite
- e=ti/60 : Update Elapsed Timer
- return : Return to Main Program Loop
This is my favorite thing on the internet. And the cover art is fantastic!