Créez un jeu de labyrinthe avec Python

Je crois que tout le monde a joué au jeu des labyrinthes. Pour les labyrinthes simples, nous pouvons voir le passage en un coup d'œil, mais pour les labyrinthes complexes, la recherche approfondie peut prendre beaucoup de temps, voire plusieurs jours, puis il peut être nécessaire de rechercher de l'entrée et de la sortie respectivement.Peut seulement trouver l'accès, et peut-être même pas trouver l'accès.

Bien que le problème de marcher dans le labyrinthe soit plus compliqué pour nous les humains, c'est un problème très simple pour les ordinateurs. Pourquoi dites-vous cela, car cela semble compliqué mais il y a des règles à suivre.

Nous pouvons le faire en portant une longue corde et en marchant depuis l'entrée.S'il y a une bifurcation dans la route, nous irons à la fourche la plus à gauche jusqu'à ce que nous atteignions une impasse ou trouvons une issue. Si c'est une impasse, alors retirez-vous à la dernière bifurcation de la route, nous l'appelons fourche A,

À ce moment, entrez dans la deuxième fourche sur la gauche et répétez les étapes de la première fourche après être entré dans la deuxième fourche jusqu'à ce que vous trouviez un moyen de sortir ou de vous retirer d'une impasse. Après avoir traversé toutes les fourches de la fourche, revenez le long de la corde avant de trouver la sortie, jusqu'à l'intersection B avant la fourche A, et répétez les étapes ci-dessus.

Je ne sais pas si vous avez découvert qu’il s’agit en fait d’un processus de récurrence constante, et c’est dans ce domaine que les ordinateurs sont bons.

L'algorithme ci-dessus pour parcourir le labyrinthe est l'algorithme de traversée de la profondeur d'abord que nous disons souvent, et l'inverse est l'algorithme de traversée de la largeur d'abord. Avec les bases théoriques, essayons d'utiliser le programme pour mettre en œuvre un petit programme qui parcourt le labyrinthe.


Génération de labyrinthes Il existe de nombreux algorithmes pour générer des labyrinthes, les plus couramment utilisés sont le backtracking récursif, la segmentation récursive et l'algorithme de Prim aléatoire.

Les principales étapes de l'algorithme sont les suivantes:
1. Les lignes et colonnes du labyrinthe doivent être impaires
2. L'intersection des rangées impaires et des colonnes impaires est la route, le reste des points sont des murs et le labyrinthe est entouré de murs
3. Sélectionnez une cellule qui est la route (Dans cet exemple, sélectionnez [1,1]), puis placez son mur adjacent dans le mur de liste
4. Lorsqu'il y a un mur dans le mur de liste:
4.1. Aléatoire sélectionnez un mur dans la liste, si le mur sépare deux unités Une seule cellule de la cellule a été visitée
4.1.1, puis supprimez le mur de la liste, et ouvrez le mur en même temps
4.1.2, marquez la cellule comme visité
4.1.3, définir le voisin de la cellule non visitée Ajouter le mur au mur de la liste
4.2. Si les cellules des deux côtés du mur ont été visitées, supprimer le mur de la liste

Nous définissons une classe Maze, en utilisant un tableau bidimensionnel pour représenter la carte du labyrinthe, où 1 représente le mur et 0 représente la route, puis initialisons le coin supérieur gauche comme entrée, le coin inférieur droit comme sortie, et enfin définir le vecteur de direction descendante.

labyrinthe de classe: def init (self, width, height): self.width = widthself.height = heightself.map = [[0 if x% 2 == 1 and y% 2 == 1 else 1 for x in range (width )] pour y dans la plage (hauteur)] self.map [1] [0] = 0 # entrée self.map [hauteur-2] [largeur-1] = 0 # sortie self.visited = [] # droite en haut à gauche downself.dx = [1, 0, -1, 0] self.dy = [0, -1, 0, 1] L'
étape suivante consiste à générer la fonction principale du labyrinthe.

def generate (self):
start = [1, 1] self.visited.append (start)
wall_list = self.get_nequart_wall (start) tandis que wall_list:
wall_position = random.choice (wall_list)
prochain_road = self.get_nequart_road (wall_position)
wall_list. remove (wall_position) self.deal_with_not_visited (Neighbor_road [0], wall_position, wall_list) self.deal_with_not_visited (Neighbor_road [1], wall_position, wall_list)
Il y a deux fonctions principales dans cette fonction get_nequart_road (point) et deal_with_not_visited () Le nœud voisin de le point de coordonnée entrant, la valeur de retour est un tableau bidimensionnel, et la dernière fonction deal_with_not_visited () traite la logique de l'étape 4.1.

