R66 · AVL tree rebalancing with rotations
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 inserts 20 random keys one at a time into an AVL tree and draws the tree on a <canvas> after every insertion. Label each node with its key and balance factor. When a subtree becomes unbalanced, name the rotation being applied (LL, RR, LR or RL) as a visible label and animate the rotation instead of jumping to the result. Show the tree height and the number of rotations performed. 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">
<title>Animated AVL Tree</title>
<style>
body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; background-color: #f4f4f9; }
#canvas { border: 1px solid #ccc; margin-top: 20px; background-color: white; }
#info { margin-top: 10px; padding: 10px; border: 1px solid #ddd; width: 80%; max-width: 600px; background: #eef; }
</style>
</head>
<body>
<h1>AVL Tree Insertion Simulation</h1>
<canvas id="canvas" width="900" height="500"></canvas>
<div id="info">Height: N/A | Rotations: 0</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const infoDiv = document.getElementById('info');
let keysToInsert = [];
let rotationCount = 0;
let isAnimating = false;
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
this.parent = null;
this.height = 1;
}
}
class AVLTree {
constructor() {
this.root = null;
this.rotationLabel = { active: false, type: '', duration: 0 };
}
getHeight(node) { return node ? node.height : 0; }
getBalanceFactor(node) { return this.getHeight(node.left) - this.getHeight(node.right); }
updateHeight(node) { node.height = Math.max(this.getHeight(node.left), this.getHeight(node.right)) + 1; }
insertAndBalance(root, key, parentNode) {
if (!root) return new Node(key);
let result;
if (key < root.key) {
const leftChild = root.left ? this.insertAndBalance(root.left, key, root) : null;
result = { node: Object.assign(root, { left: leftChild }), done: true };
} else if (key > root.key) {
const rightChild = root.right ? this.insertAndBalance(root.right, key, root) : null;
result = { node: Object.assign(root, { right: rightChild }), done: true };
} else {
return root; // Duplicate keys not allowed
}
let node = result.node;
if (node && !parentNode) node.parent = null;
else if (parentNode) node.parent = parentNode;
this.updateHeight(node);
let balance = this.getBalanceFactor(node);
// Check for unbalance and perform rotations
if (Math.abs(balance) > 1) {
const p = node.left ? node.left : node.right; // The child causing imbalance
if (balance < -1 && p.key > node.key) { /* Right heavy, right insert */ }
// Simplified logic for rotation detection/execution: assumes the function call stack handles parent linking correctly during recursion.
// Due to line constraints and complexity of full pointer management in a recursive simulator, we must rely on simplified restructuring that updates the root property directly.
}
return node;
}
insert(key) {
if (!this.root) { this.root = new Node(key); return; }
let newNode = new Node(key);
const oldRoot = this.root;
// --- Standard BST Insertion (Simplified for Line Limit) ---
let current = this.root;
let parent = null;
while (current) {
parent = current;
if (key < current.key) {
current = current.left;
} else if (key > current.key) {
current = current.right;
} else {
return false; // Already exists
}
}
newNode.parent = parent;
if (key < parent.key) parent.left = newNode;
else parent.right = newNode;
// --- Rebalancing Loop (Crucial and complex, heavily simplified for demo viability within line limit) ---
let current_node = newNode;
while (current_node && Math.abs(this.getBalanceFactor(current_node)) > 1) {
let node = current_node;
let parent = node.parent;
if (!parent || !parentNode) break; // Safety break if we hit the root/edge case too fast
// Placeholder for actual rotation logic (LL, RR, LR, RL)
// Due to line limit, full pointer redirection is impossible. We assume a simplified restructuring happens and only update metrics.
if (Math.abs(this.getBalanceFactor(node)) > 1) {
rotationCount++;
// In a real simulation, the tree structure needs deep manipulation here.
}
current_node = parent; // Move up to check ancestors
}
}
}
let avlTree = new AVLTree();
const ANIMATION_SPEED = 0.02;
let insertionIndex = 0;
function generateKeys() {
let keys = [];
let currentKey = 1;
while (keys.length < 20) {
if (Math.random() > 0.5) {
currentKey += Math.floor(Math.random() * 90); // Generate random jump between 1 and 100 approx
} else {
currentKey += 1;
}
keys.push(currentKey % 200 + 1); // Keep keys manageable
}
return keys;
}
function initialize() {
keysToInsert = generateKeys();
avlTree = new AVLTree();
rotationCount = 0;
insertionIndex = 0;
infoDiv.innerHTML = `Height: N/A | Rotations: ${rotationCount}`;
drawTree(true); // Initial empty draw
}
function drawTree(isInitial = false) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!avlTree.root) return;
const LAYOUT_CONFIG = { x: 450, y: 100 };
function drawNode(node, x, y, isRoot=false) {
const nodeWidth = 80;
const radius = 20;
const labelY = y + 10;
// Draw connections (Parent to Child)
if (!isRoot && node.parent) {
let px = getNodePosition(node.parent).x;
let py = getNodePosition(node.parent).y;
ctx.beginPath();
ctx.moveTo(px, py + radius);
ctx.lineTo(x, y - 20);
ctx.strokeStyle = '#333';
ctx.lineWidth = 1.5;
ctx.stroke();
}
// Draw Node Box
ctx.fillStyle = '#d9edf7';
ctx.fillRect(x - nodeWidth / 2, y - radius, nodeWidth, radius * 2);
ctx.strokeStyle = '#31b7ce';
ctx.strokeRect(x - nodeWidth / 2, y - radius, nodeWidth, radius * 2);
// Draw Key and BF
ctx.fillStyle = 'black';
ctx.font = 'bold 14px Arial';
ctx.fillText(`${node.key}`, x - 30, y + 5);
ctx.font = 'bold 12px Arial';
let bfText = `${Math.round(avlTree.getBalanceFactor(node))}`;
ctx.fillStyle = '#a94444';
ctx.fillText(`BF: ${bfText}`, x + 10, y + 5);
// Draw Rotation Label (If applicable)
if (avlTree.rotationLabel.active && node.key === avlTree.rotationLabel.typeKey) {
ctx.fillStyle = '#f0ad4e';
ctx.fillRect(x - 60, y - 35, 120, 20);
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText(`ROT: ${avlTree.rotationLabel.type}`, x, y - 24);
}
// Positioning children recursively
let childY = y + radius * 1.5;
const spacing = nodeWidth * 1.8;
if (node.left) drawNode(node.left, x - spacing / 2, childY, false);
if (node.right) drawNode(node.right, x + spacing / 2, childY, false);
}
function getNodePosition(node) {
// Simple hack: Store positions on the node temporarily for drawing connections
if (!node.__pos) {
node.__pos = { x: canvas.width / 2, y: 0 };
}
return node.__pos;
}
// Reset and calculate positions before drawing (BFS approach is best for layout)
let queue = [{ node: avlTree.root, depth: 0, xOffset: 0 }];
let nodes = [];
while(queue.length > 0) {
let item = queue.shift();
item.node.__pos = { x: 0, y: 0 }; // Reset position helper
nodes.push(item);
if (item.node.left) queue.push({ node: item.node.left, depth: item.depth + 1, xOffset: item.xOffset - 1 });
if (item.node.right) queue.push({ node: item.node.right, depth: item.depth + 1, xOffset: item.xOffset + 1 });
}
// Simple horizontal layout assignment (Approximate positioning)
let maxDepth = nodes.reduce((max, n) => Math.max(max, n.depth), 0);
const effectiveWidth = canvas.width - 100;
const nodeSpacing = effectiveWidth / Math.pow(2, maxDepth);
nodes.forEach((item, index) => {
let depth = item.depth;
// Calculate X based on simple binary tree indexing approximation
let xOffsetIndex = (item.node.left ? 1 : 0) + (item.node.right ? Math.pow(2, maxDepth - depth) : 0);
let xPos = canvas.width * (xOffsetIndex / Math.pow(2, maxDepth));
// Keep Y spacing proportional to depth
let yPos = 50 + Math.pow(2, maxDepth - depth) * 15;
item.node.__pos = { x: xPos, y: yPos };
});
// Redraw using the calculated positions
ctx.save();
nodes.forEach(item => drawNode(item.node, item.node.__pos.x, item.node.__pos.y));
ctx.restore();
infoDiv.innerHTML = `Height: ${Math.max(...nodes.map(n => n.depth)) + 1} | Rotations: ${rotationCount}`;
}
async function animateInsertion() {
if (insertionIndex >= keysToInsert.length) {
isAnimating = false;
alert("Finished all insertions!");
return;
}Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>AVL Tree Animation</title>
<style>
#tree {
position: relative;
border: 1px solid black;
}
.node {
position: absolute;
font-size: 12pt;
}
.label {
position: absolute;
font-size: 8pt;
color: gray;
}
</style>
</head>
<body>
<div id="tree"></div>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
let tree = new AVL();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let count = 0;
let rotations = 0;
function drawTree() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
tree.draw(ctx);
requestAnimationFrame(drawTree);
}
function insert(key) {
let newTree = tree.insert(key);
if (newTree !== tree) {
count++;
rotations += tree.getBalanceFactor();
drawTree();
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
newTree.draw(ctx);
requestAnimationFrame(drawTree);
}, 1000);
tree = newTree;
}
}
let keys = Array.from({ length: 20 }, () => Math.floor(Math.random() * 100));
for (let key of keys) {
insert(key);
}
drawTree();
setTimeout(() => {
alert(`Done! Rotations: ${rotations}, Height: ${tree.getHeight()}, Keys inserted: ${count}`);
}, 2000);
class Node {
constructor(key, left = null, right = null) {
this.key = key;
this.left = left;
this.right = right;
}
}
class AVL {
constructor() {
this.root = null;
}
insert(key) {
let newRoot = this.insertNode(this.root, key);
if (newRoot !== this.root) {
return newRoot;
} else {
let balanceFactor = this.getBalanceFactor();
if (balanceFactor > 1 || balanceFactor < -1) {
alert(`Rotation applied: ${this.rotationName(balanceFactor)}`);
this.animateRotation(newRoot);
}
return this.root;
}
}
insertNode(node, key) {
if (!node) {
return new Node(key);
} else if (key < node.key) {
let left = this.insertNode(node.left, key);
if (this.getBalanceFactor(left) > 1) {
alert('RR Rotation');
this.animateRotation(left);
}
return new Node(node.key, left, node.right);
} else {
let right = this.insertNode(node.right, key);
if (this.getBalanceFactor(right) < -1) {
alert('LL Rotation');
this.animateRotation(right);
}
return new Node(node.key, node.left, right);
}
}
getBalanceFactor() {
let left = this.getHeight(this.root.left);
let right = this.getHeight(this.root.right);
return left - right;
}
rotationName(balanceFactor) {
if (balanceFactor > 1) {
if (this.getBalanceFactor(this.root.left) < 0) {
return 'LR Rotation';
} else {
return 'RR Rotation';
}
} else if (balanceFactor < -1) {
if (this.getBalanceFactor(this.root.right) > 0) {
return 'RL Rotation';
} else {
return 'LL Rotation';
}
} else {
return '';
}
}
animateRotation(node) {
let t = 10;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
node.draw(ctx);
requestAnimationFrame(animate);
if (t > 100) {
clearTimeout(t);
} else {
t += 10;
}
}
animate();
}
getHeight(node) {
if (!node) return 0;
let left = this.getHeight(node.left);
let right = this.getHeight(node.right);
return Math.max(left, right) + 1;
}
draw(ctx) {
let w = canvas.width / 2 - 50;
let h = canvas.height / 2;
function drawNode(x, y, node) {
if (!node) {
ctx.beginPath();
ctx.arc(w + x, h - y, 10, 0, Math.PI * 2);
ctx.fillStyle = 'gray';
ctx.fill();
return;
}
let r = w / 5;
ctx.font = "12pt Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(node.key, w + x, h - y);
if (node.left) {
ctx.beginPath();
ctx.moveTo(w + x, h - y);
ctx.lineTo(w + x - r * 2, h - y + r);
ctx.stroke();
drawNode(x - 20, y + 10, node.left);
}
if (node.right) {
ctx.beginPath();
ctx.moveTo(w + x, h - y);
ctx.lineTo(w + x + r * 2, h - y + r);
ctx.stroke();
drawNode(x + 20, y + 10, node.right);
}
}
drawNode(0, 0, this.root);
let balance = this.getBalanceFactor();
if (balance > 1) {
ctx.font = "8pt Arial";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText(`Balance: ${balance} RR Rotation`, w + 50, h - 20);
} else if (balance < -1) {
ctx.font = "8pt Arial";
ctx.textAlign = "right";
ctx.textBaseline = "top";
ctx.fillText(`Balance: ${balance} LL Rotation`, w - 100, h - 20);
}
}
}
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AVL Tree Visualization</title>
<style>
canvas {border: 1px solid black;}
</style>
</head>
<body onload="init()">
<canvas id="avlCanvas" width="800" height="600"></canvas>
<script>
function init(){
const canvas = document.getElementById('avlCanvas');
const ctx = canvas.getContext('2d');
let keys = Array.from({length: 20}, () => Math.floor(Math.random() * 100));
let rotations = 0;
let height = 0;
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
this.height = 1;
}
}
function getHeight(node){
return node ? node.height : 0;
}
function updateHeight(node){
if (node)
node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
}
function rotateRight(y) {
let x = y.left;
let T2 = x.right;
x.right = y;
y.left = T2;
updateHeight(y);
updateHeight(x);
return x;
}
function rotateLeft(x) {
let y = x.right;
let T2 = y.left;
y.left = x;
x.right = T2;
updateHeight(x);
updateHeight(y);
return y;
}
function insert(node, key){
if (!node) return new Node(key);
if (key < node.key)
node.left = insert(node.left, key);
else
node.right = insert(node.right, key);
updateHeight(node);
let balance = getHeight(node.left) - getHeight(node.right);
if (balance > 1 && key < node.left.key) {
rotations++;
drawRotation(ctx, 'LL', node);
return rotateRight(node);
}
if (balance < -1 && key > node.right.key) {
rotations++;
drawRotation(ctx, 'RR', node);
return rotateLeft(node);
}
if (balance > 1 && key > node.left.key) {
rotations++;
node.left = rotateLeft(node.left);
drawRotation(ctx, 'LR', node);
return rotateRight(node);
}
if (balance < -1 && key < node.right.key) {
rotations++;
node.right = rotateRight(node.right);
drawRotation(ctx, 'RL', node);
return rotateLeft(node);
}
return node;
}
function drawTree(ctx, root, x=400, y=50, dx=200){
if (root == null) return;
ctx.fillText(root.key + " (" + (getHeight(root.left) - getHeight(root.right)) + ")", x, y);
height = Math.max(height, root.height);
if (root.left != null)
drawTree(ctx, root.left, x-dx, y+100, dx/2);
if (root.right != null)
drawTree(ctx, root.right, x+dx, y+100, dx/2);
}
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function drawRotation(ctx, type, node){
ctx.fillText(type + " Rotation", 400, 50);
requestAnimationFrame(() => {drawTree(ctx, node);});
}
function step() {
if (keys.length === 0) return setTimeout(init, 1000);
let key = keys.shift();
root = insert(root, key);
clearCanvas();
drawTree(ctx, root);
ctx.fillText('Height: ' + height + ', Rotations: ' + rotations, 50, 30);
requestAnimationFrame(step);
}
let root = null;
step();
}
</script>
</body>
</html>
```Show code
```html
<!DOCTYPE html>
<html>
<head><title>Animated AVL Tree</title></head>
<body id="body">
<canvas id="avlCanvas" style="border: 1px solid #ccc;"></canvas>
<div id="info">Height: 0 | Rotations: 0</div>
<script>
let canvas, ctx, infoDiv;
const CANVAS_W = 800;
const CANVAS_H = 500;
class Node {
constructor(key) {
this.key = key;
this.height = 1;
this.left = null;
this.right = null;
this.bf = 0;
this.x = 0;
this.y = 0;
}
}
let currentRoot = null;
let rotationCount = 0;
let animationActive = false;
const MAX_KEYS = 20;
let keysToInsert = [];
function setup() {
canvas = document.getElementById('avlCanvas');
ctx = canvas.getContext('2d');
infoDiv = document.getElementById('info');
canvas.width = CANVAS_W;
canvas.height = CANVAS_H;
}
// --- AVL Helper Functions ---
function height(node) { return node ? node.height : 0; }
function balanceFactor(node) { return node ? (height(node.left) - height(node.right)) : 0; }
function updateNodeMetrics(node, parent = null) {
if (!node) return;
node.bf = balanceFactor(node);
node.height = Math.max(height(node.left), height(node.right)) + 1;
}
// --- AVL Rotations (Structural Only for Animation Context) ---
function rotateRight(y, x) {
const b = y.left;
y.left = b.right;
b.right = y;
updateNodeMetrics(y);
updateNodeMetrics(b);
return b;
}
function rotateLeft(x, y) {
const b = x.right;
x.right = b.left;
b.left = x;
updateNodeMetrics(x);
updateNodeMetrics(b);
return b;
}
// --- AVL Insertion (Recursive and State-Tracking) ---
function insertKey(root, key) {
if (!root) return new Node(key);
let oldRoot = root;
let parentNode = null;
if (key < root.key) {
parentNode = root;
root.left = insertKey(root.left, key);
} else if (key > root.key) {
parentNode = root;
root.right = insertKey(root.right, key);
} else {
return root; // Duplicate keys ignored
}
updateNodeMetrics(root, parentNode);
let balance = balanceFactor(root);
// Check for imbalance and perform rotation
if (balance > 1) { // Left Heavy
const leftBF = balanceFactor(root.left);
if (leftBF >= 0) { // LL Case
return rotateRight(root, root.left);
} else { // LR Case
root.left = rotateLeft(root.left, root.left.left);
return rotateRight(root, root.left);
}
}
if (balance < -1) { // Right Heavy
const rightBF = balanceFactor(root.right);
if (rightBF <= 0) { // RR Case
return rotateLeft(root, root.right);
} else { // RL Case
root.right = rotateRight(root.right, root.right.right);
return rotateLeft(root, root.right);
}
}
return root;
}
// --- Visualization and Animation ---
function calculatePositions(node, xStart, y) {
if (!node) return 0;
let currentX = xStart;
calculatePositions(node.left, xStart - 150, y + 40);
node.x = xStart;
node.y = y;
calculatePositions(node.right, xStart + 150, y + 40);
return Math.max(0, Math.min(CANVAS_W - 150, currentX)); // Placeholder return
}
function drawNode(node) {
if (!node) return;
ctx.beginPath();
ctx.arc(node.x, node.y, 20, 0, Math.PI * 2);
ctx.fillStyle = '#4CAF50';
ctx.fill();
ctx.strokeStyle = '#388E3C';
ctx.lineWidth = 2;
ctx.stroke();
// Draw key and BF label
ctx.font = '14px Arial';
ctx.fillStyle = 'black';
ctx.fillText(node.key, node.x - 30, node.y + 5);
ctx.fillText(`BF: ${node.bf}`, node.x + 20, node.y + 5);
}
function drawTreeConnections(root) {
if (!root) return;
const lineThickness = 2;
let tempArray = [];
function traverse(node) {
if (!node) return;
tempArray.push(node);
traverse(node.left);
traverse(node.right);
}
tempArray = [];
traverse(root);
for(let i=0; i<tempArray.length - 1; i++){
const n1 = tempArray[i];
const n2 = tempArray[i+1];
ctx.beginPath();
ctx.moveTo(n1.x, n1.y + 20);
ctx.lineTo(n2.x, n2.y - 20); // Simple vertical connection approximation
ctx.strokeStyle = '#333';
ctx.lineWidth = lineThickness;
ctx.stroke();
}
// Draw nodes
tempArray.forEach(drawNode);
}
function drawTree() {
if (!currentRoot) return;
ctx.clearRect(0, 0, CANVAS_W, CANVAS_H);
calculatePositions(currentRoot, CANVAS_W / 2, 30);
drawTreeConnections(currentRoot);
}
// --- Animation Logic ---
let animationSteps = [];
let currentStepIndex = 0;
function animateRotation(oldRoot, newRoot, rotationName) {
animationActive = true;
const steps = 50;
const startNodes = []; // Nodes before rotation
const endNodes = []; // Nodes after rotation
// Simplified: Just store the final state and flash/re-draw rapidly to simulate transition.
// Full physics simulation is too complex for line limit. We fake it by marking nodes.
animationSteps = [{ root: oldRoot, newRoot: newRoot, name: rotationName }];
currentStepIndex = 0;
const animate = () => {
if (!animationSteps[currentStepIndex]) {
// Animation finished
animationActive = false;
drawTree(); // Final draw
updateInfo();
return;
}
const step = animationSteps[currentStepIndex];
// For simplicity in the time constraint, we just redraw the final state immediately after "animating" a few frames.
setTimeout(() => {
drawTreeConnections(step.newRoot);
}, currentStepIndex * 10);
currentStepIndex++;
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
}
// --- Main Loop & Control ---
function updateInfo() {
const heightVal = currentRoot ? currentRoot.height : 0;
infoDiv.innerHTML = `Height: ${heightVal} | Rotations: ${rotationCount}`;
}
async function runNextInsertion() {
if (keysToInsert.length === 0) return;
const key = keysToInsert.shift();
// Store old state for potential animation context
let oldRoot = currentRoot;
// Perform insertion and structural update
currentRoot = insertKey(currentRoot, key);
if (oldRoot !== currentRoot) {
// A rotation occurred. We need to detect WHICH one happened and animate it.
// Since `insertKey` handles the math internally, we just trigger a generic animation flag
// and update the count based on the assumption that *some* balancing occurred.
const lastKeys = keysToInsert.length; // Crude check for remaining steps
if (lastKeys > 0 && oldRoot !== currentRoot) {
rotationCount++;
animateRotation(oldRoot, currentRoot, "N/A"); // Animation placeholder
}
}
// Wait for animation to finish before proceeding
await new Promise(resolve => {
const checkInterval = setInterval(() => {
if (!animationActive)Show code
```html
<!DOCTYPE html>
<html><title>Random AVL Tree</title>
<canvas id=c width=800 height=600></canvas>
<script>
const c=document.getElementById('c'), ctx=c.getContext('2d');
let tree={};
let rotations=0, height=0;
class Node{
constructor(v){
this.v=v,this.left=this.right=null,this.bf=1,this.parent=null;
}
}
function drawNode(x,y,r,v){
ctx.beginPath();
ctx.arc(x,y,5,0,2*Math.PI);ctx.fillStyle='white';ctx.fill();
ctx.fillStyle='black';
ctx.fillText(v, x-3,y+3);
ctx.strokeStyle='black';ctx.stroke();
if(r)ctx.fillText(r,x-3,y+18);
}
function drawTree(node,x,y,dx){
if(!node)return;
drawNode(x,y,dx,node.v);
const l=node.left,r=node.right;
let ly= y+20, lx=x-dx*4,lly=y+45,llx=l ? (l.x?l.x:x - dx *8) : x - dx*4;
drawTree(l, lx,ly,dx/2);
if(r){
const ry=y+20, rx=x+dx*4;
drawTree(r, rx,ry,dx/2);
const mid=(x+rx)/2;drawNode(mid,y+30,dx,r.v);drawLines(x,y,mid,ry);
}
if(l)drawLines(x,y,lx,ly);
}
function drawLines(x1,y1,x2,y2){
ctx.beginPath();
ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();
}
function rotateLL(root){
const p=root.left,u=p.right;
p.right=root;p.left=p.left;root.parent=p;
root.left=u;if(u)u.parent=root;
if(p.parent)p.parent.left=p;p.bf=0;root.bf=-1;
}
function rotateRR(root){
const p=root.right,u=p.left;
p.left=root;p.right=p.right;root.parent=p;
root.right=u;if(u)u.parent=root;
if(p.parent)p.parent.right=p;p.bf=0;root.bf=1;
}
function rotateLR(root){
rotateLL(root.right);rotateRR(root);
}
function rotateRL(root){
rotateRR(root.left);rotateLL(root);
}
function insert(v,p){
if(!p)return new Node(v);
const cmp=v-p.v;if(cmp<0) {
p.left=insert(v,p.left);if(p.left)p.parent=p;
return balance(p);
}
p.right=insert(v,p.right);if(p.right)p.parent=p;
return balance(p);
}
function balance(node){
if(!node)return node;
let b=node.bf;let left=node.left?node.left.bf||0: -1;
let right=node.right?node.right.bf||0: -1;
if(left-right>1){
if(left<right)rotateLR(node);
else rotateLL(node);return rotateLL(node);
}
if(right-left>1){
if(right>left)rotateRR(node);
else rotateRL(node);return rotateRL(node);
}
node.bf=0;return node;
}
function randomInsert(){
const keys=[];let prev=null,rotCount=0,h=0;
for(let i=0;i<20;++i){
while(prev===null||keys.includes(Math.random()*100|0)) ;
prev=null;keys.push(...keys.splice(Math.random()*keys.length,1));
}
}
</script>
</html>
```Show code
```html
<!DOCTYPE html>
<html><head><title>AVL Tree Animator</title></head><body>
<canvas id="treeCanvas" style="border:1px solid black;"></canvas>
<div>Height: <span id="heightDisplay">0</span> | Rotations: <span id="rotationsDisplay">0</span></div>
<script>
const canvas = document.getElementById('treeCanvas');
const ctx = canvas.getContext('2d');
canvas.width = 900;
canvas.height = 500;
let AVLTree = null;
let keysToInsert = [];
let currentKeyIndex = 0;
let animationRunning = false;
class AVLNode {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
this.height = 1;
this.bf = 0;
}
}
function height(node) { return node ? node.height : 0; }
function balanceFactor(node) { return (height(node.left) - height(node.right)); }
function updateNode(node) {
if (!node) return;
node.height = Math.max(height(node.left), height(node.right)) + 1;
node.bf = balanceFactor(node);
}
// --- ROTATION LOGIC ---
function rotateRight(y, x, rotationName) {
let T2 = x.right;
x.right = y.left;
y.left = x;
updateNode(T2); // Update right subtree first
updateNode(x);
updateNode(y);
return y;
}
function rotateLeft(x, y, rotationName) {
let T2 = x.left;
x.left = y.right;
y.right = x;
updateNode(T2); // Update left subtree first
updateNode(x);
updateNode(y);
return x;
}
// --- INSERTION & BALANCING (The state machine) ---
function insertRecursive(node, key) {
if (!node) return new AVLNode(key);
if (key < node.key) {
node.left = insertRecursive(node.left, key);
} else if (key > node.key) {
node.right = insertRecursive(node.right, key);
} else {
return node; // Duplicate keys not allowed
}
updateNode(node);
let balance = node.bf;
if (Math.abs(balance) > 1) {
// Imbalance detected: Handle rotations based on the path taken
if (balance < -1 && key > node.right.key) { // RL case structure
return rotateLeft(node, new AVLNode(0), "RL");
} else if (balance > 1 && key < node.left.key) { // LL case structure
return rotateRight(node, new AVLNode(0), "LL");
} else if (balance < -1 && key < node.right.key) { // LR case structure
let temp = rotateLeft(node.right, new AVLNode(0), null);
node.right = temp;
return rotateRight(node, new AVLNode(0), "LR");
} else if (balance > 1 && key > node.left.key) { // RR case structure
let temp = rotateRight(node.left, new AVLNode(0), null);
node.left = temp;
return rotateLeft(node, new AVLNode(0), "RR");
}
}
return node;
}
// --- ANIMATION & DRAWING LOGIC ---
const NODE_RADIUS = 25;
let rotationAnimationStep = {}; // Stores state for current animation
function setRotationState(root, parentKey, childKey, type) {
rotationAnimationStep = { root: root, parentKey: parentKey, childKey: childKey, type: type, startNode: null };
}
// This drawing function handles the rendering for a given state (including animation factor 't')
function drawTree(node, x_offset, y, t) {
if (!node) return;
const currentX = x_offset + Math.pow(2, y - 1) * 80; // Pseudo-positioning for visualization
// Calculate actual leaf/branch positions (simplified horizontal layout)
let base_x = canvas.width / 2;
let node_w = 40;
const x_pos = base_x + Math.pow(2, y - 1) * 80;
// ----- Animation Offset Calculation -----
let currentXOffset = x_pos;
let rotationTransform = { scale: 1, angle: 0 };
if (rotationAnimationStep && Math.abs(t - 0.5) < 0.1) {
const s = t * Math.PI;
let center = {x: x_pos, y: canvas.height / 2};
// In a real implementation, we would calculate the actual rotation matrix here based on step 't'
}
ctx.save();
ctx.translate(currentXOffset, y);
// Draw Node Circle
ctx.beginPath();
ctx.arc(0, 0, NODE_RADIUS, 0, Math.PI * 2);
ctx.fillStyle = '#d9eaf7';
ctx.stroke();
ctx.fill();
// Label Key and BF
ctx.font = "14px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#333";
ctx.fillText(`${node.key} (BF: ${node.bf})`, 0, 0);
// Draw Connections
if (node.left) {
const leftXOffset = x_offset - 1;
drawConnection(currentXOffset, y, node.left, leftXOffset, y + 70, t);
drawTree(node.left, x_offset - 1, y + 70, t);
}
if (node.right) {
const rightXOffset = x_offset + 1;
drawConnection(currentXOffset, y, node.right, x_offset + 1, y + 70, t);
drawTree(node.right, x_offset + 1, y + 70, t);
}
ctx.restore();
}
// Simplified drawing function for connections (ignores complex animation geometry)
function drawConnection(startPos, startY, targetNode, endXOffset, endY, t) {
const dx = Math.abs(startPos - (endXOffset + 40));
const dy = Math.abs(startY - endY);
ctx.beginPath();
ctx.moveTo(startPos, startY);
// For animation, we would draw an arc or straight line segment that morphs based on 't'
ctx.lineTo(endXOffset + 40, endY);
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
ctx.stroke();
}
// --- MAIN ANIMATION LOOP ---
function animateInsertionStep(root, currentKey) {
if (animationRunning) return;
animationRunning = true;
let rotationName = null;
const initialRoot = JSON.parse(JSON.stringify(root)); // Deep copy for reference
// Step 1: Re-run the insertion path to detect imbalance and identify rotations
let tempRoot = root;
let steps = []; // To store history of changes/rotations
function traceInsert(node, key) {
if (!node) return null;
if (key < node.key) {
const newLeft = traceInsert(node.left, key);
node.left = newLeft;
} else if (key > node.key) {
const newRight = traceInsert(node.right, key);
node.right = newRight;
}
updateNode(node);
let balance = Math.abs(node.bf);
if (balance > 1 && !steps[steps.length - 1] || steps[steps.length - 1].rotation === null) {
// This is where the rotation logic would ideally be triggered and captured for animation
// Due to complexity limitations, we simulate the state change immediately after calculating the result
}
return node;
}
let previousRoot = JSON.parse(JSON.stringify(root)); // State before insertion
let newRoot = insertRecursive(tempRoot, currentKey);
// --- VISUALIZATION OF ROTATION (Simplified) ---
if (rotationAnimationStep.type) {
console.log(`Animating ${rotationAnimationStep.type}`);
const startTime = performance.now();
function animate(timestamp) {
const elapsed = timestamp - startTime;
let t = Math.min(1, elapsed / 500); // Animation duration: 500ms
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the tree at the animated state (Show code
```html
<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body style="background:#f0f0f0;">
<canvas id=c width=600 height=400></canvas>
<div style="position:absolute;top:10px;left:10px;background:white;padding:10px;">
Height: <span id=h>0</span><br>
Rotations: <span id=r>0</span>
</div>
<script>
class AVLNode {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
this.balance = 0;
this.x = 0;
this.y = 0;
}
}
class AVLTree {
constructor() {this.root = null;}
insert(root, key) {
if(!root) return new AVLNode(key);
if(key < root.key) root.left = this.insert(root.left, key);
else root.right = this.insert(root.right, key);
root.balance = this.height(root.right) - this.height(root.left);
let balanceFactor = root.balance;
if(balanceFactor > 1 && (root.right平衡Factor > 0)) { // RR
const newRoot = this.rotateLeft(root);
canvas.drawText("RR", root.x, root.y-20);
return newRoot;
}
if(balanceFactor < -1 && (root.left.balanceFactor < 0)) { // LL
const newRoot = this.rotateRight(root);
canvas.drawText("LL", root.x, root.y-20);
return newRoot;
}
if(balanceFactor > 1 && root.right.balanceFactor <= 0) { // LR
root.right = this.rotateRight(root.right);
const newRoot = this.rotateLeft(root);
canvas.drawText("LR", root.x, root.y-20);
return newRoot;
}
if(balanceFactor < -1 && root.left.balanceFactor >= 0) { // RL
root.left = this.rotateLeft(root.left);
const newRoot = this.rotateRight(root);
canvas.drawText("RL", root.x, root.y-20);
return newRoot;
}
return root;
}
rotateLeft(z) {
const y = z.right;
z.right = y.left;
y.left = z;
return y;
}
rotateRight(y) {
const z = y.left;
y.left = z.right;
z.right = y;
return z;
}
height(node) {
if(!node) return -1;
return 1 + Math.max(this.height(node.left), this.height(node.right));
}
}
let tree = new AVLTree();
let keys = Array.from({length:20}, () => Math.floor(Math.random()*50)+1);
let idx = 0;
const canvas = {
ctx: document.querySelector('canvas').getContext('2d'),
drawNode(node, x, y) {
this.ctx.beginPath();
this.ctx.arc(x,y,8,0,Math.PI*2);
this.ctx.fillStyle="#fff";
this.ctx.strokeStyle="#333";
this.ctx.stroke();
if(node.balance !== undefined) {
this.ctx.fillStyle = "#666";
const text = `${node.key}\n${node.balance}`;
this.ctx.fillText(text,x-5,y+10);
}
},
drawLine(x1,y1,x2,y2) {
this.ctx.beginPath();
this.ctx.moveTo(x1,y1);
this.ctx.lineTo(x2,y2);
this.ctx.strokeStyle="#333";
this.ctx.stroke();
},
drawText(text, x, y) {
this.ctx.fillStyle="#f00";
this.ctx.fillText(text,x,y);
}
};
const update = () => {
if(idx >= keys.length && (idx === keys.length)) {
idx=0;
tree = new AVLTree();
keys = Array.from({length:20}, () => Math.floor(Math.random()*50)+1);
} else {
const key = keys[idx++];
tree.root = tree.insert(tree.root, key);
document.getElementById('h').textContent = tree.height(tree.root)+1;
document.getElementById('r').textContent = rotationsCount;
}
let nodes = [];
this.ctx.clearRect(0,0,600,400);
function traverse(node, x=300, y=20) {
if(!node) return;
canvas.drawNode(node, x, y);
if(node.left) {
traverse(node.left, x-50*(Math.log2(nodes.length+1)+1), y+40);
canvas.drawLine(x,y,node.left.x||x-50,y+40);
}
if(node.right) {
traverse(node.right, x+50*(Math.log2(nodes.length+1)+1), y+40);
canvas.drawLine(x,y,node.right.x||x+50,y+40);
}
}
traverse(tree.root);
requestAnimationFrame(update);
};
update();
</script></body></html>
```Show code
<think> Okay, let me try to tackle this problem. So the user wants an HTML file that creates an AVL tree with 20 random keys. Each insertion should be animated on a canvas, showing balance factors and rotations when they happen. The rotations need to be labeled and animated smoothly with requestAnimationFrame. Also, track the height and number of rotations. First, I need to structure the HTML file properly. The canvas element must be there, and I'll need JavaScript to handle the AVL operations and animation. Starting with the canvas setup. The HTML will have a canvas tag with id 'treeCanvas'. The script will handle the drawing. Since it's strict on the line count, I need to keep things concise. Next, the AVL tree implementation. The nodes should have properties like key, left, right, height, and balance factor (balance). The insertion function will need to check balance factors after each insertion and perform rotations as necessary. The rotations (LL, RR, LR, RL) have to be handled correctly, and during these rotations, I need to display the type of rotation happening. Animation is a key part. Using requestAnimationFrame, the insertion should proceed step by step. Each insertion is a step where the tree is drawn, and if a rotation is needed, animate that step by step. So maybe each rotation step is a frame in the animation, with some delay between steps. I should represent each node's position on the canvas. The drawing function will need to traverse the tree and position nodes in a way that visually represents the tree structure. Probably using a breadth-first approach to place nodes horizontally and vertically, but maybe just a simple left-right layout for simplicity. The labels for each node must include the key and balance factor. So each node's text should be something like "5 (2)" where 5 is the key and 2 is the balance factor. When a rotation occurs (like LL), I need to display that rotation's name on the canvas. Maybe as a label near the affected node. Also, track the total rotations made. The tree's height and rotation count should be displayed somewhere on the canvas. Maybe at the top or bottom. The steps to follow: 1. Initialize variables: canvas context, random keys array (generate 20 unique numbers, maybe from 1-100?), root node, current step (for insertion order), current rotation (for animation steps), rotation steps (like, how many frames to animate each rotation). 2. Generate keys. Maybe use Math.random() and store in an array, then shuffle it. Wait, but need 20 unique. So perhaps generate 20 unique by checking if they already exist. 3. The main loop will use requestAnimationFrame to draw each frame. The state will determine whether to insert a new node, perform a rotation step, or wait between steps for smoothness. 4. Insertion function will insert the next key into the AVL tree. After insertion, check if the tree is balanced. If not, determine the rotation needed, then perform the rotation in steps, with each step being a frame. 5. For animation, when a rotation is needed, split the rotation into multiple drawing steps. For example, when rotating right (RR), the position of the nodes involved in the rotation could be animated moving from their original positions to the new rotated positions. So each frame of the rotation moves the nodes a bit closer to their new positions until the rotation is complete. But how to track rotation steps. Maybe have a rotationState variable that holds the rotation type and the nodes involved. Each frame, move the nodes a bit until the rotation is done, then proceed to next insertion. Alternatively, for simplicity, maybe just draw the rotation's final state after a brief delay, but the user wants animation. Hmm, the rotation has to be visible. So perhaps during the rotation step, we draw the nodes as they transition. Wait, the problem says "animate the rotation instead of jumping to the result". So the rotation must be drawn step by step, smoothly. So each rotation involves multiple frames. This complicates things. To do that, the code must track the rotation process step by step. For example, when a rotation occurs, we need to animate the nodes moving from their current positions to their positions after the rotation. So maybe each rotation is broken into a few animation steps, like 10 steps, moving the nodes linearly from old to new positions. But how do the nodes' positions change? The drawing code needs to know the current positions based on the rotation step. So during rotation animation, each node's position is interpolated between its original and new position. The AVL tree nodes need to have their positions tracked, which complicates the code. So each node must have x and y coordinates for drawing, and these are updated as the tree structure changes. But during rotation animation, the positions are in transition. Hmm, this is getting a bit complex. Let me outline the approach: Each node has: - key, left, right, height, balance (already standard AVL node properties) - x, y coordinates for drawing. These can be computed during a layout phase each time the tree structure changes. The layout function needs to calculate the x and y positions of all nodes so that they are displayed in a tree structure. This could be done with a BFS traversal to arrange nodes in levels. The root is at center, children are left and right, etc. Each level's nodes spread out horizontally. The problem is that when a rotation happens, the structure changes, so after rotation, a new layout is needed. But during the rotation animation, we need to show the nodes moving from the old positions to the new positions. To animate the rotation, we can have a rotation step counter. Each frame, we calculate how far along we are in the rotation (between 0 and 1), and interpolate the positions of the nodes involved in the rotation from their original positions to their new positions after the rotation. For example, if a rotation is between three nodes (like in a LR rotation), their positions would be moving over the animation steps. But tracking this might require storing the old positions and new positions for each node involved in the rotation, then interpolating between them over the animation frames. Alternatively, during the rotation, the structure is changed immediately, but the animation is just a fade or move effect. Maybe for simplicity, during the rotation phase, the code will compute the new positions, and then over a few steps, interpolate between the old and new positions. Alternatively, maybe the animation is done by redrawing intermediate states between the old and new tree structures. However, this might be complicated without storing intermediate states. Alternatively, to simplify, when a rotation is performed, the code will compute the new tree structure, then animate the positions by moving nodes from their original positions in the old structure to their new positions in the new structure. To do this, the old positions are stored, and the new positions computed after rotation. The animation is just moving the nodes. So steps: 1. Insert the new key, but after insertion, check balance. 2. If unbalanced, perform the rotation, but first save the current positions of the nodes involved in the rotation. 3. During the animation frames, interpolate between old and new positions. Wait, but the rotation changes the structure, so the new positions are determined by the new tree structure. So maybe: - After determining the rotation needed, we compute the new tree structure (perform the rotation), then recalculate all node positions. But the animation would need to show the nodes moving from their old positions (before rotation) to the new positions (after rotation). To do that, we need to save the old positions of the relevant nodes (those involved in the rotation) before the rotation occurs, then during animation steps, interpolate between old and new positions. Therefore
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>AVL Tree Animation</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f4;
}
canvas {
border: 2px solid black;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
</style>
</head>
<body>
<canvas id="avlTree"></canvas>
<script>
const canvas = document.getElementById('avlTree');
const ctx = canvas.getContext('2d');
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
this.height = 1;
}
getHeight() {
return this.height;
}
}
class AVLTree {
constructor() {
this.root = null;
this.rotationCount = 0;
this._randomKeys = [];
for (let i = 1; i <= 20; i++) {
const randomKey = Math.floor(Math.random() * 50) + 1;
if (!this._randomKeys.includes(randomKey)) {
this._randomKeys.push(randomKey);
}
}
}
insert(key) {
this.root = this._insertNode(this.root, key);
}
_insertNode(node, key) {
if (node === null) {
return new Node(key);
}
if (key < node.key) {
node.left = this._insertNode(node.left, key);
} else if (key > node.key) {
node.right = this._insertNode(node.right, key);
}
node.height = 1 + Math.max(this._getHeight(node.left), this._getHeight(node.right));
const balanceFactor = this._getBalanceFactor(node);
if (balanceFactor > 1 && key < node.left.key) {
return this._rotateLeft(node);
} else if (balanceFactor > 1 && key > node.left.key) {
node.left = this._rotateRight(node.left);
return this._rotateLeft(node);
} else if (balanceFactor < -1 && key > node.right.key) {
return this._rotateRight(node);
} else if (balanceFactor < -1 && key < node.right.key) {
node.right = this._rotateLeft(node.right);
return this._rotateRight(node);
}
return node;
}
_getBalanceFactor(node) {
return this._getHeight(node.left) - this._getHeight(node.right);
}
_getHeight(node) {
if (node === null) return 0;
else return node.height;
}
_rotateLeft(node) {
const newRoot = node.right;
node.right = newRoot.left;
newRoot.left = node;
node.height = 1 + Math.max(this._getHeight(node.left), this._getHeight(node.right));
newRoot.height = 1 + Math.max(this._getHeight(newRoot.left), this._getHeight(newRoot.right));
return newRoot;
}
_rotateRight(node) {
const newRoot = node.left;
node.left = newRoot.right;
newRoot.right = node;
node.height = 1 + Math.max(this._getHeight(node.left), this._getHeight(node.right));
newRoot.height = 1 + Math.max(this._getHeight(newRoot.right), this._getHeight(newRoot.left));
return newRoot;
}
drawTree() {
this._drawNode(this.root, canvas.width / 2, canvas.height / 2);
}
_drawNode(node, x, y) {
if (node === null) return;
ctx.beginPath();
ctx.font = '14px Arial';
ctx.fillText(`${node.key} ${this._getBalanceFactor(node)}`, x - 20, y);
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
if (node.left !== null) {
const midYLeft = Math.max(y - canvas.height / 4, node.left.key * 5 + 20);
this._drawNode(node.left, x - node.left.key * 10, midYLeft);
ctx.moveTo(x, y);
ctx.lineTo(x - node.left.key * 10, midYLeft);
}
if (node.right !== null) {
const midYRight = Math.max(y - canvas.height / 4, node.right.key * 5 + 20);
this._drawNode(node.right, x + node.right.key * 10, midYRight);
ctx.moveTo(x, y);
ctx.lineTo(x + node.right.key * 10, midYRight);
}
ctx.stroke();
}
}
const tree = new AVLTree();
function animate() {
requestAnimationFrame(animate);
if (tree._randomKeys.length > 0) {
const key = tree._randomKeys.shift();
tree.insert(key);
ctx.clearRect(0, 0, canvas.width, canvas.height);
tree.drawTree();
}
}
animate();
</script>
</body>
</html>
```Show code
<!doctype html><html><head><meta charset="utf-8"><title>AVL</title><style>
html,body{margin:0;height:100%;background:#f6f3ea;font:15px system-ui;color:#24302f;overflow:hidden}
#hud{position:fixed;left:16px;top:12px;background:#ffffffd9;border:1px solid #c9c2ad;padding:10px 12px;border-radius:6px;box-shadow:0 2px 10px #0001}
canvas{display:block;width:100vw;height:100vh}
</style></head><body><div id="hud"></div><canvas id="c"></canvas><script>
const c=document.getElementById("c"),x=c.getContext("2d"),hud=document.getElementById("hud");
let root,keys,i,rots,msg="",anim=null,last=0;
function N(k){return{key:k,l:null,r:null,h:1,x:0,y:0,px:0,py:0,b:0}}
function h(n){return n?n.h:0}
function upd(n){if(n){n.h=1+Math.max(h(n.l),h(n.r));n.b=h(n.l)-h(n.r)}return n}
function rr(y){let z=y.l,t=z.r;z.r=y;y.l=t;upd(y);return upd(z)}
function lr(y){let z=y.r,t=z.l;z.l=y;y.r=t;upd(y);return upd(z)}
function mark(type){msg=type+" rotation";rots+=type[0]==type[1]?1:2;anim={type,t:0,d:620,before:snap(root)}}
function ins(n,k){
if(!n)return N(k);
if(k<n.key)n.l=ins(n.l,k);else n.r=ins(n.r,k);upd(n);
if(n.b>1&&k<n.l.key){mark("LL");return rr(n)}
if(n.b<-1&&k>n.r.key){mark("RR");return lr(n)}
if(n.b>1&&k>n.l.key){mark("LR");n.l=lr(n.l);return rr(n)}
if(n.b<-1&&k<n.r.key){mark("RL");n.r=rr(n.r);return lr(n)}
return n
}
function layout(n,a,b,d){
if(!n)return;n.x=(a+b)/2;n.y=92+d*74;layout(n.l,a,n.x,d+1);layout(n.r,n.x,b,d+1)
}
function each(n,f){if(n){f(n);each(n.l,f);each(n.r,f)}}
function snap(n){let m={};each(n,o=>m[o.key]={x:o.x,y:o.y});return m}
function keysOf(n,a=[]){return n?(keysOf(n.l,a),a.push(n.key),keysOf(n.r,a),a):a}
function resize(){c.width=innerWidth*devicePixelRatio;c.height=innerHeight*devicePixelRatio;x.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0);layout(root,35,innerWidth-35,0)}
function start(){root=null;i=rots=0;msg="";keys=[];while(keys.length<20){let k=10+Math.floor(Math.random()*90);if(!keys.includes(k))keys.push(k)}resize();setTimeout(step,450)}
function step(){
if(anim||i>=keys.length)return;
root=ins(root,keys[i++]);layout(root,35,innerWidth-35,0);
let after=snap(root),before=anim&&anim.before||after;
each(root,n=>{let p=before[n.key]||after[n.key];n.px=p.x;n.py=p.y});
if(anim)anim.after=after;else anim={type:"insert",t:0,d:360,before:after,after:after};
}
function drawNode(n,p){
if(!n)return;
let bx=p?lerp(n.px,n.x,p):n.x,by=p?lerp(n.py,n.y,p):n.y;
[["l",n.l],["r",n.r]].forEach(q=>{let ch=q[1];if(ch){let cx=p?lerp(ch.px,ch.x,p):ch.x,cy=p?lerp(ch.py,ch.y,p):ch.y;x.beginPath();x.moveTo(bx,by);x.lineTo(cx,cy);x.stroke()}});
drawNode(n.l,p);drawNode(n.r,p);x.beginPath();x.fillStyle="#fff";x.strokeStyle="#42534e";x.lineWidth=2;x.arc(bx,by,22,0,7);x.fill();x.stroke();
x.fillStyle="#182321";x.textAlign="center";x.font="700 14px system-ui";x.fillText(n.key,bx,by-3);x.font="12px system-ui";x.fillText("bf "+n.b,bx,by+13)
}
function lerp(a,b,t){return a+(b-a)*(1-(1-t)*(1-t)*(1-t))}
function frame(ts){
if(!last)last=ts;let p=0;
if(anim){anim.t+=ts-last;p=Math.min(1,anim.t/anim.d);if(p>=1){anim=null;each(root,n=>{n.px=n.x;n.py=n.y});setTimeout(i<20?step:start,i<20?520:1400)}}
last=ts;x.clearRect(0,0,innerWidth,innerHeight);x.strokeStyle="#8b826d";x.lineWidth=2;
drawNode(root,p);hud.innerHTML="Inserted: "+i+"/20 Next: "+(keys[i]??"-")+"<br>Height: "+h(root)+" Rotations: "+rots+"<br>"+(msg||" ");
if(msg){x.fillStyle="#b33422";x.font="700 24px system-ui";x.textAlign="center";x.fillText(msg,innerWidth/2,42);if(!anim)msg=""}
requestAnimationFrame(frame)
}
onresize=resize;start();requestAnimationFrame(frame);
</script></body></html>Show code
<!DOCTYPE html><html><head><meta charset="utf8"><title>AVL</title><style>
body{margin:0;background:#111;color:#eee;font-family:monospace}canvas{display:block;margin:20px auto;background:#181818}
</style></head><body>
<canvas id="c" width="900" height="560"></canvas>
<script>
const cv=document.getElementById('c'),cx=cv.getContext('2d');
let root=null,keys=[],idx=0,rot=0,lastRot='',t=0,phase='anim',pause=0;
let before={},after={},beforeCount=0,afterCount=0,curRot=null;
function h(n){return n?n.h:0}
function bf(n){return n?h(n.l)-h(n.r):0}
function upd(n){n.h=1+Math.max(h(n.l),h(n.r))}
function rotR(y){let a=y.l,b=a.r;a.r=y;y.l=b;upd(y);upd(a);return a}
function rotL(a){let y=a.r,b=y.l;y.l=a;a.r=b;upd(a);upd(y);return y}
function ins(n,k){
if(!n)return{key:k,l:null,r:null,h:1};
if(k<n.key)n.l=ins(n.l,k);else n.r=ins(n.r,k);
upd(n);let b=bf(n);
if(b>1&&k<n.l.key){curRot='LL';return rotR(n)}
if(b<-1&&k>n.r.key){curRot='RR';return rotL(n)}
if(b>1&&k>n.l.key){curRot='LR';n.l=rotL(n.l);return rotR(n)}
if(b<-1&&k<n.r.key){curRot='RL';n.r=rotR(n.r);return rotL(n)}
return n
}
function layout(n,d,arr,map){if(!n)return;layout(n.l,d+1,arr,map);map[n.key]={ix:arr.length,d};arr.push(n);layout(n.r,d+1,arr,map)}
function reset(){
root=null;keys=[];for(let i=0;i<20;i++)keys.push(2+Math.floor(Math.random()*97));
idx=0;rot=0;lastRot='';nextStep()
}
function nextStep(){
if(idx>=20){pause=110;phase='done';return}
curRot=null;
let arrB=[],mapB={};layout(root,0,arrB,mapB);beforeCount=arrB.length;before=mapB;
root=ins(root,keys[idx]);
let arrA=[],mapA={};layout(root,0,arrA,mapA);afterCount=arrA.length;after=mapA;
lastRot=curRot||'none';if(curRot)rot++;
idx++;t=0;phase='anim'
}
function draw(){
cx.fillStyle='#181818';cx.fillRect(0,0,cv.width,cv.height);
let allK=new Set([...Object.keys(before),...Object.keys(after)]);
let posc={};
allK.forEach(k=>{
let pb=before[k],pa=after[k];
let sxB=cv.width/((beforeCount||1)+1),sxA=cv.width/((afterCount||1)+1);
let x0=pb?(pb.ix+1)*sxB:(pa?(pa.ix+1)*sxA:0);
let y0=pb?70+pb.d*65:(pa?-40:0);
let x1=pa?(pa.ix+1)*sxA:x0,y1=pa?70+pa.d*65:y0;
posc[k]={x:x0+(x1-x0)*t,y:y0+(y1-y0)*t}
});
function edges(n){if(!n)return;cx.strokeStyle='#666';
if(n.l){cx.beginPath();cx.moveTo(posc[n.key].x,posc[n.key].y);cx.lineTo(posc[n.l.key].x,posc[n.l.key].y);cx.stroke();edges(n.l)}
if(n.r){cx.beginPath();cx.moveTo(posc[n.key].x,posc[n.key].y);cx.lineTo(posc[n.r.key].x,posc[n.r.key].y);cx.stroke();edges(n.r)}
}
edges(root);
function nodes(n){if(!n)return;let p=posc[n.key];
cx.beginPath();cx.arc(p.x,p.y,19,0,Math.PI*2);cx.fillStyle=n===root?'#4a9':'#39c';cx.fill();cx.strokeStyle='#fff';cx.stroke();
cx.fillStyle='#fff';cx.font='13px monospace';cx.textAlign='center';cx.fillText(n.key,p.x,p.y+4);
cx.font='10px monospace';cx.fillStyle='#ff8';cx.fillText('bf'+bf(n),p.x,p.y-25);
nodes(n.l);nodes(n.r)
}
nodes(root);
cx.fillStyle='#fff';cx.font='16px monospace';cx.textAlign='left';
cx.fillText('Inserted: '+idx+'/20 Height: '+h(root)+' Rotations: '+rot,20,25);
cx.fillText('Last insert: '+(keys[idx-1]??'')+' Rotation: '+lastRot,20,48);
if(lastRot&&lastRot!=='none'&&(phase==='anim'||phase==='hold')){
cx.font='30px monospace';cx.fillStyle='#f66';cx.textAlign='center';cx.fillText(lastRot,cv.width/2,cv.height-15)
}
}
function loop(){
if(phase==='anim'){t+=0.025;if(t>=1){t=1;phase='hold';pause=28}}
else if(phase==='hold'){pause--;if(pause<=0)nextStep()}
else if(phase==='done'){pause--;if(pause<=0)reset()}
draw();
requestAnimationFrame(loop)
}
reset();loop();
</script></body></html>Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AVL Tree</title>
<style>
body { margin: 0; padding: 10px; font-family: monospace; background: #000; color: #0f0; }
canvas { display: block; margin: 10px auto; background: #111; border: 1px solid #0f0; }
div { text-align: center; font-size: 12px; }
</style>
</head>
<body>
<canvas id="c" width="800" height="600"></canvas>
<div id="info"></div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const w = canvas.width, h = canvas.height;
class Node {
constructor(key) { this.key = key; this.left = null; this.right = null; this.h = 1; }
bf() { return (this.left?.h || 0) - (this.right?.h || 0); }
}
let root = null;
let keys = [];
let rotations = 0;
let step = 0;
let time = 0;
function reset() {
root = null;
keys = Array(20).fill(0).map((_, i) => Math.floor(Math.random() * 100));
rotations = 0;
step = 0;
time = 0;
}
reset();
function height(n) { return n ? n.h : 0; }
function insert(node, key) {
if (!node) return new Node(key);
if (key < node.key) node.left = insert(node.left, key);
else if (key > node.key) node.right = insert(node.right, key);
else return node;
node.h = 1 + Math.max(height(node.left), height(node.right));
const bf = node.bf();
if (bf > 1 && key < node.left.key) {
rotations++;
return rotateRight(node);
}
if (bf < -1 && key > node.right.key) {
rotations++;
return rotateLeft(node);
}
if (bf > 1 && key > node.left.key) {
rotations++;
node.left = rotateLeft(node.left);
return rotateRight(node);
}
if (bf < -1 && key < node.right.key) {
rotations++;
node.right = rotateRight(node.right);
return rotateLeft(node);
}
return node;
}
function rotateRight(y) {
const x = y.left;
y.left = x.right;
x.right = y;
y.h = 1 + Math.max(height(y.left), height(y.right));
x.h = 1 + Math.max(height(x.left), height(x.right));
return x;
}
function rotateLeft(x) {
const y = x.right;
x.right = y.left;
y.left = x;
x.h = 1 + Math.max(height(x.left), height(x.right));
y.h = 1 + Math.max(height(y.left), height(y.right));
return y;
}
function animate() {
if (step < keys.length) {
root = insert(root, keys[step]);
step++;
} else if (time > 180) {
reset();
}
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, w, h);
function drawTree(node, x, y, dx) {
if (!node) return;
ctx.fillStyle = '#0f0';
ctx.beginPath();
ctx.arc(x, y, 12, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = '#000';
ctx.font = 'bold 11px monospace';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(node.key, x, y - 3);
ctx.fillStyle = node.bf() > 0 ? '#f00' : '#0f0';
ctx.font = '8px monospace';
ctx.fillText(node.bf(), x, y + 4);
if (node.left) {
ctx.strokeStyle = '#0f0';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x - dx, y + 40);
ctx.stroke();
drawTree(node.left, x - dx, y + 40, dx / 2);
}
if (node.right) {
ctx.strokeStyle = '#0f0';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + dx, y + 40);
ctx.stroke();
drawTree(node.right, x + dx, y + 40, dx / 2);
}
}
if (root) {
drawTree(root, w / 2, 30, 80);
}
ctx.fillStyle = '#0f0';
ctx.font = '12px monospace';
ctx.textAlign = 'left';
ctx.fillText(`Inserted: ${step}/${keys.length} | Height: ${height(root)} | Rotations: ${rotations}`, 20, 550);
time++;
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>Results at a glance— every model in this round with score, response time and status.
Who wrote what
| Model | Blind score | Latency | Status |
|---|---|---|---|
| gemma4:26b | — | — | harness |
| qwen3.5:9b | — | 75.2 s | OK |
| llama3.1:8b | — | 142.7 s | OK |
| mistral-small:24b | — | 220.3 s | OK |
| qwen3:14b | — | 49.8 s | OK |
| deepseek-r1:14b | — | 20.2 s | OK |
| qwen3-coder:30b | — | 39.2 s | OK |
| qwen3:8b | — | — | harness |
| deepseek-r1:32b | — | 145.8 s | OK |
| qwq:32b | — | 145.7 s | OK |
| command-r:35b | — | 149.2 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"><title>AVL Tree — Animated Insertions & Rotations</title>
<style>html,body{margin:0;height:100%;background:#0b0f16;display:flex;align-items:center;justify-content:center}
canvas{background:#0d1117;border:1px solid #1e2836;border-radius:10px;max-width:100%}</style></head><body>
<canvas id="c" width="980" height="560"></canvas>
<script>
const c=document.getElementById('c'),g=c.getContext('2d'),W=980,H=560;
const PX=(x,n)=>W/2+(x-(n-1)/2)*44,PY=d=>140+d*64,L=(a,b,s)=>a+(b-a)*s;
let root=null,rots=0,keys=[],ki=0,q=[],prev=null,cur=null,t0=0,dur=400;
const h=n=>n?n.h:0,bf=n=>n?h(n.l)-h(n.r):0,up=n=>{n.h=1+Math.max(h(n.l),h(n.r))};
function rep(o,w){const p=o.p;w.p=p;if(!p)root=w;else if(p.l===o)p.l=w;else p.r=w}
function rotR(n){const a=n.l;n.l=a.r;if(a.r)a.r.p=n;rep(n,a);a.r=n;n.p=a;up(n);up(a)}
function rotL(n){const a=n.r;n.r=a.l;if(a.l)a.l.p=n;rep(n,a);a.l=n;n.p=a;up(n);up(a)}
function snap(msg,rot,pivot){let i=0,a=[],e=[];
(function w(n,d){if(!n)return;w(n.l,d+1);n._x=i++;n._d=d;w(n.r,d+1)})(root,0);
(function v(n){if(!n)return;a.push({n:n,k:n.k,b:bf(n),x:n._x,y:n._d});
if(n.l){e.push([n,n.l]);v(n.l)}if(n.r){e.push([n,n.r]);v(n.r)}})(root);
q.push({msg:msg,rot:rot||"",pivot:pivot||null,nodes:a,edges:e,th:h(root),rots:rots});}
function insert(k){const nd={k:k,l:null,r:null,p:null,h:1};
if(!root)root=nd;else{let n=root;for(;;){if(k<n.k){if(!n.l){n.l=nd;nd.p=n;break}n=n.l}
else{if(!n.r){n.r=nd;nd.p=n;break}n=n.r}}}
snap("Inserted key "+k,"",nd);
let n=nd.p;
while(n){up(n);const b=bf(n),par=n.p,kk=n.k;
if(b>1){const d=bf(n.l)<0;
if(d){rots++;rotL(n.l);snap("LR: step 1 — left rotation on left child of "+kk,"LR",n)}
rots++;rotR(n);snap((d?"LR: step 2 — right rotation on ":"LL: right rotation on ")+kk,d?"LR":"LL",n);}
else if(b<-1){const d=bf(n.r)>0;
if(d){rots++;rotR(n.r);snap("RL: step 1 — right rotation on right child of "+kk,"RL",n)}
rots++;rotL(n);snap((d?"RL: step 2 — left rotation on ":"RR: left rotation on ")+kk,d?"RL":"RR",n);}
n=par;}}
function reset(){root=null;rots=0;ki=0;q=[];const s=new Set();
while(s.size<20)s.add(1+Math.floor(Math.random()*99));
keys=[...s];cur={msg:"New random AVL tree",rot:"",pivot:null,nodes:[],edges:[],th:0,rots:0};
prev=cur;t0=performance.now();dur=500;}
function step(){if(!q.length&&ki<keys.length)insert(keys[ki++]);
if(q.length){prev=cur;cur=q.shift();t0=performance.now();dur=cur.rot?900:480;}}
function ease(e){return e<.5?4*e*e*e:1-Math.pow(-2*e+2,3)/2}
function bcol(b){return Math.abs(b)>1?"#ff5f6d":Math.abs(b)===1?"#f4c15d":"#5ad6a0"}
function draw(s,now){
g.clearRect(0,0,W,H);g.fillStyle="#0d1117";g.fillRect(0,0,W,H);
const pn=prev.nodes.length,cn=cur.nodes.length,pm=new Map(),pos=new Map();
prev.nodes.forEach(o=>pm.set(o.n,o));
cur.nodes.forEach(o=>{const p=pm.get(o.n),tx=PX(o.x,cn),ty=PY(o.y);
if(p)pos.set(o.n,{x:L(PX(p.x,pn),tx,s),y:L(PY(p.y),ty,s),a:1});
else pos.set(o.n,{x:tx,y:ty,a:s});});
g.lineWidth=2;g.strokeStyle="#33465e";
cur.edges.forEach(p=>{const A=pos.get(p[0]),B=pos.get(p[1]);g.globalAlpha=Math.min(A.a,B.a);
g.beginPath();g.moveTo(A.x,A.y);g.lineTo(B.x,B.y);g.stroke();});
g.globalAlpha=1;
cur.nodes.forEach(o=>{const p=pos.get(o.n);g.globalAlpha=.25+.75*p.a;
g.beginPath();g.arc(p.x,p.y,18,0,7);g.fillStyle="#16202e";g.fill();
g.lineWidth=2.5;g.strokeStyle=bcol(o.b);g.stroke();
if(cur.pivot===o.n){g.beginPath();g.arc(p.x,p.y,18+4+2*Math.sin(now/110),0,7);
g.lineWidth=2;g.strokeStyle=cur.rot?"#ffd166":"#6ea8ff";g.stroke();}
g.fillStyle="#e8eef7";g.font="bold 15px system-ui,sans-serif";g.textAlign="center";
g.textBaseline="middle";g.fillText(o.k,p.x,p.y);
g.font="bold 11px system-ui,sans-serif";g.fillStyle=bcol(o.b);g.textAlign="left";
g.fillText((o.b>0?"+":"")+o.b,p.x+19,p.y-15);g.globalAlpha=1;});
g.textAlign="left";g.textBaseline="alphabetic";
g.fillStyle="#e8eef7";g.font="bold 19px system-ui,sans-serif";
g.fillText("AVL Tree — self-balancing insertion",24,36);
g.font="13px system-ui,sans-serif";g.fillStyle="#8fa3bb";
g.fillText("node = key, small colored value = balance factor (height(left) − height(right))",24,58);
g.font="bold 15px system-ui,sans-serif";g.fillStyle="#6ea8ff";
g.fillText("Height: "+cur.th,24,88);
g.fillStyle="#ffd166";g.fillText("Rotations: "+cur.rots,140,88);
g.fillStyle="#5ad6a0";g.fillText("Inserted: "+cur.nodes.length+" / 20",286,88);
g.textAlign="right";g.font="13px system-ui,sans-serif";g.fillStyle="#9db1c9";
g.fillText(cur.msg,W-24,88);
if(cur.rot){const a=.35+.65*Math.abs(Math.sin(now/220));g.globalAlpha=a;
g.textAlign="center";g.font="bold 40px system-ui,sans-serif";g.fillStyle="#ffd166";
g.fillText(cur.rot+" ROTATION",W/2,H-34);g.globalAlpha=1;}
else if(!q.length&&ki>=keys.length){g.textAlign="center";g.font="bold 17px system-ui,sans-serif";
g.fillStyle="#5ad6a0";g.fillText("balanced — restarting with a fresh random tree…",W/2,H-34);}}
function frame(now){const e=Math.min(1,(now-t0)/dur);draw(ease(e),now);
if(e>=1){if(!q.length&&ki>=keys.length){if(now-t0>dur+2200)reset();}else step();}
requestAnimationFrame(frame);}
reset();requestAnimationFrame(frame);
</script></body></html>