Copyright © 2017 by Víctor Parada and Kevin Savetz
This is a little game for the 2017 NOMAM's BASIC 10-liners Contest. This program fits in the PUR-120 category, and it was written using TurboBASIC XL 1.5 for the 8-bits ATARI XL/XE. Development started on 2017-02-12, and it took 3+2 days with Kevin Savetz. The final version's date is 2017-03-31.
UPDATE: It obtained the 1st place of 19 entries in the category.
Kill the caterpillars before they reach the bottom of the garden. Be careful! They can eat you.
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. | |
Use the joystick to move around in the bottom of the screen and avoid to be eaten by the caterpillar. | |
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. | |
Mushrooms appear every time you hit the caterpillar, and when the caterpillar steps down or when it goes away. | |
The game is over when the caterpillar eats you or when 5 caterpillars go away. |
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.
Single caterpillar prototype
Splitting prototype
After I tried his prototypes a few times and analyze the behavior, I suggested two options:
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.
New single caterpillar prototype
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.
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 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: 2020-08-15)