Files
DEV5.2/TP01/Nim.ipynb

563 lines
16 KiB
Plaintext
Raw Permalink Normal View History

2024-10-23 11:58:29 +02:00
{
"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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Bonus"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# création d'un système de sauvegarde des parties jouées\n",
"\n",
"def sauvegarder_partie(historique):\n",
" \"\"\"\n",
" Sauvegarde l'historique de la partie dans un fichier.\n",
" \n",
" :param historique: Liste des coups effectués pendant la partie.\n",
" :param fichier: Nom du fichier où sauvegarder l'historique.\n",
" \"\"\"\n",
" fichier = \"historique.txt\"\n",
" with open(fichier, \"a\") as f:\n",
" f.write(\"Nouvelle partie :\")\n",
" for coup in historique:\n",
" f.write(f\"{coup} \")\n",
" f.write(\"\\n\")\n",
"\n",
"\n",
"# modification de la fonction jeu_nim pour sauvegarder l'historique des coups\n",
"def jeu_nim(etat_initial):\n",
" etat = etat_initial[:]\n",
" historique = []\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",
" historique.append(f\"Joueur: Pile {pile + 1}, Objets retirés: {nb_objets}\")\n",
" appliquer_mouvement(etat, pile, nb_objets)\n",
" historique.append(f\"Player: {etat}\")\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",
" historique.append(f\"IA: Pile {pile + 1}, Objets retirés: {nb_objets}\")\n",
" print(\"\\nL'IA joue...\")\n",
" etat = etat_ia\n",
" historique.append(f\"AI: {etat}\")\n",
"\n",
" if est_etat_final(etat):\n",
" print(\"\\n L'IA a gagné !\")\n",
" break\n",
" sauvegarder_partie(historique)"
]
2024-10-23 11:58:29 +02:00
}
],
"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
}