0% found this document useful (0 votes)
7 views113 pages

Text 3

The document outlines the requirements for creating a powerful football match simulator, including a complete overhaul of an existing application. It includes HTML structure, CSS styling, and JavaScript functionalities to enhance user experience. The design emphasizes responsiveness, interactivity, and a visually appealing interface with various UI components.

Uploaded by

rubenboga04
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views113 pages

Text 3

The document outlines the requirements for creating a powerful football match simulator, including a complete overhaul of an existing application. It includes HTML structure, CSS styling, and JavaScript functionalities to enhance user experience. The design emphasizes responsiveness, interactivity, and a visually appealing interface with various UI components.

Uploaded by

rubenboga04
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 113

voici mes exigences je veux créer un simulateur très puissant j'ai confiance en toi

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;

/* Couleurs du thème clair */


--bg-main: #F8F9FA;
--bg-card: #FFFFFF;
--bg-input: #FFFFFF;
--text-primary: #181838;
--text-secondary: #5E5E8C;
--text-muted: #8E8EA9;
--border-color: #E2E2F0;
--shadow-color: rgba(0, 0, 0, 0.05);

/* 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;
}

h1, h2, h3, h4, h5, h6 {


font-family: 'Montserrat', sans-serif;
font-weight: 600;
}

.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); }

.mt-1 { margin-top: var(--spacing-xs); }


.mt-2 { margin-top: var(--spacing-sm); }
.mt-3 { margin-top: var(--spacing-md); }
.mt-4 { margin-top: var(--spacing-lg); }
.mt-5 { margin-top: var(--spacing-xl); }

.flex { display: flex; }


.flex-col { flex-direction: column; }
.justify-between { justify-content: space-between; }
.items-center { align-items: center; }
.gap-1 { gap: var(--spacing-xs); }
.gap-2 { gap: var(--spacing-sm); }
.gap-3 { gap: var(--spacing-md); }

.text-center { text-align: center; }


.font-bold { font-weight: 700; }
.text-sm { font-size: 0.85rem; }
.text-lg { font-size: 1.1rem; }
.text-xl { font-size: 1.25rem; }
.text-2xl { font-size: 1.5rem; }

.text-success { color: var(--success); }


.text-danger { color: var(--danger); }
.text-warning { color: var(--warning); }
.text-muted { color: var(--text-muted); }
/* Animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@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 */


.import-data-card {
border: 2px dashed var(--border-color);
border-radius: var(--border-radius);
padding: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
text-align: center;
}

.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;
}

/* Nouveau sélecteur d'équipe avec badges */


.team-selector {
position: relative;
display: flex;
flex-direction: column;
}

.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;
}

/* Planification des matchs */


.datetime-picker {
display: flex;
gap: var(--spacing-md);
}

.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);
}
}

/* Style pour appareils mobiles */


@media (max-width: 768px) {
.container {
padding: 0 var(--spacing-sm);
}

.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;
}
}

/* Style pour très petits écrans */


@media (max-width: 480px) {
.match-teams {
flex-direction: column;
gap: var(--spacing-md);
}

.match-stats {
grid-template-columns: 1fr;
}

.datetime-picker {
flex-direction: column;
}
}

/* Nouvelles catégories d'équipes */


.team-category-badge {
display: inline-flex;
align-items: center;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
font-size: 0.75rem;
font-weight: 500;
margin-right: var(--spacing-sm);
}

.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;
}

/* Visualisation du momentum et de l'intensité */


.match-dynamic-indicators {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-md);
margin-top: var(--spacing-md);
}

.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 id="tab-import-data" class="tab-content active">


<div class="card">
<div class="card-header">
<h2 class="card-title">Importer les données d'équipes</h2>
</div>
<div class="card-body">
<p class="mb-3">Collez les données tabulaires des équipes ci-
dessous pour commencer (format avec tabs/tabulations).</p>

<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 id="dataLoadedIndicator" class="mb-3 p-3 bg-success bg-


opacity-10 rounded" style="display: none;">
<div class="flex items-center">
<i class="fas fa-check-circle text-success mr-2"></i>
<div>
<span class="font-bold">Données chargées avec
succès</span>
<p class="text-sm mb-0"><span
id="teamsLoadedCount">0</span> équipes importées</p>
</div>
</div>
</div>

<div class="flex justify-end">


<button id="clearDataBtn" class="btn btn-secondary mr-3">
<i class="fas fa-trash"></i> Effacer données
</button>
<button id="loadDemoDataBtn" class="btn btn-primary">
<i class="fas fa-database"></i> Charger données de démo
</button>
</div>
</div>
</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 id="teamsContainer" class="grid grid-cols-1 md:grid-cols-2


lg:grid-cols-3 gap-3">
<!-- Les équipes seront ajoutées ici -->
<div class="text-center text-muted py-4">
<i class="fas fa-database fa-2x mb-2"></i>
<p>Aucune équipe importée</p>
<p class="text-sm">Importez des données pour
commencer</p>
</div>
</div>
</div>
</div>
</div>

<div id="tab-match-setup" class="tab-content">


<div class="card" id="matchSetupCard">
<div class="card-header">
<h2 class="card-title">Configuration du Match</h2>
</div>
<div class="card-body">
<form id="matchSetupForm">
<div class="form-grid">
<div class="form-group">
<label for="team1">Équipe Domicile</label>
<select id="team1" class="form-control" required>
<option value="">Sélectionner une
équipe</option>
<!-- Options générées dynamiquement -->
</select>
<div class="flex gap-2 mt-2">
<span id="team1Category" class="team-
category"></span>
<span id="team1Style"
class="team-style"></span>
</div>
</div>
<div class="form-group">
<label for="team2">Équipe Extérieur</label>
<select id="team2" class="form-control" required>
<option value="">Sélectionner une
équipe</option>
<!-- Options générées dynamiquement -->
</select>
<div class="flex gap-2 mt-2">
<span id="team2Category" class="team-
category"></span>
<span id="team2Style"
class="team-style"></span>
</div>
</div>
</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="form-group mt-4">


<label class="mb-2 font-bold">Options avancées</label>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox"
id="commentaryEnabled" checked>
<span class="toggle-slider"></span>
</label>
<span>Commentaire détaillé</span>
</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 id="scheduleDatetimeContainer" class="form-group mt-3"


style="display: none;">
<label for="scheduleDateTime">Date et heure du
match</label>
<input type="datetime-local" id="scheduleDateTime"
class="form-control">
</div>
<div class="speed-control mt-4">
<div class="speed-label">Vitesse de simulation:</div>
<div class="speed-options">
<button type="button" class="speed-option" data-
speed="real">Temps réel (1h30)</button>
<button type="button" class="speed-option" data-
speed="fast">Rapide (7min30)</button>
<button type="button" class="speed-option active"
data-speed="very-fast">Très rapide (1min30)</button>
</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 id="simulationContainer" style="display: none;">


<div class="match-scoreboard">
<div class="match-time" id="matchTime">00:00</div>
<div class="match-teams">
<div class="match-team" id="homeTeam">Équipe Domicile</div>
<div class="match-score">
<span id="homeScore">0</span> - <span
id="awayScore">0</span>
</div>
<div class="match-team" id="awayTeam">Équipe
Extérieur</div>
</div>
<div class="flex justify-between items-center mt-2 text-sm">
<span class="badge badge-primary"
id="matchType">Type</span>
<span class="badge badge-warning"
id="matchInfo">Informations</span>
</div>
</div>

<div class="grid grid-cols-1 md:grid-cols-2 gap-4">


<div class="match-commentary">
<div class="commentary-title">
<i class="fas fa-microphone"></i> Commentaire du match
</div>
<ul class="commentary-list" id="commentaryList">
<!-- Événements générés dynamiquement -->
</ul>
</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 id="advancedStatsContainer" class="mt-4">


<h4 class="font-bold mb-2">Statistiques
avancées</h4>
<div class="grid grid-cols-2 gap-2">
<div class="match-stat">
<div class="stat-label">xG (Expected
Goals)</div>
<div class="stat-values">
<div class="stat-value"
id="homeXG">0.00</div>
<div class="stat-value"
id="awayXG">0.00</div>
</div>
<div class="stat-bar">
<div class="stat-fill" id="xgBar"
style="width: 50%;"></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="match-dynamic-indicators mt-4">


<div class="dynamic-indicator">
<div class="dynamic-indicator-title">Rapport de
force</div>
<div class="dynamic-indicator-bar">
<div class="dynamic-indicator-fill
momentum-indicator-fill" id="momentumIndicator" style="width: 20px; left:
50%;"></div>
</div>
<div class="flex justify-between text-xs mt-1">
<span id="team1MomentumName">Équipe
1</span>
<span id="team2MomentumName">Équipe
2</span>
</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>

<div id="matchSummary" class="card mt-4" style="display: none;">


<div class="card-header">
<h3 class="card-title">Résumé du Match</h3>
</div>
<div class="card-body" id="matchSummaryContent">
<!-- Contenu généré dynamiquement -->
</div>
<div class="flex justify-between mt-4">
<button id="newSimulationBtn" class="btn btn-secondary">
<i class="fas fa-plus"></i> Nouvelle simulation
</button>
<button id="saveResultBtn" class="btn btn-primary">
<i class="fas fa-save"></i> Sauvegarder le résultat
</button>
</div>
</div>
</div>

<div id="tab-scheduled-matches" class="tab-content">


<div class="card">
<div class="card-header">
<h2 class="card-title">Matchs Planifiés</h2>
</div>
<div class="card-body">
<div id="scheduledMatchesEmpty" class="text-center text-muted
py-4">
<i class="fas fa-calendar-alt fa-2x mb-2"></i>
<p>Aucun match planifié</p>
<p class="text-sm">Utilisez l'option "Planifier pour plus
tard" lors de la configuration d'un match</p>
</div>

<div id="scheduledMatchesList" class="scheduled-matches-list">


<!-- Les matchs planifiés seront ajoutés ici -->
</div>
</div>
</div>
</div>

<div id="tab-match-history" class="tab-content">


<div class="card">
<div class="card-header">
<h3 class="card-title">Historique des Matchs</h3>
<button id="clearHistoryBtn" class="btn btn-sm btn-danger">
<i class="fas fa-trash"></i> Effacer
</button>
</div>
<div class="card-body">
<div id="matchHistoryList" class="match-history-list">
<!-- Contenu généré dynamiquement -->
<div class="text-center text-muted py-4">
<i class="fas fa-history fa-2x mb-2"></i>
<p>Aucun match dans l'historique</p>
<p class="text-sm">Lancez une simulation pour
commencer</p>
</div>
</div>
</div>
</div>
</div>
</div>

<!-- Notifications -->


<div id="notificationContainer"></div>

<!-- Bouton de thème -->


<div class="theme-switch">
<button id="themeToggle" class="theme-switch-button">
<i class="fas fa-moon"></i>
</button>
</div>

<!-- Animation de but -->


<div id="goalAnimation" style="display: none;" class="goal-animation">
<div class="goal-content">
<div class="goal-text">BUUUUT!</div>
<div class="goal-team" id="goalTeam"></div>
</div>
</div>

<!-- Loader -->


<div id="loaderContainer" class="loader-container" style="display: none;">
<div class="loader"></div>
</div>
<script>
/* =========================================================
INITIALISATION ET GESTION DES DONNÉES
========================================================= */

// 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

// Constantes pour la simulation


const DEFAULT_SIMULATION_SPEED = 60000; // 60 secondes = 1 minute (simulation en
temps quasi-réel)
const FAST_SIMULATION_SPEED = 5000; // 5 secondes = 1 minute (simulation
rapide)
const VERY_FAST_SIMULATION_SPEED = 1000; // 1 seconde = 1 minute (simulation très
rapide)
let CURRENT_SIMULATION_SPEED = VERY_FAST_SIMULATION_SPEED;
const MAX_MATCH_MINUTES = 90; // Durée standard d'un match
// Création du manifeste pour l'installation PWA
const manifestObj = {
"name": "Football Match Simulator Pro",
"short_name": "FootSim",
"description": "Application de simulation de matches de football avec
statistiques avancées",
"start_url": "./",
"display": "standalone",
"background_color": "#5D5CDE",
"theme_color": "#5D5CDE",
"icons": [
{
"src":
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZp
ZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjNUQ1Q0RFIiBkPSJNNDE3LjMgMjA0LjJjLTE4Ljg
tMTQuNy00Mi45LTIyLjQtNjguOS0yMi40aC03LjFjLTI2IDAtNTAuMSA3LjctNjguOSAyMi40LTE5LjIgMT
UtMjkuOSAzNC44LTI5LjkgNTUuNXY0MGMwIDIwLjYgMTAuNyA0MC41IDI5LjkgNTUuNSAxOC44IDE0LjcgN
DIuOSAyMi40IDY4LjkgMjIuNGg3LjFjMjYgMCA1MC4xLTcuNyA2OC45LTIyLjQgMTkuMi0xNSAyOS45LTM0
LjggMjkuOS01NS41di00MGMwLTIwLjYtMTAuNy00MC41LTI5LjktNTUuNXoiPjwvcGF0aD48cGF0aCBmaWx
sPSIjRkZGRkZGIiBkPSJNMzIxLjQgMzI1LjdjMC02LjQgNS4yLTExLjcgMTEuNy0xMS43aDM2LjdjNi40ID
AgMTEuNyA1LjIgMTEuNyAxMS43djEwYzAgNi40LTUuMiAxMS43LTExLjcgMTEuN2gtMzYuN2MtNi40IDAtM
TEuNy01LjItMTEuNy0xMS43di0xMHptLTE1OC43LTExLjdoMzYuN2M2LjQgMCAxMS43IDUuMiAxMS43IDEx
Ljd2MTBjMCA2LjQtNS4yIDExLjctMTEuNyAxMS43aC0zNi43Yy02LjQgMC0xMS43LTUuMi0xMS43LTExLjd
2LTEwYzAtNi40IDUuMi0xMS43IDExLjctMTEuN3oiPjwvcGF0aD48cGF0aCBmaWxsPSIjMDAwMDAwIiBkPS
JNMjU2IDgwQzE0OSA4MCA2NCAxNjUgNjQgMjcxLjYgNjQgMzc5IDEzNyA0NTMgMjU2IDQ1M3MxOTItNzQgM
TkyLTE4MS40QzQ0OCAxNjUgMzYzIDgwIDI1NiA4MHptMCAyOTguN2MtMjQgMC00Ni4zLTUuNy02Ni4zLTE1
LjVDMTgxIDM1OCAxNzYuOCAzNTAgMTc2LjggMzQyLjV2LTM2LjZjMC0zLjIuOS02LjMgMi43LTkuMWwyLS4
yYzI4LjItMjcuMSA0NC45LTUxLjUgNTAuMy03MiAzLjEtMTEuOS44LTI0LjgtNi4yLTM0LjktNy4zLTEwLj
YtMTAuNi0xOS41LTEwLTI3IDEuMi0xNC43IDE2LjItMjIuNCAyOC4xLTE0LjcgMTIuOCA4LjMgMjUuOSAyM
S41IDM2IDM2LjJsLjQuMWMzNS44IDIgNjYuMSAyNi40IDgwLjQgNjUuMSAxMC42IDI4LjggNC4xIDYwLjUg
MTYuNyA4MiAxMSAxOC44IDI5LjQgMjcuMyA0LjQgNTUuNEMzMzYuNyAzNjkuOSAyOTcuNiAzNzguNyAyNTY
gMzc4Ljd6Ii8+PHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTI1NiAyMzQuMmMxOSAwIDM0LjQtMTUuNCAzNC
40LTM0LjQgMC0xOS0xNS40LTM0LjQtMzQuNC0zNC40LTE5IDAtMzQuNCAxNS40LTM0LjQgMzQuNCAwIDE5I
DE1LjQgMzQuNCAzNC40IDM0LjR6Ii8+PC9zdmc+",
"sizes": "512x512",
"type": "image/svg+xml",
"purpose": "any maskable"
}
]
};

const manifestStr = URL.createObjectURL(


new Blob([JSON.stringify(manifestObj)], {type: "application/json"})
);
document.getElementById("manifestPlaceholder").setAttribute("href", manifestStr);

