We thought it might be tough to make this a fun game on such a limited device so we decided to keep things simple. With only 25 red LEDs it was tough to think how we could show the various elements of the maze. We decided that:
- The player would be a very bright pixel and always in the middle of the screen. The rest of the maze would scroll around you.
- Walls would be dim pixels.
- Doors would be slowly flashing pixels.
- Keys would be rapidly flashing pixels.
- The exit would be flashing in some cool way that made it look enticing.
- Son's first idea was to tap the A button to move left and hold it to move down. Similarly, tapping the B button would move right while holding it would move up. This worked out pretty well and was very accurate but ultimately felt slow and sometimes we just got confused and moved in the wrong direction!
- Next up we tried using the micro:bit's accelerometer as a tilt controller: tilt the unit up, down, left or right to move. We added a delay so that movement didn't repeat too quickly and we set a minimum tilt amount so that you didn't move around when you were holding the device level. This method worked pretty well but it was too easy to accidentally move when you didn't want to. We added a sensitivity setting which let us tweak how far you needed to tilt the micro:bit before movement was triggered but no setting felt right - they were either too sensitive (leading to accidental movement) or not sensitive enough (meaning that you had to tilt so far that you couldn't comfortably see the display).
- Finally we tweaked the second method ever so slightly by adding a button press - you now had to tilt the micro:bit and press the A button to move. This felt much better. Not as satisfying as method 2 but much more accurate.
The code is probably the most complex thing we've written so far. Here it is in full:
There's one interesting thing about this project - we started to hit the limits of the micro:bit memory. We will expand on what this means (and how to scrounge more memory!) in a future post but suffice it to say that, for now, we worked our way around the limits. Probably the most interesting technique we used to save memory is the one that deals with changes to the map. The map is represented as an array of letters (which we call map blocks), so 'w' represents a wall block, 'k' is a key, ' ' is for a space that we can walk on etc.. When the player picks up a key we ned to erase it from the map. Python is great at this and we could have simply made a new copy of the map with the key removed, then swap the maps over. This would work, but requires (at least) twice as much memory - that's too much. The solution is quite neat - we never remove blocks from the map, meaning that we never need to copy the map! Instead we maintain a list (called map_blocks_cleared) of all the blocks that we've "erased". Whenever we try to read from the array of map blocks we first look in this list and if the block we're looking for is in there then we return ' ' instead of what's really there. We wrap all of this functionality up in get_map_block() so that's very easy to query map blocks.
The Python and hex file are here:
Don't forget that you can just copy the hex file to your device to run it. To play the game tilt the micro:bit in the direction you wish to move and press A. You're looking for the exit (a crazy flashing/strobing pixels) and you'll need to collect keys (rapidly flashing pixels) to open doors (slowly flashing pixels).
Once you've completed the game, how about looking at the source code and figuring out how to make your own maze? Please share your creations in the comments if you do!