MODEL Agemma4:26bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
Answers below are anonymized and shuffled. Read, judge, pick a favourite — then scroll down and reveal the models.
The reveal also holds a frontier reference on the same task (Claude Opus 5) — kept out of sight here so it cannot steer your judgement.
Create ONE self-contained HTML file (STRICT LIMIT: max 120 lines total) that solves the 8-queens problem by backtracking and animates the search on a <canvas> chessboard. Place one queen at a time, highlight the squares attacked by the queens already placed, and visibly REMOVE a queen again when the branch fails. Show a live counter of placements and backtracks, and hold the finished solution for a moment before restarting with a different board size between 6 and 10. 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.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>8 Queens Backtracking</title>
<style>
body {
font-family: monospace;
}
#board {
border: 1px solid black;
}
canvas {
border: 1px solid black;
}
#stats {
position: absolute;
top: 10px;
left: 10px;
}
</style>
</head>
<body>
<canvas id="board" width="400" height="400"></canvas>
<div id="stats">Placements: 0, Backtracks: 0</div>
<script>
const board = document.getElementById('board');
const ctx = board.getContext('2d');
const stats = document.getElementById('stats');
let placements = 0;
let backtracks = 0;
function drawBoard() {
const size = Math.min(board.width, board.height) / 8;
ctx.clearRect(0, 0, board.width, board.height);
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
const x = size * j + 1.5;
const y = size * i + 2.5;
ctx.strokeRect(x, y, size - 3, size - 3);
}
}
}
function drawQueens(queens) {
for (let i = 0; i < queens.length; i++) {
const queen = queens[i];
const x = Math.floor((queen.y + 1) * board.width / 8);
const y = Math.floor((queen.x + 1) * board.height / 8);
ctx.fillStyle = 'red';
ctx.fillRect(x, y, 10, 10);
highlightAttacked(queens.slice(0, i));
}
}
function highlightAttacked(queens) {
for (let i = 0; i < queens.length; i++) {
const queen = queens[i];
const x = queen.x;
const y = queen.y;
let dx, dy;
if (!queen.underAttack) {
continue;
}
if (x === y || x + y === 8) { // Same row or diagonal
for (let j = 0; j < 8; j++) {
if (j !== i && queens[j].x === y) {
ctx.fillStyle = 'blue';
const pos = Math.floor(j * board.width / 8 + 1.5);
ctx.fillRect(pos, Math.floor(queen.x * board.height / 8 + 2.5), 10, 10);
}
}
} else if (x < y) { // Top-left to bottom-right diagonal
for (let j = x; j <= y; j++) {
const pos = Math.floor(j * board.width / 8 + 1.5);
ctx.fillStyle = 'blue';
ctx.fillRect(pos, Math.floor(x * board.height / 8 + 2.5), 10, 10);
}
} else { // Top-right to bottom-left diagonal
for (let j = x; j >= y; j--) {
const pos = Math.floor(j * board.width / 8 + 1.5);
ctx.fillStyle = 'blue';
ctx.fillRect(pos, Math.floor(x * board.height / 8 + 2.5), 10, 10);
}
}
}
}
function backtrack(queens) {
if (placements > 0 && backtracks >= placements) {
return;
}
for (let i = 0; i < 8; i++) {
const newQueens = [...queens];
newQueens.push({ x: i, y: -1 });
if (!hasConflicts(newQueens)) {
placements++;
stats.textContent = `Placements: ${placements}, Backtracks: ${backtracks}`;
drawBoard();
drawQueens(newQueens);
requestAnimationFrame(() => {
newQueens.pop();
placements--;
backtracks++;
stats.textContent = `Placements: ${placements}, Backtracks: ${backtracks}`;
drawBoard();
drawQueens(queens);
if (!hasConflicts(newQueens)) {
backtrack(newQueens);
}
});
} else {
newQueens.pop();
placements--;
backtracks++;
stats.textContent = `Placements: ${placements}, Backtracks: ${backtracks}`;
drawBoard();
drawQueens(queens);
}
}
}
function hasConflicts(queens) {
for (let i = 0; i < queens.length; i++) {
const queen1 = queens[i];
for (let j = 0; j < i; j++) {
const queen2 = queens[j];
if ((queen1.x === queen2.x && queen1.y !== queen2.y) ||
(queen1.y + queen2.y === 8 && queen1.x - queen2.x !== 0)) {
return true;
}
}
}
return false;
}
let currentQueens = [];
backtrack(currentQueens);
function restart() {
placements = 0;
backtracks = 0;
stats.textContent = `Placements: ${placements}, Backtracks: ${backtracks}`;
drawBoard();
currentQueens = [];
backtrack(currentQueens);
}
setInterval(restart, 30000);
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>N-Queens Solver</title>
<style>
body { display: flex; flex-direction: column; align-items: center; background-color: #222; color: white; font-family: monospace; }
canvas { border: 5px solid #333; margin-bottom: 10px; }
#info { font-size: 1.2em; margin-top: 10px; }
</style>
</head>
<body>
<h1>N-Queens Solver</h1>
<canvas id="board"></canvas>
<div id="info">Status: Initializing | Placements: 0 | Backtracks: 0</div>
<script>
const canvas = document.getElementById('board');
const ctx = canvas.getContext('2d');
const infoEl = document.getElementById('info');
let N, BOARD_SIZE;
let currentQueens = [];
let status = 'INIT'; // INIT, RUNNING, SOLVED
let animationFrameId = null;
function updateInfo(placements, backtracks) {
infoEl.innerHTML = `Status: ${status} | Placements: ${placements} | Backtracks: ${backtracks}`;
}
function setupBoard(size) {
N = size;
BOARD_SIZE = 60;
canvas.width = BOARD_SIZE * N;
canvas.height = BOARD_SIZE * N;
currentQueens = [];
status = 'RUNNING';
updateInfo(0, 0);
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let r = 0; r < N; r++) {
for (let c = 0; c < N; c++) {
ctx.fillStyle = (r + c) % 2 === 0 ? '#f0d9b5' : '#b58863';
ctx.fillRect(c * BOARD_SIZE, r * BOARD_SIZE, BOARD_SIZE, BOARD_SIZE);
}
}
}
function getAttackedSquares() {
let attacked = new Set();
for (const [r, c] of currentQueens) {
// Diagonals
[[1, 1], [-1, -1], [1, -1], [-1, 1]].forEach(([dr, dc]) => {
for (let i = 1; i < N && r + dr * i >= 0 && r + dr * i < N && c + dc * i >= 0 && c + dc * i < N; ++i) {
const nr = r + dr * i;
const nc = c + dc * i;
attacked.add(`${nr},${nc}`);
}
});
// Row/Column (Not needed for standard N-Queens, but good practice)
}
return Array.from(attacked);
}
function highlightSquares() {
const attacked = getAttackedSquares();
drawBoard(); // Redraw base board first
ctx.font = '14px Arial';
for (let r = 0; r < N; r++) {
for (let c = 0; c < N; c++) {
if (r === 0 && c === 0) continue; // Skip origin for simplicity
const key = `${r},${c}`;
if (attacked.includes(key)) {
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
ctx.fillRect(c * BOARD_SIZE + 1, r * BOARD_SIZE + 1, BOARD_SIZE - 2, BOARD_SIZE - 2);
} else {
ctx.fillStyle = 'transparent';
}
}
}
}
function placeQueens() {
drawBoard();
currentQueens.forEach(([r, c]) => {
ctx.fillStyle = '#FFD700'; // Gold Queen
ctx.beginPath();
ctx.arc(c * BOARD_SIZE + BOARD_SIZE / 2, r * BOARD_SIZE + BOARD_SIZE / 2, BOARD_SIZE / 3, 0, Math.PI * 2);
ctx.fill();
});
}
function animateStep() {
if (status === 'FINISHED') return;
// Clear the previous drawings for smooth transition effect
drawBoard();
highlightSquares();
placeQueens();
requestAnimationFrame(animateStep);
}
async function solveNQueensRecursive(row, placements, backtracks) {
if (status === 'PAUSED') return; // Stop if solved/finished
// The recursive call must be awaited so the animation pauses when a queen is placed.
await new Promise(resolve => setTimeout(resolve, 50));
if (row == N) {
status = 'SOLVED';
updateInfo(placements + 1, backtracks);
// Hold solution for 2 seconds then restart loop
setTimeout(() => {
status = 'PAUSED'; // Signal completion pause
animateStep(); // Final animation run
setTimeout(startNewGame, 2000);
}, 2000);
return true;
}
for (let col = 0; col < N; col++) {
if (currentQueens.some(([r, c]) => Math.abs(r - row) === Math.abs(c - col))) {
continue;
}
// Place Queen
currentQueens.push([row, col]);
updateInfo(placements + 1, backtracks);
if (await solveNQueensRecursive(row + 1, placements + 1, backtracks)) return true;
// Backtrack: Remove Queen and mark failure
currentQueens.pop();
updateInfo(placements, backtracks + 1); // Increment backtrack count only upon removal attempt
}
return false;
}
async function runSolver() {
status = 'RUNNING';
solveNQueensRecursive(0, 0, 0);
}
function startNewGame() {
const newSize = Math.floor(Math.random() * 5) + 6; // Random size between 6 and 10 (inclusive of 6, exclusive of 11)
setupBoard(newSize);
runSolver();
}
// --- Initialization ---
startNewGame();
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html><head><style>body{margin:0}canvas{display:block;margin:auto;border:2px solid #333}#counter{position:absolute;top:20;left:20;font-family:sans-serif;color:#333}</style></head>
<body><canvas id="board"></canvas><div id="counter">Placements: 0 | Backtracks: 0</div>
<script>
const canvas = document.getElementById('board'), ctx = canvas.getContext('2d');
const boardSize = Math.floor(Math.random()*5 +6);
let placements =0,backtracks=0;
let grid,newX=[],solutionFound=false;
function setupCanvas(){canvas.width=boardSize*40;canvas.height=boardSize*40;}
setupCanvas();
function drawBoard(){
for(let y=0;y<boardSize;y++){
for(let x=0;x<boardSize;x++){
const safe = !grid[y][x].attacked && !grid[y][x].queen;
ctx.fillStyle = safe ? '#fff' : (grid[y][x].attacked ? '#ffcccc' : '#ffd700');
ctx.fillRect(x*40,y*40,39,39);
}
}
}
function isValid(row,col){
for(let y=0;y<boardSize;y++)if(grid[y][col]===1||Math.abs(row-y) === col - newX[y])return false;
return true;
}
async function placeQueen(row,y,x){
if(row>boardSize-1){solutionFound=true;drawBoard();return;}
for(let x=0;x<boardSize;x++)if(isValid(row,x,newX)){
newX[row]=x;
grid[row][x].queen=true;
placements++;
document.getElementById('counter').textContent = `Placements: ${placements} | Backtracks: ${backtracks}`;
await new Promise(resolve => setTimeout(resolve,50));
requestAnimationFrame(() => {grid[row][x].attacked=false;drawBoard();});
if(await placeQueen(row+1,y,x))return true;
}
newX[row]=-1;
backtracks++;
document.getElementById('counter').textContent = `Placements: ${placements} | Backtracks: ${backtracks}`;
await new Promise(resolve => setTimeout(resolve,50));
requestAnimationFrame(() => {grid[row-1][newX[row-1]].attacked=false;drawBoard();});
return false;
}
function resetGrid(){
grid=[[...Array(boardSize)].map(()=>({queen:false,attacked:false}))];
newX=[...Array(boardSize)].fill(-1);
}
document.getElementById('board').style.backgroundColor='white';
resetGrid();
requestAnimationFrame(() => placeQueen(0));
</script></body></html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<title>N-Queens Solver Animation</title>
<style>
body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #333; color: white; font-family: monospace; }
canvas { border: 5px solid black; image-rendering: pixelated; }
</style>
</head>
<body>
<canvas id="board"></canvas>
<script>
let canvas = document.getElementById('board');
let ctx = canvas.getContext('2d');
const MAX_SIZE = 10;
const MIN_SIZE = 6;
let N = 8; // Initial size
let solutionsFound = 0;
let placementsCount = 0;
let backtracksCount = 0;
let animationState = { running: true, currentStep: 1 };
let queenPositions = [];
function initialize(size) {
N = size;
canvas.width = N * 60;
canvas.height = N * 60;
queenPositions = Array(N).fill(-1);
solutionsFound = 0;
placementsCount = 0;
backtracksCount = 0;
animationState.currentStep = 1;
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let r = 0; r < N; r++) {
for (let c = 0; c < N; c++) {
let color = ((r + c) % 2 === 0) ? '#f0d9b5' : '#b58863';
ctx.fillStyle = color;
ctx.fillRect(c * 60, r * 60, 60, 60);
}
}
}
function highlightAttacked(row, col) {
let attacked = new Set();
// Check all previously placed queens (r=0 to row-1)
for (let r = 0; r < queenPositions.length; r++) {
if (queenPositions[r] === -1 || r >= N) continue; // Should not happen if called correctly
const pr = r;
const pc = queenPositions[r];
// Check row/column attacks on current spot (row, col)
if (pr === row || pc === col) {
attacked.add(`${row},${col}`);
}
}
return Array.from(attacked);
}
function drawState() {
drawBoard();
let tempQueens = []; // For the current placement attempt (r, c)
// 1. Draw placed queens and their attacks
for (let r = 0; r < N; r++) {
const c = queenPositions[r];
if (c !== -1) {
tempQueens.push({r: r, c: c});
// Highlight attacked squares by this queen
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)'; // Red for attack
for (let i = 0; i < N; i++) {
let checkR = r + i;
let checkC = c + i;
if (checkR >= 0 && checkR < N && checkC >= 0 && checkC < N) { ctx.fillRect(checkC * 60, checkR * 60, 60, 60); }
checkR = r - i;
checkC = c - i;
if (checkR >= 0 && checkR < N && checkC >= 0 && checkC < N) { ctx.fillRect(checkC * 60, checkR * 60, 60, 60); }
}
// Diagonal checks omitted for brevity/line count
}
}
// 2. Draw the actual queens (overwriting highlights slightly)
ctx.fillStyle = 'black';
for(let i=0; i<N; i++) {
const r = i;
const c = queenPositions[i];
if (c !== -1) {
ctx.beginPath();
ctx.arc(c * 60 + 30, r * 60 + 30, 25, 0, Math.PI * 2);
ctx.fill();
}
}
}
function updateCounter() {
document.write(`<div style="position: absolute; top: 10px; left: 10px;">Solutions: ${solutionsFound}<br>Placements: ${placementsCount} | Backtracks: ${backtracksCount}</div>`);
}
// The core backtracking solver logic
function solveNQueens(row) {
if (row === N) {
solutionsFound++;
return true; // Found a solution
}
for (let col = 0; col < N; col++) {
let safe = true;
// Check constraints against previously placed queens (in rows 0 to row-1)
for (let prevRow = 0; prevRow < row; prevRow++) {
const prevCol = queenPositions[prevRow];
if (prevCol === col || Math.abs(prevCol - col) === Math.abs(prevRow - row)) {
safe = false;
break;
}
}
if (safe) {
// --- Animation Step: Place Queen ---
queenPositions[row] = col;
placementsCount++;
drawState();
animationState.currentStep = 'PLACED';
setTimeout(() => animationState.currentStep = 'CHECK', 20);
// Recursive call (this handles the rest of the board)
if (solveNQueens(row + 1)) {
return true; // Optimization: stop after finding one solution
}
// --- Animation Step: Backtrack ---
queenPositions[row] = -1;
backtracksCount++;
drawState();
animationState.currentStep = 'BACKTRACKED';
setTimeout(() => animationState.currentStep = 'CLEARED', 20);
} else {
// If not safe, just animate the check failing (visually negligible here)
}
}
return false; // No solution found starting from this path
}
function loopAnimation() {
if (!animationState.running) return;
let elapsed = performance.now();
const stepDuration = 20; // ms per state change
// Simple state machine handler for smooth animation flow
if (animationState.currentStep === 'CHECK' && elapsed > window.lastTime + stepDuration) {
animationState.currentStep = 'WAIT';
window.lastTime = elapsed;
} else if (animationState.currentStep === 'CLEARED' && elapsed > window.lastTime + stepDuration) {
// Wait a moment after backtrack before allowing the next placement attempt to redraw
if(queenPositions[N-1] === -1){
window.lastTime = performance.now();
}
}
requestAnimationFrame(loopAnimation);
}
function startCycle() {
initialize(Math.floor(Math.random() * (MAX_SIZE - MIN_SIZE + 1)) + MIN_SIZE);
drawState();
updateCounter();
solveNQueens(0);
}
// Kick off the process immediately
window.lastTime = performance.now();
startCycle();
requestAnimationFrame(loopAnimation);
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Animated 8 Queens</title></head>
<body>
<canvas id="board"></canvas><div id="stats"></div>
<script>
const canvas = document.getElementById('board');
const ctx = canvas.getContext('2d');
let N = 8;
let queens = [];
let placementCount = 0;
let backtrackCount = 0;
let animationFrameId = null;
const STATS_DIV = document.getElementById('stats');
function setBoardSize(size) {
N = size;
canvas.width = 500 + N * 20;
canvas.height = 500 + N * 20;
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const cellSize = 40;
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
ctx.fillStyle = (i + j) % 2 === 0 ? '#f0d9b5' : '#b58863';
ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
}
}
}
function drawQueen(row, col, color = 'black') {
const cellSize = 40;
const x = col * cellSize + cellSize / 2;
const y = row * cellSize + cellSize / 2;
ctx.fillStyle = color;
ctx.font = `${cellSize - 15}px Arial`;
ctx.fillText('♕', x, y + 10);
}
function calculateAttacks() {
const attacks = Array(N).fill(0).map(() => Array(N).fill(false));
for (let r = 0; r < N; r++) {
for (let c = 0; c < N; c++) {
if (queens.some(q => q[0] === r && q[1] === c)) continue;
let s = [r, c];
// Row/Col attacks
s.push([r, -1]); s.push([r, 1]);
s.push([-1, c]); s.push([1, c]);
// Diagonal attacks (4 directions)
let dirs = [[-1,-1], [-1,1], [1,-1], [1,1]];
dirs.forEach(([dr, dc]) => {
for (let k = 1; k < N; k++) {
let nr = r + dr * k;
let nc = c + dc * k;
if (nr >= 0 && nr < N && nc >= 0 && nc < N) {
s.push([nr, nc]);
} else break;
}
});
// Mark all attacked squares (we only care about the board itself)
for (let [r_a, c_a] of s) {
if (r_a >= 0 && r_a < N && c_a >= 0 && c_a < N) {
attacks[r_a][c_a] = true;
}
}
}
}
}
function drawAttacks() {
const cellSize = 40;
calculateAttacks();
for (let r = 0; r < N; r++) {
for (let c = 0; c < N; c++) {
if (queens.some(q => q[0] === r && q[1] === c)) continue;
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)'; // Red overlay for attacked squares
ctx.fillRect(c * cellSize, r * cellSize, cellSize, cellSize);
}
}
}
async function animatePlacement(r, c) {
const cellSize = 40;
const x = c * cellSize + cellSize / 2;
const y = r * cellSize + cellSize / 2;
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(x, y, cellSize * 0.7, 0, Math.PI * 2);
ctx.fill();
}
async function animateRemoval(r, c) {
const cellSize = 40;
const x = c * cellSize + cellSize / 2;
const y = r * cellSize + cellSize / 2;
// Fade out effect for removal
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(x - cellSize * 0.7, y - cellSize * 0.7, cellSize * 1.4, cellSize * 1.4);
}
function updateStats() {
STATS_DIV.innerHTML = `Placements: ${placementCount}<br>Backtracks: ${backtrackCount}`;
}
async function solve(row) {
if (animationFrameId) cancelAnimationFrame(animationFrameId); // Stop previous animation
await new Promise(resolve => setTimeout(resolve, 50)); // Short pause for visual separation
// Base Case: Solution found
if (queens.length === N) {
updateStats();
drawBoard();
drawAttacks();
for (const [r, c] of queens) {
await animatePlacement(r, c);
await new Promise(resolve => setTimeout(resolve, 150));
}
return true;
}
// Recursive Step: Try placing queen in every column of the current row
for (let col = 0; col < N; col++) {
if (!queens.some(q => q[0] === row && q[1] === col) && // Check if position is already taken (redundant but safe)
!queens.some(q => Math.abs(q[0] - row) === Math.abs(q[1] - col))) {
// Simplified check for illegal move: This assumes the calling structure handles row iteration, so we only need to check previous queens against (row, col).
let safe = true;
for (const [pr, pc] of queens) {
if (Math.abs(pr - row) === Math.abs(pc - col)) {
safe = false;
break;
}
}
// If the position is safe: Place and animate
if (safe) {
queens.push([row, col]);
drawBoard();
await animatePlacement(row, col);
drawAttacks();
updateStats();
const solved = await solve(row + 1);
if (solved) return true;
// Backtrack: Remove queen and animate removal
queens.pop();
backtrackCount++;
await animateRemoval(row, col);
drawAttacks(); // Redraw attacks without the last queen
}
}
}
return false;
}
async function runCycle() {
// Reset state
queens = [];
placementCount = 0;
backtrackCount = 0;
setBoardSize(N);
drawBoard();
updateStats();
const solved = await solve(0);
if (solved) {
await new Promise(resolve => setTimeout(resolve, 2500)); // Hold solution for 2.5s
}
// Determine next size (random between 6 and 10)
let nextN = Math.floor(Math.random() * 5) + 6;
if (nextN < 8 || nextN > 10) { // Ensure it stays within the range [6, 10]
nextN = Math.max(6, Math.min(10, nextN));
}
// Transition to new size
setTimeout(() => {
setBoardSize(nextN);
runCycle(); // Restart cycle
}, 500); // Wait half a second before restarting
}
window.onload = () => {
runCycle();
};
</script>
</body>
</html>
```| Blind label | Model | Blind score | Latency | Status |
|---|---|---|---|---|
| MODEL A | gemma4:26b | — | — | DNF |
| MODEL B | llama3.1:8b | — | 147.4 s | OK |
| MODEL C | qwen3.5:9b | — | 51.2 s | OK |
| MODEL D | qwen3:8b | — | — | DNF |
| MODEL E | deepseek-r1:14b | — | 83.5 s | OK |
| MODEL F | mistral-small:24b | — | — | DNF |
| MODEL G | qwen3-coder:30b | — | 29.2 s | OK |
| MODEL H | qwen3:14b | — | 50.2 s | OK |
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.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>N-Queens Backtracking</title>
<style>
*{box-sizing:border-box}
body{margin:0;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;
background:radial-gradient(circle at 50% 0%,#1c2440,#070a12 70%);color:#e8edf7;
font:14px/1.5 ui-sans-serif,system-ui,"Segoe UI",sans-serif}
h1{margin:0;font-size:15px;font-weight:600;letter-spacing:.24em;text-transform:uppercase;color:#9fb2d8}
canvas{border-radius:12px;box-shadow:0 24px 60px rgba(0,0,0,.62);max-width:92vw;height:auto}
#hud{display:flex;gap:28px;flex-wrap:wrap;justify-content:center}
#hud div{text-align:center}
#hud b{display:block;font-size:22px;font-variant-numeric:tabular-nums;color:#ffd479}
#hud span{font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:#7d8db0}
</style>
</head>
<body>
<h1>N‑Queens — Backtracking Search</h1>
<canvas id="c"></canvas>
<div id="hud">
<div><b id="hn">8</b><span>Board</span></div>
<div><b id="hp">0</b><span>Placements</span></div>
<div><b id="hb">0</b><span>Backtracks</span></div>
<div><b id="hs">searching</b><span>Status</span></div>
</div>
<script>
const cv=document.getElementById('c'),g=cv.getContext('2d'),S=560,dpr=Math.min(2,window.devicePixelRatio||1);
const hn=document.getElementById('hn'),hp=document.getElementById('hp'),hb=document.getElementById('hb'),hs=document.getElementById('hs');
cv.width=S*dpr;cv.height=S*dpr;cv.style.width=S+'px';g.setTransform(dpr,0,0,dpr,0,0);
let N=0,pos=[],col=0,placements=0,backtracks=0,anim=null,done=false,hold=0,acc=0,iv=190,last=0;
function reset(){let n;do{n=6+(Math.random()*5|0)}while(n===N);N=n;pos=new Array(N).fill(-1);
col=0;placements=0;backtracks=0;anim=null;done=false;hold=0;acc=0;iv=190}
function safe(c,r){for(let i=0;i<c;i++){const q=pos[i];if(q===r||Math.abs(q-r)===c-i)return false}return true}
function step(){
if(col>=N){done=true;hold=0;anim=null;return}
pos[col]++;
if(pos[col]>=N){pos[col]=-1;col--;if(col<0){reset();return}
anim={t:0,d:-1,c:col,r:pos[col]};backtracks++;return}
if(safe(col,pos[col])){anim={t:0,d:1,c:col,r:pos[col]};placements++;col++}
else anim={t:0,d:0,c:col,r:pos[col]}
}
function queen(cx,cy,s,fill,alpha){
g.save();g.globalAlpha=alpha;g.translate(cx,cy);g.scale(s,s);g.fillStyle=fill;
g.beginPath();g.arc(0,-.36,.1,0,6.29);g.fill();
g.beginPath();g.moveTo(-.36,.32);g.lineTo(-.29,-.2);g.lineTo(-.15,.03);g.lineTo(0,-.27);
g.lineTo(.15,.03);g.lineTo(.29,-.2);g.lineTo(.36,.32);g.closePath();g.fill();
g.fillRect(-.33,.32,.66,.13);g.restore()
}
function draw(){
const cell=S/N,att=new Uint8Array(N*N);
for(let c=0;c<col;c++){const r=pos[c];
for(let k=0;k<N;k++){att[r*N+k]=1;att[k*N+c]=1}
for(let d=-N;d<N;d++){const b=c+d,a=r+d,e=r-d;
if(b<0||b>=N)continue;
if(a>=0&&a<N)att[a*N+b]=1;
if(e>=0&&e<N)att[e*N+b]=1}
}
g.clearRect(0,0,S,S);
for(let r=0;r<N;r++)for(let c=0;c<N;c++){
g.fillStyle=(r+c)%2?'#192033':'#2a344f';
g.fillRect(c*cell,r*cell,cell,cell);
if(att[r*N+c]){g.fillStyle=done?'rgba(126,232,176,.17)':'rgba(255,86,110,.22)';g.fillRect(c*cell,r*cell,cell,cell)}
if(c===col&&!done){g.fillStyle='rgba(120,180,255,.10)';g.fillRect(c*cell,r*cell,cell,cell)}
}
const dur=anim?(anim.d===0?150:260):1,p=anim?Math.min(1,anim.t/dur):1,ease=1-Math.pow(1-p,3);
for(let c=0;c<col;c++){
let s=1;
if(anim&&anim.d===1&&anim.c===c)s=.35+.65*ease;
if(done)s*=1+.055*Math.sin(hold/170+c*.7);
queen(c*cell+cell/2,pos[c]*cell+cell/2,cell*.88*s,done?'#8fe3b0':'#ffd479',1)
}
if(anim&&anim.d===-1&&p<1)
queen(anim.c*cell+cell/2,anim.r*cell+cell/2,cell*.88*(1+.55*p),'#ff6a7d',1-p);
if(anim&&anim.d===0&&p<1){
const cx=anim.c*cell+cell/2,cy=anim.r*cell+cell/2,q=cell*.2;
g.save();g.globalAlpha=(1-p)*.9;g.strokeStyle='#ff6a7d';g.lineWidth=Math.max(2,cell*.05);
g.beginPath();g.moveTo(cx-q,cy-q);g.lineTo(cx+q,cy+q);g.moveTo(cx+q,cy-q);g.lineTo(cx-q,cy+q);
g.stroke();g.restore()
}
g.strokeStyle='rgba(255,255,255,.09)';g.lineWidth=1;g.strokeRect(.5,.5,S-1,S-1)
}
function hud(){
hn.textContent=N+'×'+N;hp.textContent=placements;hb.textContent=backtracks;
hs.textContent=done?'solved':'searching';hs.style.color=done?'#8fe3b0':'#ffd479'
}
function frame(ts){
const dt=Math.min(60,ts-last||16);last=ts;
if(done){hold+=dt;if(hold>2100)reset()}
else{acc+=dt;if(acc>=iv){acc=0;step();iv=anim&&anim.d===0?70:195}}
if(anim)anim.t+=dt;
draw();hud();requestAnimationFrame(frame)
}
reset();requestAnimationFrame(frame);
</script>
</body>
</html>