// Initialiser l'application
document.addEventListener('DOMContentLoaded', function() {
// Charger les données depuis le stockage local
loadData();

// Initialiser les gestionnaires d'événements


initEventHandlers();

// Vérifier le thème
checkTheme();

// Afficher une notification de bienvenue


showNotification('Bienvenue sur le Simulateur Pro de Matchs de Football!',
'info');

// Configurer le traitement des données collées par l'utilisateur


document.getElementById('dataInput').addEventListener('input', function(event)
{
const data = event.target.value;
if (data && data.trim() !== '') {
parseData(data);
populateTeamOptions();
updateTeamsContainer();

// Afficher l'indicateur de chargement réussi


const teamsCount = Object.keys(teamData).length;
document.getElementById('teamsLoadedCount').textContent = teamsCount;
document.getElementById('dataLoadedIndicator').style.display = 'block';

showNotification('Données chargées avec succès', 'success');


} else {
document.getElementById('dataLoadedIndicator').style.display = 'none';
}
});

// Configurer la recherche d'équipes


document.getElementById('teamSearch').addEventListener('input', function(event)
{
updateTeamsContainer(event.target.value);
});

// Configurer l'option de planification


document.getElementById('scheduleForLater').addEventListener('change',
function() {
document.getElementById('scheduleDatetimeContainer').style.display =
this.checked ? 'block' : 'none';

// Régler la date/heure par défaut à maintenant + 1 heure


if (this.checked) {
const now = new Date();
now.setHours(now.getHours() + 1);

// 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}`;
}
});

// Initialiser les onglets


initTabs();

// Vérifier et démarrer les matchs planifiés


checkScheduledMatches();
});
// Charger les données depuis le stockage local
function loadData() {
try {
const storedTeamData = localStorage.getItem('simulatorTeamData');
if (storedTeamData) {
teamData = JSON.parse(storedTeamData);
populateTeamOptions();
updateTeamsContainer();

// Mettre à jour l'indicateur de données chargées


const teamsCount = Object.keys(teamData).length;
if (teamsCount > 0) {
document.getElementById('teamsLoadedCount').textContent =
teamsCount;
document.getElementById('dataLoadedIndicator').style.display =
'block';
}
}

const storedMatchHistory = localStorage.getItem('simulatorMatchHistory');


if (storedMatchHistory) {
matchHistory = JSON.parse(storedMatchHistory);
updateMatchHistory();
}

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');
}
}

// Sauvegarder les données dans le stockage local


function saveData() {
try {
localStorage.setItem('simulatorTeamData', JSON.stringify(teamData));
localStorage.setItem('simulatorMatchHistory',
JSON.stringify(matchHistory));
localStorage.setItem('simulatorScheduledMatches',
JSON.stringify(scheduledMatches));
} catch (error) {
console.error('Erreur lors de la sauvegarde des données:', error);
showNotification('Erreur lors de la sauvegarde des données', 'error');
}
}

// Parser les données collées


function parseData(data) {
teamData = {};
const lines = data.split('\n');
const headers = lines[0].split('\t'); // Split by tab

for (let i = 1; i < lines.length; i++) {


const values = lines[i].split('\t'); // Split by tab
if (values.length >= 30) { // Assurons-nous qu'il y a suffisamment de
colonnes
const teamName = values[1]; // Squad name est dans la deuxième colonne
if (teamName && teamName.trim() !== "") {
try {
teamData[teamName] = {
"Age": parseFloat(values[4]) || 26.0,
"Poss": parseFloat(values[5]) || 0,
"MP": parseInt(values[6]) || 0,
"Starts": parseInt(values[7]) || 0,
"Min": parseInt((values[8] || "0").replace(",", "")) || 0,
"Gls": parseFloat(values[10]) || 0,
"Ast": parseFloat(values[11]) || 0,
"G+A": parseFloat(values[12]) || 0,
"G-PK": parseFloat(values[13]) || 0,
"PK": parseFloat(values[14]) || 0,
"PKatt": parseFloat(values[15]) || 0,
"CrdY": parseFloat(values[16]) || 0,
"CrdR": parseFloat(values[17]) || 0,
"xG": parseFloat(values[18]) || 0,
"npxG": parseFloat(values[19]) || 0,
"xAG": parseFloat(values[20]) || 0,
"npxG+xAG": parseFloat(values[21]) || 0,
"PrgC": parseFloat(values[22]) || 0,
"PrgP": parseFloat(values[23]) || 0,
"xG/90": parseFloat(values[28]) || 0,
"xAG/90": parseFloat(values[29]) || 0,
// Ajoutons des champs calculés pour la défense
"GA": parseFloat(values[10]) || 1.5, // Utilisons les buts
comme approximation des GA
"xGA": parseFloat(values[18]) || 0, // En supposant que
c'est dans les données
"Comp": values[2] || "", // Ajout de la compétition/ligue
// Nouveaux champs pour la classification avancée
"TeamProfile": null,
"TeamCategory": null,
"TeamStyle": null,
"TeamDiscipline": null
};

// Calculer les champs dérivés immédiatement


teamData[teamName].TeamProfile =
determineTeamProfile(teamName);
teamData[teamName].TeamCategory = classifyTeam(teamName);
teamData[teamName].TeamStyle = identifyTeamStyle(teamName);
teamData[teamName].TeamDiscipline =
evaluateDiscipline(teamName);

} catch (error) {
console.error(`Erreur de parsing pour l'équipe ${teamName}:`,
error);
}
}
}
}

// Sauvegarder les données


saveData();
}
// Initialiser les gestionnaires d'événements
function initEventHandlers() {
// Gestionnaires pour le formulaire de configuration
document.getElementById('team1').addEventListener('change', updateTeamInfo);
document.getElementById('team2').addEventListener('change', updateTeamInfo);
document.getElementById('resetBtn').addEventListener('click', resetForm);
document.getElementById('startSimulationBtn').addEventListener('click',
startMatch);
document.getElementById('clearDataBtn').addEventListener('click', clearData);
document.getElementById('loadDemoDataBtn').addEventListener('click',
loadDemoData);

// Gestionnaires pour les contrôles de simulation


document.getElementById('pauseResumeBtn').addEventListener('click',
togglePauseResume);
document.getElementById('stopSimulationBtn').addEventListener('click',
stopSimulation);

// Gestionnaires pour les options de vitesse


const speedButtons = document.querySelectorAll('.speed-option');
speedButtons.forEach(button => {
button.addEventListener('click', function() {
speedButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
changeSimulationSpeed(this.getAttribute('data-speed'));
});
});

// Gestionnaire pour le bouton de thème


document.getElementById('themeToggle').addEventListener('click', toggleTheme);

// Ajout des gestionnaires pour les onglets


document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
activateTab(this.getAttribute('data-tab'));
});
});

// 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);
}
}

// Gestion des onglets


function initTabs() {
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');

// S'assurer qu'au moins un onglet est actif


let hasActiveTab = false;
tabs.forEach(tab => {
if (tab.classList.contains('active')) {
hasActiveTab = true;
const tabId = tab.getAttribute('data-tab');
document.getElementById(`tab-${tabId}`).classList.add('active');
}
});

// Si aucun onglet n'est actif, activer le premier


if (!hasActiveTab && tabs.length > 0) {
const firstTab = tabs[0];
firstTab.classList.add('active');
const tabId = firstTab.getAttribute('data-tab');
document.getElementById(`tab-${tabId}`).classList.add('active');
}
}

// Activer un onglet spécifique


function activateTab(tabId) {
// Désactiver tous les onglets et contenus
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c =>
c.classList.remove('active'));

// Activer l'onglet et son contenu


document.querySelector(`.tab[data-tab="${tabId}"]`).classList.add('active');
document.getElementById(`tab-${tabId}`).classList.add('active');
}

// Afficher une notification


function showNotification(message, type = 'info') {
const notificationContainer = document.getElementById('notificationContainer');

// Créer l'élément de notification


const notification = document.createElement('div');
notification.className = `notification ${type}`;

// Déterminer l'icône en fonction du type


let icon = '';
switch (type) {
case 'success': icon = '<i class="fas fa-check-circle"></i>'; break;
case 'error': icon = '<i class="fas fa-exclamation-circle"></i>'; break;
case 'warning': icon = '<i class="fas fa-exclamation-triangle"></i>';
break;
case 'info':
default: icon = '<i class="fas fa-info-circle"></i>';
}

// Construire le contenu
notification.innerHTML = `
${icon}
<div class="notification-message">${message}</div>
<span class="notification-close">&times;</span>
`;
// Ajouter au conteneur
notificationContainer.appendChild(notification);

// Configurer le bouton de fermeture


const closeBtn = notification.querySelector('.notification-close');
closeBtn.addEventListener('click', function() {
notification.classList.add('fadeout');
setTimeout(() => {
notificationContainer.removeChild(notification);
}, 300);
});

// Disparaître automatiquement après 5 secondes


setTimeout(() => {
if (notification.parentElement === notificationContainer) {
notification.classList.add('fadeout');
setTimeout(() => {
if (notification.parentElement === notificationContainer) {
notificationContainer.removeChild(notification);
}
}, 300);
}
}, 5000);

// Log dans la console


console.log(`[${type.toUpperCase()}]`, message);
}

// 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>';
}
}

// Fonction pour mettre à jour les infos d'équipe


function updateTeamInfo() {
const team1 = document.getElementById('team1').value;
const team2 = document.getElementById('team2').value;

// Nettoyer les éléments existants


document.getElementById('team1Category').innerHTML = '';
document.getElementById('team1Style').innerHTML = '';
document.getElementById('team2Category').innerHTML = '';
document.getElementById('team2Style').innerHTML = '';

// Supprimer les badges de profil existants


document.querySelectorAll('.team-category-badge').forEach(badge =>
badge.remove());

// Mettre à jour les catégories et styles


if (team1 && teamData[team1]) {
const team1Category = classifyTeam(team1);
const team1Style = identifyTeamStyle(team1);
const team1Profile = determineTeamProfile(team1);

const categoryElement = document.getElementById('team1Category');


categoryElement.textContent = team1Category;
categoryElement.className = `team-category category-$
{team1Category.toLowerCase()}`;

const styleElement = document.getElementById('team1Style');


styleElement.textContent = team1Style;
styleElement.className = `team-style style-${team1Style.toLowerCase()}`;

// Ajouter le badge de profil avancé


const profileBadge = document.createElement('span');
profileBadge.className = `team-category-badge category-$
{team1Profile.toLowerCase().replace(/\s+/g, '-')}`;
profileBadge.textContent = team1Profile;
categoryElement.parentNode.appendChild(profileBadge);
}

if (team2 && teamData[team2]) {


const team2Category = classifyTeam(team2);
const team2Style = identifyTeamStyle(team2);
const team2Profile = determineTeamProfile(team2);

const categoryElement = document.getElementById('team2Category');


categoryElement.textContent = team2Category;
categoryElement.className = `team-category category-$
{team2Category.toLowerCase()}`;

const styleElement = document.getElementById('team2Style');


styleElement.textContent = team2Style;
styleElement.className = `team-style style-${team2Style.toLowerCase()}`;

// Ajouter le badge de profil avancé


const profileBadge = document.createElement('span');
profileBadge.className = `team-category-badge category-$
{team2Profile.toLowerCase().replace(/\s+/g, '-')}`;
profileBadge.textContent = team2Profile;
categoryElement.parentNode.appendChild(profileBadge);
}

// Vérifier si les deux équipes sont identiques


if (team1 && team2 && team1 === team2) {
showNotification('Veuillez sélectionner deux équipes différentes',
'warning');
document.getElementById('startSimulationBtn').disabled = true;
} else {
document.getElementById('startSimulationBtn').disabled = false;
}
}

// Peupler les options de sélection d'équipe


function populateTeamOptions() {
const team1Select = document.getElementById('team1');
const team2Select = document.getElementById('team2');

// Sauvegarder les sélections actuelles


const team1Value = team1Select.value;
const team2Value = team2Select.value;

// Vider les listes


team1Select.innerHTML = '<option value="">Sélectionner une équipe</option>';
team2Select.innerHTML = '<option value="">Sélectionner une équipe</option>';

// Ajouter les options pour chaque équipe


const teams = Object.keys(teamData).sort();

teams.forEach(team => {
const option1 = document.createElement('option');
option1.value = team;
option1.textContent = team;

const option2 = document.createElement('option');


option2.value = team;
option2.textContent = team;

team1Select.appendChild(option1);
team2Select.appendChild(option2);
});

// Restaurer les sélections si possible


if (teams.includes(team1Value)) team1Select.value = team1Value;
if (teams.includes(team2Value)) team2Select.value = team2Value;
}

// Mettre à jour l'affichage des équipes


function updateTeamsContainer(searchTerm = '') {
const container = document.getElementById('teamsContainer');
container.innerHTML = '';

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;
}

// Filtrer les équipes selon le terme de recherche


let teams = Object.keys(teamData);

if (searchTerm) {
const lowerSearchTerm = searchTerm.toLowerCase();
teams = teams.filter(team => team.toLowerCase().includes(lowerSearchTerm));
}

// Trier par nom d'équipe


teams.sort();

// Créer une carte pour chaque équipe


teams.forEach(team => {
const teamCard = document.createElement('div');
teamCard.className = 'card team-card';

// Calculer des données dérivées


const teamData_item = teamData[team];
const matchesPlayed = teamData_item.MP || 0;
const goalsFor = teamData_item.Gls || 0;
const goalsAgainst = teamData_item.GA || 0;
const goalDiff = goalsFor - goalsAgainst;
const goalsPerMatch = matchesPlayed > 0 ? (goalsFor /
matchesPlayed).toFixed(2) : 0;
const categoryClass = `category-${(teamData_item.TeamCategory ||
'Moyenne').toLowerCase()}`;
const styleClass = `style-${(teamData_item.TeamStyle ||
'Balanced').toLowerCase()}`;
const disciplineRating = teamData_item.TeamDiscipline || 3;
const disciplineStars = '★'.repeat(disciplineRating) + '☆'.repeat(5 -
disciplineRating);

// Générer le contenu de la carte


teamCard.innerHTML = `
<div class="card-header">
<h3 class="card-title">${team}</h3>
<span class="team-category ${categoryClass}">$
{teamData_item.TeamCategory || 'Moyenne'}</span>
</div>
<div class="card-body">
<div class="flex gap-2 mb-2">
<span class="badge badge-primary">${teamData_item.TeamProfile
|| 'Mid-Table'}</span>
<span class="badge badge-secondary ${styleClass}">$
{teamData_item.TeamStyle || 'Balanced'}</span>
</div>
<div class="grid grid-cols-2 gap-2">
<div><span class="text-muted">Matches:</span> $
{matchesPlayed}</div>
<div><span class="text-muted">Buts:</span> ${goalsFor}-$
{goalsAgainst} (${goalDiff >= 0 ? '+' : ''}${goalDiff})</div>
<div><span class="text-muted">Buts/match:</span> $
{goalsPerMatch}</div>
<div><span class="text-muted">Possession:</span> $
{teamData_item.Poss || 50}%</div>
<div><span class="text-muted">xG/90:</span> $
{teamData_item['xG/90'] ? teamData_item['xG/90'].toFixed(2) : '1.20'}</div>
<div><span class="text-muted">Discipline:</span> <span
class="text-warning">${disciplineStars}</span></div>
</div>
</div>
<div class="flex justify-between mt-2">
<button class="btn btn-secondary btn-sm btn-edit" data-team="$
{team}"><i class="fas fa-edit"></i> Modifier</button>
<button class="btn btn-primary btn-sm btn-select" data-team="$
{team}"><i class="fas fa-check"></i> Sélectionner</button>
</div>
`;

// Ajouter à la liste des équipes


container.appendChild(teamCard);
});

// Ajouter les gestionnaires d'événements


document.querySelectorAll('.btn-edit').forEach(btn => {
btn.addEventListener('click', function() {
const team = this.getAttribute('data-team');
editTeamData(team);
});
});

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';

// Réinitialiser les catégories d'équipe


document.getElementById('team1Category').textContent = '';
document.getElementById('team1Style').textContent = '';
document.getElementById('team2Category').textContent = '';
document.getElementById('team2Style').textContent = '';

// Supprimer les badges de profil s'ils existent


const badges = document.querySelectorAll('.team-category-badge');
badges.forEach(badge => badge.remove());
}

// Sélectionner une équipe pour le match


function selectTeam(team) {
// Vérifier si team1 est vide
if (!document.getElementById('team1').value) {
document.getElementById('team1').value = team;
}
// Sinon vérifier si team2 est vide
else if (!document.getElementById('team2').value) {
document.getElementById('team2').value = team;
}
// Sinon remplacer team1
else {
document.getElementById('team1').value = team;
}

// Mettre à jour les infos d'équipe


updateTeamInfo();

// Passer à l'onglet de configuration


activateTab('match-setup');
}

// Charger des données de démonstration


