Copyright © 2025 by Víctor Parada
This is a little game for the 2025 NOMAM's BASIC 10-liners Contest. This program fits in the EXTREM-256 category, and it was written using FastBasic 4.7 for the 8-bits ATARI XL/XE computers line. Development started on 2024-10-31, and it took 3+5+3 days. The final version's date is 2025-03-26.
You are an intruder humanoid that must escape from the maze destroying all the guardian robots.
![]() |
Press the button to start. |
![]() |
Use the joystick to move around the maze and reach any of the room exits. There are robots trying to chase and destroy you. |
![]() |
Use the trigger to shoot to any robot in your way out. You will score 10 points for each destroyed robot. You can perform only one shot at a time. Beware! Robots can dodge shots towards their legs. |
![]() |
If there are no more robots in a room when you leave it, you will receive a bonus of 100 points. But more robots will chase you in the next room. |
![]() |
If you stay in a room for too much time, the evil smiling boss will came to chase you. He can't be killed. |
![]() |
Don't touch the electrified walls, or you will die just like as a robot chased you. |
![]() |
You have three lives. The game is over when you loose all of them. Press the trigger to play again. |
In the search for classic games that had not been ported to a tenliner, I found Berzerk. I thought that it could be really simple to code it and gave it a try. My idea was to use both a modified character set and player/missile sprites for static and moving robots respectively.
My first attempt was to use the sprites from the Atari 2600 version of the game, because the robots could fit ANTIC 7 chars (GR.2) and it could very easy to animate their tracking eye. But I realize there was a problem: it could be hard to draw the maze using that screen resolution, because I should have to manage all 15 posible combinations of wall junctions for random mazes.
Original Berzerk arcade game.
Port of Berzerk for Atari 2600 console.
Trying to solve that, I changed the graphics mode to ANTIC 4 (GR.12), using the whole character as a block for the walls. PLOT and DRAWTO could be used to draw the maze. But, each robot required a tile of 4 chars, and each scan line should be doubled, using more bytes for the redefined charset in the source and wasting some valuable bytes for coding. The good thing is that the tiles for the robots can be placed half the way, giving a less grid-like look of the playfield.
In three days I had a playable prototype of the game in FastBasic, with some animations for the player and the robots. I could replicate the maze generation algorithm, but I did not use a seed for random numbers. Instead, I used the random number generator from the POKEY chip through the RAND() function.
Test using tiles in ANTIC 4 mode.
Robots move smoothly towards the player.
Then, the headache started: I faced a bug while testing, but I couldn't find where in the code it was, because it appeared few times during a lot of hours playing and tracing and debugging. After a couple of days I got frustrated and suspended the project. A couple of weeks later, with a fresh mind, I gave it a new try, added more tracing code but, again, I couldn't find what was going on and dropped the project.
Tracing to find the bug.
Some months later, it was opened the call for the 2025 tenliners contest and I had no fresh ideas. I reviewed some of the many dropped prototype games I've written through the years and this came in front of them. I played it a few times, removed many of the trace code and sat down to think. I was sure there was a border condition that triggered the bug, and it could be related to the hardware registers for collision detection and when the events happens during the screen refresh. If this was true, I could probably be detecting a bit late a collision of the bullet with a robot that it is no longer there. To solve this without adding some extra PAUSE statements to force a screen refresh with the cost of a slower game, I might change the whole game logic.
With less than 3 weeks for the deadline, I decided to rewrite the game logic. It was required to add many new variables to precompute and hold temporary data in order to perform many actions quickly and get the full screen refreshed in just one frame. This was good and bad at the same time: The bug disappeared, but the cost was less coding space because most of the required extra variables were of two letters. I did some statistics of variable usage and reassigned them in order to keep the most used ones as a single letter variable. Anyway, the coding space was almost exhausted and many of the pending features should be discarded and/or some existing features removed to make room for the most important ones.
New code and new color palette.
I added some sound FX, defined the final color palette, restricted and/or released game dynamics and made them all fit the 10 lines of code. After some days, I was satisfied with the result, even when I couldn't include some of the features I wanted to. The game was hard enough in order to make it even harder!
While cleanning and commenting the code for this page, I decided to change the name of the game. Instead of "Berzerk", during a night call with friends from the retro community, I told them that the game I was working on would be called "Krezreb", and it remained in the source code until it was finished. But one of the features I couldn't include was a background sound routine to mimic the "Intruder alert, intruder alert! The humanoid must not escape!" digitized speechs from the original game. Then I thought about a new game name based on those speechs, and some options were "Intruder" and "Intruder humanoid", but "Intruder alert!" was selected.
Get the INTRUDER.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.
The abbreviated BASIC code is the following:
The full and expanded BASIC listing is:
|
Intruder Alert! - KREZREB - BERZERK (c) 2024-2025 Victor Parada G. |
o=adr("{binary data}")+14 |
Binary data Outlined player (13 bytes) Frozen player (right) (13-1 bytes) Walking player (right) (13-1 bytes) Frozen player (left) (13-1 bytes) Walking player (left) (13-1 bytes) Walking robot bitmap (19 bytes) Color palette (9-2 bytes) Otto bitmap (16 bytes) Gates locations (4 bytes) Border walls locations (12 bytes) Maze walls locations (8 bytes) Otto jumping height (10 bytes) Animation of standby robots eyes (40 bytes) Horizontal coordinates of entry points (4 bytes) Vertical coordinates of entry points (4 bytes) Robot bitmaps (32 bytes) Playfield blocks (16 bytes) |
graphics 28 |
Graphics mode 12+16 (40x24 in ANTIC mode 4) |
pmgraphics 2 |
Double line resolution P/M graphics |
move o+67,704,9 |
Set color palette |
dpoke dpeek(560)+27,7 |
Enable score line by changing bottom two lines by one ANTIC 7 line and a blank scan line |
move $E000,$8000,$400 move o+172,$8008,48 poke 756,$80 |
Redefine charset |
dim r(9),p(800),oh(9) byte,re(19), wx(3) byte,wy(3) byte |
R(): Position of each robot P(): Lookup table; tells which robot is at a given screen position (-1=none) OH(): Otto bouncing height RE(): Sequence of bitmaps for the animation of the robot's eyes WX() and WY(): Coordinates of the 4 entry points of the playfield for the Player and Otto |
move o+114,adr(oh),58 |
Populate array tables |
s=dpeek(88)+42 |
Address of Playfield |
o1=o+74 |
Pointer to Otto's bitmap |
position 3,22 print #6,"INTRUDER ALERT" |
Print title |
data _d()=0,7,0,-7 |
Drawing direction for walls X=Up,Right,Down,Left Y=Left,Down,Right,Up |
do |
Main loop |
while strig(0) wend |
Wait for the trigger to start the game |
w=3 |
Initial position of the player |
m=1 |
Number of robots |
li=3 |
Number of lives |
sc=0 |
Score |
position 3,22 print #6,3 rtab(17) sc |
Print initial lives and score |
repeat |
Game loop |
poke 77,0 |
Disable ATRACT mode |
sound |
Turn off every sound |
mset pmadr(-1),$280,0 |
Clear all P/M bitmaps |
mset dpeek(88),880,0 |
Clear the playfield |
mset adr(p),1602,$FF |
Clear the robots lookup table |
t=0 for q=0 to 3 |
Draw the opened and closed paths |
color 37+129*(w<>q) exec _m peek(o+90+q) next q |
Use a different color for closed gate |
color 37 for n=0 to 19 sound 0,n,2,n/2 |
Draw the walls, first 12 segments are surrounding walls (fixed), last 8 are center ones (random direction) |
if n>11 then t=rand(4) |
Select a random direction |
exec _m peek(o+94+n) next n sound |
Draw a wall segment |
n=0 while n<m |
Add M robots to the playfield |
c=rand(15) |
Select a zone |
if (c-(c>7)) mod 3<2 |
Check that it is not one of the gate zones |
c=40*(rand(5)+c/5*7)+rand(5)+c mod 5*7 |
Select a random place inside the selected zone |
if dpeek(c+s)+dpeek(c+s+40)=0 |
Check that there is space for a robot tile |
exec _r c,1 |
Add a robot |
r(n)=c |
Save position of the robot |
p(c)=n |
Add the robot to the lookup table |
inc n endif endif wend |
Increment the number of robots |
t=0 |
Global timer (0-9) |
g=0 |
Moving robot steps counter |
x=wx(w) |
id of the robot that is moving Horizontal coord of player |
y=wy(w) |
Vertical coord of player |
u=0 |
Horizontal coord of moving robot |
v=0 |
Vertical coord of moving robot |
h=0 |
Robot's moving axis (0=horizontal 1=vertical) |
i=0 |
Robot's moving direction (0=no robot is moving) |
f=0 |
Fire flag |
dx=1-2*(w=1) |
Horizontal speed of player when moving |
dy=0 |
Vertical speed of player when moving |
gx=0 |
Horizontal movement of the shot |
gy=0 |
Vertical movement of the shot |
d=0 |
Horizontal position of the shot |
e=0 |
Vertical position of the shot |
l=0 |
Current/next action of the moving robot |
oc=500 |
Back-counter for Otto to appear |
k=0 |
Shooting sound FX counter |
z=0 |
Robot explosion FX counter |
repeat |
Round loop |
t=(t+1) mod 10 |
Update global timer |
dpoke $800A,re(t) dpoke $801A,re(t+10) |
Rotate robot's eyes |
j=stick(0) |
Move the Player? |
if j<15 |
If the joystick was moved... |
sound 0,j,8,2 |
Walking sound |
dx=(j&8=0)-(j&4=0) dy=(j&2=0)-(j&1=0) x=x+dx y=y+dy |
Compute new deltas and player coordinates |
sound 0 endif |
Walking sound (step 2) |
if oc |
Evil Otto |
dec oc |
Update coundown |
if oc=0 |
Release Otto? |
ox=wx(w) oy=wy(w) endif |
Set staring point of Otto |
elif t&1 |
Otto is active, but moves every two frames |
ox=ox+sgn(x-ox) oy=oy+sgn(y-oy) |
Compute new psosition towards the palyer |
sound 2,200,12,9-t endif |
Boing sound FX |
if g dec g if h |
Is there a walking robot? |
v=v+i else |
Vertical |
u=u+i endif |
Horizontal |
if g=0 |
Reached the robot its destination? |
p(r(q))=-1 |
Remove the robot from the old location pointer |
r(q)=r(q)+i+39*i*h p(r(q))=q |
Set Tile at new position |
if rand(3) then exec _w |
Continue moving? |
if g=0 then l=3 endif |
No direction? Remove the P/M of the robot on the next frame |
elif l=0 and n |
Select a robot to move |
q=rand(n) |
Select a robot |
u=r(q) mod 40*4+56 v=r(q)/40*4+20 |
Compute P/M coordinates of that robot |
exec _w |
Select a direction to move the robot |
if g then l=1 endif |
If the selected direction is free, enable the movement |
if z dec z sound 1,200-4*z,6,z endif |
Perform explosion sound FX |
if f |
Shooting? |
sound 1,40-3*k,6,k k=k-(k>0) |
Shooting sound FX might not last the whole path of the bullet |
d=d+gx e=e+gy |
Update position of the shot |
else |
The shot is not active |
if strig(0)=0 |
Is the trigger pressed? |
gx=2*dx gy=2*dy |
Same direction than the player but a double speed |
d=(x+7-6*(dx<0))&$FE e=(y+5)&$FE |
Fire in even position |
if gx |
Bitmap of the projectile |
fz=3 else |
Horizontal Bitmap (also valid on diagonals) |
fz=$202 endif |
Verical bitmap |
k=9 |
Enable shooting sound FX |
f=1 endif endif |
Shooting is now active |
|
Precompute element positions |
p1=o+12*(j<15)*(t&1)+24*(dx<0) p2=pmadr(0)+y-1 |
Player |
o2=pmadr(2)+oy-oh(t) |
Otto |
b1=pmadr(-1)+e b2=b1-gy |
Missile |
r2=pmadr(1)+v-1 r3=o+48+9*t&1 |
Robot |
pause |
Wait for the next frame |
poke $D01E,0 |
Reset hit registers |
|
Move elements in screen |
pmhpos 0,x move p1,p2,13 |
Draw player |
if f pmhpos 4,d dpoke b2,0 dpoke b1,fz endif |
Draw the missile |
|
Draw a walking robot |
if l=1 exec _b exec _r r(q),0 l=2 |
Convert tile into P/M |
elif l=2 exec _b |
Move P/M robot |
elif l=3 exec _r r(q),1 mset r2,10,0 l=0 endif |
Convert P/M robot back to a tile |
if oc=0 move o1,o2,16 pmhpos 2,ox endif |
Draw Evil Otto |
pause |
Wait for the frame to be displayed |
fk=peek($D000)+peek($D008)&2*8 if fk |
Did our shot hit something? |
sound 1 |
Shut up the shooting FX |
dpoke b1,0 |
Remove shot from screen |
if fk&$10 |
Hit a walking robot? |
l=0 |
Disable robot animation |
g=0 |
No robots are walking now |
mset r2,10,0 |
Remove bitmap of the robot |
c=q |
Last walking robot |
elif fk&4 |
Hit a standby robot? |
a=(d-56)/4+(e-20)/4*40 |
Find contact byte of the tile |
b=a-peek(adr("{binary data}")+peek(s+a)) |
Adjust to upper left byte |
c=p(b) |
Identify the robot number |
exec _r r(c),0 p(b)=-1 endif |
Remove robot |
if fk&$14 p(r(c))=-1 |
Hit a robot? |
if n>1 and c<n-1 r(c)=r(n-1) p(r(c))=c |
If the removed robot is not the last in the list, move the last one to fill the gap |
if q=n-1 then q=c endif |
If the moving robot was the last of the list, update its position in the list |
dec n |
One robot less |
exec _s 1 |
Increase the score |
z=12 |
Enable explosions sound FX |
if n=0 then oc=oc/4 endif |
If there are no more robots, force Otto to appear earlier |
f=0 endif |
Disable shot |
ss=peek($D004)+peek($D00C)*16 until ss |
Check for player collision |
if ss&$F7 |
Killed? |
for a=0 to 14 sound 0,9,2,a move o-13*a&1,pmadr(0)+y-1, 13 pause sound pause next a |
Animate the electric shock of the player |
mset pmadr(0)+y,13,0 |
Remove the player bitmap from screen |
dec li |
Decrease lives |
position 3,22 print #6,li |
Print new number of lives |
else |
Got a pathway |
w=(x>wx(1))*3+(y<wy(0))*2+(x<wx(3)) |
Select the next entry point |
m=m+(m<10) |
Increase the number of robots for the next room |
if n=0 then exec _s 10 endif |
Bonus for killing all the robots in the current room |
until li=0 |
Game ends when there are no more lives |
loop |
Restart game |
proc _s a sc=sc+a position 5,22 print #6,rtab(16) sc endproc |
Increase and print the score. There previously is a fixed trailing zero on screen. |
proc _m c |
Draws a wall segment C bits: - 7: unused (0) - 6-5: direction - 4-0: 24 spots of the playfield (6x4) |
a=c&31 mod 6*7+2 |
Horizontal coord of starting point |
b=c&31/6*7 |
Vertical coord of starting point |
c=c/32+t plot a,b drawto a+_d(c),b+_d(3-c) endproc |
Direction to draw a segment |
proc _w |
Check if current robot can move towards the player |
a=x-u+1 b=y-v-2 |
Compute deltas to the player |
if rand(5)=0 or rand(3) and abs(a)>abs(b) |
Select (most of the time) the axis where the robot is far from the player |
h=0 i=sgn(a) a=s+r(q)+i+i*(i>0) |
Horizontal axis |
if peek(a)!peek(a+40)=0 then g=4 else |
Nothing is blocking, prepare to move |
h=1 i=sgn(b) |
Vertical axis |
if dpeek(s+r(q)+40*(i+i*(i>0)))=0 then g=4 endif endproc |
Nothing is blocking, prepare to move |
proc _r a b dpoke s+a,b*$301 dpoke s+a+40,b*$402 endproc |
Add or remove a robot tile |
proc _b pmhpos 1,u move r3,r2,10 endproc |
Draws a moving robot |
Return to my 10-liners page.
© 2025 by Víctor Parada - 2025-03-26 (updated: 2025-03-30)