Skip to content

Instantly share code, notes, and snippets.

@iitalics
Created September 10, 2025 16:45
Show Gist options
  • Select an option

  • Save iitalics/9f250946411ae5068185aa36839aeea2 to your computer and use it in GitHub Desktop.

Select an option

Save iitalics/9f250946411ae5068185aa36839aeea2 to your computer and use it in GitHub Desktop.
<html>
<head>
<title>Tiles</title>
<style>
html {
--alt1: oklch(0.68 0.17 276);
--alt2: oklch(0.68 0.17 40);
}
.tiles {
display: grid;
grid-template: 1fr 1fr 1fr / 1fr 1fr 1fr;
width: calc(48px * 3 + 16px * 2);
height: calc(48px * 3 + 16px * 2);
column-gap: 16px;
row-gap: 16px;
.tile {
display: flex;
justify-content: center;
align-items: center;
border: 4px solid transparent;
border-radius: 8px;
box-sizing: border-box;
transition: all 0.1s;
cursor: pointer;
&.alt1 {
background: var(--alt1);
&.sel { border-color: var(--alt2); }
}
&.alt2 {
background: var(--alt2);
&.sel { border-color: var(--alt1); }
}
}
}
</style>
</head>
<body>
<div id="tiles" class="tiles">
</div>
<br/>
<button id="reset">Reset</button>
<button id="randomize">Randomize</button>
<label><input type="checkbox" id="solve"> Show solution</label>
<script>
const solveElem = document.querySelector('#solve');
const tilesElem = document.querySelector('#tiles');
const cellElems = [];
for (let y = 0; y < 3; y++) {
cellElems.push([]);
for (let x = 0; x < 3; x++) {
const cellElem = document.createElement('div');
cellElems[y][x] = cellElem;
tilesElem.appendChild(cellElem);
}
}
let cells = [];
let solution = [];
function coordToPhase(x, y) {
if (y === 0) {
return x;
} else if (y === 1) {
if (x === 0) {
return 7;
} else { // x === 2
return 3;
}
} else { // y === 2
return 6 - x;
}
}
function phaseToCoord(p) {
if (p < 3) {
return [p, 0];
} if (p === 3) {
return [2, 1];
} else if (p < 7) {
return [6 - p, 2];
} else { // p === 7
return [0, 1];
}
}
function updateSolution() {
const flip = new Uint8Array(8);
for (let i = 0; i < 8; i++) {
const [x, y] = phaseToCoord(i);
if (cells[y][x]) {
for (let d of [0, 2, 3, 5, 6]) {
flip[(i + d) % 8] ^= 1;
}
}
}
for (let i = 0; i < 8; i++) {
const [x, y] = phaseToCoord(i);
solution[y][x] = flip[i] === 0;
}
}
function* affectedCoords(x, y) {
const phase = coordToPhase(x, y);
for (let delta = 7; delta <= 9; delta++) {
const aphase = (phase + delta) % 8;
yield phaseToCoord(aphase);
}
}
function setupCell(x, y) {
const elem = cellElems[y][x];
elem.classList.add('tile');
updateCell(x, y);
elem.onmouseover = () => onCellMouseOver(x, y);
elem.onmouseout = () => onCellMouseOut();
elem.onmousedown = () => onCellMouseDown(x, y);
}
function updateCell(x, y) {
const elem = cellElems[y][x];
if (cells[y][x]) {
elem.classList.remove('alt1');
elem.classList.add('alt2');
} else {
elem.classList.remove('alt2');
elem.classList.add('alt1');
}
if (solveElem.checked && solution[y][x]) {
elem.innerText = '!';
} else {
elem.innerText = '';
}
}
function onCellMouseOver(x, y) {
for (const [ax, ay] of affectedCoords(x, y)) {
const elem = cellElems[ay][ax];
elem.classList.add('sel');
}
}
function onCellMouseOut() {
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
const elem = cellElems[y][x];
if (elem) {
elem.classList.remove('sel');
}
}
}
}
function onCellMouseDown(x, y) {
for (const [ax, ay] of affectedCoords(x, y)) {
cells[ay][ax] = !cells[ay][ax];
}
updateSolution();
for (const [ax, ay] of affectedCoords(x, y)) {
updateCell(ax, ay);
}
}
for (let y = 0; y < 3; y++) {
cells.push([]);
solution.push([]);
for (let x = 0; x < 3; x++) {
cells[y].push(x === 0 && y === 0);
solution[y].push(false);
}
}
updateSolution();
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
if (x === 1 && y === 1) {
continue;
}
setupCell(x, y);
}
}
document.querySelector('#randomize').onclick = () => {
for (let i = 0; i < 16; i++) {
const phase = Math.floor(Math.random() * 8);
const [x, y] = phaseToCoord(phase);
onCellMouseDown(x, y);
}
};
document.querySelector('#reset').onclick = () => {
for (let phase = 0; phase < 8; phase++) {
const [x, y] = phaseToCoord(phase);
cells[y][x] = false;
updateCell(x, y);
}
updateSolution();
onCellMouseOut();
};
solveElem.onchange = () => {
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
if (x === 1 && y === 1) {
continue;
}
updateCell(x, y);
}
}
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment