commit f6992c8cab2ff4424cf29fce8bbf342d79f5b811 Author: thuret Date: Wed Oct 23 11:58:29 2024 +0200 first commit diff --git a/TP01/JeuNim.pdf b/TP01/JeuNim.pdf new file mode 100644 index 0000000..b0b97f8 Binary files /dev/null and b/TP01/JeuNim.pdf differ diff --git a/TP01/Nim.ipynb b/TP01/Nim.ipynb new file mode 100644 index 0000000..ca01e40 --- /dev/null +++ b/TP01/Nim.ipynb @@ -0,0 +1,496 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Implémentation du jeu de Nim" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "etat_jeu = [3, 4, 5] # Trois tas contenant respectivement 3, 4 et 5 objets.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 1 " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[3, 4, 5]\n", + "[0, 0, 0]\n", + "[1, 0, 2]\n" + ] + } + ], + "source": [ + "# A.\n", + "def afficher_etat(etat_jeu) :\n", + " print(etat_jeu)\n", + "\n", + "\n", + "# B.\n", + "test1 = [3, 4, 5]\n", + "test2 = [0, 0, 0]\n", + "test3 = [1, 0, 2]\n", + "\n", + "afficher_etat(test1)\n", + "afficher_etat(test2)\n", + "afficher_etat(test3)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 2 : " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5)]\n", + "[]\n", + "[(0, 1), (2, 1), (2, 2)]\n" + ] + } + ], + "source": [ + "# A.\n", + "\n", + "def generer_mouvement(etat_jeu) : \n", + " mouvements = []\n", + " for i in range(len(etat_jeu)) : # On parcourt les tas\n", + " for j in range(etat_jeu[i]) : # On tire le nombre d'objets dans le tas\n", + " if etat_jeu[i] > 0 :\n", + " mouvements.append((i, j+1)) # On ajoute le mouvement à la liste\n", + " return mouvements\n", + "\n", + "# B.\n", + "print(generer_mouvement(test1))\n", + "print(generer_mouvement(test2))\n", + "print(generer_mouvement(test3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 3 : " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# A et B\n", + "def appliquer_mouvement(etat_jeu, tas_index, nombre_objets) :\n", + " if nombre_objets > etat_jeu[tas_index] :\n", + " print(\"Erreur : nombre d'objets à retirer supérieur au nombre d'objets dans le tas.\")\n", + " return etat_jeu\n", + " else : \n", + " etat_jeu[tas_index] -= nombre_objets\n", + " return etat_jeu\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 4 :" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\" étant donné que heuristique est égale à la somme des objets dans les tas, \\nc'est donc un calcul simple qui ne dépend pas de l'ordre des tas. \"" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# A.\n", + "'''\n", + "def heuristique(etat_jeu) :\n", + " return sum(etat_jeu)\n", + "\n", + "print(heuristique(test1))\n", + "print(heuristique(test2))\n", + "print(heuristique(test3))'''\n", + "\n", + "# B.\n", + "''' étant donné que heuristique est égale à la somme des objets dans les tas, \n", + "c'est donc un calcul simple qui ne dépend pas de l'ordre des tas. '''\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 5 :" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "# A.\n", + "def est_etat_final(etat_jeu) :\n", + " return sum(etat_jeu) == 0\n", + "\n", + "# B.\n", + "def cout_transition(etat_courant, etat_suivant):\n", + " return 1\n", + "\n", + "# Exemple d'utilisation\n", + "etat_courant = [3, 4, 5]\n", + "etat_suivant = appliquer_mouvement(etat_courant, 0, 1) # Devrait retourner [2, 4, 5]\n", + "print(cout_transition(etat_courant, etat_suivant)) # Devrait retourner 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 6 : " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[4, 8, 5], [4, 0, 5], [0, 0, 5], [0, 0, 0]]\n" + ] + } + ], + "source": [ + "import heapq\n", + "\n", + "class Noeud:\n", + " def __init__(self, etat, parent, cout, heuristique):\n", + " self.etat = etat\n", + " self.parent = parent\n", + " self.cout = cout\n", + " self.heuristique = heuristique\n", + "\n", + " def __lt__(self, other):\n", + " return self.cout + self.heuristique < other.cout + other.heuristique\n", + "\n", + " def __eq__(self, other):\n", + " return self.etat == other.etat\n", + "\n", + " def __hash__(self):\n", + " return hash(tuple(self.etat))\n", + "\n", + " def __str__(self):\n", + " return str(self.etat)\n", + " \n", + "def xor_etat(etat_jeu):\n", + " xor_total = 0\n", + " for nb_objets in etat_jeu:\n", + " xor_total ^= nb_objets\n", + " return xor_total\n", + "\n", + "def heuristique(etat_jeu):\n", + " xor_valeur = xor_etat(etat_jeu)\n", + " if xor_valeur == 0:\n", + " # Position désavantageuse (perdante)\n", + " return 100 + sum(etat_jeu) # Pénalité\n", + " else:\n", + " # Position avantageuse (gagnante)\n", + " return sum(etat_jeu) # Heuristique standard\n", + "\n", + "def est_etat_final(etat):\n", + " return all(pile == 0 for pile in etat)\n", + "\n", + "def generer_successeurs(etat):\n", + " successeurs = []\n", + " for i in range(len(etat)):\n", + " if etat[i] > 0:\n", + " for j in range(1, etat[i] + 1):\n", + " nouvel_etat = etat[:]\n", + " nouvel_etat[i] -= j\n", + " successeurs.append(nouvel_etat)\n", + " return successeurs\n", + "\n", + "def algorithme_a_star(etat_initial):\n", + " noeud_initial = Noeud(etat_initial, None, 0, heuristique(etat_initial))\n", + " frontiere = []\n", + " heapq.heappush(frontiere, noeud_initial)\n", + " visites = set()\n", + "\n", + " while frontiere:\n", + " noeud_courant = heapq.heappop(frontiere)\n", + "\n", + " if est_etat_final(noeud_courant.etat):\n", + " chemin = []\n", + " while noeud_courant:\n", + " chemin.append(noeud_courant.etat)\n", + " noeud_courant = noeud_courant.parent\n", + " return chemin[::-1]\n", + "\n", + " visites.add(tuple(noeud_courant.etat))\n", + "\n", + " for successeur in generer_successeurs(noeud_courant.etat):\n", + " if tuple(successeur) not in visites:\n", + " cout = noeud_courant.cout + cout_transition(noeud_courant.etat, successeur)\n", + " heur = heuristique(successeur)\n", + " noeud_successeur = Noeud(successeur, noeud_courant, cout, heur)\n", + " heapq.heappush(frontiere, noeud_successeur)\n", + "\n", + " return None\n", + "\n", + "# Exemple d'utilisation\n", + "etat_initial = [4, 8, 5]\n", + "chemin_optimal = algorithme_a_star(etat_initial)\n", + "print(chemin_optimal)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 7 :" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(voir cellule précédante)\n", + "\n", + "A. \n", + "- Initialisation:\n", + " - Crée un nœud initial avec l'état initial du jeu.\n", + " - Utilise une file de priorité (heap) pour gérer les nœuds à explorer.\n", + " - Utilise un ensemble (set) pour stocker les états déjà visités.\n", + "- Boucle principale:\n", + " - Tant que la file de priorité n'est pas vide, extraire le nœud avec le coût total (coût + heuristique) le plus bas.\n", + " - Si l'état du nœud courant est un état final, reconstruire et retourner le chemin depuis l'état initial jusqu'à l'état final.\n", + " - Ajouter l'état courant à l'ensemble des états visités.\n", + " - Générer les états successeurs et les ajouter à la file de priorité s'ils n'ont pas été visités.\n", + "- Retour: Si aucun chemin n'est trouvé, retourner None.\n", + "\n", + "B.\n", + "- Vérification des états visités, avant d'ajouter un successeur à la file de priorité, on vérifie si ils ont été visité.\n", + "- Une fois qu'un successeur est ajouté, il est ajouté à la file de priorité et marqué comme visité.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 8 : " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A. \n", + "Une représentation de son parent est crée dans la classe noeud. Lorsque qu'un successeur est crée, le noeud courant est défini comme parent\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[3, 4, 5], [3, 4, 4], [3, 4, 0]]\n" + ] + } + ], + "source": [ + "# B.\n", + "\n", + "def reconstruire_chemin(noeud_final):\n", + " \"\"\"\n", + " Reconstruit la séquence de mouvements depuis l’état initial jusqu’à l’état final.\n", + " \n", + " :param noeud_final: Le nœud final de l'algorithme A*.\n", + " :return: Une liste des états représentant le chemin de l'état initial à l'état final.\n", + " \"\"\"\n", + " chemin = []\n", + " noeud_courant = noeud_final\n", + " while noeud_courant:\n", + " chemin.append(noeud_courant.etat)\n", + " noeud_courant = noeud_courant.parent\n", + " return chemin[::-1]\n", + "\n", + "# Création d'un chemin d'exemple pour tester la fonction\n", + "etat_initial = [3, 4, 5]\n", + "etat_intermediaire = [3, 4, 4]\n", + "etat_final = [3, 4, 0]\n", + "\n", + "noeud_final = Noeud(etat_final, Noeud(etat_intermediaire, Noeud(etat_initial, None, 0, 0), 1, 0), 2, 0)\n", + "chemin = reconstruire_chemin(noeud_final)\n", + "print(chemin) # Devrait retourner [[3, 4, 5], [3, 4, 4], [3, 4, 0]]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 9 : " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[3, 4, 5]\n", + "[3, 4, 5]\n", + "\n", + "L'IA joue...\n", + "[3, 4, 0]\n", + "[0, 4, 0]\n", + "\n", + "L'IA joue...\n", + "\n", + " L'IA a gagné !\n" + ] + } + ], + "source": [ + "def jeu_nim(etat_initial):\n", + " etat = etat_initial[:]\n", + " while not est_etat_final(etat):\n", + " afficher_etat(etat)\n", + " try:\n", + " pile = int(input(\"Entrez le numéro de la pile (1, 2, 3, ...): \")) - 1\n", + " nb_objets = int(input(\"Entrez le nombre d'objets à retirer: \"))\n", + " appliquer_mouvement(etat, pile, nb_objets)\n", + " afficher_etat(etat)\n", + " except ValueError:\n", + " print(\"Entrée invalide. Veuillez entrer des nombres entiers.\")\n", + " continue\n", + "\n", + " if est_etat_final(etat):\n", + " print(\"\\nFélicitations ! Vous avez gagné !\")\n", + " break\n", + "\n", + " # Tour de l'IA\n", + " chemin_optimal = algorithme_a_star(etat)\n", + " if chemin_optimal and len(chemin_optimal) > 1:\n", + " etat_ia = chemin_optimal[1]\n", + " print(\"\\nL'IA joue...\")\n", + " etat = etat_ia\n", + "\n", + " if est_etat_final(etat):\n", + " print(\"\\n L'IA a gagné !\")\n", + " break\n", + "\n", + "# Exemple d'utilisation\n", + "etat_initial = [3, 4, 5]\n", + "jeu_nim(etat_initial)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 10 :" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "def xor_etat(etat_jeu):\n", + " xor_total = 0\n", + " for nb_objets in etat_jeu:\n", + " xor_total ^= nb_objets\n", + " return xor_total\n", + "\n", + "def heuristique_amelioree(etat_jeu):\n", + " xor_valeur = xor_etat(etat_jeu)\n", + " if xor_valeur == 0:\n", + " # Position désavantageuse (perdante)\n", + " return 100 + sum(etat_jeu) # Pénalité\n", + " else:\n", + " # Position avantageuse (gagnante)\n", + " return sum(etat_jeu) # Heuristique standard" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/TP01/code.py b/TP01/code.py new file mode 100644 index 0000000..556bc06 --- /dev/null +++ b/TP01/code.py @@ -0,0 +1,197 @@ +import heapq +import random + +class Noeud: + def __init__(self, etat, parent, cout, heuristique): + self.etat = etat + self.parent = parent + self.cout = cout + self.heuristique = heuristique + + def __lt__(self, other): + return self.cout + self.heuristique < other.cout + other.heuristique + + def __eq__(self, other): + return self.etat == other.etat + + def __hash__(self): + return hash(tuple(self.etat)) + + def __str__(self): + return str(self.etat) + +def afficher_etat(etat_jeu) : + print(etat_jeu) + +def xor_etat(etat_jeu): + xor_total = 0 + for nb_objets in etat_jeu: + xor_total ^= nb_objets + return xor_total + +def heuristique(etat_jeu): + xor_valeur = xor_etat(etat_jeu) + if xor_valeur == 0: + # Position désavantageuse (perdante) + return 100 + sum(etat_jeu) # Pénalité + else: + # Position avantageuse (gagnante) + return sum(etat_jeu) # Heuristique standard + +def appliquer_mouvement(etat_jeu, tas_index, nombre_objets) : + if nombre_objets > etat_jeu[tas_index] : + print("Erreur : nombre d'objets à retirer supérieur au nombre d'objets dans le tas.") + return etat_jeu + else : + etat_jeu[tas_index] -= nombre_objets + return etat_jeu + +def cout_transition(etat_courant, etat_suivant): + return 1 + + +def est_etat_final(etat): + return all(pile == 0 for pile in etat) + +def generer_successeurs(etat): + successeurs = [] + for i in range(len(etat)): + if etat[i] > 0: + for j in range(1, etat[i] + 1): + nouvel_etat = etat[:] + nouvel_etat[i] -= j + successeurs.append(nouvel_etat) + return successeurs + +def algorithme_a_star(etat_initial): + noeud_initial = Noeud(etat_initial, None, 0, heuristique(etat_initial)) + frontiere = [] + heapq.heappush(frontiere, noeud_initial) + visites = set() + + while frontiere: + noeud_courant = heapq.heappop(frontiere) + + if est_etat_final(noeud_courant.etat): + chemin = [] + while noeud_courant: + chemin.append(noeud_courant.etat) + noeud_courant = noeud_courant.parent + return chemin[::-1] + + visites.add(tuple(noeud_courant.etat)) + + for successeur in generer_successeurs(noeud_courant.etat): + if tuple(successeur) not in visites: + cout = noeud_courant.cout + cout_transition(noeud_courant.etat, successeur) + heur = heuristique(successeur) + noeud_successeur = Noeud(successeur, noeud_courant, cout, heur) + heapq.heappush(frontiere, noeud_successeur) + + return None + + +def reconstruire_chemin(noeud_final): + """ + Reconstruit la séquence de mouvements depuis l’état initial jusqu’à l’état final. + + :param noeud_final: Le nœud final de l'algorithme A*. + :return: Une liste des états représentant le chemin de l'état initial à l'état final. + """ + chemin = [] + noeud_courant = noeud_final + while noeud_courant: + chemin.append(noeud_courant.etat) + noeud_courant = noeud_courant.parent + return chemin[::-1] + +def sauvegarder_partie(historique, fichier="historique_parties.txt"): + """ + Sauvegarde l'historique de la partie dans un fichier. + + :param historique: Liste des coups effectués pendant la partie. + :param fichier: Nom du fichier où sauvegarder l'historique. + """ + with open(fichier, "a") as f: + f.write("Nouvelle partie\n") + for coup in historique: + f.write(f"{coup}\n") + f.write("\n") + + +def jeu_nim(etat_initial): + etat = etat_initial[:] + historique = [] + while not est_etat_final(etat): + afficher_etat(etat) + try: + pile = int(input("Entrez le numéro de la pile (1, 2, 3, ...): ")) - 1 + nb_objets = int(input("Entrez le nombre d'objets à retirer: ")) + historique.append(f"Joueur: Pile {pile + 1}, Objets retirés: {nb_objets}") + appliquer_mouvement(etat, pile, nb_objets) + afficher_etat(etat) + except ValueError: + print("Entrée invalide. Veuillez entrer des nombres entiers.") + continue + + if est_etat_final(etat): + print("\nFélicitations ! Vous avez gagné !") + break + + # Tour de l'IA + chemin_optimal = algorithme_a_star(etat) + if chemin_optimal and len(chemin_optimal) > 1: + etat_ia = chemin_optimal[1] + historique.append(f"IA: Pile {pile + 1}, Objets retirés: {nb_objets}") + print("\nL'IA joue...") + etat = etat_ia + + if est_etat_final(etat): + print("\n L'IA a gagné !") + break + sauvegarder_partie(historique) + +def pile_random(etat) : + piles_non_vides = [i for i, pile in enumerate(etat) if pile > 0] + return random.choice(piles_non_vides) + + +def nb_objets_random(etat, pile): + return random.randint(1, etat[pile]) + + +def jeu_nim_random(etat_initial): + etat = etat_initial[:] + historique = [] + while not est_etat_final(etat): + afficher_etat(etat) + try: + pile = pile_random(etat) + nb_objets = nb_objets_random(etat, pile) + historique.append(f"Joueur: Pile {pile + 1}, Objets retirés: {nb_objets}") + appliquer_mouvement(etat, pile, nb_objets) + afficher_etat(etat) + except ValueError: + print("Entrée invalide. Veuillez entrer des nombres entiers.") + continue + + if est_etat_final(etat): + print("\nFélicitations ! Vous avez gagné !") + break + + # Tour de l'IA + chemin_optimal = algorithme_a_star(etat) + if chemin_optimal and len(chemin_optimal) > 1: + etat_ia = chemin_optimal[1] + historique.append(f"IA: Pile {pile + 1}, Objets retirés: {nb_objets}") + print("\nL'IA joue...") + etat = etat_ia + + if est_etat_final(etat): + print("\n L'IA a gagné !") + break + sauvegarder_partie(historique) + + +etat_initial = [3, 4, 5] +jeu_nim_random(etat_initial) \ No newline at end of file