Overview › Code › R85
Code · blind round

R85 · Travelling salesman improved by 2-opt

blind round 9/11 local models answered

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.

Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 7000 characters in total — this is a budget on characters, not on lines, so removing line breaks does not help) that animates a travelling-salesman tour over 60 random cities on a <canvas>. Start from a nearest-neighbour tour, then repeatedly apply 2-opt: highlight the two edges being tested, and when a swap shortens the tour, redraw the reversed segment so the crossing visibly untangles. Show the current tour length and the number of improvements made. 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

Local models (Brain cluster) — identical prompt, shuffled order (the sequence is not a ranking)
mistral-small:24bERRBLANKFORMAT
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>Travelling Salesman Tour Animation</title>
<style>
body { margin: 0; display:flex;justify-content:center;align-items:center;height:100vh;background:#f0f0f0;}
canvas { border: 1px solid #000; }
#info { position:absolute;top:10px;left:10px;color:#333;font-family:Arial, sans-serif;}
</style>
</head>
<body>
<canvas id="canvas" width="800" height="600"></canvas>
<div id="info">Tour Length: &nbsp; Improvements: </div>

<script>
function random(lower, upper) { return Math.random() * (upper - lower) + lower; }
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let cities = [];
for (let i = 0; i < 60; i++) {
    let x = random(50, canvas.width - 50);
    let y = random(50, canvas.height - 50);
    cities.push({x, y});
}
let tour = Array.from({length: 60}, (_, i) => i);
function distance(a, b) { return Math.hypot(b.x - a.x, b.y - a.y); }
function totalDistance(tour) {
    let d = 0;
    for (let i = 0; i < tour.length; i++) {
        d += distance(cities[tour[i]], cities[tour[(i + 1) % tour.length]]);
    }
    return d;
}
function drawTour() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = '#000'; let lastPoint = null; for (let i = 0; i < tour.length; i++) { ctx.beginPath(); if (lastPoint) { ctx.moveTo(lastPoint.x, lastPoint.y); } else {ctx.moveTo(cities[tour[0]].x, cities[tour[0]].y);}
    let point = cities[tour[i]];
    ctx.lineTo(point.x, point.y);
    ctx.stroke();
    lastPoint = point; }
    drawCities(); ctx.fillStyle = '#f00'; ctx.font = '16px Arial';
    document.getElementById('info').innerText = `Tour Length: ${totalDistance(tour).toFixed(2)} &nbsp; Improvements: ${improvements}`;
}
function drawCities() { for (let city of cities) {
        ctx.beginPath();
        ctx.arc(city.x, city.y, 5, 0, Math.PI * 2);
        ctx.fillStyle = '#0f0';
        ctx.fill(); }
    }
drawTour();
let improvements = 0;
function swap(a, b) { [tour[a], tour[b]] = [tour[b], tour[a]]; }
function twoOpt() {
    let bestScore = -1;
    let bestI, bestJ;
    for (let i = 0; i < tour.length; i++) {
        for (let j = i + 2; j < tour.length && j <= i + 6; j++) {
            let score = distance(cities[tour[i]], cities[tour[j]]) - distance(cities[tour[(i+1)%tour.length]], cities[tour[(j-1)%tour.length]]); if (score > bestScore) { bestScore = score;
                bestI = i; bestJ = j; }
            }
        }
    }
if(bestScore>0){improvements++;swap(bestI,bestJ+1);}
for(let k=bestI+1 ;k<=(bestI+bestJ)/2;k++){swap(k, bestJ - (k-bestI));}drawTour();
}
function animate() {
    if( improvements < 60){requestAnimationFrame(animate);
        twoOpt(); }else{ setTimeout(()=> { window.location.reload(); },500); }
}
animate();
</script>
</body>
</html>
```
qwen3-coder:30bBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><title>2-Opt TSP Animation</title></head><body style="background:#111;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;"><canvas id="c" width="800" height="600" style="border:1px solid #333;"></canvas><div id="info" style="position:absolute;top:20px;left:20px;color:#ccc;font-family:monospace;">Length: 0.00 | Improvements: 0</div><script>
const c = document.getElementById('c');
const ctx = c.getContext('2d');
let cities = [];
let tour = [];
let bestTour = [];
let improvementCount = 0;
let animationRunning = true;

const CANVAS_W = c.width;
const CANVAS_H = c.height;
const N_CITIES = 60; // Target number of cities

function dist(p1, p2) {
    return Math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2);
}

function generateCities() {
    cities = [];
    for (let i = 0; i < N_CITIES; i++) {
        cities.push({ x: Math.random() * CANVAS_W, y: Math.random() * CANVAS_H });
    }
}

function calculateTourLength(t) {
    let length = 0;
    for (let i = 0; i < t.length; i++) {
        const p1 = cities[t[i]];
        const p2 = cities[t[(i + 1) % t.length]];
        length += dist(p1, p2);
    }
    return length;
}

function nearestNeighborTour() {
    let current = 0;
    let newTour = [current];
    const visited = new Set([current]);

    for (let i = 1; i < N_CITIES; i++) {
        let nearestIndex = -1;
        let minD = Infinity;

        for (let j = 0; j < N_CITIES; j++) {
            if (!visited.has(j)) {
                const d = dist(cities[current], cities[j]);
                if (d < minD) {
                    minD = d;
                    nearestIndex = j;
                }
            }
        }

        newTour.push(nearestIndex);
        visited.add(nearestIndex);
        current = nearestIndex;
    }
    return newTour;
}

function initializeTSP() {
    generateCities();
    tour = nearestNeighborTour();
    bestTour = [...tour];
    improvementCount = 0;
    updateInfoDisplay();
    drawCanvas(true); // Initial clear draw
}

function updateInfoDisplay() {
    const infoDiv = document.getElementById('info');
    infoDiv.innerHTML = `Length: ${currentLength.toFixed(2)} | Improvements: ${improvementCount}`;
}

let currentLength = 0;
let bestLength = Infinity;

// Drawing function (optimized for performance)
function drawCanvas(highlightEdges = [], tempTour = null, isInitial = false) {
    ctx.clearRect(0, 0, CANVAS_W, CANVAS_H);
    ctx.lineWidth = 2;

    // Draw all cities
    cities.forEach(c => {
        ctx.beginPath();
        ctx.arc(c.x, c.y, 4, 0, Math.PI * 2);
        ctx.fillStyle = '#aaa';
        ctx.fill();
    });

    // Draw current best tour (faintly)
    if (!isInitial && bestTour.length > 0) {
        ctx.strokeStyle = 'rgba(150, 150, 255, 0.3)';
        ctx.beginPath();
        for (let i = 0; i < N_CITIES; i++) {
            const p1 = cities[bestTour[i]];
            const p2 = cities[bestTour[(i + 1) % N_CITIES]];
            if (i === 0) ctx.moveTo(p1.x, p1.y);
            else ctx.lineTo(p1.x, p1.y);
        }
        ctx.stroke();
    }

    // Draw the current tour/segment
    let drawTour = tempTour || [...tour];
    if (drawTour.length === 0) return;
    
    for (let i = 0; i < N_CITIES; i++) {
        const p1 = cities[drawTour[i]];
        const p2 = cities[drawTour[(i + 1) % N_CITIES]];

        // Check if this edge needs highlighting
        const isHighlighted = highlightEdges.some(e => (Math.abs(e.start - i) < 1 && Math.abs(e.end - ((i + 1) % N_CITIES)) < 1));
        
        ctx.strokeStyle = isHighlighted ? '#ff4500' : (i === 0 ? '#ccc' : '#666');
        ctx.globalAlpha = isHighlighted ? 1.0 : 0.8;

        ctx.beginPath();
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.stroke();
    }
    ctx.globalAlpha = 1.0;
}


// Main 2-Opt Improvement Loop
async function runTwoOpt() {
    if (!animationRunning) return;

    let improvedInCycle = false;
    let bestImprovement = 0;
    let bestSwap = null;
    let initialTourLength = currentLength;

    for (let i = 0; i < N_CITIES; i++) {
        for (let j = i + 2; j < N_CITIES; j++) {
            // Check for edge overlap/adjacency constraint (i and j+1 are the endpoints)
            if ((i === 0 && j === N_CITIES - 1)) continue;

            const pAIndex = tour[i];
            const pBIndex = tour[(i + 1) % N_CITIES]; // Edge 1: A -> B
            const pCIndex = tour[j];
            const pDIndex = tour[(j + 1) % N_CITIES]; // Edge 2: C -> D

            // The two edges being tested are (A->B) and (C->D).
            // Swapping reverses the segment B...C. New path: A -> C ... D -> B.
            
            const currentDist = dist(cities[pAIndex], cities[pBIndex]) + dist(cities[pCIndex], cities[pDIndex]);
            
            // Calculate cross-over length (A->C) + (B->D)
            let newDist;
            if ((i === 0 && j < N_CITIES - 1)) { // Case: Closing the loop handles indices correctly
                newDist = dist(cities[pAIndex], cities[pCIndex]) + dist(cities[pBIndex], cities[pDIndex]);
            } else {
                 // General case assuming linear segment for simplicity in quick code structure, need to handle wrap-around carefully.
                 // Since we iterate i < j and the tour is cyclic, we simplify the indices based on the standard 2-opt definition: swap edges (i, i+1) and (j, j+1).
                const pA = cities[tour[i]];
                const pB = cities[tour[(i + 1) % N_CITIES]];
                const pC = cities[tour[j]];
                const pD = cities[tour[(j + 1) % N_CITIES]];

                // For simplicity and speed, let's use the standard index approach: swap edges i->i+1 and j->j+1.
                newDist = dist(cities[tour[i]], cities[tour[j]]) + dist(cities[tour[(i + 1) % N_CITIES]], cities[tour[(j + 1) % N_CITIES]]);
            }


            const improvement = currentDist - newDist;

            if (improvement > bestImprovement && improvement > 0.01) {
                bestImprovement = improvement;
                bestSwap = { i: i, j: j }; // Store indices for the swap
            }
        }
    }

    // --- Visualization & Execution ---

    if (!bestSwap || bestImprovement < 0.01) {
        currentLength = calculateTourLength(tour);
        updateInfoDisplay();
        drawCanvas([]); // No highlighting
        improvementCount++; // Fake increment if no change, to prevent infinite loop on flat terrain (though it shouldn't happen often enough).

        // If we finished or improvement is minimal, pause and restart/exit.
        if (!improvedInCycle && bestImprovement < 0.01) {
            setTimeout(() => {
                console.log("Optimization converged.");
                bestLength = currentLength;
                animationRunning = false; // Stop the loop for now
            }, 500);
        } else {
             // If we didn't find a swap but haven't reached convergence, just redraw and wait.
             setTimeout(() => requestAnimationFrame(runTwoOpt), 100);
        }
        return;
    }

    improvedInCycle = true;
    bestLength = Math.min(bestLength === Infinity ? currentLength : bestLength, calculateTourLength(tour) - bestImprovement);

    // 1. Highlight tested edges (i -> i+1 and j -> j+1)
    const highlight = [];
    highlight.push({start: bestSwap.i, end: (bestSwap.i + 1) % N_CITIES});
    highlight.push({start: bestSwap.j, end: (bestSwap.j + 1) % N_CITIES});

    // 2. Visualize the swap transition (The actual complex part - drawing the reversed segment)
    const newTour = [...tour];
    const startSegment = Math.min(bestSwap.i + 1, bestSwap.j); // Index after first edge
    const endSegment = Math.max(bestSwap.i, bestSwap.j);

    // Reverse the segment from index i+1 to j (inclusive)
    let reversedSegment = [];
    for (let k = startSegment; k <= endSegment; k++) {
        reversedSegment.push(newTour[k]);
    }
    reversedSegment.reverse(); // This is indices B...C, now C...B

    // Apply the swap: T' = T[0...i] + reverse(T[i+1...j]) + T[j+1...]
    let temp = [];
    for (let k = 0; k <= bestSwap.i; k++) {
        temp.push(newTour[k]); // Keep T[0...i]
    }
    temp.push(...reversedSegment); // Insert reversed segment
    for (let k = bestSwap.j + 1; k < N_CITIES; k++) {
        // This assumes a linear array model, which is fine if we map the indices correctly.
        // Given the complexity of handling cyclic indices in JS arrays for reversal, we will simply replace the segment B...C with C...B based on index list manipulation.
    }

    // Simple replacement: [0..i] + reverse(T[i+1..j]) + [j+1..N-1]
    let newTourSimple = [...tour];
    let segmentToReverse = [];
    for (let k = bestSwap.i + 1; k <= bestSwap.j; k++) {
        segmentToReverse.push(newTour[k]);
    }
    segmentToReverse.reverse();

    // Overwrite
deepseek-r1:14bBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><title>TSP Animation</title><style>canvas{border:1px solid black}</style></head>
<body><h1 style="color:#00f">Traveling Salesman</h1><canvas id=c id=mainCanvas width=800 height=600></canvas><div id=s stats></div>
<script>
const canvas=document.getElementById('c'), ctx=canvas.getContext('2d');
const W=canvas.width, H=canvas.height;
let cities, path, dist, cstat, animating, loops;

class City{constructor(){this.x=Math.random()*W; this.y=Math.random()*H;}

distance(c){return Math.hypot(this.x-c.x,this.y-c.y)}

color(){return `hsl(${Math.random()*360},100%,50%)`}}

function init(){cities=[]; for(let i=0;i<60;i++) cities.push(new City());}

let nearestNeighborTour=(()=>{
  const tour=[], visited=new Set;
  let start=cities[Math.floor(Math.random() * cities.length)];
  while(tour.length < 60){
    let next=start;
    for(let c of cities) if(!visited.has(c)){
      if(next===start&&!tour.length) next=c; else if(dist(start,c)<dist(start,next)) next=c;
    }
    tour.push(next); visited.add(next); start=next;
  } return {path:tour, dist:tour.reduce((sum,[a,b])=>sum+dist(a,b),0)};
})();

function reversePath(a,b){return [...Array(b-a+1)].map((_,i)=>path[a+i]||path[path.length - (a+i)]);}

let twoOpt=(()=>{
  let improved=0, best=Infinity;
  while(true){
    let i,j,minImprovement=-1;
    for(let a=0;a<path.length-2;a++)for(let b=a+2;b<path.length;b++){
      const d1=dist(path[a],path[a+1])+dist(path[b-1],path[b]);
      const d2=dist(path[a],path[b-1])+dist(path[a+1],path[b]);
      if(d2<d1 && (d1-d2>minImprovement)){
        minImprovement=d1-d2;
        i=a;j=b-1;
      }
    }if(minImprovement<0)break;if(minImprovement>0){
      const oldPath=path[i+1,...path.slice(i+1,j)], newPath=rever
```
qwen3:8bharness
No answer reached us (THIN) — and the reason lies with our setup, not with the model: reasoning consumed the entire token budget and the answer stayed empty; the gateway rejects budgets above 3000 tokens Counted as a did-not-finish, but it is not evidence about this model.
qwen3.5:9bBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>TSP 2-Opt Optimization</title>
<style>
    body { background: #111; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: sans-serif; color: white; }
    canvas { border: 1px solid #333; background: #000; }
    #info { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); color: #ccc; font-size: 1.2em; }
</style>
</head>
<body>

<canvas id="tspCanvas" width="800" height="600"></canvas>
<div id="info">Length: 0 | Improvements: 0</div>

<script>
    const canvas = document.getElementById('tspCanvas');
    const ctx = canvas.getContext('2d');
    const infoDiv = document.getElementById('info');

    let points = [];
    let tourPathIndices = []; // Stores the indices of cities in order
    let currentLength = 0;
    let improvementsCount = 0;
    let isOptimizingPass = false;
    let animationStartTime = 0;
    const MAX_ITERATIONS = 15;

    // --- Utility Functions ---

    function distance(p1, p2) {
        return Math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2);
    }

    function generatePoints() {
        points = [];
        const count = 60;
        for (let i = 0; i < count; i++) {
            points.push({
                x: Math.random() * canvas.width,
                y: Math.random() * canvas.height
            });
        }
    }

    function updateInfo(length) {
        infoDiv.innerHTML = `Length: ${length.toFixed(2)} | Improvements: ${improvementsCount}`;
    }

    // --- Visualization Functions ---

    function drawTour(pathIndices, highlightEdges = []) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Draw all cities
        points.forEach(p => {
            ctx.beginPath();
            ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
            ctx.fillStyle = '#4CAF50';
            ctx.fill();
        });

        // Draw tour lines
        for (let i = 0; i < pathIndices.length; i++) {
            const p1Index = pathIndices[i];
            const p2Index = pathIndices[(i + 1) % pathIndices.length]; // Loop back to start
            
            let isHighlighted = false;
            for (const edges of highlightEdges) {
                // Check if the current segment matches a tested edge pair
                if ((p1Index === edges[0] && p2Index === edges[1]) || 
                    (p1Index === edges[1] && p2Index === edges[0])) {
                    isHighlighted = true;
                    break;
                }
            }

            ctx.beginPath();
            ctx.moveTo(points[p1Index].x, points[p1Index].y);
            ctx.lineTo(points[p2Index].x, points[p2Index].y);
            ctx.strokeStyle = isHighlighted ? '#FFEB3B' : '#607D8B';
            ctx.lineWidth = 1.5;
            ctx.stroke();
        }

        // Highlight the start/end point (for clarity)
        const startIndex = pathIndices[0];
        ctx.beginPath();
        ctx.arc(points[startIndex].x, points[startIndex].y, 6, 0, Math.PI * 2);
        ctx.fillStyle = 'yellow';
        ctx.fill();
    }

    // --- Optimization Algorithms ---

    function calculateInitialTour() {
        // Nearest Neighbor heuristic
        let currentCityIndex = 0;
        tourPathIndices = [currentCityIndex];
        const visited = new Array(points.length).fill(false);
        visited[currentCityIndex] = true;

        for (let count = 1; count < points.length; count++) {
            let nearestNeighborIndex = -1;
            let minDist = Infinity;
            
            for (let i = 0; i < points.length; i++) {
                if (!visited[i]) {
                    const dist = distance(points[currentCityIndex], points[i]);
                    if (dist < minDist) {
                        minDist = dist;
                        nearestNeighborIndex = i;
                    }
                }
            }

            tourPathIndices.push(nearestNeighborIndex);
            visited[nearestNeighborIndex] = true;
            currentCityIndex = nearestNeighborIndex;
        }
    }

    function calculateTourLength() {
        let length = 0;
        for (let i = 0; i < tourPathIndices.length; i++) {
            const p1Index = tourPathIndices[i];
            const p2Index = tourPathIndices[(i + 1) % tourPathIndices.length]; // Loop back
            length += distance(points[p1Index], points[p2Index]);
        }
        return length;
    }

    function applyTwoOptSwap(i, j) {
        // Swaps edges (i-1, i) and (j, j+1) to (i-1, j) and (i, j+1)
        if (Math.abs(i - j) < 2 || i === j) return false;

        let newPath = [];
        // Keep segment 0 until i-1
        for (let k = 0; k <= Math.min(i, j); k++) {
            newPath.push(tourPathIndices[k]);
        }
        // Reverse segment i to j
        const startReverse = Math.min(i, j);
        const endReverse = Math.max(i, j);
        for (let k = endReverse; k >= startReverse; k--) {
            newPath.push(tourPathIndices[k]);
        }
        // Keep segment j+1 until end
        for (let k = Math.min(i, j) + 1; k < tourPathIndices.length; k++) {
             if (!isNaN(parseInt(tourPathIndices[k]))) newPath.push(tourPathIndices[k]);
        }

        // Note: Due to the complexity of path indices and wrapping around, we simplify by just reversing the necessary segment within the array structure.
        let oldPath = [...tourPathIndices];
        
        // Simple reversal logic (assuming i < j): Reverse elements from index i up to j inclusive
        let newTour = [
            ...oldPath.slice(0, Math.min(i, j)),
            ...oldPath.slice(Math.min(i, j), Math.max(i, j) + 1).reverse(),
            ...oldPath.slice(Math.max(i, j) + 1)
        ];

        let newLength = calculateTourLengthFromPath(newTour);
        return { success: true, newPath: newTour, lengthDifference: newLength - currentLength };
    }

    function calculateTourLengthFromPath(path) {
         let length = 0;
         for (let i = 0; i < path.length; i++) {
            const p1Index = path[i];
            const p2Index = path[(i + 1) % path.length]; 
            length += distance(points[p1Index], points[p2Index]);
        }
        return length;
    }


    // --- Main Loop & State Machine ---

    let optimizationAttemptCount = 0;

    function findImprovement() {
        if (optimizationAttemptCount++ > MAX_ITERATIONS) return null;
        
        const N = points.length;
        
        for (let i = 1; i < N - 2; i++) { // Start at index 1, cannot swap last two edges
            for (let j = i + 1; j < N - 1; j++) {
                // Test swapping segments [i-1, i] and [j, j+1]
                
                // Create a temporary path for testing the swap: reverse segment i to j
                let testPath = [...tourPathIndices];
                let swappedSegment = testPath.slice(i, j + 1).reverse();
qwen3:14bBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>2-Opt TSP Optimization</title>
<style>body {margin:0;overflow:hidden;}canvas {display:block;background:#111;}</style>
</head>
<body>
<canvas id="tspCanvas"></canvas>
<script>
const canvas = document.getElementById('tspCanvas');
const ctx = canvas.getContext('2d');

// Set canvas size based on screen dimensions for better viewing experience
let W, H;
function resize() {
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = W * 0.95;
    canvas.height = H * 0.85;
}
window.addEventListener('resize', resize);
resize();

const NUM_CITIES = 60;
let cities = [];
let tour = [];
let currentLength = 0;
let improvements = 0;

// Helper function for distance squared (avoids Math.sqrt until necessary)
function distSq(p1, p2) {
    return (p1[0] - p2[0])**2 + (p1[1] - p2[1])**2;
}

// Calculate Euclidean distance
function dist(p1, p2) {
    return Math.sqrt(distSq(p1, p2));
}

// Function to generate random cities
function generateCities() {
    cities = [];
    for (let i = 0; i < NUM_CITIES; i++) {
        cities.push([Math.random() * W * 0.8 + W * 0.1, Math.random() * H * 0.8 + H * 0.1]);
    }
}

// Nearest Neighbor heuristic for initial tour
function nearestNeighbor() {
    tour = [];
    const visited = new Set();
    let currentCityIndex = 0; // Start at city 0
    tour.push(currentCityIndex);
    visited.add(currentCityIndex);

    for (let i = 1; i < NUM_CITIES; i++) {
        let nearestIndex = -1;
        let minD = Infinity;

        for (let j = 0; j < NUM_CITIES; j++) {
            if (!visited.has(j)) {
                const d = dist(cities[tour[i - 1]], cities[j]);
                if (d < minD) {
                    minD = d;
                    nearestIndex = j;
                }
            }
        }

        if (nearestIndex !== -1) {
            tour.push(nearestIndex);
            visited.add(nearestIndex);
        }
    }
    // Complete the circuit by returning to the start (city 0)
    currentLength = calculateTourLength();
}

function calculateTourLength() {
    let totalDist = 0;
    for (let i = 0; i < tour.length - 1; i++) {
        totalDist += dist(cities[tour[i]], cities[tour[i+1]]);
    }
    // Close the loop: last city to starting city
    totalDist += dist(cities[tour[tour.length - 1]], cities[tour[0]]);
    return totalDist;
}

/**
 * Implements a single 2-opt swap and checks for improvement.
 * @param {number} i Start index of the first edge (A -> B)
 * @param {number} j End index of the second edge (C -> D)
 * @returns {boolean} True if an improvement was found and applied.
 */
function tryTwoOpt(i, j) {
    // Indices must be valid and non-adjacent for a proper swap (i+1 < j)
    if (i >= j || i < 0 || j >= tour.length - 1) return false;

    const A = cities[tour[i]];     // City before first cut
    const B = cities[tour[i + 1]]; // City after first cut
    const C = cities[tour[j]];     // City before second cut
    const D = cities[tour[j + 1]]; // City after second cut

    const oldLength = dist(A, B) + dist(C, D);
    const newLength = dist(A, C) + dist(B, D);

    if (newLength < oldLength - 0.1) { // Use tolerance for floating point comparison
        // Improvement found! Swap the segment [i+1, j]
        for (let k = i + 1; k <= j; k++) {
            tour[k] = tour[k - 1];
        }
        tour[i + 1] = tour[j]; // This assignment is technically wrong for a simple shift. We need reversal logic.

        // Correct Reversal Logic: Reverse the segment from index i+1 up to j
        const start = i + 1;
        const end = j;
        while (start < Math.floor((start + end) / 2)) {
            const mid = Math.floor((start + end) / 2);
            const mirror = start + end - mid;
            [tour[start], tour[mirror]] = [tour[mirror], tour[start]];
            start++;
            end--;
        }

        // Update the length and state
        currentLength = currentLength - (dist(A, B) + dist(C, D)) + newLength;
        improvements++;
        return true;
    }
    return false;
}


function draw() {
    ctx.clearRect(0, 0, W, H);

    // Draw all cities
    ctx.fillStyle = '#fff';
    for (let i = 0; i < NUM_CITIES; i++) {
        ctx.beginPath();
        ctx.arc(cities[i][0], cities[i][1], 5, 0, Math.PI * 2);
        ctx.fill();
    }

    // Draw the tour lines (background)
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 1;
    for (let i = 0; i < NUM_CITIES; i++) {
        const p1 = cities[tour[i]];
        const p2 = cities[tour[(i + 1) % NUM_CITIES]];
        ctx.beginPath();
        ctx.moveTo(p1[0], p1[1]);
        ctx.lineTo(p2[0], p2[1]);
        ctx.stroke();
    }

    // Draw current optimization info
    ctx.fillStyle = '#fff';
    ctx.font = '18px monospace';
    const statsDiv = document.createElement('div');
    statsDiv.style.position = 'absolute';
    statsDiv.style.top = '10px';
    statsDiv.style.left = '10px';
    statsDiv.innerHTML = `Tour Length: ${currentLength.toFixed(2)} | Improvements: ${improvements}`;
    document.body.appendChild(statsDiv);

    requestAnimationFrame(draw);
}


// --- Main Animation Loop and State Management ---

let animationState = {
    active: false,
    iteration: 0,
    swap_i: -1, // i for edge (i, i+1)
    swap_j: -1  // j for edge (j, j+1)
};

function findNextImprovement() {
    let bestI = -1;
    let bestJ = -1;
    let maxImprovement = 0;

    // Optimization search loop: Iterate over all pairs i and j such that i < j-1
    for (let i = 0; i < NUM_CITIES - 2; i++) {
        for (let j = i + 2; j < NUM_CITIES - 1; j++) {
            // Temporarily store current state to check the potential gain without modifying 'tour'
            const originalTour = [...tour];
            const tempImprovements = improvements;
            const tempLength = currentLength;

            // Simulate the swap (this requires a clean copy of the tour)
            let simulatedCities = JSON.parse(JSON.stringify(originalTour));
            
            // Reversal logic for simulation
            const start = i + 1;
            const end = j;
            while (start < Math.floor((start + end) / 2)) {
                const mid = Math.floor((start + end) / 2);
                const mirror = start + end - mid;
                [simulatedCities[start], simulatedCities[mirror]] = [simulatedCities[mirror], simulatedCities[start]];
                start++;
                end--;
            }

            // Calculate the length difference for this potential swap
            const A_s = cities[originalTour[i]];
            const B_s = cities[originalTour[i + 1]];
            const C_s = cities[originalTour[j]];
            const D_s = cities[originalTour[j + 1]];

            const oldL = dist(A_s, B_s) + dist(C_s, D_s);
            const newL = dist(A_s, C_s) + dist(B_s, D_s);
            const improvement = oldL - newL;

            if (improvement > maxImprovement && improvement > 0.1) {
                maxImprovement = improvement;
                bestI = i;
                bestJ = j;
            }
        }
    }
    return { i: bestI, j: bestJ, improve: maxImprovement };
}

function animateOptimization(deltaTime) {
    // 1. Find the optimal swap (must be done outside the animation loop for performance)
    const bestSwap = findNextImprovement();

    if (!bestSwap.i) {
        // No more improvements possible or found in this pass
        console.log("Optimal solution reached.");
        return true; // Signal completion
    }

    animationState.swap_i = bestSwap.i;
    animationState.swap_j = bestSwap.j;
    animationState.active = true;

    // 2. Perform the swap and update state immediately (this is complex, so we skip frame-by-frame visual animation for simplicity and focus on convergence)
    // Instead of animating the segment reversal, we apply the change instantly upon detection to maintain smoothness and rapid convergence feel.
    const i = bestSwap.i;
    const j = bestSwap.j;

    // Apply Reversal Logic: Reverse the segment from index i+1 up to j
    const start = i + 1;
    const end = j;
    while (start < Math.floor((start + end) / 2)) {
        const mid = Math.floor((start + end) / 2);
        const mirror = start + end - mid;
        [tour[start], tour[mirror]] = [tour[mirror], tour[start]];
        start++;
        end--;
    }

    // Update length and improvements on successful swap
    currentLength -= (dist(cities[tour[i]], cities[tour[i+1]]) + dist(cities[tour[j]], cities[tour[j+1]]));
    currentLength += (dist(cities[tour[i]], cities[tour[j]]) + dist(cities[tour[i+1]], cities[tour[j+1]]));
    improvements++;

    // Continue the process immediately to find the next improvement
    return false; // Not finished
}


function mainLoop(timestamp) {
    const deltaTime = timestamp - (mainLoop.lastTime || timestamp);
    mainLoop.lastTime = timestamp
llama3.1:8bERRSTATFORMAT
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>Travelling Salesman Tour</title>
    <style>
        body {
            margin: 0;
            background-color: #f0f0f0;
        }
        canvas {
            border: 1px solid black;
        }
    </style>
</head>
<body>
<script>
    const cities = [];
    for (let i = 0; i < 60; i++) {
        cities.push({x: Math.random() * 800, y: Math.random() * 600});
    }

    let tour = [...cities.keys()];
    let shortestTour = [...tour];
    let improvementsCount = 0;
    const canvas = document.createElement('canvas');
    document.body.appendChild(canvas);
    canvas.width = 800;
    canvas.height = 600;

    const ctx = canvas.getContext('2d');

    function nearestNeighbour() {
        tour = [0];
        for (let i = 1; i < cities.length - 1; i++) {
            let minDist = Infinity, argMinDist = null;
            for (let j = 0; j < cities.length; j++) {
                if (!tour.includes(j)) {
                    const dist = Math.hypot(cities[tour[i - 1]].x - cities[j].x, cities[tour[i - 1]].y - cities[j].y);
                    if (dist < minDist) {
                        minDist = dist;
                        argMinDist = j;
                    }
                }
            }
            tour.push(argMinDist);
        }
    }

    function twoOpt() {
        let improved = false;
        for (let i = 1; i < tour.length - 2; i++) {
            for (let j = i + 1; j < tour.length - 1; j++) {
                if (j - i > Math.abs(tour[j] - tour[i]) && !tour.slice(i, j).includes(tour[j])) {
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    for (let k = 0; k < tour.length; k++) {
                        const x1 = cities[tour[k]].x;
                        const y1 = cities[tour[k]].y;
                        const x2 = cities[tour[(k + 1) % tour.length]].x;
                        const y2 = cities[tour[(k + 1) % tour.length]].y;
                        ctx.beginPath();
                        ctx.moveTo(x1, y1);
                        ctx.lineTo(x2, y2);
                        ctx.stroke();
                    }
                    for (let k = i; k < j; k++) {
                        ctx.beginPath();
                        ctx.arc(cities[tour[k]].x, cities[tour[k]].y, 3, 0, 2 * Math.PI);
                        ctx.fill();
                    }
                    ctx.beginPath();
                    ctx.moveTo(cities[tour[i]].x, cities[tour[i]].y);
                    ctx.lineTo(cities[tour[j]].x, cities[tour[j]].y);
                    ctx.stroke();

                    const newTour = [...tour.slice(0, i), ...tour.slice(i, j).reverse(), ...tour.slice(j)];
                    let newLength = 0;
                    for (let k = 0; k < newTour.length - 1; k++) {
                        newLength += Math.hypot(cities[newTour[k]].x - cities[newTour[k + 1]].x, cities[newTour[k]].y - cities[newTour[k + 1]].y);
                    }
                    if (newLength < tour.length) {
                        improved = true;
                        shortestTour = [...tour];
                        improvementsCount++;
                        tour = newTour.slice();
                    } else {
                        ctx.beginPath();
                        ctx.moveTo(cities[tour[i]].x, cities[tour[i]].y);
                        ctx.lineTo(cities[tour[j]].x, cities[tour[j]].y);
                        ctx.stroke();

                        const x1_2 = cities[newtour[i]].x;
                        const y1_2 = cities[newtour[i]].y;
                        const x2_2 = cities[newtour[(i + 1) % tour.length]].x;
                        const y2_2 = cities[newtour[(i + 1) % tour.length]].y;

                        ctx.beginPath();
                        ctx.moveTo(x1_2, y1_2);
                        ctx.lineTo(x2_2, y2_2);
                        ctx.stroke();

                        const x1_3 = cities[tour[i]].x;
                        const y1_3 = cities[tour[i]].y;
                        const x2_3 = cities[tour[j]].x;
                        const y2_3 = cities[tour[j]].y;

                        ctx.beginPath();
                        ctx.moveTo(x1_3, y1_3);
                        ctx.lineTo(x2_3, y2_3);
                        ctx.stroke();

                        ctx.beginPath();
                        ctx.arc(cities[newtour[i]].x, cities[newtour[i]].y, 3, 0, 2 * Math.PI);
                        ctx.fill();

                        ctx.beginPath();
                        ctx.arc(cities[tour[i]].x, cities[tour[i]].y, 3, 0, 2 * Math.PI);
                        ctx.fill();
                    }
                }
            }
        }

        if (improved) {
            requestAnimationFrame(twoOpt);
        } else {
            document.title = `Tour length: ${tour.length}, improvements: ${improvementsCount}`;
            ctx.clearRect(0, canvas.height - 20, canvas.width, 20);
            const x1_4 = cities[tour[shortestTour.indexOf(tour[0])]].x;
            const y1_4 = cities[tour[shortestTour.indexOf(tour[0])]].y;
            const x2_4 = cities[tour[(shortestTour.indexOf(tour[0]) + 1) % tour.length]].x;
            const y2_4 = cities[tour[(shortestTour.indexOf(tour[0]) + 1) % tour.length]].y;

            ctx.beginPath();
            ctx.moveTo(x1_4, y1_4);
            ctx.lineTo(x2_4, y2_4);
            ctx.stroke();

            ctx.font = '14px Arial';
            ctx.fillStyle = 'black';
            ctx.textAlign = 'left';
            ctx.textBaseline = 'top';
            ctx.fillText(`Tour length: ${tour.length}, improvements: ${improvementsCount}`, 10, canvas.height - 5);
        }
    }

    function animate() {
        requestAnimationFrame(animate);

        for (let i = 0; i < tour.length; i++) {
            const x1 = cities[tour[i]].x;
            const y1 = cities[tour[i]].y;
            const x2 = cities[tour[(i + 1) % tour.length]].x;
            const y2 = cities[tour[(i + 1) % tour.length]].y;

            ctx.beginPath();
            ctx.moveTo(x1, y1);
            ctx.lineTo(x2, y2);
            ctx.stroke();

            ctx.font = '14px Arial';
            ctx.fillStyle = 'black';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'top';
            ctx.fillText(i + 1, (x1 + x2) / 2, (y1 + y2) / 2 - 16);
        }

        ctx.font = '14px Arial';
        ctx.fillStyle = 'black';
        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        ctx.fillText(`Tour length: ${tour.length}, improvements: ${improvementsCount}`, 10, canvas.height - 5);

        twoOpt();
    }

    nearestNeighbour();
    animate();
</script>
</body>
</html>
```
gemma4:26bharness
No answer reached us (THIN) — and the reason lies with our setup, not with the model: reasoning consumed the entire token budget (3 min 28 s, zero characters of answer); larger budgets are refused by the gateway Counted as a did-not-finish, but it is not evidence about this model.
deepseek-r1:32bOKFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><title>TSP 2-opt Animation</title><style>
body { background:#1a1a1a; margin:0; padding:5px }
canvas { border:1px solid white }
text { color:white; font-family:sans-serif }
</style></head><body>
<canvas id=c width=800 height=600></canvas><div id=d style="position:absolute;top:5px;left:5px;">Length: <span id=l>0</span><br>Improvements: <span id=i>0</span></div>
<script>
let c=document.getElementById('c'),ctx=c.getContext('2d'),w=800,h=600,n=60,cities=[],paths,x1=-1,y1,z1,tour=[],i=0,totalLength=0,improvements=0,running=true;
function city(x,y){this.x=x;this.y=y}
function init(){
cities=[];tour=[];totalLength=0;improvements=0;i=0;
for(let j=0;j<n;j++)cities.push(new city(Math.random()*w,Math.random()*h));
let start=cities[0];
tour=[start];
let remaining=[...cities.filter(c=>c!=start)];
while(remaining.length){
    let minDist=Infinity,next=null;
    for(let c of remaining){
        if(dist(tour[tour.length-1],c)<minDist){
            minDist=dist(tour[tour.length-1],c);
            next=c;
        }
    }
    tour.push(next);
    remaining.splice(remaining.indexOf(next),1);
}
tour.push(start);
paths=tour.map((c,j)=>({x:c.x,y:c.y,next:tour[(j+1)%n]}));
startAnimation();
}
function dist(p1,p2){return Math.sqrt((p1.x-p2.x)**2+(p1.y-p2.y)**2)}
function draw(){
ctx.fillStyle='#1a1a1a';ctx.fillRect(0,0,w,h);
ctx.strokeStyle='white';ctx.lineWidth=1;
for(let p of paths){
    ctx.beginPath();
    ctx.moveTo(p.x,p.y);
    ctx.lineTo(p.next.x,p.next.y);
    ctx.stroke();
}
if(x1!=-1){
    ctx.strokeStyle='red';
    for(let j of [x1,z1]){
        let p=paths[j];
        ctx.beginPath();
        ctx.moveTo(p.x,p.y);
        ctx.lineTo(p.next.x,p.next.y);
        ctx.stroke();
    }
    if(y1!=-1){
        let j=x1,k=z1;
        while(j!=k){
            let temp=paths[j].next;
            paths[j].next=paths[k].next;
            paths[k].next=temp;
            j++;
            k--;
        }
        totalLength-=y1;
        x1=y1=-1;
    }
}
document.getElementById('l').textContent=Math.round(totalLength);
document.getElementById('i').textContent=improvements;
if(i++<n*n&&running)setTimeout(draw,10);
else{init();}
}
function startAnimation(){
x1=y1=z1=-1;
totalLength=tour.reduce((s,p,j)=>s+dist(p,tour[(j+1)%n]),0);
let k=0;
for(let m=0;m<n-2;m++){
    for(let j=m+1;j<n-1;j++){
        let a=cities[m],b=tour[tour.indexOf(cities[m])+1];
        let c=cities[j],d=tour[tour.indexOf(cities[j])+1];
        if(b===c||a===d)continue;
        totalLength-=dist(a,b)+dist(c,d);
        let newDist=dist(a,c)+dist(b,d);
        if(newDist<dist(a,b)+dist(c,d)){
            improvements++;
            x1=m,z1=j,y1=newDist-dist(a,c)-dist(b,d);
            draw();
            return;
        }
        totalLength+=newDist>dist(a,b)+dist(c,d)?0:dist(a,b)+dist(c,d)-newDist;
    }
}
running=false;init();
}
document.getElementById('d').style.textAlign='left';
init();
function requestAnimationFramePolyfill(callback) {
    setTimeout(callback, 16);
}
</script></body></html>
```
qwq:32bSTATLIM
Show code
<think>

Okay, I need to create a self-contained HTML file that animates a traveling salesman problem solution using the 2-opt algorithm on 60 random cities. The user specified it should start with a nearest neighbor tour, then apply 2-opt swaps, highlighting the edges being tested. When a swap improves the tour, it should visually show the "untangling" of the crossing. Also, it needs to display the current tour length and the number of improvements made. The animation should use requestAnimationFrame, no user input, auto-start, and restart when done. All in under 7000 characters.

Hmm, first, the HTML structure. I'll need a canvas and some div elements for the length and improvements. The canvas will handle the drawing. The JavaScript part has to be concise. Let me outline the steps:

1. Initialize the canvas and context.
2. Generate 60 random cities.
3. Create the initial tour using the nearest neighbor algorithm.
4. Implement the 2-opt algorithm with animation.
5. Animate the swapping edges and the tour updates.
6. Keep track of the tour length and improvements.
7. Handle animation frames with requestAnimationFrame.
8. Restart when the improvements stop.

Starting with the random cities: I'll use Math.random() to place points on the canvas. The nearest neighbor starts at a random city, then adds the closest unvisited city each step.

For the 2-opt, I need to loop through all pairs of edges, check if swapping them improves the tour. When a swap is found, animate the reversal. The tricky part is the animation frames. Each step of the 2-opt should wait for a frame to draw the edges being considered, then if swapped, draw the new path with the reversed segment.

I need to structure the code so that each animation frame either processes a step of the 2-opt or restarts. The animation loop will alternate between processing the swaps and rendering the current state.

Variables needed: cities array, tour array (indices of the cities), current tour length, improvements counter, and animation state variables like current indices being tested (i,j), whether a swap is happening, etc.

To animate the swapping, when a swap is made, the segment from i to j is reversed, so during animation frames, we can draw the reversed path step by step? Or maybe just redraw the path once the swap is done. But the user wants to highlight the edges being tested. So during testing, the two edges (between i and i+1, j and j+1) are drawn in a different color, then when swapped, the new edges are drawn.

Wait, the problem says when a swap is done, redraw the reversed segment so the crossing untangles. So perhaps when a swap is found, the reversed segment is animated? Maybe not necessary to animate each step, just show the before and after in quick succession.

Hmm, the animation should be smooth using requestAnimationFrame. So each frame, we can process a small part of the 2-opt steps. Since the 2-opt could take many iterations, we need to structure it so that each animation frame handles a few iterations to prevent blocking the UI.

Alternatively, each frame could handle one swap step. To do that, we can loop through the possible edges incrementally. For example, keep track of the current i and j indices being checked, increment them each frame until done, then reset the problem.

Wait, the problem states to "repeatedly apply 2-opt", so perhaps it's a loop over all possible 2-opt moves until no more improvements. But to animate this, we can't do all steps at once, so we have to process one candidate swap per animation frame, or a few.

Let me think about how to structure the code. The main loop could be inside requestAnimationFrame. The function would do something like:

- If currently testing a swap, highlight the edges, then check if swap improves the tour.
- If improvement, animate the swap by redrawing the path with the reversed segment.
- Keep track of how many improvements have been made.
- Once no more improvements, reset the cities and start over.

But how to handle the timing? Each swap step (testing the edges and deciding to swap) would need to be done at each frame, but spread out so that it doesn't freeze.

Alternatively, each frame processes a single 2-opt move (check one pair of edges, decide swap). Since 60 cities have O(n^2) edges, that might take too long. Maybe it's better to process a small number of steps per frame, then continue until all pairs are checked once, then repeat until no more improvements.

But the total number of iterations could be large. Maybe the 2-opt is applied until no more improvements, but with a maximum number of passes? Not sure. The problem says to "repeatedly apply 2-opt" until finished, so perhaps it stops when a full pass through all possible edges finds no improvements.

So, the algorithm steps would be:

1. Generate cities and initial tour.
2. Compute initial tour length.
3. Until no more improvements:
   a. Iterate over all possible pairs of edges (i,j)
   b. For each pair, compute the new tour if swapped
   c. If shorter, swap the segment and note improvement
   d. Repeat until no improvement in a full pass.

But this is computationally intensive. For 60 cities, the number of edge pairs is (60 choose 2) which is 1770, so each full pass is O(n^2). To animate this, each frame would handle a few pairs, so that after many frames, a full pass is done.

So structuring the code to loop through all possible (i,j) pairs, incrementally. Let's say each frame processes one (i,j) pair. We can have variables tracking current i and j. For each frame:

- Get the next i and j (start from 0,0, then go through all combinations)
- Check the current pair.
- If swap improves, update the tour, increment improvements, and redraw.
- Keep going until all pairs have been checked without improvement.

But how to track when to restart? Maybe after a full pass with no improvements, then reset the cities and start again.

Now, to display the improvement count and tour length. We need to update those text elements each frame.

Also, for the animation of the edges being tested, when checking a pair (i,j), the code would highlight the two edges. So, during the frame when that pair is being evaluated, the current edges between i and i+1, and j and j+1 are drawn in a different color (like red). Then, when a swap is done, the new edges (i to j and j+1 to i+1?) need to be drawn to show the untangling.

Wait, in 2-opt, the edges between i and i+1 and between j and j+1 are removed, and replaced with edges between i and j and between i+1 and j+1. So the segment from i to j is reversed.

Therefore, when a swap is made, the path between i and j is reversed, changing the direction from i to j+1. So visually, that would "untangle" any crossing lines.

To show this, when a swap is made, the segment between the two edges should be redrawn with the reversed order. So, the animation code would need to redraw the path with the reversed segment.

Putting this together, here's a rough code outline:

- The canvas is initialized, ctx, etc.
- Variables for cities (array of objects with x,y), tour (indices), length, improvements, etc.
- Functions for calculating distance
command-r:35bSTAT
Show code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Traveling Salesman Animation</title>
    <style>
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f4f4f4;
        }
        canvas {
            border: 2px solid #333;
            box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.1);
        }
    </style>
</head>
<body>
    <canvas id="tspCanvas" width="800" height="600"></canvas>

    <script>
        const canvas = document.getElementById('tspCanvas');
        const ctx = canvas.getContext('2d');

        const numCities = 60;
        let cities = [];
        let tour = [];
        let improvements = 0;
        let totalLength = Infinity;

        function randomCity() {
            return [Math.random() * canvas.width, Math.random() * canvas.height];
        }

        function distance(city1, city2) {
            const dx = city1[0] - city2[0];
            const dy = city1[1] - city2[1];
            return Math.sqrt(dx * dx + dy * dy);
        }

        function init() {
            cities = Array.from({ length: numCities }, randomCity);
            tour = [];
            improvements = 0;
            totalLength = Infinity;

            for (let i = 0; i < numCities; i++) {
                const city = cities[i];
                ctx.fillStyle = 'blue';
                ctx.fillRect(city[0], city[1], 5, 5);
                tour.push({ x: city[0], y: city[1] });
            }

            updateTour();
        }

        function findNearestCity(currentCity) {
            let nearestDistance = Infinity;
            let nearestIndex = -1;

            for (let i = 0; i < numCities; i++) {
                if (i !== currentCity && cities[i]) {
                    const dist = distance(cities[i], tour[currentCity]);
                    if (dist < nearestDistance) {
                        nearestDistance = dist;
                        nearestIndex = i;
                    }
                }
            }

            return nearestIndex;
        }

        function updateTour() {
            if (!tour.length || improvements >= 1e6) {
                init(); // Restart with a fresh instance
                return;
            }
            
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            for (const city of cities) {
                ctx.fillStyle = 'blue';
                ctx.fillRect(city[0], city[1], 5, 5);
            }

            let currentCity = tour.length - 1;
            while (tour.length < numCities) {
                const nearestIndex = findNearestCity(currentCity);
                cities[nearestIndex] = null; // Mark as visited
                
                const newCity = cities[nearestIndex];
                const newX = newCity[0];
                const newY = newCity[1];

                ctx.beginPath();
                ctx.moveTo(tour[currentCity].x, tour[currentCity].y);
                ctx.lineTo(newX, newY);
                ctx.strokeStyle = 'green'; // Highlight the new edge being added
                ctx.stroke();
                
                if (nearestIndex === 0 || nearestIndex === numCities - 1) {
                    currentCity = nearestIndex;
                } else {
                    tour.splice(currentCity + 1, 0, { x: newX, y: newY });
                    currentCity++;
                }
            }

            let length = 0;
            for (let i = 0; i < numCities - 1; i++) {
                const cityA = tour[i];
                const cityB = tour[i + 1];
                length += distance(cityA, cityB);
            }
            length += distance(tour[numCities - 1], tour[0]);

            if (length < totalLength) {
                improvements++;
                totalLength = length;
            }

            ctx.font = '24px Arial';
            ctx.fillStyle = '#333';
            ctx.textAlign = 'left';
            ctx.textBaseline = 'top';
            ctx.fillText(`Tour Length: ${totalLength.toFixed(2)}`, 10, 30);
            ctx.fillText(`Improvements: ${improvements}`, canvas.width - 250, 30);
        }

        function twoOptSwap(i, j) {
            if (j < i + 1 || !tour[i] || !tour[j]) return false;
            
            const [cityI, cityJ] = tour.slice(i, j + 1).reverse();

            ctx.beginPath();
            for (const city of cities) {
                if (!city) continue; // Skip visited cities
                
                ctx.moveTo(city[0], city[1]);
                ctx.lineTo(cityI.x, cityI.y);
                ctx.strokeStyle = 'red'; // Highlight the two edges being tested
                ctx.stroke();

                const newLength = distance(tour[(i - 1 + numCities) % numCities], cityI)
                                + distance(cityI, tour[(j + 1) % numCities])
                                + distance(tour[((i - 1 + numCities) % numCities)], tour[(j + 1) % numCities]);

                if (newLength < totalLength) {
                    improvements++;
                    totalLength = newLength;

                    ctx.beginPath();
                    ctx.moveTo(cityI.x, cityI.y);
                    for (let k = i; k <= j; k++) {
                        const nextCity = tour[k];
                        ctx.lineTo(nextCity.x, nextCity.y);
                        cities[k] = null; // Mark as visited
                    }
                    ctx.strokeStyle = '#8e3fb2'; // Redraw the reversed segment in a different color for clarity
                    ctx.stroke();
                    
                    tour.splice(i + 1, j - i, ...tour.slice(j).concat(tour.slice(0, i + 1)).reverse());
                }

                return true;
            }

            return false;
        }

        function optimize() {
            if (!twoOptSwap(0, numCities)) twoOptSwap(numCities - 1, 0);

            requestAnimationFrame(optimize);
        }

        init();
        optimize();
    </script>
</body>
</html>
Outside the Brain cluster — same task, same character budget, collected through their own tooling (response times not comparable)
Claude Opus 5frontier referenceOK
Show code
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>2-opt Travelling Salesman</title>
<style>
html,body{margin:0;height:100%;background:#080b14;overflow:hidden}
canvas{display:block}
#h{position:fixed;left:20px;top:16px;font:12px/1.7 ui-monospace,SFMono-Regular,Menlo,monospace;color:#7f95bd;letter-spacing:.08em;pointer-events:none;text-shadow:0 2px 10px #000}
#h .k{color:#e6efff;font-size:14px;letter-spacing:.22em}
#h b{color:#e6efff;font-weight:600}
#h .a{color:#ffb347}
#h .t{color:#4cd9c0}
</style></head><body>
<canvas id="c"></canvas>
<div id="h"></div>
<script>
const g=c.getContext('2d'),N=60,rnd=(a,b)=>a+Math.random()*(b-a);
let W,H,P,T,i,j,imp,ph,fl,fi,fj,grow,pass,L,L0;
function fit(){const d=Math.min(devicePixelRatio||1,2);W=innerWidth;H=innerHeight;c.width=W*d;c.height=H*d;c.style.width=W+'px';c.style.height=H+'px';g.setTransform(d,0,0,d,0,0)}
const D=(a,b)=>Math.hypot(P[a].x-P[b].x,P[a].y-P[b].y);
function tlen(){let s=0;for(let k=0;k<N;k++)s+=D(T[k],T[(k+1)%N]);return s}
function init(){
 P=[];const px=Math.max(36,W*.05),py=Math.max(34,H*.05);
 for(let k=0;k<N;k++)P.push({x:rnd(px,W-px),y:rnd(py+70,H-py)});
 const u=new Array(N).fill(0);T=[0];u[0]=1;
 for(let k=1;k<N;k++){const a=T[k-1];let b=-1,bd=1e18;
  for(let q=0;q<N;q++)if(!u[q]){const d=D(a,q);if(d<bd){bd=d;b=q}}
  u[b]=1;T.push(b)}
 i=0;j=1;imp=0;ph=0;fl=0;fi=0;fj=0;grow=1;pass=0;L=tlen();L0=L}
function step(){
 if(ph==0){grow++;if(grow>=N){ph=1;L=tlen();L0=L}return}
 if(ph==2){if(--fl<0){fit();init()}return}
 if(fl>0){fl--;return}
 for(let t=0;t<13;t++){
  j++;
  if(j>=N){i++;j=i+2}
  if(j>=N){if(!pass){ph=2;fl=100;return}pass=0;i=0;j=2}
  const a=T[i],b=T[i+1],x=T[j],y=T[(j+1)%N];
  if(a==y)continue;
  const dl=D(a,x)+D(b,y)-D(a,b)-D(x,y);
  if(dl<-1e-9){let lo=i+1,hi=j;while(lo<hi){const m=T[lo];T[lo]=T[hi];T[hi]=m;lo++;hi--}
   imp++;pass=1;L+=dl;fi=i;fj=j;fl=9;return}}}
function poly(s,e){g.beginPath();for(let k=s;k<=e;k++){const p=P[T[k%N]];k==s?g.moveTo(p.x,p.y):g.lineTo(p.x,p.y)}g.stroke()}
function draw(){
 g.fillStyle='#080b14';g.fillRect(0,0,W,H);
 g.lineJoin=g.lineCap='round';
 if(ph==0){g.strokeStyle='rgba(90,160,255,.75)';g.lineWidth=1.6;poly(0,grow-1);
  const p=P[T[grow-1]];g.fillStyle='#ffb347';g.beginPath();g.arc(p.x,p.y,5,0,7);g.fill()}
 else{
  g.strokeStyle='rgba(86,150,246,.5)';g.lineWidth=1.4;poly(0,N);
  if(fl>0){g.shadowColor='#4cd9c0';g.shadowBlur=16;g.strokeStyle='#4cd9c0';g.lineWidth=2.8;poly(fi,fj+1);g.shadowBlur=0}
  else if(ph==1){
   const a=P[T[i]],b=P[T[i+1]],x=P[T[j]],y=P[T[(j+1)%N]];
   g.strokeStyle='rgba(255,179,71,.3)';g.lineWidth=1.1;g.setLineDash([4,6]);g.beginPath();
   g.moveTo(a.x,a.y);g.lineTo(x.x,x.y);g.moveTo(b.x,b.y);g.lineTo(y.x,y.y);g.stroke();g.setLineDash([]);
   g.strokeStyle='#ffb347';g.lineWidth=2.5;g.shadowColor='#ffb347';g.shadowBlur=10;g.beginPath();
   g.moveTo(a.x,a.y);g.lineTo(b.x,b.y);g.moveTo(x.x,x.y);g.lineTo(y.x,y.y);g.stroke();g.shadowBlur=0}}
 for(let k=0;k<N;k++){const p=P[k];g.fillStyle='#0b1120';g.beginPath();g.arc(p.x,p.y,4.2,0,7);g.fill();
  g.fillStyle='#dce9ff';g.beginPath();g.arc(p.x,p.y,2.3,0,7);g.fill()}
 const st=ph==0?'<span class="a">building nearest-neighbour tour</span>':ph==2?'<span class="t">local optimum &mdash; new instance</span>':'<span class="a">testing 2-opt swap &nbsp;i='+i+' j='+j+'</span>';
 h.innerHTML='<div class="k">2-OPT &middot; TRAVELLING SALESMAN</div>cities <b>'+N+'</b> &nbsp; tour length <b>'+L.toFixed(1)+'</b><br>improvements <b class="t">'+imp+'</b> &nbsp; gain <b>'+(L0?((1-L/L0)*100).toFixed(1):'0.0')+'%</b><br>'+st}
function loop(){step();draw();requestAnimationFrame(loop)}
addEventListener('resize',()=>{fit();init()});
fit();init();loop();
</script></body></html>
GPT-5.5 (Codex)frontier referenceOKMINIFIED
Show code
<!doctype html><html><head><meta charset=utf-8><title>TSP 2-opt</title><style>html,body{margin:0;height:100%;background:#101318;color:#e8edf2;font:15px system-ui,sans-serif}canvas{display:block;width:100vw;height:100vh}.hud{position:fixed;left:14px;top:12px;background:#101318cc;border:1px solid #3a414b;border-radius:8px;padding:10px 12px;line-height:1.5}.b{color:#7bd88f}.y{color:#ffd166}</style></head><body><canvas id=c></canvas><div class=hud>length <span id=l class=b></span><br>improvements <span id=m class=y></span></div><script>
let C=c.getContext("2d"),W,H,P,T,A=0,B=2,S=0,I=0,last=0,mode=0,prog=0,base=0,after=0,old=[],seg=[];
onresize=()=>{W=c.width=innerWidth*devicePixelRatio;H=c.height=innerHeight*devicePixelRatio;C.setTransform(1,0,0,1,0,0)};onresize();
function d(a,b){let x=P[a].x-P[b].x,y=P[a].y-P[b].y;return Math.hypot(x,y)}
function len(t){let s=0;for(let i=0;i<t.length;i++)s+=d(t[i],t[(i+1)%t.length]);return s}
function start(){let n=60,mar=50*devicePixelRatio;P=[];for(let i=0;i<n;i++)P.push({x:mar+Math.random()*(W-2*mar),y:mar+Math.random()*(H-2*mar)});let u=[...Array(n).keys()],cur=u.splice(Math.random()*n|0,1)[0];T=[cur];while(u.length){let k=0,bd=1/0;for(let i=0;i<u.length;i++){let q=d(cur,u[i]);if(q<bd)bd=q,k=i}cur=u.splice(k,1)[0];T.push(cur)}A=0;B=2;S=0;I=0;mode=0;prog=0;base=len(T);l.textContent=base.toFixed(0);m.textContent=I}
function pts(t){let r=[];for(let i=0;i<t.length;i++)r.push(P[t[i]]);return r}
function path(t,alpha=1){C.globalAlpha=alpha;C.beginPath();let p=P[t[0]];C.moveTo(p.x,p.y);for(let i=1;i<t.length;i++){p=P[t[i]];C.lineTo(p.x,p.y)}C.closePath();C.stroke();C.globalAlpha=1}
function edge(a,b,col,w){C.strokeStyle=col;C.lineWidth=w;C.beginPath();C.moveTo(P[a].x,P[a].y);C.lineTo(P[b].x,P[b].y);C.stroke()}
function draw(){C.clearRect(0,0,W,H);C.lineCap="round";if(mode==2){C.strokeStyle="#39424e";C.lineWidth=2*devicePixelRatio;path(old,.45);let q=T.slice();let r=seg.length*prog|0;q.splice(A+1,r,...seg.slice(0,r).reverse());C.strokeStyle="#80e6ff";C.lineWidth=3*devicePixelRatio;path(q,.95);edge(old[A],old[(A+1)%60],"#ff6464",5*devicePixelRatio);edge(old[B],old[(B+1)%60],"#ff6464",5*devicePixelRatio);edge(T[A],T[(B+1)%60],"#7bd88f",5*devicePixelRatio);edge(T[A+1],T[B],"#7bd88f",5*devicePixelRatio)}else{C.strokeStyle="#7aa2f7";C.lineWidth=2*devicePixelRatio;path(T,.9);if(mode==1){edge(T[A],T[(A+1)%60],"#ffd166",5*devicePixelRatio);edge(T[B],T[(B+1)%60],"#ff8fab",5*devicePixelRatio)}}C.fillStyle="#f6f7fb";for(let p of P){C.beginPath();C.arc(p.x,p.y,3.2*devicePixelRatio,0,7);C.fill()}}
function next(){let n=T.length;for(;;){B++;if(B>=n-(A==0?1:0)){A++;B=A+2}if(A>=n-2){setTimeout(start,550);return}let a=T[A],b=T[A+1],c=T[B],e=T[(B+1)%n],gain=d(a,b)+d(c,e)-d(a,c)-d(b,e);mode=1;prog=0;if(gain>1e-6){old=T.slice();seg=T.slice(A+1,B+1);T.splice(A+1,seg.length,...seg.reverse());base-=gain;I++;l.textContent=base.toFixed(0);m.textContent=I;mode=2;prog=0;return}return}}
function tick(t){let dt=Math.min(40,t-last||16);last=t;prog+=dt/(mode==2?500:90);if(prog>=1){prog=0;next()}draw();requestAnimationFrame(tick)}
start();requestAnimationFrame(tick);
</script></body></html>
Claude Sonnet 5frontier referenceOK
Show code
<!DOCTYPE html><html><head><meta charset="utf-8"><title>TSP 2-opt</title>
<style>html,body{margin:0;background:#05060c;overflow:hidden;height:100%}canvas{display:block}
#h{position:fixed;top:8px;left:10px;color:#9fe;font:14px monospace;text-shadow:0 0 4px #000}</style>
</head><body><canvas id="c"></canvas><div id="h"></div>
<script>
const cv=document.getElementById('c'),x=cv.getContext('2d');
let W,H;function rz(){W=cv.width=innerWidth;H=cv.height=innerHeight;}
rz();addEventListener('resize',rz);
const n=60;
let cx,cy,tour,ti,tj,improvements,total,passImproved,pause,margin;
function d(a,b){const dx=cx[a]-cx[b],dy=cy[a]-cy[b];return Math.hypot(dx,dy);}
function tourLen(){let s=0;for(let k=0;k<n;k++)s+=d(tour[k],tour[(k+1)%n]);return s;}
function nnTour(){
  const used=new Uint8Array(n);
  const t=[0];used[0]=1;
  for(let k=1;k<n;k++){
    const last=t[t.length-1];
    let best=-1,bd=Infinity;
    for(let m=0;m<n;m++){
      if(used[m])continue;
      const dd=d(last,m);
      if(dd<bd){bd=dd;best=m;}
    }
    t.push(best);used[best]=1;
  }
  return t;
}
function init(){
  margin=40;
  cx=new Float64Array(n);cy=new Float64Array(n);
  for(let i=0;i<n;i++){
    cx[i]=margin+Math.random()*(W-2*margin);
    cy[i]=margin+Math.random()*(H-2*margin);
  }
  tour=nnTour();
  total=tourLen();
  improvements=0;ti=0;tj=1;passImproved=false;pause=0;
}
init();
function tryStep(){
  if(pause>0){pause--;if(pause===0)init();return;}
  const steps=4;
  for(let s=0;s<steps;s++){
    if(ti>n-2){
      if(!passImproved){pause=90;return;}
      passImproved=false;ti=0;tj=1;continue;
    }
    if(tj>n-1){ti++;tj=ti+1;continue;}
    const i=ti,j=tj;
    if(!(i===0&&j===n-1)){
      const a=tour[i],b=tour[(i+1)%n],c=tour[j],e=tour[(j+1)%n];
      const oldD=d(a,b)+d(c,e);
      const newD=d(a,c)+d(b,e);
      if(newD<oldD-1e-7){
        let lo=i+1,hi=j;
        while(lo<hi){const tmp=tour[lo];tour[lo]=tour[hi];tour[hi]=tmp;lo++;hi--;}
        total+=newD-oldD;
        improvements++;passImproved=true;
      }
    }
    tj++;
  }
}
function draw(){
  x.fillStyle='#05060c';x.fillRect(0,0,W,H);
  x.strokeStyle='#5fb3ff';x.lineWidth=1.6;
  x.beginPath();
  for(let k=0;k<=n;k++){
    const p=tour[k%n];
    if(k===0)x.moveTo(cx[p],cy[p]);else x.lineTo(cx[p],cy[p]);
  }
  x.stroke();
  if(pause===0){
    const a=tour[ti],b=tour[(ti+1)%n],c=tour[Math.min(tj,n-1)],e=tour[(Math.min(tj,n-1)+1)%n];
    x.strokeStyle='#ff5050';x.lineWidth=2.6;
    x.beginPath();x.moveTo(cx[a],cy[a]);x.lineTo(cx[b],cy[b]);x.stroke();
    x.beginPath();x.moveTo(cx[c],cy[c]);x.lineTo(cx[e],cy[e]);x.stroke();
  }
  x.fillStyle='#ffd166';
  for(let i=0;i<n;i++){
    x.beginPath();x.arc(cx[i],cy[i],3.4,0,7);x.fill();
  }
  document.getElementById('h').textContent=
    `tour length: ${total.toFixed(1)}   improvements: ${improvements}${pause>0?'   (optimal, restarting...)':''}`;
}
function loop(){
  tryStep();draw();
  requestAnimationFrame(loop);
}
loop();
</script></body></html>
Claude Haiku 4.5frontier referenceOKMINIFIED
Show code
<!DOCTYPE html><html><head><meta charset="utf-8"><style>body{margin:0;background:#000;overflow:hidden}#c{display:block}#i{position:absolute;top:5px;left:5px;color:#0f0;font-family:monospace;font-size:12px}</style></head><body><canvas id="c"></canvas><div id="i"></div><script>const c=document.getElementById('c'),ctx=c.getContext('2d'),i=document.getElementById('i');c.width=800;c.height=600;let ct=[];for(let j=0;j<60;j++){ct.push({x:Math.random()*700+50,y:Math.random()*500+50})}function d(p1,p2){return Math.hypot(p1.x-p2.x,p1.y-p2.y)}function tl(t){let l=0;for(let j=0;j<t.length;j++){l+=d(ct[t[j]],ct[t[(j+1)%t.length]])}return l}let t=Array.from({length:60},((_,i)=>i));t=[t[0]];let u=new Set([0]);for(let j=1;j<60;j++){let mi=-1,md=1e9;for(let k=0;k<60;k++){if(!u.has(k)){let d2=d(ct[t[t.length-1]],ct[k]);if(d2<md){md=d2;mi=k}}}t.push(mi);u.add(mi)}let im=0;function dr(){ctx.fillStyle='#000';ctx.fillRect(0,0,800,600);for(let k=0;k<5;k++){let i1=Math.floor(Math.random()*60),i2=Math.floor(Math.random()*60);if(i1===i2)continue;let tl1=tl(t);let t2=t.slice();let p1=t2.indexOf(i1),p2=t2.indexOf(i2);if(p1>p2)[p1,p2]=[p2,p1];t2=t2.slice(0,p1+1).concat(t2.slice(p1+1,p2+1).reverse()).concat(t2.slice(p2+1));let tl2=tl(t2);if(tl2<tl1){t=t2;im++}}ctx.strokeStyle='#0f0';ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(ct[t[0]].x,ct[t[0]].y);for(let j=1;j<60;j++){ctx.lineTo(ct[t[j]].x,ct[t[j]].y)}ctx.lineTo(ct[t[0]].x,ct[t[0]].y);ctx.stroke();ctx.fillStyle='#0f0';for(let j=0;j<60;j++){ctx.fillRect(ct[t[j]].x-2,ct[t[j]].y-2,4,4)}i.textContent=`Dist: ${tl(t).toFixed(0)} Impr: ${im}`}setInterval(dr,50);</script></body></html>
Results at a glance— every model in this round with score, response time and status.

Who wrote what

ModelBlind scoreLatencyStatus
mistral-small:24b —145.3 sOK
qwen3-coder:30b —40.5 sOK
deepseek-r1:14b —36.3 sOK
qwen3:8b ——harness
qwen3.5:9b —79.0 sOK
qwen3:14b —74.1 sOK
llama3.1:8b —148.3 sOK
gemma4:26b ——harness
deepseek-r1:32b —143.9 sOK
qwq:32b —142.8 sOK
command-r:35b —152.1 sOK