awacke1 commited on
Commit
16fe8d4
·
verified ·
1 Parent(s): 7cc37fc

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1571 -19
index.html CHANGED
@@ -1,19 +1,1571 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Enhanced Road-Following AI Evolution Simulator</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ overflow: hidden;
11
+ font-family: Arial, sans-serif;
12
+ background: #000;
13
+ }
14
+ #ui {
15
+ position: absolute;
16
+ top: 10px;
17
+ left: 10px;
18
+ color: white;
19
+ background-color: rgba(0,0,0,0.9);
20
+ padding: 15px;
21
+ border-radius: 8px;
22
+ z-index: 100;
23
+ font-size: 14px;
24
+ min-width: 200px;
25
+ }
26
+ #controls {
27
+ position: absolute;
28
+ top: 10px;
29
+ right: 10px;
30
+ color: white;
31
+ background-color: rgba(0,0,0,0.9);
32
+ padding: 15px;
33
+ border-radius: 8px;
34
+ z-index: 100;
35
+ }
36
+ button {
37
+ background-color: #4CAF50;
38
+ border: none;
39
+ color: white;
40
+ padding: 8px 16px;
41
+ margin: 5px;
42
+ cursor: pointer;
43
+ border-radius: 4px;
44
+ font-size: 12px;
45
+ }
46
+ button:hover {
47
+ background-color: #45a049;
48
+ }
49
+ #stats {
50
+ position: absolute;
51
+ bottom: 10px;
52
+ left: 10px;
53
+ color: white;
54
+ background-color: rgba(0,0,0,0.9);
55
+ padding: 15px;
56
+ border-radius: 8px;
57
+ z-index: 100;
58
+ font-size: 12px;
59
+ min-width: 200px;
60
+ }
61
+ #roadStats {
62
+ position: absolute;
63
+ bottom: 10px;
64
+ right: 10px;
65
+ color: white;
66
+ background-color: rgba(0,0,0,0.9);
67
+ padding: 15px;
68
+ border-radius: 8px;
69
+ z-index: 100;
70
+ font-size: 12px;
71
+ min-width: 180px;
72
+ }
73
+ #behaviorStats {
74
+ position: absolute;
75
+ top: 50%;
76
+ right: 10px;
77
+ transform: translateY(-50%);
78
+ color: white;
79
+ background-color: rgba(0,0,0,0.9);
80
+ padding: 15px;
81
+ border-radius: 8px;
82
+ z-index: 100;
83
+ font-size: 12px;
84
+ min-width: 180px;
85
+ }
86
+ .highlight { color: #ffcc00; font-weight: bold; }
87
+ .success { color: #00ff00; font-weight: bold; }
88
+ .road { color: #00aaff; }
89
+ .traveling { color: #ff8800; }
90
+ .stopped { color: #ff00ff; font-weight: bold; }
91
+ .navigating { color: #00ffff; }
92
+ .following { color: #88ff88; }
93
+ .progress-bar {
94
+ width: 100%;
95
+ height: 10px;
96
+ background-color: #333;
97
+ border-radius: 5px;
98
+ overflow: hidden;
99
+ margin: 5px 0;
100
+ }
101
+ .progress-fill {
102
+ height: 100%;
103
+ background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1);
104
+ transition: width 0.3s ease;
105
+ }
106
+ </style>
107
+ </head>
108
+ <body>
109
+ <div id="ui">
110
+ <div class="highlight">Road-Following AI Evolution</div>
111
+ <div>Epoch: <span id="epoch">1</span></div>
112
+ <div>Time: <span id="epochTime">60</span>s</div>
113
+ <div class="progress-bar"><div class="progress-fill" id="timeProgress"></div></div>
114
+ <div>Population: <span id="population">100</span></div>
115
+ <div>Best Fitness: <span id="bestFitness">0</span></div>
116
+ <div>Road Mastery: <span id="roadMastery">0</span>%</div>
117
+ <div>Navigation IQ: <span id="navigationIQ">50</span></div>
118
+ </div>
119
+
120
+ <div id="controls">
121
+ <button id="pauseBtn">Pause</button>
122
+ <button id="resetBtn">Reset</button>
123
+ <button id="speedBtn">Speed: 1x</button>
124
+ <button id="viewBtn">View: Follow</button>
125
+ <button id="pathBtn">Paths: ON</button>
126
+ <button id="debugBtn">Debug: OFF</button>
127
+ </div>
128
+
129
+ <div id="stats">
130
+ <div><span class="highlight">Performance Metrics:</span></div>
131
+ <div>Destinations Reached: <span id="destinationsReached">0</span></div>
132
+ <div>Road Usage: <span id="roadUsage">0</span>%</div>
133
+ <div>Formation Quality: <span id="formationQuality">0</span>%</div>
134
+ <div>Path Efficiency: <span id="pathEfficiency">0</span>%</div>
135
+ <div>Stop Compliance: <span id="stopCompliance">0</span>%</div>
136
+ <div>Traffic Violations: <span id="trafficViolations">0</span></div>
137
+ </div>
138
+
139
+ <div id="roadStats">
140
+ <div><span class="highlight">Road Behavior:</span></div>
141
+ <div><span class="road">On Roads:</span> <span id="onRoadCount">0</span></div>
142
+ <div><span class="traveling">Traveling:</span> <span id="travelingCount">0</span></div>
143
+ <div><span class="stopped">Stopped:</span> <span id="stoppedCount">0</span></div>
144
+ <div><span class="navigating">Navigating:</span> <span id="navigatingCount">0</span></div>
145
+ <div>Single File Lines: <span id="singleFileCount">0</span></div>
146
+ <div>Avg Line Length: <span id="avgLineLength">0</span></div>
147
+ <div>Traffic Flow: <span id="trafficFlow">0</span>%</div>
148
+ </div>
149
+
150
+ <div id="behaviorStats">
151
+ <div><span class="highlight">AI Behaviors:</span></div>
152
+ <div>Path Planning: <span id="pathPlanning">0</span>%</div>
153
+ <div>Road Following: <span id="roadFollowing">0</span>%</div>
154
+ <div>Formation Control: <span id="formationControl">0</span>%</div>
155
+ <div>Navigation Skills: <span id="navigationSkills">0</span>%</div>
156
+ <div style="margin-top: 10px;"><span class="highlight">Learning Progress:</span></div>
157
+ <div>Exploration: <span id="explorationRate">0</span>%</div>
158
+ <div>Adaptation: <span id="adaptationRate">0</span>%</div>
159
+ <div>Specialization: <span id="specializationRate">0</span>%</div>
160
+ </div>
161
+
162
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
163
+ <script>
164
+ // Global variables
165
+ let scene, camera, renderer, clock;
166
+ let world = {
167
+ roads: [],
168
+ intersections: [],
169
+ buildings: [],
170
+ pathNetwork: new Map(),
171
+ roadSegments: []
172
+ };
173
+
174
+ // Evolution system
175
+ let epoch = 1;
176
+ let epochTime = 90; // Longer epochs for complex behaviors
177
+ let timeLeft = 90;
178
+ let population = [];
179
+ let populationSize = 80; // Smaller population for more complex AI
180
+ let bestFitness = 0;
181
+ let paused = false;
182
+ let speedMultiplier = 1;
183
+ let cameraMode = 'follow';
184
+ let showPaths = true;
185
+ let debugMode = false;
186
+
187
+ // Road network constants
188
+ const ROAD_WIDTH = 12;
189
+ const ROAD_SPACING = 120;
190
+ const INTERSECTION_SIZE = 15;
191
+ const LANE_WIDTH = 6;
192
+ const BUILDING_COUNT = 16;
193
+
194
+ // Enhanced Neural Network for Road Navigation
195
+ class RoadNavigationBrain {
196
+ constructor() {
197
+ this.inputSize = 32; // Expanded for road navigation
198
+ this.hiddenLayers = [48, 36, 24]; // Deeper network
199
+ this.outputSize = 12; // More nuanced control
200
+
201
+ this.weights = [];
202
+ this.biases = [];
203
+ this.memory = new Array(8).fill(0); // Road memory
204
+ this.pathMemory = []; // Remember planned paths
205
+
206
+ this.initializeNetwork();
207
+
208
+ // Specialized road behaviors
209
+ this.roadFollowingStrength = Math.random();
210
+ this.formationPreference = Math.random();
211
+ this.navigationSkill = Math.random();
212
+ this.pathPlanningAbility = Math.random();
213
+ this.stopDiscipline = Math.random();
214
+ }
215
+
216
+ initializeNetwork() {
217
+ let prevSize = this.inputSize + this.memory.length;
218
+ for (let i = 0; i < this.hiddenLayers.length; i++) {
219
+ this.weights.push(this.randomMatrix(prevSize, this.hiddenLayers[i]));
220
+ this.biases.push(this.randomArray(this.hiddenLayers[i]));
221
+ prevSize = this.hiddenLayers[i];
222
+ }
223
+ this.weights.push(this.randomMatrix(prevSize, this.outputSize));
224
+ this.biases.push(this.randomArray(this.outputSize));
225
+ }
226
+
227
+ randomMatrix(rows, cols) {
228
+ return Array(rows).fill().map(() =>
229
+ Array(cols).fill().map(() => (Math.random() - 0.5) * 2)
230
+ );
231
+ }
232
+
233
+ randomArray(size) {
234
+ return Array(size).fill().map(() => (Math.random() - 0.5) * 2);
235
+ }
236
+
237
+ activate(inputs) {
238
+ let currentInput = [...inputs, ...this.memory];
239
+
240
+ for (let layer = 0; layer < this.hiddenLayers.length; layer++) {
241
+ currentInput = this.forwardLayer(currentInput, this.weights[layer], this.biases[layer]);
242
+ }
243
+
244
+ const outputs = this.forwardLayer(currentInput,
245
+ this.weights[this.weights.length - 1],
246
+ this.biases[this.biases.length - 1]);
247
+
248
+ // Update memory with important road information
249
+ this.updateMemory(inputs);
250
+
251
+ return outputs;
252
+ }
253
+
254
+ forwardLayer(inputs, weights, biases) {
255
+ const outputs = new Array(weights[0].length).fill(0);
256
+
257
+ for (let i = 0; i < outputs.length; i++) {
258
+ for (let j = 0; j < inputs.length; j++) {
259
+ outputs[i] += inputs[j] * weights[j][i];
260
+ }
261
+ outputs[i] += biases[i];
262
+ outputs[i] = this.activationFunction(outputs[i]);
263
+ }
264
+
265
+ return outputs;
266
+ }
267
+
268
+ activationFunction(x) {
269
+ return Math.tanh(Math.max(-10, Math.min(10, x)));
270
+ }
271
+
272
+ updateMemory(inputs) {
273
+ // Store road-following information
274
+ const roadInfo = inputs.slice(16, 20); // Road detection inputs
275
+ this.memory = [...roadInfo, ...this.memory.slice(0, 4)];
276
+ }
277
+
278
+ mutate(rate = 0.1) {
279
+ this.weights.forEach(matrix => {
280
+ for (let i = 0; i < matrix.length; i++) {
281
+ for (let j = 0; j < matrix[i].length; j++) {
282
+ if (Math.random() < rate) {
283
+ matrix[i][j] += (Math.random() - 0.5) * 0.5;
284
+ matrix[i][j] = Math.max(-3, Math.min(3, matrix[i][j]));
285
+ }
286
+ }
287
+ }
288
+ });
289
+
290
+ this.biases.forEach(bias => {
291
+ for (let i = 0; i < bias.length; i++) {
292
+ if (Math.random() < rate) {
293
+ bias[i] += (Math.random() - 0.5) * 0.5;
294
+ bias[i] = Math.max(-3, Math.min(3, bias[i]));
295
+ }
296
+ }
297
+ });
298
+
299
+ // Mutate behavioral traits
300
+ if (Math.random() < rate) {
301
+ this.roadFollowingStrength += (Math.random() - 0.5) * 0.2;
302
+ this.roadFollowingStrength = Math.max(0, Math.min(1, this.roadFollowingStrength));
303
+ }
304
+ if (Math.random() < rate) {
305
+ this.formationPreference += (Math.random() - 0.5) * 0.2;
306
+ this.formationPreference = Math.max(0, Math.min(1, this.formationPreference));
307
+ }
308
+ }
309
+
310
+ copy() {
311
+ const newBrain = new RoadNavigationBrain();
312
+ newBrain.weights = this.weights.map(matrix => matrix.map(row => [...row]));
313
+ newBrain.biases = this.biases.map(bias => [...bias]);
314
+ newBrain.memory = [...this.memory];
315
+ newBrain.roadFollowingStrength = this.roadFollowingStrength;
316
+ newBrain.formationPreference = this.formationPreference;
317
+ newBrain.navigationSkill = this.navigationSkill;
318
+ newBrain.pathPlanningAbility = this.pathPlanningAbility;
319
+ newBrain.stopDiscipline = this.stopDiscipline;
320
+ return newBrain;
321
+ }
322
+
323
+ crossover(other) {
324
+ const child = new RoadNavigationBrain();
325
+
326
+ for (let layer = 0; layer < this.weights.length; layer++) {
327
+ for (let i = 0; i < this.weights[layer].length; i++) {
328
+ for (let j = 0; j < this.weights[layer][i].length; j++) {
329
+ child.weights[layer][i][j] = Math.random() < 0.5 ?
330
+ this.weights[layer][i][j] : other.weights[layer][i][j];
331
+ }
332
+ }
333
+
334
+ for (let i = 0; i < this.biases[layer].length; i++) {
335
+ child.biases[layer][i] = Math.random() < 0.5 ?
336
+ this.biases[layer][i] : other.biases[layer][i];
337
+ }
338
+ }
339
+
340
+ // Blend behavioral traits
341
+ child.roadFollowingStrength = (this.roadFollowingStrength + other.roadFollowingStrength) / 2;
342
+ child.formationPreference = (this.formationPreference + other.formationPreference) / 2;
343
+ child.navigationSkill = (this.navigationSkill + other.navigationSkill) / 2;
344
+ child.pathPlanningAbility = (this.pathPlanningAbility + other.pathPlanningAbility) / 2;
345
+ child.stopDiscipline = (this.stopDiscipline + other.stopDiscipline) / 2;
346
+
347
+ return child;
348
+ }
349
+ }
350
+
351
+ // Enhanced AI Car with Road Navigation
352
+ class RoadNavigationCar {
353
+ constructor(x = 0, z = 0) {
354
+ this.brain = new RoadNavigationBrain();
355
+ this.mesh = this.createCarMesh();
356
+ this.mesh.position.set(x, 1, z);
357
+
358
+ // Movement properties
359
+ this.velocity = new THREE.Vector3(0, 0, 0);
360
+ this.acceleration = new THREE.Vector3();
361
+ this.maxSpeed = 20;
362
+ this.minSpeed = 2;
363
+ this.targetSpeed = 15;
364
+
365
+ // Road navigation state
366
+ this.currentRoad = null;
367
+ this.roadPosition = 0.5; // Position on road (0-1)
368
+ this.roadDirection = new THREE.Vector3(1, 0, 0);
369
+ this.targetDestination = null;
370
+ this.currentPath = [];
371
+ this.pathIndex = 0;
372
+ this.state = 'seeking'; // seeking, traveling, stopped, following
373
+
374
+ // Formation and following
375
+ this.leader = null;
376
+ this.followers = [];
377
+ this.formationPosition = 0;
378
+ this.targetFollowingDistance = 8;
379
+
380
+ // Timing and stops
381
+ this.stopTimer = 0;
382
+ this.stopDuration = 10; // 10 seconds
383
+ this.lastDestinationTime = 0;
384
+
385
+ // Fitness tracking
386
+ this.fitness = 0;
387
+ this.roadUsageScore = 0;
388
+ this.destinationsReached = 0;
389
+ this.formationScore = 0;
390
+ this.pathEfficiencyScore = 0;
391
+ this.stopComplianceScore = 0;
392
+ this.trafficViolations = 0;
393
+ this.distanceTraveled = 0;
394
+
395
+ // Sensors
396
+ this.sensors = {
397
+ obstacles: Array(8).fill(0),
398
+ roads: Array(4).fill(0),
399
+ traffic: Array(6).fill(0),
400
+ destinations: Array(4).fill(0),
401
+ formation: Array(4).fill(0),
402
+ navigation: Array(6).fill(0)
403
+ };
404
+
405
+ this.initializeNavigation();
406
+ this.createVisualization();
407
+ }
408
+
409
+ createCarMesh() {
410
+ const group = new THREE.Group();
411
+
412
+ // Car body
413
+ const bodyGeometry = new THREE.BoxGeometry(1.5, 0.8, 3);
414
+ this.bodyMaterial = new THREE.MeshLambertMaterial({
415
+ color: new THREE.Color().setHSL(Math.random(), 0.8, 0.6)
416
+ });
417
+ const body = new THREE.Mesh(bodyGeometry, this.bodyMaterial);
418
+ body.position.y = 0.4;
419
+ body.castShadow = true;
420
+ group.add(body);
421
+
422
+ // State indicator
423
+ this.stateIndicator = new THREE.Mesh(
424
+ new THREE.SphereGeometry(0.3, 8, 6),
425
+ new THREE.MeshLambertMaterial({ color: 0x00ff00 })
426
+ );
427
+ this.stateIndicator.position.set(0, 1.5, 0);
428
+ group.add(this.stateIndicator);
429
+
430
+ // Direction arrow
431
+ const arrowGeometry = new THREE.ConeGeometry(0.2, 0.8, 6);
432
+ this.directionArrow = new THREE.Mesh(arrowGeometry,
433
+ new THREE.MeshLambertMaterial({ color: 0xffffff }));
434
+ this.directionArrow.position.set(0, 2, 0);
435
+ this.directionArrow.rotation.x = -Math.PI / 2;
436
+ group.add(this.directionArrow);
437
+
438
+ return group;
439
+ }
440
+
441
+ createVisualization() {
442
+ // Path visualization with proper geometry initialization
443
+ const pathGeometry = new THREE.BufferGeometry();
444
+ // Initialize with a basic line to prevent undefined geometry
445
+ pathGeometry.setFromPoints([
446
+ new THREE.Vector3(0, 0, 0),
447
+ new THREE.Vector3(0, 0, 0)
448
+ ]);
449
+
450
+ this.pathLine = new THREE.Line(
451
+ pathGeometry,
452
+ new THREE.LineBasicMaterial({
453
+ color: 0x00ffff,
454
+ transparent: true,
455
+ opacity: 0.6
456
+ })
457
+ );
458
+ this.pathLine.visible = showPaths;
459
+ scene.add(this.pathLine);
460
+
461
+ // Destination marker
462
+ this.destinationMarker = new THREE.Mesh(
463
+ new THREE.RingGeometry(2, 3, 8),
464
+ new THREE.MeshBasicMaterial({
465
+ color: 0xff0000,
466
+ transparent: true,
467
+ opacity: 0.5
468
+ })
469
+ );
470
+ this.destinationMarker.rotation.x = -Math.PI / 2;
471
+ this.destinationMarker.visible = false;
472
+ scene.add(this.destinationMarker);
473
+ }
474
+
475
+ initializeNavigation() {
476
+ // Delay initial destination selection to ensure world is ready
477
+ setTimeout(() => {
478
+ this.selectNewDestination();
479
+ this.mesh.rotation.y = Math.random() * Math.PI * 2;
480
+ }, 100);
481
+ }
482
+
483
+ selectNewDestination() {
484
+ if (!world.buildings || world.buildings.length === 0) {
485
+ console.warn('No buildings available for navigation');
486
+ return;
487
+ }
488
+
489
+ // Choose a random building different from current position
490
+ let targetBuilding;
491
+ let attempts = 0;
492
+ do {
493
+ targetBuilding = world.buildings[Math.floor(Math.random() * world.buildings.length)];
494
+ attempts++;
495
+ } while (this.targetDestination &&
496
+ targetBuilding.position.distanceTo(this.targetDestination) < 50 &&
497
+ attempts < 10); // Prevent infinite loop
498
+
499
+ this.targetDestination = targetBuilding.position.clone();
500
+ this.planPath();
501
+ this.state = 'seeking';
502
+ this.lastDestinationTime = Date.now();
503
+
504
+ // Update destination marker
505
+ if (this.destinationMarker) {
506
+ this.destinationMarker.position.copy(this.targetDestination);
507
+ this.destinationMarker.position.y = 0.1;
508
+ this.destinationMarker.visible = true;
509
+ }
510
+ }
511
+
512
+ planPath() {
513
+ if (!this.targetDestination) return;
514
+
515
+ try {
516
+ // Simple pathfinding using road network
517
+ const startPos = this.mesh.position.clone();
518
+ const endPos = this.targetDestination.clone();
519
+
520
+ // Find nearest road intersections
521
+ const startIntersection = this.findNearestIntersection(startPos);
522
+ const endIntersection = this.findNearestIntersection(endPos);
523
+
524
+ if (startIntersection && endIntersection) {
525
+ this.currentPath = this.findPathBetweenIntersections(startIntersection, endIntersection);
526
+ } else {
527
+ // Direct path if no road network available
528
+ this.currentPath = [startPos, endPos];
529
+ }
530
+
531
+ this.pathIndex = 0;
532
+ this.updatePathVisualization();
533
+ } catch (error) {
534
+ console.warn('Path planning error:', error);
535
+ // Fallback to direct path
536
+ this.currentPath = [this.mesh.position.clone(), this.targetDestination.clone()];
537
+ this.pathIndex = 0;
538
+ }
539
+ }
540
+
541
+ findNearestIntersection(pos) {
542
+ let nearest = null;
543
+ let minDist = Infinity;
544
+
545
+ for (let x = -300; x <= 300; x += ROAD_SPACING) {
546
+ for (let z = -300; z <= 300; z += ROAD_SPACING) {
547
+ const intersection = new THREE.Vector3(x, 0, z);
548
+ const dist = pos.distanceTo(intersection);
549
+ if (dist < minDist) {
550
+ minDist = dist;
551
+ nearest = intersection;
552
+ }
553
+ }
554
+ }
555
+
556
+ return nearest;
557
+ }
558
+
559
+ findPathBetweenIntersections(start, end) {
560
+ // Simple A* pathfinding on grid
561
+ const path = [];
562
+ const current = start.clone();
563
+
564
+ while (current.distanceTo(end) > ROAD_SPACING / 2) {
565
+ path.push(current.clone());
566
+
567
+ const dx = end.x - current.x;
568
+ const dz = end.z - current.z;
569
+
570
+ if (Math.abs(dx) > Math.abs(dz)) {
571
+ current.x += Math.sign(dx) * ROAD_SPACING;
572
+ } else {
573
+ current.z += Math.sign(dz) * ROAD_SPACING;
574
+ }
575
+ }
576
+
577
+ path.push(end.clone());
578
+ return path;
579
+ }
580
+
581
+ updatePathVisualization() {
582
+ if (!this.pathLine || !this.pathLine.geometry || !showPaths || this.currentPath.length < 2) {
583
+ if (this.pathLine) this.pathLine.visible = false;
584
+ return;
585
+ }
586
+
587
+ try {
588
+ const points = this.currentPath.map(p => {
589
+ const point = p.clone();
590
+ point.y = 3;
591
+ return point;
592
+ });
593
+
594
+ this.pathLine.geometry.setFromPoints(points);
595
+ this.pathLine.visible = true;
596
+ } catch (error) {
597
+ console.warn('Path visualization error:', error);
598
+ if (this.pathLine) this.pathLine.visible = false;
599
+ }
600
+ }
601
+
602
+ updateSensors() {
603
+ this.updateObstacleSensors();
604
+ this.updateRoadSensors();
605
+ this.updateTrafficSensors();
606
+ this.updateDestinationSensors();
607
+ this.updateFormationSensors();
608
+ this.updateNavigationSensors();
609
+ }
610
+
611
+ updateObstacleSensors() {
612
+ const directions = [
613
+ [0, 0, 1], [0.7, 0, 0.7], [1, 0, 0], [0.7, 0, -0.7],
614
+ [0, 0, -1], [-0.7, 0, -0.7], [-1, 0, 0], [-0.7, 0, 0.7]
615
+ ];
616
+
617
+ const raycaster = new THREE.Raycaster();
618
+
619
+ directions.forEach((dir, i) => {
620
+ const direction = new THREE.Vector3(...dir);
621
+ direction.applyQuaternion(this.mesh.quaternion);
622
+
623
+ raycaster.set(this.mesh.position, direction);
624
+ const intersects = raycaster.intersectObjects(this.getObstacles(), true);
625
+
626
+ if (intersects.length > 0 && intersects[0].distance <= 12) {
627
+ this.sensors.obstacles[i] = 1 - (intersects[0].distance / 12);
628
+ } else {
629
+ this.sensors.obstacles[i] = 0;
630
+ }
631
+ });
632
+ }
633
+
634
+ updateRoadSensors() {
635
+ const pos = this.mesh.position;
636
+
637
+ // Detect road in 4 directions
638
+ this.sensors.roads[0] = this.detectRoad(pos, 'north');
639
+ this.sensors.roads[1] = this.detectRoad(pos, 'east');
640
+ this.sensors.roads[2] = this.detectRoad(pos, 'south');
641
+ this.sensors.roads[3] = this.detectRoad(pos, 'west');
642
+
643
+ // Update current road status
644
+ this.updateCurrentRoad();
645
+ }
646
+
647
+ detectRoad(pos, direction) {
648
+ const roadStrength = this.getRoadStrength(pos);
649
+ if (roadStrength === 0) return 0;
650
+
651
+ // Check road direction alignment
652
+ const directionVector = this.getDirectionVector(direction);
653
+ const roadDir = this.getRoadDirection(pos);
654
+
655
+ if (roadDir) {
656
+ const alignment = Math.abs(directionVector.dot(roadDir));
657
+ return roadStrength * alignment;
658
+ }
659
+
660
+ return roadStrength;
661
+ }
662
+
663
+ getRoadStrength(pos) {
664
+ const roadWidth = ROAD_WIDTH;
665
+
666
+ // Check horizontal roads
667
+ const nearestHorizontalRoad = Math.round(pos.z / ROAD_SPACING) * ROAD_SPACING;
668
+ const distToHorizontalRoad = Math.abs(pos.z - nearestHorizontalRoad);
669
+ const onHorizontalRoad = distToHorizontalRoad <= roadWidth / 2;
670
+
671
+ // Check vertical roads
672
+ const nearestVerticalRoad = Math.round(pos.x / ROAD_SPACING) * ROAD_SPACING;
673
+ const distToVerticalRoad = Math.abs(pos.x - nearestVerticalRoad);
674
+ const onVerticalRoad = distToVerticalRoad <= roadWidth / 2;
675
+
676
+ if (onHorizontalRoad || onVerticalRoad) {
677
+ const hStrength = onHorizontalRoad ? 1 - (distToHorizontalRoad / (roadWidth / 2)) : 0;
678
+ const vStrength = onVerticalRoad ? 1 - (distToVerticalRoad / (roadWidth / 2)) : 0;
679
+ return Math.max(hStrength, vStrength);
680
+ }
681
+
682
+ return 0;
683
+ }
684
+
685
+ getRoadDirection(pos) {
686
+ const roadWidth = ROAD_WIDTH;
687
+
688
+ const nearestHorizontalRoad = Math.round(pos.z / ROAD_SPACING) * ROAD_SPACING;
689
+ const distToHorizontalRoad = Math.abs(pos.z - nearestHorizontalRoad);
690
+ const onHorizontalRoad = distToHorizontalRoad <= roadWidth / 2;
691
+
692
+ const nearestVerticalRoad = Math.round(pos.x / ROAD_SPACING) * ROAD_SPACING;
693
+ const distToVerticalRoad = Math.abs(pos.x - nearestVerticalRoad);
694
+ const onVerticalRoad = distToVerticalRoad <= roadWidth / 2;
695
+
696
+ if (onHorizontalRoad && onVerticalRoad) {
697
+ // At intersection, prefer current movement direction
698
+ return this.velocity.length() > 0 ?
699
+ this.velocity.clone().normalize() : new THREE.Vector3(1, 0, 0);
700
+ } else if (onHorizontalRoad) {
701
+ return new THREE.Vector3(1, 0, 0);
702
+ } else if (onVerticalRoad) {
703
+ return new THREE.Vector3(0, 0, 1);
704
+ }
705
+
706
+ return null;
707
+ }
708
+
709
+ getDirectionVector(direction) {
710
+ switch (direction) {
711
+ case 'north': return new THREE.Vector3(0, 0, 1);
712
+ case 'east': return new THREE.Vector3(1, 0, 0);
713
+ case 'south': return new THREE.Vector3(0, 0, -1);
714
+ case 'west': return new THREE.Vector3(-1, 0, 0);
715
+ default: return new THREE.Vector3(1, 0, 0);
716
+ }
717
+ }
718
+
719
+ updateCurrentRoad() {
720
+ const roadStrength = this.getRoadStrength(this.mesh.position);
721
+ const roadDir = this.getRoadDirection(this.mesh.position);
722
+
723
+ if (roadStrength > 0.5 && roadDir) {
724
+ this.currentRoad = {
725
+ strength: roadStrength,
726
+ direction: roadDir
727
+ };
728
+ this.roadDirection = roadDir;
729
+ } else {
730
+ this.currentRoad = null;
731
+ }
732
+ }
733
+
734
+ updateTrafficSensors() {
735
+ const nearbyVehicles = population.filter(car =>
736
+ car !== this &&
737
+ this.mesh.position.distanceTo(car.mesh.position) < 25
738
+ );
739
+
740
+ // Traffic in 6 directions (front, back, left, right, front-left, front-right)
741
+ const directions = [
742
+ new THREE.Vector3(0, 0, 1), // front
743
+ new THREE.Vector3(0, 0, -1), // back
744
+ new THREE.Vector3(-1, 0, 0), // left
745
+ new THREE.Vector3(1, 0, 0), // right
746
+ new THREE.Vector3(-0.7, 0, 0.7), // front-left
747
+ new THREE.Vector3(0.7, 0, 0.7) // front-right
748
+ ];
749
+
750
+ directions.forEach((dir, i) => {
751
+ const worldDir = dir.clone().applyQuaternion(this.mesh.quaternion);
752
+ let trafficDensity = 0;
753
+
754
+ nearbyVehicles.forEach(car => {
755
+ const toVehicle = car.mesh.position.clone().sub(this.mesh.position).normalize();
756
+ const alignment = worldDir.dot(toVehicle);
757
+
758
+ if (alignment > 0.5) {
759
+ const distance = this.mesh.position.distanceTo(car.mesh.position);
760
+ trafficDensity += Math.max(0, 1 - distance / 25);
761
+ }
762
+ });
763
+
764
+ this.sensors.traffic[i] = Math.min(trafficDensity, 1);
765
+ });
766
+ }
767
+
768
+ updateDestinationSensors() {
769
+ if (!this.targetDestination) {
770
+ this.sensors.destinations.fill(0);
771
+ return;
772
+ }
773
+
774
+ const toDestination = this.targetDestination.clone().sub(this.mesh.position);
775
+ const distance = toDestination.length();
776
+ const direction = toDestination.normalize();
777
+
778
+ // Convert to local coordinates
779
+ const localDirection = direction.clone();
780
+ localDirection.applyQuaternion(this.mesh.quaternion.clone().invert());
781
+
782
+ // Destination sensors: front, back, left, right
783
+ this.sensors.destinations[0] = Math.max(0, localDirection.z) / Math.sqrt(distance / 100 + 1);
784
+ this.sensors.destinations[1] = Math.max(0, -localDirection.z) / Math.sqrt(distance / 100 + 1);
785
+ this.sensors.destinations[2] = Math.max(0, -localDirection.x) / Math.sqrt(distance / 100 + 1);
786
+ this.sensors.destinations[3] = Math.max(0, localDirection.x) / Math.sqrt(distance / 100 + 1);
787
+ }
788
+
789
+ updateFormationSensors() {
790
+ // Single-file formation awareness
791
+ const nearbyVehicles = population.filter(car =>
792
+ car !== this &&
793
+ this.mesh.position.distanceTo(car.mesh.position) < 20
794
+ );
795
+
796
+ let frontVehicle = null;
797
+ let backVehicle = null;
798
+ let frontDistance = Infinity;
799
+ let backDistance = Infinity;
800
+
801
+ const forward = new THREE.Vector3(0, 0, 1).applyQuaternion(this.mesh.quaternion);
802
+
803
+ nearbyVehicles.forEach(car => {
804
+ const toVehicle = car.mesh.position.clone().sub(this.mesh.position);
805
+ const dot = forward.dot(toVehicle.normalize());
806
+ const distance = toVehicle.length();
807
+
808
+ if (dot > 0.8 && distance < frontDistance) {
809
+ frontVehicle = car;
810
+ frontDistance = distance;
811
+ } else if (dot < -0.8 && distance < backDistance) {
812
+ backVehicle = car;
813
+ backDistance = distance;
814
+ }
815
+ });
816
+
817
+ this.sensors.formation[0] = frontVehicle ? Math.max(0, 1 - frontDistance / 15) : 0;
818
+ this.sensors.formation[1] = backVehicle ? Math.max(0, 1 - backDistance / 15) : 0;
819
+ this.sensors.formation[2] = nearbyVehicles.length / 10; // Local density
820
+ this.sensors.formation[3] = this.currentRoad ? this.currentRoad.strength : 0;
821
+ }
822
+
823
+ updateNavigationSensors() {
824
+ // Path following and navigation status
825
+ this.sensors.navigation[0] = this.currentPath.length > 0 ? 1 : 0;
826
+ this.sensors.navigation[1] = this.targetDestination ?
827
+ Math.min(1, 50 / this.mesh.position.distanceTo(this.targetDestination)) : 0;
828
+ this.sensors.navigation[2] = this.state === 'stopped' ? 1 : 0;
829
+ this.sensors.navigation[3] = this.state === 'following' ? 1 : 0;
830
+ this.sensors.navigation[4] = this.stopTimer / this.stopDuration;
831
+ this.sensors.navigation[5] = this.currentRoad ? 1 : 0;
832
+ }
833
+
834
+ update(deltaTime) {
835
+ this.updateSensors();
836
+ this.updateBehaviorState();
837
+
838
+ // Get neural network decision
839
+ const inputs = this.getAllInputs();
840
+ const outputs = this.brain.activate(inputs);
841
+
842
+ // Apply movement based on neural network and current state
843
+ this.applyMovement(outputs, deltaTime);
844
+
845
+ // Update fitness
846
+ this.updateFitness(deltaTime);
847
+
848
+ // Update visuals
849
+ this.updateVisuals();
850
+
851
+ // Update timers
852
+ this.updateTimers(deltaTime);
853
+ }
854
+
855
+ getAllInputs() {
856
+ return [
857
+ ...this.sensors.obstacles, // 8 inputs
858
+ ...this.sensors.roads, // 4 inputs
859
+ ...this.sensors.traffic, // 6 inputs
860
+ ...this.sensors.destinations, // 4 inputs
861
+ ...this.sensors.formation, // 4 inputs
862
+ ...this.sensors.navigation // 6 inputs
863
+ // Total: 32 inputs
864
+ ];
865
+ }
866
+
867
+ updateBehaviorState() {
868
+ if (!this.targetDestination) {
869
+ this.selectNewDestination();
870
+ return;
871
+ }
872
+
873
+ const distToDestination = this.mesh.position.distanceTo(this.targetDestination);
874
+
875
+ switch (this.state) {
876
+ case 'seeking':
877
+ // Look for formation opportunities
878
+ const nearbyLeader = this.findNearbyLeader();
879
+ if (nearbyLeader && this.brain.formationPreference > 0.6) {
880
+ this.state = 'following';
881
+ this.leader = nearbyLeader;
882
+ } else if (distToDestination < 8) {
883
+ this.state = 'stopped';
884
+ this.stopTimer = this.stopDuration;
885
+ }
886
+ break;
887
+
888
+ case 'following':
889
+ if (!this.leader || this.mesh.position.distanceTo(this.leader.mesh.position) > 30) {
890
+ this.state = 'seeking';
891
+ this.leader = null;
892
+ } else if (distToDestination < 8) {
893
+ this.state = 'stopped';
894
+ this.stopTimer = this.stopDuration;
895
+ this.leader = null;
896
+ }
897
+ break;
898
+
899
+ case 'stopped':
900
+ if (this.stopTimer <= 0) {
901
+ this.selectNewDestination();
902
+ this.destinationsReached++;
903
+ }
904
+ break;
905
+ }
906
+ }
907
+
908
+ findNearbyLeader() {
909
+ const candidates = population.filter(car =>
910
+ car !== this &&
911
+ car.state !== 'stopped' &&
912
+ this.mesh.position.distanceTo(car.mesh.position) < 20 &&
913
+ this.currentRoad && car.currentRoad
914
+ );
915
+
916
+ // Find car moving in similar direction on same road
917
+ return candidates.find(car => {
918
+ const alignment = this.roadDirection.dot(car.roadDirection);
919
+ return alignment > 0.8 || car.fitness > this.fitness;
920
+ });
921
+ }
922
+
923
+ applyMovement(outputs, deltaTime) {
924
+ const [
925
+ forward, brake, turnLeft, turnRight,
926
+ emergencyStop, speedUp, formationAdjust, roadFollow,
927
+ stopHere, pathCorrect, laneChange, precision
928
+ ] = outputs;
929
+
930
+ // Emergency stop override
931
+ if (emergencyStop > 0.8 || this.state === 'stopped') {
932
+ this.velocity.multiplyScalar(0.9);
933
+ return;
934
+ }
935
+
936
+ // Calculate desired direction
937
+ let desiredDirection = new THREE.Vector3(0, 0, 1);
938
+ desiredDirection.applyQuaternion(this.mesh.quaternion);
939
+
940
+ // Road following behavior
941
+ if (this.currentRoad && roadFollow > 0.3) {
942
+ const roadInfluence = roadFollow * this.brain.roadFollowingStrength;
943
+ desiredDirection.lerp(this.roadDirection, roadInfluence);
944
+ }
945
+
946
+ // Formation following
947
+ if (this.state === 'following' && this.leader) {
948
+ const followDirection = this.getFollowDirection();
949
+ const formationInfluence = formationAdjust * this.brain.formationPreference;
950
+ desiredDirection.lerp(followDirection, formationInfluence);
951
+ }
952
+
953
+ // Path following
954
+ if (this.currentPath.length > 0 && pathCorrect > 0.2) {
955
+ const pathDirection = this.getPathDirection();
956
+ if (pathDirection) {
957
+ desiredDirection.lerp(pathDirection, pathCorrect * 0.8);
958
+ }
959
+ }
960
+
961
+ // Apply turning
962
+ const currentForward = new THREE.Vector3(0, 0, 1);
963
+ currentForward.applyQuaternion(this.mesh.quaternion);
964
+
965
+ const turnAngle = currentForward.angleTo(desiredDirection);
966
+ const turnDirection = currentForward.cross(desiredDirection).y;
967
+
968
+ if (turnAngle > 0.1) {
969
+ const turnAmount = Math.sign(turnDirection) * Math.min(turnAngle, 0.05) * deltaTime;
970
+ this.mesh.rotation.y += turnAmount;
971
+ }
972
+
973
+ // Neural network turning input
974
+ const neuralTurn = (turnRight - turnLeft) * 0.03 * deltaTime;
975
+ this.mesh.rotation.y += neuralTurn;
976
+
977
+ // Speed control
978
+ let targetSpeed = this.targetSpeed;
979
+
980
+ if (this.state === 'following' && this.leader) {
981
+ const distToLeader = this.mesh.position.distanceTo(this.leader.mesh.position);
982
+ if (distToLeader < this.targetFollowingDistance) {
983
+ targetSpeed = Math.min(targetSpeed, this.leader.velocity.length() * 0.8);
984
+ }
985
+ }
986
+
987
+ if (speedUp > 0.6) targetSpeed *= 1.3;
988
+ if (brake > 0.4) targetSpeed *= 0.5;
989
+
990
+ // Apply acceleration
991
+ const forward3d = new THREE.Vector3(0, 0, 1);
992
+ forward3d.applyQuaternion(this.mesh.quaternion);
993
+
994
+ const currentSpeed = this.velocity.length();
995
+ const speedDiff = targetSpeed - currentSpeed;
996
+
997
+ if (forward > 0.2 && speedDiff > 0) {
998
+ this.acceleration.add(forward3d.multiplyScalar(speedDiff * 0.5 * deltaTime));
999
+ }
1000
+
1001
+ // Apply movement
1002
+ this.velocity.add(this.acceleration);
1003
+ this.acceleration.multiplyScalar(0.1); // Decay
1004
+
1005
+ // Speed limits
1006
+ if (this.velocity.length() > this.maxSpeed) {
1007
+ this.velocity.normalize().multiplyScalar(this.maxSpeed);
1008
+ } else if (this.velocity.length() < this.minSpeed && forward > 0.1) {
1009
+ this.velocity.normalize().multiplyScalar(this.minSpeed);
1010
+ }
1011
+
1012
+ // Apply position
1013
+ const oldPosition = this.mesh.position.clone();
1014
+ this.mesh.position.add(this.velocity.clone().multiplyScalar(deltaTime));
1015
+ this.distanceTraveled += oldPosition.distanceTo(this.mesh.position);
1016
+
1017
+ this.keepInBounds();
1018
+ }
1019
+
1020
+ getFollowDirection() {
1021
+ if (!this.leader) return new THREE.Vector3(0, 0, 1);
1022
+
1023
+ const toLeader = this.leader.mesh.position.clone().sub(this.mesh.position);
1024
+ const distance = toLeader.length();
1025
+
1026
+ if (distance > this.targetFollowingDistance) {
1027
+ return toLeader.normalize();
1028
+ } else {
1029
+ // Match leader's direction
1030
+ return this.leader.velocity.clone().normalize();
1031
+ }
1032
+ }
1033
+
1034
+ getPathDirection() {
1035
+ if (this.currentPath.length === 0 || this.pathIndex >= this.currentPath.length) {
1036
+ return null;
1037
+ }
1038
+
1039
+ const targetWaypoint = this.currentPath[this.pathIndex];
1040
+ const toWaypoint = targetWaypoint.clone().sub(this.mesh.position);
1041
+ const distance = toWaypoint.length();
1042
+
1043
+ if (distance < 15) {
1044
+ this.pathIndex++;
1045
+ if (this.pathIndex < this.currentPath.length) {
1046
+ return this.getPathDirection();
1047
+ }
1048
+ }
1049
+
1050
+ return toWaypoint.normalize();
1051
+ }
1052
+
1053
+ updateFitness(deltaTime) {
1054
+ const oldFitness = this.fitness;
1055
+
1056
+ // Road usage reward
1057
+ const roadBonus = this.currentRoad ? this.currentRoad.strength * deltaTime * 100 : 0;
1058
+ this.roadUsageScore += roadBonus;
1059
+
1060
+ // Formation reward
1061
+ if (this.state === 'following' && this.leader) {
1062
+ const distance = this.mesh.position.distanceTo(this.leader.mesh.position);
1063
+ if (distance > 5 && distance < 12) {
1064
+ this.formationScore += deltaTime * 50;
1065
+ }
1066
+ }
1067
+
1068
+ // Destination reaching reward
1069
+ if (this.state === 'stopped') {
1070
+ this.stopComplianceScore += deltaTime * 30;
1071
+ }
1072
+
1073
+ // Path efficiency
1074
+ if (this.currentPath.length > 0) {
1075
+ this.pathEfficiencyScore += deltaTime * 20;
1076
+ }
1077
+
1078
+ // Traffic violations penalty
1079
+ const nearbyVehicles = population.filter(car =>
1080
+ car !== this && this.mesh.position.distanceTo(car.mesh.position) < 3
1081
+ );
1082
+ if (nearbyVehicles.length > 0) {
1083
+ this.trafficViolations += deltaTime;
1084
+ }
1085
+
1086
+ // Total fitness calculation
1087
+ this.fitness =
1088
+ this.roadUsageScore +
1089
+ this.formationScore +
1090
+ this.destinationsReached * 200 +
1091
+ this.pathEfficiencyScore +
1092
+ this.stopComplianceScore +
1093
+ this.distanceTraveled * 0.5 -
1094
+ this.trafficViolations * 50;
1095
+
1096
+ // Update global best
1097
+ if (this.fitness > bestFitness) {
1098
+ bestFitness = this.fitness;
1099
+ }
1100
+ }
1101
+
1102
+ updateVisuals() {
1103
+ // State color coding
1104
+ const stateColors = {
1105
+ seeking: 0x00ff00,
1106
+ following: 0x0088ff,
1107
+ stopped: 0xff0000,
1108
+ traveling: 0xffff00
1109
+ };
1110
+
1111
+ this.stateIndicator.material.color.setHex(stateColors[this.state] || 0xffffff);
1112
+
1113
+ // Direction arrow points toward target or leader
1114
+ if (this.state === 'following' && this.leader) {
1115
+ const toLeader = this.leader.mesh.position.clone().sub(this.mesh.position);
1116
+ this.directionArrow.lookAt(this.mesh.position.clone().add(toLeader));
1117
+ } else if (this.targetDestination) {
1118
+ this.directionArrow.lookAt(this.targetDestination);
1119
+ }
1120
+
1121
+ // Body color based on performance
1122
+ const performance = Math.min(this.fitness / 1000, 1);
1123
+ this.bodyMaterial.color.setHSL(
1124
+ performance * 0.3, // Green to red spectrum
1125
+ 0.8,
1126
+ 0.4 + performance * 0.4
1127
+ );
1128
+ }
1129
+
1130
+ updateTimers(deltaTime) {
1131
+ if (this.state === 'stopped') {
1132
+ this.stopTimer -= deltaTime;
1133
+ }
1134
+ }
1135
+
1136
+ getObstacles() {
1137
+ const obstacles = [];
1138
+ try {
1139
+ if (population) {
1140
+ population.forEach(car => {
1141
+ if (car !== this && car.mesh) obstacles.push(car.mesh);
1142
+ });
1143
+ }
1144
+ if (world.buildings) {
1145
+ world.buildings.forEach(building => {
1146
+ if (building.mesh) obstacles.push(building.mesh);
1147
+ });
1148
+ }
1149
+ } catch (error) {
1150
+ console.warn('Error getting obstacles:', error);
1151
+ }
1152
+ return obstacles;
1153
+ }
1154
+
1155
+ keepInBounds() {
1156
+ const bounds = 350;
1157
+ if (Math.abs(this.mesh.position.x) > bounds) {
1158
+ this.mesh.position.x = Math.sign(this.mesh.position.x) * bounds;
1159
+ this.velocity.x *= -0.5;
1160
+ }
1161
+ if (Math.abs(this.mesh.position.z) > bounds) {
1162
+ this.mesh.position.z = Math.sign(this.mesh.position.z) * bounds;
1163
+ this.velocity.z *= -0.5;
1164
+ }
1165
+ }
1166
+
1167
+ destroy() {
1168
+ try {
1169
+ if (this.pathLine && this.pathLine.parent) {
1170
+ scene.remove(this.pathLine);
1171
+ if (this.pathLine.geometry) this.pathLine.geometry.dispose();
1172
+ if (this.pathLine.material) this.pathLine.material.dispose();
1173
+ }
1174
+ if (this.destinationMarker && this.destinationMarker.parent) {
1175
+ scene.remove(this.destinationMarker);
1176
+ if (this.destinationMarker.geometry) this.destinationMarker.geometry.dispose();
1177
+ if (this.destinationMarker.material) this.destinationMarker.material.dispose();
1178
+ }
1179
+ if (this.mesh && this.mesh.parent) {
1180
+ scene.remove(this.mesh);
1181
+ // Dispose of car mesh materials and geometries
1182
+ this.mesh.traverse((child) => {
1183
+ if (child.geometry) child.geometry.dispose();
1184
+ if (child.material) {
1185
+ if (Array.isArray(child.material)) {
1186
+ child.material.forEach(mat => mat.dispose());
1187
+ } else {
1188
+ child.material.dispose();
1189
+ }
1190
+ }
1191
+ });
1192
+ }
1193
+ } catch (error) {
1194
+ console.warn('Cleanup error:', error);
1195
+ }
1196
+ }
1197
+ }
1198
+
1199
+ function init() {
1200
+ // Scene setup
1201
+ scene = new THREE.Scene();
1202
+ scene.background = new THREE.Color(0x87CEEB);
1203
+ scene.fog = new THREE.Fog(0x87CEEB, 200, 800);
1204
+
1205
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
1206
+ camera.position.set(0, 80, 80);
1207
+ camera.lookAt(0, 0, 0);
1208
+
1209
+ renderer = new THREE.WebGLRenderer({ antialias: true });
1210
+ renderer.setSize(window.innerWidth, window.innerHeight);
1211
+ renderer.shadowMap.enabled = true;
1212
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
1213
+ renderer.setClearColor(0x001122);
1214
+ document.body.appendChild(renderer.domElement);
1215
+
1216
+ // Lighting
1217
+ const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
1218
+ scene.add(ambientLight);
1219
+
1220
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
1221
+ directionalLight.position.set(50, 100, 50);
1222
+ directionalLight.castShadow = true;
1223
+ scene.add(directionalLight);
1224
+
1225
+ createWorld();
1226
+ createPopulation();
1227
+
1228
+ clock = new THREE.Clock();
1229
+
1230
+ window.addEventListener('resize', onWindowResize);
1231
+ setupControls();
1232
+
1233
+ animate();
1234
+ }
1235
+
1236
+ function createWorld() {
1237
+ // Ground
1238
+ const groundGeometry = new THREE.PlaneGeometry(800, 800);
1239
+ const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x228B22 });
1240
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
1241
+ ground.rotation.x = -Math.PI / 2;
1242
+ ground.receiveShadow = true;
1243
+ scene.add(ground);
1244
+
1245
+ createRoadNetwork();
1246
+ createBuildings();
1247
+ }
1248
+
1249
+ function createRoadNetwork() {
1250
+ const roadMaterial = new THREE.MeshLambertMaterial({ color: 0x444444 });
1251
+
1252
+ // Create grid of roads
1253
+ for (let x = -300; x <= 300; x += ROAD_SPACING) {
1254
+ // Vertical road
1255
+ const vRoadGeometry = new THREE.PlaneGeometry(ROAD_WIDTH, 600);
1256
+ const vRoad = new THREE.Mesh(vRoadGeometry, roadMaterial);
1257
+ vRoad.rotation.x = -Math.PI / 2;
1258
+ vRoad.position.set(x, 0.1, 0);
1259
+ scene.add(vRoad);
1260
+
1261
+ // Lane dividers
1262
+ for (let z = -290; z <= 290; z += 20) {
1263
+ const dividerGeometry = new THREE.PlaneGeometry(0.5, 8);
1264
+ const dividerMaterial = new THREE.MeshLambertMaterial({ color: 0xffff00 });
1265
+ const divider = new THREE.Mesh(dividerGeometry, dividerMaterial);
1266
+ divider.rotation.x = -Math.PI / 2;
1267
+ divider.position.set(x, 0.2, z);
1268
+ scene.add(divider);
1269
+ }
1270
+ }
1271
+
1272
+ for (let z = -300; z <= 300; z += ROAD_SPACING) {
1273
+ // Horizontal road
1274
+ const hRoadGeometry = new THREE.PlaneGeometry(600, ROAD_WIDTH);
1275
+ const hRoad = new THREE.Mesh(hRoadGeometry, roadMaterial);
1276
+ hRoad.rotation.x = -Math.PI / 2;
1277
+ hRoad.position.set(0, 0.1, z);
1278
+ scene.add(hRoad);
1279
+
1280
+ // Lane dividers
1281
+ for (let x = -290; x <= 290; x += 20) {
1282
+ const dividerGeometry = new THREE.PlaneGeometry(8, 0.5);
1283
+ const dividerMaterial = new THREE.MeshLambertMaterial({ color: 0xffff00 });
1284
+ const divider = new THREE.Mesh(dividerGeometry, dividerMaterial);
1285
+ divider.rotation.x = -Math.PI / 2;
1286
+ divider.position.set(x, 0.2, z);
1287
+ scene.add(divider);
1288
+ }
1289
+ }
1290
+ }
1291
+
1292
+ function createBuildings() {
1293
+ world.buildings = [];
1294
+ const buildingMaterial = new THREE.MeshLambertMaterial({ color: 0x666666 });
1295
+
1296
+ // Place buildings at strategic locations away from roads
1297
+ const buildingPositions = [
1298
+ [-180, -180], [-60, -180], [60, -180], [180, -180],
1299
+ [-180, -60], [-60, -60], [60, -60], [180, -60],
1300
+ [-180, 60], [-60, 60], [60, 60], [180, 60],
1301
+ [-180, 180], [-60, 180], [60, 180], [180, 180]
1302
+ ];
1303
+
1304
+ buildingPositions.forEach(([x, z]) => {
1305
+ const width = 20 + Math.random() * 15;
1306
+ const height = 10 + Math.random() * 25;
1307
+ const depth = 20 + Math.random() * 15;
1308
+
1309
+ const buildingGeometry = new THREE.BoxGeometry(width, height, depth);
1310
+ const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
1311
+ building.position.set(x, height / 2, z);
1312
+ building.castShadow = true;
1313
+ scene.add(building);
1314
+
1315
+ world.buildings.push({
1316
+ mesh: building,
1317
+ position: new THREE.Vector3(x, 0, z)
1318
+ });
1319
+ });
1320
+ }
1321
+
1322
+ function createPopulation() {
1323
+ population = [];
1324
+
1325
+ // Start cars at various intersections
1326
+ const startPositions = [
1327
+ [-120, -120], [0, -120], [120, -120],
1328
+ [-120, 0], [0, 0], [120, 0],
1329
+ [-120, 120], [0, 120], [120, 120]
1330
+ ];
1331
+
1332
+ for (let i = 0; i < populationSize; i++) {
1333
+ const startPos = startPositions[i % startPositions.length];
1334
+ const x = startPos[0] + (Math.random() - 0.5) * 20;
1335
+ const z = startPos[1] + (Math.random() - 0.5) * 20;
1336
+
1337
+ const car = new RoadNavigationCar(x, z);
1338
+ population.push(car);
1339
+ scene.add(car.mesh);
1340
+ }
1341
+ }
1342
+
1343
+ function evolve() {
1344
+ console.log(`Epoch ${epoch} complete. Best fitness: ${bestFitness.toFixed(1)}`);
1345
+
1346
+ // Sort population by fitness
1347
+ population.sort((a, b) => b.fitness - a.fitness);
1348
+
1349
+ // Keep top performers
1350
+ const eliteCount = Math.floor(populationSize * 0.3);
1351
+ const newPopulation = [];
1352
+
1353
+ // Elite selection
1354
+ for (let i = 0; i < eliteCount; i++) {
1355
+ const elite = population[i];
1356
+ const newCar = createNewCar();
1357
+ newCar.brain = elite.brain.copy();
1358
+ newPopulation.push(newCar);
1359
+ }
1360
+
1361
+ // Crossover and mutation
1362
+ while (newPopulation.length < populationSize) {
1363
+ const parent1 = tournamentSelection();
1364
+ const parent2 = tournamentSelection();
1365
+
1366
+ const child = createNewCar();
1367
+ child.brain = parent1.brain.crossover(parent2.brain);
1368
+ child.brain.mutate(0.1);
1369
+
1370
+ newPopulation.push(child);
1371
+ }
1372
+
1373
+ // Replace population
1374
+ population.forEach(car => car.destroy());
1375
+ population = newPopulation;
1376
+ population.forEach(car => scene.add(car.mesh));
1377
+
1378
+ // Reset for new epoch
1379
+ epoch++;
1380
+ timeLeft = epochTime;
1381
+ }
1382
+
1383
+ function createNewCar() {
1384
+ const startPositions = [
1385
+ [-120, -120], [0, -120], [120, -120],
1386
+ [-120, 0], [0, 0], [120, 0],
1387
+ [-120, 120], [0, 120], [120, 120]
1388
+ ];
1389
+
1390
+ const startPos = startPositions[Math.floor(Math.random() * startPositions.length)];
1391
+ const x = startPos[0] + (Math.random() - 0.5) * 20;
1392
+ const z = startPos[1] + (Math.random() - 0.5) * 20;
1393
+
1394
+ return new RoadNavigationCar(x, z);
1395
+ }
1396
+
1397
+ function tournamentSelection(tournamentSize = 3) {
1398
+ let best = null;
1399
+ let bestFitness = -Infinity;
1400
+
1401
+ for (let i = 0; i < tournamentSize; i++) {
1402
+ const candidate = population[Math.floor(Math.random() * population.length)];
1403
+ if (candidate.fitness > bestFitness) {
1404
+ best = candidate;
1405
+ bestFitness = candidate.fitness;
1406
+ }
1407
+ }
1408
+
1409
+ return best;
1410
+ }
1411
+
1412
+ function animate() {
1413
+ requestAnimationFrame(animate);
1414
+
1415
+ if (!paused) {
1416
+ const deltaTime = Math.min(clock.getDelta() * speedMultiplier, 0.05);
1417
+
1418
+ timeLeft -= deltaTime;
1419
+ if (timeLeft <= 0) {
1420
+ evolve();
1421
+ }
1422
+
1423
+ updatePopulation(deltaTime);
1424
+ updateCamera();
1425
+ updateUI();
1426
+ }
1427
+
1428
+ renderer.render(scene, camera);
1429
+ }
1430
+
1431
+ function updatePopulation(deltaTime) {
1432
+ population.forEach(car => car.update(deltaTime));
1433
+ }
1434
+
1435
+ function updateCamera() {
1436
+ if (cameraMode === 'follow') {
1437
+ const bestCar = population.reduce((best, car) =>
1438
+ car.fitness > best.fitness ? car : best
1439
+ );
1440
+
1441
+ const targetPos = bestCar.mesh.position.clone();
1442
+ targetPos.y += 40;
1443
+ targetPos.add(bestCar.velocity.clone().normalize().multiplyScalar(20));
1444
+
1445
+ camera.position.lerp(targetPos, 0.02);
1446
+ camera.lookAt(bestCar.mesh.position);
1447
+ } else {
1448
+ camera.position.lerp(new THREE.Vector3(0, 150, 150), 0.02);
1449
+ camera.lookAt(0, 0, 0);
1450
+ }
1451
+ }
1452
+
1453
+ function updateUI() {
1454
+ // Basic stats
1455
+ document.getElementById('epoch').textContent = epoch;
1456
+ document.getElementById('epochTime').textContent = Math.ceil(timeLeft);
1457
+ document.getElementById('population').textContent = population.length;
1458
+ document.getElementById('bestFitness').textContent = Math.round(bestFitness);
1459
+
1460
+ // Progress bar
1461
+ const progress = ((epochTime - timeLeft) / epochTime) * 100;
1462
+ document.getElementById('timeProgress').style.width = `${progress}%`;
1463
+
1464
+ // Calculate aggregate stats
1465
+ let stats = {
1466
+ onRoad: 0,
1467
+ traveling: 0,
1468
+ stopped: 0,
1469
+ navigating: 0,
1470
+ totalDestinations: 0,
1471
+ totalRoadUsage: 0,
1472
+ totalFormation: 0,
1473
+ totalPathEfficiency: 0,
1474
+ totalStopCompliance: 0,
1475
+ totalViolations: 0,
1476
+ singleFileLines: 0,
1477
+ totalLineLength: 0
1478
+ };
1479
+
1480
+ population.forEach(car => {
1481
+ if (car.currentRoad) stats.onRoad++;
1482
+ if (car.state === 'seeking') stats.traveling++;
1483
+ if (car.state === 'stopped') stats.stopped++;
1484
+ if (car.currentPath.length > 0) stats.navigating++;
1485
+
1486
+ stats.totalDestinations += car.destinationsReached;
1487
+ stats.totalRoadUsage += car.roadUsageScore;
1488
+ stats.totalFormation += car.formationScore;
1489
+ stats.totalPathEfficiency += car.pathEfficiencyScore;
1490
+ stats.totalStopCompliance += car.stopComplianceScore;
1491
+ stats.totalViolations += car.trafficViolations;
1492
+ });
1493
+
1494
+ // Update UI elements
1495
+ document.getElementById('onRoadCount').textContent = stats.onRoad;
1496
+ document.getElementById('travelingCount').textContent = stats.traveling;
1497
+ document.getElementById('stoppedCount').textContent = stats.stopped;
1498
+ document.getElementById('navigatingCount').textContent = stats.navigating;
1499
+
1500
+ document.getElementById('destinationsReached').textContent = stats.totalDestinations;
1501
+ document.getElementById('roadUsage').textContent = Math.round((stats.totalRoadUsage / population.length) / 10);
1502
+ document.getElementById('formationQuality').textContent = Math.round((stats.totalFormation / population.length) / 10);
1503
+ document.getElementById('pathEfficiency').textContent = Math.round((stats.totalPathEfficiency / population.length) / 10);
1504
+ document.getElementById('stopCompliance').textContent = Math.round((stats.totalStopCompliance / population.length) / 10);
1505
+ document.getElementById('trafficViolations').textContent = Math.round(stats.totalViolations);
1506
+
1507
+ // Behavioral stats
1508
+ const avgRoadFollowing = population.reduce((sum, car) => sum + car.brain.roadFollowingStrength, 0) / population.length;
1509
+ const avgFormationPref = population.reduce((sum, car) => sum + car.brain.formationPreference, 0) / population.length;
1510
+ const avgNavSkill = population.reduce((sum, car) => sum + car.brain.navigationSkill, 0) / population.length;
1511
+
1512
+ document.getElementById('roadFollowing').textContent = Math.round(avgRoadFollowing * 100);
1513
+ document.getElementById('formationControl').textContent = Math.round(avgFormationPref * 100);
1514
+ document.getElementById('navigationSkills').textContent = Math.round(avgNavSkill * 100);
1515
+ document.getElementById('navigationIQ').textContent = Math.round(avgNavSkill * 100);
1516
+ document.getElementById('roadMastery').textContent = Math.round(avgRoadFollowing * 100);
1517
+ }
1518
+
1519
+ function setupControls() {
1520
+ document.getElementById('pauseBtn').addEventListener('click', () => {
1521
+ paused = !paused;
1522
+ document.getElementById('pauseBtn').textContent = paused ? 'Resume' : 'Pause';
1523
+ });
1524
+
1525
+ document.getElementById('resetBtn').addEventListener('click', () => {
1526
+ population.forEach(car => car.destroy());
1527
+ epoch = 1;
1528
+ timeLeft = epochTime;
1529
+ bestFitness = 0;
1530
+ createPopulation();
1531
+ });
1532
+
1533
+ document.getElementById('speedBtn').addEventListener('click', () => {
1534
+ speedMultiplier = speedMultiplier === 1 ? 2 : speedMultiplier === 2 ? 5 : 1;
1535
+ document.getElementById('speedBtn').textContent = `Speed: ${speedMultiplier}x`;
1536
+ });
1537
+
1538
+ document.getElementById('viewBtn').addEventListener('click', () => {
1539
+ cameraMode = cameraMode === 'follow' ? 'overview' : 'follow';
1540
+ document.getElementById('viewBtn').textContent = `View: ${cameraMode === 'follow' ? 'Follow' : 'Overview'}`;
1541
+ });
1542
+
1543
+ document.getElementById('pathBtn').addEventListener('click', () => {
1544
+ showPaths = !showPaths;
1545
+ document.getElementById('pathBtn').textContent = `Paths: ${showPaths ? 'ON' : 'OFF'}`;
1546
+ population.forEach(car => {
1547
+ if (car.pathLine) {
1548
+ car.pathLine.visible = showPaths;
1549
+ if (showPaths) {
1550
+ car.updatePathVisualization();
1551
+ }
1552
+ }
1553
+ });
1554
+ });
1555
+
1556
+ document.getElementById('debugBtn').addEventListener('click', () => {
1557
+ debugMode = !debugMode;
1558
+ document.getElementById('debugBtn').textContent = `Debug: ${debugMode ? 'ON' : 'OFF'}`;
1559
+ });
1560
+ }
1561
+
1562
+ function onWindowResize() {
1563
+ camera.aspect = window.innerWidth / window.innerHeight;
1564
+ camera.updateProjectionMatrix();
1565
+ renderer.setSize(window.innerWidth, window.innerHeight);
1566
+ }
1567
+
1568
+ init();
1569
+ </script>
1570
+ </body>
1571
+ </html>