Minipede

Copyright © 2017 by Víctor Parada and Kevin Savetz

Minipede

This is a little game for the 2017 NOMAM's BASIC 10-liners Contest. This program fits in the PUR-120 category, and was written in TurboBASIC XL for the 8-bits ATARI XL/XE.

UPDATE: It won the 1st place of 19 entries in the category.

Description

Kill the caterpillars before they reach the bottom of the garden. Be careful! They can eat you.

Instructions

MINIPEDE round begins When the game starts, your player is located at the bottom of the garden. There are many mushrooms in it. A caterpillar will appear at the top.
MINIPEDE move Use the joystick to move around in the bottom of the screen and avoid to be eaten by the caterpillar.
MINIPEDE shoot Use fire button to shoot at the caterpillar. Every time you hit it, the caterpillar will shrink a bit. You must hit it 10 times to kill it, and every time it shrinks, it will be more difficult to hit it again.
MINIPEDE-mushrooms Mushrooms appear every time you hit the caterpillar, and when the caterpillar steps down or when it goes away.
MINIPEDE game over The game is over when the caterpillar eats you or when 5 caterpillars go away.

Development of the game

Some weeks ago, Kevin sent me a piece of code for a game he was working on for the BASIC Tenliners Contest. It was a very nice mini version of Centipede. He currently has the algorithm that allowed the caterpillar to move in the screen and both the movement of the player and the shooting routines. But some things were not working well and/or not implemented yet, for example, the splitting routine to get two half caterpillars if you shoot it in the middle. He told me that he was almost abandoning it, but then he wanted me to evaluate if it was possible to do something to continue with the project.

PROTOTYPE single

Single caterpillar prototype

 
PROTOTYPE split

Splitting prototype

After I tried his prototypes a few times and analyze the behavior, I suggested two options:

  1. Play it differently. For example: when a part of the body is hit, it shrinks in one piece and the last one turns into a mushroom.
  2. Allow only 1 or 2 splits. After that, kill the whole split. This is to avoid too much slow down, and it could require a rewrite some part of the code.

Before selecting the first option, I decided to review his code, and I realized that the way he managed the caterpillar movement was great for only one piece, but it won't be able to manage more than one piece after a split, because it would require to juggle with many pointers, and the program was aborting with an ERROR-3 (invalid value error) if you shoot the caterpillar near the head.

To simplify things, instead of trying to modify the code to solve the different issues I identified during some game plays, I decided to rewrite the code from scratch in a way that it could be able to manage many segments at the same time, but trying to preserve the program structure and variable names from his code, to let him resume the work. I sent the new code to Kevin with a working demo with 1 and then 2 caterpillars moving in the screen at the same time.

PROTOTYPE new single

New single caterpillar prototype

 
PROTOTYPE new split

New multiple caterpillar prototype

Both of us coincided that the demo with 2 caterpillars was too slow and not playable, so I let him continue programing the first option: a new gameplay with its own set of rules. He might use his original code or the new one that I wrote, but restricted to only 1 caterpillar at a time and no splitting, just like the first proposed option.

Few days later, I took my demo and I turned array references used to manage multiple segments into normal variables and added a new set of rules I thought about during shower time. Voilá!!! I got a nice minigame in my hands, and I sent it to Kevin to motivate him to finish his game using this aproach. Also, I told him that if a line with debugging code is removed from the version with splitts, it could be fast enough to allow at least one or two splits, and he could try what it was his original idea. Many days later, Kevin told me that he prefered to submit to the contest my finished single caterpillar version I've sent him.

That version fitted in PUR-120 category of the contest, without space for hard changes. Before I submit it, I wanted to test it in real hardware, which took me a while because my Atari remains stored in a plastic box, and I have to mount it on my dinner table only for some hours (and then store it again!). During these tests I found some issues that required changes in the code. One of the simple changes was to adjust the colors for the TV set, but there were others that required to remove another features. For example, I forgot to include a reset for the ATRACT mode register (POKE 77,0) because the screen started this mode and changed the colors while I was playing.

To be continued...

Download and try

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

The code

The abbreviated BASIC code is the following:

The full and expanded BASIC listing is:

graphics 28
Initialize the screen in graphics mode 12+16. This is a ANTIC 4 screen without a text box. It has a resolution of 40x24 characters and allows 4 colors and a background.
dpoke $BB83,$B446
In a standard Atari running TurboBASIC XL, all the memory locations for graphics modes are fixed, so this DPOKE with constant values replaces the following expression:
dpoke dpeek(560)+3,((dpeek(88)+20) mod 256)*256+(6+$40)
This changes the first line from ANTIC mode 4 to mode 6 (text line like in graphics mode 1) and, at the same time, moves the screen data display pointer 20 bytes ahead, as mode 6 has requires only 20 bytes instead of 40. This line will be used for the score and the number of quits of the caterpillars.
s=$BBA0
This is the constant value of screen data pointer for graphics mode 12+16 (incluing those 20 bytes that won't be displayed). It's the same as s=dpeek(88)
move adr("{data}"),708,4
Sets up the screen colors used for sprites and numbers: yellow, white, green and red (PAL). Background remains black.
move $E000,$B000,512
move adr("{data}"),$B008,32
poke 756,$B0
Set up of the modified charset. The 4 elements are: player, bullet, caterpillar and mushroom, 8 bytes each.
for t=0 to 15
  poke s+rand(880)+40,$84
next t
Draws the initial screen, placing some mushrooms ramdomly on it. The playfield is only 40x23 (skipping the first line), and the bottom line will never have a mushroom. Only 40x22=880 places are candidates.
poke s+25,16
poke s+34,85
Displays the initial counters in the first line, each with a different color.
m=10
Max length of the caterpillar.
dim p(m-1)
Array that stores the screen position of every segment of the caterpillar.
x=20
y=3
w=940
poke s+w,1
Initial position on screen of the player. w=x+y*40+800, because the player can only move through the bottom 4 lines of the playfield.
pause 60
Adds a small pause before the start of the game.
r=5
Initial number of caterpillars that could go away.
v=0
Initial score.
g=0
Flag that indicates that the player was hit by the caterpillar.
repeat
Main loop of the game:
  poke 77,0
Resets ATRACT mode.
  t=x<20
  d=-(-1^t)
  e=d
  p(0)=39*t
Initialize the caterpillar. Depending of the current horizontal position of the player, the caterpillar will start the extreme top at the other side. Only the position of the head is initialized and the moving direction of it. Actually, both possible positions are in the first line of the screen data but never displayed over the score line, because, at the first move, the caterpillar will step down to the first displayed line of the playfield.
  h=0
  l=10
Sets the head position in the array and the length of the caterpillar (number of segments) inside the array. As it could be seen, this is the whole array. Remember that only the head position on screen was set up in the array.
  while g=0 and l
Round loop:
    q=p(h)+d
Finds where in screen is the next step. First, try the next horizontal position of the head based in the current movement direction.
    if peek(s+q)>3 or p(h) mod 40=39*(d>0)
Checks if in that position there is a mushroom or if it is already at the border of the playfield.
      q=p(h)+40
      e=-d
Sets the next step one row down and change the moving direction.
      t=s+rand(880)+40
      if peek(t)=0
        poke t,$84
      endif
Places a new mushroom in the playfield (if the randomly selected location is clear) every time the caterpillar moves down.
    endif
    c=peek(s+q)
Retrieves the current element at the next head position.
    g=c=1
Sets a flag if the player is at that position. If so, the caterpillar ate the player.
    a=(h+m-1) mod m
New index in the screen position's array for the head. It'll be one less tha the previous, and wraps to the end of the array if it is already at the first one.
    b=(a+l) mod m
Current index of the tail for it's screen position.
    if p(b)
      poke s+p(b),0
      p(b)=0
    endif
Remove the tail segment from screen, if it was already put. Then cleans the register in the array.
    if q<960
      poke s+q,3
      p(a)=q
      h=a
      d=e
It adds the new head segment at the new position of the screen and records that position in the array. If the caterpillar has the full length, it'll be the same index as the released by the tail. This only happens if the new position is inside the playfield...
    else
      l=l-1
      r=r-(l=0)
      poke s+34,r+$50
    endif
If the new head position is out if the playfield, the length of the caterpillar is reduced by one, the head remains in the same place and decreases the number of lost rounds if it was the last piece of the caterpillar.
    sound 0,255,12,8
Starts the stepping sound in channel 0.
    c=c<>2
Sets a flag for the bullet routine that indicates if the caterpillar hit the bullet in it's last movement.
    t=stick(0)
    j=x+(t&8=0)*(x<39)-(t&4=0)*(x>0)
    k=y+(t&2=0)*(y<3)-(t&1=0)*(y>0)
    z=800+j+k*40
Calculates the new location of the player based in its current location and the joystick position.
    if g=0 and peek(s+z)=0 and w<>u
      poke s+w,0
      poke s+z,1
      w=z
      x=j
      y=k
    endif
If the new location on screen is empty, this moves the player and sets its new location as the current one.
    sound 0,0,0,0
Shuts up the stepping sound in channel 0.
    if u<40
Checks if the current location of the bullet is out of the screen, enabling the player to fire.
      if strig(0)=0
        u=w
        sound 1,0,8,15
If the fire button is pressed, sets the current location of the bullet at the player and starts a shot sound.
      else
        sound
      endif
    endif
Shuts up the sound if the bullet is not on screen.
    n=0
The bullet will step up two times on each game cycle. This initializes a counter.
    while u>=40 and n<2
If the bullet is located in the playfield and this is one of the two times:
      sound 1,240-u/5,8,u/80
Changes the shot sound based on the bullet's screen position.
      if c and u<>w
        poke s+u,0
      endif
Remove the bullet form its current location.
      u=u-40*c
Sets the current location of the bullet one position up, unless it already was hit by the caterpilar's head in the last movement.
      if u>=40
        t=peek(s+u)
If the new location of the bullet is still inside the playfield, chech first if there is another thing there.
        if t>3
If there is a mushroom:
          sound 1,10,10,3
Turns the shot sound by a hit into a mushroom sound.
          poke s+u,0
Removes the mushroom from the playfield.
          v=v+1
Increases the score.
          u=0
Disables the bullet.
          position 25,0
          ? #6;v
Prints the new score.
        else
          if t=3
If there is a piece of the caterpillar:
            sound 1,30,12,15
Turns the current shot sound into a caterpillar hit sound.
            v=v+(m-l+1)*5
Increases the score based on the current length of the caterpillar.
            b=(h+l-1) mod m
Finds the index in the positions array for the current tail segment.
            poke s+p(b),$84
Replaces the tail by a mushroom in the playfield.
            p(b)=0
Clears the tail location in the array.
            l=l-1
Decreases the length of the caterpillar.
            u=0
Disables the bullet.
            position 25,0
            ? #6;v
Prints the updated score.
          else
If nothing was hit:
            poke s+u,2
          endif
Draws the bullet in its new location.
        endif
      endif
      n=n+1
Increases the iteration counter for bullet movement during the game cycle.
    wend
End of the bullet movement loop.
  wend
End of the round loop.
until r=0 or g
End of the game loop. It finishes when the too many caterpillar quit or when the player was eatten by a caterpillar.
sound 1,99,2,8
Starts a buzzer.
color 164
text 4,4,"GAME"
text 4,11,"OVER"
Displays a "game over" message.
pause 30
sound
Shuts up the buzzer after a short moment.
get k
Waits for a key press from the player.
run
Restarts the game.

Return to my 10-liners page.

© 2017 by Víctor Parada - 2017-03-31 (updated: 2017-04-23)