# ########################################################################### # Projet : Chifoumi # Programmeurs : Renan, Samuel et Joseph # Mai 2026 # nsi.gecif.net # ########################################################################### # ========================= # IMPORTATIONS # ========================= from turtle import* from random import* from time import* import csv import os from collections import Counter from collections import defaultdict # ========================= # CRÉATION DU FICHIER SI INEXISTANT # ========================= # On crée un fichier CSV pour stocker l'historique des parties # afin de permettre à l'IA d'apprendre des coups précédents. if not os.path.exists("historique.csv"): with open("historique.csv", "w", newline="", encoding="utf-8") as fichier: writer = csv.writer(fichier) # en-tête du fichier writer.writerow(["coup"]) # ========================= # VARIABLES DE STYLE (TURTLE) # ========================= font_color = "white" ordi_color = "red" human_color = "blue" pn_color = "black" choice_color = "purple" pencolor(pn_color) bgcolor(font_color) # ============================================================================== # E t a b l i s s e m e n t d e s C l a s s : # ============================================================================== # ========================= # CLASSE JOUEUR # ========================= class Joueurs: """ Représente un joueur (humain ou IA) """ def __init__(self): self.main = None # coup actuel du joueur def choix_possible(self): # Les 3 choix possibles du jeu return ['pierre','papier','ciseaux'] def choisir(self,ch): # transforme un indice (0,1,2) en coup réel self.main = self.choix_possible()[ch] return self.main # ========================= # CLASSE DU JEU CHIFOUMI # ========================= class Chifoumi: """ Gère : - les joueurs - les scores - l'affichage des coups """ def __init__(self,j1,j2,s1,s2): self.premier = j1 self.deuxieme = j2 self.scorepres = s1 self.scoredeusio = s2 def jouer(self): # système du jeu selon les règles du chifoumi """ Compare les deux coups et met à jour le score """ global nb_total t = [self.premier.main,self.deuxieme.main] self.show() # Égalité if t[0] == t[1]: return # victoire joueur 1 elif t == ['pierre','ciseaux'] or t == ['papier','pierre'] or t == ['ciseaux','papier']: self.scorepres += 1 self.score_1() # victoire joueur 2 elif t == ['pierre','papier'] or t == ['papier','ciseaux'] or t == ['ciseaux','pierre']: self.scoredeusio += 1 self.score_2() def scores(self): """ retourne les scores actuels """ return (self.scorepres,self.scoredeusio) # ========================= # AFFICHAGE SCORE JOUEUR 1 # ========================= def score_1(self): up() goto(-340,-250) down() pencolor(font_color) fillcolor(font_color) begin_fill() # rectangle de "reset" visuel goto(-140,-250) goto(-140,-140) goto(-340,-140) goto(-340,-250) end_fill() up() goto(-300,-250) down() pencolor(human_color) write(self.scorepres,font=('Arial Unicode MS', 80)) # ========================= # AFFICHAGE SCORE JOUEUR 2 # ========================= def score_2(self): up() goto(185,-250) down() pencolor(font_color) fillcolor(font_color) begin_fill() goto(385,-250) goto(385,-140) goto(185,-140) goto(185,-250) end_fill() up() goto(225,-250) down() pencolor(ordi_color) write(self.scoredeusio,font=('Arial Unicode MS', 80)) def show(self): """ Affiche les coups des deux joueurs avec des emojis """ pencolor(choice_color) # JOUEUR 1 up() goto(-150, -100) down() if self.premier.main == 'pierre': write('\u270a', font=('Arial Unicode MS', 70)) elif self.premier.main == 'papier': write('\u270b', font=('Arial Unicode MS', 70)) elif self.premier.main == 'ciseaux': write('\u270c', font=('Arial Unicode MS', 70)) # JOUEUR 2 up() goto(50, -100) down() if self.deuxieme.main == 'pierre': write('\u270a', font=('Arial Unicode MS', 70)) elif self.deuxieme.main == 'papier': write('\u270b', font=('Arial Unicode MS', 70)) elif self.deuxieme.main == 'ciseaux': write('\u270c', font=('Arial Unicode MS', 70)) sleep(2) # Nettoyage visuel (efface les coups affichés) pencolor(font_color) up() goto(-150, -100) down() fillcolor(font_color) begin_fill() goto(-20, -100) goto(-20, 3) goto(-150, 3) goto(-150, -100) end_fill() up() goto(50, -100) down() begin_fill() goto(170, -100) goto(170, 3) goto(50, 3) goto(50, -100) end_fill() # ============================================================================== # D é f i n i t i o n d e s f o n c t i o n s : # ============================================================================== def choisir_manches(): """ Permet au joueur de choisir un nombre de manches. Si le joueur entre 0 : - le mode illimité est activé. La fonction remet aussi les scores à zéro lorsqu'une nouvelle partie commence. """ global nb_manches global nb_total global enmanche enmanche = True # AJOUT : reset scores et nombres de manches quand on change de mode nb_total = 0 partie.scorepres = 0 partie.scoredeusio = 0 partie.score_1() partie.score_2() texte = input("Nombre de manches (0 = illimité) : ") assert texte.isnumeric()==True,"METTEZ UN NOMBRE POSITIF S'IL VOUS PLAIT !!!" nb_manches = int(texte) if nb_manches == 0: print("Mode illimité activé") elif nb_manches > 0: up() goto(-450,20) write("le nombre de manche est défini a %d"%nb_manches,font=('Arial Unicode MS', 10)) def verifier_fin(): """ Vérifie si la partie est terminée. En mode limité : - la partie se termine lorsque le nombre total de manches jouées atteint le nombre demandé. """ global nb_total, nb_manches # mode illimité if nb_manches == 0: return # fin de partie if nb_total >= nb_manches: end() # ------------------------------------------------------------------------------ # Fonctions d'intéractivitées: # ------------------------------------------------------------------------------ # ========================= # IA PAR RECHERCHE DE PATTERN # ========================= def prediction_pattern(historique): """ Analyse l'entièreté de l'historique afin de retrouver des tactiques déjà  utilisées par le joueur. L'IA cherche une suite de coups déjà  jouée auparavant puis regarde quel coup suivait généralement cette suite. """ # pas assez de données if len(historique) < 5: return None # taille maximale du pattern recherché taille_max = min(10, len(historique)//2) # on teste les plus grands patterns d'abord for taille in range(taille_max, 1, -1): # derniers coups du joueur pattern = historique[-taille:] suivants = [] # recherche du pattern dans tout l'historique for i in range(len(historique) - taille): ancien_pattern = historique[i:i+taille] if ancien_pattern == pattern: # on regarde le coup joué après if i + taille < len(historique): suivants.append(historique[i + taille]) # si on a trouvé des suites similaires if suivants: # retourne le coup le plus fréquent return max(set(suivants), key=suivants.count) else: return None return None def choix_ia_pattern(): """ IA utilisant la reconnaissance de tactiques et de patterns. """ historique = lire_historique() # récupère uniquement les coups du joueur coups_joueur = [h[0] for h in historique] prediction = prediction_pattern(coups_joueur) # si aucune prédiction if prediction is None: choix = randrange(0,3) # joue le coup gagnant elif prediction == "pierre": choix = 1 elif prediction == "papier": choix = 2 elif prediction == "ciseaux": choix = 0 partie.deuxieme.choisir(choix) # ========================= # LECTURE / ÉCRITURE HISTORIQUE # ========================= def enregistrer(joueur, ia): """ Sauvegarde un coup dans le CSV """ with open("historique.csv", "a", newline="", encoding="utf-8") as fichier: writer = csv.writer(fichier) writer.writerow([joueur, ia]) def lire_historique(): """ Lit le fichier CSV et reconstruit l'historique """ historique = [] with open("historique.csv", "r", encoding="utf-8") as fichier: reader = csv.reader(fichier) next(reader) for ligne in reader: if len(ligne) >= 2: joueur = ligne[0] ia = ligne[1] historique.append((joueur, ia)) return historique def prochain_coup(main): """ Retourne le coup qui bat celui donné. Exemple : - pierre -> papier - papier -> ciseaux - ciseaux -> pierre """ if main == 'pierre': return 'papier' elif main == 'papier': return 'ciseaux' elif main == 'ciseaux': return 'pierre' # ========================= # CLIQUE UTILISATEUR # ========================= def cliquer_gauche(x,y): """ Permet la possibilté de choisir un choix directement sur Turtle en ciquant sur un des boutons déssinés. """ # on a cliqué gauche sur le point de coordonnées (x,y) # IA joue avant le joueur choix_ia_pattern() global nb_total,nb_pierre,nb_papier,nb_ciseaux,l_mains global clic_autorise # bloque les clics pendant l'animation if clic_autorise == False: return clic_autorise = False # JOUEUR # analyse clic if -375 partie.scoredeusio: goto(-180,50) write("Le joueur humain gagne !!!",font=('Arial Unicode MS',25)) elif partie.scorepres < partie.scoredeusio: goto(-145,50) write("L'ordinateur gagne !!!",font=('Arial Unicode MS',25)) elif nb_total == 0: goto(-200,50) write("Aucun des joueurs n'ont joué",font=('Arial Unicode MS',25)) else: goto(-50,50) write("Égalité !!!",font=('Arial Unicode MS',25)) elif nb_total == 0: goto(-200,50) write("Aucun des joueurs n'ont joué",font=('Arial Unicode MS',25)) sleep(5) bye() def pierre(): """ Dessine le bouton 'pierre' avec son emoji correspondant. """ up() goto(-300,100) down() circle(75) up() goto(-345,115) down() write('\u270a', font=('Arial Unicode MS', 70)) def papier(): """ Dessine le bouton 'papier' avec son emoji correspondant. """ up() goto(0,100) down() circle(75) up() goto(-45,115) down() write('\u270b', font=('Arial Unicode MS', 70)) def ciseaux(): """ Dessine le bouton 'ciseaux' avec son emoji correspondant. """ up() goto(300,100) down() circle(75) up() goto(275,115) down() write('\u270c', font=('Arial Unicode MS', 80)) def grille_score(): """ Dessine la grille séparant les zones d'affichage des scores. """ up() goto(-1000,20) down() goto(1000,20) up() goto(0,20) down() goto(0,-1000) up() goto(-350,-20) write('Score Joueur humain', font=50) up() goto(200,-20) write('Score Joueur ordinateur', font=50) def commande(): """ Affiche les textes principaux de l'interface du jeu : - le titre, - les commandes clavier, - les indications pour le joueur. """ up() goto(-325,250) write("CHIFOUMI",font=('Arial Unicode MS',100)) goto(-90,20) write("press echap pour finir la partie",font=('Arial Unicode MS',10)) goto(-120,40) write("press m pour définir le nombre de manche",font=('Arial Unicode MS',10)) goto(-90,60) write("press a pour activer le mode auto",font=('Arial Unicode MS',10)) # ========================= # MODE AUTO JOUEUR ALÉATOIRE # ========================= def auto(): """ Active / désactive le mode automatique. Quand il est actif : - le joueur humain joue aléatoirement - contre l'ordinateur """ global mode_auto mode_auto = not mode_auto up() goto(-200, -320) down() pencolor(font_color) fillcolor(font_color) begin_fill() goto(250, -320) goto(250, -280) goto(-200, -280) goto(-200, -320) end_fill() up() goto(-200, -320) down() pencolor("green" if mode_auto else "red") if mode_auto: write("MODE AUTO ACTIVÉ", font=('Arial Unicode MS', 20)) jouer_auto() else: write("MODE AUTO DESACTIVÉ", font=('Arial Unicode MS', 20)) def jouer_auto(): """ Fait jouer automatiquement un joueur aléatoire contre l'IA. """ global mode_auto global nb_total global nb_pierre global nb_papier global nb_ciseaux # stop si désactivé if mode_auto == False: return # IA ordinateur choix_ia_pattern() # joueur humain aléatoire choix_humain = randrange(0, 3) partie.premier.choisir(choix_humain) # statistiques if choix_humain == 0: nb_pierre += 1 elif choix_humain == 1: nb_papier += 1 else: nb_ciseaux += 1 # joue la manche partie.jouer() nb_total += 1 # sauvegarde historique enregistrer(partie.premier.main, partie.deuxieme.main) verifier_fin() # relance automatiquement ontimer(jouer_auto, 1000) # ============================================================================== # P R O G R A M M E P R I N C I P A L : # ============================================================================== up() mode_auto = False # Vérifie si le jeueur est en mode auto enmanche = False # Vérifie si un jeu en manche est lancé nb_manches = 0 # Nombre de manches ( 0 : illimité ) nb_total = 0 nb_pierre = 0 nb_papier = 0 nb_ciseaux = 0 clic_autorise = True joueur1 = Joueurs() joueur2 = Joueurs() partie = Chifoumi(joueur1,joueur2,0,0) # Rendu visuel title("Chifoumi") screen=Screen() screen.getcanvas().winfo_toplevel().iconbitmap("icon_chifoumi.ico") setup() hideturtle() speed(0) tracer(0) commande() # Affichage des boutons pierre() papier() ciseaux() # Affichage de la grille des scores grille_score() #affiche du nombre de point partie.score_1() partie.score_2() # fonctions à lancer si on clique avec un bouton de la souris : onscreenclick(cliquer_gauche) # fonction à lancer si on appuie sur une touche : onkeypress(choisir_manches,"m") onkeypress(end,"Escape") onkeypress(auto,"a") # lance le gestionniare d'évènement : listen() # lance la boucle principale : done() # ============================================================================== # F i n d u p r o g r a m m e # ==============================================================================