using CoreLibrary.Persistance;
using CoreLibrary.Core;
using CoreLibrary.Evenements;
using CoreLibrary.Joueurs;
using CoreLibrary.Regles;
using System.Runtime.Serialization;
namespace CoreLibrary
{
///
/// Classe représentant une partie.
///
[DataContract]
[KnownType(typeof(ReglesClassiques))]
[KnownType(typeof(ReglesDifficiles))]
public class Partie : IEstPersistant
{
///
/// Evénement déclenché lorsqu'il faut ajouter un joueur à la partie.
///
public event EventHandler? PartieDemanderJoueur;
///
/// Evénement déclenché lors du commencement de la partie.
///
public event EventHandler? PartieDebutPartie;
///
/// Evénement déclenché lorsqu'il faut demander à un joueur de jouer.
///
public event EventHandler? PartieDemanderJoueurJouer;
///
/// Evénement déclenché lorsqu'un nouveau tour commence.
///
public event EventHandler? PartieNouveauTour;
///
/// Evénement déclenché lorsqu'on passe la main au joueur suivant.
///
public event EventHandler? PartiePasserLaMain;
///
/// Evénement déclenché lorsque la partie est terminée.
///
public event EventHandler? PartiePartieTerminee;
///
/// Méthode pour déclencher l'événement qui demande le joueur.
///
/// .
private void QuandPartieDemanderJoueur(Joueur joueurDemande) => PartieDemanderJoueur?.Invoke(this, new PartieDemanderJoueurEventArgs(joueurs.Count + 1, joueurDemande));
///
/// Méthode pour déclencher l'événement de début de partie.
///
private void QuandPartieDebutPartie() => PartieDebutPartie?.Invoke(this, new PartieDebutPartieEventArgs());
///
/// Méthode pour déclencher l'événement demandant à un joueur de jouer.
///
/// Le code à utiliser pour jouer.
private void QuandPartieDemanderJoueurJouer(Code code) => PartieDemanderJoueurJouer?.Invoke(this, new PartieDemanderJoueurJouerEventArgs(Tour, Joueurs.ElementAt(courant), plateaux.ElementAt(courant), code, joueurs[Joueurs.ElementAt(courant)]));
///
/// Méthode pour déclencher l'événement d'un nouveau tour.
///
/// Le code utilisé pour le nouveau tour.
private void QuandPartieNouveauTour(Code code) => PartieNouveauTour?.Invoke(this, new PartieNouveauTourEventArgs(Tour, Joueurs.ElementAt(courant), plateaux.ElementAt(courant), code, joueurs[Joueurs.ElementAt(courant)]));
///
/// Méthode pour déclencher l'événement de passer la main au joueur suivant.
///
private void QuandPartiePasserLaMain() => PartiePasserLaMain?.Invoke(this, new PartiePasserLaMainEventArgs(Joueurs.ElementAt(courant)));
///
/// Méthode pour déclencher l'événement de fin de partie.
///
/// Liste des noms des joueurs gagnants.
/// Liste des noms des joueurs perdants.
private void QuandPartiePartieTerminee(IReadOnlyList gagnants, IReadOnlyList perdants) => PartiePartieTerminee?.Invoke(this, new PartiePartieTermineeEventArgs(gagnants, perdants));
///
/// Dictionnaire des joueurs, avec leur nom et le statut de leur joueur (humain ou robot).
///
[DataMember]
private readonly Dictionary joueurs = new Dictionary();
///
/// Liste des plateaux de jeu.
/// plateaux = new List();
///
/// Indice correspondant au joueur courant.
///
[DataMember]
private int courant = 0;
///
/// Liste des noms des joueurs.
///
public IEnumerable Joueurs => joueurs.Keys;
///
/// Liste des noms des robots.
///
public IEnumerable Robots => joueurs.Where(joueur => joueur.Value).Select(joueur => joueur.Key);
///
/// Indique si la partie est terminée.
///
[DataMember]
public bool Termine { get; private set; } = false;
///
/// Numéro du tour actuel.
///
[DataMember]
public int Tour { get; private set; } = 0;
///
/// Règles utilisées dans la partie.
///
[DataMember]
public IRegles Regles { get; private init; }
///
/// Premier constructeur de la partie.
///
/// Les règles de la partie.
public Partie(IRegles regles)
{
Regles = regles;
}
///
/// Deuxième constructeur de la partie.
///
/// La partie à reprendre en cours.
public Partie(Partie partie)
{
joueurs = partie.joueurs;
plateaux = partie.plateaux;
courant = partie.courant;
Tour = partie.Tour;
Regles = partie.Regles;
partie.PartieDemanderJoueur = null;
partie.PartieDebutPartie = null;
partie.PartieDemanderJoueurJouer = null;
partie.PartieNouveauTour = null;
partie.PartiePasserLaMain = null;
partie.PartiePartieTerminee = null;
foreach (string joueur in Joueurs)
(joueurs[joueur] ? new Joueur(joueur) : new Robot(joueur)).JouerPartie(this);
}
///
/// Lance le déroulement de la partie.
///
public void Jouer()
{
if (joueurs.Count != Regles.NbJoueurs)
DemanderJoueur();
else
DebutPartie();
}
///
/// Demande un joueur.
///
private void DemanderJoueur()
{
Joueur joueurDemande = new Joueur();
joueurDemande.JoueurSeConnecter += JoueurConnecte;
QuandPartieDemanderJoueur(joueurDemande);
}
///
/// Connecte les joueurs à la partie.
///
/// La classe qui appelle l'événement, ici Joueur.
/// L'instance de l'événement JoueurSeConnecterEventArgs créée par Joueur.
private void JoueurConnecte(object? sender, JoueurSeConnecterEventArgs e)
{
joueurs.Add(e.Joueur.Nom, e.Joueur.GetType().Equals(typeof(Joueur)));
plateaux.Add(new Plateau(Regles.TailleCode, Regles.NbTour));
e.Joueur.JouerPartie(this);
if (joueurs.Count < Regles.NbJoueurs)
{
DemanderJoueur();
}
else
{
DebutPartie();
}
}
///
/// Lance le début de la partie.
///
private void DebutPartie()
{
if (Tour == 0)
++Tour;
foreach (Plateau plateau in plateaux)
{
plateau.PlateauAjouterCode += PlateauAjouterCode;
}
QuandPartieDebutPartie();
NouveauTour();
}
///
/// Passe au tour suivant.
///
private void NouveauTour()
{
Code code = new Code(Regles.TailleCode);
QuandPartieDemanderJoueurJouer(code);
QuandPartieNouveauTour(code);
}
///
/// Ajoute un code au plateau.
///
/// La classe qui appelle l'événement, ici Plateau.
/// L'instance de l'événement PlateauAjouterCodeEventArgs créée par Plateau.
private void PlateauAjouterCode(object? sender, PlateauAjouterCodeEventArgs e)
{
QuandPartiePasserLaMain();
if (courant + 1 == joueurs.Count && (e.Plateau.Complet || plateaux.Any(plateau => plateau.Victoire)))
{
PartieTerminee();
}
else
{
if (courant + 1 == joueurs.Count)
{
++Tour;
courant = 0;
}
else
++courant;
NouveauTour();
}
}
///
/// La partie est terminée.
///
private void PartieTerminee()
{
Termine = true;
List gagnants = new List();
List perdants = new List();
for (int i = 0; i < joueurs.Count; ++i)
{
if (plateaux.ElementAt(i).Victoire)
gagnants.Add(Joueurs.ElementAt(i));
else
perdants.Add(Joueurs.ElementAt(i));
}
QuandPartiePartieTerminee(gagnants, perdants);
}
}
}