function loadDemoData() {
if (confirm('Charger des données de démonstration? Cela remplacera toutes les
données existantes.')) {
// Simuler le chargement d'équipes de démo
const demoTeams = {
"Paris Saint-Germain": {
"Age": 27.5,
"Poss": 62.3,
"MP": 38,
"Gls": 85,
"GA": 29,
"xG/90": 2.5,
"CrdY": 52,
"CrdR": 3,
"PrgP": 1320,
"TeamProfile": "Top Team",
"TeamCategory": "Grande",
"TeamStyle": "Offensive",
"TeamDiscipline": 4
},
"Olympique de Marseille": {
"Age": 26.8,
"Poss": 55.6,
"MP": 38,
"Gls": 63,
"GA": 40,
"xG/90": 1.8,
"CrdY": 68,
"CrdR": 4,
"PrgP": 980,
"TeamProfile": "Challenger",
"TeamCategory": "Grande",
"TeamStyle": "Balanced",
"TeamDiscipline": 3
},
"AS Monaco": {
"Age": 25.4,
"Poss": 54.1,
"MP": 38,
"Gls": 70,
"GA": 42,
"xG/90": 1.9,
"CrdY": 61,
"CrdR": 2,
"PrgP": 1050,
"TeamProfile": "Challenger",
"TeamCategory": "Grande",
"TeamStyle": "Offensive",
"TeamDiscipline": 4
},
"Olympique Lyonnais": {
"Age": 26.2,
"Poss": 57.3,
"MP": 38,
"Gls": 67,
"GA": 44,
"xG/90": 1.85,
"CrdY": 59,
"CrdR": 2,
"PrgP": 1100,
"TeamProfile": "Challenger",
"TeamCategory": "Grande",
"TeamStyle": "Balanced",
"TeamDiscipline": 4
},
"LOSC Lille": {
"Age": 25.9,
"Poss": 53.8,
"MP": 38,
"Gls": 58,
"GA": 36,
"xG/90": 1.65,
"CrdY": 63,
"CrdR": 3,
"PrgP": 920,
"TeamProfile": "Challenger",
"TeamCategory": "Grande",
"TeamStyle": "Balanced",
"TeamDiscipline": 3
},
"Stade Rennais": {
"Age": 25.1,
"Poss": 55.2,
"MP": 38,
"Gls": 60,
"GA": 38,
"xG/90": 1.7,
"CrdY": 55,
"CrdR": 1,
"PrgP": 990,
"TeamProfile": "Challenger",
"TeamCategory": "Moyenne",
"TeamStyle": "Offensive",
"TeamDiscipline": 4
},
"RC Lens": {
"Age": 26.5,
"Poss": 51.4,
"MP": 38,
"Gls": 52,
"GA": 41,
"xG/90": 1.5,
"CrdY": 70,
"CrdR": 4,
"PrgP": 860,
"TeamProfile": "Mid-Table",
"TeamCategory": "Moyenne",
"TeamStyle": "Balanced",
"TeamDiscipline": 3
},
"OGC Nice": {
"Age": 25.3,
"Poss": 52.1,
"MP": 38,
"Gls": 49,
"GA": 35,
"xG/90": 1.4,
"CrdY": 64,
"CrdR": 2,
"PrgP": 880,
"TeamProfile": "Mid-Table",
"TeamCategory": "Moyenne",
"TeamStyle": "Defensive",
"TeamDiscipline": 3
},
"Stade de Reims": {
"Age": 24.8,
"Poss": 48.6,
"MP": 38,
"Gls": 45,
"GA": 47,
"xG/90": 1.25,
"CrdY": 67,
"CrdR": 4,
"PrgP": 790,
"TeamProfile": "Mid-Table",
"TeamCategory": "Moyenne",
"TeamStyle": "Balanced",
"TeamDiscipline": 3
},
"Montpellier HSC": {
"Age": 26.1,
"Poss": 47.3,
"MP": 38,
"Gls": 48,
"GA": 52,
"xG/90": 1.3,
"CrdY": 72,
"CrdR": 5,
"PrgP": 760,
"TeamProfile": "Mid-Table",
"TeamCategory": "Moyenne",
"TeamStyle": "Balanced",
"TeamDiscipline": 2
},
"Clermont Foot": {
"Age": 27.2,
"Poss": 45.8,
"MP": 38,
"Gls": 38,
"GA": 58,
"xG/90": 1.1,
"CrdY": 65,
"CrdR": 3,
"PrgP": 680,
"TeamProfile": "Underdog",
"TeamCategory": "Petite",
"TeamStyle": "Defensive",
"TeamDiscipline": 3
},
"FC Lorient": {
"Age": 25.6,
"Poss": 44.2,
"MP": 38,
"Gls": 40,
"GA": 60,
"xG/90": 1.15,
"CrdY": 68,
"CrdR": 4,
"PrgP": 650,
"TeamProfile": "Underdog",
"TeamCategory": "Petite",
"TeamStyle": "Defensive",
"TeamDiscipline": 3
}
};

teamData = demoTeams;

// Sauvegarder et mettre à jour l'interface


saveData();
populateTeamOptions();
updateTeamsContainer();

// Mettre à jour l'indicateur de données chargées


const teamsCount = Object.keys(teamData).length;
document.getElementById('teamsLoadedCount').textContent = teamsCount;
document.getElementById('dataLoadedIndicator').style.display = 'block';

showNotification('Données de démonstration chargées avec succès',


'success');
}
}

// Effacer toutes les données


function clearData() {
if (confirm('Êtes-vous sûr de vouloir effacer toutes les données? Cette action
est irréversible.')) {
// Effacer les données
teamData = {};
matchHistory = [];
scheduledMatches = [];

// Effacer le stockage local


try {
localStorage.removeItem('simulatorTeamData');
localStorage.removeItem('simulatorMatchHistory');
localStorage.removeItem('simulatorScheduledMatches');
} catch (error) {
console.error('Erreur lors de la suppression des données:', error);
}

// Réinitialiser l'interface
populateTeamOptions();
updateTeamsContainer();
updateMatchHistory();
updateScheduledMatchesDisplay();

// Masquer l'indicateur de données chargées


document.getElementById('dataLoadedIndicator').style.display = 'none';

showNotification('Toutes les données ont été effacées', 'success');


}
}

// Pour la compatibilité avec les parties suivantes


function updateScheduledMatchesDisplay() {
const container = document.getElementById('scheduledMatchesList');
if (!container) return;

container.innerHTML = '';

if (scheduledMatches.length === 0) {
document.getElementById('scheduledMatchesEmpty').style.display = 'block';
return;
}

document.getElementById('scheduledMatchesEmpty').style.display = 'none';

// Afficher les matchs planifiés


scheduledMatches.forEach(match => {
const matchDate = new Date(match.scheduledAt);
const matchElement = document.createElement('div');
matchElement.className = 'scheduled-match-item';
matchElement.innerHTML = `
<div>
<div class="scheduled-match-teams">${match.team1} vs $
{match.team2}</div>
<div
class="scheduled-match-time">${matchDate.toLocaleString()}</div>
</div>
<div class="scheduled-match-buttons">
<button class="btn btn-primary btn-sm" data-match-id="$
{match.id}">Jouer maintenant</button>
<button class="btn btn-danger btn-sm" data-match-id="$
{match.id}">Supprimer</button>
</div>
`;

container.appendChild(matchElement);
});

// Ajouter les gestionnaires d'événements


document.querySelectorAll('.scheduled-match-buttons .btn-primary').forEach(btn
=> {
btn.addEventListener('click', function() {
const matchId = this.getAttribute('data-match-id');
startScheduledMatch(matchId);
});
});

document.querySelectorAll('.scheduled-match-buttons .btn-danger').forEach(btn
=> {
btn.addEventListener('click', function() {
const matchId = this.getAttribute('data-match-id');
deleteScheduledMatch(matchId);
});
});
}

// Stub pour la compatibilité - sera complété dans la partie 2


function updateMatchHistory() {
// Sera implémenté dans la partie suivante
}

// Stub pour la compatibilité - sera complété dans la partie 2


function checkScheduledMatches() {
// Sera implémenté dans la partie suivante
}

// Stub pour la compatibilité - sera complété dans la partie 2


function startScheduledMatch(matchId) {
// Sera implémenté dans la partie suivante
}

// Stub pour la compatibilité - sera complété dans la partie 2


function deleteScheduledMatch(matchId) {
// Sera implémenté dans la partie suivante
}

// Stub pour la compatibilité - sera complété dans la partie 2


function determineTeamProfile(teamName) {
if (!teamData[teamName]) return "Mid-Table";

// Valeur par défaut


return teamData[teamName].TeamProfile || "Mid-Table";
}

// Stub pour la compatibilité - sera complété dans la partie 2


function identifyTeamStyle(teamName) {
if (!teamData[teamName]) return "Balanced";

// Valeur par défaut


return teamData[teamName].TeamStyle || "Balanced";
}

// Stub pour la compatibilité - sera complété dans la partie 2


function classifyTeam(teamName) {
if (!teamData[teamName]) return "Moyenne";

// Valeur par défaut


return teamData[teamName].TeamCategory || "Moyenne";
}

// Stub pour la compatibilité - sera complété dans la partie 2


function evaluateDiscipline(teamName) {
if (!teamData[teamName]) return 3;

// Valeur par défaut (1-5)


return teamData[teamName].TeamDiscipline || 3;
}

// Stub pour la compatibilité - sera complété dans la partie 2


function editTeamData(teamName) {
showNotification(`Édition de l'équipe ${teamName} non implémentée dans cette
version démo`, 'info');
}

// Stub pour la compatibilité - sera complété dans la partie 2


function changeSimulationSpeed(speed) {
switch (speed) {
case 'real':
CURRENT_SIMULATION_SPEED = DEFAULT_SIMULATION_SPEED;
break;
case 'fast':
CURRENT_SIMULATION_SPEED = FAST_SIMULATION_SPEED;
break;
case 'very-fast':
CURRENT_SIMULATION_SPEED = VERY_FAST_SIMULATION_SPEED;
break;
default:
CURRENT_SIMULATION_SPEED = VERY_FAST_SIMULATION_SPEED;
}

// Si la simulation est en cours, l'ajuster


if (currentMatch && currentMatch.status === "En cours" && !simulationPaused) {
clearTimeout(simulationTimer);
simulationTimer = setTimeout(simulateMatchMinutes,
CURRENT_SIMULATION_SPEED);
}
}

/* =========================================================
FONCTIONS AVANCÉES DE CLASSIFICATION ET CALCUL
========================================================= */

// Classification détaillée des équipes selon leur profil de jeu


function determineTeamProfile(team) {
if (!teamData[team]) return "Mid-Table"; // Valeur par défaut

const data = teamData[team];

// Extraire les métriques clés


const possession = data["Poss"] || 50;
const goalsPerMatch = (data["Gls"] || 0) / Math.max(data["MP"] || 1, 1);
const goalsAgainstPerMatch = (data["GA"] || 0) / Math.max(data["MP"] || 1, 1);
const xG = data["xG/90"] || 1.5;
const xGA = data["GA"] ? data["GA"] / Math.max(data["MP"] || 1, 1) : 1.5;
const prgP = data["PrgP"] || 800;
const yellowCards = (data["CrdY"] || 0) / Math.max(data["MP"] || 1, 1);
const redCards = (data["CrdR"] || 0) / Math.max(data["MP"] || 1, 1);

// Calculer différents scores de tendance


let offensiveScore = 0;
let defensiveScore = 0;
let possessionScore = 0;
let physicalScore = 0;

// Score offensif
if (goalsPerMatch > 2.0) offensiveScore += 3;
else if (goalsPerMatch > 1.5) offensiveScore += 2;
else if (goalsPerMatch > 1.0) offensiveScore += 1;

if (xG > 1.8) offensiveScore += 3;


else if (xG > 1.4) offensiveScore += 2;
else if (xG > 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;

if (xGA < 1.0) defensiveScore += 3;


else if (xGA < 1.3) defensiveScore += 2;
else if (xGA < 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;

if (prgP > 1200) possessionScore += 3;


else if (prgP > 1000) possessionScore += 2;
else if (prgP > 800) possessionScore += 1;

// Score physique
if (yellowCards > 2.0) physicalScore += 2;
else if (yellowCards > 1.5) physicalScore += 1;

if (redCards > 0.15) physicalScore += 2;


else if (redCards > 0.05) physicalScore += 1;

// Déterminer le profil principal en fonction des scores


const qualityScore = offensiveScore + defensiveScore + possessionScore;

// Déterminer la catégorie principale


if (qualityScore >= 12) {
if (offensiveScore >= 5 && possessionScore >= 4) return "Top Team";
return "Challenger";
} else if (qualityScore >= 8) {
if (defensiveScore >= 5 && offensiveScore <= 2) return "Ultra-Defensive";
if (offensiveScore >= 5 && defensiveScore <= 2) return "Ultra-Offensive";
if (possessionScore >= 4) return "Technical";
if (physicalScore >= 3) return "Physical";
return "Mid-Table";
} else if (qualityScore >= 4) {
if (defensiveScore > offensiveScore && physicalScore >= 2) return
"Underdog";
if (Math.abs(offensiveScore - defensiveScore) <= 1) return "Opportunist";
return "Mid-Table";
} else {
return "Underdog";
}
}

// Fonction améliorée pour déterminer le style de jeu


function identifyTeamStyle(team) {
if (!teamData[team]) return "Balanced";

const data = teamData[team];

const prgP = (data["PrgP"] || 0) / Math.max(data["MP"] || 1, 1);


const prgC = (data["PrgC"] || 0) / Math.max(data["MP"] || 1, 1);
const possession = data["Poss"] || 50;
const goalsPerMatch = (data["Gls"] || 0) / Math.max(data["MP"] || 1, 1);
const xGPer90 = data["xG/90"] || 1.2;
const yellowCards = (data["CrdY"] || 0) / Math.max(data["MP"] || 1, 1);

// Scoring system for offensive tendency


let offensiveScore = 0;

if (prgP > 50) offensiveScore += 2;


else if (prgP > 40) offensiveScore += 1;

if (prgC > 40) offensiveScore += 2;


else if (prgC > 30) offensiveScore += 1;

if (possession > 55) offensiveScore += 2;


else if (possession > 48) offensiveScore += 1;

if (goalsPerMatch > 1.8) offensiveScore += 2;


else if (goalsPerMatch > 1.3) offensiveScore += 1;

if (xGPer90 > 1.7) offensiveScore += 2;


else if (xGPer90 > 1.3) offensiveScore += 1;

// Determine style based on score


if (offensiveScore >= 7) return "Offensive";
if (offensiveScore <= 3) return "Defensive";
return "Balanced";
}

// Classification enrichie des équipes en catégories (Grande, Moyenne, Petite)


function classifyTeam(team) {
if (!teamData[team]) return "Moyenne"; // Par défaut, équipe moyenne

const data = teamData[team];

const possession = data["Poss"] || 50;


const xG = data["xG/90"] || 1.5;
const GA = (data["GA"] || 0) / Math.max(data["MP"] || 1, 1);
const prgP = data["PrgP"] || 0;
const goalsForPerMatch = (data["Gls"] || 0) / Math.max(data["MP"] || 1, 1);

// Méthode de score multi-critères


let score = 0;

if (possession > 55) score += 2;


else if (possession > 50) score += 1;
else if (possession < 45) score -= 1;
if (xG > 2.0) score += 3;
else if (xG > 1.5) score += 2;
else if (xG > 1.2) score += 1;
else if (xG < 1.0) score -= 1;

if (GA < 1.0) score += 2;


else if (GA < 1.3) score += 1;
else if (GA > 1.8) score -= 1;

if (prgP > 1200) score += 2;


else if (prgP > 1000) score += 1;
else if (prgP < 700) score -= 1;

if (goalsForPerMatch > 2.0) score += 2;


else if (goalsForPerMatch > 1.5) score += 1;
else if (goalsForPerMatch < 1.0) score -= 1;

if (score >= 5) return "Grande";


if (score <= -2) return "Petite";
return "Moyenne";
}

// Évaluer la discipline d'une équipe (1-5)


function evaluateDiscipline(team) {
if (!teamData[team]) return 3; // Discipline moyenne par défaut

const data = teamData[team];

const yellowsPer90 = (data["CrdY"] || 0) / Math.max(data["MP"] || 1, 1);


const redsPer90 = (data["CrdR"] || 0) / Math.max(data["MP"] || 1, 1);

// Pondération: un carton rouge vaut 5 cartons jaunes


const disciplineScore = yellowsPer90 + (redsPer90 * 5);

// Inverser l'échelle: plus le score est bas, meilleure est la discipline


if (disciplineScore < 1.0) return 5; // Excellente discipline
if (disciplineScore < 1.8) return 4; // Bonne discipline
if (disciplineScore < 2.5) return 3; // Discipline moyenne
if (disciplineScore < 3.2) return 2; // Discipline médiocre
return 1; // Mauvaise discipline
}

// Calculer les xG avec tous les facteurs


function calculateXG(team, opponent, matchType, venue, weather, crowd) {
if (!teamData[team] || !teamData[opponent]) return 1.2; // Valeur par défaut

const teamDataObj = teamData[team];


const opponentData = teamData[opponent];

// Calculer les données de base


const baseXGPerMatch = teamDataObj["xG/90"] || 1.2;
const teamPossession = teamDataObj["Poss"] || 50;
const opponentPossession = opponentData["Poss"] || 50;
const teamDefense = opponentData["GA"] / Math.max(opponentData["MP"] || 1, 1)
|| 1.5;

// Calculer l'indice d'attaque et de défense


const attackIndex = (baseXGPerMatch / 1.5) * 1.2; // Normaliser par rapport à
1.5 xG
const defenseIndex = (1.5 / teamDefense) * 0.8; // Normaliser par rapport à 1.5
GA

// Calculer l'indice de possession


const possessionRatio = teamPossession / (teamPossession + opponentPossession);
const possessionFactor = 0.8 + (possessionRatio * 0.4); // entre 0.8 et 1.2

// 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;

// Limiter à des valeurs réalistes


return Math.min(Math.max(finalXG, 0.3), 3.0);
}

// Analyser les résultats récents


function analyzeRecentForm(formString) {
if (!formString || formString.trim() === '') {
return null;
}

const results = formString.toUpperCase().split(',');


let wins = 0, draws = 0, losses = 0;

results.forEach(result => {
const cleanResult = result.trim();
if (cleanResult === 'V') wins++;
else if (cleanResult === 'N') draws++;
else if (cleanResult === 'D') losses++;
});

const total = wins + draws + losses;


if (total === 0) return null;

const winRate = (wins / total) * 100;


const drawRate = (draws / total) * 100;
const lossRate = (losses / total) * 100;

return {
last5: formString,
winRate: winRate,
drawRate: drawRate,
lossRate: lossRate,
form: (wins * 3 + draws * 1) / total // Score de forme entre 0 et 3
};
}

// Analyser les confrontations directes


function analyzeHeadToHead(pastResults) {
if (!pastResults || pastResults.trim() === '') {
return null;
}

const results = pastResults.split(',');


let team1Wins = 0, team2Wins = 0, draws = 0;

results.forEach(result => {
const parts = result.trim().split('-');
if (parts.length === 2) {
const score1 = parseInt(parts[0]);
const score2 = parseInt(parts[1]);

if (!isNaN(score1) && !isNaN(score2)) {


if (score1 > score2) team1Wins++;
else if (score1 < score2) team2Wins++;
else draws++;
}
}
});

return {
team1Wins: team1Wins,
team2Wins: team2Wins,
draws: draws,
total: team1Wins + team2Wins + draws,
h2hAdvantage: team1Wins - team2Wins
};
}

/* =========================================================
LOGIQUE DE SIMULATION DE MATCH
========================================================= */

// Initialiser une nouvelle simulation de match


function initializeMatchSimulation(team1, team2, matchType) {
// Vérifier que les équipes existent dans les données
if (!teamData[team1] || !teamData[team2]) {
showNotification('Équipe(s) non trouvée(s) dans les données', 'error');
return null;
}
// Récupérer les données de forme récente si disponibles
const formTeam1 = analyzeRecentForm(document.getElementById("last5Team1").value
|| "");
const formTeam2 = analyzeRecentForm(document.getElementById("last5Team2").value
|| "");

// Récupérer les confrontations directes si disponibles


const pastMatches = document.getElementById("pastMatches").value || "";
const h2hStats = analyzeHeadToHead(pastMatches);

// Récupérer les profils et styles des équipes


const team1Profile = determineTeamProfile(team1);
const team2Profile = determineTeamProfile(team2);
const team1Style = identifyTeamStyle(team1);
const team2Style = identifyTeamStyle(team2);
const team1Category = classifyTeam(team1);
const team2Category = classifyTeam(team2);

// Récupérer les options du match


const venue = document.getElementById('venue').value;
const weather = document.getElementById('weather').value;
const crowd = document.getElementById('crowd').value;

// Calculer les xG en fonction de tous les facteurs


const xgTeam1 = calculateXG(team1, team2, matchType, venue, weather, crowd);
const xgTeam2 = calculateXG(team2, team1, matchType, venue === "home" ?
"away" : (venue === "away" ? "home" : "neutral"), weather, crowd);

// 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;

// Statistiques attendues basées sur les xG


const expectedShotsTeam1 = Math.max(5, Math.round(xgTeam1 * 4.5)); // ~4.5 tirs
par xG
const expectedShotsTeam2 = Math.max(5, Math.round(xgTeam2 * 4.5));

const expectedCornersTeam1 = Math.max(3, Math.round(xgTeam1 * 3)); // ~3


corners par xG
const expectedCornersTeam2 = Math.max(3, Math.round(xgTeam2 * 3));

// Force relative des équipes basée sur la catégorie


let team1StrengthFactor = 1.0;
let team2StrengthFactor = 1.0;

if (team1Category === "Grande") team1StrengthFactor = 1.3;


else if (team1Category === "Petite") team1StrengthFactor = 0.7;

if (team2Category === "Grande") team2StrengthFactor = 1.3;


else if (team2Category === "Petite") team2StrengthFactor = 0.7;

// Données de possession basées sur le profil d'équipe


let team1Possession = 50;
let team2Possession = 50;

if (team1Style === "Offensive") team1Possession += 5;


else if (team1Style === "Defensive") team1Possession -= 5;

if (team2Style === "Offensive") team2Possession += 5;


else if (team2Style === "Defensive") team2Possession -= 5;

// Normaliser la possession
const totalPoss = team1Possession + team2Possession;
team1Possession = Math.round((team1Possession / totalPoss) * 100);
team2Possession = 100 - team1Possession;

// Créer l'objet du match


const match = {
id: `match_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
team1: team1,
team2: team2,
team1Profile: team1Profile,
team2Profile: team2Profile,
team1Category: team1Category,
team2Category: team2Category,
matchType: matchType,
xgTeam1: xgTeam1,
xgTeam2: xgTeam2,

// 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;
}

// Démarrer la simulation du match


function startMatch() {
// Récupérer les équipes sélectionnées
const team1 = document.getElementById('team1').value;
const team2 = document.getElementById('team2').value;
const matchType = document.getElementById('matchType').value;

// Validation
if (!team1 || !team2) {
showNotification('Veuillez sélectionner les deux équipes', 'warning');
return;
}

if (team1 === team2) {


showNotification('Veuillez sélectionner deux équipes différentes',
'warning');
return;
}

// Vérifier si le match doit être planifié


if (document.getElementById('scheduleForLater').checked) {
const scheduledDateTime =
document.getElementById('scheduleDateTime').value;

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();

// Vérifier que la date est dans le futur


if (scheduledTimestamp <= now) {
showNotification('La date et heure planifiées doivent être dans le
futur', 'warning');
return;
}

// Récupérer toutes les options du formulaire


const matchOptions = {
id: `scheduled_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
team1: team1,
team2: team2,
matchType: matchType,
venue: document.getElementById('venue').value,
weather: document.getElementById('weather').value,
crowd: document.getElementById('crowd').value,
pastMatches: document.getElementById('pastMatches').value,
last5Team1: document.getElementById('last5Team1').value,
last5Team2: document.getElementById('last5Team2').value,
commentaryEnabled:
document.getElementById('commentaryEnabled').checked,
advancedStats: document.getElementById('advancedStats').checked,
realismMode: document.getElementById('realismMode').checked,
intensityFactor: document.getElementById('intensityFactor').checked,
backgroundNotifications:
document.getElementById('backgroundNotifications').checked,
detailedEvents: document.getElementById('detailedEvents').checked,
simulationSpeed: CURRENT_SIMULATION_SPEED,
scheduledAt: scheduledTimestamp
};

// Ajouter à la liste des matchs planifiés


scheduledMatches.push(matchOptions);
saveData();

// Mettre à jour l'affichage des matchs planifiés


updateScheduledMatchesDisplay();

showNotification(`Match ${team1} vs ${team2} planifié pour le ${new


Date(scheduledTimestamp).toLocaleString()}`, 'success');

// Réinitialiser le formulaire
resetForm();

// Passer à l'onglet des matchs planifiés


activateTab('scheduled-matches');

return;
}

// Initialiser la simulation
currentMatch = initializeMatchSimulation(team1, team2, matchType);

if (!currentMatch) {
showNotification('Erreur lors de l\'initialisation de la simulation',
'error');
return;
}

// Cacher le formulaire et afficher la simulation


document.getElementById('matchSetupCard').style.display = 'none';
document.getElementById('simulationContainer').style.display = 'block';

// Mettre à jour l'interface


updateMatchDisplay();

// Démarrer la simulation
currentMatch.status = "En cours";
simulationPaused = false;

// Notification de démarrage du match


showNotification(`🏁 Le match ${team1} vs ${team2} commence!`, 'info');

// Ajouter un événement de début de match


addMatchEvent(currentMatch, "🏁 Coup d'envoi!");

// Démarrer la simulation minute par minute


simulateMatchMinutes();
}

// Simuler les minutes du match


function simulateMatchMinutes() {
if (!currentMatch || simulationPaused) {
return;
}
// Si le match est terminé, arrêter la simulation
if (currentMatch.status === "Terminé" || currentMatch.minute >=
MAX_MATCH_MINUTES) {
finishMatch();
return;
}

// Passer à la minute suivante


currentMatch.minute++;

// Pause mi-temps (45 minutes)


if (currentMatch.minute === 45) {
addMatchEvent(currentMatch, "🏁 Mi-temps! Les équipes rentrent aux
vestiaires.");
updateMatchDisplay();

// Notification de mi-temps
showNotification(` Mi-temps : ${currentMatch.team1} ${currentMatch.score1}-
${currentMatch.score2} ${currentMatch.team2}`, 'info');

// Planifier la reprise après 3 secondes (simule la mi-temps)


setTimeout(() => {
if (currentMatch && currentMatch.status === "En cours") {
currentMatch.minute++; // Avancer à la 46e minute
addMatchEvent(currentMatch, "🏁 Début de la seconde mi-temps!");

// Notification de reprise
showNotification(`🏁 2ème mi-temps : ${currentMatch.team1} $
{currentMatch.score1}-${currentMatch.score2} ${currentMatch.team2}`, 'info');

// Petit regain d'énergie après la mi-temps


currentMatch.fatigue.team1 = Math.min(1.0,
currentMatch.fatigue.team1 + 0.1);
currentMatch.fatigue.team2 = Math.min(1.0,
currentMatch.fatigue.team2 + 0.1);

simulateMatchEvents();
updateMatchDisplay();

// Continuer la simulation
simulationTimer = setTimeout(simulateMatchMinutes,
CURRENT_SIMULATION_SPEED);
}
}, 3000); // Pause de 3 secondes pour la mi-temps

return;
}

// Mise à jour de la fatigue (augmente progressivement)


updateFatigue();

// Mise à jour de l'intensité (peut varier au fil du match)


updateIntensity();

// Simulation des événements de cette minute


simulateMatchEvents();

// Mise à jour de l'affichage


updateMatchDisplay();
// Planifier la prochaine minute
simulationTimer = setTimeout(simulateMatchMinutes, CURRENT_SIMULATION_SPEED);
}

// Simuler les événements d'une minute


function simulateMatchEvents() {
// Mise à jour de la possession
const possessionChange = (Math.random() * 10) - 5; // Entre -5 et 5
currentMatch.stats.possession.team1 = Math.max(30, Math.min(70,
currentMatch.stats.possession.team1 + possessionChange));
currentMatch.stats.possession.team2 = 100 -
currentMatch.stats.possession.team1;

// 15% de chance d'avoir un tir


if (Math.random() < 0.15) {
const shootingTeam = Math.random() < (currentMatch.stats.possession.team1 /
100) ? 'team1' : 'team2';
const teamName = shootingTeam === 'team1' ? currentMatch.team1 :
currentMatch.team2;

// Mise à jour des statistiques


currentMatch.stats.shots[shootingTeam]++;

// 40% de chance que le tir soit cadré


if (Math.random() < 0.4) {
currentMatch.stats.shotsOnTarget[shootingTeam]++;

// Ajouter un commentaire
addMatchEvent(currentMatch, `🎯 Tir cadré de ${teamName}!`);

// 20% de chance de marquer sur un tir cadré


if (Math.random() < 0.2) {
if (shootingTeam === 'team1') {
currentMatch.score1++;
} else {
currentMatch.score2++;
}

// Ajouter un événement de but


addMatchEvent(currentMatch, `⚽ BUT! ${teamName} marque!`);

// Afficher l'animation de but


showGoalAnimation(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}.`);
}
}

// 10% de chance d'avoir un corner


if (Math.random() < 0.1) {
const cornerTeam = Math.random() < 0.5 ? 'team1' : 'team2';
const teamName = cornerTeam === 'team1' ? currentMatch.team1 :
currentMatch.team2;

currentMatch.stats.corners[cornerTeam]++;
addMatchEvent(currentMatch, `🚩 Corner pour ${teamName}.`);
}

// 5% de chance d'avoir un carton jaune


if (Math.random() < 0.05) {
const cardTeam = Math.random() < 0.5 ? 'team1' : 'team2';
const teamName = cardTeam === 'team1' ? currentMatch.team1 :
currentMatch.team2;

currentMatch.stats.yellowCards[cardTeam]++;
addMatchEvent(currentMatch, `🟨 Carton jaune pour ${teamName}.`);
}

// 1% de chance d'avoir un carton rouge


if (Math.random() < 0.01) {
const cardTeam = Math.random() < 0.5 ? 'team1' : 'team2';
const teamName = cardTeam === 'team1' ? currentMatch.team1 :
currentMatch.team2;

currentMatch.stats.redCards[cardTeam]++;
addMatchEvent(currentMatch, `🟥 Carton rouge pour ${teamName}!`);

// Notification
showNotification(`🟥 Carton rouge pour ${teamName}!`, 'error');
}

// Mise à jour du momentum


updateMomentum();
}

// Mise à jour de la fatigue


function updateFatigue() {
if (!currentMatch) return;

// La fatigue augmente plus rapidement après la 60e minute


let fatigueRate = currentMatch.minute < 60 ? 0.003 : 0.006;

// L'intensité élevée accélère la fatigue


fatigueRate *= currentMatch.intensity;

// Ajustement selon le style de jeu


if (currentMatch.team1Style === "Offensive") {
currentMatch.fatigue.team1 = Math.max(0.7, currentMatch.fatigue.team1 -
fatigueRate * 1.2);
} else if (currentMatch.team1Style === "Defensive") {
currentMatch.fatigue.team1 = Math.max(0.7, currentMatch.fatigue.team1 -
fatigueRate * 0.9);
} else {
currentMatch.fatigue.team1 = Math.max(0.7, currentMatch.fatigue.team1 -
fatigueRate);
}

if (currentMatch.team2Style === "Offensive") {


currentMatch.fatigue.team2 = Math.max(0.7, currentMatch.fatigue.team2 -
fatigueRate * 1.2);
} else if (currentMatch.team2Style === "Defensive") {
currentMatch.fatigue.team2 = Math.max(0.7, currentMatch.fatigue.team2 -
fatigueRate * 0.9);
} else {
currentMatch.fatigue.team2 = Math.max(0.7, currentMatch.fatigue.team2 -
fatigueRate);
}

// Les cartons rouges augmentent la fatigue de l'équipe concernée


if (currentMatch.stats.redCards.team1 > 0) {
currentMatch.fatigue.team1 = Math.max(0.6, currentMatch.fatigue.team1 -
0.002 * currentMatch.stats.redCards.team1);
}

if (currentMatch.stats.redCards.team2 > 0) {
currentMatch.fatigue.team2 = Math.max(0.6, currentMatch.fatigue.team2 -
0.002 * currentMatch.stats.redCards.team2);
}
}

// Mise à jour de l'intensité


function updateIntensity() {
if (!currentMatch) return;

// Option : facteur d'intensité aléatoire


if (currentMatch.intensityFactor) {
// L'intensité varie naturellement au cours du match
if (Math.random() < 0.1) {
// 10% de chance de changement d'intensité
const change = (Math.random() - 0.5) * 0.3; // Entre -0.15 et +0.15
currentMatch.intensity = Math.min(Math.max(0.8, currentMatch.intensity
+ change), 1.5);
}
}

// Les derbys sont plus intenses


if (currentMatch.matchType === "Derby") {
currentMatch.intensity = Math.min(1.5, currentMatch.intensity + 0.1);
}

// Les fins de match serrées sont plus intenses


if (currentMatch.minute > 75 && Math.abs(currentMatch.score1 -
currentMatch.score2) <= 1) {
currentMatch.intensity = Math.min(1.5, currentMatch.intensity + 0.1);
}

// Un écart de buts important réduit l'intensité


if (Math.abs(currentMatch.score1 - currentMatch.score2) >= 3) {
currentMatch.intensity = Math.max(0.8, currentMatch.intensity - 0.1);
}
}

// Mise à jour du momentum


function updateMomentum() {
if (!currentMatch) return;

// Décroissance naturelle du momentum (retour à l'équilibre)


if (currentMatch.momentum.team1 > 0) currentMatch.momentum.team1 -= 0.05;
if (currentMatch.momentum.team1 < 0) currentMatch.momentum.team1 += 0.05;
if (currentMatch.momentum.team2 > 0) currentMatch.momentum.team2 -= 0.05;
if (currentMatch.momentum.team2 < 0) currentMatch.momentum.team2 += 0.05;

// Impact de la possession actuelle


const possessionImpact = (currentMatch.stats.possession.team1 - 50) * 0.02;
currentMatch.momentum.team1 += possessionImpact;
currentMatch.momentum.team2 -= possessionImpact;

// Moments clés du match - regain de momentum


if (currentMatch.minute === 46 || currentMatch.minute === 60 ||
currentMatch.minute === 75) {
// L'équipe perdante reçoit un boost
if (currentMatch.score1 < currentMatch.score2) {
currentMatch.momentum.team1 = Math.min(5, currentMatch.momentum.team1 +
1);
} else if (currentMatch.score1 > currentMatch.score2) {
currentMatch.momentum.team2 = Math.min(5, currentMatch.momentum.team2 +
1);
}
}

// Borner le momentum entre -5 et 5


currentMatch.momentum.team1 = Math.min(Math.max(-5,
currentMatch.momentum.team1), 5);
currentMatch.momentum.team2 = Math.min(Math.max(-5,
currentMatch.momentum.team2), 5);
}

// Ajouter un événement au match


function addMatchEvent(match, description) {
if (!match) return;

// Créer l'objet événement


const event = {
minute: match.minute,
description: description,
timestamp: new Date().toISOString()
};

// Ajouter à l'historique
match.events.push(event);

// Afficher l'événement si l'option est activée


if (match.commentaryEnabled) {
const commentaryList = document.getElementById('commentaryList');
if (commentaryList) {
const eventItem = document.createElement('li');
eventItem.className = 'commentary-item';
eventItem.innerHTML = `<span class="commentary-time">$
{match.minute}'</span> ${description}`;
commentaryList.prepend(eventItem);
}
}
}

// Mettre à jour l'affichage du match


function updateMatchDisplay() {
if (!currentMatch) return;

// Mettre à jour le temps


const matchTimeDisplay = currentMatch.minute >= 45 && currentMatch.minute <
46 ?
"Mi-temps" : (currentMatch.minute > 90 ? "90+" : currentMatch.minute +
"'");
document.getElementById('matchTime').textContent = matchTimeDisplay;

// Mettre à jour les équipes et le score


document.getElementById('homeTeam').textContent = currentMatch.team1;
document.getElementById('awayTeam').textContent = currentMatch.team2;
document.getElementById('homeScore').textContent = currentMatch.score1;
document.getElementById('awayScore').textContent = currentMatch.score2;

// Mettre à jour les informations du match


document.getElementById('matchType').textContent = currentMatch.matchType;

// Déterminer l'info contextuelle


let matchInfoText = '';

if (currentMatch.minute < 45) {


matchInfoText = '1ère mi-temps';
} else if (currentMatch.minute === 45) {
matchInfoText = 'Mi-temps';
} else if (currentMatch.minute > 45 && currentMatch.minute <= 90) {
matchInfoText = '2ème mi-temps';
} else if (currentMatch.minute > 90) {
matchInfoText = 'Temps additionnel';
}

document.getElementById('matchInfo').textContent = matchInfoText;

// Mettre à jour les statistiques


updateStats();

// Mettre à jour les indicateurs dynamiques


updateDynamicIndicators();
}

// Mettre à jour les statistiques


function updateStats() {
if (!currentMatch) return;

// Possession
const team1Possession = Math.round(currentMatch.stats.possession.team1);
const team2Possession = 100 - team1Possession;

document.getElementById('homePossession').textContent = team1Possession + '%';


document.getElementById('awayPossession').textContent = team2Possession + '%';
document.getElementById('possessionBar').style.width = 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;

// Statistiques avancées si activées


if (currentMatch.advancedStats) {
document.getElementById('advancedStatsContainer').style.display = 'block';

// 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';
}
}

// Mettre à jour les indicateurs dynamiques


function updateDynamicIndicators() {
if (!currentMatch) return;

// Indicateur de momentum
const momentumIndicator = document.getElementById('momentumIndicator');
const momentumDifference = currentMatch.momentum.team1 -
currentMatch.momentum.team2;

// Calculer la position du momentum (0-100%)


// 0% = momentum team2 max, 50% = neutre, 100% = momentum team1 max
const momentumPosition = 50 + (momentumDifference / 10) * 50;

// Mettre à jour la largeur (fixe) et la position de l'indicateur


momentumIndicator.style.width = '20px';
momentumIndicator.style.left = momentumPosition + '%';

// Mettre à jour les noms d'équipe


document.getElementById('team1MomentumName').textContent = currentMatch.team1;
document.getElementById('team2MomentumName').textContent = currentMatch.team2;

// Indicateur d'intensité
const intensityIndicator = document.getElementById('intensityIndicator');

// Convertir l'intensité (0.8-1.5) en pourcentage (0-100%)


const intensityPercentage = ((currentMatch.intensity - 0.8) / 0.7) * 100;

intensityIndicator.style.width = intensityPercentage + '%';


}

// Afficher l'animation de but


function showGoalAnimation(scoringTeam) {
const goalAnimation = document.getElementById('goalAnimation');
const goalTeam = document.getElementById('goalTeam');

goalTeam.textContent = scoringTeam;
goalAnimation.style.display = 'flex';

// Masquer après 3 secondes


setTimeout(() => {
goalAnimation.style.display = 'none';
}, 3000);
}

// Mettre en pause ou reprendre la simulation


function togglePauseResume() {
if (!currentMatch) return;

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

showNotification('Simulation arrêtée', 'warning');


}
}

// Terminer le match
function finishMatch(forceStop = false) {
if (!currentMatch) return;

// Arrêter le timer
clearTimeout(simulationTimer);

// Marquer le match comme terminé


currentMatch.status = "Terminé";
currentMatch.endTime = new Date();

// Ajouter un événement final


if (!forceStop) {
addMatchEvent(currentMatch, "🏁 Coup de sifflet final!");

// 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.");
}

// Notification du résultat final


const score = `${currentMatch.team1} ${currentMatch.score1}-$
{currentMatch.score2} ${currentMatch.team2}`;
showNotification(`🏁 Fin du match: ${score}`, 'info');

// Mettre à jour l'affichage une dernière fois


updateMatchDisplay();

// Afficher le résumé du match


showMatchSummary();
}

// Afficher le résumé du match


function showMatchSummary() {
if (!currentMatch) return;

// Masquer la simulation et afficher le résumé


document.getElementById('simulationContainer').style.display = 'none';
document.getElementById('matchSummary').style.display = 'block';

const summaryContent = document.getElementById('matchSummaryContent');

// Générer le contenu du résumé


let summaryHTML = `
<div class="match-result-header mb-4">
<div class="text-center mb-2">
<span class="badge badge-primary">${currentMatch.matchType}</span>
<span class="badge badge-secondary">$
{formatMatchDuration(currentMatch.startTime, currentMatch.endTime)}</span>
</div>
<div class="flex justify-center items-center gap-4">
<div class="text-center">
<div class="font-bold text-xl">${currentMatch.team1}</div>
<div class="text-sm mb-1">${currentMatch.team1Category}</div>
<div class="text-3xl font-bold ${currentMatch.score1 >
currentMatch.score2 ? 'text-success' : ''}">${currentMatch.score1}</div>
</div>
<div class="text-center">vs</div>
<div class="text-center">
<div class="font-bold text-xl">${currentMatch.team2}</div>
<div class="text-sm mb-1">${currentMatch.team2Category}</div>
<div class="text-3xl font-bold ${currentMatch.score2 >
currentMatch.score1 ? 'text-success' : ''}">${currentMatch.score2}</div>
</div>
</div>
</div>

<div class="match-stats-summary mb-4">


<h4 class="font-bold mb-2">Statistiques du match</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div class="stat-row flex justify-between mb-1">
<span>${currentMatch.stats.possession.team1}%</span>
<span class="font-medium">Possession</span>
<span>${currentMatch.stats.possession.team2}%</span>
</div>
<div class="stat-bar mb-3">
<div class="stat-fill" style="width: $
{currentMatch.stats.possession.team1}%;"></div>
</div>

<div class="stat-row flex justify-between mb-1">


<span>${currentMatch.stats.shots.team1}</span>
<span class="font-medium">Tirs</span>
<span>${currentMatch.stats.shots.team2}</span>
</div>
<div class="stat-bar mb-3">
<div class="stat-fill" style="width: $
{calculatePercentage(currentMatch.stats.shots.team1,
currentMatch.stats.shots.team2)}%;"></div>
</div>

<div class="stat-row flex justify-between mb-1">


<span>${currentMatch.stats.shotsOnTarget.team1}</span>
<span class="font-medium">Tirs cadrés</span>
<span>${currentMatch.stats.shotsOnTarget.team2}</span>
</div>
<div class="stat-bar mb-3">
<div class="stat-fill" style="width: $
{calculatePercentage(currentMatch.stats.shotsOnTarget.team1,
currentMatch.stats.shotsOnTarget.team2)}%;"></div>
</div>
</div>

<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="stat-row flex justify-between mb-1">


<span>${currentMatch.stats.yellowCards.team1}</span>
<span class="font-medium">Cartons jaunes</span>
<span>${currentMatch.stats.yellowCards.team2}</span>
</div>

<div class="stat-row flex justify-between mb-1">


<span>${currentMatch.stats.redCards.team1}</span>
<span class="font-medium">Cartons rouges</span>
<span>${currentMatch.stats.redCards.team2}</span>
</div>

<div class="stat-row flex justify-between mb-1">


<span>${(currentMatch.xgTeam1).toFixed(2)}</span>
<span class="font-medium">xG</span>
<span>${(currentMatch.xgTeam2).toFixed(2)}</span>
</div>
<div class="stat-bar mb-3">
<div class="stat-fill" style="width: $
{calculatePercentage(currentMatch.xgTeam1, currentMatch.xgTeam2)}%;"></div>
</div>
</div>
</div>
</div>

<div class="match-events-summary">
<h4 class="font-bold mb-2">Faits marquants</h4>
<ul class="pl-0 list-none">
`;

// Ajouter les événements importants


let importantEvents = currentMatch.events.filter(event =>
event.description.includes("BUT") ||
event.description.includes("Carton rouge") ||
event.description.includes("🏁") ||
event.description.includes("🏆")
);

// Si pas assez d'événements importants, ajouter quelques tirs cadrés


if (importantEvents.length < 3) {
const additionalEvents = currentMatch.events.filter(event =>
event.description.includes("Tir cadré") ||
event.description.includes("bel arrêt")
).slice(0, 3);

importantEvents = importantEvents.concat(additionalEvents);
}

// Trier les événements par minute


importantEvents.sort((a, b) => a.minute - b.minute);

// Ajouter les événements au résumé


importantEvents.forEach(event => {
summaryHTML += `
<li class="mb-2 pb-2 border-b border-gray-200">
<span class="font-medium">${event.minute}'</span> $
{event.description}
</li>
`;
});

summaryHTML += `
</ul>
</div>
`;

// Mettre à jour le contenu


summaryContent.innerHTML = summaryHTML;
}

// Fonction utilitaire pour calculer le pourcentage


function calculatePercentage(value1, value2) {
const total = value1 + value2;
if (total === 0) return 50; // Valeur par défaut si pas de données
return Math.round((value1 / total) * 100);
}

// Fonction utilitaire pour formater la durée du match


function formatMatchDuration(startTime, endTime) {
if (!startTime || !endTime) return "";

const durationMs = new Date(endTime) - new Date(startTime);


const minutes = Math.floor(durationMs / 60000);
const seconds = Math.floor((durationMs % 60000) / 1000);

return `Durée: ${minutes}m ${seconds}s`;


}

// Démarrer une nouvelle simulation


function newSimulation() {
// Réinitialiser l'interface
document.getElementById('matchSetupCard').style.display = 'block';
document.getElementById('simulationContainer').style.display = 'none';
document.getElementById('matchSummary').style.display = 'none';

// Effacer le commentaire
const commentaryList = document.getElementById('commentaryList');
if (commentaryList) {
commentaryList.innerHTML = '';
}

clearTimeout(simulationTimer);
currentMatch = null;

showNotification('Prêt pour une nouvelle simulation', 'info');


}

// Sauvegarder le résultat du match


function saveMatchResult() {
if (!currentMatch || currentMatch.status !== "Terminé") {
showNotification('Aucun match terminé à sauvegarder', 'warning');
return;
}

// Créer un objet résultat simplifié


const matchResult = {
id: currentMatch.id,
team1: currentMatch.team1,
team2: currentMatch.team2,
score1: currentMatch.score1,
score2: currentMatch.score2,
matchType: currentMatch.matchType,
venue: currentMatch.venue,
date: currentMatch.endTime.toISOString(),
stats: {
possession: currentMatch.stats.possession,
shots: currentMatch.stats.shots,
shotsOnTarget: currentMatch.stats.shotsOnTarget,
corners: currentMatch.stats.corners,
yellowCards: currentMatch.stats.yellowCards,
redCards: currentMatch.stats.redCards
},
xG: {
team1: currentMatch.xgTeam1,
team2: currentMatch.xgTeam2
}
};

// Ajouter à l'historique
matchHistory.push(matchResult);

// Sauvegarder les données


saveData();

// Mettre à jour l'historique


updateMatchHistory();

// Notification
showNotification('Résultat du match sauvegardé', 'success');

// Passer à l'onglet d'historique


activateTab('match-history');
}

// Mettre à jour l'historique des matchs


function updateMatchHistory() {
const container = document.getElementById('matchHistoryList');
if (!container) return;

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));

// Créer l'affichage pour chaque match


matchHistory.forEach((match, index) => {
const matchDate = new Date(match.date);
const formattedDate = `${matchDate.toLocaleDateString()} $
{matchDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;

// Déterminer le résultat (victoire, défaite, nul)


let resultClass = 'draw';
let resultText = 'Match nul';
if (match.score1 > match.score2) {
resultClass = 'win-team1';
resultText = `Victoire ${match.team1}`;
} else if (match.score1 < match.score2) {
resultClass = 'win-team2';
resultText = `Victoire ${match.team2}`;
}

const matchElement = document.createElement('div');


matchElement.className = `match-history-item ${resultClass}`;
matchElement.innerHTML = `
<div class="flex justify-between mb-2">
<div>
<span class="badge badge-primary">${match.matchType}</span>
<span class="text-sm text-muted">${formattedDate}</span>
</div>
<div>
<span class="badge badge-${resultClass === 'draw' ? 'warning' :
'success'}">${resultText}</span>
</div>
</div>
<div class="match-result flex justify-between items-center mb-2">
<div class="w-2/5 text-right">${match.team1}</div>
<div class="w-1/5 text-center font-bold">${match.score1} - $
{match.score2}</div>
<div class="w-2/5 text-left">${match.team2}</div>
</div>
<div class="match-stats-brief grid grid-cols-3 gap-2 text-sm">
<div><span class="text-muted">Poss.:</span> $
{match.stats.possession.team1}% - ${match.stats.possession.team2}%</div>
<div><span class="text-muted">Tirs:</span> $
{match.stats.shots.team1} - ${match.stats.shots.team2}</div>
<div><span class="text-muted">xG:</span> $
{match.xG.team1.toFixed(1)} - ${match.xG.team2.toFixed(1)}</div>
</div>
<div class="flex justify-end mt-2">
<button class="btn btn-sm btn-secondary btn-details" data-match-
index="${index}">Détails</button>
<button class="btn btn-sm btn-danger ml-2 btn-delete" data-match-
index="${index}">Supprimer</button>
</div>
`;

container.appendChild(matchElement);
});

// Ajouter les gestionnaires d'événements


document.querySelectorAll('.btn-details').forEach(btn => {
btn.addEventListener('click', function() {
const matchIndex = parseInt(this.getAttribute('data-match-index'));
showMatchDetails(matchHistory[matchIndex]);
});
});

document.querySelectorAll('.btn-delete').forEach(btn => {
btn.addEventListener('click', function() {
const matchIndex = parseInt(this.getAttribute('data-match-index'));
deleteMatchFromHistory(matchIndex);
});
});
}

// Vérifier et démarrer les matchs planifiés


function checkScheduledMatches() {
const now = Date.now();

// Filtrer les matchs qui devraient déjà être en cours


const matchesToStart = scheduledMatches.filter(match => match.scheduledAt <=
now);

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');
});
}
}

// Démarrer un match planifié


function startScheduledMatch(matchId) {
const matchIndex = scheduledMatches.findIndex(m => m.id === matchId);

if (matchIndex === -1) {


showNotification('Match planifié non trouvé', 'error');
return;
}

const matchOptions = scheduledMatches[matchIndex];

// Supprimer de la liste des matchs planifiés


scheduledMatches.splice(matchIndex, 1);
saveData();
updateScheduledMatchesDisplay();

// Remplir le formulaire avec les options du match


document.getElementById('team1').value = matchOptions.team1;
document.getElementById('team2').value = matchOptions.team2;
document.getElementById('matchType').value = matchOptions.matchType;
document.getElementById('venue').value = matchOptions.venue;
document.getElementById('weather').value = matchOptions.weather;
document.getElementById('crowd').value = matchOptions.crowd;
document.getElementById('pastMatches').value = matchOptions.pastMatches || '';
document.getElementById('last5Team1').value = matchOptions.last5Team1 || '';
document.getElementById('last5Team2').value = matchOptions.last5Team2 || '';
document.getElementById('commentaryEnabled').checked =
matchOptions.commentaryEnabled;
document.getElementById('advancedStats').checked = matchOptions.advancedStats;
document.getElementById('realismMode').checked = matchOptions.realismMode;
document.getElementById('intensityFactor').checked =
matchOptions.intensityFactor;
document.getElementById('backgroundNotifications').checked =
matchOptions.backgroundNotifications;
document.getElementById('detailedEvents').checked =
matchOptions.detailedEvents;

// Mettre à jour les informations d'équipe


updateTeamInfo();
// Revenir à l'onglet de configuration
activateTab('match-setup');

// Notification
showNotification(`Match ${matchOptions.team1} vs ${matchOptions.team2} prêt à
démarrer!`, 'info');
}

// Supprimer un match planifié


function deleteScheduledMatch(matchId) {
const matchIndex = scheduledMatches.findIndex(m => m.id === matchId);

if (matchIndex === -1) {


showNotification('Match planifié non trouvé', 'error');
return;
}

// Supprimer de la liste
scheduledMatches.splice(matchIndex, 1);
saveData();

// Mettre à jour l'affichage


updateScheduledMatchesDisplay();

showNotification('Match planifié supprimé', 'success');


}

// Afficher les détails d'un match de l'historique


function showMatchDetails(match) {
if (!match) return;

// Afficher une version simplifiée des détails pour l'instant


showNotification(`Match: ${match.team1} ${match.score1} - ${match.score2} $
{match.team2}`, 'info');
}

// Supprimer un match de l'historique


function deleteMatchFromHistory(index) {
if (confirm('Êtes-vous sûr de vouloir supprimer ce match de l\'historique?')) {
matchHistory.splice(index, 1);
saveData();
updateMatchHistory();

showNotification('Match supprimé de l\'historique', 'success');


}
}

// Effacer l'historique des matchs


function clearMatchHistory() {
if (confirm('Êtes-vous sûr de vouloir effacer tout l\'historique des matchs?'))
{
matchHistory = [];
saveData();
updateMatchHistory();

showNotification('Historique des matchs effacé', 'success');


}
}
/* =========================================================
FONCTIONNALITÉS AVANCÉES ET ÉDITION DES ÉQUIPES
========================================================= */

// Modification des données d'équipe


function editTeamData(teamName) {
// Vérifier que l'équipe existe
if (!teamData[teamName]) {
showNotification(`L'équipe ${teamName} n'existe pas dans les données`,
'error');
return;
}

// Créer une modal pour l'édition


const modalHtml = `
<div id="editTeamModal" class="fixed inset-0 flex items-center justify-
center z-50" style="background-color: rgba(0,0,0,0.5);">
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 w-full max-w-md">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-bold">Modifier ${teamName}</h3>
<button id="closeEditModal" class="text-gray-500 hover:text-
gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="grid grid-cols-2 gap-3 mb-4">
<div class="form-group">
<label for="editPoss">Possession (%)</label>
<input type="number" id="editPoss" class="form-control"
value="${teamData[teamName].Poss || 50}" min="0" max="100">
</div>
<div class="form-group">
<label for="editGls">Buts marqués</label>
<input type="number" id="editGls" class="form-control"
value="${teamData[teamName].Gls || 0}" min="0">
</div>
<div class="form-group">
<label for="editGA">Buts encaissés</label>
<input type="number" id="editGA" class="form-control"
value="${teamData[teamName].GA || 0}" min="0">
</div>
<div class="form-group">
<label for="editxG90">xG per 90</label>
<input type="number" id="editxG90" class="form-control"
value="${teamData[teamName]['xG/90'] || 1.2}" min="0" step="0.1">
</div>
<div class="form-group">
<label for="editMP">Matchs joués</label>
<input type="number" id="editMP" class="form-control"
value="${teamData[teamName].MP || 0}" min="0">
</div>
<div class="form-group">
<label for="editPrgP">Passes progressives</label>
<input type="number" id="editPrgP" class="form-control"
value="${teamData[teamName].PrgP || 0}" min="0">
</div>
<div class="form-group">
<label for="editCrdY">Cartons jaunes</label>
<input type="number" id="editCrdY" class="form-control"
value="${teamData[teamName].CrdY || 0}" min="0">
</div>
<div class="form-group">
<label for="editCrdR">Cartons rouges</label>
<input type="number" id="editCrdR" class="form-control"
value="${teamData[teamName].CrdR || 0}" min="0">
</div>
</div>
<div class="form-group">
<label>Classification</label>
<div class="grid grid-cols-2 gap-2 mt-2">
<div>
<span class="font-medium">Catégorie:</span>
<span id="editCategory" class="ml-2 badge badge-
primary">${teamData[teamName].TeamCategory || 'Moyenne'}</span>
</div>
<div>
<span class="font-medium">Style:</span>
<span id="editStyle" class="ml-2 badge badge-
secondary">${teamData[teamName].TeamStyle || 'Balanced'}</span>
</div>
<div>
<span class="font-medium">Profil:</span>
<span id="editProfile" class="ml-2 badge badge-info">$
{teamData[teamName].TeamProfile || 'Mid-Table'}</span>
</div>
<div>
<span class="font-medium">Discipline:</span>
<span id="editDiscipline" class="ml-2 badge badge-
warning">${teamData[teamName].TeamDiscipline || 3}/5</span>
</div>
</div>
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelEditBtn" class="btn
btn-secondary">Annuler</button>
<button id="saveEditBtn" class="btn
btn-primary">Enregistrer</button>
</div>
</div>
</div>
`;

// Ajouter la modal au DOM


const modalContainer = document.createElement('div');
modalContainer.innerHTML = modalHtml;
document.body.appendChild(modalContainer);

// Ajouter les gestionnaires d'événements


document.getElementById('closeEditModal').addEventListener('click',
closeEditModal);
document.getElementById('cancelEditBtn').addEventListener('click',
closeEditModal);
document.getElementById('saveEditBtn').addEventListener('click', () =>
saveEditedTeam(teamName));

// Mettre à jour les classifications en direct


const editableFields = ['editPoss', 'editGls', 'editGA', 'editxG90', 'editMP',
'editPrgP', 'editCrdY', 'editCrdR'];
editableFields.forEach(field => {
document.getElementById(field).addEventListener('input', () =>
updateClassificationPreview(teamName));
});

// Mettre à jour l'aperçu initial


updateClassificationPreview(teamName);
}

// Mettre à jour l'aperçu de classification lors de l'édition


function updateClassificationPreview(teamName) {
// Créer une copie temporaire des données d'équipe avec les valeurs modifiées
const tempTeamData = {...teamData[teamName]};

// Mettre à jour les valeurs depuis les champs de formulaire


tempTeamData.Poss = parseFloat(document.getElementById('editPoss').value) ||
50;
tempTeamData.Gls = parseFloat(document.getElementById('editGls').value) || 0;
tempTeamData.GA = parseFloat(document.getElementById('editGA').value) || 0;
tempTeamData['xG/90'] = parseFloat(document.getElementById('editxG90').value)
|| 1.2;
tempTeamData.MP = parseInt(document.getElementById('editMP').value) || 0;
tempTeamData.PrgP = parseInt(document.getElementById('editPrgP').value) || 0;
tempTeamData.CrdY = parseFloat(document.getElementById('editCrdY').value) || 0;
tempTeamData.CrdR = parseFloat(document.getElementById('editCrdR').value) || 0;

// Remplacer temporairement dans teamData pour utiliser les fonctions de


classification
const originalTeamData = teamData[teamName];
teamData[teamName] = tempTeamData;

// Calculer les nouvelles classifications


const category = classifyTeam(teamName);
const style = identifyTeamStyle(teamName);
const profile = determineTeamProfile(teamName);
const discipline = evaluateDiscipline(teamName);

// Restaurer les données originales


teamData[teamName] = originalTeamData;

// Mettre à jour l'aperçu


document.getElementById('editCategory').textContent = category;
document.getElementById('editCategory').className = `badge badge-primary
category-${category.toLowerCase()}`;

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`;
}

// Sauvegarder les données d'équipe modifiées


function saveEditedTeam(teamName) {
// Récupérer les valeurs du formulaire
const updatedTeam = {...teamData[teamName]};

updatedTeam.Poss = parseFloat(document.getElementById('editPoss').value) || 50;


updatedTeam.Gls = parseFloat(document.getElementById('editGls').value) || 0;
updatedTeam.GA = parseFloat(document.getElementById('editGA').value) || 0;
updatedTeam['xG/90'] = parseFloat(document.getElementById('editxG90').value) ||
1.2;
updatedTeam.MP = parseInt(document.getElementById('editMP').value) || 0;
updatedTeam.PrgP = parseInt(document.getElementById('editPrgP').value) || 0;
updatedTeam.CrdY = parseFloat(document.getElementById('editCrdY').value) || 0;
updatedTeam.CrdR = parseFloat(document.getElementById('editCrdR').value) || 0;

// Mettre à jour les classifications


updatedTeam.TeamCategory = classifyTeam(teamName);
updatedTeam.TeamStyle = identifyTeamStyle(teamName);
updatedTeam.TeamProfile = determineTeamProfile(teamName);
updatedTeam.TeamDiscipline = evaluateDiscipline(teamName);

// Enregistrer les données mises à jour


teamData[teamName] = updatedTeam;
saveData();

// Mettre à jour l'affichage


updateTeamsContainer();
populateTeamOptions();
updateTeamInfo();

// Fermer la modal
closeEditModal();

// Notification
showNotification(`Données de ${teamName} mises à jour avec succès`, 'success');
}

// Fermer la modal d'édition


function closeEditModal() {
const modal = document.getElementById('editTeamModal');
if (modal && modal.parentNode) {
modal.parentNode.remove();
}
}

/* =========================================================
SIMULATION MONTE CARLO ET FONCTIONS AVANCÉES
========================================================= */

// Simulation Monte Carlo pour prédire les résultats de match


class MonteCarloSimulation {
constructor(team1, team2, matchType, venue, weather, crowd) {
this.team1 = team1;
this.team2 = team2;
this.matchType = matchType;
this.venue = venue;
this.weather = weather;
this.crowd = crowd;

// Calculer les xG de base


this.xgTeam1 = calculateXG(team1, team2, matchType, venue, weather, crowd);
this.xgTeam2 = calculateXG(team2, team1, matchType, venue === "home" ?
"away" : (venue === "away" ? "home" : "neutral"), weather, crowd);

// 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;

// Tracker pour les scores les plus fréquents


const scoreFrequency = {};

for (let i = 0; i < iterations; i++) {


// Ajouter une petite variance à chaque simulation pour plus de
réalisme
const variance1 = (Math.random() - 0.5) * 0.5; // ±25% de variance
const variance2 = (Math.random() - 0.5) * 0.5;

const adjustedXG1 = Math.max(0, this.xgTeam1 + variance1);


const adjustedXG2 = Math.max(0, this.xgTeam2 + variance2);

// Simuler les buts avec une distribution de Poisson


const goals1 = this.poissonRandom(adjustedXG1);
const goals2 = this.poissonRandom(adjustedXG2);

// Enregistrer le résultat
this.results.push({ goals1, goals2 });

// Mettre à jour les statistiques


if (goals1 > goals2) team1Wins++;
else if (goals1 < goals2) team2Wins++;
else draws++;

// Mettre à jour la fréquence des scores


const scoreKey = `${goals1}-${goals2}`;
scoreFrequency[scoreKey] = (scoreFrequency[scoreKey] || 0) + 1;
}

// Calculer les pourcentages


this.team1WinRate = (team1Wins / iterations) * 100;
this.team2WinRate = (team2Wins / iterations) * 100;
this.drawRate = (draws / iterations) * 100;

// Trouver le score le plus fréquent


let maxFrequency = 0;
for (const [score, frequency] of Object.entries(scoreFrequency)) {
if (frequency > maxFrequency) {
maxFrequency = frequency;
this.mostCommonScore = score;
}
}
// Calculer les autres statistiques
this.calculateExtraStats();

return {
team1WinRate: this.team1WinRate,
team2WinRate: this.team2WinRate,
drawRate: this.drawRate,
mostCommonScore: this.mostCommonScore,
results: this.results
};
}

// Fonction pour générer un nombre aléatoire selon une distribution de Poisson


poissonRandom(lambda) {
if (lambda <= 0) return 0;

const L = Math.exp(-lambda);
let p = 1.0;
let k = 0;

do {
k++;
p *= Math.random();
} while (p > L);

return k - 1;
}

// Calculer des statistiques supplémentaires


calculateExtraStats() {
// Calculer la moyenne des buts
let totalGoals1 = 0;
let totalGoals2 = 0;
let cleanSheets1 = 0;
let cleanSheets2 = 0;
let bothTeamsScore = 0;

this.results.forEach(result => {
totalGoals1 += result.goals1;
totalGoals2 += result.goals2;

if (result.goals1 === 0) cleanSheets2++;


if (result.goals2 === 0) cleanSheets1++;
if (result.goals1 > 0 && result.goals2 > 0) bothTeamsScore++;
});

const iterations = this.results.length;


this.averageGoals1 = totalGoals1 / iterations;
this.averageGoals2 = totalGoals2 / iterations;
this.cleanSheetRate1 = (cleanSheets1 / iterations) * 100;
this.cleanSheetRate2 = (cleanSheets2 / iterations) * 100;
this.bothTeamsScoreRate = (bothTeamsScore / iterations) * 100;
this.over25GoalsRate = this.results.filter(r => (r.goals1 + r.goals2) >
2.5).length / iterations * 100;
}

// Générer un rapport HTML avec les résultats


generateHTMLReport() {
// Formatage pour 1 décimale
const format = (val) => val.toFixed(1);

// Parser le score le plus fréquent


let scoreText = "N/A";
if (this.mostCommonScore) {
const [g1, g2] = this.mostCommonScore.split('-').map(Number);
scoreText = `${this.team1} ${g1}-${g2} ${this.team2}`;
}

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>

<div class="flex justify-between">


<span>Match nul:</span>
<span class="font-medium">${format(this.drawRate)}
%</span>
</div>
<div class="bg-gray-200 dark:bg-gray-700 h-2 rounded-
full overflow-hidden">
<div class="bg-yellow-500 h-full" style="width: $
{this.drawRate}%"></div>
</div>

<div class="flex justify-between">


<span>Victoire ${this.team2}:</span>
<span class="font-medium">$
{format(this.team2WinRate)}%</span>
</div>
<div class="bg-gray-200 dark:bg-gray-700 h-2 rounded-
full overflow-hidden">
<div class="bg-red-500 h-full" style="width: $
{this.team2WinRate}%"></div>
</div>
</div>
</div>

<div class="card p-4">


<h4 class="font-bold mb-2">Statistiques</h4>
<div class="flex flex-col gap-2">
<div class="grid grid-cols-2 gap-2">
<div>
<div class="text-muted">xG</div>
<div class="flex justify-between">
<span>${this.team1}:</span>
<span class="font-medium">$
{format(this.xgTeam1)}</span>
</div>
<div class="flex justify-between">
<span>${this.team2}:</span>
<span class="font-medium">$
{format(this.xgTeam2)}</span>
</div>
</div>
<div>
<div class="text-muted">Buts moyens</div>
<div class="flex justify-between">
<span>${this.team1}:</span>
<span class="font-medium">$
{format(this.averageGoals1)}</span>
</div>
<div class="flex justify-between">
<span>${this.team2}:</span>
<span class="font-medium">$
{format(this.averageGoals2)}</span>
</div>
</div>
</div>

<div class="text-center mt-2">


<div class="font-bold mb-1">Score le plus
probable</div>
<div class="text-xl">${scoreText}</div>
</div>
</div>
</div>
</div>

<div class="grid grid-cols-1 md:grid-cols-3 gap-3">


<div class="stat-card">
<div class="stat-value">${format(this.bothTeamsScoreRate)}
%</div>
<div class="stat-label">Les deux équipes marquent</div>
</div>
<div class="stat-card">
<div
class="stat-value">${format(this.over25GoalsRate)}%</div>
<div class="stat-label">Plus de 2.5 buts</div>
</div>
<div class="stat-card">
<div class="stat-value">${format(this.cleanSheetRate1)}% /
${format(this.cleanSheetRate2)}%</div>
<div class="stat-label">Clean sheets (${this.team1} / $
{this.team2})</div>
</div>
</div>
</div>
`;
}
}

// Exécuter une simulation Monte Carlo pour le match configuré


function runMonteCarloSimulation() {
const team1 = document.getElementById('team1').value;
const team2 = document.getElementById('team2').value;

if (!team1 || !team2 || team1 === team2) {


showNotification('Veuillez sélectionner deux équipes différentes',
'warning');
return;
}

// Récupérer les options du match


const matchType = document.getElementById('matchType').value;
const venue = document.getElementById('venue').value;
const weather = document.getElementById('weather').value;
const crowd = document.getElementById('crowd').value;

// Créer la simulation
const simulation = new MonteCarloSimulation(team1, team2, matchType, venue,
weather, crowd);

// Afficher un loader
document.getElementById('loaderContainer').style.display = 'flex';

// Exécuter la simulation de manière asynchrone pour ne pas bloquer l'interface


setTimeout(() => {
// Exécuter la simulation
const results = simulation.runSimulation(5000);

// Créer une modal pour afficher les résultats


const modalHtml = `
<div id="monteCarloModal" class="fixed inset-0 flex items-center
justify-center z-50" style="background-color: rgba(0,0,0,0.5);">
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 w-full max-w-
4xl">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-bold">Prédiction: ${team1} vs $
{team2}</h3>
<button id="closeMonteCarloModal" class="text-gray-500
hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div id="monteCarloResults">
${simulation.generateHTMLReport()}
</div>
<div class="flex justify-end mt-4">
<button id="closeMonteCarloBtn" class="btn btn-
primary">Fermer</button>
</div>
</div>
</div>
`;

// Ajouter la modal au DOM


const modalContainer = document.createElement('div');
modalContainer.innerHTML = modalHtml;
document.body.appendChild(modalContainer);

// Ajouter les gestionnaires d'événements


document.getElementById('closeMonteCarloModal').addEventListener('click',
closeMonteCarloModal);
document.getElementById('closeMonteCarloBtn').addEventListener('click',
closeMonteCarloModal);

// Cacher le loader
document.getElementById('loaderContainer').style.display = 'none';
}, 100);
}

// Fermer la modal Monte Carlo


function closeMonteCarloModal() {
const modal = document.getElementById('monteCarloModal');
if (modal && modal.parentNode) {
modal.parentNode.remove();
}
}

/* =========================================================
OPTIMISATIONS ET ANIMATIONS AVANCÉES
========================================================= */

// Options de débogage pour les développeurs


let DEBUG_MODE = false;

// Activer/désactiver le mode débogage


function toggleDebugMode() {
DEBUG_MODE = !DEBUG_MODE;
showNotification(`Mode débogage ${DEBUG_MODE ? 'activé' : 'désactivé'}`,
'info');

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);
}
}

// Améliorer l'animation de but


function enhancedGoalAnimation(scoringTeam, otherTeam, minute) {
const goalAnimation = document.getElementById('goalAnimation');
const goalTeam = document.getElementById('goalTeam');

if (!goalAnimation || !goalTeam) return;

// Ajouter le nom de l'équipe et le score


const score = currentMatch ?
`${currentMatch.team1} ${currentMatch.score1}-${currentMatch.score2} $
{currentMatch.team2}` :
`${scoringTeam} marque!`;

goalTeam.textContent = `${scoringTeam} (${minute}')`;


goalAnimation.style.display = 'flex';

// Ajouter des classes pour les animations


goalAnimation.classList.add('goal-animation-active');

// Jouer un son (facultatif si l'utilisateur a activé le son)


playGoalSound();
// Masquer après 3 secondes
setTimeout(() => {
goalAnimation.classList.remove('goal-animation-active');
setTimeout(() => {
goalAnimation.style.display = 'none';
}, 300);
}, 3000);
}

// Jouer le son de but (généré dynamiquement)


function playGoalSound() {
try {
// Créer un oscillateur pour générer un son simple
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();

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 }
];

let time = audioCtx.currentTime;

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);
}
}

// Optimiser les performances d'affichage pour les listes longues


function optimizeListDisplay(container, items, itemRenderer, limitPerPage = 20) {
let currentPage = 0;
const totalPages = Math.ceil(items.length / limitPerPage);

// Fonction pour rendre la page courante


const renderPage = (page) => {
container.innerHTML = '';
const start = page * limitPerPage;
const end = Math.min(start + limitPerPage, items.length);

// Rendre uniquement les éléments de la page courante


for (let i = start; i < end; i++) {
const itemElement = itemRenderer(items[i], i);
container.appendChild(itemElement);
}

// Ajouter la pagination si nécessaire


if (totalPages > 1) {
const paginationElement = document.createElement('div');
paginationElement.className = 'pagination flex justify-center gap-2 mt-
4';

// 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);
}

// Afficher le numéro de page


const pageInfo = document.createElement('span');
pageInfo.className = 'pagination-info flex items-center px-2';
pageInfo.textContent = `Page ${page + 1}/${totalPages}`;
paginationElement.appendChild(pageInfo);

// 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);
}
};

// Rendre la première page


renderPage(currentPage);
}

// Créer un graphique de statistiques pour les matchs


function createMatchStatsChart(matchData) {
// Vérifier si la bibliothèque Chart.js est disponible
if (!window.Chart) {
console.error('Chart.js n\'est pas disponible');
return null;
}

// Créer un canevas pour le graphique


const canvas = document.createElement('canvas');
canvas.className = 'match-stats-chart';
canvas.height = 200;
// Créer le graphique
const ctx = canvas.getContext('2d');
const chart = new Chart(ctx, {
type: 'radar',
data: {
labels: ['Possession', 'Tirs', 'Tirs cadrés', 'Corners', 'xG',
'Défense'],
datasets: [
{
label: matchData.team1,
data: [
matchData.stats.possession.team1,
matchData.stats.shots.team1,
matchData.stats.shotsOnTarget.team1,
matchData.stats.corners.team1,
matchData.xG.team1 * 20, // Échelle ajustée pour
correspondre aux autres valeurs
100 - (matchData.stats.shots.team2 * 5) // Valeur inversée
pour représenter la défense
],
backgroundColor: 'rgba(93, 92, 222, 0.2)',
borderColor: 'rgba(93, 92, 222, 1)',
pointBackgroundColor: 'rgba(93, 92, 222, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(93, 92, 222, 1)'
},
{
label: matchData.team2,
data: [
matchData.stats.possession.team2,
matchData.stats.shots.team2,
matchData.stats.shotsOnTarget.team2,
matchData.stats.corners.team2,
matchData.xG.team2 * 20, // Échelle ajustée
100 - (matchData.stats.shots.team1 * 5) // Défense
],
backgroundColor: 'rgba(231, 76, 60, 0.2)',
borderColor: 'rgba(231, 76, 60, 1)',
pointBackgroundColor: 'rgba(231, 76, 60, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(231, 76, 60, 1)'
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
elements: {
line: {
tension: 0.1
}
},
scales: {
r: {
angleLines: {
display: true
},
suggestedMin: 0,
suggestedMax: 100
}
}
}
});

return canvas;
}

/* =========================================================
GESTION DES ÉVÉNEMENTS SUPPLÉMENTAIRES
========================================================= */

// Ajouter un bouton Monte Carlo à l'interface


function addMonteCarloButton() {
// Vérifier si le bouton existe déjà
if (document.getElementById('monteCarloBtn')) return;

// 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);

// Ajouter le bouton dans la section des actions du formulaire


const formActions = document.querySelector('.form-actions');
if (formActions) {
formActions.insertBefore(btn, formActions.firstChild);
}
}

// Initialiser les nouvelles fonctionnalités


function initializeAdditionalFeatures() {
// Ajouter le bouton Monte Carlo
addMonteCarloButton();

// Vérifier si le code s'exécute dans Poe et ajouter des fonctionnalités


spécifiques
if (window.Poe) {
// Ajouter un lien pour pouvoir partager les résultats de match
document.addEventListener('click', function(e) {
if (e.target.classList.contains('share-match-result')) {
const resultText = e.target.getAttribute('data-result');
if (resultText) {
window.Poe.sendUserMessage(
`@Claude-3.7-Sonnet Voici le résultat du match que j'ai
simulé: ${resultText}. Peux-tu me donner une analyse détaillée de ce résultat?`,
{ openChat: true }
).catch(err => {
console.error('Erreur lors du partage:', err);
});
}
}
});
}

// Ajouter un gestionnaire pour le clic droit pour le mode débogage


document.addEventListener('contextmenu', function(e) {
if (e.ctrlKey && e.altKey) {
e.preventDefault();
toggleDebugMode();
}
});
}

// Ajouter un bouton de partage aux résultats de match


function addShareButton(container, matchData) {
const resultText = `${matchData.team1} ${matchData.score1}-${matchData.score2}
${matchData.team2} (${matchData.matchType})
Statistiques: Possession ${matchData.stats.possession.team1}%/$
{matchData.stats.possession.team2}%, Tirs ${matchData.stats.shots.team1}/$
{matchData.stats.shots.team2}, xG ${matchData.xG.team1.toFixed(2)}/$
{matchData.xG.team2.toFixed(2)}`;

const shareButton = document.createElement('button');


shareButton.className = 'btn btn-sm btn-primary share-match-result';
shareButton.setAttribute('data-result', resultText);
shareButton.innerHTML = '<i class="fas fa-share-alt"></i> Partager dans Poe';

container.appendChild(shareButton);
}

// Recherche avancée d'équipes


function searchTeams(query, options = {}) {
if (!query) return Object.keys(teamData);

const { limit = 10, minScore = 0.3, includeCriteria = [] } = options;


query = query.toLowerCase();

// Fonction de score simple


const scoreTeam = (team) => {
const teamLC = team.toLowerCase();
let score = 0;

// 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;

// Correspondance de mots individuels


const queryWords = query.split(/\s+/);
const teamWords = teamLC.split(/\s+/);

for (const qWord of queryWords) {


for (const tWord of teamWords) {
if (tWord === qWord) score += 0.4;
else if (tWord.startsWith(qWord)) score += 0.3;
else if (tWord.includes(qWord)) score += 0.2;
}
}

// 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;
}
}
}

return Math.min(1.0, score);


};

// Calculer les scores pour chaque équipe


const scoredTeams = Object.keys(teamData).map(team => ({
team,
score: scoreTeam(team)
})).filter(item => item.score >= minScore);

// Trier par score décroissant


scoredTeams.sort((a, b) => b.score - a.score);

// Limiter le nombre de résultats


return scoredTeams.slice(0, limit).map(item => item.team);
}

// Améliorer la recherche d'équipes dans l'interface


function enhanceTeamSearch() {
const searchInput = document.getElementById('teamSearch');
if (!searchInput) return;

// Créer un conteneur de suggestions


const suggestionsContainer = document.createElement('div');
suggestionsContainer.className = 'search-suggestions absolute bg-white dark:bg-
gray-800 shadow-lg rounded-lg p-2 max-h-60 overflow-y-auto z-10 w-full';
suggestionsContainer.style.display = 'none';

// Insérer après l'input


searchInput.parentNode.style.position = 'relative';
searchInput.parentNode.insertBefore(suggestionsContainer,
searchInput.nextSibling);

// Gestionnaire pour l'input


searchInput.addEventListener('input', function() {
const query = this.value.trim();
if (query.length < 2) {
suggestionsContainer.style.display = 'none';
return;
}

// Rechercher les équipes


const results = searchTeams(query, { limit: 8 });

if (results.length === 0) {
suggestionsContainer.style.display = 'none';
return;
}

// Afficher les suggestions


suggestionsContainer.innerHTML = '';
results.forEach(team => {
const suggestion = document.createElement('div');
suggestion.className = 'suggestion cursor-pointer p-2 hover:bg-gray-100
dark:hover:bg-gray-700 rounded';
suggestion.textContent = team;

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';
});

// Gestionnaire pour les clics en dehors


document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !
suggestionsContainer.contains(e.target)) {
suggestionsContainer.style.display = 'none';
}
});
}

// Initialisation finale de l'application


document.addEventListener('DOMContentLoaded', function() {
// Initialiser toutes les fonctionnalités supplémentaires
initializeAdditionalFeatures();

// Améliorer la recherche d'équipes


enhanceTeamSearch();

// Compléter l'initialisation en vérifiant l'état de l'application


setTimeout(() => {
// Si aucune donnée n'est chargée, proposer de charger les données de démo
if (Object.keys(teamData).length === 0) {
const shouldLoadDemo = confirm('Aucune donnée d\'équipe n\'est chargée.
Voulez-vous charger les données de démonstration?');
if (shouldLoadDemo) {
loadDemoData();
}
}
}, 1000);
});

// Appliquer les correctifs pour les navigateurs mobiles


function applyMobileCompatibilityFixes() {
// Correction du zoom sur les inputs sur iOS
const inputs = document.querySelectorAll('input[type="text"],
input[type="number"], select, textarea');
inputs.forEach(input => {
input.style.fontSize = '16px';
});

// Correction pour les événements tactiles


document.addEventListener('touchstart', function() {}, {passive: true});
}

// Appliquer les correctifs mobiles au chargement


if (typeof window !== 'undefined') {
applyMobileCompatibilityFixes();
}

</script>
</body>
</html>

maintenant voici mes exigences

ce que je veux en réalité

📌 Cahier des Charges Complet – Simulateur de Match de Football Ultra-Avancé

L’objectif est de développer le simulateur de match de football le plus puissant et


réaliste au monde, intégrant des modèles statistiques avancés, des données en temps
réel, et une IA dynamique pour recréer le déroulement d’un match avec un réalisme
inégalé.

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.

---

🔹 1. Système de Simulation en Temps Réel

✅ Déroulement du match en 90 minutes virtuelles


✅ Évolution progressive du score, des actions et des événements (cartons,
blessures, remplacements, etc.)
✅ Affichage en temps réel des événements du match (live texte, actions, tirs, buts,
fautes, etc.)
✅ Possibilité de simuler plusieurs matchs en parallèle dans une interface organisée
par championnats

📌 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èle Monte-Carlo : pour générer des scénarios réalistes en fonction des


probabilités de chaque événement.

---

🔹 2. Facteurs d’Influence sur le Match

Le simulateur prend en compte tous les paramètres réalistes influençant un match de


football :

✅ Statistiques des équipes (forme récente, puissance offensive/défensive,


historique H2H, etc.)
✅ Cotes des bookmakers (utilisées pour ajuster les probabilités des résultats)
✅ Avantage du terrain (modification du pressing, de la fatigue, de l’intensité du
match)
✅ Facteurs psychologiques (momentum, confiance, réaction aux événements du match)
✅ Conditions météorologiques (pluie, vent, chaleur, neige) affectant la fatigue et
la précision des passes/tirs)

📌 Modèles utilisés :

Régression logistique pour estimer l’impact de chaque facteur sur la probabilité de


victoire.

Modèle de Markov pour simuler les transitions d’état dans le match (possession,
attaque, défense, etc.).

---

🔹 3. Modélisation des Joueurs et Influence Individuelle

Chaque joueur aura une influence basée sur ses caractéristiques statistiques :

✅ Précision des tirs et passes (impacte le nombre d’occasions créées et converties)


✅ Vitesse et endurance (impacte la fatigue et la capacité à tenir un rythme élevé)
✅ Risque de blessures (selon le style de jeu et les conditions du match)
✅ Capacité défensive (influence les interceptions et tacles réussis)
✅ Expérience et mental (impacte la réaction aux événements comme un but encaissé ou
un carton rouge)

📌 Modèles utilisés :

Modèle Monte-Carlo pour simuler l’impact aléatoire des performances individuelles.

Processus de Poisson modifié pour la génération des actions des joueurs.

---

🔹 4. Gestion des Actions et du Ballon


Le simulateur doit afficher une évolution fluide des actions de jeu :

✅ Possession dynamique avec animations des joueurs sur le terrain


✅ Déclenchement d’attaques dangereuses, passes clés, frappes, interceptions, etc.
✅ Live texte immersif généré en temps réel pour suivre le déroulement du match

📌 Modèles utilisés :

Chaînes de Markov pour modéliser la transition des phases de jeu (possession →


attaque → tir → but ou arrêt).

Modèle Bayésien pour évaluer la probabilité de réussite de chaque action selon la


situation du match.

---

🔹 5. Calcul des Expected Goals (xG) et Qualité des Occasions

L’Expected Goals (xG) permet d’évaluer la probabilité qu’un tir aboutisse à un but
en fonction de :

✅ Type d’occasion (contre-attaque, coup franc, corner, tir lointain, face-à-face,


etc.)
✅ Distance et angle du tir
✅ Présence de défenseurs et position du gardien
✅ Qualité du tireur (pied fort, précision, puissance, etc.)

📌 Modèles utilisés :

Modèle de régression logistique pour calculer le xG selon plusieurs paramètres.

Modèle probabiliste ajusté aux données historiques pour déterminer le taux de


conversion des occasions.

---

🔹 6. Gestion des Fautes, Cartons et Arbitrage Dynamique

L’arbitrage est influencé par plusieurs paramètres :

✅ Style de l’arbitre (tolérant ou strict)


✅ Équipe dominante (les équipes dominantes subissent parfois moins de fautes
sifflées)
✅ Accumulation de fautes (influence la probabilité de recevoir un carton)
✅ Facteurs psychologiques (nervosité, réactions aux événements du match)

📌 Modèles utilisés :

Processus de Poisson pour la fréquence des fautes.

Modèle Bayésien pour la distribution des cartons en fonction du match.

---
🔹 7. Gestion des Remplacements et Adaptation des Tactiques

Les équipes ajustent leur jeu en fonction du score et de la fatigue :

✅ Remplacements après la 60ᵉ minute pour reposer les joueurs fatigués


✅ Changement de tactique en fonction du score (défensif si 2-0, offensif si 0-1,
etc.)
✅ Utilisation des statistiques des joueurs pour maximiser l’efficacité des
changements

📌 Modèles utilisés :

Modèle d’apprentissage automatique pour prédire les choix de remplacements les plus
optimaux.

Simulation Monte-Carlo pour tester différentes stratégies et choisir la meilleure


option.

---

🔹 8. Interface Graphique et Affichage des Résultats

✅ Simulation affichée en temps réel avec animations des joueurs et du ballon


✅ Score et statistiques mises à jour en direct
✅ Live texte immersif pour suivre l’évolution du match
✅ Possibilité de voir un résumé des moments clés après la fin du match

📌 Technologies utilisées :

JavaScript + HTML5 Canvas pour l’animation du terrain et des joueurs.

CSS et animations WebGL pour améliorer l’affichage.

Stockage des résultats dans une base de données pour consultation ultérieure.

---

📌 Conclusion Générale du Cahier des Charges

🎯 Objectif final : Créer un moteur de simulation ultra-réaliste combinant plusieurs


modèles avancés.
📌 Modèles statistiques utilisés :

Monte-Carlo (simulations probabilistes des résultats et performances)

Processus de Poisson (distribution des buts et des fautes)

Chaînes de Markov (transitions entre phases de jeu)

Modèle de régression logistique (prédiction des Expected Goals)

Modèle Bayésien (distribution des événements)


✅ Tout doit être intégré de manière optimisée et directement fonctionnelle dans
l’application.

premièrement n'utilise pas ton outil de remplacement de code interdiction


maintenant deuxièmement réponds-moi en français troisièmement voilà comment je veux
que tu possèdes je vais te projeter ma pensée c'est qui est dans ma tête et on va
voir comment procéder ce que je veux en fait ce à quoi j'aspire

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

✅ Résumé Global du Projet – Simulateur de Match Ultra-Avancé

L'objectif est de concevoir le simulateur de match de football le plus réaliste et


avancé possible en intégrant toutes les données disponibles : statistiques
détaillées des équipes et joueurs, cotes des bookmakers, historique des
confrontations, conditions de match et modèles avancés d’intelligence artificielle
pour simuler un match en temps réel avec une logique dynamique.

---

1 1 Fonctionnalités Globales
1️⃣

✅ Collage Automatique des Données

L’utilisateur colle les données brutes des équipes et joueurs.

Le système analyse et structure automatiquement ces données.

Identification des équipes, joueurs, postes et statistiques associées.

✅ Gestion des Titulaires et Remplaçants

Sélection automatique ou manuelle des 11 titulaires et remplaçants.

Intégration des statistiques individuelles pour chaque joueur.

✅ Détection et Classification des Joueurs

L’IA reconnaît automatiquement les postes (FW, MF, DF, GK).

Attribution des rôles selon les performances et caractéristiques.

✅ Influence Dynamique des Joueurs sur le Match

Attaquants : xG, tirs, précision, vitesse.

Milieux : passes clés, possession, organisation du jeu.

Défenseurs : tacles, interceptions, duels gagnés.


Gardiens : arrêts, buts encaissés, PSxG.

✅ Simulation du Match en Temps Réel

Évolution dynamique en fonction des événements du match.

Influence des décisions tactiques, fatigue, cartons et momentum.

Animations interactives et commentaires en direct (live texte).

---

2️⃣ Données & Variables Exploitées

📌 Équipes & Contexte du Match

TeamName, Form, H2H, Odds, Home/Away Advantage

📊 Influence sur : probabilité de victoire, domination tactique

📌 Performances Offensives

Goals, Shots, xG, SoT%, Key Passes, Dribbles

📊 Influence sur : nombre d’occasions, qualité des tirs

📌 Performances Défensives

Tackles, Interceptions, Clearances, Blocks, xGA

📊 Influence sur : résistance défensive, erreurs individuelles

📌 Milieux & Construction du Jeu

Passes, Possession%, xA, Pressing, Duels gagnés

📊 Influence sur : contrôle du jeu, création d’occasions

📌 Gardiens & Performance dans les Buts

Saves, PSxG, GA, Crosses Claimed, Sweeper Actions

📊 Influence sur : résistance face aux attaques adverses

📌 Facteurs Externes & Psychologiques

Momentum, Fatigue, Form, Weather, Referee Strictness

📊 Influence sur : agressivité, risques de fautes, endurance


---

3️⃣ Algorithmes et Modèles Utilisés

📌 Modèle d’Intensité Dynamique


➡️ Ajuste le rythme du match selon les buts, fatigue, contexte.

📌 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 de Régression Logistique


➡️ Prédit les buts, passes et événements clés selon les stats historiques.

📌 Modèle Bayésien
➡️ Ajuste les résultats du match en fonction des performances en direct.

---

4️⃣ Déroulement du Processus Automatique

1️⃣ Collage & Traitement des Données

📌 L’utilisateur colle les statistiques des équipes et joueurs.


📌 Le système détecte, trie et classe les informations.

2️⃣ Organisation & Sélection des Joueurs

📌 Extraction des 11 titulaires + remplaçants.


📌 Assignation des rôles (FW, MF, DF, GK).

3️⃣ Simulation Dynamique du Match

📌 Déroulement en 90 minutes virtuelles.


📌 Gestion des événements (buts, passes, fautes, blessures, cartons, remplacements).

4️⃣ Calcul en Temps Réel des Actions & Probabilités

📌 xG/xA ajustés en fonction des situations.


📌 Probabilités de tirs réussis ou ratés en fonction du contexte.

5️⃣ Affichage en Direct & Résumé Final

📌 Live texte avec animations et commentaires réalistes.


📌 Affichage des stats finales (xG, possession, tirs, passes, etc.).

---

5️⃣ Interfaces & Technologies Utilisées

✅ Interface Interactive
Sélection des équipes et joueurs via une interface fluide et ergonomique.

Animations sur le terrain pour voir les actions en direct.

Live texte détaillant chaque phase du jeu.

✅ Technologies et Frameworks

JavaScript : Logique et simulation du match.

HTML5 Canvas : Affichage des actions et déplacements des joueurs.

CSS & WebGL : Animations et graphismes en direct.

✅ Résumé Global – Simulation de Match avec Intégration Complète des Données

L'objectif est de développer un simulateur ultra-réaliste où l’utilisateur colle


les données brutes des équipes et joueurs. Le système analyse automatiquement,
structure les informations et utilise des modèles avancés pour prédire et simuler
le match en temps réel.

---

1️⃣ Fonctionnalités Globales et Automatisation

✅ Collage Automatique des Données

L’utilisateur colle les statistiques complètes (équipes, joueurs, cotes,


historique).

L’application analyse et extrait les valeurs clés automatiquement.

Détection des équipes, joueurs, positions et rôles.

✅ Gestion des Équipes et Joueurs

Sélection des titulaires et remplaçants via interface dynamique.

Détection automatique des postes (FW, MF, DF, GK).

Intégration des statistiques spécifiques pour chaque joueur.

✅ Simulation du Match en Temps Réel

Évolution dynamique du score, des événements et des performances.

Modélisation du comportement tactique et physique des équipes.

Gestion des événements (buts, passes, fautes, blessures, cartons, remplacements).

Animations interactives et commentaires en direct (live texte).


---

2️⃣ Catégories de Données & Variables Exploitées

📌 Équipes & Contexte du Match

TeamName, Forme, H2H, Odds, Home/Away Advantage

Utilisation : Calcul des probabilités de victoire, influence sur le style de jeu.

📌 Performances Offensives

Goals, Shots, xG, SoT%, Key Passes, Dribbles

Utilisation : Nombre d’occasions, efficacité devant le but.

📌 Performances Défensives

Tackles, Interceptions, Clearances, Blocks, xGA

Utilisation : Solidité défensive, capacité à limiter les occasions adverses.

📌 Milieux & Construction du Jeu

Passes, Possession%, xA, Pressing, Duels gagnés

Utilisation : Contrôle du jeu, création d’occasions.

📌 Gardiens & Performance dans les Buts

Saves, PSxG, GA, Crosses Claimed, Sweeper Actions

Utilisation : Capacité à stopper les tirs, influence sur le match.

📌 Facteurs Externes & Psychologiques

Momentum, Fatigue, Forme récente, Weather, Referee Strictness

Utilisation : Influence sur agressivité, risques de fautes, endurance.

---

3️⃣ Algorithmes et Modèles Utilisés

📌 Modèle d’Intensité Dynamique


➡️ Ajuste le rythme du match selon les événements (buts, fatigue, cartons).

📌 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 de Régression Logistique


➡️ Prédit buts, passes et événements clés en fonction des statistiques historiques.

📌 Modèle Bayésien
➡️ Ajuste les résultats du match en fonction des performances en direct.

---

4️⃣ Déroulement du Processus Automatique

1️⃣ Collage & Traitement des Données

📌 L’utilisateur colle les statistiques des équipes et joueurs.


📌 Le système détecte, trie et classe les informations.

2️⃣ Organisation & Sélection des Joueurs

📌 Extraction des 11 titulaires + remplaçants.


📌 Assignation des postes (FW, MF, DF, GK).
📌 Calcul des notes de performance individuelle.

3️⃣ Simulation Dynamique du Match

📌 Déroulement en 90 minutes virtuelles.


📌 Gestion des événements (buts, passes, fautes, blessures, cartons, remplacements).
📌 Modélisation des tactiques et stratégies.

4️⃣ Calcul en Temps Réel des Actions & Probabilités

📌 xG/xA ajustés en fonction des situations de jeu.


📌 Probabilités de réussite des tirs, passes et dribbles.
📌 Impact de la fatigue et de la pression psychologique.

5️⃣ Affichage en Direct & Résumé Final

📌 Live texte avec animations et commentaires réalistes.


📌 Affichage des statistiques finales (xG, possession, tirs, passes, etc.).

---

5️⃣ Interfaces & Technologies Utilisées

✅ Interface Interactive

Sélection dynamique des équipes et joueurs.

Animations sur le terrain pour voir les actions en direct.

Live texte détaillant chaque phase du jeu.

✅ Technologies et Frameworks
JavaScript : Logique et simulation du match.

HTML5 Canvas : Affichage des actions et déplacements des joueurs.

CSS & WebGL : Animations et graphismes en direct.

📌 Structuration de l'importation des données

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).

---

1️⃣ Détection et Structuration des Données Collées

📌 Objectif : L’utilisateur colle les données et l’application les détecte


automatiquement.

📌 Méthode utilisée :

Reconnaissance des catégories (Squad Shooting, Passing, Defensive Actions, etc.).

Extraction automatique des valeurs numériques.

Tri des données par équipes & joueurs.

📌 Étapes :

1. Analyse du texte collé pour détecter la catégorie et l’équipe associée.

2. Extraction des données chiffrées (ex: Gls, xG, Cmp%, etc.).

3. Identification des joueurs et de leurs positions (FW, MF, DF, GK).

4. Stockage des données dans des objets structurés.

📌 Exemple de Structure des Données après Import :

{
"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
}
}
}
}
}

---

2️⃣ Détection des Joueurs & Positions Automatique

📌 Objectif : Identifier automatiquement les titulaires et remplaçants + leurs


statistiques.

📌 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 de séparation des joueurs :


Si un joueur a +60 minutes → Titulaire
Si un joueur a -30 minutes → Remplaçant

Exemple :

{
"match": {
"team1": "Angers",
"team2": "Auxerre",
"lineups": {
"Angers": {
"starters": ["Himad Abdelli", "Joueur X", "Joueur Y"],
"substitutes": ["Joueur Z", "Joueur W"]
}
}
}
}

