124 lines
5.7 KiB
HTML
124 lines
5.7 KiB
HTML
|
|
<!doctype html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
|
<title>Maze Generator</title>
|
||
|
|
<style>
|
||
|
|
body { font-family: "Segoe UI", Tahoma, sans-serif; margin: 24px; background: #f4f6f8; color: #222; }
|
||
|
|
.layout { display: grid; grid-template-columns: 320px 1fr; gap: 20px; align-items: start; }
|
||
|
|
.panel { background: #fff; border: 1px solid #d8dee5; border-radius: 10px; padding: 16px; }
|
||
|
|
label { display: block; margin: 10px 0 6px; font-size: 14px; }
|
||
|
|
input[type="range"] { width: 100%; }
|
||
|
|
.row { display: grid; grid-template-columns: 1fr 60px; align-items: center; gap: 8px; }
|
||
|
|
.rgb { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
|
||
|
|
.swatch { width: 100%; height: 34px; border-radius: 8px; border: 1px solid #ccc; }
|
||
|
|
.submit-btn { margin-top: 14px; width: 100%; padding: 10px; border-radius: 8px; border: 0; background: #1b5fc6; color: #fff; font-weight: 600; }
|
||
|
|
#shuffle_seed { margin: 0; width: 100%; padding: 8px; border-radius: 8px; border: 1px solid #c8d0da; background: #f7f9fb; color: #222; font-weight: 600; }
|
||
|
|
img { max-width: 100%; border: 1px solid #d8dee5; border-radius: 10px; background: #fff; }
|
||
|
|
@media (max-width: 900px) { .layout { grid-template-columns: 1fr; } }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<h1>Maze Generator</h1>
|
||
|
|
<div class="layout">
|
||
|
|
<form class="panel" method="get" action="/">
|
||
|
|
<h3>Maze</h3>
|
||
|
|
<label>Width</label>
|
||
|
|
<div class="row"><input id="w" type="range" min="3" max="151" step="2" name="w" value="{{.Width}}"><output id="w_out">{{.Width}}</output></div>
|
||
|
|
|
||
|
|
<label>Height</label>
|
||
|
|
<div class="row"><input id="h" type="range" min="3" max="151" step="2" name="h" value="{{.Height}}"><output id="h_out">{{.Height}}</output></div>
|
||
|
|
|
||
|
|
<label>Scale (px per cell)</label>
|
||
|
|
<div class="row"><input id="scale" type="range" min="2" max="24" step="1" name="scale" value="{{.Scale}}"><output id="scale_out">{{.Scale}}</output></div>
|
||
|
|
|
||
|
|
<label>Seed</label>
|
||
|
|
<div class="row"><input id="seed" type="number" min="0" max="18446744073709551615" step="1" name="seed" value="{{.Seed}}"><button id="shuffle_seed" type="button">Random</button></div>
|
||
|
|
|
||
|
|
<label><input type="checkbox" name="solve" value="1" {{if .Highlight}}checked{{end}}> Highlight solution path</label>
|
||
|
|
|
||
|
|
<h3>Wall Color</h3>
|
||
|
|
<div class="rgb">
|
||
|
|
<div><label>R</label><input type="range" min="0" max="255" name="wall_r" value="{{.WallR}}"></div>
|
||
|
|
<div><label>G</label><input type="range" min="0" max="255" name="wall_g" value="{{.WallG}}"></div>
|
||
|
|
<div><label>B</label><input type="range" min="0" max="255" name="wall_b" value="{{.WallB}}"></div>
|
||
|
|
</div>
|
||
|
|
<div class="swatch" style="background: rgb({{.WallR}}, {{.WallG}}, {{.WallB}});"></div>
|
||
|
|
|
||
|
|
<h3>Path Color</h3>
|
||
|
|
<div class="rgb">
|
||
|
|
<div><label>R</label><input type="range" min="0" max="255" name="path_r" value="{{.PathR}}"></div>
|
||
|
|
<div><label>G</label><input type="range" min="0" max="255" name="path_g" value="{{.PathG}}"></div>
|
||
|
|
<div><label>B</label><input type="range" min="0" max="255" name="path_b" value="{{.PathB}}"></div>
|
||
|
|
</div>
|
||
|
|
<div class="swatch" style="background: rgb({{.PathR}}, {{.PathG}}, {{.PathB}});"></div>
|
||
|
|
|
||
|
|
<h3>Solution Color</h3>
|
||
|
|
<div class="rgb">
|
||
|
|
<div><label>R</label><input type="range" min="0" max="255" name="solve_r" value="{{.SolveR}}"></div>
|
||
|
|
<div><label>G</label><input type="range" min="0" max="255" name="solve_g" value="{{.SolveG}}"></div>
|
||
|
|
<div><label>B</label><input type="range" min="0" max="255" name="solve_b" value="{{.SolveB}}"></div>
|
||
|
|
</div>
|
||
|
|
<div class="swatch" style="background: rgb({{.SolveR}}, {{.SolveG}}, {{.SolveB}});"></div>
|
||
|
|
|
||
|
|
<button class="submit-btn" type="submit">Generate</button>
|
||
|
|
</form>
|
||
|
|
|
||
|
|
<div class="panel">
|
||
|
|
<img id="maze_img" src="{{.MazeURL}}" alt="Generated maze">
|
||
|
|
<p><code id="maze_get">GET /maze.png?{{.Query}}</code></p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<script>
|
||
|
|
var form = document.querySelector("form");
|
||
|
|
var img = document.getElementById("maze_img");
|
||
|
|
var getCode = document.getElementById("maze_get");
|
||
|
|
var seedInput = document.getElementById("seed");
|
||
|
|
var randomSeedButton = document.getElementById("shuffle_seed");
|
||
|
|
|
||
|
|
[["w","w_out"],["h","h_out"],["scale","scale_out"]].forEach(function(pair) {
|
||
|
|
var input = document.getElementById(pair[0]);
|
||
|
|
var out = document.getElementById(pair[1]);
|
||
|
|
if (!input || !out) return;
|
||
|
|
input.addEventListener("input", function() { out.textContent = input.value; });
|
||
|
|
});
|
||
|
|
|
||
|
|
function buildQuery() {
|
||
|
|
return new URLSearchParams(new FormData(form)).toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
function refreshMaze() {
|
||
|
|
var query = buildQuery();
|
||
|
|
img.src = "/maze.png?" + query;
|
||
|
|
getCode.textContent = "GET /maze.png?" + query;
|
||
|
|
history.replaceState(null, "", "/?" + query);
|
||
|
|
}
|
||
|
|
|
||
|
|
form.querySelectorAll("input").forEach(function(el) {
|
||
|
|
el.addEventListener("input", refreshMaze);
|
||
|
|
el.addEventListener("change", refreshMaze);
|
||
|
|
});
|
||
|
|
|
||
|
|
function randomUint64String() {
|
||
|
|
if (typeof BigInt !== "function") {
|
||
|
|
return String(Date.now());
|
||
|
|
}
|
||
|
|
if (window.crypto && window.crypto.getRandomValues) {
|
||
|
|
var values = new Uint32Array(2);
|
||
|
|
window.crypto.getRandomValues(values);
|
||
|
|
return ((BigInt(values[0]) << 32n) | BigInt(values[1])).toString();
|
||
|
|
}
|
||
|
|
var hi = Math.floor(Math.random() * 4294967296);
|
||
|
|
var lo = Math.floor(Math.random() * 4294967296);
|
||
|
|
return (BigInt(hi) << 32n | BigInt(lo)).toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
randomSeedButton.addEventListener("click", function() {
|
||
|
|
seedInput.value = randomUint64String();
|
||
|
|
refreshMaze();
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|