JavaScript: Amazing Maze Game
As an experiment in coding we've created a maze generator in PHP, and now a JavaScript add-on to convert it into an interactive game for your enjoyment. Merry Xmas.
Explore the maze
Make your way through the maze by pressing the arrow keys. Take the shortest route and avoid monsters or your score will zero out and the game will end.
Gather treasure along the way to earn bonus points, and don't forget you will need the key 🔑 in order to unlock the exit door and escape.
Your starting score is the minimum number of steps it will take to fetch the key and complete the maze. If you find it too easy, try at a larger size.
Maze generation
We have created a PHP class to make mazes based on a recursive division algorithm, with a bit of embellishment.
The mazes can be any size and either square or rectangular. The key is automatically placed at the furthest point from both the entrance and exit.
If you view the HTML source code you will see that the markup is nothing more than an array of DIV elements with class attributes.
<div id="maze">
<div><div class="wall"></div><div class="wall"></div><div class="wall"></div>...</div>
<div><div class="wall"></div><div></div><div class="wall"></div>...</div>
...
</div>
There are separate classes for monsters, treasure, the hero, door and key.
Update: we've now released a pure JavaScript maze generator which you can download and run locally.
CSS elements
No graphics were used in creating the maze. The green background is made up of overlapping radial gradients, while the walls instead use linear gradients to create a rough texture.
The colourful icons are all Unicode characters so will vary between platforms:
Monsters | 🐊 🐍 |
---|---|
Treasures | 🌼 🍄 🌻 💎 🎁 |
Door & Key | 🚪 🔑 |
Our Hero | 🚶 🚶 |
Allowing the hero to face in the other direction is achieved using a CSS Transform of scale(-1, 1).
Gameplay
Our JavaScript class (see below) simply accepts the maze grid as presented in the HTML and allows movement by checking the class attribute of the grid element in the selected direction whenever an arrow key is pressed.
We move our hero through the maze by switching the class of the relevant tiles. Similarly when the key or a treasure is taken, we just remove the relevant class attribute.
Source code for mazing.js
// Original JavaScript code by Chirp Internet: chirpinternet.eu
// Please acknowledge use of this code by including this header.
function Position(x, y) {
this.x = x;
this.y = y;
}
Position.prototype.toString = function() {
return this.x + ":" + this.y;
};
function Mazing(id) {
// bind to HTML element
this.mazeContainer = document.getElementById(id);
this.mazeScore = document.createElement("div");
this.mazeScore.id = "maze_score";
this.mazeMessage = document.createElement("div");
this.mazeMessage.id = "maze_message";
this.heroScore = this.mazeContainer.getAttribute("data-steps") - 2;
this.maze = [];
this.heroPos = {};
this.heroHasKey = false;
this.childMode = false;
this.utter = null;
for(i=0; i < this.mazeContainer.children.length; i++) {
for(j=0; j < this.mazeContainer.children[i].children.length; j++) {
var el = this.mazeContainer.children[i].children[j];
this.maze[new Position(i, j)] = el;
if(el.classList.contains("entrance")) {
// place hero at entrance
this.heroPos = new Position(i, j);
this.maze[this.heroPos].classList.add("hero");
}
}
}
var mazeOutputDiv = document.createElement("div");
mazeOutputDiv.id = "maze_output";
mazeOutputDiv.appendChild(this.mazeScore);
mazeOutputDiv.appendChild(this.mazeMessage);
mazeOutputDiv.style.width = this.mazeContainer.scrollWidth + "px";
this.setMessage("first find the key");
this.mazeContainer.insertAdjacentElement("afterend", mazeOutputDiv);
// activate control keys
this.keyPressHandler = this.mazeKeyPressHandler.bind(this);
document.addEventListener("keydown", this.keyPressHandler, false);
};
Mazing.prototype.enableSpeech = function() {
this.utter = new SpeechSynthesisUtterance()
this.setMessage(this.mazeMessage.innerText);
};
Mazing.prototype.setMessage = function(text) {
this.mazeMessage.innerHTML = text;
this.mazeScore.innerHTML = this.heroScore;
if(this.utter) {
this.utter.text = text;
window.speechSynthesis.cancel();
window.speechSynthesis.speak(this.utter);
}
};
Mazing.prototype.heroTakeTreasure = function() {
this.maze[this.heroPos].classList.remove("nubbin");
this.heroScore += 10;
this.setMessage("yay, treasure!");
};
Mazing.prototype.heroTakeKey = function() {
this.maze[this.heroPos].classList.remove("key");
this.heroHasKey = true;
this.heroScore += 20;
this.mazeScore.classList.add("has-key");
this.setMessage("you now have the key!");
};
Mazing.prototype.gameOver = function(text) {
// de-activate control keys
document.removeEventListener("keydown", this.keyPressHandler, false);
this.setMessage(text);
this.mazeContainer.classList.add("finished");
};
Mazing.prototype.heroWins = function() {
this.mazeScore.classList.remove("has-key");
this.maze[this.heroPos].classList.remove("door");
this.heroScore += 50;
this.gameOver("you finished !!!");
};
Mazing.prototype.tryMoveHero = function(pos) {
if("object" !== typeof this.maze[pos]) {
return;
}
var nextStep = this.maze[pos].className;
// before moving
if(nextStep.match(/sentinel/)) {
this.heroScore = Math.max(this.heroScore - 5, 0);
if(!this.childMode && this.heroScore <= 0) {
this.gameOver("sorry, you didn't make it");
} else {
this.setMessage("ow, that hurt!");
}
return;
}
if(nextStep.match(/wall/)) {
return;
}
if(nextStep.match(/exit/)) {
if(this.heroHasKey) {
this.heroWins();
} else {
this.setMessage("you need a key to unlock the door");
return;
}
}
// move hero one step
this.maze[this.heroPos].classList.remove("hero");
this.maze[pos].classList.add("hero");
this.heroPos = pos;
// after moving
if(nextStep.match(/nubbin/)) {
this.heroTakeTreasure();
return;
}
if(nextStep.match(/key/)) {
this.heroTakeKey();
return;
}
if(nextStep.match(/exit/)) {
return;
}
if(this.heroScore >= 1) {
if(!this.childMode) {
this.heroScore--;
}
if(!this.childMode && (this.heroScore <= 0)) {
this.gameOver("sorry, you didn't make it");
} else {
this.setMessage("...");
}
}
};
Mazing.prototype.mazeKeyPressHandler = function(e) {
var tryPos = new Position(this.heroPos.x, this.heroPos.y);
switch(e.keyCode)
{
case 37: // left
this.mazeContainer.classList.remove("face-right");
tryPos.y--;
break;
case 38: // up
tryPos.x--;
break;
case 39: // right
this.mazeContainer.classList.add("face-right");
tryPos.y++;
break;
case 40: // down
tryPos.x++;
break;
default:
return;
}
this.tryMoveHero(tryPos);
e.preventDefault();
};
Mazing.prototype.setChildMode = function() {
this.childMode = true;
this.heroScore = 0;
this.setMessage("collect all the treasure");
};
Usage:
On a page which includes the maze HTML code (which can be copied and pasted from the page source):
<link rel="stylesheet" type="text/css" href="/mazing.css">
<div id="maze_container">
<div id="maze" data-steps="???">
<!-- insert the maze HTML code here -->
</div>
</div>
<script src="/mazing.js"></script>
<script>
window.addEventListener("DOMContentLoaded", function(e) {
var maze = new Mazing("maze");
/* maze.setChildMode(); */
}, false);
</script>
A reminder that you will need to make a local copy of the CSS and JavaScript files rather than hot-linking directly from this website.
Related Articles - Games
- JavaScript Amazing Maze Game
- JavaScript Random Maze Generator
- JavaScript CSS-animated Card Game
- JavaScript Playable Maze Game Generator
- JavaScript Twister Controller
- JavaScript Graphing Game
Send a message to The Art of Web:
press <Esc> or click outside this box to close
Glacxy 17 April, 2022
How do I place the treasures and monsters ? I thought it was part of the javascript. But it does not seem to be there. Please help. THanks
josie 17 November, 2021
Hey, I couldn't find the maze HTML code. Could you please tell me where exactly is it or could you post it? Thank you!
Hrishikesh 2 November, 2021
Hey, thank you so much for this script.
Do you know how to place the monsters using your JS script? At the moment, I can only get the door an key along with the player.
The monsters are not actually placed - they're just wall sections dressed up using a CSS class
Gil 13 December, 2020
Hey I'd also love to see the random generation of the maze if you get it working!
Also...Am I allowed to use this code for my own projects if I credit you?
It must be Christmas: www.the-art-of-web.com/javascript/maze-generator/
Henry Poppelwell 25 February, 2020
I have been following your Java script Maze Game tutorial and it has been amazing.
I cannot, however get it to randomly generate, do you know why that would be?
Thanks,
Henry
We have a PHP script running in the background to generate the maze, and the JavaScript just brings it to life. One day I will get around to converting the PHP script to JavaScript, which makes more sense