---

3️⃣ Association des Données aux Équipes et Joueurs

📌 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.

📌 Exemple d’attribution automatique des stats :

{
"match": {
"Angers": {
"players": {
"Himad Abdelli": {
"pos": "MF, FW",
"Gls": 6,
"Sh": 24,
"SoT": 6,
"xG": 5.6
}
}
}
}
}

---

4️⃣ Préparation des Données pour la Simulation

📌 Objectif : Convertir les données importées en valeurs exploitables par


l’algorithme de simulation.

📌 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
}
}
}

✅ Résumé Global – Simulation de Match avec Intégration Complète des Données

L'objectif est de développer un simulateur ultra-réaliste où l’utilisateur colle


les données brutes des équipes et joueurs. Le système analyse automatiquement,
structure les informations et utilise des modèles avancés pour prédire et simuler
le match en temps réel.

---

1️⃣ Fonctionnalités Globales et Automatisation


✅ Collage Automatique des Données

L’utilisateur colle les statistiques complètes (équipes, joueurs, cotes,


historique).

L’application analyse et extrait les valeurs clés automatiquement.

Détection des équipes, joueurs, positions et rôles.

✅ Gestion des Équipes et Joueurs

Sélection des titulaires et remplaçants via interface dynamique.

Détection automatique des postes (FW, MF, DF, GK).

Intégration des statistiques spécifiques pour chaque joueur.

✅ Simulation du Match en Temps Réel

Évolution dynamique du score, des événements et des performances.

Modélisation du comportement tactique et physique des équipes.

Gestion des événements (buts, passes, fautes, blessures, cartons, remplacements).

Animations interactives et commentaires en direct (live texte).

---

2️⃣ Catégories de Données & Variables Exploitées

📌 Équipes & Contexte du Match

TeamName, Forme, H2H, Odds, Home/Away Advantage

Utilisation : Calcul des probabilités de victoire, influence sur le style de jeu.

📌 Performances Offensives

Goals, Shots, xG, SoT%, Key Passes, Dribbles

Utilisation : Nombre d’occasions, efficacité devant le but.

📌 Performances Défensives

Tackles, Interceptions, Clearances, Blocks, xGA

Utilisation : Solidité défensive, capacité à limiter les occasions adverses.

📌 Milieux & Construction du Jeu

Passes, Possession%, xA, Pressing, Duels gagnés


Utilisation : Contrôle du jeu, création d’occasions.

📌 Gardiens & Performance dans les Buts

Saves, PSxG, GA, Crosses Claimed, Sweeper Actions

Utilisation : Capacité à stopper les tirs, influence sur le match.

📌 Facteurs Externes & Psychologiques

Momentum, Fatigue, Forme récente, Weather, Referee Strictness

Utilisation : Influence sur agressivité, risques de fautes, endurance.

---

3️⃣ Algorithmes et Modèles Utilisés

📌 Modèle d’Intensité Dynamique


➡️ Ajuste le rythme du match selon les événements (buts, fatigue, cartons).

📌 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 de Régression Logistique


➡️ Prédit buts, passes et événements clés en fonction des statistiques historiques.

📌 Modèle Bayésien
➡️ Ajuste les résultats du match en fonction des performances en direct.

---

4️⃣ Déroulement du Processus Automatique

1️⃣ Collage & Traitement des Données

📌 L’utilisateur colle les statistiques des équipes et joueurs.


📌 Le système détecte, trie et classe les informations.

2️⃣ Organisation & Sélection des Joueurs

📌 Extraction des 11 titulaires + remplaçants.


📌 Assignation des postes (FW, MF, DF, GK).
📌 Calcul des notes de performance individuelle.

3️⃣ Simulation Dynamique du Match


📌 Déroulement en 90 minutes virtuelles.
📌 Gestion des événements (buts, passes, fautes, blessures, cartons, remplacements).
📌 Modélisation des tactiques et stratégies.

4️⃣ Calcul en Temps Réel des Actions & Probabilités

📌 xG/xA ajustés en fonction des situations de jeu.


📌 Probabilités de réussite des tirs, passes et dribbles.
📌 Impact de la fatigue et de la pression psychologique.

5️⃣ Affichage en Direct & Résumé Final

📌 Live texte avec animations et commentaires réalistes.


📌 Affichage des statistiques finales (xG, possession, tirs, passes, etc.).

---

5️⃣ Interfaces & Technologies Utilisées

✅ Interface Interactive

Sélection dynamique des équipes et joueurs.

Animations sur le terrain pour voir les actions en direct.

Live texte détaillant chaque phase du jeu.

✅ Technologies et Frameworks

JavaScript : Logique et simulation du match.

HTML5 Canvas : Affichage des actions et déplacements des joueurs.

CSS & WebGL : Animations et graphismes en direct.

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

Squad Shooting 2024-2025 Ligue 1

Glossary

Toggle Per90 Stats

Squad Stats Opponent Stats Standard Expected Squad

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

donc avant de commencer à poser on va faire un résumé performant et rapide

You might also like