SAE 1.01 : 421
Contexte
Premier projet de développement en équipe à l'IUT, réalisé en 2 semaines. L'objectif : programmer le jeu de dés 421 en Java et créer une intelligence artificielle capable de rivaliser avec un joueur humain.
Le jeu 421 : Deux joueurs s'affrontent en lançant 3 dés pour former des combinaisons (figures) qui valent des points. La meilleure combinaison possible est 4-2-1, d'où le nom du jeu. Les joueurs misent des jetons selon la qualité de leurs résultats.
Le défi technique : Au-delà de l'implémentation des règles, développer un algorithme de décision pour le bot : quand relancer les dés ? Quelles combinaisons conserver ? Comment optimiser les chances de victoire ?
Objectifs
- Implémenter les règles complètes du jeu 421 en Java
- Développer une interface utilisateur pour jouer contre le bot
- Créer un système de décision intelligent pour le bot adversaire
- Gérer les paris et les combinaisons de dés
Itérations & variantes
- Version procédurale : règles et scoring codés en logique classique pour valider la mécanique de base.
- Version POO : refonte avec dés, lancer, manche et parties modélisés en classes pour clarifier les responsabilités.
- Version IA : bot amélioré avec heuristique de décision (garde/relance) pour maximiser les chances de figures fortes.
Technologies
À propos
J'ai pu, à travers ce projet, me familiariser avec les compétences suivantes :
- Le développement efficace : structuration du code et optimisation des performances
- Le développement d'applications : création d'une application fonctionnelle complète
- La programmation technique : POO, algorithmes de jeu, gestion des états
- Le suivi de projet et code : versioning avec GitLab, travail d'équipe, intégration continue
Réalisations techniques
- Logique algorithmique : détection des figures, calcul des scores
- Intelligence artificielle : bot prenant des décisions intelligentes basées sur l'analyse des situations de jeu
- Interface utilisateur : développement d'une interface permettant de jouer contre le bot
Extrait de code : classe Joueur
Portion de l'implémentation Java gérant un joueur du 421 : état, nombre de jetons, coups joués et interactions avec le leader ou le bot.
/**
* Définition
* La classe Joueur permet de créer et manipuler des joueurs.
*
* Attributs d'une instance :
* - Le numéro du joueur (entier)
* - Le nombre de jetons du joueur (entier)
* - Si le joueur est un humain ou non (un ordinateur) (booléen)
* - Un triplet qui représente le résultat actuel du joueur (instance de Triplet)
*
* Comportement d'une instance :
* - On peut créer un nouveau joueur (humain ou ordinateur) à partir d'un numéro, d'un nombre de jetons.
* - On peut connaître le numéro d'un joueur.
* - On peut savoir si le joueur possède encore au moins un jeton.
* - On peut demander au joueur de donner des jetons à un autre joueur.
* - On peut jouer un coup en relançant des dés.
* - On peut décider, lorsqu'on est leader, si on souhaite jouer un nouveau coup/
* - On peut faire choisir un jet au joueur.
* - On peut effectuer le tour d'un joueur leader.
* - On peut effectuer le tour d'un joueur non leader.
*/
public class Joueur {
private final int num;
private int nbJetonsInit;
private final boolean jHum;
private final Triplet resultat;
private String nom;
/**
* Pré-requis : 1 <= num <= 2 et nbJetonsInit > 0
* Action : construit un nouveau joueur à partir de son numéro, son nombre
* de jetons et s'il est humain ou non. Le résultat actuel du joueur (son
* triplet) est initialisé par le constructeur : il est vide au début
*/
public Joueur(int num, int nbJetonsInit, boolean jHum, String nom) {
this.num = num;
this.nbJetonsInit = nbJetonsInit;
this.jHum = jHum;
this.resultat = new Triplet();
this.nom = this.nom;
}
public String getNom() {
return this.nom;
}
public void setNom(String nom) {
this.nom = nom;
}
//Méthodes d'instance
/**
* Résultat : le numéro du joueur
*/
public int getNumero() {
return this.num;
}
/**
* Résultat : une représentation du joueur "this" en une chaîne de
* caractères. Exemple : si j est le joueur ayant le numéro 5 et 1 jeton
* j.toString() -> la chaîne "Joueur 5 : 1 jeton." si j2 est le joueur ayant
* le numéro 1 et 4 jetons j2.toString() -> la chaîne "Joueur 1 : 4 jetons."
*/
@Override
public String toString() {
return this.nom + " : " + this.nbJetonsInit + " jeton" + (this.nbJetonsInit > 1 ? "s" : "") + ".";
}
/**
* Résultat : true si le joueur possède au moins un jeton, false sinon.
*/
public boolean aAuMoinsUnJeton() {
return (this.nbJetonsInit > 0);
}
/**
* Action : this donne des jetons à autreJoueur sachant que le nombre de
* points obtenue par this pour sa figure est nbPoints.
*/
public void donneJetons(int nbPoints, Joueur autreJoueur) {
this.nbJetonsInit -= nbPoints;
autreJoueur.nbJetonsInit += nbPoints;
}
/**
* Action : Relance les dés indiqués par le Jet jet passé en paramètre sur
* le résultat actuel du joueur puis affiche le nouveau résultat obtenu. Le
* numéro de coup est seulement utilisé pour l'affichage.
*/
public void jouerCoup(int numeroDeCoup, Jet jet) {
this.resultat.lancerDes(jet);
Ut.afficherSL("Coup " + numeroDeCoup + " : " + this.resultat.toString());
}
/**
* Action : demande à this, qui est le joueur leader, s'il veut jouer un
* nouveau coup (avec re-saisie éventuelle de sa réponse jusqu'à ce qu'elle
* soit correcte) s'il est humain, sinon le choix est fait d'après la
* stratégie de la classe Strategie. Résultat : vrai si this veut jouer un
* nouveau coup et faux sinon.
*/
public boolean leaderVeutJouerNouveauCoup() {
return this.jHum ? JeuUt.premierChoix("Voulez-vous jouer un nouveau coup ? ", "O", "N") : IntelligenceArtificielle.leaderRejoue(this.resultat.getFigure());
}
/**
* Action : si this est humain, lui demande de choisir les dés à relancer
* (avec re-saisie éventuelle de sa réponse jusqu'à ce qu'elle soit
* correcte). Résultat : le jet choisi par this s'il est humain, un jet
* choisi d'après la stratégie de la classe Strategie sinon.
*/
public Jet jetChoisiParJoueur() {
return this.jHum ? Jet.jetChoisiParHumain() : IntelligenceArtificielle.meilleurCoup(this.resultat.getFigure());
}
/**
* Action : joue un tour du joueur leader. On annonce le début et la fin du
* tour du joueur avec son numéro. Pour chaque coup à partir du 2ème coup du
* tour du joueur, celui-ci commence par choisir les dés à relancer. Après
* chaque coup, on affiche le numéro du coup et le résultat obtenu. On
* rappelle que le joueur leader peut relacer des dés 1 ou 2 fois (3 coups
* maximum). Résultat : un ResultatLeader contenant la figure obtenue par
* this à l'issue de ce tour et le nombre de coups effectués.
*/
public ResultatLeader jouerTourLeader() {
Ut.afficherSL("Début du tour du joueur leader, " + this.nom);
int nbCoups = 1;
Jet jet = new Jet(true, true, true);
jouerCoup(nbCoups++, jet);
while (nbCoups < 3 && leaderVeutJouerNouveauCoup()) {
jet = jetChoisiParJoueur();
jouerCoup(nbCoups, jet);
nbCoups++;
}
Ut.afficherSL("Fin du tour du joueur leader, " + this.nom);
return new ResultatLeader(this.resultat.getFigure(), nbCoups);
}
/**
* Action : joue un tour du joueur non leader en connaisant les resultats du
* leader (et donc son nombre de coups). On annonce le début et la fin du
* tour du joueur avec son numéro. Pour chaque coup à partir du 2ème coup du
* tour du joueur, celui-ci commence par choisir les dés à relancer. Après
* chaque coup, on affiche le numéro du coup et le résultat obtenu. Résultat
* : la figure obtenue par this à l'issue de ce tour.
*/
public Figure jouerTourNonLeader(ResultatLeader resultatLeader) {
Ut.afficherSL("Début du tour du joueur non leader, " + this.nom);
int nbCoups = 1;
Jet jet = jetChoisiParJoueur();
while (nbCoups <= resultatLeader.getNbCoups()) {
jouerCoup(nbCoups, jet);
jet = jetChoisiParJoueur();
nbCoups++;
}
Ut.afficherSL("Fin du tour du joueur non leader, " + this.nom);
return this.resultat.getFigure();
}
}Galerie & Rendu
Galerie en cours de préparation