Beetles

Copyright © 2023 by Víctor Parada

Beetles

This is a little game for the 2023 NOMAM's BASIC 10-liners Contest. This program fits in the PUR-120 category, and it was written using FastBasic 4.6 for the 8-bits ATARI XL/XE computers line. Development started on 2022-01-28, and it took 2+6 days. The final version's date is 2023-02-25.

UPDATE: It obtained the 1st place of 9 entries in the category.


Description

Proud of your white Beetle, participate in the Beetles Great Parade. How far can you go?


Instructions

BEETLES start When the title scren appears, press the button to join the Beetles Great Parade.
BEETLES move You can move to the left and to the right using the joystick. Upwards you can accelerate and downwards you can reduce your speed. You earn a point for every car you pass.
BEETLES round You have to overtake 20 cars in less than 20 seconds. If you do it, the time is extended to overtake 20 more cars, and so on. Every time you get extra time, you also get bonus points (the square of the remaining time), but the path is narrowed a bit.
BEETLES crash When you crash against another beetle or when you get out of the way, your the hood of your beetle will open and you must close it to see again. Precious time will be lost as you have to join the Parade again.
BEETLES honk When you feel that a crash is inminent, press the button to honk. One of the other cars might change to the other side. If you are lucky, you could find a path to be safe. Using the honk too much might lead to a major crash. Use it carefully!
BEETLES gameover When there is no more time, your beetle will stop and the game is over. Honk to join the Parade again.

Development of the game

Big and chunky sprites have a some kind of mystique, and I've written some games with such kind of sprites. While reviewing for the Atari Homebrew Awards lots of games released in 2022, a small game with huge sprites surprized me. It made recall the old Speed Race arcade, and I thought that someting similar could be done in Atari BASIC for the tenliners contest in the PUR-80 category (up to 800 bytes), using a string manipulation trick I developed to manage P/M graphics. I tried a proof of concept, using P0 for the players car, and P1 to P3 as the enemy cars, I could make them scroll down showing at most three of them at any time. The result was too slow for an action game. Instead of discarding this prototype or trying it in Turbo Basic XL, I turned it into Fastbasic and tried it again. The result was nice enough to continue the development.

BEETLES prototype

Initial prototype in Atari BASIC.

BEETLES prototype

Second prototype using Fastbasic and wide P/M graphics.

Not being an Atari BASIC game, the max size restriction increased to 1200 bytes for the PUR-120 category. That is a lot of space for a simple mini game, so I thought on how many features could be added and which would the game rules be. In the features, I thought about a narrowing or zig-zagging path, cars that move with different horizontal speeds and, obviously vertical speed control relative to our own car, which it should be fixed to a given height in the screen. About the rules, I thought about a challenge game counting how many cars you could pass in a given amount of time, or how many cars could you pass until you crash.

BEETLES prototype

Adding car bitmaps and shoulders.

BEETLES prototype

First playable version with collision detection.

After I coded some of the game action, I realized a couple of issues: the road was too wide with many open space to go through and sometimes the enemy cars were randomly aligned to force you into a crash. The solution for the first issue was simple: to reduce the width of the highway and use the sides to show the score and timer in a column. For the second one, it was fun to add a horn: you can press the button to beep-beep and make them change the direction, but not every one, just one of the three, and that one was randomly selected, so you have to horn many times to move them all, but sometimes against yourself!

BEETLES prototype

Adding a left pannel, new bitmaps and crash animation.

BEETLES prototype

New scoring system is displayed.

With enough space available, I added extra animations and sound effects. The bitmap for the car turned into a VW Beetle after some iterations, making it bigger. The rules of the game changed to a stage based ones: you have to avoid a given number of cars in a given amount of times to get into the next stage in a continuous run. At the end of every stage, the paths is narrowed a couple of pixels, so at some time it will be impossible to continue without a crash. Every crash just makes you waste time trying to get the rush. I fine tuned the timer during my tests, so you have to speed up your run if you crashed to complete the stage. Three crashes in the same stage are enough to be disqualified.

BEETLES prototype

Simplifying the animation to save bytes.

