Skip to content

Instantly share code, notes, and snippets.

@mdchaney
Created May 22, 2025 23:16
Show Gist options
  • Save mdchaney/c816094ae7b3898cdc24a8016a1cf5d8 to your computer and use it in GitHub Desktop.
Save mdchaney/c816094ae7b3898cdc24a8016a1cf5d8 to your computer and use it in GitHub Desktop.
Square puzzle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Five Squares Puzzle Demo</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
text-align: center;
background-color: #f0f0f0;
}
.controls {
padding: 20px;
}
svg {
width: 100vw;
height: calc(100vh - 80px);
background-color: #e0e0e0;
}
.square {
fill: none;
stroke: black;
stroke-width: 2;
}
#smallest-square {
fill: #ff9999;
}
#h-line {
stroke: red;
stroke-width: 3;
}
#h-label {
fill: red;
font-size: 20px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="controls">
<label for="scale-slider">Scale Factor (c): <span id="scale-value">2</span></label><br>
<input type="range" id="scale-slider" min="1" max="5" step="0.1" value="2">
<p>h = <span id="h-value">4</span></p>
</div>
<svg id="puzzle-svg"></svg>
<script>
const svg = document.getElementById('puzzle-svg');
const slider = document.getElementById('scale-slider');
const scaleValueDisplay = document.getElementById('scale-value');
const hValueDisplay = document.getElementById('h-value');
function drawPuzzle(c) {
svg.innerHTML = '';
const svgWidth = svg.clientWidth;
const svgHeight = svg.clientHeight;
// Side lengths
const s = 1;
const a = c + 1;
const b = c + 2;
const d = c + 3;
// Compute h
const h = (b + d) - (a + c); // Should be 4
const scale_factor = 5;
// Adjust coordinates
const xShift = scale_factor + 1;
const yShift = scale_factor;
// Define the squares' positions
const s1 = { x: 0, y: 0 };
const s2 = { x: 1, y: 0 };
const s3 = { x: 1, y:1 };
const s4 = { x: 0, y: 1 };
const a1 = { x: -c, y: 0 };
const a2 = { x: 0, y: 0 };
const a3 = { x: 0, y: c };
const a4 = { x: -c, y: c };
const b1 = { x: -c, y: 0 };
const b2 = { x: 1, y: 0 };
const b3 = { x: 1, y: -c - 1 };
const b4 = { x: -c, y: -c - 1 };
const c1 = { x: 1, y: 1 };
const c2 = { x: 1, y: -c - 1 };
const c3 = { x: c + 3, y: -c - 1 };
const c4 = { x: c + 3, y: 1 };
const d1 = { x: 0, y: 1 };
const d2 = { x: c + 3, y: 1 };
const d3 = { x: c + 3, y: c + 4 };
const d4 = { x: 0, y: c + 4 };
// Scale to fit SVG - scale factor is always 5
const totalWidth = (scale_factor + 4) - (-1);
const totalHeight = (2 * scale_factor + 3) - 0 + h; // Include h below square C
const scale = Math.min(svgWidth / totalWidth, svgHeight / totalHeight) * 0.8;
const offsetX = (svgWidth - totalWidth * scale) / 2;
const offsetY = (svgHeight - totalHeight * scale) / 2;
const transform = (x, y) => ({
x: (x + xShift) * scale + offsetX,
y: (y + yShift) * scale + offsetY
});
// Transform all points
const squares = [
{ points: [s1, s2, s3, s4].map(p => transform(p.x, p.y)), id: 'smallest-square' },
{ points: [a1, a2, a3, a4].map(p => transform(p.x, p.y)) },
{ points: [b1, b2, b3, b4].map(p => transform(p.x, p.y)) },
{ points: [c1, c2, c3, c4].map(p => transform(p.x, p.y)) },
{ points: [d1, d2, d3, d4].map(p => transform(p.x, p.y)) }
];
squares.forEach(square => {
const pointsStr = square.points.map(p => `${p.x},${p.y}`).join(' ');
const poly = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
poly.setAttribute('points', pointsStr);
poly.setAttribute('class', 'square');
if (square.id) poly.setAttribute('id', square.id);
svg.appendChild(poly);
});
// Draw the h line (from bottom of square A downward (+Y) by 4 units)
const hStart = transform(-0.5 * c, c); // Top of square C
const hEnd = transform(-0.5 * c, c + h); // Down by h units
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', hStart.x);
line.setAttribute('y1', hStart.y);
line.setAttribute('x2', hEnd.x);
line.setAttribute('y2', hEnd.y);
line.setAttribute('id', 'h-line');
svg.appendChild(line);
const hMid = transform(-0.5 * c - 0.5, c + h / 2);
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', hMid.x);
text.setAttribute('y', hMid.y);
text.setAttribute('id', 'h-label');
text.textContent = 'h';
svg.appendChild(text);
// Draw a blue circle at 0,0 to show the origin, with
// a z index of 999 so it's on top of the other elements
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
const origin = transform(0, 0);
circle.setAttribute('cx', origin.x);
circle.setAttribute('cy', origin.y);
circle.setAttribute('r', 5);
circle.setAttribute('fill', 'blue');
circle.setAttribute('stroke', 'green');
circle.setAttribute('stroke-width', 0.1);
circle.setAttribute('z-index', 999);
svg.appendChild(circle);
// And a red circle at 1,1
const redCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
const redOrigin = transform(1, 1);
redCircle.setAttribute('cx', redOrigin.x);
redCircle.setAttribute('cy', redOrigin.y);
redCircle.setAttribute('r', 5);
redCircle.setAttribute('fill', 'red');
redCircle.setAttribute('stroke', 'green');
redCircle.setAttribute('stroke-width', 0.1);
redCircle.setAttribute('z-index', 999);
svg.appendChild(redCircle);
scaleValueDisplay.textContent = c.toFixed(1);
hValueDisplay.textContent = h.toFixed(2);
}
drawPuzzle(parseFloat(slider.value));
slider.addEventListener('input', () => {
const c = parseFloat(slider.value);
drawPuzzle(c);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment