From c28bd609e57cfb918fcd320b431897fb1233aa25 Mon Sep 17 00:00:00 2001 From: Adrien Bourmault Date: Wed, 10 May 2023 10:09:36 +0200 Subject: [PATCH] Cours du 4 mai --- game.py | 244 +++++++++++++++++++++++++++++ graphics.py | 3 + maps.py | 81 ++++++++++ wall_is_you.py | 418 ++++++++++++------------------------------------- 4 files changed, 426 insertions(+), 320 deletions(-) create mode 100644 game.py create mode 100644 graphics.py create mode 100644 maps.py diff --git a/game.py b/game.py new file mode 100644 index 0000000..a5b7c67 --- /dev/null +++ b/game.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 + +import random, time + +## Gestion de partie + +def créer_salle_random(): + """ + Créer une salle aléatoire + @params void + @return une salle, tuple de taille 4 + """ + res0 = True if random.random() * 100 > 50 else False + res1 = True if random.random() * 100 > 50 else False + res2 = True if random.random() * 100 > 50 else False + res3 = True if random.random() * 100 > 50 else False + + return (res0, res1, res2, res3) + +def créer_dragon(pos): + """ + Créer un dragon dans la salle @pos + @params pos, un tuple + @return un dragon, dictionnaire + """ + niveau = 0 + + while niveau == 0: + niveau = int(random.random() * 20) + for dragon in dragons: + if dragon["niveau"] == niveau: + niveau = 0 + break + + return { 'position' : pos, 'niveau' : niveau } + +def peupler_donjon(donjon, dragons): + """ + Créer l'état initial du donjon au début du jeu + @params void + @return void + """ + donjon.clear() + dragons.clear() + + for i in range(TAILLE_DONJON): + ligne = [] + for j in range(TAILLE_DONJON): + ligne.append(créer_salle_random()) + + if random.random() * 100 > 75: # 25% de chance + dragons.append(créer_dragon((i,j))) + donjon.append(ligne) + + + +def pivoter_donjon(donjon, position): + """ + Pivote la salle pointée par @position dans @donjon + @params donjon, une liste contenant des salles + position, un tuple identifiant une salle + @return void + """ + + x = position[0] + y = position[1] + + donjon[x][y] = tuple([donjon[x][y][-1]] + list(donjon[x][y][:-1])) + + +def connecte(donjon, pos0, pos1): + """ + Vérifie que les salles pointées par @pos0 et @pos1 dans @donjon + sont adjacentes et connectées + @params donjon, une liste contenant des salles + pos0, un tuple identifiant une salle + pos1, un tuple identifiant une salle + @return booléen + """ + + x0 = pos0[0] + y0 = pos0[1] + x1 = pos1[0] + y1 = pos1[1] + + # On vérifie les limites + if x0 < 0 or y0 < 0 or x1 < 0 or y1 < 0: + return False + + if x0 >= len(donjon) or y0 >= len(donjon) or x1 >= len(donjon) or y1 >= len(donjon): + return False + + + # Calcul des distances + axe0 = x0 - x1 + axe1 = y0 - y1 + distance = abs(axe0) + abs(axe1) + + + # Vérifier l'adjacence + if distance > 1: + return False + + # Chercher point de connexion + if axe1 == 0: + if axe0 <= -1: + # le pôle sud de pos1 touche le pôle nord de pos0 + if donjon[x1][y1][S] and donjon[x0][y0][N]: + return True + elif axe0 >= 1: + # le pôle sud de pos0 touche le pôle nord de pos1 + if donjon[x0][y0][S] and donjon[x1][y1][N]: + return True + else: + if axe1 <= -1: + # le pôle est de pos1 touche le pôle ouest de pos0 + if donjon[x1][y1][E] and donjon[x0][y0][O]: + return True + elif axe1 >= 1: + # le pôle est de pos0 touche le pôle ouest de pos1 + if donjon[x0][y0][E] and donjon[x1][y1][O]: + return True + return False + + +def calcul_chemin(donjon, position, dragons, visite): + """ + Support récursif pour intention + @params donjon, une liste contenant des salles + position, un tuple identifiant une salle (dans laquelle l'aventurier + se trouve) + dragons, une liste de dragons + @return booléen + """ + résultats = [] + + # Déjà visité + if position in visite: + return None + else: + visite.add(position) + + # Cas de base + for dragon in dragons: + if position == dragon["position"]: + return ([position], dragon["niveau"]) + + # Test récursif des potentielles connexions + x, y = position + + if connecte(donjon, position, (x+1,y)): + résultats.append(calcul_chemin(donjon, (x+1,y), dragons, visite)) + + if connecte(donjon, position, (x-1,y)): + résultats.append(calcul_chemin(donjon, (x-1,y), dragons, visite)) + + if connecte(donjon, position, (x,y+1)): + résultats.append(calcul_chemin(donjon, (x,y+1), dragons, visite)) + + if connecte(donjon, position, (x,y-1)): + résultats.append(calcul_chemin(donjon, (x,y-1), dragons, visite)) + + # Aucun couple dans résultats + if résultats == [None] * len(résultats): + return None + else: + niveau = 0 + candidat = (0,0) + for couple in résultats: + if couple is None: + continue + if couple[1] > niveau: + niveau = couple[1] + candidat = (couple[0], couple[1]) + return ([position] + candidat[0], candidat[1]) + +def intention(donjon, position, dragons): + """ + Calcule récursivement le chemin à parcourir jusqu'à un dragon du plus haut + niveau accessible + @params donjon, une liste contenant des salles + position, un tuple identifiant une salle (dans laquelle l'aventurier + se trouve) + dragons, une liste de dragons + @return liste, chemin à parcourir + """ + + visite = set() + + return calcul_chemin(donjon, position, dragons, visite) + +def rencontre(aventurier, dragons): + """ + Vérifie si l'@aventurier est à la même position qu'un dragon de @dragons et + agit en conséquence + @params aventurier, dictionnaire représentant un aventurier + dragons, liste de dragons (dictionnaires) + @return void + """ + versus = False + ennemi = None + + for dragon in dragons: + if aventurier["position"] == dragon["position"]: + versus = True + ennemi = dragon + + # Un dragon sauvage apparaît ! + if versus: + if ennemi["niveau"] <= aventurier["niveau"]: + # Victoire + dragons.remove(ennemi) + aventurier["niveau"] = aventurier["niveau"] + 1 + else: + # Décès + aventurier["vivant"] = False + +def appliquer_chemin(aventurier, dragons, chemin): + """ + déplace l'aventurier + @return rien + """ + + ([(2, 1), (2, 0)], 16) + + position = chemin[0][0] + chemin[0].remove(position) + + aventurier["position"] = position + + rencontre(aventurier, dragons) + +def fin_partie(aventurier, dragons): + """ + @return 1 si la partie est gagnée (tous les dragons ont été tués), + -1 si la partie est perdue (l’aventurier a été tué), + 0 si la partie continue. + """ + if len(dragons) == 0: + return 1 + elif not aventurier["vivant"]: + return -1 + else: + return 0 diff --git a/graphics.py b/graphics.py new file mode 100644 index 0000000..529abed --- /dev/null +++ b/graphics.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +# Doc FLTK : https://antoinemeyer.frama.io/fltk/ diff --git a/maps.py b/maps.py new file mode 100644 index 0000000..cdcc28e --- /dev/null +++ b/maps.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +import random, time + +## Gestion des maps + +def créer_salle(forme_salle): + """ + Créer une salle aléatoire (N,E,S,O) + @params void + @return une salle, tuple de taille 4 + """ + + correspondance = { + "╬" : (True,)*4, + + "╡" : (False, False, False, True), + "╞" : (False, True, False, False), + "╥" : (False, False, True, False), + "╨" : (True, False, False, False), + + "╔" : (False, True, True, False), + "╗" : (False, False, True, True), + "╝" : (True, False, False, True), + "╚" : (True, True, False, False), + + "║" : (True, False, True, False), + "═" : (False, True, False, True), + + "╦" : (False, True, True, True), + "╩" : (True, True, False, True), + "╠" : (True, True, True, False), + "╣" : (True, False, True, True) + } + + return correspondance[forme_salle] + + +def charger(fichier, donjon, dragons, aventurier): + """ + Charge un fichier et peuple la structure donjon + @params fichier, chemin d'accès à un fichier + @return 1 ou None + """ + donjon.clear() + dragons.clear() + + # Lecture fichier brut + contenu = "" + with open(fichier, 'r') as fichier: + contenu = fichier.read() + + try: + # Séparation entre carte et coordonnées des personnages + donjon_map = contenu[:contenu.find("A")-1].split("\n") + donjon_declaration = contenu[contenu.find("A"):].split("\n") + + # Création du donjon + for i in range(len(donjon_map[0])): + ligne = [] + for j in range(len(donjon_map)): + ligne.append(créer_salle(donjon_map[i][j])) + donjon.append(ligne) + + # Création des personnages + for declaration in donjon_declaration: + # Déclaration de l'aventurier + if "A" in declaration: + cible, x, y = declaration.split(" ") + aventurier["position"] = (x,y) + aventurier["niveau"] = 1 + + # Déclaration d'un dragon + elif "D" in declaration: + cible, x, y, niveau = declaration.split(" ") + dragons.append({ 'position' : (x,y), 'niveau' : niveau }) + + return 1 + except Exception as e: + print("Erreur : {}".format(e)) + return None diff --git a/wall_is_you.py b/wall_is_you.py index a1000ad..66ac119 100755 --- a/wall_is_you.py +++ b/wall_is_you.py @@ -9,11 +9,14 @@ # - Intention : cible le dragon le plus fort accessible (orgueil) # # Doc FLTK : https://antoinemeyer.frama.io/fltk/ - -import random, fltk +import random, time, fltk, os +import maps +import game ## VARIABLES DE CONFIGURATION TAILLE_DONJON = 6 +LARGEUR_FENETRE = 800 +HAUTEUR_FENETRE = 400 ## Variables globales @@ -30,340 +33,115 @@ aventurier = { 'vivant': True } -## Gestion de partie - -def créer_salle_random(): +## Fonctions +def selection_menu(): """ - Créer une salle aléatoire + Home selection menu @params void - @return une salle, tuple de taille 4 - """ - res0 = True if random.random() * 100 > 50 else False - res1 = True if random.random() * 100 > 50 else False - res2 = True if random.random() * 100 > 50 else False - res3 = True if random.random() * 100 > 50 else False - - return (res0, res1, res2, res3) - -def créer_dragon(pos): + @return path, chemin d'accès à un fichier de map """ - Créer un dragon dans la salle @pos - @params pos, un tuple - @return un dragon, dictionnaire - """ - niveau = 0 - - while niveau == 0: - niveau = int(random.random() * 20) - for dragon in dragons: - if dragon["niveau"] == niveau: - niveau = 0 - break - return { 'position' : pos, 'niveau' : niveau } + list_maps = os.listdir("maps") + + fltk.cree_fenetre(LARGEUR_FENETRE, HAUTEUR_FENETRE) + fltk.texte(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/8, + "Bienvenue dans Wall is You !", + couleur="black", + taille=40, + ancrage='center') + + fltk.texte(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/4, + "Vous allez pouvoir sélectionner un donjon auquel jouer" + " dans la liste ci-dessous." + " Cliquez sur le donjon de votre choix pour y jouer.", + couleur="black", + taille=10, + ancrage='center') -def peupler_donjon(): - """ - Créer l'état initial du donjon au début du jeu - @params void - @return void - """ - global donjon - global dragons + fltk.texte(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/3, + "Tapez Echap pour quitter le jeu", + couleur="green", + taille=10, + ancrage='center') - donjon.clear() - dragons.clear() + if len(list_maps) == 0: + fltk.texte(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2, + "Aucun donjon jouable installé.", + couleur="red", + taille=30, + ancrage='center') + else: + x1 = LARGEUR_FENETRE/40 + x2 = HAUTEUR_FENETRE/2.22 + y1 = HAUTEUR_FENETRE/2.22 + y2 = LARGEUR_FENETRE/3 + for i in range(len(list_maps)): + fltk.rectangle(x1, y1, x2, y2, remplissage="white") + fltk.texte(x1+(x2-x1)/2, + y1+(y2-y1)/2, + list_maps[i].replace(".txt", ""), + couleur="blue", + taille=20, + ancrage='center') + x1 += LARGEUR_FENETRE/4 + x2 += LARGEUR_FENETRE/4 + if max(x1, x2) > LARGEUR_FENETRE: + x1 = LARGEUR_FENETRE/40 + x2 = HAUTEUR_FENETRE/2.22 + y1 += HAUTEUR_FENETRE/3.5 + y2 += HAUTEUR_FENETRE/3.5 - for i in range(TAILLE_DONJON): - ligne = [] - for j in range(TAILLE_DONJON): - ligne.append(créer_salle_random()) + while True: + event = fltk.attend_ev() + + if "Quitte" in fltk.type_ev(event): + fltk.ferme_fenetre() + return None + + if "Touche" in fltk.type_ev(event) and "Escape" in fltk.touche(event): + fltk.ferme_fenetre() + return None + + if "ClicGauche" in fltk.type_ev(event): + # XXX à améliorer + x = fltk.abscisse(event) + y = fltk.ordonnee(event) - if random.random() * 100 > 75: # 25% de chance - dragons.append(créer_dragon((i,j))) - donjon.append(ligne) - - - -def pivoter_donjon(donjon, position): - """ - Pivote la salle pointée par @position dans @donjon - @params donjon, une liste contenant des salles - position, un tuple identifiant une salle - @return void - """ - - x = position[0] - y = position[1] - - donjon[x][y] = tuple([donjon[x][y][-1]] + list(donjon[x][y][:-1])) - - -def connecte(donjon, pos0, pos1): - """ - Vérifie que les salles pointées par @pos0 et @pos1 dans @donjon - sont adjacentes et connectées - @params donjon, une liste contenant des salles - pos0, un tuple identifiant une salle - pos1, un tuple identifiant une salle - @return booléen - """ - - x0 = pos0[0] - y0 = pos0[1] - x1 = pos1[0] - y1 = pos1[1] - - # On vérifie les limites - if x0 < 0 or y0 < 0 or x1 < 0 or y1 < 0: - return False - - if x0 >= len(donjon) or y0 >= len(donjon) or x1 >= len(donjon) or y1 >= len(donjon): - return False - - - # Calcul des distances - axe0 = x0 - x1 - axe1 = y0 - y1 - distance = abs(axe0) + abs(axe1) - - - # Vérifier l'adjacence - if distance > 1: - return False - - # Chercher point de connexion - if axe1 == 0: - if axe0 <= -1: - # le pôle sud de pos1 touche le pôle nord de pos0 - if donjon[x1][y1][S] and donjon[x0][y0][N]: - return True - elif axe0 >= 1: - # le pôle sud de pos0 touche le pôle nord de pos1 - if donjon[x0][y0][S] and donjon[x1][y1][N]: - return True - else: - if axe1 <= -1: - # le pôle est de pos1 touche le pôle ouest de pos0 - if donjon[x1][y1][E] and donjon[x0][y0][O]: - return True - elif axe1 >= 1: - # le pôle est de pos0 touche le pôle ouest de pos1 - if donjon[x0][y0][E] and donjon[x1][y1][O]: - return True - return False - - -def calcul_chemin(donjon, position, dragons, visite): - """ - Support récursif pour intention - @params donjon, une liste contenant des salles - position, un tuple identifiant une salle (dans laquelle l'aventurier - se trouve) - dragons, une liste de dragons - @return booléen - """ - résultats = [] - - # Déjà visité - if position in visite: - return None - else: - visite.add(position) - - # Cas de base - for dragon in dragons: - if position == dragon["position"]: - return ([position], dragon["niveau"]) - - # Test récursif des potentielles connexions - x, y = position - - if connecte(donjon, position, (x+1,y)): - résultats.append(calcul_chemin(donjon, (x+1,y), dragons, visite)) - - if connecte(donjon, position, (x-1,y)): - résultats.append(calcul_chemin(donjon, (x-1,y), dragons, visite)) - - if connecte(donjon, position, (x,y+1)): - résultats.append(calcul_chemin(donjon, (x,y+1), dragons, visite)) - - if connecte(donjon, position, (x,y-1)): - résultats.append(calcul_chemin(donjon, (x,y-1), dragons, visite)) - - # Aucun couple dans résultats - if résultats == [None] * len(résultats): - return None - else: - niveau = 0 - candidat = (0,0) - for couple in résultats: - if couple is None: + x_i = LARGEUR_FENETRE/40 + y_i = HAUTEUR_FENETRE/2.22 + + x_n = int((x - x_i) / (LARGEUR_FENETRE/4)) + y_n = int((y - y_i) / (HAUTEUR_FENETRE/3.5)) + + if x_n+y_n*4 < 0 or x_n+y_n*4 >= len(list_maps): continue - if couple[1] > niveau: - niveau = couple[1] - candidat = (couple[0], couple[1]) - return ([position] + candidat[0], candidat[1]) - -def intention(donjon, position, dragons): - """ - Calcule récursivement le chemin à parcourir jusqu'à un dragon du plus haut - niveau accessible - @params donjon, une liste contenant des salles - position, un tuple identifiant une salle (dans laquelle l'aventurier - se trouve) - dragons, une liste de dragons - @return liste, chemin à parcourir - """ + + fltk.ferme_fenetre() + return "./maps/" + list_maps[x_n+y_n*4] - visite = set() - - return calcul_chemin(donjon, position, dragons, visite) + fltk.ferme_fenetre() + return "./maps/map1.txt" -def rencontre(aventurier, dragons): +def run_game(): """ - Vérifie si l'@aventurier est à la même position qu'un dragon de @dragons et - agit en conséquence - @params aventurier, dictionnaire représentant un aventurier - dragons, liste de dragons (dictionnaires) + Running game + @params void @return void """ - versus = False - ennemi = None - - for dragon in dragons: - if aventurier["position"] == dragon["position"]: - versus = True - ennemi = dragon - - # Un dragon sauvage apparaît ! - if versus: - if ennemi["niveau"] <= aventurier["niveau"]: - # Victoire - dragons.remove(ennemi) - aventurier["niveau"] = aventurier["niveau"] + 1 - else: - # Décès - aventurier["vivant"] = False - -def appliquer_chemin(aventurier, dragons, chemin): - """ - déplace l'aventurier - @return rien - """ - - ([(2, 1), (2, 0)], 16) - - position = chemin[0][0] - chemin[0].remove(position) - - aventurier["position"] = position - - rencontre(aventurier, dragons) - -def fin_partie(aventurier, dragons): - """ - @return 1 si la partie est gagnée (tous les dragons ont été tués), - -1 si la partie est perdue (l’aventurier a été tué), - 0 si la partie continue. - """ - if len(dragons) == 0: - return 1 - elif not aventurier["vivant"]: - return -1 - else: - return 0 - -## Gestion des maps - -def créer_salle(forme_salle): - """ - Créer une salle aléatoire (N,E,S,O) - @params void - @return une salle, tuple de taille 4 - """ - - correspondance = { - "╬" : (True,)*4, - - "╡" : (False, False, False, True), - "╞" : (False, True, False, False), - "╥" : (False, False, True, False), - "╨" : (True, False, False, False), - - "╔" : (False, True, True, False), - "╗" : (False, False, True, True), - "╝" : (True, False, False, True), - "╚" : (True, True, False, False), - - "║" : (True, False, True, False), - "═" : (False, True, False, True), - - "╦" : (False, True, True, True), - "╩" : (True, True, False, True), - "╠" : (True, True, True, False), - "╣" : (True, False, True, True) - } - - return correspondance[forme_salle] - - -def charger(fichier): - """ - Charge un fichier et peuple la structure donjon - @params fichier, chemin d'accès à un fichier - @return 1 ou None - """ - global donjon - global dragons - - donjon.clear() - dragons.clear() - - # Lecture fichier brut - contenu = "" - with open(fichier, 'r') as fichier: - contenu = fichier.read() - - try: - # Séparation entre carte et coordonnées des personnages - donjon_map = contenu[:contenu.find("A")-1].split("\n") - donjon_declaration = contenu[contenu.find("A"):].split("\n") - - # Création du donjon - for i in range(len(donjon_map[0])): - ligne = [] - for j in range(len(donjon_map)): - ligne.append(créer_salle(donjon_map[i][j])) - donjon.append(ligne) - - # Création des personnages - for declaration in donjon_declaration: - # Déclaration de l'aventurier - if "A" in declaration: - cible, x, y = declaration.split(" ") - aventurier["position"] = (x,y) - aventurier["niveau"] = 1 - - # Déclaration d'un dragon - elif "D" in declaration: - cible, x, y, niveau = declaration.split(" ") - dragons.append({ 'position' : (x,y), 'niveau' : niveau }) - - return 1 - except Exception as e: - print("Erreur : {}".format(e)) - return None - -## Fonctions graphiques - - - -## Fonction principale + pass def main(): - cree_fenetre(400, 300) - ferme_fenetre() + while True: + map_choice = selection_menu() + if not map_choice: + break + else: + print("chargement de {}".format(map_choice)) + if maps.charger(map_choice, donjon, dragons, aventurier): + run_game() + else: + return 1 + return 0 main()