Étant donné que l'algorithme aléatoire Prim sélectionne au hasard toutes les cellules de la liste au hasard, la probabilité de sélection de la cellule nouvellement ajoutée et de l'ancienne cellule ajoutée est la même, il comporte donc plus de branches et le labyrinthe généré est plus compliqué. et bien sûr, cela semble plus naturel. Le labyrinthe généré.
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 1 , 1, 1, 1, 0, 1, 1, 1, 1, 1]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 1, 1, 0 , 1, 0, 1, 0, 1, 0, 1]
[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1]
[1, 0, 1, 0, 1, 0 , 1, 1, 1, 1, 1]
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1]
[1, 0, 1, 0, 1, 1, 1, 0 , 1, 0, 1]
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1 , 1]

Sortez du labyrinthe et
récupérez la carte du labyrinthe, puis suivez simplement l'idée de notre premier article pour parcourir le labyrinthe. La logique de la fonction principale est la suivante:

def dfs (soi, x, y, chemin, visité = []): # outOfIndexif self.is_out_of_index (x, y): retourne False # visité ou est wallif [x, y] dans visité ou self.get_value ([x, y]) == 1: renvoie False

visité.append ([x, y])
path.append ([x, y]) # end ... if x == self.width - 2 et y == self.height - 2: return True # recursivefor i in range (4): if 0 <x + self.dx [i] <self.width - 1 et 0 <y + self.dy [i] <self.height - 1 et \ self.get_value ([x + self.dx [i], y + self.dy [i]]) == 0: if self.dfs (x + self.dx [i], y + self.dy [i], chemin, visité): renvoie True
        elif pas self.is_out_of_index (x, y) et chemin [-1]! = [x, y]:
            chemin.append ([x, y])

De toute évidence, il s'agit d'un programme récursif typique. Lorsque la coordonnée du nœud est hors limites, que le nœud a été visité, ou que le nœud est un mur, retournez directement, car le nœud ne fait certainement pas partie du chemin que nous recherchons, sinon le nœud sera ajouté au ensemble de nœuds et de chemins visités dans.

Ensuite, si le nœud est une sortie, cela signifie que l'exécution du programme est terminée et que le chemin est trouvé. Sinon, parcourez les quatre vecteurs de direction, passez le chemin voisin du nœud dans la fonction dfs et continuez les étapes ci-dessus jusqu'à ce qu'un chemin soit trouvé ou que tous les nœuds du programme soient traversés.

Jetons un coup d'œil aux résultats de chemin obtenus par notre dfs:

[[0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 1], [ 8, 1], [9, 1], [9, 1], [8, 1], [7, 1], [6, 1], [5, 1], [5, 2], [5, 3], [6, 3], [7, 3], [8, 3], [9, 3], [9, 4], [9, 5], [9, 5], [9, 4] , [9, 3], [8, 3], [7, 3], [7, 4], [7, 5], [7, 5], [7, 4], [7, 3], [ 6, 3], [5, 3], [4, 3], [3, 3], [2, 3], [1, 3], [1, 3], [2, 3], [3, 3], [3, 4], [3, 5], [2, 5], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9] , [1, 9], [1, 8], [1, 7], [1, 6], [1, 5], [2, 5], [3, 5], [3, 6], [ 3, 7], [3, 8], [3, 9], [3, 9], [3, 8], [3, 7], [3, 6], [3, 5], [3, 4], [3, 3], [4, 3], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [6, 7] , [7, 7], [8, 7], [9, 7], [9, 8], [9, 9], [10, 9]]
Visualisez la
carte du labyrinthe et le chemin du chemin, le reste du travail Il s'agit de rendre ces points de coordonnées. La bibliothèque de visualisation que nous utilisons aujourd'hui est pyxel, qui est une bibliothèque Python pour écrire des jeux au niveau des pixels.

Bien sûr, vous devez installer cette bibliothèque avant de l'utiliser.

Les utilisateurs Win peuvent l'installer directement avec la commande pip install -U pyxel.

Les utilisateurs de Mac utilisent la commande suivante pour installer:

brew install python3 gcc sdl2 sdl2_image gifsicle
pip3 install -U pyxel
Regardons d'abord une simple démo.

filigrane, size_16, text_QDUxQ1RP5Y2a5a6i, color_FFFFFF, t_100, g_se, x_10, y_10, shadow_90, type_ZmFuZ3poZW5naGVpdGk =

import application pyxelclass: def init (self):
pyxel.init (160, 120) self.x = 0
pyxel.run (self.update, self.draw) def mise à jour (self): self.x = (self.x + 1)% pyxel.widthdef draw (self):
pyxel.cls (0)
pyxel.rect (self.x, 0, 8, 8, 9)


La logique d'exécution de la classe App () App est d'appeler en permanence la fonction de mise à jour et la fonction de dessin, afin que vous puissiez mettre à jour les coordonnées de l'objet dans la fonction de mise à jour, puis dessiner l'image à l'écran dans la fonction de dessin.

De cette façon, nous dessinons d'abord le labyrinthe, puis nous rendons l'animation de parcours DFS.

