Skip to content

Instantly share code, notes, and snippets.

@XWolfOverride
Last active January 14, 2022 12:41
Show Gist options
  • Save XWolfOverride/dad15edd8d3d966917e713e0f84f2761 to your computer and use it in GitHub Desktop.
Save XWolfOverride/dad15edd8d3d966917e713e0f84f2761 to your computer and use it in GitHub Desktop.
Maze generator and resolvers in JS / HTML
<html>
<head>
<title>Mz</title>
<style>
table {
border-spacing: 0;
border-collapse: spaced;
}
td {
width: 10px;
height: 10px;
box-sizing: border-box;
}
td:hover {
border: 3px solid cornflowerblue !important;
}
html,
body {
margin: 0;
}
#toolbar {
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
font-size: 12px;
font-weight: bold;
background-color: lightgrey;
box-shadow: 0 2px 5px lightgrey;
}
#maze {
margin: 10px;
}
</style>
<script>
ssz = 5;
if (!Object.prototype.merge)
Object.defineProperty(Object.prototype, "merge", {
writable: true,
value: function (src) {
if (!src)
return this;
for (var key in src)
this[key] = src[key];
return this;
}
});
function rnd(max) {
return Math.trunc(Math.random() * max);
}
function Maze(w, h) {
var tbl = document.createElement("table");
if (w > 0 && h > 0 && (w > 1 || h > 1)) {
tbl.c = [];
function cell(x, y) {
if (x < 0 || y < 0 || x >= w || y >= h)
return null;
return tbl.c[y][x];
}
// Build table body
for (var y = 0; y < h; y++) {
var tr = document.createElement("tr");
tbl.c[y] = [];
for (var x = 0; x < w; x++) {
var td = document.createElement("td");
tbl.c[y][x] = td;
td.merge({
x: x,
y: y,
_step: 0
})
tr.appendChild(td);
td.addEventListener('contextmenu', function (ev) {
ev.preventDefault();
tbl.end = this;
stylice();
return false;
}, false);
td.addEventListener('click', function (ev) {
ev.preventDefault();
tbl.start = this;
stylice();
resolveMaze(1);
return false;
}, false);
}
tbl.appendChild(tr);
}
// Create entry/exit and container wall
if (rnd(2) == 0)
tbl.start = tbl.c[rnd(h)][rnd(2) * (w - 1)];
else
tbl.start = tbl.c[rnd(2) * (h - 1)][rnd(w)];
while (!tbl.end || tbl.end == tbl.start) {
if (rnd(2) == 0)
tbl.end = tbl.c[rnd(h)][rnd(2) * (w - 1)];
else
tbl.end = tbl.c[rnd(2) * (h - 1)][rnd(w)];
}
// Maze
function MazeTail(c) {
c.style.backgroundColor = "red";
c._done = true;
var n, d, ttl;
ttl = 20;
while (ttl-- > 0) {
d = rnd(4);
switch (d) {
case 0:
n = cell(c.x, c.y - 1);
break;
case 1:
n = cell(c.x + 1, c.y);
break;
case 2:
n = cell(c.x, c.y + 1);
break;
case 3:
n = cell(c.x - 1, c.y);
break;
}
if (n && !n._done) {
n.style.backgroundColor = "blue";
switch (d) {
case 0:
c._u = n;
n._d = c;
break;
case 1:
c._r = n;
n._l = c;
break;
case 2:
c._d = n;
n._u = c;
break;
case 3:
c._l = n;
n._r = c;
break;
}
MazeTail(n);
}
}
}
MazeTail(tbl.start);
// Stylice table
function stylice() {
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var c = tbl.c[y][x];
c._step = 0;
c.style.cssText = null;
if (!c._u)
c.style.borderTop = "1px solid black";
if (!c._d)
c.style.borderBottom = "1px solid black";
if (!c._l)
c.style.borderLeft = "1px solid black";
if (!c._r)
c.style.borderRight = "1px solid black";
if (!c._done)
c.style.backgroundColor = "black";
}
}
tbl.start.style.backgroundColor = "#AFA";
tbl.end.style.backgroundColor = "#FAA";
}
stylice();
}
// public members
this.table = tbl;
this.stylice = stylice;
}
function MazeMinner(m, pt, anim, onend) {
m.stylice();
var c = m.table.start, steps = 0;
function step() {
// step control
steps++;
if (pt)
pt.innerHTML = steps;
if (c == m.table.end) {
c.style.backgroundColor = "#5F5";
if (onend)
onend();
return;
}
// decide direction
var lvl, dirs = [c._r, c._l, c._d, c._u], nc;
for (lvl = 0; lvl <= 1; lvl++) {
for (i in dirs) {
cl = dirs[i];
if (cl && cl._step == lvl) {
nc = cl;
break
}
}
if (nc)
break;
}
if (!nc) {
c.style.backgroundColor = "red";
if (pt)
pt.innerHTML = "Error (" + steps + ")";
if (onend)
onend();
return;
}
// mark
c._step = nc._step + 1;
if (c != m.table.start)
c.style.backgroundColor = "#" + ["fff", "66a", "fee"][c._step];
// next step
c = nc;
if (anim)
setTimeout(step);
else
return true;
}
if (anim)
step();
else while (step());
}
function createMaze() {
var w = Number(document.getElementById("mw").value);
var h = Number(document.getElementById("mh").value);
if (window.mz)
document.getElementById("maze").removeChild(window.mz.table);
window.mz = new Maze(w, h);
document.getElementById("maze").appendChild(window.mz.table);
}
function resolveMaze(mm) {
if (!window.mz)
return;
var anim = document.getElementById("anim").checked;
new MazeMinner(window.mz, document.getElementById("mm"), anim);
}
</script>
</head>
<body onload="createMaze()">
<div id="toolbar">
Size
<input id="mw" type="number" value="20" width="10px" onchange="createMaze()"> x
<input id="mh" type="number" value="20" width="10px" onchange="createMaze()">,
<input type="button" value="Create" onclick="createMaze()">
<input id="anim" type="checkbox">Animate
<input type="button" value="Resolve" onclick="resolveMaze()"> &nbsp;&nbsp;&nbsp;&nbsp; steps:
<span id="mm"></span>
</div>
<div id="maze"></div>
</body>
</html>
@XWolfOverride
Copy link
Author

Only tested on Chrome

@hoo-svg
Copy link

hoo-svg commented Nov 2, 2020

Can you show me a picture of what it looks like?

@XWolfOverride
Copy link
Author

As it is a test / sample the UI is very simple, but a lot of CSS can be changed in order to enhance it.
Screenshot 2020-11-02 at 19 05 19

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment