skip to content

JavaScript: Amazing Maze Game

 Tweet Share0 Tweets

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 this version 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.

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🐊 🐍
Treasure🌼 🍄 🌻 💎 🌲
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: www.chirp.com.au // 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() { // bind to HTML elements this.mazeContainer = document.getElementById("maze"); this.mazeOutputDiv = document.getElementById("maze_output"); this.mazeMessage = document.getElementById("maze_message"); this.mazeScore = document.getElementById("maze_score"); this.heroScore = this.mazeContainer.getAttribute("data-steps") - 2; this.maze = []; this.heroPos = {}; this.heroHasKey = false; this.childMode = false; 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"); } } } this.mazeOutputDiv.style.width = this.mazeContainer.scrollWidth + "px"; this.setMessage("first find the key"); // activate control keys this.keyPressHandler = this.mazeKeyPressHandler.bind(this); document.addEventListener("keydown", this.keyPressHandler, false); } Mazing.prototype.setMessage = function(text) { this.mazeMessage.innerHTML = text; this.mazeScore.innerHTML = this.heroScore; }; 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 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) { 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"); };

expand code box

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"> <!-- insert the maze HTML code here --> </div> <div id="maze_output"> <div id="maze_score"><!-- --></div> <div id="maze_message"><!-- --></div> </div> <script src="/mazing.js"></script> <script> window.addEventListener("DOMContentLoaded", function(e) { var maze = new Mazing(); /* 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.

< JavaScript

Send a message to The Art of Web:


used only for us to reply, and to display your gravatar.

<- copy the digits from the image into this box

press <Esc> or click outside this box to close

Post your comment or question
top