Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions AI-Car-Racer/buttonResponse.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ function pauseGame(){
btn.classList.remove('start-cta');
}
window.__firstStart = false;

// Gentle first-time onboarding hint (only shown once)
if (!localStorage.getItem('seenFirstStartHint')) {
localStorage.setItem('seenFirstStartHint', '1');
setTimeout(() => {
try {
const hint = document.createElement('div');
hint.style.cssText = 'position:fixed;bottom:12px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.78);color:#ddd;padding:6px 14px;border-radius:4px;font-size:12px;z-index:9999;white-space:nowrap;';
hint.innerHTML = 'Demo running — cars are evolving. Use “✏️ Customize Track” or 🧪 Experiments for more options.';
document.body.appendChild(hint);
setTimeout(() => { if (hint && hint.parentNode) hint.parentNode.removeChild(hint); }, 7000);
} catch (_) {}
}, 1400);
}
// Halt / resume the worker's AI step loop too. Without this, sim-worker
// would keep burning CPU while the user has paused — and on resume the
// accumulator would stampede a huge backlog of physics steps at once.
Expand Down
15 changes: 9 additions & 6 deletions AI-Car-Racer/car.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,14 @@ class Car{
// distance cue; see docs/plan/ruvector-proof/arch-a1/PROOF.md.
const cpList = checkPointList;
let lf = 0, lr = 0;
if (cpList && cpList.length){

// When pureLocalSensors is active, we deliberately give the brain
// ZERO information about where the next checkpoint is.
// This is the "embodied local signals only" mode for comparison.
// Guard works in both main thread (window) and Web Worker (self).
const isPureLocal = (typeof window !== 'undefined' && window.pureLocalSensors) ||
(typeof self !== 'undefined' && self.pureLocalSensors);
if (!isPureLocal && cpList && cpList.length){
const passed = this.checkPointsPassed;
const nextIdx = passed.length === 0
? 0
Expand All @@ -139,11 +146,7 @@ class Car{
const s = Math.sin(this.angle), c = Math.cos(this.angle);
const lfRaw = dx * s + dy * c;
const lrRaw = dx * c - dy * s;
// Canvas diagonal as track-invariant scale. `road` is a
// global populated by main.js (or handleInit in the
// worker); both paths set `right` and `bottom` = canvas
// dims. Fallback constant guards the very-early frame
// before handleInit lands.
// Canvas diagonal as track-invariant scale.
const W = (typeof road !== 'undefined' && road && road.right) ? (road.right - road.left) : 3200;
const H = (typeof road !== 'undefined' && road && road.bottom) ? (road.bottom - road.top) : 1800;
const D = Math.hypot(W, H);
Expand Down
4 changes: 2 additions & 2 deletions AI-Car-Racer/eli15/chapters/lineage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ export default {
oneLiner: 'parentIds + getLineage() reconstruct a brain\'s family tree on demand.',
body: [
'<p>When a generation ends, the best car\'s brain gets archived — but we don\'t just',
'save the 92 weights. We also save <strong>which brains it came from</strong>. Each',
'save the 244 weights. We also save <strong>which brains it came from</strong>. Each',
'archive entry carries a <code>parentIds: string[]</code>: the ids of the seeds the GA',
'warm-started this batch from. Those parents have their <em>own</em> parents stored',
'alongside their weights. String enough of those together and you have a <strong>family',
'tree</strong> of neural networks.</p>',
'<p><code>ruvectorBridge.js:200</code> exposes <code>getLineage(id, maxDepth = 6)</code>.',
'<p><code>ruvectorBridge.js:1338</code> exposes <code>getLineage(id, maxDepth = 6)</code>.',
'Starting from any brain id, it walks <code>parentIds</code> backwards. When a brain has',
'multiple parents (the batch was seeded from multiple retrievals), it picks the parent',
'with the highest fitness — <em>"the line of descent we credit this genome to"</em>. The',
Expand Down
30 changes: 15 additions & 15 deletions AI-Car-Racer/eli15/chapters/neural-network.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
// eli15/chapters/neural-network.js
// The 92-weight 6→8→4 feed-forward network that decides W/A/S/D.
// The 244-weight 10→16→4 feed-forward network that decides W/A/S/D.
export default {
id: 'neural-network',
title: 'A brain made of 92 numbers',
oneLiner: 'Six sensor inputs → eight hidden neurons → four pedal/steer outputs.',
title: 'A brain made of 244 numbers',
oneLiner: 'Ten sensor inputs → sixteen hidden neurons → four pedal/steer outputs.',
body: [
'<p>Every car carries a tiny neural network. It is almost comically small:',
'<strong>6 inputs → 8 hidden neurons → 4 outputs</strong>. See it in',
'<strong>10 inputs → 16 hidden neurons → 4 outputs</strong>. See it in',
'<code>network.js</code> (the <code>Level</code> and <code>NeuralNetwork</code> classes) and',
'<code>car.js:37-38</code> where the car wires it up.</p>',
'<code>car.js:41-42</code> where the car wires it up.</p>',
'<p>"A neuron" here is just a weighted sum with a threshold. It multiplies each input',
'by a <strong>weight</strong>, adds them all up, and compares the total to a <strong>bias</strong>.',
'If the sum beats the bias, the neuron fires a 1. Otherwise a 0. That\'s it — no fancy',
'activation function, no backprop, no gradients. The whole network can be serialised',
'as a flat <code>Float32Array(92)</code> — see <code>brainCodec.js:2</code>:',
'<code>FLAT_LENGTH = 92</code>. Where does 92 come from? For each layer with <em>in</em>',
'as a flat <code>Float32Array(244)</code> — see <code>brainCodec.js:5</code>:',
'<code>FLAT_LENGTH = 244</code>. Where does 244 come from? For each layer with <em>in</em>',
'inputs and <em>out</em> outputs you need <em>in × out</em> weights plus <em>out</em> biases:',
'<code>(6×8 + 8) + (8×4 + 4) = 56 + 36 = 92</code>.</p>',
'<code>(10×16 + 16) + (16×4 + 4) = 176 + 68 = 244</code>.</p>',
'<p>The four outputs are Boolean: <em>forward</em>, <em>reverse</em>, <em>left</em>, <em>right</em>.',
'The car presses the pedals whose neuron fired a 1. Combine that with the 5 rays',
'(plus 1 bias) from <code>sensors</code> and you get the whole perception-to-action pipeline',
'The car presses the pedals whose neuron fired a 1. Combine that with the 7 rays + 3 features',
' from <code>sensors</code> + car and you get the whole perception-to-action pipeline',
'in ~50 lines of JS.</p>',
'<p>No learning happens inside the brain itself — the brain is just a lookup function.',
'Learning happens <em>across generations</em>, by the genetic algorithm tweaking those 92',
'Learning happens <em>across generations</em>, by the genetic algorithm tweaking those 244',
'numbers.</p>',
'<h3>Try it yourself</h3>',
'<ul><li>In phase 4, watch the four-box display in the right panel (the',
Expand All @@ -31,7 +31,7 @@ export default {
].join('\n'),
diagram: [
'<pre class="eli15-ascii">',
' INPUTS (6) HIDDEN (8) OUTPUTS (4)',
' INPUTS (10) HIDDEN (16) OUTPUTS (4)',
'',
' ray 0 ●───┐',
' ray 1 ●───┼─── ●─┐',
Expand All @@ -44,9 +44,9 @@ export default {
' ●',
'',
' weights + biases:',
' layer 0 → 1 : 6×8 + 8 = 56',
' layer 1 → 2 : 8×4 + 4 = 36',
' total = 92 floats',
' layer 0 → 1 : 10×16 + 16 = 176',
' layer 1 → 2 : 16×4 + 4 = 68',
' total = 244 floats',
'</pre>',
].join('\n'),
related: [
Expand Down
30 changes: 30 additions & 0 deletions AI-Car-Racer/eli15/chapters/pure-local-experiment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Short chapter explaining the "Pure Local Signals" comparison experiment.
// Registered in eli15/index.js and linked from the Experiments panel.
export default {
id: 'pure-local-experiment',
title: 'Pure local signals vs track hints',
oneLiner: 'What happens when we remove the "next checkpoint" features and force the brain to drive from raw sensors only?',
body: [
'<p>This experiment lets you directly compare two versions of the same brain:</p>',
'<ul>',
'<li><strong>Normal mode</strong> — the network receives the usual 10 inputs: 7 ray readings + speed + two extra "track orientation" features (lf + lr). These two features quietly tell the brain the direction and distance to the next checkpoint in its own local frame.</li>',
'<li><strong>Pure local mode</strong> (enable with the 🧪 Experiments toggle or <code>?pure-local=1</code>) — lf and lr are forced to zero. The brain only sees the raw rays + its own speed. No explicit hint about where the next gate is.</li>',
'</ul>',
'<p>The goal is to understand how much the current system relies on those hidden "map-like" signals versus learning to drive from truly local, embodied perception — the kind a real car or robot would have.</p>',
'<h3>Why this matters on hard tracks like Triangle</h3>',
'<p>On easy tracks the extra features are convenient but not essential. On the Triangle the difference becomes dramatic because the critical 180° turn requires anticipation. A brain that only reacts to what its rays see <em>right now</em> often discovers the wall too late.</p>',
'<p>This mode is deliberately not "better" — it is a diagnostic tool. Use it (ideally with <code>?rv=0</code> for a clean GA baseline) to feel how much the network depends on the extra signals versus raw sensor data.</p>',
'<h3>Try it yourself</h3>',
'<ul>',
'<li>Toggle "Pure local sensors (no lf/lr)" in the 🧪 Experiments panel.</li>',
'<li>Watch the persistent <code>👁️ PURE LOCAL</code> badge at the top.</li>',
'<li>Restart training and compare behavior (and the brain input bars) against a normal run.</li>',
'<li>Look at the brain visualization: the last two input bars will stay near zero.</li>',
'</ul>',
].join('\n'),
related: [
'why-cars-crash',
'sensors',
'neural-network',
],
};
4 changes: 2 additions & 2 deletions AI-Car-Racer/eli15/chapters/sensors.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default {
'rays — wide enough to hide an apex corner until the car is already on top of it.',
'Bumping to 7 closes those gaps to ~17°. Combined with speed and two track-orientation',
'features, that\'s the <strong>10 inputs</strong> feeding the first hidden layer.',
'Look at <code>car.js:42</code> and you\'ll see it: <code>new NeuralNetwork([sensor.rayCount+3, 8, 4])</code>.</p>',
'Look at <code>car.js:42</code> and you\'ll see it: <code>new NeuralNetwork([sensor.rayCount+3, 16, 4])</code>.</p>',
'<p>The rays are drawn on the training canvas as faint lines sticking out of each car.',
'Where a ray hits a wall, there\'s a tiny dot. That dot\'s distance is what the brain sees.</p>',
'<h3>Try it yourself</h3>',
Expand All @@ -43,7 +43,7 @@ export default {
' 0 = wall touching nose',
' 1 = clear for 400px',
'',
' 5 rays + 1 bias input → feeds NN layer 0 (6 neurons)',
' 7 rays + 3 features → feeds NN layer 0 (10 neurons)',
'</pre>',
].join('\n'),
related: [
Expand Down
4 changes: 2 additions & 2 deletions AI-Car-Racer/eli15/chapters/what-is-this-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export default {
oneLiner: 'A browser-based genetic-algorithm racer with a vector-memory bridge.',
body: [
'<p>This is a car racing game where nobody writes the driving logic by hand. Each',
'car has a tiny neural network — 92 floating-point numbers, arranged as a',
'<code>68 → 4</code> topology — that reads from sensors (rays poking out of the car)',
'car has a tiny neural network — 244 floating-point numbers, arranged as a',
'<code>1016 → 4</code> topology — that reads from sensors (rays poking out of the car)',
'and decides which pedals and steering to press. At the start of training the',
'networks are <em>random</em>, so the cars drive like drunk toddlers. A few make it a',
'little further than the others. Those are the "winners" of the generation.</p>',
Expand Down
10 changes: 5 additions & 5 deletions AI-Car-Racer/eli15/chapters/why-cars-crash.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export default {
'"the AI isn\'t smart enough yet."</p>',

'<h3>1. The brain is a frozen reflex table, not an agent</h3>',
'<p>Every car\'s "brain" is the 92-number neural net from the',
'<code>neural-network</code> chapter. Once a car is born, those 92 numbers are <strong>locked</strong>',
'<p>Every car\'s "brain" is the 244-number neural net from the',
'<code>neural-network</code> chapter. Once a car is born, those 244 numbers are <strong>locked</strong>',
'for its entire life. No learning, no memory, no "oh I bumped that wall, I\'ll try something',
'different next time." Each frame it does the same thing: feed sensor readings in the top,',
'read four booleans out the bottom. It\'s a <em>lookup function</em> dressed up as a driver.',
Expand All @@ -21,7 +21,7 @@ export default {

'<h3>2. Generation 0 is a bag of dice rolls</h3>',
'<p>When a fresh population is born, every weight and bias is a uniform random number',
'in [-1, 1] — see <code>network.js:68</code>: <code>w[k] = Math.random()*2-1</code>.',
'in [-1, 1] — see <code>network.js:73</code>: <code>w[k] = Math.random()*2-1</code>.',
'That means ~1 in 16 gen-0 brains happen to roll a bias pattern that makes the',
'<em>forward</em> output neuron fire regardless of what the sensors say. Those cars are',
'"always-forward zombies" — they drive perfectly straight until the first corner',
Expand All @@ -32,9 +32,9 @@ export default {

'<h3>3. The first half-decent car becomes the ancestor of everyone</h3>',
'<p>Fitness is brutally simple: <code>checkPointsCount + laps × cpLen</code>',
'(<code>sim-worker.js:296</code>). Whichever car grabs the first checkpoint <em>first</em>',
'(<code>sim-worker.js:331</code>). Whichever car grabs the first checkpoint <em>first</em>',
'wins the generation, even if it immediately crashes one metre later. The GA then copies',
'that winner\'s 92 weights, sprinkles a bit of mutation noise, and the whole next',
'that winner\'s 244 weights, sprinkles a bit of mutation noise, and the whole next',
'generation is its near-siblings. After ~10 generations of this, the population has',
'<strong>locked in</strong> on whatever reflex the first lucky car happened to have —',
'even if that reflex is "hug the left wall at 0.3 × max speed." Escaping that local',
Expand Down
11 changes: 8 additions & 3 deletions AI-Car-Racer/eli15/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,25 @@
loader: function () { return import('./chapters/what-is-this-project.js'); },
},
'sensors': {
title: 'The car\'s eyes are five invisible rays',
title: 'The car\'s eyes are seven invisible rays',
oneLiner: 'Ray-cast sensors feed a number per ray into the neural network.',
loader: function () { return import('./chapters/sensors.js'); },
},
'neural-network': {
title: 'A brain made of 92 numbers',
oneLiner: 'Six sensor inputs → eight hidden neurons → four pedal/steer outputs.',
title: 'A brain made of 244 numbers',
oneLiner: 'Ten sensor inputs → sixteen hidden neurons → four pedal/steer outputs.',
loader: function () { return import('./chapters/neural-network.js'); },
},
'why-cars-crash': {
title: 'Why your car keeps driving into walls',
oneLiner: 'Four reasons: frozen reflexes, random gen-0, elite lock-in, and physics.',
loader: function () { return import('./chapters/why-cars-crash.js'); },
},
'pure-local-experiment': {
title: 'Pure local signals vs track hints',
oneLiner: 'What happens when we remove the "next checkpoint" features and force the brain to drive from raw sensors only?',
loader: function () { return import('./chapters/pure-local-experiment.js'); },
},
'genetic-algorithm': {
title: 'Breeding brains instead of training them',
oneLiner: 'Copy the winners, nudge their weights, discard the losers. Repeat.',
Expand Down
4 changes: 2 additions & 2 deletions AI-Car-Racer/eli15/tour.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
{
id: 'sensors',
anchor: '#inputCanvas',
rationale: 'The five rays feeding the brain — this canvas shows what the car "sees".',
rationale: 'The seven rays feeding the brain — this canvas shows what the car "sees".',
},
{
id: 'neural-network',
anchor: '#inputCanvas',
rationale: '92 numbers turning sensor rays into pedal + steer outputs.',
rationale: '244 numbers turning sensor rays into pedal + steer outputs.',
},
{
id: 'genetic-algorithm',
Expand Down
Loading
Loading