JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html>
<body>
<div id="lightsOut">
<table id="display">
<tbody>
</tbody>
<caption>
Solve
</caption>
</table>
<form id="options">
<label>
Width:
<input name="width" type="number" value="3" min="3" max="9" required>
</label>
<label>
Height:
<input name="height" type="number" value="3" min="3" max="9" required>
</label>
<button>
Reset
</button>
<label>
Input:
<select name="mode">
<option value="lights">Lights</option>
<option value="walls">Walls</option>
<option value="play">Play</option>
</select>
</label>
</form>
</div>
</body>
<head>
<link rel="stylesheet" href="LO.css">
<script src="LO.js"></script>
</head>
</html>
CSS
body {
/* base size */
--mult: 30px;
/* margins */
--margin: calc(var(--mult) * 0.1);
--margin2: calc(var(--margin) * 0.5);
/* space between elements */
--gap: calc(var(--mult) * 0.3);
/* input heights */
--input: calc(var(--mult) * 1.5);
--select: calc(var(--input) * 0.5);
/* colors */
--main-bg: rgb(245, 245, 245);
--ui-bg-on: rgb(235, 235, 235);
--ui-bg-off: rgb(195, 195, 195);
/* default border */
--border: var(--margin) solid rgb(155, 155, 155);
--solution: var(--margin) solid rgb(255, 0, 0);
/* font style */
font-size: 30px;
font-family: Arial, Helvetica, sans-serif;
margin: 0;
#lightsOut {
width: fit-content;
background: var(--main-bg);
border: var(--border);
display: flex;
line-height: var(--input);
#display {
margin: var(--margin);
border-collapse: collapse;
tbody {
tr {
z-index: 0;
td {
cursor: pointer;
position: relative;
box-sizing: border-box;
z-index: 0;
width: var(--input);
height: var(--input);
background: var(--ui-bg-on);
border: var(--border);
&.off {
background: var(--ui-bg-off);
}
&.solution::after {
width: var(--gap);
height: var(--gap);
content: "";
display: flex;
position: relative;
margin: auto;
border-radius: 100%;
background-color: black;
}
&.wall {
...
JavaScript
// inRange function: Alexander (via Stack Overflow)
// Lesson: Keith Schwarz (via keithschwarz.com)
// Walls do not work, otherwise perfect!
// Shorthand Key:
// p = puzzle
// t = toggle
// x = horizontal index / column number
// y = vertical index / row number
// w = temporary width
// h = temporary height
// i = index
// j = jindex
// next = next free row
//
// Variables
//
// initial matrices
var puzzle = [];
var toggle = [];
var solution = [];
// initial variables
var width = 0;
var height = 0;
var area = 0;
var inputMode = 0;
// elements
const table = document.querySelector("tbody");
const form = document.getElementById("options");
const submit = document.querySelector("caption");
const select = document.querySelector("select");
//
// Matrix Functions
//
// check if coords (x, y) are in bounds
function inBounds(x, y) {
return 0 <= y && y < height && 0 <= x && x < width
}
//
// Create the puzzle and its corresponding arrays
//
// update width, height, and area
function update() {
let formData = new FormData(form);
for (const entry of formData.entries()) {
if (entry[0] == "width") width = Math.round(entry[1]);
if (entry[0] == "height") height = Math.round(entry[1]);
if (entry[0] == "mode") inputMode = entry[1];
}
area = width * height;
}
// update dimensions, populate display
function populate() {
select.value = "lights";
update();
// populate
table.replaceChildren();
puzzle = Array(area).fill(true);
toggle = Array.from({ length: area }, () => Array(area).fill(false));
for (let y = 0; y < height; y++) {
table.insertRow();
for (let x = 0; x < width; x++) {
let i = x + y * width;
// table, puzzle
table.rows[y].insertCell().addEventListener("click", function () {
update();
let cells = table.querySelectorAll("td")
this.classList.remove("solution");
if (inputMode ==...