R67 · Sieve of Eratosthenes on a number grid
Every answer below carries the name of the model that wrote it. Blind refers to the scoring: the review was done without knowing which model produced which answer — the published result names them.
2 of the 11 local models delivered nothing through our test harness in this round — for reasons that lie with our setup, not with the models. Those cards are marked DNF like any other, but they are not evidence about those models — the reason is noted on the card itself.
A frontier reference answered the same task (Claude Opus 5) — outside the field and deliberately unscored, further down the page.
Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 120 lines total) that animates the Sieve of Eratosthenes for the numbers 2 to 400 laid out as a grid of cells on a <canvas>. Step through the primes in order: highlight the current prime, then strike out its multiples one by one in a colour tied to that prime, so the diagonal striping patterns become visible. Show the current prime, the count of primes found and the count of cells eliminated. Smooth requestAnimationFrame animation, NO user input, starts immediately and keeps running (restart with a fresh random instance when finished). No external libraries, no comments needed. Output ONLY the HTML file content, nothing else.
The answers
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sieve of Eratosthenes</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="50"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
function randomInstance() {
let instance = Array.apply(null, Array(401)).map(function () { return 1; });
for (let i = 0; i <= Math.floor(Math.random()*200); i++) {
instance[Math.floor(Math.random()*400)+2] = 0;
}
return instance;
}
let currentPrime = 2, primesCount = 0, eliminatedCells = 0;
function draw(instance) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 1; i <= 400; i++) {
if (instance[i]) {
ctx.fillStyle = 'black';
ctx.fillRect((i-1)%25*15+2, Math.floor(i/25)*16+2, 13, 14);
} else {
ctx.fillStyle = 'white';
ctx.fillRect((i-1)%25*15+2, Math.floor(i/25)*16+2, 13, 14);
}
}
ctx.font = "10px Arial";
ctx.fillStyle = 'black';
ctx.textAlign = "left";
ctx.fillText('Prime: ' + currentPrime, 10, canvas.height - 20);
ctx.textAlign = "right";
ctx.fillText('Primes: ' + primesCount, canvas.width - 40, canvas.height - 20);
ctx.textAlign = "center";
ctx.fillText('Eliminated: ' + eliminatedCells, canvas.width/2, canvas.height - 60);
if (currentPrime <= 400 && instance[currentPrime]) {
ctx.strokeStyle = '#' + currentPrime.toString(16).padStart(6, '0');
ctx.lineWidth = 3;
for (let multiple = currentPrime*2; multiple < 401; multiple += currentPrime) {
ctx.beginPath();
ctx.moveTo((multiple-1)%25*15+10, Math.floor(multiple/25)*16+20);
ctx.lineTo(((multiple-currentPrime-1)/currentPrime*15)+10, (Math.floor(multiple/currentPrime)-1)*16+20);
ctx.stroke();
instance[multiple] = 0;
eliminatedCells++;
}
}
if (++currentPrime > 400) {
currentPrime = 2;
primesCount++;
instance = randomInstance();
}
}
let instance = randomInstance();
draw(instance);
function animate() {
requestAnimationFrame(animate);
draw(instance);
}
animate();
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sieve of Eratosthenes Animation</title>
<style>
body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; }
canvas { border: 1px solid black; }
#info { position: absolute; top: 20px; left: 20px; font-size: 20px; }
</style>
</head>
<body>
<canvas id="sieveCanvas" width="800" height="450"></canvas>
<div id="info">Prime: -, Primes Found: 0, Cells Eliminated: 0</div>
<script>
const canvas = document.getElementById('sieveCanvas');
const ctx = canvas.getContext('2d');
const info = document.getElementById('info');
const size = 40;
let primes = [];
let eliminated = 0;
function drawGrid() {
for (let i = 0; i < size * size; i++) {
const x = i % size;
const y = Math.floor(i / size);
ctx.strokeRect(x * 19, y * 20 + 50, 18, 18);
if (x === 0 || x === size - 1) ctx.fillText(y + 2, x * 19 + 4, y * 20 + 65);
}
}
function drawPrime(n) {
const color = `hsl(${n * 3}, 100%, 70%)`;
for (let i = n; i < size * size; i += n) {
ctx.fillStyle = color;
if (i !== n && primes.indexOf(i) === -1) {
const x = i % size;
const y = Math.floor(i / size);
ctx.fillRect(x * 19 + 2, y * 20 + 52, 14, 14);
}
}
}
function updateInfo(prime, primeCount, eliminatedCount) {
info.innerText = `Prime: ${prime}, Primes Found: ${primeCount}, Cells Eliminated: ${eliminatedCount}`;
}
let currentPrime = 0;
let step = 0;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (step === 1) drawGrid();
if (currentPrime > size * size || primes.length >= Math.sqrt(size * size)) {
setTimeout(() => {currentPrime = 2; step = 1;}, 500);
return;
}
if (primes.indexOf(currentPrime) === -1 && currentPrime !== 1) primes.push(currentPrime);
if (step === 0) drawGrid();
drawPrime(currentPrime);
updateInfo(currentPrime, primes.length , eliminated );
step++;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
function removeColor(n){
for(let i = n; i < size * size ; i +=n){
const x = i % size;
const y = Math.floor(i / size);
ctx.fillRect(x * 19 + 2, y * 20 + 52, 14, 14);
}
}
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Sieve of Eratosthenes</title>
<style>
body { display: flex; justify-content: center; align-items: flex-start; background-color: #111; color: white; font-family: sans-serif; padding-top: 20px; }
#container { display: flex; gap: 30px; max-width: 1200px; }
canvas { border: 1px solid #333; background-color: #222; }
#info { padding: 15px; background-color: #2a2a2a; border-radius: 8px; width: 250px; }
h3 { margin-top: 0; color: #4CAF50; }
</style>
</head>
<body>
<div id="container">
<canvas id="sieveCanvas" width="700" height="400"></canvas>
<div id="info">
<h3>Sieve Status</h3>
<p>Current Prime: <span id="currentPrime">N/A</span></p>
<p>Primes Found: <span id="primeCount">0</span></p>
<p>Eliminated Cells: <span id="eliminatedCount">0</span></p>
</div>
</div>
<script>
const canvas = document.getElementById('sieveCanvas');
const ctx = canvas.getContext('2d');
const N = 400;
const COLS = Math.floor(Math.sqrt(N));
const CELL_SIZE = 35;
canvas.width = COLS * CELL_SIZE;
let isRunning = true;
let primeState = {
isSieved: new Array(N + 1).fill(false),
primes: [],
currentIndex: 0,
eliminatedCount: 0,
primeCounter: 0
};
function setupCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 2; i <= N; i++) {
const x = ((i - 2) % COLS) * CELL_SIZE + 15;
const y = Math.floor((i - 2) / COLS) * CELL_SIZE + 15;
ctx.fillStyle = '#3a3a3a';
ctx.fillRect(x, y, CELL_SIZE - 30, CELL_SIZE - 30);
}
}
function renderState() {
const state = primeState;
document.getElementById('currentPrime').textContent = state.currentIndex > 1 && state.primes[state.primes.length - 1] > 1 ? state.primes[state.primes.length - 1] : 'N/A';
document.getElementById('primeCount').textContent = state.primeCounter;
document.getElementById('eliminatedCount').textContent = state.eliminatedCount;
// Draw the grid cells based on current state (simplified drawing for performance)
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 2; i <= N; i++) {
const x = ((i - 2) % COLS) * CELL_SIZE + 15;
const y = Math.floor((i - 2) / COLS) * CELL_SIZE + 15;
if (state.isSieved[i]) {
ctx.fillStyle = '#3a3a3a';
} else if (i === state.currentIndex && i > 1) {
// Highlight current prime candidate
ctx.fillStyle = '#4CAF50';
} else if (state.primes.includes(i)) {
ctx.fillStyle = '#6b6b6b'; // Prime found color
} else if ((i % 2 === 0 && i > 2) || state.eliminated[i]) {
// Striked out/composite color (using the associated prime's color for effect)
let eliminatedBy = -1;
for(const p of state.primes) {
if (p * Math.floor(i / p) === i && p < Math.sqrt(N)) {
eliminatedBy = p;
break;
}
}
if (eliminatedBy > 0) {
ctx.fillStyle = `rgba(${Math.sin(eliminatedBy/10)*127 + 128}, ${Math.cos(eliminatedBy/10)*127 + 128}, ${eliminatedBy/5} * 4 % 255 || 50, 0.6)`;
} else {
ctx.fillStyle = '#3a3a3a'; // Default composite fill
}
} else {
ctx.fillStyle = '#3a3a3a';
}
ctx.fillRect(x, y, CELL_SIZE - 30, CELL_SIZE - 30);
}
}
function sieveStep() {
const state = primeState;
let currentPrime = state.primes[state.primeCounter];
if (currentPrime === undefined || currentPrime * currentPrime > N) {
// Sieve complete or no more candidates
clearInterval(animationId);
document.getElementById('currentPrime').textContent = 'FINISHED';
setTimeout(() => {
setupCanvas(); // Reset the canvas visual state
primeState = { isSieved: new Array(N + 1).fill(false), primes: [], currentIndex: 0, eliminatedCount: 0, primeCounter: 0 };
window.requestAnimationFrame(() => loop());
}, 2000);
return;
}
let foundNewPrime = false;
while (state.currentIndex <= N) {
const i = state.currentIndex;
if (i < 2) {
state.isSieved[i] = true; continue;
}
// Check if it has already been eliminated (Composite check)
let isEliminated = false;
for(const p of state.primes) {
if (p * Math.floor(i / p) === i && p < Math.sqrt(N)) {
isEliminated = true;
break;
}
}
if (!isEliminated) {
// This is a prime!
state.primes.push(i);
state.primeCounter++;
foundNewPrime = true;
}
// Mark it as checked for the next iteration, regardless of status
state.isSieved[i] = true;
state.currentIndex++;
}
if (foundNewPrime) {
// We only need to find primes up to sqrt(N). The actual step is finding the *next* prime candidate.
const nextPrimeCandidateIndex = state.primes[state.primeCounter - 1] + Math.floor((Math.random() * 5) + 1);
if (nextPrimeCandidateIndex > N) {
// If we jumped past N, reset the index to start checking from the last known prime's successor
state.currentIndex = state.primes[state.primeCounter - 1] + 1;
} else {
state.currentIndex = Math.max(2, state.currentIndex);
}
}
renderState();
}
let animationId;
function loop() {
if (isRunning) {
sieveStep();
animationId = window.requestAnimationFrame(loop);
}
}
// Initial setup and start
setupCanvas();
window.onload = () => {
loop();
};
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Sieve of Eratosthenes</title>
<style>body{display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#222;color:#eee}#container{padding:20px;border-radius:10px;background:#333;box-shadow:0 0 15px rgba(0,0,0,0.5)}canvas{border:1px solid #444;display:block}#stats{margin-top:15px;font-family:monospace;color:#ccc}
</style>
</head>
<body>
<div id="container">
<canvas id="sieveCanvas" width="800" height="320"></canvas>
<div id="stats">Primes Found: <span id="primesCount">0</span> | Eliminated: <span id="eliminatedCount">0</span></div>
</div>
<script>
const canvas = document.getElementById('sieveCanvas');
const ctx = canvas.getContext('2d');
const N = 400;
const COLS = Math.ceil(Math.sqrt(N));
const CELL_SIZE = (canvas.width / COLS);
let isRunning = true;
let state = {
isSieve: false,
primeIndex: 0,
primesFound: 0,
eliminatedCount: 0,
sifted: new Array(N + 1).fill(false), // True if eliminated/visited
currentPrime: null,
multiplesToMark: []
};
function getNextRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
let colorCode = '';
for (let i = 0; i < 6; i++) {
colorCode = Math.floor(Math.random() * 16).toString(16);
color += colorCode;
}
return color;
}
function resetState() {
state.sifted.fill(false);
state.primesFound = 0;
state.eliminatedCount = 0;
state.primeIndex = 0;
state.currentPrime = null;
state.multiplesToMark = [];
for (let i = 2; i <= N; i++) {
// Initial fill: false means not visited/not prime
if (!state.sifted[i]) state.sifted[i] = true;
}
state.isSieve = true;
}
function drawCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const offset = (x) => x - 1; // Numbers start at 2, index starts at 0 for drawing logic
for (let i = 2; i <= N; i++) {
const col = Math.floor((i - 2) / COLS);
const row = (i - 2) % COLS;
const x = col * CELL_SIZE;
const y = row * CELL_SIZE;
ctx.strokeStyle = '#444';
ctx.lineWidth = 1;
if (state.sifted[i]) {
// Crossed out or visited non-prime multiple
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + CELL_SIZE, y + CELL_SIZE);
ctx.stroke();
} else if (state.currentPrime === i) {
// Current prime being highlighted
ctx.fillStyle = '#00FF7F';
ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE);
} else if (!state.sifted[i] && i > 1) {
// Prime number (but not currently active/highlighted)
ctx.fillStyle = '#3a3a50';
ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE);
} else {
// Background for non-numbers or initial state empty cells
}
}
// Draw labels (optional but good for visualization)
ctx.fillStyle = '#aaa';
for (let i = 2; i <= N; i++) {
const col = Math.floor((i - 2) / COLS);
const row = (i - 2) % COLS;
ctx.fillText(i, col * CELL_SIZE + 5, row * CELL_SIZE + CELL_SIZE * 0.7);
}
}
function updateStats() {
document.getElementById('primesCount').textContent = state.primesFound;
document.getElementById('eliminatedCount').textContent = state.eliminatedCount;
}
function checkAndRunSieveStep() {
if (!state.isSieve) return;
let i = 2;
while (i * i <= N && state.primesFound < Math.sqrt(N)) {
const num = i;
// Check if 'num' is prime (i.e., not yet eliminated/sifted)
if (!state.sifted[num]) {
state.currentPrime = num;
// Highlight current prime
drawCanvas();
updateStats();
// Mark multiples of 'num'
let tempMultiples = [];
for (let j = num * num; j <= N; j += num) {
if (!state.sifted[j]) {
tempMultiples.push(j);
}
}
state.multiplesToMark = tempMultiples;
// Animation step delay (process the multiples one by one)
setTimeout(() => {
for (let k = 0; k < state.multiplesToMark.length; k++) {
const multiple = state.multiplesToMark[k];
state.sifted[multiple] = true; // Mark as eliminated/visited
// Draw the specific elimination step
drawCanvas();
updateStats();
}
// After marking all multiples, finalize this prime's count and move on
state.primesFound++;
state.eliminatedCount += state.multiplesToMark.length;
if (state.primesFound < Math.sqrt(N)) {
i++; // Move to the next potential prime
setTimeout(() => checkAndRunSieveStep(), 50); // Delay for visual separation between primes
} else {
// Sieve complete or near completion
console.log("Sieve finished.");
state.isSieve = false;
}
}, 20); // Timeout delay per multiple elimination step
}
i++;
}
}
function animationLoop() {
if (state.isSieve) {
checkAndRunSieveStep();
} else if (!state.sifted[state.currentPrime] && state.primesFound < Math.sqrt(N)) {
// If the loop finished naturally before finding enough primes, restart or stop gracefully
console.log("Animation complete.");
}
requestAnimationFrame(animationLoop);
}
// Initialization
resetState();
drawCanvas(); // Initial draw
window.onload = () => {
animationLoop();
};
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html><body style="margin:0;padding:0;background-color:#2a2a2a;">
<canvas id="c" width="800" height="600"></canvas>
<div id="info" style="position:absolute;top:10px;left:10px;color:white;font-family:Arial;"></div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const info = document.getElementById('info');
const MAX_NUMBER = 400;
const COLORS = ['#ff0000','#00ff00','#0000ff','#ffff00','#ff00ff','#00ffff','#fda0d8','#a0d8ff'];
let sieve, primes, currentPrimeIndex;
function initSieve() {
sieve = Array(MAX_NUMBER + 1).fill(true);
sieve[0] = sieve[1] = false;
return sieve;
}
function findPrimes(current=2) {
if (current > MAX_NUMBER) return [];
if (!sieve[current]) return findPrimes(current+1);
const multiples = [];
for(let i=current*2; i<=MAX_NUMBER; i+=current)
multiples.push(i);
return [current, ...findPrimes(current+1, [...sieve,...multiples.map(n=>(n=current))]]];
}
function updateSieve() {
if (primes.length === currentPrimeIndex + 1) {
currentPrimeIndex = 0;
primes = findPrimes();
info.textContent = `Found: ${primes.length} primes`;
return false;
}
const prime = primes[currentPrimeIndex];
info.textContent = `Current Prime: ${prime}, Found: ${currentPrimeIndex + 1}, Eliminated: ${MAX_NUMBER - currentPrimeIndex - (primes.length - currentPrimeIndex)}`;
// Draw sieve
ctx.clearRect(0,0,canvas.width,canvas.height);
// Highlight current prime
const pos = getPosition(prime);
ctx.fillStyle = 'white';
ctx.fillRect(pos.x-5, pos.y-5, 10,10);
// Draw numbers
for(let i=2; i<=MAX_NUMBER; i++) {
if (i === primes[currentPrimeIndex]) continue;
const [x,y] = getPosition(i);
if (!sieve[i]) {
ctx.fillStyle = COLORS[primes.indexOf(i)];
ctx.fillRect(x-5, y-5, 10,10);
} else {
ctx.fillStyle = '#333';
ctx.fillRect(x-5, y-5, 10,10);
}
if (i === primes[currentPrimeIndex]) continue;
}
// Mark multiples
const next = () => {
if (currentPrimeIndex >= primes.length -1) return requestAnimationFrame(updateSieve);
currentPrimeIndex++;
requestAnimationFrame(updateSieve);
};
if (!sieve[primes[currentPrimeIndex +1]]) {
requestAnimationFrame(next);
} else {
sieve[primes[currentPrimeIndex +1]] = false;
requestAnimationFrame(next);
}
}
function getPosition(n) {
const maxCol = Math.floor(Math.sqrt(MAX_NUMBER)) * 2 |0;
return [ (n-2) % maxCol * 20 + 10,
(n-2) / maxCol |0 * 20 + 10 ];
}
sieve = initSieve();
primes = findPrimes();
currentPrimeIndex = -1;
requestAnimationFrame(updateSieve);
</script>
</body></html>
```Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sieve of Eratosthenes Animation</title>
<style>
body { background: #111; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; color: white; font-family: monospace; }
canvas { border: 2px solid #444; background: #222; }
#status { margin-left: 30px; padding: 10px; background: #333; border-radius: 5px; width: 300px; }
</style>
</head>
<body>
<canvas id="sieveCanvas" width="600" height="240"></canvas>
<div id="status"></div>
<script>
const canvas = document.getElementById('sieveCanvas');
const ctx = canvas.getContext('2d');
const statusDiv = document.getElementById('status');
const MAX_N = 400;
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 240;
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// State variables
let sieve = Array(MAX_N + 1).fill(true);
let isPrime = Array(MAX_N + 1).fill(false);
let eliminated = Array(MAX_N + 1).fill(0); // Tracks which prime struck it out
// Animation state
let currentPrimeIndex = 2;
let stepCount = 0;
let totalEliminated = 0;
let primesFound = 0;
let animationRunning = true;
function setupSieve() {
sieve.fill(true);
isPrime.fill(false);
eliminated.fill(0);
currentPrimeIndex = 2;
stepCount = 0;
totalEliminated = 0;
primesFound = 0;
animationRunning = true;
}
function drawGrid() {
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
const gapX = CANVAS_WIDTH / (MAX_N - 1);
const cellWidth = Math.max(1, gapX * 0.8); // Slightly smaller than ideal spacing for visibility
for (let i = 2; i <= MAX_N; i++) {
const x = (i - 2) * cellWidth + (cellWidth / 2);
const y = CANVAS_HEIGHT / 2;
// Draw the number text
ctx.fillStyle = '#fff';
ctx.fillText(String(i), x, y + 5);
if (!sieve[i]) continue; // Skip drawing if marked composite
let color = '#333'; // Default background for active numbers
let alpha = 1.0;
// Check if this cell has been struck out by any prime
const p = eliminated[i];
if (p !== 0) {
color = `rgba(255, ${255 - p * 3}px, ${255 - p*3}px, 0.4)`; // Pseudo-random color based on prime index
alpha = 0.6;
} else if (isPrime[i]) {
color = '#4CAF50'; // Highlight the current prime/found primes
}
ctx.fillStyle = color;
ctx.fillRect(x - cellWidth / 2, y - cellWidth / 2, cellWidth, cellWidth);
}
}
function updateStatus() {
statusDiv.innerHTML = `
<strong>Prime Found:</strong> ${currentPrimeIndex === undefined ? 'N/A' : currentPrimeIndex}<br>
<strong>Primes Count:</strong> ${primesFound}/${Math.floor(MAX_N / Math.log(MAX_N))} (Est.)<br>
<strong>Cells Eliminated:</strong> ${totalEliminated}/${MAX_N - 2}
`;
}
function animate() {
if (!animationRunning) return;
// Check if we need to find the next prime or finish
if (currentPrimeIndex === undefined || currentPrimeIndex * currentPrimeIndex > MAX_N) {
const finished = sieve.every(b => b); // Quick check for completion state (not perfect, but good enough)
if (totalEliminated >= MAX_N - 2 && primesFound > 0) {
animationRunning = false;
// Final cleanup visualization
currentPrimeIndex = 'Finished';
drawGrid();
updateStatus();
return;
}
// Find next prime starting from current index + 1
let foundNext = false;
for (let i = Math.max(2, currentPrimeIndex === undefined ? 2 : currentPrimeIndex); i <= MAX_N; i++) {
if (sieve[i] && !isPrime[i]) {
currentPrimeIndex = i;
foundNext = true;
break;
}
}
if (!foundNext) {
// This handles the case where we passed 400 or only unmarked numbers remain.
animationRunning = false;
currentPrimeIndex = 'Finished';
drawGrid();
updateStatus();
return;
}
}
const p = currentPrimeIndex;
// --- Core Animation Step Logic ---
if (p < 2) {
currentPrimeIndex++;
requestAnimationFrame(animate);
return;
}
// Highlight the prime just found
isPrime[p] = true;
drawGrid(); // Redraw to show highlight
updateStatus();
// Determine the current multiple step (k)
const startMultiple = Math.max(p * p, p * 2);
let lastMultipleProcessed = (p === 2 && totalEliminated < 10) ? 6 : (totalEliminated > 0 ? Math.floor((stepCount - 1) / 5) + 2 : 4);
// Limit steps to prevent immediate overflow/underflow issues on small ranges
let k = lastMultipleProcessed;
for (; k * p <= MAX_N && totalEliminated < MAX_N - 2; k++) {
const multiple = k * p;
if (multiple > MAX_N) break;
if (sieve[multiple]) { // Only strike out if it hasn't been struck out yet
sieve[multiple] = false;
eliminated[multiple] = p;
totalEliminated++;
stepCount++;
// Progressive visual update: Redraw every single step
drawGrid();
updateStatus();
}
}
// Move to the next prime candidate
currentPrimeIndex++;
primesFound++; // Count the prime just processed
requestAnimationFrame(animate);
}
setupSieve();
animate();
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html><head><title>Sieve of Eratosthenes Animation</title><style>canvas{border:1px solid black}.info{position:absolute;top:20px;left:20px;font-family:sans-serif}</style></head><body>
<canvas id=c width=300 height=300></canvas><div class="info" id=i></div>
<script>
let c=document.getElementById('c'),ctx=c.getContext('2d'),w=300,h=300,s=12,nx=20,ny=20,primes=[],counts={current:0,totalPrimes:0,totalEliminated:0},currentNumber=2,step=2;
const sieve=[...Array(401)].map(_=>true);
sieve[0]=sieve[1]=false;
function drawGrid(){ctx.fillStyle='white';ctx.fillRect(0,0,w,h);for(let i=0;i<=nx+1;i++)for(let j=0;j<=ny+1;j++){let x=i*s,y=j*s;ctx.strokeStyle='lightgray';ctx.strokeRect(x,y,s,s)}}
function highlightPrime(n){let [x,y]=[n%nx,nx*Math.floor(n/nx)];ctx.fillStyle='lightblue';ctx.fillRect(x*s+1,y*s+1,s-2,s-2)}
function strikeThrough(m,p){let hue=(p*360/400)%360;ctx.fillStyle=`hsla(${hue},70%,50%,0.3)`;let [x,y]=[m%nx,nx*Math.floor(m/nx)];ctx.fillRect(x*s+1,y*s+1,s-2,s-2)}
function updateStep(n){
counts.current=n;
primes.length=counts.totalPrimes;
for(let i=currentNumber*currentNumber;i<=400;i+=currentNumber)if(sieve[i]){sieve[i]=false;counts.totalEliminated++}
primes.push(currentNumber);
counts.totalPrimes++;
currentNumber++;
}
function animate(t){
drawGrid();
highlightPrime(primes.length>=1?primes[primes.length-1]:2);
for(let i=0;i<primes.length;i++)strikeThrough(primes[i]*step,primes[i]);
document.getElementById('i').textContent=`Current: ${counts.current}\nPrimes Found: ${counts.totalPrimes}\nEliminated: ${counts.totalEliminated}`;
if(counts.totalPrimes!==primes.length)updateStep();
else{
if(step*primes[primes.length-1]<=400)strikeThrough(primes[primes.length-1]*step,primes[primes.length-1]);else step++;
}
requestAnimationFrame(animate);
}
drawGrid();
highlightPrime(2);
document.getElementById('i').textContent=`Current: 2\nPrimes Found: 0\nEliminated: 0`;
animate();
</script></body></html>
```Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sieve of Eratosthenes</title>
<style>
body {
margin: 0;
}
#container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
canvas {
border: 2px solid black;
}
</style>
</head>
<body>
<div id="container">
<canvas id="siegeCanvas"></canvas>
</div>
<script>
const maxNum = 400;
const cellSize = 10; // px
const canvasWidth = (maxNum - 2) * cellSize + 2 /* border */;
const canvasHeight = maxNum * cellSize;
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
window.addEventListener('load', () => {
const ctx = document.getElementById('siegeCanvas').getContext('2d');
document.getElementById('siegeCanvas').width = canvasWidth;
document.getElementById('siegeCanvas').height = canvasHeight;
let primes = [];
let eliminated = 0;
function draw() {
requestAnimationFrame(draw);
if (primes.length === 0) {
// Start with a random prime
const randPrime = randomInt(2, maxNum / 2 + 1);
primes.push(randPrime);
ctx.fillStyle = '#ccc';
for (let i = cellSize; i < canvasWidth; i += cellSize) {
ctx.fillRect(i, cellSize, cellSize - 2, canvasHeight - 2 * cellSize); // Horizontal lines
}
} else {
const currentPrime = primes[primes.length - 1];
if (currentPrime ** 2 > maxNum) {
// Restart with a fresh instance when done
primes = [];
} else {
let x0 = (currentPrime - 2) * cellSize;
ctx.fillStyle = `hsl(${currentPrime * 4}, 100%, 50%)`;
ctx.fillRect(x0, cellSize, cellSize, canvasHeight - 2 * cellSize); // Highlight the current prime
for (let x = x0 + cellSize; x < canvasWidth; x += currentPrime * cellSize) {
eliminated++;
ctx.clearRect(x, cellSize, cellSize, canvasHeight - 2 * cellSize);
}
if (!primes.includes(currentPrime ** 2)) primes.push(currentPrime ** 2);
}
}
const stats = document.createElement('div');
stats.innerHTML = `Primes: ${primes.length}<br>Eliminated: ${eliminated}`;
container.appendChild(stats);
}
draw();
});
</script>
</body>
</html>
```Show code
<think> Alright, let me tackle this problem step by step. So, the user wants an HTML file that animates the Sieve of Eratosthenes for numbers 2 to 400. The animation needs to highlight the current prime, then strike out its multiples one by one with colors tied to each prime. It also needs to show the current prime, the count of primes found, and the count of cells eliminated. Plus, it should use smooth animation with requestAnimationFrame, start immediately, and restart when finished without user input. No external libraries, and the HTML must be self-contained under 120 lines. First, I need to set up the basic HTML structure. The body should have a canvas element and some divs to display the current prime, primes found, and cells eliminated. Since it's self-contained, all the JavaScript has to be inline. Next, the sieve algorithm. The sieve needs to generate primes up to 400. The numbers will be laid out in a grid on the canvas. Let's decide on the grid dimensions. Since 400 is 20x20, that's a good grid size. So, each cell will be (canvas width / 20) in both width and height. Let me calculate that based on a reasonable canvas size, say 600x600, so each cell is 30px. But perhaps I should make the grid dynamic based on the canvas size so it can adjust. Hmm, but maybe fixed is okay for simplicity. The animation steps: first, highlight the current prime, then animate striking out its multiples. Each step should have a delay between removing each multiple. The color for each prime's multiples should be unique. Maybe generate a color based on the prime number. For example, using a hash function to get a hue. The animation loop should use requestAnimationFrame. The state needs to track the current prime, the current multiple index, and whether to continue. Once all primes are processed, it should restart with a shuffled grid? Or a new random instance? The problem says "fresh random instance", so maybe after finishing, the grid resets with numbers in a different order? Wait, actually, the sieve works by keeping the order, so maybe "random instance" refers to a new seed? Or perhaps the order of processing primes is randomized? Wait, the problem says "Step through the primes in order", so the primes must be processed in their natural order: 2, 3, 5, etc. So "fresh random instance" might mean that after completion, it restarts the sieve with the grid reinitialized, perhaps with a different color scheme or some other random element. Maybe the color assignment is random? The problem says "a colour tied to that prime", so maybe per prime a color, but that could be fixed. However, the user says "random instance", so perhaps the colors are randomized each time? Let me check the problem statement again. It says "a fresh random instance when finished". Hmm, maybe the numbers are arranged in a different order? But the sieve requires the numbers to be in order. Alternatively, maybe the grid is randomized in layout? But that complicates the sieve's process. Maybe the color for each prime is randomly chosen each time, so the striping patterns can be seen differently each time. That makes sense. So, colors are assigned randomly per prime each restart. So, steps to implement: 1. Initialize the sieve array (primes and non-primes). 2. Create a grid layout on the canvas with 20x20 cells, each 30px? Maybe the canvas is 600x600. 3. The animation will loop through primes (starting at 2, then next unmarked prime), for each prime: a. Highlight it. b. Then, step through each multiple of the prime, striking them out with a color tied to the prime. Each multiple is removed with a delay between steps. 4. The current prime, primes count, and eliminated cells need to be displayed in the divs. 5. After all primes are processed, restart the animation with new random colors. First, let's think about the sieve setup. The sieve array can be an array of objects representing each number (index 0-399, but starting from 2). Wait, numbers from 2 to 400, so indices 0 would be 2, but maybe better to use indices 2 to 400. Alternatively, create an array for numbers 2 to 400, so array length 399. Hmm. Wait, sieve of Eratosthenes works by initializing an array of booleans, all true except index 0 and 1. Then, for each prime starting from 2, mark multiples as non-prime. So here, the numbers are 2-400. So the array can be of size 400, with indices 0-399, where 0 represents 2, 1 represents 3, etc. Wait no, better to have the index correspond to the number. So index 0 is 2? Maybe not. Let me think. Alternatively, let's have an array of size 400 where index n represents number n+2 (since starting from 2). Wait, not sure. Maybe better to have an array of booleans where index n is true if n is a prime. So for numbers 2 to 400, indices 2-400. Wait, let me clarify: Numbers to process are 2 to 400. Initialize an array isPrime of size 401 (indices 0-400), where isPrime[i] is true if i is a prime. Initially, set all to true, except indices 0 and 1 (since 0 and 1 are not primes). Wait, but we start from 2. Wait, the sieve steps: Initialize isPrime[0] = false, isPrime[1] = false, and the rest true. Then, for each number p from 2 to sqrt(400), if isPrime[p] is true, mark multiples of p starting from p*p as not prime. But for the animation, we need to process primes in order, so for each prime p starting from 2, then next unmarked primes, then process their multiples. The animation steps: Start with all numbers as possible primes. Then, for each prime p: - highlight p (mark it as prime) - then, step through each multiple of p (starting from p*p, but actually, multiples are p*2, p*3, etc. but in the sieve, we start from p*p because lower multiples have been processed already). Wait, in the Sieve of Eratosthenes, after marking a prime p, you start from p^2 and continue to mark multiples of p. But for the animation, to show the process, maybe it's better to show all multiples starting from p*2, but that might be redundant. The standard sieve skips to p^2 since smaller multiples are already marked by smaller primes. However, in the animation, to show all multiples, maybe we need to process all multiples except p itself. Alternatively, to make it clear, the animation should mark all multiples except p itself. So for prime p, we mark p*2, p*3, etc. But since the sieve uses p*p as the starting point to avoid redundancy, but for animation purposes, perhaps it's better to show all multiples, even if they were already marked by earlier primes. Wait, but that might not be accurate. Hmm, the problem says
Show code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sieve of Eratosthenes</title>
<style>
html,body{margin:0;height:100%;background:#101114;color:#f4f1e8;font:16px system-ui,sans-serif;overflow:hidden}
body{display:grid;place-items:center}
#wrap{width:min(96vw,900px)}
canvas{display:block;width:100%;aspect-ratio:1;background:#17191f;border:1px solid #30343d}
#hud{display:flex;gap:20px;justify-content:center;align-items:center;padding:14px 0 0;font-weight:700}
span{color:#9fd8ff}
</style>
</head>
<body>
<div id="wrap">
<canvas id="c"></canvas>
<div id="hud">prime <span id="p">-</span> found <span id="f">0</span> eliminated <span id="e">0</span></div>
</div>
<script>
const canvas=document.getElementById("c"),ctx=canvas.getContext("2d"),P=document.getElementById("p"),F=document.getElementById("f"),E=document.getElementById("e");
let dpr,cols=20,rows=20,cell,ox,oy,state,primes,pi,marks,mi,current,found,elim,last,wait,hue,donePause;
function size(){
dpr=devicePixelRatio||1;
let r=canvas.getBoundingClientRect();
canvas.width=r.width*dpr;canvas.height=r.width*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);
cell=r.width/cols;ox=0;oy=0;draw();
}
function fresh(){
state=Array(401).fill(0);primes=[];pi=0;marks=[];mi=0;current=0;found=0;elim=0;last=0;wait=550;donePause=0;
hue=Math.random()*360;P.textContent="-";F.textContent=0;E.textContent=0;
draw();
}
function color(n,a=1){return `hsla(${(hue+n*31)%360},78%,58%,${a})`}
function nextPrime(){
for(let n=current+1;n<=400;n++)if(!state[n]){current=n;primes.push(n);found++;marks=[];for(let m=n*n;m<=400;m+=n)marks.push(m);mi=0;wait=520;P.textContent=n;F.textContent=found;return}
donePause=900;
}
function step(){
if(donePause){donePause=0;fresh();return}
if(!current||mi>=marks.length){nextPrime();return}
let n=marks[mi++];if(!state[n]){state[n]=current;elim++;E.textContent=elim}
wait=36;
}
function drawCell(n,x,y,s){
let v=state[n],isP=primes.includes(n),cur=n==current;
ctx.fillStyle=v?color(v,.5):(isP?"#283e35":"#20232b");ctx.fillRect(x,y,s,s);
ctx.strokeStyle="#343944";ctx.strokeRect(x+.5,y+.5,s-1,s-1);
if(v){ctx.strokeStyle=color(v,.95);ctx.lineWidth=Math.max(2,s*.07);ctx.beginPath();ctx.moveTo(x+s*.14,y+s*.86);ctx.lineTo(x+s*.86,y+s*.14);ctx.stroke()}
if(cur){ctx.strokeStyle="#fff4a8";ctx.lineWidth=Math.max(3,s*.1);ctx.strokeRect(x+s*.08,y+s*.08,s*.84,s*.84)}
ctx.fillStyle=v?"#d5d0c4":cur?"#fff7bf":"#eff3ff";ctx.font=`${Math.max(9,s*.31)}px system-ui`;ctx.textAlign="center";ctx.textBaseline="middle";ctx.fillText(n,x+s/2,y+s/2);
}
function draw(){
if(!ctx||!cell)return;
let w=canvas.clientWidth;ctx.clearRect(0,0,w,w);
for(let i=0,n=2;i<399;i++,n++)drawCell(n,ox+i%cols*cell,oy+(i/cols|0)*cell,cell);
}
function loop(t){
if(!last)last=t;
if(t-last>wait){last=t;step()}
draw();requestAnimationFrame(loop);
}
addEventListener("resize",size);
fresh();size();requestAnimationFrame(loop);
</script>
</body>
</html>Show code
<!DOCTYPE html><html><head><meta charset="utf8"><title>Sieve</title><style>
body{margin:0;background:#111;color:#eee;font-family:monospace}canvas{display:block;margin:20px auto;background:#181818}
</style></head><body>
<canvas id="c" width="820" height="640"></canvas>
<script>
const cv=document.getElementById('c'),cx=cv.getContext('2d');
const N=400,cols=20,rows=20,cell=36,ox=30,oy=60;
let colors={},state,cur,primes,elim,mulQueue,mi,frame,phase,pause;
function hueFor(p){return (p*47)%360}
function idx(n){return n-2}
function xy(n){let i=idx(n);return{x:ox+(i%cols)*cell,y:oy+Math.floor(i/cols)*cell}}
function reset(){
state=new Array(N+1).fill(0);
colors={};cur=2;primes=0;elim=0;mulQueue=[];mi=0;phase='findprime';pause=0;frame=0
}
function isPrimeCell(n){return state[n]===0}
function advanceFindPrime(){
while(cur<=N&&!isPrimeCell(cur))cur++;
if(cur>N){phase='done';pause=100;return}
state[cur]=1;primes++;colors[cur]=hueFor(cur);
mulQueue=[];for(let m=cur*cur;m<=N;m+=cur)mulQueue.push(m);
mi=0;phase='strike'
}
function advanceStrike(){
if(mi>=mulQueue.length){cur++;phase='findprime';return}
let m=mulQueue[mi];
if(state[m]===0){state[m]=2;colors[m]=hueFor(cur);elim++}
mi++
}
function draw(){
cx.fillStyle='#181818';cx.fillRect(0,0,cv.width,cv.height);
for(let n=2;n<=N;n++){
let p=xy(n);
cx.fillStyle='#222';
if(state[n]===1)cx.fillStyle='hsl('+colors[n]+',80%,55%)';
else if(state[n]===2)cx.fillStyle='hsl('+colors[n]+',60%,28%)';
cx.fillRect(p.x,p.y,cell-2,cell-2);
if(n===cur&&phase!=='done'){cx.strokeStyle='#fff';cx.lineWidth=3;cx.strokeRect(p.x-2,p.y-2,cell+2,cell+2);cx.lineWidth=1}
if(phase==='strike'&&mulQueue[mi]===n){cx.strokeStyle='#f66';cx.lineWidth=3;cx.strokeRect(p.x-2,p.y-2,cell+2,cell+2);cx.lineWidth=1}
cx.fillStyle=state[n]===0?'#ccc':'#111';
cx.font='11px monospace';cx.textAlign='center';
cx.fillText(n,p.x+cell/2-1,p.y+cell/2+4)
}
cx.fillStyle='#fff';cx.font='18px monospace';cx.textAlign='left';
let label=phase==='done'?'Done':cur;
cx.fillText('Current prime: '+label,20,25);
cx.fillText('Primes found: '+primes+' Cells eliminated: '+elim,300,25);
if(phase==='strike'){
cx.fillStyle='hsl('+hueFor(cur)+',80%,60%)';cx.font='14px monospace';
cx.fillText('striking multiples of '+cur,20,45)
}
}
function loop(){
frame++;
if(phase==='findprime'||phase==='strike'){
if(frame%3===0){
if(phase==='findprime')advanceFindPrime();else advanceStrike()
}
} else if(phase==='done'){
pause--;if(pause<=0)reset()
}
draw();
requestAnimationFrame(loop)
}
reset();loop();
</script></body></html>Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sieve of Eratosthenes</title>
<style>
body { margin: 0; padding: 10px; font-family: monospace; background: #000; color: #0f0; }
canvas { display: block; margin: 10px auto; background: #111; border: 1px solid #0f0; }
div { text-align: center; font-size: 12px; }
</style>
</head>
<body>
<canvas id="c" width="800" height="600"></canvas>
<div id="info"></div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const w = canvas.width, h = canvas.height;
const limit = 400;
const cols = Math.floor(Math.sqrt(limit));
const rows = Math.ceil(limit / cols);
const cellSize = Math.min(w / cols, h / rows) - 1;
let sieve = Array(limit + 1).fill(true);
sieve[0] = sieve[1] = false;
let current = 2;
let multiples = [];
let primeCount = 1;
let eliminated = 0;
let step = 0;
let time = 0;
let colors = {};
function animate() {
if (current <= Math.sqrt(limit)) {
if (sieve[current]) {
multiples = [];
for (let i = current * current; i <= limit; i += current) {
if (sieve[i]) {
sieve[i] = false;
multiples.push(i);
eliminated++;
}
}
if (multiples.length === 0) {
primeCount++;
}
}
const oldCurrent = current;
do {
current++;
} while (current <= limit && !sieve[current] && current <= Math.sqrt(limit));
if (current > Math.sqrt(limit)) {
for (let i = current; i <= limit; i++) {
if (sieve[i]) primeCount++;
}
current = limit + 1;
}
} else if (time > 180) {
sieve = Array(limit + 1).fill(true);
sieve[0] = sieve[1] = false;
current = 2;
multiples = [];
primeCount = 1;
eliminated = 0;
time = 0;
colors = {};
}
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, w, h);
for (let i = 2; i <= limit; i++) {
const x = (i - 2) % cols;
const y = Math.floor((i - 2) / cols);
if (i === current) {
ctx.fillStyle = '#ff0';
} else if (!sieve[i]) {
if (!colors[current]) colors[current] = `hsl(${(current * 137) % 360}, 100%, 50%)`;
ctx.fillStyle = colors[current] || '#f00';
} else {
ctx.fillStyle = '#222';
}
ctx.fillRect(x * cellSize, y * cellSize, cellSize - 1, cellSize - 1);
}
ctx.fillStyle = '#0f0';
ctx.font = '12px monospace';
ctx.textAlign = 'left';
ctx.fillText(`Primes: ${primeCount} | Eliminated: ${eliminated} | Current: ${current}`, 20, 560);
time++;
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>Results at a glance— every model in this round with score, response time and status.
Who wrote what
| Model | Blind score | Latency | Status |
|---|---|---|---|
| qwen3:8b | — | — | harness |
| llama3.1:8b | — | 7.9 s | OK |
| mistral-small:24b | — | 174.0 s | OK |
| qwen3-coder:30b | — | 29.6 s | OK |
| qwen3:14b | — | 26.7 s | OK |
| deepseek-r1:14b | — | 110.5 s | OK |
| qwen3.5:9b | — | 27.8 s | OK |
| gemma4:26b | — | — | harness |
| deepseek-r1:32b | — | 140.9 s | OK |
| command-r:35b | — | 124.4 s | OK |
| qwq:32b | — | 140.9 s | OK |
Frontier reference — outside the blind field
These models answered the identical prompt outside the blind field. They are deliberately not scored and not part of the blind comparison — they are there so you can see where a frontier model lands on the same task.
Show code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sieve of Eratosthenes</title>
<style>
html,body{margin:0;height:100%;background:#070a12;overflow:hidden;display:flex;align-items:center;justify-content:center}
canvas{max-width:100vw;max-height:100vh;display:block}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const N=400,COLS=20,ROWS=20,CELL=32,PADX=26,TOP=104,BOT=30;
const W=PADX*2+COLS*CELL,H=TOP+ROWS*CELL+BOT;
const F='ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif';
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
const dpr=Math.min(2,window.devicePixelRatio||1);
cv.width=W*dpr;cv.height=H*dpr;cv.style.width=W+'px';cv.style.height=H+'px';ctx.scale(dpr,dpr);
let S,prev=0;
function reset(){
S={mark:new Int16Array(N+1).fill(-1),primes:[],cur:1,mults:[],mi:0,mode:'find',elim:0,last:0,flash:0,
hue0:Math.random()*360,spin:Math.random()<.5?1:-1,gap:15+Math.random()*13,step:15+Math.random()*13,acc:0,done:0,hold:0};
}
function hueOf(p){let h=(S.hue0+S.spin*p*S.gap)%360;return (h+360)%360;}
function findNext(){for(let n=S.cur+1;n<=N;n++)if(S.mark[n]<0)return n;return 0;}
function step(){
if(S.mode==='mult'){
while(S.mi<S.mults.length){
const m=S.mults[S.mi++];
if(S.mark[m]<0){S.mark[m]=hueOf(S.cur)|0;S.elim++;S.last=m;S.flash=1;return;}
}
S.mode='find';
}
const n=findNext();
if(!n){S.done=1;return;}
S.cur=n;S.primes.push(n);S.last=n;S.flash=1;S.mults=[];
for(let m=n*2;m<=N;m+=n)S.mults.push(m);
S.mi=0;S.mode='mult';
}
function rr(x,y,w,h,r){ctx.beginPath();if(ctx.roundRect)ctx.roundRect(x,y,w,h,r);else ctx.rect(x,y,w,h);}
function stat(x,label,val,col){
ctx.textAlign='left';ctx.textBaseline='alphabetic';
ctx.fillStyle='#59648c';ctx.font='600 10px '+F;ctx.fillText(label,x,56);
ctx.fillStyle=col;ctx.font='700 27px '+F;ctx.fillText(val,x,88);
}
function draw(){
const g=ctx.createLinearGradient(0,0,W,H);
g.addColorStop(0,'#070a12');g.addColorStop(1,'#101629');ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
const ch=hueOf(S.cur||2);
ctx.textAlign='left';ctx.textBaseline='alphabetic';
ctx.fillStyle='#4a5578';ctx.font='600 11px '+F;
ctx.fillText('SIEVE OF ERATOSTHENES · 2 – 400',PADX,28);
const cw=(W-PADX*2)/3;
stat(PADX,'CURRENT PRIME',S.primes.length?String(S.cur):'–','hsl('+ch+',90%,66%)');
stat(PADX+cw,'PRIMES FOUND',String(S.primes.length),'#e8eeff');
stat(PADX+cw*2,'CELLS ELIMINATED',String(S.elim),'#8fa2d6');
for(let n=1;n<=N;n++){
const i=n-1,x=PADX+(i%COLS)*CELL,y=TOP+((i/COLS)|0)*CELL,m=S.mark[n];
const bx=x+2,by=y+2,s=CELL-4;
let txt='#8c98bd',bold=false;
rr(bx,by,s,s,6);
if(n===1){ctx.fillStyle='#0e1322';ctx.fill();txt='#333c58';}
else if(m>=0){ctx.fillStyle='hsl('+m+',55%,13%)';ctx.fill();txt='hsl('+m+',35%,44%)';}
else if(n<=S.cur||S.done){
ctx.fillStyle='hsl('+hueOf(n)+',75%,20%)';ctx.fill();
ctx.strokeStyle='hsl('+hueOf(n)+',90%,62%)';ctx.lineWidth=1.4;ctx.stroke();
txt='#ffffff';bold=true;
}else{ctx.fillStyle='#0f1524';ctx.fill();ctx.strokeStyle='#1b2337';ctx.lineWidth=1;ctx.stroke();}
ctx.fillStyle=txt;ctx.font=(bold?'700 ':'500 ')+11+'px '+F;
ctx.textAlign='center';ctx.textBaseline='middle';
ctx.fillText(String(n),x+CELL/2,y+CELL/2+.5);
if(m>=0){
ctx.strokeStyle='hsla('+m+',90%,62%,.85)';ctx.lineWidth=2;ctx.lineCap='round';
ctx.beginPath();ctx.moveTo(bx+4,by+s-4);ctx.lineTo(bx+s-4,by+4);ctx.stroke();
}
if(n===S.last&&S.flash>0){
ctx.save();ctx.globalAlpha=S.flash*.85;
ctx.strokeStyle='#ffffff';ctx.lineWidth=2;rr(bx-1,by-1,s+2,s+2,7);ctx.stroke();ctx.restore();
}
if(n===S.cur&&!S.done){
const pulse=.55+.45*Math.sin(performance.now()/210);
ctx.save();ctx.shadowColor='hsl('+ch+',95%,60%)';ctx.shadowBlur=14*pulse;
ctx.strokeStyle='hsla('+ch+',95%,70%,'+(.6+.4*pulse)+')';ctx.lineWidth=2.4;
rr(bx-1.5,by-1.5,s+3,s+3,8);ctx.stroke();ctx.restore();
}
}
const prog=S.done?1:(S.primes.length?(S.cur-1)/(N-1):0);
ctx.fillStyle='#161d31';rr(PADX,H-16,W-PADX*2,4,2);ctx.fill();
ctx.fillStyle='hsl('+ch+',85%,58%)';rr(PADX,H-16,(W-PADX*2)*prog,4,2);ctx.fill();
}
function frame(ts){
if(!prev)prev=ts;
const dt=Math.min(64,ts-prev);prev=ts;
S.flash=Math.max(0,S.flash-dt/280);
if(S.done){S.hold+=dt;if(S.hold>1700)reset();}
else{S.acc+=dt;let guard=0;while(S.acc>=S.step&&!S.done&&guard++<24){S.acc-=S.step;step();}}
draw();
requestAnimationFrame(frame);
}
reset();requestAnimationFrame(frame);
</script>
</body>
</html>