During the last development iterations, the code was about 1200 bytes, but hard enough to make it fit 10 lines of at most 120 chars, and I had to decide which features to remove. The first feature to be removed was the title screen, turning it into a very simple text screen. I also replaced the original crash animation using zig-zagging cars with a simpler one that move them straight to the top. This gave me more control over the score and the speed of the game. I also changed the use of each P/M, making player's car be P3 and enemy cars P0 to P2, simplifying some formulas and memory management.

In this process I found that I was not using Fastbasic statements and functions for P/M management but standard POKE instructions for Atari BASIC or TurboBasic XL. I changed the syntax and I found that I had freed about half of a line of code, and that no speed up or slow down of the gameplay was notorious. That was enough space to restore the original intro screen and to improve the crash animation. Also, a small "tick" sound FX was added to signal when a car gets passed.

While writing this doc, I decided to change the crash, and turned it into a small "incident" in order to make the game flow: the hood of the car will open and prevent seeing ahead. That is a simple reason to make you stop and loose time.

BEETLES prototype

Final version with an open front hood.

Some weeks later, I was thinking that the game did not reward completing the stage quick and with the least amount or no crashes. So I take a look at the packaged listing to find some room for 6 bytes in the proper place to add the remaining time to the score. After making some room by changing the order of some statements, I could make room and add the statement. Verifying that the game still have 10 lines, I found that the modified line still had 2 extra bytes available, so I could add more bonus points by adding the remaining time twice or my multiplying it by 2 or 3, but at the end, I multiplied the remining time by itself, so you could get 1, 4, 9, 16 or might be 25 extra points at the end of each stage.


Download and try

Get the BEETLES.ATR file and set it as drive 1 in a real Atari (or emulator). Turn the computer on and the game should start after the loading completes. A joystick in port 1 is required.

NOTE: This game is for PAL computers. If it is played on NTSC computers, the text colors won't be the same, but the game should have the same difficulty.


The code

The abbreviated BASIC code is the following:

The full and expanded BASIC listing is:


BEETLES
(c) 2023 Víctor Parada
dim x(3) byte,y(3),d(3),c(3)
X(): Horizontal position of each car
Y(): Vertical position of each car
D(): Moving direction of each car
C(): Start delay for each car
data f() byte=0,202,180,160
Engine's sound depending on speed
g=adr("{binary data}")+1
Bitmaps of car and opened hood
graphics 18
Set screen to graphics mode 2 (ANTIC 7) without text window
pmgraphics 2
P/M graphics in double line resolution
poke 707,15
P3 player's car is white
mset $D008,4,1
Double width players
poke 623,16
GPRIOR=16: Missiles with 5th player color
mset pmadr(0),512,0
Clears P0-P3 P/M area
mset pmadr(-1),128,$F0
M2 & M3 delimits the street
r=adr(x)
Pointer to the array (buffer) of P/M's horizontal positions.
To avoid graphic glitches, all cars are moved at once after a VSYNC
u=dpeek(88)+140
Screen position of the timer
position 6,5
print #6,"{binary data}"
x(3)=116
y(3)=83
exec p 3
Displays title screen
do
GAME LOOP
  while strig(0)
  wend
Waits for trigger
  a=92
  b=188
Street side limits
  exec l
Displays street limits
  o=1
General counter
  s=0
Score
  n=20
Cars needed to pass
  t=9
Time
  print #6,"{binary data}",,
    "{binary data}",,,,"{binary data}",,n,,,
    ,"{binary data}",,"{binary data}"
Displays left panel
  repeat
RACE LOOP
    for i=0 to 2
      y(i)=55
      c(i)=i*33
    next i
Initializes traffic
    x(3)=140
    exec p 3
Initializes car position
    e=0
Delay to count cars that gone away (passed cars)
    h=0
No honk
    pause
Syncs with VBLANK
    poke $D01E,0
Clears collisions
    repeat
DRIVE LOOP
      t=t-(o=0)
Decreases the round timer
      j=stick(0)
Checks joystic position
      f=(j&8=0)-(j&4=0)
Right or left?
      x(3)=x(3)+f+f
Move 2 pixels to the selected side (if any)
      v=2+(j&1=0)-(j&2=0)
Up or down? Selects current speed
      sound 0,f(v),12,3
Engine's sound depents on speed
      m=h
Last state of the button
      h=strig(0)
Current state of the button
      if h-m
Did the state of the button change?
        if h
Changed, and is it now released?
          sound 1
Yes... shut off the horn
        else
No, it was pressed
          sound 1,75,14,8
Makes a beep
          f=rand(3)
Selects a car
          d(f)=-d(f)
        endif
      endif
makes it move to the other side
      o=(o+1)*(o<53)
Increases general counter
      for i=0 to 2
Computes the position for each of the enemy cars
        z=x(i)
Gets current horizontal position of a car
        w=(z<a)-(z>b)
Checks if it has to change the direction because it is reaching a border
        if w
          d(i)=w
        endif
Assigns new direction if it reached to a border
        x(i)=z+d(i)
Computes the new horizontal position of the car
        if c(i)>0
Checks for a delay for the selected car
          c(i)=c(i)-v
delay is based on current speed
        else
No delay
          k=y(i)
Gets current vertical position of the selected car
          if k<104
Still on screen?
            y(i)=k+v
Moves it down
          else
Reached the end
            mset pmadr(i)+y(i),23,0
Removes the car from the screen
            setcolor i-4,rand(15)+1,
              rand(8)+4
Selects the color for the next car
            inc e
Counter for incomming cars
            d(i)=2*rand(2)-1
Selects a direction for the car
            x(i)=a+rand(b-a)
Selects an horizontal starting position for the car
            y(i)=k-104+v
Selects a vertical position near the top, aligned by current speed
            if e>3
Are incomming cars reaching the bottom of screen?
              inc s
Yes, increase score
              dec n
Decreases the pending cars for the round
              sound 2,8,10,6
Makes a "tick" sound
              if n=0
Was the last car of the round?
                s=s+t*t
Yes, add time bonus
                t=9
                n=20
Resets the timer and the cars count for the round
                o=1
Resets the general counter
                inc a
                dec b
                exec l
              endif
Decreases the width of the road
              position 0,4
              print #6,n,
Updates the number of pending cars
              position 0,1
              print #6,color(32) s
            endif
Prints the new score
            exec p i
          endif
Displays the selected car in its new position at the top
          poke 77,0
        endif
Avoid ATRACT mode
        sound 2
Turns off the "tick" sound
      next i
Next car, please!
      poke u,$90+t
Updates the timer
      pause
Syncs with VBLANK to avoid glitches
      move r,$D000,4
      -move pmadr(0),pmadr(0)+v,384
Move cars (horizontally and vertically)
    until peek($D00F) or dpeek($D00A) or t=0
Exit loop if crashed or no more time
    sound
Turns off FX
    v=6
Select a sound for the game over FX
    if t
If there is enough time, it was a crash
      move g+20,pmadr(3)+$53,9
Opens the car hood
      v=0
    endif
Selects a crash sound FX
    for f=0 to 48
Move forward all the traffic
      sound 0,72+f,8+v,8-f/6
Plays the sound FX
      for i=1 to 3
        mset pmadr(i),3,0
      next i
Cleans the top of each enemy P/M area
      pause
Sync with VBLANK;
      move pmadr(0)+3,pmadr(0),384
Scrolls up the P0-P2 area
      if t
        -move pmadr(3),pmadr(3)+1,127
      endif
Scrolls down crashed P3 car
    next f
Repeat...
  until t=0
No more time?
  position 10,4
  print #6,"{binary data}",,
    "{binary data}"
loop
Prints the "GAME OVER" message
proc p i
  pmhpos i,x(i)
  move g,pmadr(i)+y(i),20
endproc
Displays the given car
proc l
  pmhpos 6,a-4
  pmhpos 7,b+18
endproc
Adjusts street borders

Return to my 10-liners page.

© 2023 by Víctor Parada - 2023-02-04 (updated: 2023-04-03)