R64 · N-Queens solved by backtracking
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 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.
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>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>
```Show code
```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>
```Show code
```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>
```Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>8-Queens Solver</title>
<style>
body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
canvas { border: 1px solid black; }
#info { position: absolute; top: 10px; left: 10px; color: white; background: rgba(0, 0, 0, 0.5); padding: 10px; font-family: monospace; }
</style>
</head>
<body>
<canvas id="board" width="400" height="400"></canvas>
<div id="info">Placements: 0<br>Backtracks: 0</div>
<script>
const canvas = document.getElementById('board');
const ctx = canvas.getContext('2d');
const info = document.getElementById('info');
let size = Math.floor(Math.random() * 5) + 6;
let board = Array(size).fill(0);
let placements = 0;
let backtracks = 0;
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const cellSize = canvas.width / size;
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
ctx.strokeStyle = 'black';
ctx.fillStyle = (i + j) % 2 === 0 ? '#eee' : '#ccc';
ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
ctx.strokeRect(j * cellSize, i * cellSize, cellSize, cellSize);
}
}
}
function highlightAttacks() {
const cellSize = canvas.width / size;
for (let i = 0; i < board.length; i++) {
if (board[i] === 0) continue;
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
ctx.fillRect(board[i] * cellSize, i * cellSize, cellSize, cellSize);
}
}
function drawQueen(x, y) {
const cellSize = canvas.width / size;
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc((x + 0.5) * cellSize, (y + 0.5) * cellSize, cellSize * 0.3, 0, 2 * Math.PI);
ctx.fill();
}
function solve(row) {
if (row === size) {
setTimeout(() => restart(), 2000);
return true;
}
for (let col = 0; col < size; col++) {
placements++;
info.innerHTML = `Placements: ${placements}<br>Backtracks: ${backtracks}`;
drawBoard();
board[row] = col;
highlightAttacks();
drawQueen(col, row);
if (isSafe(row)) {
ctx.beginPath();
if (solve(row + 1)) return true;
}
backtracks++;
info.innerHTML = `Placements: ${placements}<br>Backtracks: ${backtracks}`;
board[row] = 0;
drawBoard();
highlightAttacks();
}
return false;
}
function isSafe(row) {
for (let i = 0; i < row; i++) {
if (board[i] === board[row] || Math.abs(board[i] - board[row]) === row - i)
return false;
}
return true;
}
function restart() {
size = Math.floor(Math.random() * 5) + 6;
board = Array(size).fill(0);
canvas.width = size * 40 + 'px';
canvas.height = size * 40 + 'px';
placements = 0;
backtracks = 0;
solve(0);
}
drawBoard();
solve(0);
function animate() {
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
```Show code
```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>
```Show code
```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>
```Show code
<think>
Okay, let's tackle this problem. The user wants an HTML file that solves the 8-queens problem using backtracking and animates it on a canvas. The constraints are pretty tight: self-contained, under 120 lines, no external libraries, and it has to restart with different board sizes between 6-10 after each solution. Hmm, okay, so I need to make sure the code is concise but still functional.
First, I'll start by setting up the basic HTML structure. The canvas element is necessary, so I'll include that. Then the JavaScript part will handle the animation and backtracking. Since the user specified a strict limit of 120 lines, I have to keep the code as compact as possible. Let me think about how to structure the backtracking algorithm.
The backtracking approach for 8-queens usually involves placing a queen in each row and checking for conflicts in columns and diagonals. So I'll need a function to check if a position is safe. The animation part requires drawing the chessboard, highlighting attacked squares, and showing queens. The canvas will need to be redrawn every frame, driven by requestAnimationFrame for smooth animation.
I need to track the current state of queens placed, the number of placements and backtracks, and the current board size. The animation should update these counters live. When a solution is found, it should pause briefly and then restart with a random size between 6-10. Each restart should initialize a new board size and reset the counters.
Let me outline the steps:
1. Canvas setup: Get the canvas context and set up variables like cell size, board size (initially 8), counters for placements and backtracks.
2. The backtracking function: This will recursively try placing queens. Since JavaScript doesn't support tail recursion well, I can implement it iteratively with a stack to avoid stack overflow for larger boards (though 10 is manageable).
3. Drawing functions: Functions to draw the chessboard, queens as circles, and highlight attacked squares. The highlighting can be done by checking which squares are under attack by existing queens.
4. Animation loop: Using requestAnimationFrame to continuously update and draw the current state. The backtracking steps should be executed in chunks to prevent freezing the UI, so each step (place queen or backtrack) happens in separate frames.
5. State management: Track the current column, the positions of placed queens, and whether a solution has been found. When backtracking, remove queens and backtrack the counters.
6. Initialization and restart: After a solution is found, display it for a moment, then generate a new board size between 6-10 and reset everything.
Wait, but how to handle the animation timing? Since requestAnimationFrame is asynchronous, I need to manage the backtracking steps in a way that each step is executed once per frame. So the backtracking function can't be a regular recursive function. Maybe use a loop with a delay between steps, but since there's no user input, it's okay to let the animation frame loop control the progress.
Alternatively, use a state machine approach where each step is part of the animation loop. For example, the backtracking process is represented by a state (current row, current column), and each frame executes a step (placing queen, checking, etc.) and redraws the canvas.
Hmm, to avoid recursion depth issues, maybe implement backtracking iteratively. Let's see.
Another point: the chessboard needs to alternate colors, so the background can be a checkerboard pattern. The queens can be simple circles, maybe with labels showing their position?
Wait, the user mentioned highlighting the squares attacked by existing queens. So whenever a queen is placed, the attacked squares (same row, column, diagonals) should be highlighted. So I need to calculate for each placed queen all the squares they attack and mark those squares, then draw them with a different color.
So the steps in code:
- Initialize the canvas, context, and variables.
- Setup the animation loop with requestAnimationFrame.
- Track the current state (positions array, current step).
- In each frame, perform one step of backtracking (place a queen, check if valid, proceed or backtrack).
- Redraw the board with current positions and highlights.
- When a solution is found, delay for a moment then restart with a new size.
Now, the backtracking algorithm. Let's think of it as:
We have an array 'queens' where each index represents the row, and the value is the column of the queen in that row. We try to place a queen in the next row (starting from row 0 to N-1). For each row, we try columns from left to right. If a valid column is found, proceed to next row. If not, backtrack to previous row and try next column there.
This can be implemented iteratively with a stack or using a loop that tracks the current row and next column to try. Let me try to structure it in code:
Maybe a loop that for each step, tries to place a queen in the current row. The loop will be driven by the animation frame, so each frame handles one step.
Variables:
- queens: array of columns for each row
- currentRow: which row we're trying to place next
- currentColumn: the next column to try in currentRow
- backtrack: a boolean flag indicating if backtracking is needed
The algorithm steps:
Initialize queens array to all -1 except first row? Or start with currentRow=0, currentColumn=0.
Wait, perhaps starting with queens as an empty array (or all -1). The loop would start at row 0, trying columns 0 to N-1. Once a valid column is found for row 0, proceed to row 1, etc.
But implementing this in an animation loop where each frame does one step requires managing the state. So perhaps the following approach:
- have a step function that does one step of the backtracking.
The step function would:
- If backtracking is needed (e.g., currentColumn exceeds N-1), then reset the queens' position for currentRow, decrement currentRow, increment backtracks counter.
- Otherwise, try to place a queen in (currentRow, currentColumn). Check if this is valid (no conflicts with existing queens). If valid, increment currentRow, set currentColumn to 0, and increment placements counter.
- If invalid, increment currentColumn and try again.
Wait, this is getting a bit tangled. Maybe it's better to code it step by step.
Alternatively, here's an iterative approach for backtracking:
Initialize queens array, current row to 0, current column starts at 0.
Loop:
if current column is within bounds:
try placing queen at (currentRow, currentCol)
if valid:
queens[currentRow] = currentCol
currentRow +=1
currentCol = 0
reset backtracking (no backtrack needed?)
else:
increment currentCol (but how?)
else:
// backtrack
reset queens[currentRow] to -1
currentRow -=1
// but need to go back to previous row and increment its column?
Hmm, perhaps need to track the current row and column in a way that can backtrack. Maybe it's better to represent the state as:
queens array, currentRow, and for each row, track the next column to try.
Alternatively, here's a possible approach in code:
The 'queens' array holds the current columns for each row (0-based). The currentRow is the row we are trying to place next. For each step:
- If currentRow is N, then we've found a solution.
- Else, iterate over possible columns for currentShow code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Solving 8 Queens Problem</title>
<style>
body {
margin: 0;
overflow: hidden;
}
canvas {
image-rendering: optimizeSpeed; /* Older versions of FF */
image-rendering: -moz-crisp-edges; /* FF 6+ */
image-rendering: -webkit-optimize-contrast; /* Safari 6+ */
image-rendering: crisp-edges; /* Experimental CSS (https://css-tricks.com/back-edge-anti-aliasing/) */
-ms-interpolation-mode: nearest-neighbor; /* IE, Edge (pre-16) */
}
</style>
</head>
<body>
<canvas></canvas>
<script>
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
let boardSize = 8; // Change between 6 and 10
let queens = [];
let counter = {
placements: document.createElement('div'),
backtracks: document.createElement('div')
};
function reset() {
window.cancelAnimationFrame(animation);
ctx.clearRect(0, 0, canvas.width, canvas.height);
boardSize = Math.floor((Math.random() * (10 - 6)) + 6); // Random size between 6 and 10
queens = [];
updateCounters();
solve();
}
function solve() {
if (!placeQueen(boardSize - 1, 0)) return;
if (queens.length === boardSize) {
animateSolution();
} else {
animation = window.requestAnimationFrame(solve); // Restart search after a moment to show solution
}
}
function placeQueen(row, col) {
for (let i = 0; i < row; i++) { // Check left diagonal
if (queens[i] === col || queens[i] - i === col - row || queens[i] + i === col + row) return false;
}
queens.push(col);
drawQueen(ctx, row, col);
updateCounters();
return true;
}
function removeQueen(row) {
const removedCol = queens.pop(); // Remove queen and get column
clearSquare(ctx, row, removedCol);
updateCounters();
}
function animateSolution() {
for (let i = boardSize - 1; i >= 0; i--) {
drawQueen(ctx, i, queens[i]); // Re-place all queens
window.requestAnimationFrame(() => removeQueen(i)); // Remove after a moment to show placement animation
}
}
function updateCounters() {
counter.placements.textContent = `Placements: ${queens.length}`;
counter.backtracks.textContent = `Backtracks: ${queens.length - 1}`;
if (counter.placements.parentNode) document.body.removeChild(counter.placements); // Remove previous count element to prevent stacking
canvas.after(counter.placements, counter.backtracks);
}
function drawQueen(ctx, row, col) {
ctx.fillStyle = 'white';
ctx.fillRect(col * 50 + 2, row * 50 + 2, 46, 46); // Highlight square
ctx.fillStyle = '#3399ff';
ctx.beginPath();
ctx.arc(col * 50 + 25, row * 50 + 25, 18, 0, 2 * Math.PI);
ctx.fill(); // Draw queen
}
function clearSquare(ctx, row, col) {
ctx.clearRect(col * 50 + 2, row * 50 + 2, 46, 46); // Clear highlighted square
for (let i = 0; i < boardSize; i++) {
if (!queens[i]) continue;
const dx = queens[i] - col;
const dy = i - row;
ctx.beginPath();
ctx.moveTo(queens[i] * 50 + 2, i * 50 + 48); // Clear attacked squares
ctx.lineTo((col + Math.sign(dx)) * 50 + 2, (row + dy) * 50 + 2);
ctx.stroke();
}
}
canvas.width = boardSize * 50; // Initialize canvas
canvas.height = boardSize * 50;
reset();
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html><head><title>Animating 8-Queens Backtracking</title></head><body style="background:#2c3e50;margin:0;display:flex;justify-content:center;align-items:center;height:100vh">
<canvas id="board"></canvas>
<div style="position:absolute;top:20px;color:white">Placements: <span id="places">0</span></div>
<div style="position:absolute;top:40px;color:white">Backtracks: <span id="back">0</span></div>
<script>
let canvas = document.getElementById('board'), ctx = canvas.getContext('2d');
let N, queens = [], attacking = new Set();
let places=0, back=0;
let solving = true;
function drawBoard() {
canvas.width = Math.min(500, 40*N);
canvas.height = canvas.width;
ctx.fillStyle = '#3c6e8e';
ctx.fillRect(0,0,canvas.width,canvas.height);
// Draw chessboard
let size = canvas.width/N;
for(let i=0; i<N; i++) {
for(let j=0; j<N; j++) {
ctx.fillStyle = ((i+j)%2 == 0) ? '#85bb65' : '#4a8378';
if(attacking.has(j + ',' + i)) ctx.fillStyle = 'rgba(255,0,0,0.3)';
ctx.fillRect(i*size, j*size, size-1, size-1);
}
}
// Draw queens
for(let q of queens) {
let x = q.x * size + size/2;
let y = q.y * size + size/2;
ctx.fillStyle = '#cc0000';
ctx.beginPath();
ctx.arc(x, y, size/3, 0, Math.PI*2);
ctx.fill();
}
}
function isValid(x, y) {
for(let {x:qx, y:qy} of queens) {
if(qx == x || qy == y || Math.abs(qx-x) == Math.abs(qy-y)) return false;
}
return true;
}
function markAttacking(x, y) {
let size = canvas.width/N;
for(let i=0; i<N; i++) attacking.delete(i + ',' + y);
for(let j=0; j<N; j++) attacking.delete(x + ',' + j);
for(let dx=-N; dx<=N; dx) {
for(let dy=-N; dy<=N; dy) {
if(dx == dy || dx == -dy) {
let nx = x + dx;
let ny = y + dy;
while(0 <= nx && nx < N && 0 <= ny && ny < N) {
attacking.add(nx + ',' + ny);
nx += dx; ny += dy;
}
}
}
}
}
function solve(y=0) {
if(!solving) return;
if(y == N) {
setTimeout(() => {
solving = false;
drawBoard();
setTimeout(() => {
places=back=0;
N=Math.floor(Math.random()*5+6);
queens=[]; attacking.clear();
solving=true;
solve();
}, 2000);
}, 100);
return;
}
for(let x=0; x<N; x++) {
places++;
document.getElementById('places').textContent = places;
if(isValid(x, y)) {
queens.push({x,y});
markAttacking(x, y);
requestAnimationFrame(drawBoard);
yield();
solve(y+1).then(() => {
if(!solving) return;
back++;
document.getElementById('back').textContent = back;
queens.pop();
attacking.clear();
markAttacking(queens[queens.length-1].x, queens[queens.length-1].y);
requestAnimationFrame(drawBoard);
yield();
});
}
}
}
function yield() {
return new Promise(resolve => setTimeout(resolve, 0));
}
N = Math.floor(Math.random()*5+6);
setInterval(() => {
if(solving) drawBoard();
}, 100);
solve();
</script>
</body></html>
```Show code
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Queens Backtracking</title>
<style>
html,body{margin:0;height:100%;background:#10151d;color:#eef3f8;font:16px system-ui,sans-serif;overflow:hidden}
main{height:100%;display:grid;place-items:center;gap:10px;align-content:center}
canvas{width:min(92vmin,760px);height:min(92vmin,760px);box-shadow:0 18px 55px #0009;background:#ddd}
#hud{display:flex;gap:22px;align-items:center;justify-content:center;font-weight:700;letter-spacing:.02em}
#hud span{color:#8fe0ff}
</style>
</head>
<body>
<main>
<canvas id="c"></canvas>
<div id="hud">N <span id="n"></span> Placements <span id="p">0</span> Backtracks <span id="b">0</span></div>
</main>
<script>
const c=document.getElementById("c"),x=c.getContext("2d"),N=document.getElementById("n"),P=document.getElementById("p"),B=document.getElementById("b");
let n,qs,cols,d1,d2,row,tries,stack,events,i,t0,last,hold,placements,backs;
function rnd(){return 6+Math.floor(Math.random()*5)}
function shuffle(a){for(let i=a.length-1;i>0;i--){let j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}
function make(){
n=rnd();N.textContent=n;c.width=c.height=Math.min(innerWidth,innerHeight-60)*devicePixelRatio;x.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0);
qs=[];cols=new Set;d1=new Set;d2=new Set;row=0;tries=[shuffle([...Array(n).keys()])];stack=[];events=[];i=0;hold=0;placements=0;backs=0;P.textContent=B.textContent=0;
while(row>=0&&row<n){
let a=tries[row],col=a&&a.pop();
if(col==null){events.push(["fail",row]);row--;let q=stack.pop();if(q){cols.delete(q.c);d1.delete(q.r-q.c);d2.delete(q.r+q.c);events.push(["remove",q.r,q.c])}continue}
events.push(["try",row,col]);
if(cols.has(col)||d1.has(row-col)||d2.has(row+col)){events.push(["bad",row,col]);continue}
let q={r:row,c:col};stack.push(q);cols.add(col);d1.add(row-col);d2.add(row+col);events.push(["place",row,col]);row++;tries[row]=shuffle([...Array(n).keys()]);
}
events.push(["done"]);
}
function attacked(r,c){
for(const q of qs)if(q.r==r||q.c==c||Math.abs(q.r-r)==Math.abs(q.c-c))return true;
return false;
}
function draw(mark){
let s=Math.min(innerWidth*.92,innerHeight*.92-60)/n,ox=(innerWidth-s*n)/2,oy=(innerHeight-s*n-34)/2;
x.clearRect(0,0,innerWidth,innerHeight);
for(let r=0;r<n;r++)for(let c0=0;c0<n;c0++){
x.fillStyle=(r+c0)%2?"#78946d":"#e7dcc0";x.fillRect(ox+c0*s,oy+r*s,s,s);
if(attacked(r,c0)){x.fillStyle="rgba(220,55,70,.35)";x.fillRect(ox+c0*s,oy+r*s,s,s)}
}
if(mark){let [t,r,c1]=mark;x.fillStyle=t=="bad"?"rgba(255,60,60,.65)":t=="remove"?"rgba(70,150,255,.7)":"rgba(255,230,80,.65)";x.fillRect(ox+c1*s,oy+r*s,s,s)}
x.fillStyle="#17202b";x.strokeStyle="#fff";x.lineWidth=Math.max(2,s*.04);x.textAlign="center";x.textBaseline="middle";x.font=`${s*.62}px serif`;
for(const q of qs)x.fillText("♛",ox+(q.c+.5)*s,oy+(q.r+.55)*s);
}
function step(now){
if(!last)last=now;
if(now-last>260&&!hold){
last=now;let e=events[i++];
if(!e){make();return requestAnimationFrame(step)}
if(e[0]=="place"){qs.push({r:e[1],c:e[2]});P.textContent=++placements}
if(e[0]=="remove"){qs=qs.filter(q=>q.r!=e[1]);B.textContent=++backs}
if(e[0]=="done")hold=now+1400;
t0=e;
}
if(hold&&now>hold){make();last=0;t0=null}
draw(t0);
requestAnimationFrame(step);
}
addEventListener("resize",()=>{c.width=c.height=Math.min(innerWidth,innerHeight-60)*devicePixelRatio;x.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0)});
make();requestAnimationFrame(step);
</script>
</body>
</html>Show code
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>8 Queens Backtracking</title>
<style>
html,body{margin:0;height:100%;background:#111;display:flex;align-items:center;justify-content:center}
canvas{background:#111;box-shadow:0 0 20px #000}
</style></head>
<body>
<canvas id="c" width="640" height="720"></canvas>
<script>
const cvs=document.getElementById('c'),ctx=cvs.getContext('2d');
let n,queens,row,placements,backtracks,solved,holdStart,last=0,acc=0;
function reset(){
n=6+Math.floor(Math.random()*5);
queens=new Array(n).fill(-1);
row=0;placements=0;backtracks=0;solved=false;holdStart=0;
}
reset();
function safe(r,c){
for(let i=0;i<r;i++){
const qc=queens[i];
if(qc===c||Math.abs(qc-c)===Math.abs(i-r))return false;
}
return true;
}
function step(){
if(solved)return;
if(row===n){solved=true;holdStart=performance.now();return;}
let col=queens[row]+1,placed=false;
for(;col<n;col++){
if(safe(row,col)){queens[row]=col;placements++;row++;placed=true;break;}
}
if(!placed){
queens[row]=-1;row--;
if(row<0)reset();else backtracks++;
}
}
function attackedSet(){
const att=new Set();
for(let r=0;r<row;r++){
const c=queens[r];
for(let i=0;i<n;i++){att.add(r*100+i);att.add(i*100+c);}
for(let d=-n;d<=n;d++){
const rr=r+d;
if(rr>=0&&rr<n){
if(c+d>=0&&c+d<n)att.add(rr*100+(c+d));
if(c-d>=0&&c-d<n)att.add(rr*100+(c-d));
}
}
}
return att;
}
function draw(){
const boardSize=640,cell=boardSize/n;
ctx.fillStyle="#111";ctx.fillRect(0,0,cvs.width,cvs.height);
const att=attackedSet();
for(let r=0;r<n;r++){
for(let c=0;c<n;c++){
ctx.fillStyle=(r+c)%2===0?"#eee":"#555";
ctx.fillRect(c*cell,r*cell+80,cell,cell);
if(att.has(r*100+c)){
ctx.fillStyle="rgba(255,60,60,0.35)";
ctx.fillRect(c*cell,r*cell+80,cell,cell);
}
}
}
ctx.strokeStyle="#000";ctx.strokeRect(0,80,boardSize,boardSize);
ctx.font=(cell*0.7)+"px serif";ctx.textAlign="center";ctx.textBaseline="middle";ctx.fillStyle="#1a5";
for(let r=0;r<n;r++){
if(queens[r]>=0)ctx.fillText("♛",queens[r]*cell+cell/2,r*cell+80+cell/2);
}
ctx.fillStyle="#fff";ctx.font="20px sans-serif";ctx.textAlign="left";ctx.textBaseline="alphabetic";
ctx.fillText("N="+n+" Placements: "+placements+" Backtracks: "+backtracks,10,25);
if(solved){
ctx.fillStyle="#4f8";ctx.font="bold 28px sans-serif";ctx.textAlign="center";
ctx.fillText("SOLVED!",boardSize/2,65);
}
}
function loop(ts){
if(!last)last=ts;
const dt=ts-last;last=ts;
if(solved){
if(ts-holdStart>1500)reset();
}else{
acc+=dt;
const stepDelay=180;
while(acc>=stepDelay){acc-=stepDelay;step();if(solved)break;}
}
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body></html>Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>N-Queens Backtracking</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="600" height="700"></canvas>
<div id="info"></div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const w = canvas.width, h = canvas.height - 50;
let n = 8;
let board = [];
let placements = 0;
let backtracks = 0;
let solving = true;
let time = 0;
let holdTime = 0;
let attacked = new Set();
function isSafe(col) {
const row = board.length;
for (let i = 0; i < row; i++) {
const qCol = board[i];
if (qCol === col || Math.abs(qCol - col) === Math.abs(i - row)) {
return false;
}
}
return true;
}
function computeAttacked() {
attacked.clear();
for (let i = 0; i < board.length; i++) {
const col = board[i];
attacked.add(i * n + col);
for (let j = 0; j < n; j++) {
if (j !== i) {
attacked.add(i * n + j);
attacked.add(j * n + col);
}
}
for (let j = 0; j < n; j++) {
for (let k = 0; k < n; k++) {
if (Math.abs(j - i) === Math.abs(k - col) && (j !== i || k !== col)) {
attacked.add(j * n + k);
}
}
}
}
}
function solve() {
if (!solving) return;
if (board.length === n) {
solving = false;
holdTime = 0;
return;
}
const row = board.length;
for (let col = 0; col < n; col++) {
if (isSafe(col)) {
board.push(col);
placements++;
computeAttacked();
return;
}
}
if (board.length > 0) {
board.pop();
backtracks++;
computeAttacked();
}
}
function animate() {
if (holdTime > 120) {
n = Math.floor(Math.random() * 5) + 6;
board = [];
placements = 0;
backtracks = 0;
solving = true;
time = 0;
holdTime = 0;
attacked.clear();
}
if (solving && time % 10 === 0) {
solve();
}
const sq = Math.min(w, h) / n;
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, w, h);
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
const isAttacked = attacked.has(i * n + j);
const isQueen = board[i] === j;
ctx.fillStyle = (i + j) % 2 === 0 ? '#222' : '#333';
if (isAttacked) ctx.fillStyle = '#662222';
ctx.fillRect(j * sq, i * sq, sq, sq);
if (isQueen) {
ctx.fillStyle = '#0f0';
ctx.font = 'bold ' + Math.floor(sq * 0.6) + 'px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Q', j * sq + sq / 2, i * sq + sq / 2);
}
}
}
ctx.fillStyle = '#0f0';
ctx.font = '12px monospace';
ctx.textAlign = 'left';
ctx.fillText(`N=${n} | Placements: ${placements} | Backtracks: ${backtracks}`, 10, h + 30);
time++;
if (!solving) holdTime++;
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 |
|---|---|---|---|
| gemma4:26b | — | — | harness |
| llama3.1:8b | — | 147.4 s | OK |
| qwen3.5:9b | — | 51.2 s | OK |
| qwen3:8b | — | — | harness |
| deepseek-r1:14b | — | 83.5 s | OK |
| mistral-small:24b | — | 197.5 s | OK |
| qwen3-coder:30b | — | 29.2 s | OK |
| qwen3:14b | — | 50.2 s | OK |
| qwq:32b | — | 134.4 s | OK |
| command-r:35b | — | 144.0 s | OK |
| deepseek-r1:32b | — | 135.5 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>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>