Navigate through a randomly generated multi-level maze towards your goal (the checkered flag). When you hit a wall, change the season.
Here's what it's like to play the game (fast-forwarded quite a bit):
On desktop there's some keyboard shortcuts (hidden by CSS until the screen is large enough)... also, keypress events are so cool.
I wrote almost everything in TypeScript - it's easier for debugging than JavaScript, and static types are wonderful.
I initially used jQuery in the View and Character classes because I wanted some practice with the library. Since then I've converted to using vanilla JavaScript.
This was my first time using CSS Grids to handle the layout. (Definitely using grids again... so much nicer than floats!)
I needed a bit of mental gymnastics to get my head around the growing tree algorithm. It's a method of creating a maze out of a grid. Here's the steps.
My grid:
z
: 4 (levels/seasons)y
: 8 cells (rows)x
: 8 cells (columns)
Each cell is an object from the Cell
class.
Cells have cardinal directions (North
, South
, East
, West
, Up
a layer, Down
a layer). Those directions can be true
or false
.
true
: indicates a path that directionfalse
: indicates a wall that direction
For details on each class see the declaration file at: build/maze.d.ts
As a cell is created, it's added to the stack, before the end of this process, it'll be removed.
For this project, I used an array as JavaScript and TypeScript don't explicitly have stacks.
I used z:0 y:0 x:0
.
North
, South
, East
, West
, Up
a layer, Down
a layer
If you can't go that way, (the cell is already full) choose a different direction.
- Add it to the stack of cells.
- Add the cell to the grid.
- Set the current cell's direction to true and the next cell's reverse direction to true
e.g.:
Cell#1
location: grid[0]0][0] -> grid[z][y][x]
North: false
South: false
East: false
West: false
Up: false
Down: false
// direction chosen: South
// Every false will become a wall if it doesn't eventually point at a cell
Cell#1:
location: grid[0][0][0]
North: false
South: true
East: false
West: false
Up: false
Down: false
Cell#2:
location: grid[0][1][0]
North: true
South: false
East: false
West: false
Up: false
Down: false
All of the directions have cells in them.
To backtrack, we pop the last cell off the cell stack and start the process over at step 4.
The number of cells left on the stack will be zero (we backtracked all the way to the beginning.)
See links in Credits to Jamis Buck's work for more details.
// grid[z][y][x] = grid[layer][row][column]
In CSS parlance: top border, east border, south border, west border
location: grid[0]0][0]
North: false = border top
South: true = transparent border (a path)
East: false = border right
West: false = border bottom
Up: false
Down: false
Up and Down aren't used for display in my maze.
I now have the ability to generate procedural mazes. I'll use that to create different levels that users can try.
Next I'm creating a maze solving class (CharacterNavigator) to determine how difficult a maze is to solve based on how many moves it takes to solve.
Currently the maze is marked as "solved" in Character/ICharacterView, logically it makes more sense to have that in Maze, should I change that?
Might want to go to using canvas and making it look good.
This project would not be possible without Jamis Buck and his posts on Maze Generation: Growing Tree algorithm and Minecraft Maze Generator. My algorithmic and display code borrows heavily from his, and lessons learned in his blog posts.
The icons are from Vecteezy, go check them out.