top of page

3D Monster Maze Dissected

Introduction

3D Monster Maze for the ZX81 was a game that had a huge effect on me for a number of reasons. Back in 1981at the tender age of 9 I'd never seen anything like this before.

 

It had atmosphere, scares, excitement and of course 3D graphics... all accomplished with black and white low resolution pre-defined characters and no sound. When I started developing apps for the iPhone I was determined to create a tribute game to bring that original experience to life on a modern platform.

 

There are of course emulators and there's even an online Javascript one that will work on tablets and smartphones. However, I wanted to re-create the game not write an emulator. I had a couple of ideas for enhancements too and yet wanted it to be as authentic as possible. I knew the game very well, but to accurately re-create the experience I knew I had to go deeper than playing it. I had to analyse and study the code itself.

 

I have decided to share the results of my analysis here. I hope you enjoy!

Code Architecture

The game itself is a mixture of BASIC and machine code throughout. This means that the introductory scrolling text with the clown, the maze generation, main game loops and end game screens all use both.

 

It appears to me that Malcolm Evans favoured BASIC and only used machine code where it was required for speed. Since this was actually his first commercial game that makes sense.

 

The BASIC element of the program contains 124 lines of code of which the first is a REM statement containing the machine code portions of the game. This appears as gobbledygook in the first line of the program when viewed using LIST.

 

The BASIC and machine code sections pass parameters to each other using PEEK and POKE statements.

Maze Generation

When playing the game, the maze seems huge. In reality it's actually 18 x 16 spaces only. Generation of the maze occurs during the "mists of time" with the machine in FAST mode and takes around 30 seconds on the ZX81. The maze is mapped in memory and appears as shown below.

The maze generation logic is simple yet effective and results each time in a maze which is different yet similar in appearance to the above having many criss-cross passages and openings. This has the effect of making it easier for Rex to find you and keeping the maze difficult to solve and seem bigger than it is.

 

Maze generation starts by filling all tiles with solid wall. It then randomly cuts passages by choosing a direction (north, south, east or west) and a length (1 to 6). Each passage being cut starts at the end of the last one. The first passage always starts at the player start position (shown on the above map as "P"). The player start position is always in the same place at the far south-east of the map, facing west.

 

Logic makes sure that the maze never contains a passage more than one tile wide. It does this by never allowing a block of 4 empty tiles all together like this:

The only other rule the passage cutter observes is that it cannot cut into the western, northern or southern most walls. The wall to the very east of the maze gets cut into, but due to the wrap around nature of way the map is stored in memory the western most wall is also the eastern most wall so in effect the maze is always surrounded in a solid unbroken wall on all sides.

 

The code repetitively cuts passages until it has attempted to cut 800 tiles, then it considers the maze complete. There is no other code to ensure the maze is satisfactorily completed, it's just assumed that after 800 tiles it should be. The maze only contains 240 cuttable tiles so the figure of 800 assumes re-cutting of existing passageways or abandoned passage cuts due to hitting the edge or the previously descibed "no blocks of 4" rule.

 

Once the maze generation is complete, the game chooses a location for the exit. It does this using a somewhat clumsy and chaotic approach. Within the northern most 7 rows it chooses a tile at random. If this tile is a piece of wall, it chooses another. This continues until it finds an empty tile and there it places the exit (shown as "H" in the above maze). To ensure the exit is always at the end of a cul-de-sac (dead-end) it then fills any empty spaces around the chosen tile until only 1 remains. It does this in the priority order north, east, west and south.

 

Unfortunately, due to the crude logic used to place the exit, it sometimes occurs that the exit ends up in  a sealed-off tunnel and the game cannot be won. There is no code to prevent this and although rare, it happens.

 

Finally, the Rex is placed randomly in the northern most 5 rows. Note that when the game ends after Rex eats the player, Rex will be placed randomly in the northern most 13 rows and can therefore start off a lot closer to the player. Due to the above bug that sometimes creates a sealed off tunnel, it's possible that Rex ends up inside it and can never catch you.

Maze Rendering

The maze is rendered on-screen by a machine code subroutine. It draws up to 5 paces ahead depending on what lies ahead of the player in the maze. The screen is effectively divided into 6 segments as shown:

The outermost segment represents what is beside the player on their current tile. This will be wall or tunnel and is black or grey respectively. Each of the subsequent inner segments then represent 1 to 5 spaces ahead. If there is a solid wall less than 5 steps ahead then this occupies the full segment square. Interestingly, I experimented with the rendering code by allowing passages wider than 1 tile. It does look to me like Malcolm Evans originally intended support for this but perhaps never finished it as it seems to partially work.

 

The rendering is quick and, considering the crudity of the old ZX81 graphics, highly effective. 

 

If the exit is in the player's line of sight it will be rendered as a swirling pattern of random characters. A simple idea and yet again very effective. The characters are randomly chosen in the BASIC part of the game loop and POKEd into memory for the machine code subroutine to render as shown:

Rex

If you have fond memories of playing this game then you may be forgiven for thinking Rex is somewhat smarter and more stealthy than he actually is.

 

In reality, the code that controls him is simple. But like many parts of this game, simple has worked and successfully carried off a perception of much greater complexity.

 

Rex moves towards the player at every move. The criss-cross open nature of the maze lends itself to this method of hunting the player down.

 

When deciding which way to go, the dinosaur always attempts to move directly towards the player regardless of whether it has line of sight or not. It chooses a direction according to one of two sets of priorities, chosen randomly each move.

 

Set 1 is west, east, south then north.

 

Set 2 is south, north, west then east.

 

The above random helps to make the dinosaur less predictable. However, it does mean that Rex sometimes gets stuck in a corner or dead-end and cannot move towards the player. When this happens, it stops and doesn't move at all. In a published interview, Malcolm Evans said that he wanted the Rex to move more intelligently than the random movement of the Pacman ghosts. Ironically, the Pacman ghosts are considerably more sophisticated in their movements than Rex.

 

Since Rex always tries to move towards the player, it means that the game need only ever draw it facing you. You cannot sneak up on Rex or see him from any other angle. The game has no graphics for rendering him in any other orientation.

 

Like everything else in the game, Rex can only be seen 5 tiles away or less. The approach of Rex is rendered as shown:

As you can see, there are actually 10 frames of animation for the approaching dinosaur. This is actually another neat trick that the game employs. Since the dinosaur moves at half the speed of the player the game chooses alternate dinosaur images to display each frame. This has the effect of making the dinosaur take ten half-tile steps towards you rather than 5 full tile steps which would probably look somewhat like 5 big jumps.

 

If the dinosaur comes out of a side tunnel to appear in front of you, there is no side-on animation and he just suddenly appears in his full glory facing you head on.

 

After seeing the largest of the above animation frames, it's too late to run away and the game is lost. The next screen frame shown is then as shown:

Ok so I added the text as a little joke! :-)

 

The Rex himself is stored in memory using a rudimentary data compression scheme, similar to run-length encoding, which actually means that white-space is skipped. This means that when decompressed onto screen, the character positions which would be empty spaces are left alone. The maze is rendered first, then the Rex is rendered over the top of it, which effectively super-imposes him into view.

 

The same data compression (or perhaps better named compaction) is employed for the tooth filled screens you see when you've just been eaten.

In Game Messages

One thing that really adds to the tension and suspense of the game is the messages that pop up on screen as you play. These are displayed according to a number of simple rules.

 

REX LIES IN WAIT - this appears at the start of the game before you move and will stay there until you move. It also appears whenever Rex is stuck and cannot move towards the player.

 

HE IS HUNTING FOR YOU - this indicates that Rex is more than 9 tiles away from the player as the crow flies.

 

FOOTSTEPS APPROACHING - this is shown when Rex is less than 7 tiles away from the player as the crow flies.

 

RUN HE IS BESIDE/BEHIND YOU - this is shown when Rex is less than 3 tiles away from the player.

 

REX HAS SEEN YOU - this appears when Rex has a direct line of sight to the player, regardless of how far away he is. This changes nothing in the game and he still behaves the same way. 

 

Whilst the dinosaur is visible on screen, the game never shows any messages.

Enhancements

After all the analysis I enjoyed re-writing the game as a tribute.

 

I added enhancements to give you bearings to the exit, distance to Rex and some additional messages to add to the game's atmosphere. If you play it I hope you enjoy them.

 

All in all, I have loved this game for over 30 years. I've played it on the original hardware, emulators and I've played other re-makes of it. Although it's not a game you're likely to play for hours on end, it is truly a classic and I think the best game for the ZX81 ever.

 

Thanks for reading, enjoy the game however you play it!

bottom of page