filigrane, size_16, text_QDUxQ1RP5Y2a5a6i, color_FFFFFF, t_100, g_se, x_10, y_10, shadow_90, type_ZmFuZ3poZW5naGVpdGk =

width, height = 37, 21
my_maze = Maze (largeur, hauteur)
my_maze.generate () class App: def init (self):
pyxel.init (width * pixel, height * pixel)
pyxel.run (self.update, self .draw) def update (self): if pyxel.btn (pyxel.KEY_Q):
pyxel.quit () if pyxel.btn (pyxel.KEY_S): self.death = Falsedef draw (self): # dessiner le labyrinthe pour x dans la plage (hauteur): pour y dans la plage (largeur):
color = road_color si my_maze.map [x] [y] vaut 0 sinon wall_color
pyxel.rect (y * pixel, x * pixel, pixel, pixel, couleur)
pyxel.rect (0, pixel, pixel, pixel, start_point_color)
pyxel.rect ((largeur - 1) * pixel, (hauteur - 2) * pixel, pixel, pixel, end_point_color)

L'application ()
semble correcte. J'ai utilisé des grilles de 37 et 21 pixels pour générer respectivement la largeur et la hauteur, donc le labyrinthe généré n'est pas très compliqué. S'il y a beaucoup de pixels, ce sera compliqué.

Ensuite, nous devons modifier la fonction de mise à jour et la fonction de dessin pour rendre le chemin. Afin de faciliter l'opération, nous avons ajouté plusieurs attributs à la fonction init.

self.index = 0 self.route = [] # Utilisé pour enregistrer le chemin à rendre self.step = 1 # Longueur du pas, plus la valeur est petite, plus la vitesse est rapide, 1: une grille à chaque fois; 10: 1 / 10 grille à chaque fois self .color = start_point_colorself.bfs_route = my_maze.bfs_route ()
où index et step sont utilisés pour contrôler la vitesse de rendu. Dans la fonction draw, l'index est incrémenté de 1 à chaque fois, puis le reste de l'étape est calculé pour obtenir l'indice réel actuel real_index. En d'autres termes, real_index augmentera de un à chaque fois que l'index augmentera pas à pas, et le chemin de rendu fera un pas en avant.

def draw (self): # draw mazefor x in range (height): for y in range (width):
color = road_color si my_maze.map [x] [y] vaut 0 else wall_color
pyxel.rect (y * pixel, x * pixel, pixel, pixel, couleur)
pyxel.rect (0, pixel, pixel, pixel, start_point_color)
pyxel.rect ((largeur - 1) * pixel, (hauteur - 2) * pixel, pixel, pixel, end_point_color) si self.index> 0: # draw route
offset = pixel / 2for i in range (len (self.route) - 1):
curr = self.route [i] next = self.route [i + 1] self.color = backtrack_color if curr dans self.route [: i] et next dans self.route [: i] else route_color
pyxel.line (curr [0] + offset, (curr [1] + offset), next [0] + offset, suivant [1] + offset, self.color)
pyxel.circ (self.route [-1] [0] + 2, self.route [-1] [1] + 2, 1, head_color)
def update (self): if pyxel.btn (pyxel.KEY_Q):
pyxel.quit () if pyxel.btn (pyxel.KEY_S): self.death = Falseif pas self.death: self.check_death () self.update_route ( ) def check_death (self): if self.dfs_model et len ​​(self.route) == len (self.dfs_route) - 1: self.death = True
elif pas self.dfs_model et len ​​(self.route) == len ( self.bfs_route) - 1: self.death = Truedef update_route (self):
index = int (self.index / self.step) self.index + = 1if index == len (self.route): # moveif self.dfs_model : self.route.append ([pixel * self.dfs_route [index] [0], pixel * self.dfs_route [index] [1]]) autre: self.route.append ([pixel * self.bfs_route [index] [0], pixel * self.bfs_route [index] [1]])

App () Jusqu'à
présent, nous avons complètement réalisé de la génération du labyrinthe, à la recherche du chemin, puis à la visualisation du chemin. Appelez directement la fonction principale App () et appuyez sur le clavier S pour démarrer le jeu

Résumé
Aujourd'hui, nous avons utilisé l'algorithme de la profondeur d'abord pour parcourir le labyrinthe. Pour les novices, l'idée de récursivité peut être plus difficile à comprendre, mais cela est conforme à la pensée informatique. Avec l'approfondissement de l'expérience, la compréhension deviendra de plus en plus profond.

Deuxièmement, nous utilisons la librairie pyxel pour réaliser la visualisation du chemin. La difficulté réside dans le calcul et la mise à jour des coordonnées. Les détails sont de plus en plus encombrants. Bien entendu, les lecteurs peuvent aussi utiliser d'autres librairies ou utiliser directement la page web pour y parvenir .
Source de l'article: jeu Web http://www.hp91.cn/web game

Je suppose que tu aimes

Origine blog.51cto.com/14621511/2679085
conseillé
Classement