Add 3 files
Browse files- README.md +7 -5
- index.html +677 -19
- prompts.txt +2 -0
README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 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: stp
|
| 3 |
+
emoji: π³
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: green
|
| 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,677 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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>Soccer Team Performance Predictor</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/brain/0.6.3/brain.min.js"></script>
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
| 10 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 11 |
+
<style>
|
| 12 |
+
.neuron {
|
| 13 |
+
transition: all 0.3s ease;
|
| 14 |
+
}
|
| 15 |
+
.neuron:hover {
|
| 16 |
+
transform: scale(1.1);
|
| 17 |
+
}
|
| 18 |
+
.network-container {
|
| 19 |
+
perspective: 1000px;
|
| 20 |
+
}
|
| 21 |
+
.layer {
|
| 22 |
+
transform-style: preserve-3d;
|
| 23 |
+
}
|
| 24 |
+
.connection {
|
| 25 |
+
stroke-dasharray: 1000;
|
| 26 |
+
stroke-dashoffset: 1000;
|
| 27 |
+
animation: draw 1.5s forwards;
|
| 28 |
+
}
|
| 29 |
+
@keyframes draw {
|
| 30 |
+
to {
|
| 31 |
+
stroke-dashoffset: 0;
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
.progress-ring__circle {
|
| 35 |
+
transition: stroke-dashoffset 0.35s;
|
| 36 |
+
transform: rotate(-90deg);
|
| 37 |
+
transform-origin: 50% 50%;
|
| 38 |
+
}
|
| 39 |
+
.team-card:hover {
|
| 40 |
+
transform: translateY(-5px);
|
| 41 |
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
| 42 |
+
}
|
| 43 |
+
.team-card {
|
| 44 |
+
transition: all 0.3s ease;
|
| 45 |
+
}
|
| 46 |
+
.soccer-pitch {
|
| 47 |
+
background: linear-gradient(to bottom, #4ade80, #22c55e);
|
| 48 |
+
position: relative;
|
| 49 |
+
overflow: hidden;
|
| 50 |
+
}
|
| 51 |
+
.soccer-pitch::before {
|
| 52 |
+
content: "";
|
| 53 |
+
position: absolute;
|
| 54 |
+
top: 0;
|
| 55 |
+
left: 50%;
|
| 56 |
+
width: 2px;
|
| 57 |
+
height: 100%;
|
| 58 |
+
background: white;
|
| 59 |
+
transform: translateX(-50%);
|
| 60 |
+
}
|
| 61 |
+
.soccer-pitch::after {
|
| 62 |
+
content: "";
|
| 63 |
+
position: absolute;
|
| 64 |
+
top: 50%;
|
| 65 |
+
left: 50%;
|
| 66 |
+
width: 100px;
|
| 67 |
+
height: 100px;
|
| 68 |
+
border: 2px solid white;
|
| 69 |
+
border-radius: 50%;
|
| 70 |
+
transform: translate(-50%, -50%);
|
| 71 |
+
}
|
| 72 |
+
</style>
|
| 73 |
+
</head>
|
| 74 |
+
<body class="bg-gray-50 min-h-screen">
|
| 75 |
+
<div class="container mx-auto px-4 py-8">
|
| 76 |
+
<header class="text-center mb-12">
|
| 77 |
+
<h1 class="text-4xl font-bold text-gray-800 mb-2">β½ Soccer Team Performance Predictor</h1>
|
| 78 |
+
<p class="text-gray-600 max-w-2xl mx-auto">A neural network that predicts team loss probability based on key soccer performance metrics</p>
|
| 79 |
+
</header>
|
| 80 |
+
|
| 81 |
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
| 82 |
+
<!-- Configuration Panel -->
|
| 83 |
+
<div class="bg-white rounded-xl shadow-md p-6">
|
| 84 |
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Network Configuration</h2>
|
| 85 |
+
<div class="space-y-4">
|
| 86 |
+
<div>
|
| 87 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Hidden Layers</label>
|
| 88 |
+
<input type="range" id="hiddenLayersInput" min="1" max="5" value="3" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
|
| 89 |
+
<div class="flex justify-between text-xs text-gray-500">
|
| 90 |
+
<span>1</span>
|
| 91 |
+
<span>2</span>
|
| 92 |
+
<span>3</span>
|
| 93 |
+
<span>4</span>
|
| 94 |
+
<span>5</span>
|
| 95 |
+
</div>
|
| 96 |
+
</div>
|
| 97 |
+
|
| 98 |
+
<div>
|
| 99 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Neurons per Layer</label>
|
| 100 |
+
<input type="range" id="neuronsPerLayerInput" min="3" max="10" value="6" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
|
| 101 |
+
<div class="flex justify-between text-xs text-gray-500">
|
| 102 |
+
<span>3</span>
|
| 103 |
+
<span>4</span>
|
| 104 |
+
<span>5</span>
|
| 105 |
+
<span>6</span>
|
| 106 |
+
<span>7</span>
|
| 107 |
+
<span>8</span>
|
| 108 |
+
<span>9</span>
|
| 109 |
+
<span>10</span>
|
| 110 |
+
</div>
|
| 111 |
+
</div>
|
| 112 |
+
|
| 113 |
+
<div>
|
| 114 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Learning Rate</label>
|
| 115 |
+
<input type="range" id="learningRateInput" min="0.01" max="0.5" step="0.01" value="0.2" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
|
| 116 |
+
<div class="flex justify-between text-xs text-gray-500">
|
| 117 |
+
<span>0.01</span>
|
| 118 |
+
<span>0.25</span>
|
| 119 |
+
<span>0.5</span>
|
| 120 |
+
</div>
|
| 121 |
+
</div>
|
| 122 |
+
|
| 123 |
+
<div class="flex space-x-3 pt-2">
|
| 124 |
+
<button id="trainBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg flex items-center justify-center">
|
| 125 |
+
<i class="fas fa-brain mr-2"></i> Train Network
|
| 126 |
+
</button>
|
| 127 |
+
<button id="resetBtn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-lg flex items-center justify-center">
|
| 128 |
+
<i class="fas fa-redo mr-2"></i> Reset
|
| 129 |
+
</button>
|
| 130 |
+
</div>
|
| 131 |
+
</div>
|
| 132 |
+
|
| 133 |
+
<div class="mt-6">
|
| 134 |
+
<h3 class="text-sm font-medium text-gray-700 mb-2">Training Status</h3>
|
| 135 |
+
<div id="trainingStatus" class="text-sm text-gray-600 bg-gray-100 p-3 rounded-lg">
|
| 136 |
+
Network not trained yet
|
| 137 |
+
</div>
|
| 138 |
+
</div>
|
| 139 |
+
</div>
|
| 140 |
+
|
| 141 |
+
<!-- Network Visualization -->
|
| 142 |
+
<div class="bg-white rounded-xl shadow-md p-6">
|
| 143 |
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Network Architecture</h2>
|
| 144 |
+
<div id="networkVisualization" class="network-container h-64 flex justify-center items-center">
|
| 145 |
+
<svg id="networkSvg" width="100%" height="100%" viewBox="0 0 500 300"></svg>
|
| 146 |
+
</div>
|
| 147 |
+
|
| 148 |
+
<div class="mt-6">
|
| 149 |
+
<h3 class="text-sm font-medium text-gray-700 mb-2">Error Over Time</h3>
|
| 150 |
+
<div class="bg-gray-100 p-2 rounded-lg">
|
| 151 |
+
<canvas id="errorChart" height="150"></canvas>
|
| 152 |
+
</div>
|
| 153 |
+
</div>
|
| 154 |
+
</div>
|
| 155 |
+
|
| 156 |
+
<!-- Prediction Panel -->
|
| 157 |
+
<div class="bg-white rounded-xl shadow-md p-6">
|
| 158 |
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Make a Prediction</h2>
|
| 159 |
+
<div class="space-y-3">
|
| 160 |
+
<div>
|
| 161 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Goals Conceded per Game</label>
|
| 162 |
+
<input id="goalsConcededInput" type="number" step="0.1" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" placeholder="1.2">
|
| 163 |
+
</div>
|
| 164 |
+
<div>
|
| 165 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Shots on Target per Game</label>
|
| 166 |
+
<input id="shotsOnTargetInput" type="number" step="0.1" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" placeholder="5.5">
|
| 167 |
+
</div>
|
| 168 |
+
<div>
|
| 169 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Pass Accuracy %</label>
|
| 170 |
+
<input id="passAccuracyInput" type="number" step="0.1" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" placeholder="85.0">
|
| 171 |
+
</div>
|
| 172 |
+
<div>
|
| 173 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Tackles per Game</label>
|
| 174 |
+
<input id="tacklesInput" type="number" step="0.1" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" placeholder="15.0">
|
| 175 |
+
</div>
|
| 176 |
+
|
| 177 |
+
<button id="predictBtn" class="w-full bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg mt-4 flex items-center justify-center">
|
| 178 |
+
<i class="fas fa-calculator mr-2"></i> Predict Loss Probability
|
| 179 |
+
</button>
|
| 180 |
+
|
| 181 |
+
<div id="predictionResult" class="mt-4 p-4 rounded-lg bg-green-50 hidden">
|
| 182 |
+
<h4 class="font-medium text-green-800 mb-1">Prediction Result</h4>
|
| 183 |
+
<p id="predictionText" class="text-green-600"></p>
|
| 184 |
+
<div class="mt-3 flex items-center">
|
| 185 |
+
<div class="w-12 h-12 mr-3">
|
| 186 |
+
<svg class="progress-ring" width="48" height="48">
|
| 187 |
+
<circle class="progress-ring__circle" stroke="#E5E7EB" stroke-width="4" fill="transparent" r="20" cx="24" cy="24"/>
|
| 188 |
+
<circle class="progress-ring__circle" stroke="#10B981" stroke-width="4" fill="transparent" r="20" cx="24" cy="24"/>
|
| 189 |
+
</svg>
|
| 190 |
+
</div>
|
| 191 |
+
<div class="text-2xl font-bold text-green-600" id="predictionPercentage">0%</div>
|
| 192 |
+
</div>
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
</div>
|
| 196 |
+
</div>
|
| 197 |
+
|
| 198 |
+
<!-- Team Cards -->
|
| 199 |
+
<div class="mt-12">
|
| 200 |
+
<h2 class="text-2xl font-bold text-gray-800 mb-6">Top European Clubs</h2>
|
| 201 |
+
<div id="teamPredictions" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"></div>
|
| 202 |
+
</div>
|
| 203 |
+
</div>
|
| 204 |
+
|
| 205 |
+
<script>
|
| 206 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 207 |
+
// Configuration
|
| 208 |
+
const config = {
|
| 209 |
+
hiddenLayers: 3,
|
| 210 |
+
neuronsPerLayer: 6,
|
| 211 |
+
learningRate: 0.2,
|
| 212 |
+
iterations: 20000,
|
| 213 |
+
errorThresh: 0.005
|
| 214 |
+
};
|
| 215 |
+
|
| 216 |
+
// Sample soccer teams data
|
| 217 |
+
let soccerTeams = [
|
| 218 |
+
{ name: "Manchester City", logo: "π΅", goalsConceded: 0.8, shotsOnTarget: 6.7, passAccuracy: 89.2, tackles: 12.3 },
|
| 219 |
+
{ name: "Real Madrid", logo: "βͺ", goalsConceded: 1.1, shotsOnTarget: 5.9, passAccuracy: 87.5, tackles: 14.2 },
|
| 220 |
+
{ name: "Bayern Munich", logo: "π΄", goalsConceded: 0.9, shotsOnTarget: 6.3, passAccuracy: 88.1, tackles: 13.7 },
|
| 221 |
+
{ name: "Liverpool", logo: "π΄", goalsConceded: 1.2, shotsOnTarget: 6.1, passAccuracy: 85.7, tackles: 15.8 },
|
| 222 |
+
{ name: "Paris Saint-Germain", logo: "π΅", goalsConceded: 1.0, shotsOnTarget: 5.8, passAccuracy: 86.9, tackles: 11.9 },
|
| 223 |
+
{ name: "Barcelona", logo: "π΅π΄", goalsConceded: 1.3, shotsOnTarget: 5.5, passAccuracy: 87.8, tackles: 12.5 },
|
| 224 |
+
{ name: "Chelsea", logo: "π΅", goalsConceded: 1.4, shotsOnTarget: 4.9, passAccuracy: 84.3, tackles: 16.2 },
|
| 225 |
+
{ name: "AC Milan", logo: "β«π΄", goalsConceded: 1.2, shotsOnTarget: 4.7, passAccuracy: 83.5, tackles: 14.9 }
|
| 226 |
+
];
|
| 227 |
+
|
| 228 |
+
// Initialize network
|
| 229 |
+
let net = new brain.NeuralNetwork();
|
| 230 |
+
let errorChart;
|
| 231 |
+
let trainingData = generateTrainingData();
|
| 232 |
+
|
| 233 |
+
// DOM Elements
|
| 234 |
+
const hiddenLayersInput = document.getElementById('hiddenLayersInput');
|
| 235 |
+
const neuronsPerLayerInput = document.getElementById('neuronsPerLayerInput');
|
| 236 |
+
const learningRateInput = document.getElementById('learningRateInput');
|
| 237 |
+
const trainBtn = document.getElementById('trainBtn');
|
| 238 |
+
const resetBtn = document.getElementById('resetBtn');
|
| 239 |
+
const goalsConcededInput = document.getElementById('goalsConcededInput');
|
| 240 |
+
const shotsOnTargetInput = document.getElementById('shotsOnTargetInput');
|
| 241 |
+
const passAccuracyInput = document.getElementById('passAccuracyInput');
|
| 242 |
+
const tacklesInput = document.getElementById('tacklesInput');
|
| 243 |
+
const predictBtn = document.getElementById('predictBtn');
|
| 244 |
+
const predictionResult = document.getElementById('predictionResult');
|
| 245 |
+
const predictionText = document.getElementById('predictionText');
|
| 246 |
+
const predictionPercentage = document.getElementById('predictionPercentage');
|
| 247 |
+
const teamPredictions = document.getElementById('teamPredictions');
|
| 248 |
+
const trainingStatus = document.getElementById('trainingStatus');
|
| 249 |
+
|
| 250 |
+
// Initialize UI
|
| 251 |
+
initUI();
|
| 252 |
+
drawNetwork();
|
| 253 |
+
initChart();
|
| 254 |
+
renderTeamCards();
|
| 255 |
+
|
| 256 |
+
// Event Listeners
|
| 257 |
+
hiddenLayersInput.addEventListener('input', updateConfig);
|
| 258 |
+
neuronsPerLayerInput.addEventListener('input', updateConfig);
|
| 259 |
+
learningRateInput.addEventListener('input', updateConfig);
|
| 260 |
+
trainBtn.addEventListener('click', trainNetwork);
|
| 261 |
+
resetBtn.addEventListener('click', resetNetwork);
|
| 262 |
+
predictBtn.addEventListener('click', predictLossProbability);
|
| 263 |
+
|
| 264 |
+
// Functions
|
| 265 |
+
function generateTrainingData() {
|
| 266 |
+
// Generate synthetic training data based on realistic soccer stats
|
| 267 |
+
const data = [];
|
| 268 |
+
|
| 269 |
+
for (let i = 0; i < 100; i++) {
|
| 270 |
+
// Generate random but realistic soccer stats
|
| 271 |
+
const goalsConceded = 0.5 + Math.random() * 2.5; // Between 0.5 and 3.0
|
| 272 |
+
const shotsOnTarget = 2.0 + Math.random() * 6.0; // Between 2.0 and 8.0
|
| 273 |
+
const passAccuracy = 70.0 + Math.random() * 25.0; // Between 70% and 95%
|
| 274 |
+
const tackles = 8.0 + Math.random() * 12.0; // Between 8 and 20
|
| 275 |
+
|
| 276 |
+
// Calculate a synthetic loss probability based on these stats
|
| 277 |
+
// Better stats (fewer goals conceded, more shots on target, higher pass accuracy, more tackles)
|
| 278 |
+
// should lead to lower loss probability
|
| 279 |
+
let lossProbability =
|
| 280 |
+
(goalsConceded / 3.0) * 0.4 + // Goals conceded contributes 40%
|
| 281 |
+
((8.0 - shotsOnTarget) / 6.0) * 0.3 + // Shots on target contributes 30%
|
| 282 |
+
((95.0 - passAccuracy) / 25.0) * 0.2 + // Pass accuracy contributes 20%
|
| 283 |
+
((20.0 - tackles) / 12.0) * 0.1; // Tackles contributes 10%
|
| 284 |
+
|
| 285 |
+
// Add some randomness
|
| 286 |
+
lossProbability += (Math.random() - 0.5) * 0.1;
|
| 287 |
+
|
| 288 |
+
// Ensure between 0 and 1
|
| 289 |
+
lossProbability = Math.max(0, Math.min(1, lossProbability));
|
| 290 |
+
|
| 291 |
+
data.push({
|
| 292 |
+
input: {
|
| 293 |
+
goalsConceded: normalize(goalsConceded, 0.5, 3.0),
|
| 294 |
+
shotsOnTarget: normalize(shotsOnTarget, 2.0, 8.0),
|
| 295 |
+
passAccuracy: normalize(passAccuracy, 70.0, 95.0),
|
| 296 |
+
tackles: normalize(tackles, 8.0, 20.0)
|
| 297 |
+
},
|
| 298 |
+
output: {
|
| 299 |
+
loss: lossProbability
|
| 300 |
+
}
|
| 301 |
+
});
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
return data;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
function normalize(value, min, max) {
|
| 308 |
+
return (value - min) / (max - min);
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
function denormalize(value, min, max) {
|
| 312 |
+
return value * (max - min) + min;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
function initUI() {
|
| 316 |
+
// Set initial values from config
|
| 317 |
+
hiddenLayersInput.value = config.hiddenLayers;
|
| 318 |
+
neuronsPerLayerInput.value = config.neuronsPerLayer;
|
| 319 |
+
learningRateInput.value = config.learningRate;
|
| 320 |
+
|
| 321 |
+
// Initialize prediction progress ring
|
| 322 |
+
const circle = document.querySelector('.progress-ring__circle:last-child');
|
| 323 |
+
const radius = circle.r.baseVal.value;
|
| 324 |
+
const circumference = radius * 2 * Math.PI;
|
| 325 |
+
|
| 326 |
+
circle.style.strokeDasharray = circumference;
|
| 327 |
+
circle.style.strokeDashoffset = circumference;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
function updateConfig() {
|
| 331 |
+
config.hiddenLayers = parseInt(hiddenLayersInput.value);
|
| 332 |
+
config.neuronsPerLayer = parseInt(neuronsPerLayerInput.value);
|
| 333 |
+
config.learningRate = parseFloat(learningRateInput.value);
|
| 334 |
+
|
| 335 |
+
drawNetwork();
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
function resetNetwork() {
|
| 339 |
+
net = new brain.NeuralNetwork();
|
| 340 |
+
trainingStatus.textContent = "Network reset - not trained";
|
| 341 |
+
trainingStatus.className = "text-sm text-gray-600 bg-gray-100 p-3 rounded-lg";
|
| 342 |
+
|
| 343 |
+
// Reset chart
|
| 344 |
+
if (errorChart) {
|
| 345 |
+
errorChart.data.labels = [];
|
| 346 |
+
errorChart.data.datasets[0].data = [];
|
| 347 |
+
errorChart.update();
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
// Hide prediction result
|
| 351 |
+
predictionResult.classList.add('hidden');
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
function trainNetwork() {
|
| 355 |
+
trainBtn.disabled = true;
|
| 356 |
+
trainBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Training...';
|
| 357 |
+
trainingStatus.textContent = "Training in progress...";
|
| 358 |
+
trainingStatus.className = "text-sm text-green-600 bg-green-100 p-3 rounded-lg";
|
| 359 |
+
|
| 360 |
+
// Configure network
|
| 361 |
+
net = new brain.NeuralNetwork({
|
| 362 |
+
hiddenLayers: [Array(config.hiddenLayers).fill(config.neuronsPerLayer)].flat(),
|
| 363 |
+
learningRate: config.learningRate,
|
| 364 |
+
iterations: config.iterations,
|
| 365 |
+
errorThresh: config.errorThresh,
|
| 366 |
+
log: true,
|
| 367 |
+
logPeriod: 1000,
|
| 368 |
+
callback: function(info) {
|
| 369 |
+
updateTrainingStatus(info.iterations, info.error);
|
| 370 |
+
updateChart(info.iterations, info.error);
|
| 371 |
+
},
|
| 372 |
+
callbackPeriod: 1000
|
| 373 |
+
});
|
| 374 |
+
|
| 375 |
+
// Train in a timeout to allow UI to update
|
| 376 |
+
setTimeout(() => {
|
| 377 |
+
net.train(trainingData, (err, info) => {
|
| 378 |
+
trainBtn.disabled = false;
|
| 379 |
+
trainBtn.innerHTML = '<i class="fas fa-brain mr-2"></i> Train Network';
|
| 380 |
+
|
| 381 |
+
if (err) {
|
| 382 |
+
trainingStatus.textContent = "Training failed: " + err;
|
| 383 |
+
trainingStatus.className = "text-sm text-red-600 bg-red-100 p-3 rounded-lg";
|
| 384 |
+
} else {
|
| 385 |
+
trainingStatus.textContent = `Training complete! Final error: ${info.error.toFixed(6)} after ${info.iterations} iterations`;
|
| 386 |
+
trainingStatus.className = "text-sm text-green-600 bg-green-100 p-3 rounded-lg";
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
// Animate network connections
|
| 390 |
+
animateNetwork();
|
| 391 |
+
});
|
| 392 |
+
}, 100);
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
+
function updateTrainingStatus(iterations, error) {
|
| 396 |
+
trainingStatus.textContent = `Training... Iteration: ${iterations}, Error: ${error.toFixed(6)}`;
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
function initChart() {
|
| 400 |
+
const ctx = document.getElementById('errorChart').getContext('2d');
|
| 401 |
+
errorChart = new Chart(ctx, {
|
| 402 |
+
type: 'line',
|
| 403 |
+
data: {
|
| 404 |
+
labels: [],
|
| 405 |
+
datasets: [{
|
| 406 |
+
label: 'Training Error',
|
| 407 |
+
data: [],
|
| 408 |
+
borderColor: 'rgb(16, 185, 129)',
|
| 409 |
+
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
| 410 |
+
borderWidth: 2,
|
| 411 |
+
tension: 0.4,
|
| 412 |
+
fill: true
|
| 413 |
+
}]
|
| 414 |
+
},
|
| 415 |
+
options: {
|
| 416 |
+
responsive: true,
|
| 417 |
+
maintainAspectRatio: false,
|
| 418 |
+
scales: {
|
| 419 |
+
y: {
|
| 420 |
+
beginAtZero: true,
|
| 421 |
+
title: {
|
| 422 |
+
display: true,
|
| 423 |
+
text: 'Error'
|
| 424 |
+
}
|
| 425 |
+
},
|
| 426 |
+
x: {
|
| 427 |
+
title: {
|
| 428 |
+
display: true,
|
| 429 |
+
text: 'Iterations'
|
| 430 |
+
}
|
| 431 |
+
}
|
| 432 |
+
},
|
| 433 |
+
plugins: {
|
| 434 |
+
legend: {
|
| 435 |
+
display: false
|
| 436 |
+
},
|
| 437 |
+
tooltip: {
|
| 438 |
+
callbacks: {
|
| 439 |
+
label: function(context) {
|
| 440 |
+
return `Error: ${context.parsed.y.toFixed(6)}`;
|
| 441 |
+
}
|
| 442 |
+
}
|
| 443 |
+
}
|
| 444 |
+
}
|
| 445 |
+
}
|
| 446 |
+
});
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
function updateChart(iteration, error) {
|
| 450 |
+
errorChart.data.labels.push(iteration);
|
| 451 |
+
errorChart.data.datasets[0].data.push(error);
|
| 452 |
+
errorChart.update();
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
function drawNetwork() {
|
| 456 |
+
const svg = document.getElementById('networkSvg');
|
| 457 |
+
svg.innerHTML = '';
|
| 458 |
+
|
| 459 |
+
const width = 500;
|
| 460 |
+
const height = 300;
|
| 461 |
+
const layerCount = config.hiddenLayers + 2; // Input + hidden + output
|
| 462 |
+
const neuronRadius = 15;
|
| 463 |
+
|
| 464 |
+
// Draw layers
|
| 465 |
+
for (let layer = 0; layer < layerCount; layer++) {
|
| 466 |
+
const isInput = layer === 0;
|
| 467 |
+
const isOutput = layer === layerCount - 1;
|
| 468 |
+
|
| 469 |
+
let neuronCount;
|
| 470 |
+
if (isInput) neuronCount = 4; // 4 input features
|
| 471 |
+
else if (isOutput) neuronCount = 1; // 1 output (loss probability)
|
| 472 |
+
else neuronCount = config.neuronsPerLayer;
|
| 473 |
+
|
| 474 |
+
const layerX = 50 + (width - 100) * (layer / (layerCount - 1));
|
| 475 |
+
|
| 476 |
+
// Draw neurons
|
| 477 |
+
for (let n = 0; n < neuronCount; n++) {
|
| 478 |
+
const neuronY = height / 2 + (n - (neuronCount - 1) / 2) * 40;
|
| 479 |
+
|
| 480 |
+
// Neuron circle
|
| 481 |
+
const neuron = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
| 482 |
+
neuron.setAttribute("cx", layerX);
|
| 483 |
+
neuron.setAttribute("cy", neuronY);
|
| 484 |
+
neuron.setAttribute("r", neuronRadius);
|
| 485 |
+
neuron.setAttribute("class", "neuron");
|
| 486 |
+
|
| 487 |
+
if (isInput) {
|
| 488 |
+
neuron.setAttribute("fill", "#4ADE80"); // Green for input
|
| 489 |
+
} else if (isOutput) {
|
| 490 |
+
neuron.setAttribute("fill", "#3B82F6"); // Blue for output
|
| 491 |
+
} else {
|
| 492 |
+
neuron.setAttribute("fill", "#F59E0B"); // Yellow for hidden
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
svg.appendChild(neuron);
|
| 496 |
+
|
| 497 |
+
// Neuron label
|
| 498 |
+
if (isInput) {
|
| 499 |
+
const labels = ["Goals Conceded", "Shots on Target", "Pass %", "Tackles"];
|
| 500 |
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
| 501 |
+
text.setAttribute("x", layerX - 50);
|
| 502 |
+
text.setAttribute("y", neuronY + 5);
|
| 503 |
+
text.setAttribute("text-anchor", "end");
|
| 504 |
+
text.setAttribute("class", "text-xs font-medium fill-gray-700");
|
| 505 |
+
text.textContent = labels[n];
|
| 506 |
+
svg.appendChild(text);
|
| 507 |
+
} else if (isOutput) {
|
| 508 |
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
| 509 |
+
text.setAttribute("x", layerX + 50);
|
| 510 |
+
text.setAttribute("y", neuronY + 5);
|
| 511 |
+
text.setAttribute("text-anchor", "start");
|
| 512 |
+
text.setAttribute("class", "text-xs font-medium fill-gray-700");
|
| 513 |
+
text.textContent = "Loss %";
|
| 514 |
+
svg.appendChild(text);
|
| 515 |
+
}
|
| 516 |
+
}
|
| 517 |
+
}
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
function animateNetwork() {
|
| 521 |
+
const svg = document.getElementById('networkSvg');
|
| 522 |
+
const connections = [];
|
| 523 |
+
|
| 524 |
+
const width = 500;
|
| 525 |
+
const height = 300;
|
| 526 |
+
const layerCount = config.hiddenLayers + 2;
|
| 527 |
+
const neuronRadius = 15;
|
| 528 |
+
|
| 529 |
+
// Create connections between layers
|
| 530 |
+
for (let layer = 0; layer < layerCount - 1; layer++) {
|
| 531 |
+
const isFirstLayer = layer === 0;
|
| 532 |
+
const isLastLayer = layer === layerCount - 2;
|
| 533 |
+
|
| 534 |
+
let fromNeuronCount;
|
| 535 |
+
if (isFirstLayer) fromNeuronCount = 4;
|
| 536 |
+
else fromNeuronCount = config.neuronsPerLayer;
|
| 537 |
+
|
| 538 |
+
let toNeuronCount;
|
| 539 |
+
if (isLastLayer) toNeuronCount = 1;
|
| 540 |
+
else toNeuronCount = config.neuronsPerLayer;
|
| 541 |
+
|
| 542 |
+
const fromLayerX = 50 + (width - 100) * (layer / (layerCount - 1));
|
| 543 |
+
const toLayerX = 50 + (width - 100) * ((layer + 1) / (layerCount - 1));
|
| 544 |
+
|
| 545 |
+
for (let from = 0; from < fromNeuronCount; from++) {
|
| 546 |
+
const fromY = height / 2 + (from - (fromNeuronCount - 1) / 2) * 40;
|
| 547 |
+
|
| 548 |
+
for (let to = 0; to < toNeuronCount; to++) {
|
| 549 |
+
const toY = height / 2 + (to - (toNeuronCount - 1) / 2) * 40;
|
| 550 |
+
|
| 551 |
+
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
| 552 |
+
line.setAttribute("x1", fromLayerX + neuronRadius);
|
| 553 |
+
line.setAttribute("y1", fromY);
|
| 554 |
+
line.setAttribute("x2", toLayerX - neuronRadius);
|
| 555 |
+
line.setAttribute("y2", toY);
|
| 556 |
+
line.setAttribute("stroke", "#9CA3AF");
|
| 557 |
+
line.setAttribute("stroke-width", "1");
|
| 558 |
+
line.setAttribute("class", "connection");
|
| 559 |
+
svg.appendChild(line);
|
| 560 |
+
|
| 561 |
+
connections.push(line);
|
| 562 |
+
}
|
| 563 |
+
}
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
// Animate connections with staggered delay
|
| 567 |
+
connections.forEach((conn, i) => {
|
| 568 |
+
setTimeout(() => {
|
| 569 |
+
conn.style.strokeDashoffset = "0";
|
| 570 |
+
}, i * 20);
|
| 571 |
+
});
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
function predictLossProbability() {
|
| 575 |
+
if (!net.trainOpts) {
|
| 576 |
+
predictionText.textContent = "Please train the network first";
|
| 577 |
+
predictionResult.classList.remove('hidden');
|
| 578 |
+
predictionResult.className = "mt-4 p-4 rounded-lg bg-yellow-50";
|
| 579 |
+
return;
|
| 580 |
+
}
|
| 581 |
+
|
| 582 |
+
// Get input values
|
| 583 |
+
const goalsConceded = parseFloat(goalsConcededInput.value) || 1.2;
|
| 584 |
+
const shotsOnTarget = parseFloat(shotsOnTargetInput.value) || 5.5;
|
| 585 |
+
const passAccuracy = parseFloat(passAccuracyInput.value) || 85.0;
|
| 586 |
+
const tackles = parseFloat(tacklesInput.value) || 15.0;
|
| 587 |
+
|
| 588 |
+
// Normalize inputs
|
| 589 |
+
const normalizedInput = {
|
| 590 |
+
goalsConceded: normalize(goalsConceded, 0.5, 3.0),
|
| 591 |
+
shotsOnTarget: normalize(shotsOnTarget, 2.0, 8.0),
|
| 592 |
+
passAccuracy: normalize(passAccuracy, 70.0, 95.0),
|
| 593 |
+
tackles: normalize(tackles, 8.0, 20.0)
|
| 594 |
+
};
|
| 595 |
+
|
| 596 |
+
// Make prediction
|
| 597 |
+
const output = net.run(normalizedInput);
|
| 598 |
+
const lossProbability = output.loss;
|
| 599 |
+
const percentage = Math.round(lossProbability * 100);
|
| 600 |
+
|
| 601 |
+
// Display result
|
| 602 |
+
predictionText.textContent = `Based on the team's stats (Goals Conceded: ${goalsConceded.toFixed(1)}, Shots on Target: ${shotsOnTarget.toFixed(1)}, Pass Accuracy: ${passAccuracy.toFixed(1)}%, Tackles: ${tackles.toFixed(1)}), the predicted loss probability is:`;
|
| 603 |
+
predictionPercentage.textContent = `${percentage}%`;
|
| 604 |
+
|
| 605 |
+
// Update progress ring
|
| 606 |
+
const circle = document.querySelector('.progress-ring__circle:last-child');
|
| 607 |
+
const radius = circle.r.baseVal.value;
|
| 608 |
+
const circumference = radius * 2 * Math.PI;
|
| 609 |
+
const offset = circumference - (percentage / 100) * circumference;
|
| 610 |
+
|
| 611 |
+
circle.style.strokeDashoffset = offset;
|
| 612 |
+
|
| 613 |
+
// Set color based on probability
|
| 614 |
+
if (percentage > 70) {
|
| 615 |
+
circle.style.stroke = "#EF4444"; // red
|
| 616 |
+
predictionPercentage.className = "text-2xl font-bold text-red-600";
|
| 617 |
+
} else if (percentage > 50) {
|
| 618 |
+
circle.style.stroke = "#F59E0B"; // orange
|
| 619 |
+
predictionPercentage.className = "text-2xl font-bold text-yellow-600";
|
| 620 |
+
} else {
|
| 621 |
+
circle.style.stroke = "#10B981"; // green
|
| 622 |
+
predictionPercentage.className = "text-2xl font-bold text-green-600";
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
predictionResult.classList.remove('hidden');
|
| 626 |
+
predictionResult.className = "mt-4 p-4 rounded-lg bg-green-50";
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
function renderTeamCards() {
|
| 630 |
+
teamPredictions.innerHTML = '';
|
| 631 |
+
|
| 632 |
+
soccerTeams.forEach(team => {
|
| 633 |
+
const card = document.createElement('div');
|
| 634 |
+
card.className = 'bg-white rounded-lg shadow-md p-4 team-card hover:shadow-lg transition-all';
|
| 635 |
+
card.innerHTML = `
|
| 636 |
+
<div class="flex items-center mb-3">
|
| 637 |
+
<div class="text-2xl mr-3">${team.logo}</div>
|
| 638 |
+
<h3 class="font-semibold text-gray-800">${team.name}</h3>
|
| 639 |
+
</div>
|
| 640 |
+
<div class="grid grid-cols-2 gap-2 text-sm mb-3">
|
| 641 |
+
<div><span class="text-gray-500">Goals Conceded:</span> ${team.goalsConceded.toFixed(1)}</div>
|
| 642 |
+
<div><span class="text-gray-500">Shots on Target:</span> ${team.shotsOnTarget.toFixed(1)}</div>
|
| 643 |
+
<div><span class="text-gray-500">Pass %:</span> ${team.passAccuracy.toFixed(1)}%</div>
|
| 644 |
+
<div><span class="text-gray-500">Tackles:</span> ${team.tackles.toFixed(1)}</div>
|
| 645 |
+
</div>
|
| 646 |
+
<div class="flex items-center">
|
| 647 |
+
<button class="w-full bg-green-100 hover:bg-green-200 text-green-800 py-2 px-3 rounded flex items-center justify-center predict-btn" data-team='${JSON.stringify(team)}'>
|
| 648 |
+
<i class="fas fa-calculator mr-2 text-xs"></i> Predict Performance
|
| 649 |
+
</button>
|
| 650 |
+
</div>
|
| 651 |
+
`;
|
| 652 |
+
teamPredictions.appendChild(card);
|
| 653 |
+
});
|
| 654 |
+
|
| 655 |
+
// Add event listeners to predict buttons
|
| 656 |
+
document.querySelectorAll('.predict-btn').forEach(btn => {
|
| 657 |
+
btn.addEventListener('click', function() {
|
| 658 |
+
const team = JSON.parse(this.getAttribute('data-team'));
|
| 659 |
+
goalsConcededInput.value = team.goalsConceded;
|
| 660 |
+
shotsOnTargetInput.value = team.shotsOnTarget;
|
| 661 |
+
passAccuracyInput.value = team.passAccuracy;
|
| 662 |
+
tacklesInput.value = team.tackles;
|
| 663 |
+
|
| 664 |
+
if (net.trainOpts) {
|
| 665 |
+
predictLossProbability();
|
| 666 |
+
} else {
|
| 667 |
+
predictionText.textContent = "Please train the network first";
|
| 668 |
+
predictionResult.classList.remove('hidden');
|
| 669 |
+
predictionResult.className = "mt-4 p-4 rounded-lg bg-yellow-50";
|
| 670 |
+
}
|
| 671 |
+
});
|
| 672 |
+
});
|
| 673 |
+
}
|
| 674 |
+
});
|
| 675 |
+
</script>
|
| 676 |
+
<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=MarcRyan/stp" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
| 677 |
+
</html>
|
prompts.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
put this in a browser <!DOCTYPE html> <html> <head> <title>My app</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta charset="utf-8"> <style> body { display: flex; justify-content: center; align-items: center; overflow: hidden; height: 100dvh; font-family: "Arial", sans-serif; text-align: center; } .arrow { position: absolute; bottom: 32px; left: 0px; width: 100px; transform: rotate(30deg); } h1 { font-size: 50px; } h1 span { color: #acacac; font-size: 32px; } </style> </head> <body> <h1> <span>I'm ready to work,</span><br /> Ask me anything. </h1> <img src="https://enzostvs-deepsite.hf.space/arrow.svg" class="arrow" /> <script></script> </body> </html>
|
| 2 |
+
make this but for soccer - <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Baseball Team Performance Predictor</title> <script src="https://cdn.tailwindcss.com"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/brain/0.6.3/brain.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> .neuron { transition: all 0.3s ease; } .neuron:hover { transform: scale(1.1); } .network-container { perspective: 1000px; } .layer { transform-style: preserve-3d; } .connection { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: draw 1.5s forwards; } @keyframes draw { to { stroke-dashoffset: 0; } } .progress-ring__circle { transition: stroke-dashoffset 0.35s; transform: rotate(-90deg); transform-origin: 50% 50%; } .team-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); } .team-card { transition: all 0.3s ease; } </style> </head> <body class="bg-gray-50 min-h-screen"> <div class="container mx-auto px-4 py-8"> <header class="text-center mb-12"> <h1 class="text-4xl font-bold text-gray-800 mb-2">βΎ Baseball Team Performance Predictor</h1> <p class="text-gray-600 max-w-2xl mx-auto">A neural network that predicts team loss probability based on key performance metrics</p> </header> <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> <!-- Configuration Panel --> <div class="bg-white rounded-xl shadow-md p-6"> <h2 class="text-xl font-semibold text-gray-800 mb-4">Network Configuration</h2> <div class="space-y-4"> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Hidden Layers</label> <input type="range" id="hiddenLayersInput" min="1" max="5" value="3" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> <div class="flex justify-between text-xs text-gray-500"> <span>1</span> <span>2</span> <span>3</span> <span>4</span> <span>5</span> </div> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Neurons per Layer</label> <input type="range" id="neuronsPerLayerInput" min="3" max="10" value="6" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> <div class="flex justify-between text-xs text-gray-500"> <span>3</span> <span>4</span> <span>5</span> <span>6</span> <span>7</span> <span>8</span> <span>9</span> <span>10</span> </div> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Learning Rate</label> <input type="range" id="learningRateInput" min="0.01" max="0.5" step="0.01" value="0.2" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> <div class="flex justify-between text-xs text-gray-500"> <span>0.01</span> <span>0.25</span> <span>0.5</span> </div> </div> <div class="flex space-x-3 pt-2"> <button id="trainBtn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg flex items-center justify-center"> <i class="fas fa-brain mr-2"></i> Train Network </button> <button id="resetBtn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-lg flex items-center justify-center"> <i class="fas fa-redo mr-2"></i> Reset </button> </div> </div> <div class="mt-6"> <h3 class="text-sm font-medium text-gray-700 mb-2">Training Status</h3> <div id="trainingStatus" class="text-sm text-gray-600 bg-gray-100 p-3 rounded-lg"> Network not trained yet </div> </div> </div> <!-- Network Visualization --> <div class="bg-white rounded-xl shadow-md p-6"> <h2 class="text-xl font-semibold text-gray-800 mb-4">Network Architecture</h2> <div id="networkVisualization" class="network-container h-64 flex justify-center items-center"> <svg id="networkSvg" width="100%" height="100%" viewBox="0 0 500 300"></svg> </div> <div class="mt-6"> <h3 class="text-sm font-medium text-gray-700 mb-2">Error Over Time</h3> <div class="bg-gray-100 p-2 rounded-lg"> <canvas id="errorChart" height="150"></canvas> </div> </div> </div> <!-- Prediction Panel --> <div class="bg-white rounded-xl shadow-md p-6"> <h2 class="text-xl font-semibold text-gray-800 mb-4">Make a Prediction</h2> <div class="space-y-3"> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Team ERA</label> <input id="eraInput" type="number" step="0.01" class="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="4.50"> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Team Batting Average</label> <input id="baInput" type="number" step="0.001" class="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="0.250"> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Home Runs</label> <input id="hrInput" type="number" class="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="150"> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Fielding Errors</label> <input id="errorsInput" type="number" class="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="100"> </div> <button id="predictBtn" class="w-full bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg mt-4 flex items-center justify-center"> <i class="fas fa-calculator mr-2"></i> Predict Loss Probability </button> <div id="predictionResult" class="mt-4 p-4 rounded-lg bg-blue-50 hidden"> <h4 class="font-medium text-blue-800 mb-1">Prediction Result</h4> <p id="predictionText" class="text-blue-600"></p> <div class="mt-3 flex items-center"> <div class="w-12 h-12 mr-3"> <svg class="progress-ring" width="48" height="48"> <circle class="progress-ring__circle" stroke="#E5E7EB" stroke-width="4" fill="transparent" r="20" cx="24" cy="24"/> <circle class="progress-ring__circle" stroke="#3B82F6" stroke-width="4" fill="transparent" r="20" cx="24" cy="24"/> </svg> </div> <div class="text-2xl font-bold text-blue-600" id="predictionPercentage">0%</div> </div> </div> </div> </div> </div> <!-- Team Cards --> <div class="mt-12"> <h2 class="text-2xl font-bold text-gray-800 mb-6">MLB Teams</h2> <div id="teamPredictions" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"></div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Configuration const config = { hiddenLayers: 3, neuronsPerLayer: 6, learningRate: 0.2, iterations: 20000, errorThresh: 0.005 }; // Sample baseball teams data let baseballTeams = [ { name: "New York Yankees", logo: "π", era: 3.75, ba: 0.257, hr: 245, errors: 85 }, { name: "Los Angeles Dodgers", logo: "π΅", era: 3.42, ba: 0.263, hr: 235, errors: 78 }, { name: "Houston Astros", logo: "π", era: 3.68, ba: 0.261, hr: 221, errors: 92 }, { name: "Atlanta Braves", logo: "π΄", era: 3.89, ba: 0.255, hr: 239, errors: 87 }, { name: "Boston Red Sox", logo: "π§¦", era: 4.12, ba: 0.249, hr: 198, errors: 105 }, { name: "Chicago Cubs", logo: "π»", era: 4.05, ba: 0.242, hr: 187, errors: 112 }, { name: "San Francisco Giants", logo: "π", era: 3.95, ba: 0.247, hr: 176, errors: 98 }, { name: "St. Louis Cardinals", logo: "π¦", era: 3.78, ba: 0.254, hr: 203, errors: 89 } ]; // Initialize network let net = new brain.NeuralNetwork(); let errorChart; let trainingData = generateTrainingData(); // DOM Elements const hiddenLayersInput = document.getElementById('hiddenLayersInput'); const neuronsPerLayerInput = document.getElementById('neuronsPerLayerInput'); const learningRateInput = document.getElementById('learningRateInput'); const trainBtn = document.getElementById('trainBtn'); const resetBtn = document.getElementById('resetBtn'); const eraInput = document.getElementById('eraInput'); const baInput = document.getElementById('baInput'); const hrInput = document.getElementById('hrInput'); const errorsInput = document.getElementById('errorsInput'); const predictBtn = document.getElementById('predictBtn'); const predictionResult = document.getElementById('predictionResult'); const predictionText = document.getElementById('predictionText'); const predictionPercentage = document.getElementById('predictionPercentage'); const teamPredictions = document.getElementById('teamPredictions'); const trainingStatus = document.getElementById('trainingStatus'); // Initialize UI initUI(); drawNetwork(); initChart(); renderTeamCards(); // Event Listeners hiddenLayersInput.addEventListener('input', updateConfig); neuronsPerLayerInput.addEventListener('input', updateConfig); learningRateInput.addEventListener('input', updateConfig); trainBtn.addEventListener('click', trainNetwork); resetBtn.addEventListener('click', resetNetwork); predictBtn.addEventListener('click', predictLossProbability); // Functions function generateTrainingData() { // Generate synthetic training data based on realistic baseball stats const data = []; for (let i = 0; i < 100; i++) { // Generate random but realistic baseball stats const era = 2.5 + Math.random() * 3.5; // ERA between 2.5 and 6.0 const ba = 0.220 + Math.random() * 0.080; // BA between .220 and .300 const hr = 100 + Math.random() * 200; // HR between 100 and 300 const errors = 50 + Math.random() * 100; // Errors between 50 and 150 // Calculate a synthetic loss probability based on these stats // Better stats (lower ERA, higher BA, more HR, fewer errors) should lead to lower loss probability let lossProbability = (era / 6.0) * 0.4 + // ERA contributes 40% ((0.300 - ba) / 0.080) * 0.3 + // BA contributes 30% ((300 - hr) / 200) * 0.2 + // HR contributes 20% (errors / 150) * 0.1; // Errors contribute 10% // Add some randomness lossProbability += (Math.random() - 0.5) * 0.1; // Ensure between 0 and 1 lossProbability = Math.max(0, Math.min(1, lossProbability)); data.push({ input: { era: normalize(era, 2.5, 6.0), ba: normalize(ba, 0.220, 0.300), hr: normalize(hr, 100, 300), errors: normalize(errors, 50, 150) }, output: { loss: lossProbability } }); } return data; } function normalize(value, min, max) { return (value - min) / (max - min); } function denormalize(value, min, max) { return value * (max - min) + min; } function initUI() { // Set initial values from config hiddenLayersInput.value = config.hiddenLayers; neuronsPerLayerInput.value = config.neuronsPerLayer; learningRateInput.value = config.learningRate; // Initialize prediction progress ring const circle = document.querySelector('.progress-ring__circle:last-child'); const radius = circle.r.baseVal.value; const circumference = radius * 2 * Math.PI; circle.style.strokeDasharray = circumference; circle.style.strokeDashoffset = circumference; } function updateConfig() { config.hiddenLayers = parseInt(hiddenLayersInput.value); config.neuronsPerLayer = parseInt(neuronsPerLayerInput.value); config.learningRate = parseFloat(learningRateInput.value); drawNetwork(); } function resetNetwork() { net = new brain.NeuralNetwork(); trainingStatus.textContent = "Network reset - not trained"; trainingStatus.className = "text-sm text-gray-600 bg-gray-100 p-3 rounded-lg"; // Reset chart if (errorChart) { errorChart.data.labels = []; errorChart.data.datasets[0].data = []; errorChart.update(); } // Hide prediction result predictionResult.classList.add('hidden'); } function trainNetwork() { trainBtn.disabled = true; trainBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Training...'; trainingStatus.textContent = "Training in progress..."; trainingStatus.className = "text-sm text-blue-600 bg-blue-100 p-3 rounded-lg"; // Configure network net = new brain.NeuralNetwork({ hiddenLayers: [Array(config.hiddenLayers).fill(config.neuronsPerLayer)].flat(), learningRate: config.learningRate, iterations: config.iterations, errorThresh: config.errorThresh, log: true, logPeriod: 1000, callback: function(info) { updateTrainingStatus(info.iterations, info.error); updateChart(info.iterations, info.error); }, callbackPeriod: 1000 }); // Train in a timeout to allow UI to update setTimeout(() => { net.train(trainingData, (err, info) => { trainBtn.disabled = false; trainBtn.innerHTML = '<i class="fas fa-brain mr-2"></i> Train Network'; if (err) { trainingStatus.textContent = "Training failed: " + err; trainingStatus.className = "text-sm text-red-600 bg-red-100 p-3 rounded-lg"; } else { trainingStatus.textContent = `Training complete! Final error: ${info.error.toFixed(6)} after ${info.iterations} iterations`; trainingStatus.className = "text-sm text-green-600 bg-green-100 p-3 rounded-lg"; } // Animate network connections animateNetwork(); }); }, 100); } function updateTrainingStatus(iterations, error) { trainingStatus.textContent = `Training... Iteration: ${iterations}, Error: ${error.toFixed(6)}`; } function initChart() { const ctx = document.getElementById('errorChart').getContext('2d'); errorChart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'Training Error', data: [], borderColor: 'rgb(99, 102, 241)', backgroundColor: 'rgba(99, 102, 241, 0.1)', borderWidth: 2, tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, title: { display: true, text: 'Error' } }, x: { title: { display: true, text: 'Iterations' } } }, plugins: { legend: { display: false }, tooltip: { callbacks: { label: function(context) { return `Error: ${context.parsed.y.toFixed(6)}`; } } } } } }); } function updateChart(iteration, error) { errorChart.data.labels.push(iteration); errorChart.data.datasets[0].data.push(error); errorChart.update(); } function drawNetwork() { const svg = document.getElementById('networkSvg'); svg.innerHTML = ''; const width = 500; const height = 300; const layerCount = config.hiddenLayers + 2; // Input + hidden + output const neuronRadius = 15; // Draw layers for (let layer = 0; layer < layerCount; layer++) { const isInput = layer === 0; const isOutput = layer === layerCount - 1; let neuronCount; if (isInput) neuronCount = 4; // 4 input features else if (isOutput) neuronCount = 1; // 1 output (loss probability) else neuronCount = config.neuronsPerLayer; const layerX = 50 + (width - 100) * (layer / (layerCount - 1)); // Draw neurons for (let n = 0; n < neuronCount; n++) { const neuronY = height / 2 + (n - (neuronCount - 1) / 2) * 40; // Neuron circle const neuron = document.createElementNS("http://www.w3.org/2000/svg", "circle"); neuron.setAttribute("cx", layerX); neuron.setAttribute("cy", neuronY); neuron.setAttribute("r", neuronRadius); neuron.setAttribute("class", "neuron"); if (isInput) { neuron.setAttribute("fill", "#60A5FA"); // Blue for input } else if (isOutput) { neuron.setAttribute("fill", "#10B981"); // Green for output } else { neuron.setAttribute("fill", "#F59E0B"); // Yellow for hidden } svg.appendChild(neuron); // Neuron label if (isInput) { const labels = ["ERA", "BA", "HR", "Errors"]; const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); text.setAttribute("x", layerX - 50); text.setAttribute("y", neuronY + 5); text.setAttribute("text-anchor", "end"); text.setAttribute("class", "text-xs font-medium fill-gray-700"); text.textContent = labels[n]; svg.appendChild(text); } else if (isOutput) { const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); text.setAttribute("x", layerX + 50); text.setAttribute("y", neuronY + 5); text.setAttribute("text-anchor", "start"); text.setAttribute("class", "text-xs font-medium fill-gray-700"); text.textContent = "Loss %"; svg.appendChild(text); } } } } function animateNetwork() { const svg = document.getElementById('networkSvg'); const connections = []; const width = 500; const height = 300; const layerCount = config.hiddenLayers + 2; const neuronRadius = 15; // Create connections between layers for (let layer = 0; layer < layerCount - 1; layer++) { const isFirstLayer = layer === 0; const isLastLayer = layer === layerCount - 2; let fromNeuronCount; if (isFirstLayer) fromNeuronCount = 4; else fromNeuronCount = config.neuronsPerLayer; let toNeuronCount; if (isLastLayer) toNeuronCount = 1; else toNeuronCount = config.neuronsPerLayer; const fromLayerX = 50 + (width - 100) * (layer / (layerCount - 1)); const toLayerX = 50 + (width - 100) * ((layer + 1) / (layerCount - 1)); for (let from = 0; from < fromNeuronCount; from++) { const fromY = height / 2 + (from - (fromNeuronCount - 1) / 2) * 40; for (let to = 0; to < toNeuronCount; to++) { const toY = height / 2 + (to - (toNeuronCount - 1) / 2) * 40; const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); line.setAttribute("x1", fromLayerX + neuronRadius); line.setAttribute("y1", fromY); line.setAttribute("x2", toLayerX - neuronRadius); line.setAttribute("y2", toY); line.setAttribute("stroke", "#9CA3AF"); line.setAttribute("stroke-width", "1"); line.setAttribute("class", "connection"); svg.appendChild(line); connections.push(line); } } } // Animate connections with staggered delay connections.forEach((conn, i) => { setTimeout(() => { conn.style.strokeDashoffset = "0"; }, i * 20); }); } function predictLossProbability() { if (!net.trainOpts) { predictionText.textContent = "Please train the network first"; predictionResult.classList.remove('hidden'); predictionResult.className = "mt-4 p-4 rounded-lg bg-yellow-50"; return; } // Get input values const era = parseFloat(eraInput.value) || 4.50; const ba = parseFloat(baInput.value) || 0.250; const hr = parseInt(hrInput.value) || 150; const errors = parseInt(errorsInput.value) || 100; // Normalize inputs const normalizedInput = { era: normalize(era, 2.5, 6.0), ba: normalize(ba, 0.220, 0.300), hr: normalize(hr, 100, 300), errors: normalize(errors, 50, 150) }; // Make prediction const output = net.run(normalizedInput); const lossProbability = output.loss; const percentage = Math.round(lossProbability * 100); // Display result predictionText.textContent = `Based on the team's stats (ERA: ${era.toFixed(2)}, BA: ${ba.toFixed(3)}, HR: ${hr}, Errors: ${errors}), the predicted loss probability is:`; predictionPercentage.textContent = `${percentage}%`; // Update progress ring const circle = document.querySelector('.progress-ring__circle:last-child'); const radius = circle.r.baseVal.value; const circumference = radius * 2 * Math.PI; const offset = circumference - (percentage / 100) * circumference; circle.style.strokeDashoffset = offset; // Set color based on probability if (percentage > 70) { circle.style.stroke = "#EF4444"; // red predictionPercentage.className = "text-2xl font-bold text-red-600"; } else if (percentage > 50) { circle.style.stroke = "#F59E0B"; // orange predictionPercentage.className = "text-2xl font-bold text-yellow-600"; } else { circle.style.stroke = "#10B981"; // green predictionPercentage.className = "text-2xl font-bold text-green-600"; } predictionResult.classList.remove('hidden'); predictionResult.className = "mt-4 p-4 rounded-lg bg-blue-50"; } function renderTeamCards() { teamPredictions.innerHTML = ''; baseballTeams.forEach(team => { const card = document.createElement('div'); card.className = 'bg-white rounded-lg shadow-md p-4 team-card hover:shadow-lg transition-all'; card.innerHTML = ` <div class="flex items-center mb-3"> <div class="text-2xl mr-3">${team.logo}</div> <h3 class="font-semibold text-gray-800">${team.name}</h3> </div> <div class="grid grid-cols-2 gap-2 text-sm mb-3"> <div><span class="text-gray-500">ERA:</span> ${team.era.toFixed(2)}</div> <div><span class="text-gray-500">BA:</span> ${team.ba.toFixed(3)}</div> <div><span class="text-gray-500">HR:</span> ${team.hr}</div> <div><span class="text-gray-500">Errors:</span> ${team.errors}</div> </div> <div class="flex items-center"> <button class="w-full bg-blue-100 hover:bg-blue-200 text-blue-800 py-2 px-3 rounded flex items-center justify-center predict-btn" data-team='${JSON.stringify(team)}'> <i class="fas fa-calculator mr-2 text-xs"></i> Predict Performance </button> </div> `; teamPredictions.appendChild(card); }); // Add event listeners to predict buttons document.querySelectorAll('.predict-btn').forEach(btn => { btn.addEventListener('click', function() { const team = JSON.parse(this.getAttribute('data-team')); eraInput.value = team.era; baInput.value = team.ba; hrInput.value = team.hr; errorsInput.value = team.errors; if (net.trainOpts) { predictLossProbability(); } else { predictionText.textContent = "Please train the network first"; predictionResult.classList.remove('hidden'); predictionResult.className = "mt-4 p-4 rounded-lg bg-yellow-50"; } }); }); } }); </script> </body> </html>
|