Text 3
Text 3
pour le codage
voici mon ancienne application qui sera décortiquer et modifier de a à z
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#5D5CDE">
<title>Simulateur Pro de Matchs de Football</title>
<link rel="manifest" href="#" id="manifestPlaceholder">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?
family=Montserrat:wght@400;500;600;700&family=Roboto:wght@300;400;500;700&display=s
wap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-
awesome/6.4.0/css/all.min.css">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"></
script>
<style>
/* Votre CSS reste identique */
:root {
/* Couleurs de base */
--primary: #5D5CDE;
--primary-light: #7675EF;
--primary-dark: #3E3DB0;
--accent: #F39C12;
--accent-light: #F7B541;
--accent-dark: #E67E22;
--success: #2ECC71;
--danger: #E74C3C;
--warning: #F39C12;
--info: #3498DB;
/* Animations */
--transition-speed: 0.3s;
/* Rayons de bordure */
--border-radius-sm: 0.25rem;
--border-radius: 0.5rem;
--border-radius-lg: 1rem;
--border-radius-xl: 1.5rem;
/* Espacement */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
}
/* Thème sombre */
.dark {
--bg-main: #121220;
--bg-card: #1E1E30;
--bg-input: #252538;
--text-primary: #F7F7FC;
--text-secondary: #AEAECE;
--text-muted: #8E8EA9;
--border-color: #323252;
--shadow-color: rgba(0, 0, 0, 0.2);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
background-color: var(--bg-main);
color: var(--text-primary);
transition: background-color var(--transition-speed), color var(--
transition-speed);
min-height: 100vh;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-md);
}
.card {
background-color: var(--bg-card);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
box-shadow: 0 4px 16px var(--shadow-color);
border: 1px solid var(--border-color);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px var(--shadow-color);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
border-bottom: 1px solid var(--border-color);
padding-bottom: var(--spacing-md);
}
.card-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-primary);
}
.card-body {
position: relative;
}
/* Formulaires */
.form-group {
margin-bottom: var(--spacing-md);
}
.form-group label {
display: block;
margin-bottom: var(--spacing-xs);
color: var(--text-secondary);
font-weight: 500;
font-size: 0.9rem;
}
.form-control {
width: 100%;
padding: var(--spacing-md);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background-color: var(--bg-input);
color: var(--text-primary);
font-size: 1rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.form-control:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(93, 92, 222, 0.15);
}
.form-control::placeholder {
color: var(--text-muted);
}
select.form-control {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg
xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238E8EA9'
viewBox='0 0 16 16'%3E%3Cpath d='M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204
4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 32px;
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--spacing-md);
}
.form-row {
display: flex;
gap: var(--spacing-md);
}
.form-row > * {
flex: 1;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: var(--spacing-md);
margin-top: var(--spacing-lg);
}
/* Boutons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--spacing-sm);
padding: var(--spacing-md) var(--spacing-lg);
border-radius: var(--border-radius);
font-weight: 500;
cursor: pointer;
transition: all var(--transition-speed);
text-align: center;
border: none;
font-size: 0.95rem;
}
.btn-primary {
background-color: var(--primary);
color: white;
box-shadow: 0 4px 6px rgba(93, 92, 222, 0.25);
}
.btn-primary:hover {
background-color: var(--primary-light);
box-shadow: 0 6px 8px rgba(93, 92, 222, 0.3);
}
.btn-accent {
background-color: var(--accent);
color: white;
box-shadow: 0 4px 6px rgba(243, 156, 18, 0.25);
}
.btn-accent:hover {
background-color: var(--accent-light);
box-shadow: 0 6px 8px rgba(243, 156, 18, 0.3);
}
.btn-secondary {
background-color: var(--bg-card);
color: var(--text-secondary);
border: 1px solid var(--border-color);
}
.btn-secondary:hover {
background-color: var(--border-color);
color: var(--text-primary);
}
.btn-success {
background-color: var(--success);
color: white;
box-shadow: 0 4px 6px rgba(46, 204, 113, 0.25);
}
.btn-success:hover {
background-color: var(--success);
box-shadow: 0 6px 8px rgba(46, 204, 113, 0.3);
}
.btn-danger {
background-color: var(--danger);
color: white;
box-shadow: 0 4px 6px rgba(231, 76, 60, 0.25);
}
.btn-danger:hover {
background-color: var(--danger);
box-shadow: 0 6px 8px rgba(231, 76, 60, 0.3);
}
.btn-sm {
padding: var(--spacing-sm) var(--spacing-md);
font-size: 0.85rem;
}
.btn-lg {
padding: var(--spacing-lg) var(--spacing-xl);
font-size: 1.1rem;
}
/* Barre de vitesse */
.speed-control {
display: flex;
align-items: center;
gap: var(--spacing-md);
margin-bottom: var(--spacing-md);
}
.speed-label {
font-weight: 500;
color: var(--text-secondary);
}
.speed-options {
display: flex;
gap: var(--spacing-xs);
}
.speed-option {
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
background-color: var(--bg-card);
border: 1px solid var(--border-color);
cursor: pointer;
transition: all var(--transition-speed);
}
.speed-option:hover {
border-color: var(--primary);
color: var(--primary);
}
.speed-option.active {
background-color: var(--primary);
color: white;
border-color: var(--primary);
}
/* Match Style */
.match-field {
background-color: #1b5e20;
border-radius: var(--border-radius);
padding: var(--spacing-xl);
margin-bottom: var(--spacing-lg);
position: relative;
min-height: 200px;
color: white;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.match-field-lines {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 4px solid rgba(255, 255, 255, 0.3);
pointer-events: none;
}
.match-field-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60px;
height: 60px;
border-radius: 50%;
border: 4px solid rgba(255, 255, 255, 0.3);
pointer-events: none;
}
.match-field-center::before {
content: '';
position: absolute;
top: 50%;
left: -100px;
right: -100px;
height: 4px;
background-color: rgba(255, 255, 255, 0.3);
transform: translateY(-50%);
}
.match-scoreboard {
background-color: var(--bg-card);
border-radius: var(--border-radius);
padding: var(--spacing-lg);
text-align: center;
box-shadow: 0 4px 16px var(--shadow-color);
margin-bottom: var(--spacing-lg);
}
.match-teams {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
}
.match-team {
flex: 1;
text-align: center;
}
.match-score {
font-size: 2.5rem;
font-weight: 700;
padding: 0 var(--spacing-md);
}
.match-time {
font-size: 1.2rem;
font-weight: 600;
color: var(--primary);
margin-bottom: var(--spacing-sm);
}
.match-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--spacing-sm);
margin-top: var(--spacing-lg);
padding-top: var(--spacing-md);
border-top: 1px solid var(--border-color);
}
.match-stat {
text-align: center;
}
.stat-label {
font-size: 0.8rem;
color: var(--text-secondary);
margin-bottom: var(--spacing-xs);
}
.stat-values {
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-value {
font-weight: 600;
flex: 1;
}
.stat-bar {
height: 6px;
background-color: var(--border-color);
border-radius: 3px;
margin-top: var(--spacing-xs);
position: relative;
overflow: hidden;
}
.stat-fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: var(--primary);
transition: width 0.5s ease-out;
}
/* Match Commentary */
.match-commentary {
background-color: var(--bg-card);
border-radius: var(--border-radius);
padding: var(--spacing-md);
box-shadow: 0 4px 16px var(--shadow-color);
margin-bottom: var(--spacing-lg);
height: 300px;
overflow-y: auto;
}
.commentary-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: var(--spacing-md);
padding-bottom: var(--spacing-sm);
border-bottom: 1px solid var(--border-color);
}
.commentary-list {
list-style: none;
}
.commentary-item {
padding: var(--spacing-sm) 0;
border-bottom: 1px dashed var(--border-color);
}
.commentary-time {
font-weight: 600;
color: var(--primary);
}
/* Badges */
.badge {
display: inline-block;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
font-size: 0.75rem;
font-weight: 500;
}
.badge-primary {
background-color: rgba(93, 92, 222, 0.1);
color: var(--primary);
}
.badge-success {
background-color: rgba(46, 204, 113, 0.1);
color: var(--success);
}
.badge-danger {
background-color: rgba(231, 76, 60, 0.1);
color: var(--danger);
}
.badge-warning {
background-color: rgba(243, 156, 18, 0.1);
color: var(--warning);
}
/* Team Category */
.team-category {
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
font-size: 0.75rem;
font-weight: 500;
display: inline-block;
margin-top: var(--spacing-xs);
}
.category-grande {
background-color: rgba(46, 204, 113, 0.1);
color: var(--success);
}
.category-moyenne {
background-color: rgba(243, 156, 18, 0.1);
color: var(--warning);
}
.category-petite {
background-color: rgba(231, 76, 60, 0.1);
color: var(--danger);
}
/* Team Styles */
.team-style {
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
font-size: 0.75rem;
font-weight: 500;
display: inline-block;
margin-top: var(--spacing-xs);
}
.style-offensive {
background-color: rgba(231, 76, 60, 0.1);
color: var(--danger);
}
.style-balanced {
background-color: rgba(243, 156, 18, 0.1);
color: var(--warning);
}
.style-defensive {
background-color: rgba(46, 204, 113, 0.1);
color: var(--success);
}
/* Toggle */
.toggle-container {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 26px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--border-color);
transition: .4s;
border-radius: 26px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: var(--primary);
}
input:checked + .toggle-slider:before {
transform: translateX(24px);
}
/* Utilitaires */
.mb-1 { margin-bottom: var(--spacing-xs); }
.mb-2 { margin-bottom: var(--spacing-sm); }
.mb-3 { margin-bottom: var(--spacing-md); }
.mb-4 { margin-bottom: var(--spacing-lg); }
.mb-5 { margin-bottom: var(--spacing-xl); }
@keyframes pulse {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(93, 92, 222, 0.7);
}
70% {
transform: scale(1);
box-shadow: 0 0 0 5px rgba(93, 92, 222, 0);
}
100% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(93, 92, 222, 0);
}
}
/* Goal Animation */
.goal-animation {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 100;
animation: goalFadeIn 0.3s forwards;
}
.goal-content {
padding: var(--spacing-xl);
background-color: rgba(93, 92, 222, 0.9);
border-radius: var(--border-radius-lg);
text-align: center;
color: white;
transform: scale(0.8);
animation: goalScale 0.5s forwards;
}
.goal-text {
font-size: 3rem;
font-weight: 700;
margin-bottom: var(--spacing-lg);
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}
.goal-team {
font-size: 1.5rem;
font-weight: 600;
}
@keyframes goalFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes goalScale {
from { transform: scale(0.8); }
to { transform: scale(1); }
}
/* Notifications */
#notifications {
position: fixed;
top: var(--spacing-lg);
right: var(--spacing-lg);
z-index: 1000;
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
max-width: 350px;
}
.notification {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-md);
background-color: var(--bg-card);
border-radius: var(--border-radius);
box-shadow: 0 4px 12px var(--shadow-color);
animation: slideIn 0.3s ease;
border-left: 4px solid var(--primary);
}
.notification.success {
border-color: var(--success);
}
.notification.error {
border-color: var(--danger);
}
.notification.warning {
border-color: var(--warning);
}
.notification.info {
border-color: var(--primary);
}
.notification i {
font-size: 1.2rem;
}
.notification.success i {
color: var(--success);
}
.notification.error i {
color: var(--danger);
}
.notification.warning i {
color: var(--warning);
}
.notification.info i {
color: var(--primary);
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeout {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(30px);
}
}
/* Match History */
.match-history-container {
margin-top: var(--spacing-xl);
}
.match-history-list {
max-height: 500px;
overflow-y: auto;
}
.match-history-item {
background-color: var(--bg-card);
border-radius: var(--border-radius);
padding: var(--spacing-md);
margin-bottom: var(--spacing-sm);
border: 1px solid var(--border-color);
transition: transform 0.2s ease;
}
.match-history-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px var(--shadow-color);
}
.match-result {
display: flex;
justify-content: space-between;
align-items: center;
}
.match-details {
padding-top: var(--spacing-sm);
margin-top: var(--spacing-sm);
border-top: 1px solid var(--border-color);
font-size: 0.9rem;
color: var(--text-secondary);
}
/* Switch sombre/clair */
.theme-switch {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 100;
}
.theme-switch-button {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: var(--bg-card);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 8px var(--shadow-color);
cursor: pointer;
border: none;
color: var(--text-primary);
}
.import-data-card textarea {
min-height: 120px;
resize: vertical;
}
/* Scheduled Matches */
.scheduled-matches {
margin-top: var(--spacing-lg);
}
.scheduled-match-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-md);
background-color: var(--bg-card);
border-radius: var(--border-radius);
margin-bottom: var(--spacing-sm);
border: 1px solid var(--border-color);
}
.scheduled-match-teams {
font-weight: 600;
}
.scheduled-match-time {
color: var(--text-secondary);
font-size: 0.9rem;
}
.team-selector .selected-team {
display: flex;
align-items: center;
padding: var(--spacing-sm);
border: 1px solid var(--border-color);
background-color: var(--bg-input);
border-radius: var(--border-radius);
gap: var(--spacing-sm);
cursor: pointer;
}
.team-selector .team-list {
position: absolute;
top: 100%;
left: 0;
right: 0;
max-height: 250px;
overflow-y: auto;
background-color: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
z-index: 10;
box-shadow: 0 4px 12px var(--shadow-color);
display: none;
}
.team-selector .team-item {
padding: var(--spacing-sm);
cursor: pointer;
transition: background-color 0.2s ease;
display: flex;
align-items: center;
}
.team-selector .team-item:hover {
background-color: rgba(93, 92, 222, 0.1);
}
.team-league-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
font-size: 0.75rem;
font-weight: 600;
margin-right: var(--spacing-sm);
}
.team-league-badge.L1 {
background-color: #0053a0;
color: white;
}
.team-league-badge.PL {
background-color: #3d185c;
color: white;
}
.team-league-badge.LL {
background-color: #ee8707;
color: white;
}
.team-league-badge.BL {
background-color: #d3010c;
color: white;
}
.team-league-badge.SA {
background-color: #008756;
color: white;
}
/* Onglets */
.tabs {
display: flex;
gap: 2px;
margin-bottom: var(--spacing-md);
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: var(--spacing-sm) var(--spacing-lg);
cursor: pointer;
background-color: transparent;
border: none;
color: var(--text-secondary);
border-bottom: 2px solid transparent;
transition: all 0.2s ease;
font-weight: 500;
}
.tab:hover {
color: var(--primary);
}
.tab.active {
color: var(--primary);
border-bottom-color: var(--primary);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
animation: fadeIn 0.3s ease;
}
.datetime-picker input {
flex: 1;
}
.scheduled-matches-list {
max-height: 300px;
overflow-y: auto;
}
.scheduled-match-buttons {
display: flex;
gap: var(--spacing-sm);
}
/* Loader */
.loader-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loader {
width: 50px;
height: 50px;
border: 5px solid var(--bg-card);
border-radius: 50%;
border-top-color: var(--primary);
animation: spin 1s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.card {
padding: var(--spacing-md);
}
.match-scoreboard {
padding: var(--spacing-md);
}
.match-score {
font-size: 2rem;
}
.match-stats {
grid-template-columns: 1fr 1fr;
gap: var(--spacing-md);
}
.form-grid {
grid-template-columns: 1fr;
}
.form-row {
flex-direction: column;
}
.btn {
padding: var(--spacing-sm) var(--spacing-md);
}
.tabs {
overflow-x: auto;
padding-bottom: var(--spacing-xs);
}
.tab {
padding: var(--spacing-sm) var(--spacing-md);
white-space: nowrap;
}
}
.match-stats {
grid-template-columns: 1fr;
}
.datetime-picker {
flex-direction: column;
}
}
.category-top-team {
background-color: rgba(52, 152, 219, 0.1);
color: #3498db;
}
.category-challenger {
background-color: rgba(155, 89, 182, 0.1);
color: #9b59b6;
}
.category-mid-table {
background-color: rgba(243, 156, 18, 0.1);
color: #f39c12;
}
.category-underdog {
background-color: rgba(231, 76, 60, 0.1);
color: #e74c3c;
}
.category-ultra-defensive {
background-color: rgba(52, 73, 94, 0.1);
color: #34495e;
}
.category-ultra-offensive {
background-color: rgba(231, 76, 60, 0.1);
color: #e74c3c;
}
.category-physical {
background-color: rgba(44, 62, 80, 0.1);
color: #2c3e50;
}
.category-technical {
background-color: rgba(52, 152, 219, 0.1);
color: #3498db;
}
.category-opportunist {
background-color: rgba(39, 174, 96, 0.1);
color: #27ae60;
}
.category-chaotic {
background-color: rgba(243, 156, 18, 0.1);
color: #f39c12;
}
/* Événements de match */
.event-category {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 50%;
text-align: center;
line-height: 24px;
margin-right: var(--spacing-sm);
font-size: 12px;
}
.event-attack {
background-color: rgba(231, 76, 60, 0.2);
color: #e74c3c;
}
.event-defense {
background-color: rgba(52, 152, 219, 0.2);
color: #3498db;
}
.event-foul {
background-color: rgba(243, 156, 18, 0.2);
color: #f39c12;
}
.event-duel {
background-color: rgba(155, 89, 182, 0.2);
color: #9b59b6;
}
.event-goalkeeper {
background-color: rgba(22, 160, 133, 0.2);
color: #16a085;
}
.event-set-piece {
background-color: rgba(41, 128, 185, 0.2);
color: #2980b9;
}
.event-dynamic {
background-color: rgba(39, 174, 96, 0.2);
color: #27ae60;
}
.event-referee {
background-color: rgba(44, 62, 80, 0.2);
color: #2c3e50;
}
.event-condition {
background-color: rgba(127, 140, 141, 0.2);
color: #7f8c8d;
}
.event-special {
background-color: rgba(241, 196, 15, 0.2);
color: #f1c40f;
}
.dynamic-indicator {
background-color: var(--bg-card);
border-radius: var(--border-radius);
padding: var(--spacing-md);
box-shadow: 0 2px 8px var(--shadow-color);
}
.dynamic-indicator-title {
font-size: 0.9rem;
font-weight: 600;
margin-bottom: var(--spacing-sm);
color: var(--text-secondary);
}
.dynamic-indicator-bar {
height: 8px;
background-color: var(--border-color);
border-radius: 4px;
position: relative;
overflow: hidden;
}
.dynamic-indicator-fill {
position: absolute;
top: 0;
height: 100%;
background-color: var(--primary);
transition: width 0.5s ease, left 0.5s ease;
}
.momentum-indicator-fill {
left: 50%;
transform: translateX(-50%);
background: linear-gradient(90deg, #e74c3c 0%, #f39c12 40%, #2ecc71
60%, #3498db 100%);
}
.intensity-indicator-fill {
background: linear-gradient(90deg, #95a5a6 0%, #f39c12 50%, #e74c3c
100%);
}
.fatigue-indicator-fill {
background: linear-gradient(90deg, #e74c3c 0%, #f39c12 50%, #2ecc71
100%);
}
</style>
</head>
<body>
<div class="container mt-4 mb-5">
<header class="text-center mb-5">
<h1 class="text-2xl font-bold mb-2">Simulateur Pro de Matchs de
Football</h1>
<p class="text-muted">Simulez des matchs en temps réel avec analyses
statistiques avancées</p>
</header>
<div class="tabs">
<button class="tab active" data-tab="import-data">Importer
Données</button>
<button class="tab" data-tab="match-setup">Configuration Match</button>
<button class="tab" data-tab="scheduled-matches">Matchs
Planifiés</button>
<button class="tab" data-tab="match-history">Historique</button>
</div>
<div class="form-group">
<label for="dataInput">Données d'équipes</label>
<textarea id="dataInput" class="form-control"
placeholder="Collez vos données (format avec tabs/tabulations)"></textarea>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">Équipes importées</h2>
</div>
<div class="card-body">
<div class="form-group">
<input type="text" id="teamSearch" class="form-control"
placeholder="Rechercher une équipe...">
</div>
<div class="form-grid">
<div class="form-group">
<label for="matchType">Type de Match</label>
<select id="matchType" class="form-control"
required>
<option
value="Championnat">Championnat</option>
<option value="Coupe">Coupe</option>
<option value="Derby">Derby</option>
<option value="Amical">Amical</option>
<option value="Qualification
Europe">Qualification Europe</option>
<option value="Relégation">Relégation</option>
<option value="Finale de coupe">Finale de
coupe</option>
</select>
</div>
<div class="form-group">
<label for="venue">Stade</label>
<select id="venue" class="form-control">
<option value="home">Domicile</option>
<option value="away">Extérieur</option>
<option value="neutral">Terrain neutre</option>
</select>
</div>
</div>
<div class="form-grid">
<div class="form-group">
<label for="weather">Conditions Météo</label>
<select id="weather" class="form-control">
<option value="clear">Ensoleillé</option>
<option value="cloudy">Nuageux</option>
<option value="rainy">Pluvieux</option>
<option value="snowy">Neigeux</option>
<option value="foggy">Brumeux</option>
</select>
</div>
<div class="form-group">
<label for="crowd">Affluence</label>
<select id="crowd" class="form-control">
<option value="full">Complet</option>
<option value="high">Élevée</option>
<option value="medium"
selected>Moyenne</option>
<option value="low">Faible</option>
<option value="empty">Huis clos</option>
</select>
</div>
</div>
<div class="form-grid">
<div class="form-group">
<label for="pastMatches">Confrontations directes
(optionnel)</label>
<input type="text" id="pastMatches" class="form-
control" placeholder="Ex: 2-1,0-0,3-2">
<div class="text-sm text-muted mt-1">Format:
résultats séparés par virgules</div>
</div>
<div class="form-group">
<label>Forme récente (optionnel)</label>
<div class="flex gap-2">
<input type="text" id="last5Team1" class="form-
control" placeholder="Équipe 1: V,N,D,V,V">
<input type="text" id="last5Team2" class="form-
control" placeholder="Équipe 2: D,V,V,N,D">
</div>
<div class="text-sm text-muted mt-1">Format: V
(victoire), N (nul), D (défaite)</div>
</div>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox" id="advancedStats"
checked>
<span class="toggle-slider"></span>
</label>
<span>Statistiques avancées</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox" id="realismMode"
checked>
<span class="toggle-slider"></span>
</label>
<span>Mode réalisme</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox"
id="backgroundNotifications" checked>
<span class="toggle-slider"></span>
</label>
<span>Notifications en arrière-plan</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox"
id="intensityFactor">
<span class="toggle-slider"></span>
</label>
<span>Facteur d'intensité aléatoire</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox"
id="scheduleForLater">
<span class="toggle-slider"></span>
</label>
<span>Planifier pour plus tard</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox" id="monteCarloMode">
<span class="toggle-slider"></span>
</label>
<span>Simulation Monte Carlo</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox" id="detailedEvents"
checked>
<span class="toggle-slider"></span>
</label>
<span>Événements détaillés</span>
</div>
</div>
</div>
<div class="form-actions">
<button type="button" id="resetBtn" class="btn btn-
secondary">
<i class="fas fa-undo"></i> Réinitialiser
</button>
<button type="button" id="startSimulationBtn"
class="btn btn-primary">
<i class="fas fa-play"></i> Lancer la simulation
</button>
</div>
</form>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Statistiques du match</h3>
</div>
<div class="card-body">
<div class="match-stats">
<div class="match-stat">
<div class="stat-label">Possession</div>
<div class="stat-values">
<div class="stat-value"
id="homePossession">50%</div>
<div class="stat-value"
id="awayPossession">50%</div>
</div>
<div class="stat-bar">
<div class="stat-fill" id="possessionBar"
style="width: 50%;"></div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Tirs</div>
<div class="stat-values">
<div class="stat-value"
id="homeShots">0</div>
<div class="stat-value"
id="awayShots">0</div>
</div>
<div class="stat-bar">
<div class="stat-fill" id="shotsBar"
style="width: 50%;"></div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Tirs cadrés</div>
<div class="stat-values">
<div class="stat-value"
id="homeShotsOnTarget">0</div>
<div class="stat-value"
id="awayShotsOnTarget">0</div>
</div>
<div class="stat-bar">
<div class="stat-fill"
id="shotsOnTargetBar" style="width: 50%;"></div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Corners</div>
<div class="stat-values">
<div class="stat-value"
id="homeCorners">0</div>
<div class="stat-value"
id="awayCorners">0</div>
</div>
<div class="stat-bar">
<div class="stat-fill" id="cornersBar"
style="width: 50%;"></div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Cartons jaunes</div>
<div class="stat-values">
<div class="stat-value"
id="homeYellowCards">0</div>
<div class="stat-value"
id="awayYellowCards">0</div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Cartons rouges</div>
<div class="stat-values">
<div class="stat-value"
id="homeRedCards">0</div>
<div class="stat-value"
id="awayRedCards">0</div>
</div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Attaques
dangereuses</div>
<div class="stat-values">
<div class="stat-value"
id="homeDangerousAttacks">0</div>
<div class="stat-value"
id="awayDangerousAttacks">0</div>
</div>
<div class="stat-bar">
<div class="stat-fill"
id="dangerousAttacksBar" style="width: 50%;"></div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Momentum</div>
<div class="stat-values">
<div class="stat-value"
id="homeMomentum">0</div>
<div class="stat-value"
id="awayMomentum">0</div>
</div>
<div class="stat-bar">
<div class="stat-fill" id="momentumBar"
style="width: 50%;"></div>
</div>
</div>
<div class="match-stat">
<div class="stat-label">Fatigue</div>
<div class="stat-values">
<div class="stat-value"
id="homeFatigue">100%</div>
<div class="stat-value"
id="awayFatigue">100%</div>
</div>
<div class="stat-bar">
<div class="stat-fill" id="fatigueBar"
style="width: 50%;"></div>
</div>
</div>
</div>
</div>
<div class="dynamic-indicator">
<div class="dynamic-indicator-title">Intensité
du match</div>
<div class="dynamic-indicator-bar">
<div class="dynamic-indicator-fill
intensity-indicator-fill" id="intensityIndicator" style="width: 40%;"></div>
</div>
<div class="flex justify-between text-xs mt-1">
<span>Faible</span>
<span>Élevée</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-between mt-4">
<button id="pauseResumeBtn" class="btn btn-secondary">
<i class="fas fa-pause"></i> Pause
</button>
<button id="stopSimulationBtn" class="btn btn-danger">
<i class="fas fa-stop"></i> Arrêter
</button>
</div>
</div>
// Variables globales
let teamData = {}; // Stockage des données d'équipes
let matchHistory = []; // Historique des matchs
let currentMatch = null; // Match en cours de simulation
let simulationTimer = null; // Timer pour la simulation
let simulationPaused = false; // État de pause
let scheduledMatches = []; // Matchs planifiés
let scheduledMatchesTimers = {}; // Timers pour les matchs planifiés
// Initialiser l'application
document.addEventListener('DOMContentLoaded', function() {
// Charger les données depuis le stockage local
loadData();
// Vérifier le thème
checkTheme();
// Format YYYY-MM-DDThh:mm
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
document.getElementById('scheduleDateTime').value = `${year}-${month}-$
{day}T${hours}:${minutes}`;
}
});
const storedScheduledMatches =
localStorage.getItem('simulatorScheduledMatches');
if (storedScheduledMatches) {
scheduledMatches = JSON.parse(storedScheduledMatches);
updateScheduledMatchesDisplay();
}
} catch (error) {
console.error('Erreur lors du chargement des données:', error);
showNotification('Erreur lors du chargement des données', 'error');
}
}
} catch (error) {
console.error(`Erreur de parsing pour l'équipe ${teamName}:`,
error);
}
}
}
}
// Ajout des gestionnaires pour les boutons qui n'étaient pas dans la fonction
initiale
if (document.getElementById('newSimulationBtn')) {
document.getElementById('newSimulationBtn').addEventListener('click',
newSimulation);
}
if (document.getElementById('saveResultBtn')) {
document.getElementById('saveResultBtn').addEventListener('click',
saveMatchResult);
}
if (document.getElementById('clearHistoryBtn')) {
document.getElementById('clearHistoryBtn').addEventListener('click',
clearMatchHistory);
}
}
// Construire le contenu
notification.innerHTML = `
${icon}
<div class="notification-message">${message}</div>
<span class="notification-close">×</span>
`;
// Ajouter au conteneur
notificationContainer.appendChild(notification);
// Vérifier le thème
function checkTheme() {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme:
dark)').matches) {
document.documentElement.classList.add('dark');
document.getElementById('themeToggle').innerHTML = '<i class="fas fa-
sun"></i>';
} else {
document.documentElement.classList.remove('dark');
document.getElementById('themeToggle').innerHTML = '<i class="fas fa-
moon"></i>';
}
}
// Basculer le thème
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
document.getElementById('themeToggle').innerHTML = '<i class="fas fa-
moon"></i>';
} else {
document.documentElement.classList.add('dark');
document.getElementById('themeToggle').innerHTML = '<i class="fas fa-
sun"></i>';
}
}
teams.forEach(team => {
const option1 = document.createElement('option');
option1.value = team;
option1.textContent = team;
team1Select.appendChild(option1);
team2Select.appendChild(option2);
});
if (Object.keys(teamData).length === 0) {
container.innerHTML = '<p class="text-center text-muted py-4">Aucune équipe
disponible. Veuillez charger des données d\'équipes.</p>';
return;
}
if (searchTerm) {
const lowerSearchTerm = searchTerm.toLowerCase();
teams = teams.filter(team => team.toLowerCase().includes(lowerSearchTerm));
}
document.querySelectorAll('.btn-select').forEach(btn => {
btn.addEventListener('click', function() {
const team = this.getAttribute('data-team');
selectTeam(team);
});
});
}
// Réinitialiser le formulaire
function resetForm() {
document.getElementById('team1').value = '';
document.getElementById('team2').value = '';
document.getElementById('matchType').value = 'Championnat';
document.getElementById('venue').value = 'home';
document.getElementById('weather').value = 'clear';
document.getElementById('crowd').value = 'medium';
document.getElementById('last5Team1').value = '';
document.getElementById('last5Team2').value = '';
document.getElementById('commentaryEnabled').checked = true;
document.getElementById('advancedStats').checked = true;
document.getElementById('realismMode').checked = true;
document.getElementById('intensityFactor').checked = false;
document.getElementById('backgroundNotifications').checked = true;
document.getElementById('scheduleForLater').checked = false;
document.getElementById('monteCarloMode').checked = false;
document.getElementById('detailedEvents').checked = true;
document.getElementById('scheduleDatetimeContainer').style.display = 'none';
teamData = demoTeams;
// Réinitialiser l'interface
populateTeamOptions();
updateTeamsContainer();
updateMatchHistory();
updateScheduledMatchesDisplay();
container.innerHTML = '';
if (scheduledMatches.length === 0) {
document.getElementById('scheduledMatchesEmpty').style.display = 'block';
return;
}
document.getElementById('scheduledMatchesEmpty').style.display = 'none';
container.appendChild(matchElement);
});
document.querySelectorAll('.scheduled-match-buttons .btn-danger').forEach(btn
=> {
btn.addEventListener('click', function() {
const matchId = this.getAttribute('data-match-id');
deleteScheduledMatch(matchId);
});
});
}
/* =========================================================
FONCTIONS AVANCÉES DE CLASSIFICATION ET CALCUL
========================================================= */
// Score offensif
if (goalsPerMatch > 2.0) offensiveScore += 3;
else if (goalsPerMatch > 1.5) offensiveScore += 2;
else if (goalsPerMatch > 1.0) offensiveScore += 1;
// Score défensif
if (goalsAgainstPerMatch < 1.0) defensiveScore += 3;
else if (goalsAgainstPerMatch < 1.3) defensiveScore += 2;
else if (goalsAgainstPerMatch < 1.5) defensiveScore += 1;
// Score de possession
if (possession > 60) possessionScore += 3;
else if (possession > 55) possessionScore += 2;
else if (possession > 50) possessionScore += 1;
else if (possession < 40) possessionScore -= 2;
else if (possession < 45) possessionScore -= 1;
// Score physique
if (yellowCards > 2.0) physicalScore += 2;
else if (yellowCards > 1.5) physicalScore += 1;
// Facteurs contextuels
let matchTypeFactor = 1.0;
switch (matchType) {
case "Derby": matchTypeFactor = 1.1; break;
case "Coupe": matchTypeFactor = 0.95; break;
case "Finale de coupe": matchTypeFactor = 0.9; break;
case "Qualification Europe": matchTypeFactor = 1.15; break;
case "Relégation": matchTypeFactor = 1.2; break;
default: matchTypeFactor = 1.0;
}
// Facteur de lieu
let venueFactor = 1.0;
if (venue === "home") venueFactor = 1.1;
else if (venue === "away") venueFactor = 0.9;
// Facteur météo
let weatherFactor = 1.0;
if (weather === "rainy" || weather === "snowy") weatherFactor = 0.9;
else if (weather === "foggy") weatherFactor = 0.95;
// Facteur affluence
let crowdFactor = 1.0;
if (crowd === "full" && venue === "home") crowdFactor = 1.05;
else if (crowd === "empty") crowdFactor = 0.95;
// Calcul final
let finalXG = baseXGPerMatch * attackIndex * defenseIndex * possessionFactor *
matchTypeFactor * venueFactor * weatherFactor * crowdFactor;
results.forEach(result => {
const cleanResult = result.trim();
if (cleanResult === 'V') wins++;
else if (cleanResult === 'N') draws++;
else if (cleanResult === 'D') losses++;
});
return {
last5: formString,
winRate: winRate,
drawRate: drawRate,
lossRate: lossRate,
form: (wins * 3 + draws * 1) / total // Score de forme entre 0 et 3
};
}
results.forEach(result => {
const parts = result.trim().split('-');
if (parts.length === 2) {
const score1 = parseInt(parts[0]);
const score2 = parseInt(parts[1]);
return {
team1Wins: team1Wins,
team2Wins: team2Wins,
draws: draws,
total: team1Wins + team2Wins + draws,
h2hAdvantage: team1Wins - team2Wins
};
}
/* =========================================================
LOGIQUE DE SIMULATION DE MATCH
========================================================= */
// Options avancées
const commentaryEnabled = document.getElementById('commentaryEnabled').checked;
const advancedStats = document.getElementById('advancedStats').checked;
const realismMode = document.getElementById('realismMode').checked;
const intensityFactor = document.getElementById('intensityFactor').checked;
const backgroundNotifications =
document.getElementById('backgroundNotifications').checked;
const detailedEvents = document.getElementById('detailedEvents').checked;
// Normaliser la possession
const totalPoss = team1Possession + team2Possession;
team1Possession = Math.round((team1Possession / totalPoss) * 100);
team2Possession = 100 - team1Possession;
// Paramètres supplémentaires
venue: venue,
weather: weather,
crowd: crowd,
// Options avancées
commentaryEnabled: commentaryEnabled,
advancedStats: advancedStats,
realismMode: realismMode,
intensityFactor: intensityFactor,
backgroundNotifications: backgroundNotifications,
detailedEvents: detailedEvents,
// Données additionnelles
formTeam1: formTeam1,
formTeam2: formTeam2,
h2hStats: h2hStats,
// Facteurs de force
team1StrengthFactor: team1StrengthFactor,
team2StrengthFactor: team2StrengthFactor,
expectedStats: {
shots: { team1: expectedShotsTeam1, team2: expectedShotsTeam2 },
corners: { team1: expectedCornersTeam1, team2: expectedCornersTeam2 }
},
// Données de match
score1: 0,
score2: 0,
minute: 0,
status: "En attente",
possessionTeam: team1Possession > team2Possession ? team1 : team2,
intensity: 1.0,
team1Style: team1Style,
team2Style: team2Style,
// Facteurs dynamiques
fatigue: {
team1: 1.0,
team2: 1.0
},
momentum: {
team1: 0,
team2: 0
},
// Historique d'événements
events: [],
// Statistiques de match
stats: {
yellowCards: {
team1: 0,
team2: 0
},
redCards: {
team1: 0,
team2: 0
},
dangerousAttacks: {
team1: 0,
team2: 0
},
shotsOnTarget: {
team1: 0,
team2: 0
},
shots: {
team1: 0,
team2: 0
},
corners: {
team1: 0,
team2: 0
},
possession: {
team1: team1Possession,
team2: team2Possession
}
},
// Timing
startTime: new Date(),
endTime: null
};
return match;
}
// Validation
if (!team1 || !team2) {
showNotification('Veuillez sélectionner les deux équipes', 'warning');
return;
}
if (!scheduledDateTime) {
showNotification('Veuillez spécifier une date et heure pour le match
planifié', 'warning');
return;
}
// Convertir en timestamp
const scheduledTimestamp = new Date(scheduledDateTime).getTime();
const now = Date.now();
// Réinitialiser le formulaire
resetForm();
return;
}
// Initialiser la simulation
currentMatch = initializeMatchSimulation(team1, team2, matchType);
if (!currentMatch) {
showNotification('Erreur lors de l\'initialisation de la simulation',
'error');
return;
}
// Démarrer la simulation
currentMatch.status = "En cours";
simulationPaused = false;
// Notification de mi-temps
showNotification(` Mi-temps : ${currentMatch.team1} ${currentMatch.score1}-
${currentMatch.score2} ${currentMatch.team2}`, 'info');
// Notification de reprise
showNotification(`🏁 2ème mi-temps : ${currentMatch.team1} $
{currentMatch.score1}-${currentMatch.score2} ${currentMatch.team2}`, 'info');
simulateMatchEvents();
updateMatchDisplay();
// Continuer la simulation
simulationTimer = setTimeout(simulateMatchMinutes,
CURRENT_SIMULATION_SPEED);
}
}, 3000); // Pause de 3 secondes pour la mi-temps
return;
}
// Ajouter un commentaire
addMatchEvent(currentMatch, `🎯 Tir cadré de ${teamName}!`);
// Notification
showNotification(`⚽ BUT! ${teamName} a marqué!`, 'success');
} else {
// Ajouter un commentaire pour l'arrêt
addMatchEvent(currentMatch, `🧤 Bel arrêt du gardien de $
{shootingTeam === 'team1' ? currentMatch.team2 : currentMatch.team1}!`);
}
} else {
// Tir non cadré
addMatchEvent(currentMatch, `⚽ Tir non cadré de ${teamName}.`);
}
}
currentMatch.stats.corners[cornerTeam]++;
addMatchEvent(currentMatch, `🚩 Corner pour ${teamName}.`);
}
currentMatch.stats.yellowCards[cardTeam]++;
addMatchEvent(currentMatch, `🟨 Carton jaune pour ${teamName}.`);
}
currentMatch.stats.redCards[cardTeam]++;
addMatchEvent(currentMatch, `🟥 Carton rouge pour ${teamName}!`);
// Notification
showNotification(`🟥 Carton rouge pour ${teamName}!`, 'error');
}
if (currentMatch.stats.redCards.team2 > 0) {
currentMatch.fatigue.team2 = Math.max(0.6, currentMatch.fatigue.team2 -
0.002 * currentMatch.stats.redCards.team2);
}
}
// Ajouter à l'historique
match.events.push(event);
document.getElementById('matchInfo').textContent = matchInfoText;
// Possession
const team1Possession = Math.round(currentMatch.stats.possession.team1);
const team2Possession = 100 - team1Possession;
// Tirs
document.getElementById('homeShots').textContent =
currentMatch.stats.shots.team1;
document.getElementById('awayShots').textContent =
currentMatch.stats.shots.team2;
const shotsTotal = currentMatch.stats.shots.team1 +
currentMatch.stats.shots.team2;
const shotsPct = shotsTotal > 0 ? Math.round((currentMatch.stats.shots.team1 /
shotsTotal) * 100) : 50;
document.getElementById('shotsBar').style.width = shotsPct + '%';
// Tirs cadrés
document.getElementById('homeShotsOnTarget').textContent =
currentMatch.stats.shotsOnTarget.team1;
document.getElementById('awayShotsOnTarget').textContent =
currentMatch.stats.shotsOnTarget.team2;
const shotsOnTargetTotal = currentMatch.stats.shotsOnTarget.team1 +
currentMatch.stats.shotsOnTarget.team2;
const shotsOnTargetPct = shotsOnTargetTotal > 0 ?
Math.round((currentMatch.stats.shotsOnTarget.team1 / shotsOnTargetTotal) * 100) :
50;
document.getElementById('shotsOnTargetBar').style.width = shotsOnTargetPct +
'%';
// Corners
document.getElementById('homeCorners').textContent =
currentMatch.stats.corners.team1;
document.getElementById('awayCorners').textContent =
currentMatch.stats.corners.team2;
const cornersTotal = currentMatch.stats.corners.team1 +
currentMatch.stats.corners.team2;
const cornersPct = cornersTotal > 0 ?
Math.round((currentMatch.stats.corners.team1 / cornersTotal) * 100) : 50;
document.getElementById('cornersBar').style.width = cornersPct + '%';
// Cartons
document.getElementById('homeYellowCards').textContent =
currentMatch.stats.yellowCards.team1;
document.getElementById('awayYellowCards').textContent =
currentMatch.stats.yellowCards.team2;
document.getElementById('homeRedCards').textContent =
currentMatch.stats.redCards.team1;
document.getElementById('awayRedCards').textContent =
currentMatch.stats.redCards.team2;
// xG
const xgFactor = currentMatch.minute / 90;
const currentXgTeam1 = (currentMatch.xgTeam1 * xgFactor).toFixed(2);
const currentXgTeam2 = (currentMatch.xgTeam2 * xgFactor).toFixed(2);
document.getElementById('homeXG').textContent = currentXgTeam1;
document.getElementById('awayXG').textContent = currentXgTeam2;
const xgTotal = parseFloat(currentXgTeam1) + parseFloat(currentXgTeam2);
const xgPct = xgTotal > 0 ? Math.round((parseFloat(currentXgTeam1) /
xgTotal) * 100) : 50;
document.getElementById('xgBar').style.width = xgPct + '%';
// Attaques dangereuses
document.getElementById('homeDangerousAttacks').textContent =
currentMatch.stats.dangerousAttacks?.team1 || 0;
document.getElementById('awayDangerousAttacks').textContent =
currentMatch.stats.dangerousAttacks?.team2 || 0;
const dangerousAttacksTotal = (currentMatch.stats.dangerousAttacks?.team1
|| 0) + (currentMatch.stats.dangerousAttacks?.team2 || 0);
const dangerousAttacksPct = dangerousAttacksTotal > 0 ?
Math.round(((currentMatch.stats.dangerousAttacks?.team1 || 0) /
dangerousAttacksTotal) * 100) : 50;
document.getElementById('dangerousAttacksBar').style.width =
dangerousAttacksPct + '%';
// Momentum et fatigue
document.getElementById('homeMomentum').textContent =
Math.round(currentMatch.momentum.team1 * 20); // Sur 100
document.getElementById('awayMomentum').textContent =
Math.round(currentMatch.momentum.team2 * 20);
document.getElementById('momentumBar').style.width = Math.round(50 +
(currentMatch.momentum.team1 * 5)) + '%';
document.getElementById('homeFatigue').textContent =
Math.round(currentMatch.fatigue.team1 * 100) + '%';
document.getElementById('awayFatigue').textContent =
Math.round(currentMatch.fatigue.team2 * 100) + '%';
document.getElementById('fatigueBar').style.width =
Math.round((currentMatch.fatigue.team1 / (currentMatch.fatigue.team1 +
currentMatch.fatigue.team2)) * 100) + '%';
} else {
document.getElementById('advancedStatsContainer').style.display = 'none';
}
}
// Indicateur de momentum
const momentumIndicator = document.getElementById('momentumIndicator');
const momentumDifference = currentMatch.momentum.team1 -
currentMatch.momentum.team2;
// Indicateur d'intensité
const intensityIndicator = document.getElementById('intensityIndicator');
goalTeam.textContent = scoringTeam;
goalAnimation.style.display = 'flex';
if (simulationPaused) {
// Reprendre la simulation
simulationPaused = false;
document.getElementById('pauseResumeBtn').innerHTML = '<i class="fas fa-
pause"></i> Pause';
simulationTimer = setTimeout(simulateMatchMinutes,
CURRENT_SIMULATION_SPEED);
// Notification
showNotification('Simulation reprise', 'info');
} else {
// Mettre en pause
simulationPaused = true;
document.getElementById('pauseResumeBtn').innerHTML = '<i class="fas fa-
play"></i> Reprendre';
clearTimeout(simulationTimer);
// Notification
showNotification('Simulation en pause', 'info');
}
}
// Arrêter la simulation
function stopSimulation() {
if (!currentMatch) return;
// Demander confirmation
if (confirm('Êtes-vous sûr de vouloir arrêter la simulation en cours?')) {
clearTimeout(simulationTimer);
finishMatch(true); // Forcer l'arrêt
// Terminer le match
function finishMatch(forceStop = false) {
if (!currentMatch) return;
// Arrêter le timer
clearTimeout(simulationTimer);
// Indiquer le résultat
const resultText = currentMatch.score1 > currentMatch.score2 ?
`${currentMatch.team1} remporte le match!` :
(currentMatch.score1 < currentMatch.score2 ?
`${currentMatch.team2} remporte le match!` :
`Match nul entre ${currentMatch.team1} et ${currentMatch.team2}!`);
addMatchEvent(currentMatch, `🏆 ${resultText}`);
} else {
addMatchEvent(currentMatch, "⛔ Simulation arrêtée manuellement.");
}
<div>
<div class="stat-row flex justify-between mb-1">
<span>${currentMatch.stats.corners.team1}</span>
<span class="font-medium">Corners</span>
<span>${currentMatch.stats.corners.team2}</span>
</div>
<div class="stat-bar mb-3">
<div class="stat-fill" style="width: $
{calculatePercentage(currentMatch.stats.corners.team1,
currentMatch.stats.corners.team2)}%;"></div>
</div>
<div class="match-events-summary">
<h4 class="font-bold mb-2">Faits marquants</h4>
<ul class="pl-0 list-none">
`;
importantEvents = importantEvents.concat(additionalEvents);
}
summaryHTML += `
</ul>
</div>
`;
// Effacer le commentaire
const commentaryList = document.getElementById('commentaryList');
if (commentaryList) {
commentaryList.innerHTML = '';
}
clearTimeout(simulationTimer);
currentMatch = null;
// Ajouter à l'historique
matchHistory.push(matchResult);
// Notification
showNotification('Résultat du match sauvegardé', 'success');
container.innerHTML = '';
if (matchHistory.length === 0) {
container.innerHTML = '<p class="text-center text-muted py-4">Aucun match
dans l\'historique.</p>';
return;
}
// Trier les matchs par date (du plus récent au plus ancien)
matchHistory.sort((a, b) => new Date(b.date) - new Date(a.date));
container.appendChild(matchElement);
});
document.querySelectorAll('.btn-delete').forEach(btn => {
btn.addEventListener('click', function() {
const matchIndex = parseInt(this.getAttribute('data-match-index'));
deleteMatchFromHistory(matchIndex);
});
});
}
if (matchesToStart.length > 0) {
// Afficher une notification pour chaque match à démarrer
matchesToStart.forEach(match => {
showNotification(`Le match planifié ${match.team1} vs ${match.team2}
peut être démarré!`, 'info');
});
}
}
// Notification
showNotification(`Match ${matchOptions.team1} vs ${matchOptions.team2} prêt à
démarrer!`, 'info');
}
// Supprimer de la liste
scheduledMatches.splice(matchIndex, 1);
saveData();
document.getElementById('editStyle').textContent = style;
document.getElementById('editStyle').className = `badge badge-secondary style-$
{style.toLowerCase()}`;
document.getElementById('editProfile').textContent = profile;
document.getElementById('editProfile').className = `badge badge-info category-$
{profile.toLowerCase().replace(/\s+/g, '-')}`;
document.getElementById('editDiscipline').textContent = `${discipline}/5`;
}
// Fermer la modal
closeEditModal();
// Notification
showNotification(`Données de ${teamName} mises à jour avec succès`, 'success');
}
/* =========================================================
SIMULATION MONTE CARLO ET FONCTIONS AVANCÉES
========================================================= */
// Données statistiques
this.results = [];
this.team1WinRate = 0;
this.team2WinRate = 0;
this.drawRate = 0;
this.mostCommonScore = null;
}
// Exécuter la simulation
runSimulation(iterations = 10000) {
this.results = [];
let team1Wins = 0;
let team2Wins = 0;
let draws = 0;
// Enregistrer le résultat
this.results.push({ goals1, goals2 });
return {
team1WinRate: this.team1WinRate,
team2WinRate: this.team2WinRate,
drawRate: this.drawRate,
mostCommonScore: this.mostCommonScore,
results: this.results
};
}
const L = Math.exp(-lambda);
let p = 1.0;
let k = 0;
do {
k++;
p *= Math.random();
} while (p > L);
return k - 1;
}
this.results.forEach(result => {
totalGoals1 += result.goals1;
totalGoals2 += result.goals2;
return `
<div class="monte-carlo-results">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div class="card p-4">
<h4 class="font-bold mb-2">Probabilités de résultat</h4>
<div class="flex flex-col gap-2">
<div class="flex justify-between">
<span>Victoire ${this.team1}:</span>
<span class="font-medium">$
{format(this.team1WinRate)}%</span>
</div>
<div class="bg-gray-200 dark:bg-gray-700 h-2 rounded-
full overflow-hidden">
<div class="bg-blue-500 h-full" style="width: $
{this.team1WinRate}%"></div>
</div>
// Créer la simulation
const simulation = new MonteCarloSimulation(team1, team2, matchType, venue,
weather, crowd);
// Afficher un loader
document.getElementById('loaderContainer').style.display = 'flex';
// Cacher le loader
document.getElementById('loaderContainer').style.display = 'none';
}, 100);
}
/* =========================================================
OPTIMISATIONS ET ANIMATIONS AVANCÉES
========================================================= */
if (DEBUG_MODE) {
console.log('Données équipes:', teamData);
console.log('Historique matchs:', matchHistory);
console.log('Matchs planifiés:', scheduledMatches);
if (currentMatch) console.log('Match en cours:', currentMatch);
}
}
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
// Séquence de notes
const sequence = [
{ frequency: 440, duration: 150 },
{ frequency: 554.37, duration: 150 },
{ frequency: 659.25, duration: 150 },
{ frequency: 880, duration: 300 }
];
sequence.forEach(note => {
oscillator.frequency.setValueAtTime(note.frequency, time);
gainNode.gain.setValueAtTime(0.5, time);
time += note.duration / 1000;
gainNode.gain.exponentialRampToValueAtTime(0.001, time + 0.1);
});
oscillator.start();
oscillator.stop(time + 0.1);
} catch (error) {
console.error('Erreur lors de la lecture du son:', error);
}
}
// Bouton précédent
if (page > 0) {
const prevButton = document.createElement('button');
prevButton.className = 'pagination-btn btn btn-sm btn-secondary';
prevButton.innerHTML = '<i class="fas fa-chevron-left"></i>';
prevButton.addEventListener('click', () => {
currentPage--;
renderPage(currentPage);
});
paginationElement.appendChild(prevButton);
}
// Bouton suivant
if (page < totalPages - 1) {
const nextButton = document.createElement('button');
nextButton.className = 'pagination-btn btn btn-sm btn-secondary';
nextButton.innerHTML = '<i class="fas fa-chevron-right"></i>';
nextButton.addEventListener('click', () => {
currentPage++;
renderPage(currentPage);
});
paginationElement.appendChild(nextButton);
}
container.appendChild(paginationElement);
}
};
return canvas;
}
/* =========================================================
GESTION DES ÉVÉNEMENTS SUPPLÉMENTAIRES
========================================================= */
// Créer le bouton
const btn = document.createElement('button');
btn.id = 'monteCarloBtn';
btn.className = 'btn btn-secondary';
btn.innerHTML = '<i class="fas fa-chart-bar"></i> Prédiction Monte Carlo';
btn.addEventListener('click', runMonteCarloSimulation);
container.appendChild(shareButton);
}
// Correspondance exacte
if (teamLC === query) return 1.0;
// Correspondance au début
if (teamLC.startsWith(query)) score += 0.8;
// Correspondance partielle
if (teamLC.includes(query)) score += 0.6;
// Critères supplémentaires
if (includeCriteria.length > 0) {
const data = teamData[team];
for (const criterion of includeCriteria) {
switch (criterion) {
case 'offensive':
if (data.TeamStyle === 'Offensive') score += 0.2;
break;
case 'defensive':
if (data.TeamStyle === 'Defensive') score += 0.2;
break;
case 'grande':
if (data.TeamCategory === 'Grande') score += 0.2;
break;
case 'moyenne':
if (data.TeamCategory === 'Moyenne') score += 0.2;
break;
case 'petite':
if (data.TeamCategory === 'Petite') score += 0.2;
break;
}
}
}
if (results.length === 0) {
suggestionsContainer.style.display = 'none';
return;
}
suggestion.addEventListener('click', function() {
searchInput.value = team;
suggestionsContainer.style.display = 'none';
// Déclencher l'affichage filtré
updateTeamsContainer(team);
});
suggestionsContainer.appendChild(suggestion);
});
suggestionsContainer.style.display = 'block';
});
</script>
</body>
</html>
Ce document détaille toutes les fonctionnalités, les modèles utilisés, les types de
données prises en compte, ainsi que les règles de simulation.
---
📌 Modèles utilisés :
Modèle d’intensité dynamique : simulation basée sur l’évolution du match (ex : une
équipe qui mène 2-0 jouera plus défensif).
Processus de Poisson modifié : pour simuler la distribution des buts en fonction
des forces des équipes.
---
📌 Modèles utilisés :
Modèle de Markov pour simuler les transitions d’état dans le match (possession,
attaque, défense, etc.).
---
Chaque joueur aura une influence basée sur ses caractéristiques statistiques :
📌 Modèles utilisés :
---
📌 Modèles utilisés :
---
L’Expected Goals (xG) permet d’évaluer la probabilité qu’un tir aboutisse à un but
en fonction de :
📌 Modèles utilisés :
---
📌 Modèles utilisés :
---
🔹 7. Gestion des Remplacements et Adaptation des Tactiques
📌 Modèles utilisés :
Modèle d’apprentissage automatique pour prédire les choix de remplacements les plus
optimaux.
---
📌 Technologies utilisées :
Stockage des résultats dans une base de données pour consultation ultérieure.
---
je veux ajouter plusieurs formulaires comme le formulaire qui permet de coler les
données présente directement dans le code initial
mais on va carrément repenser l'application avec les données que je possède il y
aura plusieurs catégories de formulaire pour plusieurs catégories de données bien
précise ce qui se vont utiliser dans le processus
---
1 1 Fonctionnalités Globales
1️⃣
---
📌 Performances Offensives
📌 Performances Défensives
📌 Modèle Monte-Carlo
➡️ Simule plusieurs scénarios pour obtenir un résultat réaliste.
📌 Processus de Poisson
➡️ Génère les probabilités des buts en fonction des xG et xGA.
📌 Chaînes de Markov
➡️ Simule les transitions d’état (attaque, défense, possession, etc.).
📌 Modèle Bayésien
➡️ Ajuste les résultats du match en fonction des performances en direct.
---
---
✅ Interface Interactive
Sélection des équipes et joueurs via une interface fluide et ergonomique.
✅ Technologies et Frameworks
---
📌 Performances Offensives
📌 Performances Défensives
---
📌 Modèle Monte-Carlo
➡️ Simule plusieurs scénarios probables pour obtenir des résultats réalistes.
📌 Processus de Poisson
➡️ Génère la probabilité des buts en fonction des statistiques d’attaque/défense.
📌 Chaînes de Markov
➡️ Modélise les transitions entre les différentes phases du jeu (attaque, défense,
possession).
📌 Modèle Bayésien
➡️ Ajuste les résultats du match en fonction des performances en direct.
---
---
✅ Interface Interactive
✅ Technologies et Frameworks
JavaScript : Logique et simulation du match.
L'objectif est que l’utilisateur puisse coller les données et que l’application les
détecte automatiquement pour :
✅ Associer les statistiques aux bonnes équipes et aux bons joueurs.
✅ Classer les données par catégories (Tirs, Passes, Défense, etc.).
✅ Identifier automatiquement les titulaires et remplaçants d’un match.
✅ Affecter les statistiques des joueurs aux bonnes positions (Attaquants, Milieux,
Défenseurs, Gardiens).
---
📌 Méthode utilisée :
📌 Étapes :
{
"teams": {
"Angers": {
"shooting": { "Gls": 25, "Sh": 244, "SoT": 70, "xG": 30.0 },
"passing": { "Cmp%": 78.8, "PrgP": 746 },
"defense": { "Tkl": 478, "Int": 274 },
"players": {
"Himad Abdelli": {
"pos": "MF, FW",
"Gls": 6,
"Sh": 24,
"SoT": 6,
"xG": 5.6
},
"Gardien": {
"GA": 42,
"PSxG": 52.3,
"Stp%": 9.6
}
}
}
}
}
---
📌 Méthode utilisée :
✔️ Détection des positions (FW, MF, DF, GK) via le texte.
✔️ Vérification des minutes jouées (Min) pour séparer titulaires et remplaçants.
✔️ Association automatique des statistiques aux joueurs concernés.
Exemple :
{
"match": {
"team1": "Angers",
"team2": "Auxerre",
"lineups": {
"Angers": {
"starters": ["Himad Abdelli", "Joueur X", "Joueur Y"],
"substitutes": ["Joueur Z", "Joueur W"]
}
}
}
}
---
📌 Objectif : Une fois les données importées, elles doivent être liées correctement
aux équipes et aux joueurs.
📌 Méthode :
✔️ Vérification du nom de l’équipe pour éviter les erreurs.
✔️ Attribution des statistiques correctes aux joueurs concernés.
✔️ Regroupement des titulaires & remplaçants avec leurs performances.
{
"match": {
"Angers": {
"players": {
"Himad Abdelli": {
"pos": "MF, FW",
"Gls": 6,
"Sh": 24,
"SoT": 6,
"xG": 5.6
}
}
}
}
}
---
📌 Méthode :
✔️ Normalisation des valeurs (ex: Gls/90, xG, Cmp% pour éviter les erreurs).
✔️ Conversion des performances des joueurs en probabilités d’événements pendant le
match.
✔️ Intégration des valeurs dans le modèle Monte-Carlo / Processus de Poisson.
📌 Exemple :
{
"probabilities": {
"Himad Abdelli": {
"goal_chance": 0.10,
"assist_chance": 0.05,
"shot_conversion": 0.25
}
}
}
---
---
📌 Performances Offensives
📌 Performances Défensives
---
📌 Modèle Monte-Carlo
➡️ Simule plusieurs scénarios probables pour obtenir des résultats réalistes.
📌 Processus de Poisson
➡️ Génère la probabilité des buts en fonction des statistiques d’attaque/défense.
📌 Chaînes de Markov
➡️ Modélise les transitions entre les différentes phases du jeu (attaque, défense,
possession).
📌 Modèle Bayésien
➡️ Ajuste les résultats du match en fonction des performances en direct.
---
---
✅ Interface Interactive
✅ Technologies et Frameworks
Tu oublies que je possède plusieurs types de données donc dans ton résumé là dans
toute l'utilisation de a à z là il faut la spécifier les valeurs qui sont utilisés
par catégorie de données pour toutes les tous les déroulement de la simulation le
processus de calcul tout tout tout tout
J'avais oublié de te spécifier en fait tu dois être automatique en fait parce que
l'utilisateur colle les données concernant les équipes les joueurs attends je vais
te montrer tous les données en fait
Glossary
Pl
90s Gls Sh SoT SoT% Sh/90 SoT/90 G/Sh G/SoT Dist FK PK PKatt xG npxG npxG/Sh G-xG
np:G-xG Angers 25 26.0 25 244 70 28.7 9.38 2.69 0.08 0.29 18.7 12 5 6 30.0 25.3
0.11 -5.0 -5.3 Auxerre 28 26.0 37 291 117 40.2 11.19 4.50 0.11 0.28 17.3 7 4 5 33.2
29.5 0.10 +3.8 +3.5 Brest 28 25.0 38 304 117 38.5 12.16 4.68 0.10 0.26 17.1 5 7 9
35.9 28.9 0.10 +2.1 +2.1
Squad Advanced Goalkeeping 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats
Opponent Stats Goals Expected Launched Passes Goal Kicks Crosses
Sweeper Squad # Pl 90s GA PKA FK CK OG PSxG PSxG/SoT
PSxG+/- /90 Cmp Att Cmp% Att (GK) Thr Launch% AvgLen
Att Launch% AvgLen Opp Stp Stp% #OPA #OPA/90 AvgDist
Angers 1 26.0 42 3 0 7 0 52.3 0.33 +10.3 +0.39 112
334 33.5 579 146 38.0 35.3 210 54.3 42.7 425 41 9.6 16
0.62 10.5 Auxerre 2 26.0 39 5 0 6 2 42.6 0.30
+5.6 +0.21 174 547 31.8 721 124 57.1 43.2 182 74.2 53.6 427
25 5.9 41 1.58 15.7 Brest
Squad Standard Stats 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats
Opponent Stats Playing Time Performance Expected Progression Per 90 Minutes
Squad # Pl Age Poss MP Starts Min 90s Gls Ast G+A G-PK PK
PKatt CrdY CrdR xG npxG xAG npxG+xAG PrgC PrgP Gls Ast G+A
G-PK G+A-PK xG xAG xG+xAG npxG npxG+xAG Angers 25 28.3
42.4 26 286 2,340 26.0 25 14 39 20 5 6 35 2
30.0 25.3 20.1 45.4 384 746 0.96 0.54 1.50 0.77 1.31 1.16 0.77
1.93 0.97 1.75 Auxerre 28 26.6 42.0 26 286 2,340 26.0 37
26 63 33 4 5 49 4 33.2 29.5 24.6 54.1 365 842
1.42 1.00 2.42 1.27 2.27 1.28 0.95 2.22 1.14 2.08 Brest
Squad Passing 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats Opponent
Stats Total Short Medium Long Expected Squad # Pl 90s Cmp
Att Cmp% TotDist PrgDist Cmp Att Cmp% Cmp Att Cmp% Cmp
Att Cmp% Ast xAG xA A-xAG KP 1/3 PPA CrsPA PrgP Angers 25
26.0 8642 10962 78.8 150533 57180 3938 4469 88.1 3737 4407 84.8
764 1522 50.2 14 20.1 17.8 -6.1 172 604 156 53 746 Auxerre
28 26.0 8057 10553 76.3 148888 58443 3437 3975 86.5 3420 4037
84.7 972 1933 50.3 26 24.6 18.5 +1.4 219 600 162 51 842
Brest
Squad Pass Types 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats Opponent
Stats Pass Types Corner Kicks Outcomes Squad # Pl 90s Att Live Dead
FK TB Sw Crs TI CK In Out Str Cmp Off Blocks
Angers 25 26.0 10962 9737 1171 329 46 47 431 474 91 35
39 4 8642 54 196 Auxerre 28 26.0 10553 9365 1148 350 23
125 429 450 102 37 51 0 8057 40 216 Brest
Squad Goal and Shot Creation 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad
Stats Opponent Stats SCA SCA Types GCA GCA Types Squad # Pl 90s SCA
SCA90 PassLive PassDead TO Sh Fld Def GCA GCA90 PassLive
PassDead TO Sh Fld Def Angers 25 26.0 431 16.58 308 33
19 27 26 18 45 1.73 28 2 2 4 7 2 Auxerre
28 26.0 529 20.35 387 47 23 33 21 18 67 2.58 41
8 4 6 8 0 Brest
Squad Defensive Actions 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats
Opponent Stats Tackles Challenges Blocks Squad # Pl 90s Tkl TklW
Def 3rd Mid 3rd Att 3rd Tkl Att Tkl% Lost Blocks Sh
Pass Int Tkl+Int Clr Err Angers 25 26.0 478 307 259 152
67 224 460 48.7 236 281 88 193 274 752 706 16 Auxerre
28 26.0 547 332 276 194 77 264 496 53.2 232 316 100
216 272 819 721 13 Brest
Squad Possession 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats Opponent
Stats Touches Take-Ons Carries Receiving Squad # Pl Poss 90s
Touches Def Pen Def 3rd Mid 3rd Att 3rd Att Pen Live
Att Succ Succ% Tkld Tkld% Carries TotDist PrgDist PrgC 1/3
CPA Mis Dis Rec PrgR Angers 25 42.4 26.0 13998 1619 5068 6119
2981 395 13992 492 205 41.7 258 52.4 7357 40483 20857 384 288
77 398 220 8573 737 Auxerre 28 42.0 26.0 13735 1680 5035 5900
2948 471 13730 486 186 38.3 264 54.3 7310 37083 18358 365 273
95 347 271 7995 838 Brest
Player Shooting 2024-2025 Ligue 1 Hide non-qualifiers for rate stats GlossaryToggle
Per90 Stats Standard Expected Rk Player Nation Pos Squad Age
Born 90s Gls Sh SoT SoT% Sh/90 SoT/90 G/Sh G/SoT Dist FK
PK PKatt xG npxG npxG/Sh G-xG np:G-xG Matches 1 Keyliane
Abdallah fr FRA FW Marseille 18-345 2006 0.0 0 0 0
0.00 0.00 0 0 0 0.0 0.0 0.0 0.0
Matches 2 Yunis Abdelhamid ma MAR DF Saint-Étienne 37-169
1987 10.8 0 2 1 50.0 0.18 0.09 0.00 0.00 10.8 0 0
0 0.2 0.2 0.12 -0.2 -0.2 Matches 3 Himad Abdelli dz ALG
MF,FW Angers 25-119 1999 24.7 6 24 6 25.0 0.97 0.24
0.13 0.50 24.1 0 3 4 5.6 2.5 0.10 +0.4 +0.5 Matches 4
Mohamed Abdelmoneim eg EGY DF Nice 26-043 1999 6.8 0
0 0 0.00 0.00 0 0 0 0.0 0.0
0.0 0.0 Matches 5 Ali Abdi tn TUN DF,MF Nice 31-086 1993
10.5 2 16 5 31.3 1.53 0.48 0.13 0.40 19.8 0 0 0
1.2 1.2 0.07 +0.8 +0.8 Matches 6 Matthis Abline fr FRA FW
Nantes 21-353 2003 23.1 7 60 22 36.7 2.60 0.95 0.10
0.27 14.8 0 1 1 7.2 6.4 0.11 -0.2 -0.4 Matches 7 Abner
Squad Advanced Goalkeeping 2024-2025 Ligue 1 GlossaryToggle Per90 Stats Squad Stats
Opponent Stats Goals Expected Launched Passes Goal Kicks Crosses
Sweeper Squad # Pl 90s GA PKA FK CK OG PSxG PSxG/SoT
PSxG+/- /90 Cmp Att Cmp% Att (GK) Thr Launch% AvgLen
Att Launch% AvgLen Opp Stp Stp% #OPA #OPA/90 AvgDist
Angers 1 26.0 42 3 0 7 0 52.3 0.33 +10.3 +0.39 112
334 33.5 579 146 38.0 35.3 210 54.3 42.7 425 41 9.6 16
0.62 10.5 Auxerre 2 26.0 39 5 0 6 2 42.6 0.30
+5.6 +0.21 174 547 31.8 721 124 57.1 43.2 182 74.2 53.6 427
25 5.9 41 1.58 15.7 Brest 2 25.0 40 5 1 2 1
34.6 0.29 -4.4 -0.18 175 415 42.2 683 101 44.7 36.9 175 62.9
47.5 362 25 6.9 30 1.20 15.3 Le Havre
donc pour chaque catégorie il doit avoir un formulaire ou coller les données c'est-
à-dire donc regarde le nombre de catégories