LMLK commited on
Commit
a3a2716
·
verified ·
1 Parent(s): e891192

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +713 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Greyhound Race Predictor
3
- emoji:
4
- colorFrom: purple
5
- colorTo: pink
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: greyhound-race-predictor
3
+ emoji: 🐳
4
+ colorFrom: red
5
+ colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,713 @@
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>Greyhound Race Predictor</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js"></script>
12
+ <style>
13
+ /* Custom scrollbar */
14
+ ::-webkit-scrollbar {
15
+ width: 8px;
16
+ height: 8px;
17
+ }
18
+ ::-webkit-scrollbar-track {
19
+ background: #f1f1f1;
20
+ border-radius: 10px;
21
+ }
22
+ ::-webkit-scrollbar-thumb {
23
+ background: #888;
24
+ border-radius: 10px;
25
+ }
26
+ ::-webkit-scrollbar-thumb:hover {
27
+ background: #555;
28
+ }
29
+
30
+ /* Custom slider */
31
+ input[type="range"] {
32
+ -webkit-appearance: none;
33
+ height: 8px;
34
+ background: #e2e8f0;
35
+ border-radius: 5px;
36
+ background-image: linear-gradient(#3b82f6, #3b82f6);
37
+ background-size: 50% 100%;
38
+ background-repeat: no-repeat;
39
+ }
40
+
41
+ input[type="range"]::-webkit-slider-thumb {
42
+ -webkit-appearance: none;
43
+ height: 20px;
44
+ width: 20px;
45
+ border-radius: 50%;
46
+ background: #3b82f6;
47
+ cursor: pointer;
48
+ box-shadow: 0 0 2px 0 #555;
49
+ transition: background .3s ease-in-out;
50
+ }
51
+
52
+ input[type="range"]::-webkit-slider-runnable-track {
53
+ -webkit-appearance: none;
54
+ box-shadow: none;
55
+ border: none;
56
+ background: transparent;
57
+ }
58
+
59
+ /* Radar chart styling */
60
+ .radar-chart-container {
61
+ position: relative;
62
+ height: 400px;
63
+ width: 100%;
64
+ }
65
+
66
+ /* Animation for tabs */
67
+ .tab-content {
68
+ display: none;
69
+ }
70
+
71
+ .tab-content.active {
72
+ display: block;
73
+ animation: fadeIn 0.3s ease-in-out;
74
+ }
75
+
76
+ @keyframes fadeIn {
77
+ from { opacity: 0; }
78
+ to { opacity: 1; }
79
+ }
80
+
81
+ /* Custom tooltip */
82
+ .tooltip {
83
+ position: relative;
84
+ display: inline-block;
85
+ }
86
+
87
+ .tooltip .tooltiptext {
88
+ visibility: hidden;
89
+ width: 200px;
90
+ background-color: #333;
91
+ color: #fff;
92
+ text-align: center;
93
+ border-radius: 6px;
94
+ padding: 5px;
95
+ position: absolute;
96
+ z-index: 1;
97
+ bottom: 125%;
98
+ left: 50%;
99
+ margin-left: -100px;
100
+ opacity: 0;
101
+ transition: opacity 0.3s;
102
+ }
103
+
104
+ .tooltip:hover .tooltiptext {
105
+ visibility: visible;
106
+ opacity: 1;
107
+ }
108
+ </style>
109
+ </head>
110
+ <body class="bg-gray-100 min-h-screen">
111
+ <div class="container mx-auto px-4 py-8 max-w-7xl">
112
+ <!-- Header -->
113
+ <header class="bg-blue-600 text-white rounded-lg shadow-md mb-8">
114
+ <div class="px-6 py-4">
115
+ <h1 class="text-3xl font-bold">Greyhound Race Predictor</h1>
116
+ <p class="mt-2">Advanced algorithm to predict race outcomes based on key performance metrics</p>
117
+ </div>
118
+ </header>
119
+
120
+ <!-- Tabs Navigation -->
121
+ <div class="flex border-b border-gray-300 mb-6">
122
+ <button class="tab-btn py-2 px-4 font-medium text-blue-600 border-b-2 border-blue-600" data-tab="input">Dog Data Input</button>
123
+ <button class="tab-btn py-2 px-4 font-medium text-gray-500 hover:text-blue-600" data-tab="results">Prediction Results</button>
124
+ </div>
125
+
126
+ <!-- Formula Explanation -->
127
+ <div class="bg-white rounded-lg shadow-md p-6 mb-8">
128
+ <h2 class="text-xl font-semibold mb-4 text-blue-600">Prediction Data</h2>
129
+ <div class="bg-blue-50 p-4 rounded-lg">
130
+ <p class="font-medium mb-2"></p>
131
+ <p class="mb-4"></p>
132
+ <p class="mb-2">Each metric is rated on a scale of 0-10:</p>
133
+ <ul class="list-disc pl-5 space-y-1">
134
+ <li><span class="font-medium">Form:</span> Recent race positions (higher values for better positions)</li>
135
+ <li><span class="font-medium">Speed:</span> Recent race times compared to competitors (higher for faster times)</li>
136
+ <li><span class="font-medium">Consistency:</span> How reliably the dog performs in races (higher for more consistent results)</li>
137
+ <li><span class="font-medium">Track:</span> Experience and performance at this particular track (higher for better track history)</li>
138
+ <li><span class="font-medium">Recency:</span> How recent and relevant the form data is (higher for more recent good form)</li>
139
+ </ul>
140
+ </div>
141
+ </div>
142
+
143
+ <!-- Input Tab -->
144
+ <div id="input" class="tab-content active">
145
+ <!-- Dog Input Forms -->
146
+ <div class="space-y-6 mb-8" id="dogFormsContainer">
147
+ <!-- Dog forms will be added here dynamically -->
148
+ </div>
149
+
150
+ <!-- Action Buttons -->
151
+ <div class="flex flex-wrap gap-4 mb-8">
152
+ <button id="calculateBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-lg shadow-md transition duration-300">
153
+ Calculate Predictions
154
+ </button>
155
+ <button id="clearBtn" class="bg-gray-500 hover:bg-gray-600 text-white font-medium py-2 px-6 rounded-lg shadow-md transition duration-300">
156
+ Clear All Data
157
+ </button>
158
+ <button id="sampleBtn" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-6 rounded-lg shadow-md transition duration-300">
159
+ Load Sample Data
160
+ </button>
161
+ <button id="addDogBtn" class="bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-6 rounded-lg shadow-md transition duration-300">
162
+ Add Another Dog
163
+ </button>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Results Tab -->
168
+ <div id="results" class="tab-content">
169
+ <!-- Text Results -->
170
+ <div class="bg-white rounded-lg shadow-md p-6 mb-8">
171
+ <h2 class="text-xl font-semibold mb-4 text-blue-600">Prediction Results</h2>
172
+ <div id="resultsText" class="bg-gray-50 p-4 rounded-lg max-h-96 overflow-y-auto border border-gray-200">
173
+ <!-- Results will be displayed here -->
174
+ <p class="text-gray-500 italic">No predictions calculated yet. Enter dog data and click "Calculate Predictions".</p>
175
+ </div>
176
+ </div>
177
+
178
+ <!-- Charts -->
179
+ <div class="grid grid-cols-1 gap-8">
180
+ <!-- Probability Chart -->
181
+ <div class="bg-white rounded-lg shadow-md p-6">
182
+ <h2 class="text-xl font-semibold mb-4 text-blue-600">Win Probability</h2>
183
+ <div class="h-96">
184
+ <canvas id="probabilityChart"></canvas>
185
+ </div>
186
+ </div>
187
+
188
+ <!-- Radar Chart -->
189
+ <div class="bg-white rounded-lg shadow-md p-6">
190
+ <h2 class="text-xl font-semibold mb-4 text-blue-600">Top Dogs Metrics Comparison</h2>
191
+ <div class="radar-chart-container">
192
+ <canvas id="radarChart"></canvas>
193
+ </div>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <script>
200
+ // Initialize app
201
+ document.addEventListener('DOMContentLoaded', function() {
202
+ // Constants
203
+ const MAX_DOGS = 8;
204
+ const METRICS = ['form', 'speed', 'consistency', 'track', 'recency'];
205
+ const METRIC_LABELS = ['Form', 'Speed', 'Consistency', 'Track', 'Recency'];
206
+ const METRIC_WEIGHTS = [0.8, 0.7, 0.6, 0.5, 0.4];
207
+
208
+ // State
209
+ let dogsData = [];
210
+ let dogCount = 0;
211
+ let probabilityChart = null;
212
+ let radarChart = null;
213
+
214
+ // DOM Elements
215
+ const dogFormsContainer = document.getElementById('dogFormsContainer');
216
+ const resultsText = document.getElementById('resultsText');
217
+ const calculateBtn = document.getElementById('calculateBtn');
218
+ const clearBtn = document.getElementById('clearBtn');
219
+ const sampleBtn = document.getElementById('sampleBtn');
220
+ const addDogBtn = document.getElementById('addDogBtn');
221
+ const tabBtns = document.querySelectorAll('.tab-btn');
222
+ const tabContents = document.querySelectorAll('.tab-content');
223
+
224
+ // Initialize with 6 dogs (standard race size)
225
+ for (let i = 0; i < 6; i++) {
226
+ addDogForm();
227
+ }
228
+
229
+ // Event Listeners
230
+ calculateBtn.addEventListener('click', calculatePredictions);
231
+ clearBtn.addEventListener('click', clearAllData);
232
+ sampleBtn.addEventListener('click', loadSampleData);
233
+ addDogBtn.addEventListener('click', addDogForm);
234
+
235
+ // Tab switching
236
+ tabBtns.forEach(btn => {
237
+ btn.addEventListener('click', () => {
238
+ const tabId = btn.getAttribute('data-tab');
239
+
240
+ // Update active tab button
241
+ tabBtns.forEach(tb => {
242
+ tb.classList.remove('text-blue-600', 'border-blue-600');
243
+ tb.classList.add('text-gray-500');
244
+ });
245
+ btn.classList.add('text-blue-600', 'border-blue-600');
246
+ btn.classList.remove('text-gray-500');
247
+
248
+ // Show selected tab content
249
+ tabContents.forEach(content => {
250
+ content.classList.remove('active');
251
+ });
252
+ document.getElementById(tabId).classList.add('active');
253
+ });
254
+ });
255
+
256
+ // Functions
257
+ function addDogForm() {
258
+ if (dogCount >= MAX_DOGS) {
259
+ alert(`Maximum of ${MAX_DOGS} dogs allowed.`);
260
+ return;
261
+ }
262
+
263
+ const dogNum = dogCount + 1;
264
+ const dogId = `dog-${dogNum}`;
265
+
266
+ // Initialize dog data
267
+ dogsData[dogCount] = {
268
+ id: dogId,
269
+ name: '',
270
+ form: 5,
271
+ speed: 5,
272
+ consistency: 5,
273
+ track: 5,
274
+ recency: 5
275
+ };
276
+
277
+ // Create form HTML
278
+ const dogForm = document.createElement('div');
279
+ dogForm.className = 'bg-white rounded-lg shadow-md p-6';
280
+ dogForm.id = dogId;
281
+ dogForm.innerHTML = `
282
+ <div class="flex justify-between items-center mb-4">
283
+ <h3 class="text-lg font-medium text-gray-800">Dog ${dogNum}</h3>
284
+ <button class="remove-dog-btn text-red-500 hover:text-red-700" data-dog-id="${dogId}">
285
+ Remove
286
+ </button>
287
+ </div>
288
+
289
+ <div class="mb-4">
290
+ <label for="${dogId}-name" class="block text-sm font-medium text-gray-700 mb-1">Dog Name</label>
291
+ <input type="text" id="${dogId}-name" class="dog-name-input w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="Enter dog name">
292
+ </div>
293
+
294
+ ${METRICS.map((metric, idx) => `
295
+ <div class="mb-3">
296
+ <div class="flex justify-between mb-1">
297
+ <label for="${dogId}-${metric}" class="block text-sm font-medium text-gray-700">
298
+ ${METRIC_LABELS[idx]}
299
+ <span class="tooltip">
300
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
301
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
302
+ </svg>
303
+ <span class="tooltiptext">${getMetricTooltip(METRIC_LABELS[idx])}</span>
304
+ </span>
305
+ </label>
306
+ <span id="${dogId}-${metric}-value" class="text-sm font-medium text-blue-600">5.0</span>
307
+ </div>
308
+ <input type="range" id="${dogId}-${metric}" class="w-full metric-slider" min="0" max="10" step="0.1" value="5" data-dog-id="${dogId}" data-metric="${metric}">
309
+ </div>
310
+ `).join('')}
311
+ `;
312
+
313
+ dogFormsContainer.appendChild(dogForm);
314
+ dogCount++;
315
+
316
+ // Add event listeners for this dog's inputs
317
+ document.getElementById(`${dogId}-name`).addEventListener('input', updateDogName);
318
+
319
+ METRICS.forEach(metric => {
320
+ const slider = document.getElementById(`${dogId}-${metric}`);
321
+ slider.addEventListener('input', updateMetricValue);
322
+ });
323
+
324
+ // Add event listener for remove button
325
+ document.querySelector(`button[data-dog-id="${dogId}"]`).addEventListener('click', removeDogForm);
326
+ }
327
+
328
+ function getMetricTooltip(metric) {
329
+ const tips = {
330
+ 'Form': 'Recent race positions (higher values for better positions)',
331
+ 'Speed': 'Recent race times compared to competitors (higher for faster times)',
332
+ 'Consistency': 'How reliably the dog performs in races (higher for more consistent results)',
333
+ 'Track': 'Experience and performance at this particular track (higher for better track history)',
334
+ 'Recency': 'How recent and relevant the form data is (higher for more recent good form)'
335
+ };
336
+ return tips[metric] || '';
337
+ }
338
+
339
+ function updateDogName(e) {
340
+ const dogId = e.target.closest('.bg-white').id;
341
+ const dogIndex = dogsData.findIndex(dog => dog.id === dogId);
342
+
343
+ if (dogIndex !== -1) {
344
+ dogsData[dogIndex].name = e.target.value;
345
+ }
346
+ }
347
+
348
+ function updateMetricValue(e) {
349
+ const value = parseFloat(e.target.value);
350
+ const metric = e.target.getAttribute('data-metric');
351
+ const dogId = e.target.getAttribute('data-dog-id');
352
+ const dogIndex = dogsData.findIndex(dog => dog.id === dogId);
353
+
354
+ // Update the displayed value
355
+ document.getElementById(`${dogId}-${metric}-value`).textContent = value.toFixed(1);
356
+
357
+ // Update the data model
358
+ if (dogIndex !== -1) {
359
+ dogsData[dogIndex][metric] = value;
360
+ }
361
+ }
362
+
363
+ function removeDogForm(e) {
364
+ const dogId = e.target.getAttribute('data-dog-id');
365
+ const dogIndex = dogsData.findIndex(dog => dog.id === dogId);
366
+
367
+ if (dogIndex !== -1) {
368
+ // Remove from DOM
369
+ document.getElementById(dogId).remove();
370
+
371
+ // Remove from data model
372
+ dogsData.splice(dogIndex, 1);
373
+ dogCount--;
374
+
375
+ // Reindex remaining dogs
376
+ const remainingForms = document.querySelectorAll('#dogFormsContainer > div');
377
+ remainingForms.forEach((form, index) => {
378
+ const newDogNum = index + 1;
379
+ form.querySelector('h3').textContent = `Dog ${newDogNum}`;
380
+ });
381
+ }
382
+ }
383
+
384
+ function clearAllData() {
385
+ // Clear all dog forms
386
+ dogFormsContainer.innerHTML = '';
387
+
388
+ // Reset data
389
+ dogsData = [];
390
+ dogCount = 0;
391
+
392
+ // Reinitialize with 6 dogs
393
+ for (let i = 0; i < 6; i++) {
394
+ addDogForm();
395
+ }
396
+
397
+ // Clear results
398
+ resultsText.innerHTML = '<p class="text-gray-500 italic">No predictions calculated yet. Enter dog data and click "Calculate Predictions".</p>';
399
+
400
+ // Clear charts
401
+ if (probabilityChart) {
402
+ probabilityChart.destroy();
403
+ probabilityChart = null;
404
+ }
405
+
406
+ if (radarChart) {
407
+ radarChart.destroy();
408
+ radarChart = null;
409
+ }
410
+
411
+ // Switch to input tab
412
+ document.querySelector('.tab-btn[data-tab="input"]').click();
413
+ }
414
+
415
+ function loadSampleData() {
416
+ // Sample data based on the race card
417
+ const sampleData = [
418
+ { name: "DA BOLD ROSE", form: 6.5, speed: 7.0, consistency: 8.0, track: 8.0, recency: 7.0 },
419
+ { name: "DOUBLE DROP", form: 7.0, speed: 8.5, consistency: 9.0, track: 8.0, recency: 7.5 },
420
+ { name: "QUENAGH MANILLA", form: 4.5, speed: 6.0, consistency: 6.5, track: 7.0, recency: 6.0 },
421
+ { name: "FLASHING MELODY", form: 7.0, speed: 8.5, consistency: 7.5, track: 7.5, recency: 8.0 },
422
+ { name: "DRYLAND ROXY", form: 6.0, speed: 6.5, consistency: 7.0, track: 7.0, recency: 6.5 },
423
+ { name: "GEM FRIENDLY", form: 9.5, speed: 8.0, consistency: 9.5, track: 8.0, recency: 9.0 }
424
+ ];
425
+
426
+ // Clear existing data
427
+ clearAllData();
428
+
429
+ // Load sample data
430
+ sampleData.forEach((data, index) => {
431
+ if (index < dogCount) {
432
+ const dogId = dogsData[index].id;
433
+
434
+ // Set name
435
+ document.getElementById(`${dogId}-name`).value = data.name;
436
+ dogsData[index].name = data.name;
437
+
438
+ // Set metrics
439
+ METRICS.forEach(metric => {
440
+ document.getElementById(`${dogId}-${metric}`).value = data[metric];
441
+ document.getElementById(`${dogId}-${metric}-value`).textContent = data[metric].toFixed(1);
442
+ dogsData[index][metric] = data[metric];
443
+ });
444
+ }
445
+ });
446
+ }
447
+
448
+ function calculatePredictions() {
449
+ // Filter out dogs without names
450
+ const validDogs = dogsData.filter(dog => dog.name.trim() !== '');
451
+
452
+ if (validDogs.length < 2) {
453
+ alert("Please enter data for at least 2 dogs.");
454
+ return;
455
+ }
456
+
457
+ // Calculate scores
458
+ const results = validDogs.map(dog => {
459
+ const score = METRICS.reduce((sum, metric, idx) => {
460
+ return sum + (dog[metric] * METRIC_WEIGHTS[idx]);
461
+ }, 0);
462
+
463
+ return {
464
+ name: dog.name,
465
+ score: score,
466
+ form: dog.form,
467
+ speed: dog.speed,
468
+ consistency: dog.consistency,
469
+ track: dog.track,
470
+ recency: dog.recency
471
+ };
472
+ });
473
+
474
+ // Calculate win percentages
475
+ const totalScore = results.reduce((sum, dog) => sum + dog.score, 0);
476
+
477
+ results.forEach(dog => {
478
+ dog.percentage = (dog.score / totalScore) * 100;
479
+ });
480
+
481
+ // Sort by percentage (descending)
482
+ results.sort((a, b) => b.percentage - a.percentage);
483
+
484
+ // Display results
485
+ displayTextResults(results);
486
+ createCharts(results);
487
+
488
+ // Switch to results tab
489
+ document.querySelector('.tab-btn[data-tab="results"]').click();
490
+ }
491
+
492
+ function displayTextResults(results) {
493
+ let html = `
494
+ <h3 class="text-xl font-bold mb-4 text-center">GREYHOUND RACE WIN PROBABILITY PREDICTIONS</h3>
495
+ <div class="border-b border-gray-300 mb-6"></div>
496
+ `;
497
+
498
+ // Display each dog's results
499
+ results.forEach((dog, index) => {
500
+ html += `
501
+ <div class="mb-6 pb-4 ${index < results.length - 1 ? 'border-b border-gray-200' : ''}">
502
+ <div class="flex items-center mb-2">
503
+ <span class="bg-blue-600 text-white font-bold rounded-full w-8 h-8 flex items-center justify-center mr-3">${index + 1}</span>
504
+ <h4 class="text-lg font-semibold">${dog.name}</h4>
505
+ </div>
506
+
507
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
508
+ <div>
509
+ <p class="text-sm text-gray-600">Win Probability: <span class="font-medium text-blue-600">${dog.percentage.toFixed(2)}%</span></p>
510
+ <p class="text-sm text-gray-600">Total Score: <span class="font-medium text-blue-600">${dog.score.toFixed(2)}</span></p>
511
+ </div>
512
+
513
+ <div>
514
+ <p class="text-sm font-medium mb-1">Metrics (0-10 scale):</p>
515
+ <div class="grid grid-cols-2 gap-2">
516
+ <p class="text-sm text-gray-600">Form: <span class="font-medium">${dog.form.toFixed(1)}</span></p>
517
+ <p class="text-sm text-gray-600">Speed: <span class="font-medium">${dog.speed.toFixed(1)}</span></p>
518
+ <p class="text-sm text-gray-600">Consistency: <span class="font-medium">${dog.consistency.toFixed(1)}</span></p>
519
+ <p class="text-sm text-gray-600">Track: <span class="font-medium">${dog.track.toFixed(1)}</span></p>
520
+ <p class="text-sm text-gray-600">Recency: <span class="font-medium">${dog.recency.toFixed(1)}</span></p>
521
+ </div>
522
+ </div>
523
+ </div>
524
+ </div>
525
+ `;
526
+ });
527
+
528
+ // Summary
529
+ html += `
530
+ <div class="mt-6 pt-4 border-t border-gray-300">
531
+ <h4 class="text-lg font-semibold mb-2">ANALYSIS SUMMARY</h4>
532
+ <div class="border-b border-gray-300 mb-3 w-1/4"></div>
533
+ `;
534
+
535
+ if (results[0].percentage - results[1].percentage > 5) {
536
+ html += `
537
+ <p class="text-gray-700">${results[0].name} is the clear favorite with a ${results[0].percentage.toFixed(2)}% chance of winning.</p>
538
+ `;
539
+ } else {
540
+ html += `
541
+ <p class="text-gray-700">The race appears competitive between ${results[0].name} (${results[0].percentage.toFixed(2)}%) and ${results[1].name} (${results[1].percentage.toFixed(2)}%).</p>
542
+ `;
543
+ }
544
+
545
+ html += `</div>`;
546
+
547
+ resultsText.innerHTML = html;
548
+ }
549
+
550
+ function createCharts(results) {
551
+ // Destroy existing charts if they exist
552
+ if (probabilityChart) {
553
+ probabilityChart.destroy();
554
+ }
555
+ if (radarChart) {
556
+ radarChart.destroy();
557
+ }
558
+
559
+ // Create probability chart
560
+ createProbabilityChart(results);
561
+
562
+ // Create radar chart (only for top 3 dogs)
563
+ createRadarChart(results.slice(0, 3));
564
+ }
565
+
566
+ function createProbabilityChart(results) {
567
+ const ctx = document.getElementById('probabilityChart').getContext('2d');
568
+
569
+ // Limit to top 6 dogs for better visualization
570
+ const displayResults = results.slice(0, 6);
571
+
572
+ const data = {
573
+ labels: displayResults.map(dog => dog.name),
574
+ datasets: [{
575
+ label: 'Win Probability %',
576
+ data: displayResults.map(dog => dog.percentage),
577
+ backgroundColor: [
578
+ 'rgba(239, 68, 68, 0.7)',
579
+ 'rgba(249, 115, 22, 0.7)',
580
+ 'rgba(234, 179, 8, 0.7)',
581
+ 'rgba(16, 185, 129, 0.7)',
582
+ 'rgba(59, 130, 246, 0.7)',
583
+ 'rgba(139, 92, 246, 0.7)'
584
+ ],
585
+ borderColor: [
586
+ 'rgba(239, 68, 68, 1)',
587
+ 'rgba(249, 115, 22, 1)',
588
+ 'rgba(234, 179, 8, 1)',
589
+ 'rgba(16, 185, 129, 1)',
590
+ 'rgba(59, 130, 246, 1)',
591
+ 'rgba(139, 92, 246, 1)'
592
+ ],
593
+ borderWidth: 1
594
+ }]
595
+ };
596
+
597
+ const options = {
598
+ indexAxis: 'y',
599
+ responsive: true,
600
+ maintainAspectRatio: false,
601
+ plugins: {
602
+ legend: {
603
+ display: false
604
+ },
605
+ tooltip: {
606
+ callbacks: {
607
+ label: function(context) {
608
+ return `${context.parsed.x}% win probability`;
609
+ }
610
+ }
611
+ },
612
+ datalabels: {
613
+ anchor: 'end',
614
+ align: 'end',
615
+ formatter: (value) => `${value.toFixed(1)}%`,
616
+ color: '#1f2937',
617
+ font: {
618
+ weight: 'bold'
619
+ }
620
+ }
621
+ },
622
+ scales: {
623
+ x: {
624
+ beginAtZero: true,
625
+ max: Math.min(100, Math.ceil(Math.max(...displayResults.map(dog => dog.percentage)) * 1.2)),
626
+ title: {
627
+ display: true,
628
+ text: 'Win Probability (%)'
629
+ }
630
+ },
631
+ y: {
632
+ ticks: {
633
+ autoSkip: false
634
+ }
635
+ }
636
+ }
637
+ };
638
+
639
+ probabilityChart = new Chart(ctx, {
640
+ type: 'bar',
641
+ data: data,
642
+ options: options,
643
+ plugins: [ChartDataLabels]
644
+ });
645
+ }
646
+
647
+ function createRadarChart(results) {
648
+ const ctx = document.getElementById('radarChart').getContext('2d');
649
+
650
+ const data = {
651
+ labels: METRIC_LABELS,
652
+ datasets: results.map((dog, index) => ({
653
+ label: dog.name,
654
+ data: [dog.form, dog.speed, dog.consistency, dog.track, dog.recency],
655
+ backgroundColor: [
656
+ 'rgba(239, 68, 68, 0.2)',
657
+ 'rgba(234, 179, 8, 0.2)',
658
+ 'rgba(59, 130, 246, 0.2)'
659
+ ][index],
660
+ borderColor: [
661
+ 'rgba(239, 68, 68, 1)',
662
+ 'rgba(234, 179, 8, 1)',
663
+ 'rgba(59, 130, 246, 1)'
664
+ ][index],
665
+ borderWidth: 2,
666
+ pointBackgroundColor: [
667
+ 'rgba(239, 68, 68, 1)',
668
+ 'rgba(234, 179, 8, 1)',
669
+ 'rgba(59, 130, 246, 1)'
670
+ ][index],
671
+ pointRadius: 4
672
+ }))
673
+ };
674
+
675
+ const options = {
676
+ responsive: true,
677
+ maintainAspectRatio: false,
678
+ plugins: {
679
+ legend: {
680
+ position: 'top',
681
+ },
682
+ tooltip: {
683
+ callbacks: {
684
+ label: function(context) {
685
+ return `${context.dataset.label}: ${context.raw}`;
686
+ }
687
+ }
688
+ }
689
+ },
690
+ scales: {
691
+ r: {
692
+ angleLines: {
693
+ display: true
694
+ },
695
+ suggestedMin: 0,
696
+ suggestedMax: 10,
697
+ ticks: {
698
+ stepSize: 2
699
+ }
700
+ }
701
+ }
702
+ };
703
+
704
+ radarChart = new Chart(ctx, {
705
+ type: 'radar',
706
+ data: data,
707
+ options: options
708
+ });
709
+ }
710
+ });
711
+ </script>
712
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LMLK/greyhound-race-predictor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
713
+ </html>