Python으로 미로 게임 만들기

모두가 미로 게임을했다고 생각합니다. 단순한 미로의 경우 한 눈에 통로를 볼 수 있지만 복잡한 미로의 경우 신중하게 검색하는 데 오랜 시간 또는 며칠이 걸릴 수 있으며 검색이 필요할 수 있습니다. 입구와 출구에서 각각 액세스를 찾을 수 있으며 액세스를 찾지 못할 수도 있습니다.

미로를 걷는 문제는 우리 인간에게는 더 복잡하지만 컴퓨터에게는 매우 간단한 문제입니다. 복잡해 보이지만 따라야 할 규칙이 있기 때문에 왜 그렇게 말합니까?

긴 로프를 들고 입구에서 걸어 가면 할 수 있는데, 도로에 갈림 기가 있으면 막 다른 골목에 도달하거나 탈출구를 찾을 때까지 가장 왼쪽 갈림기로 이동합니다. 막 다른 골목이라면 도로의 마지막 분기점으로 후퇴하면 분기점 A라고합니다.

이때 왼쪽 두 번째 갈림길을 입력하고 두 번째 갈림길에 들어간 후 막 다른 길을 찾거나 막 다른 곳에서 후퇴 할 때까지 첫 번째 갈림길의 단계를 반복합니다. 포크의 모든 포크를 통과 한 후 출구를 찾기 전에 로프를 따라 뒤로 걸어 가고 포크 A 앞의 교차로 B로 이동 한 후 위의 단계를 반복합니다.

이것이 실제로 지속적인 재귀의 과정이라는 것을 알았는지 모르겠습니다. 그리고 이것이 컴퓨터가 잘하는 것입니다.

위의 미로 걷기 알고리즘은 우리가 자주 말하는 깊이 우선 탐색 알고리즘이고 그 반대는 너비 우선 탐색 알고리즘입니다. 이론적 토대를 가지고 미로를 걷는 작은 프로그램을 구현하기 위해 프로그램을 사용해 봅시다.

미로
생성 미로를 생성하는 알고리즘은 여러 가지가 있으며 일반적으로 사용되는 알고리즘은 재귀 적 역 추적, 재귀 적 분할 및 임의의 Prim 알고리즘입니다. 오늘 우리는 마지막 알고리즘을 사용합니다.

알고리즘의 주요 단계는 다음과 같습니다.
1. 미로의 행과 열은 홀수 여야합니다
. 2. 홀수 행과 홀수 열의 교차점은 도로이고 나머지 점은 벽이고 미로는 다음과 같습니다. 벽으로 둘러싸여 있음
3. 도로 인 셀을 선택한 다음 (이 예에서는 [1,1]을 선택) 인접한 벽을 목록 벽에 넣습니다
. 4. 목록 벽 에 벽이있는 경우 :
4.1. 무작위로 목록에서 벽을 선택합니다. 벽이 두 개의 단위를 분리하는 경우 셀의 한 셀만 방문한 경우
4.1.1, 목록에서 벽을 제거하고 동시에 벽을 엽니 다
4.1.2, 셀을 다음과 같이 표시합니다.
4.1.3 방문 , 방문 하지 않은 셀의 이웃 설정 목록 벽에 벽 추가
4.2. 벽의 양쪽에있는 셀을 방문한 경우 목록에서 벽을 제거합니다.

미로지도를 나타내는 2 차원 배열을 사용하여 Maze 클래스를 정의합니다. 여기서 1은 벽을 나타내고 0은 도로를 나타냅니다. 그런 다음 왼쪽 상단 모서리를 입구로, 오른쪽 하단 모서리를 출구로 초기화합니다. 아래쪽 방향 벡터를 정의합니다.

class Maze : 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) )] for y in range (height)] self.map [1] [0] = 0 # 입구 self.map [height-2] [width-1] = 0 # exit self.visited = [] # right up left downself.dx = [1, 0, -1, 0] self.dy = [0, -1, 0, 1]
다음 단계는 미로의 주 함수를 생성하는 것입니다.

def generate (self) :
start = [1, 1] self.visited.append (start)
wall_list = self.get_neighbor_wall (start) while wall_list :
wall_position = random.choice (wall_list)
neighbor_road = self.get_neighbor_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)
이 함수에는 두 가지 주요 기능이 있습니다. get_neighbor_road (point) 및 deal_with_not_visited () 인접 노드 들어오는 좌표 포인트 포인트, 반환 값은 2 차원 배열이고 후자의 deal_with_not_visited () 함수는 4.1 단계의 논리를 처리합니다.

Prim random 알고리즘은 목록에있는 모든 셀을 무작위로 선택하기 때문에 새로 추가 된 셀과 기존에 추가 된 셀이 선택 될 확률이 동일하기 때문에 더 많은 분기가 있고 생성 된 미로가 더 복잡합니다. 물론 더 자연스럽게 보입니다. 생성 된 미로.
[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]

미로에서 나와 미로
의지도를 얻은 다음 미로를 걷는 첫 번째 기사의 아이디어를 따르십시오. 주요 기능 로직은 다음과 같습니다.

def dfs (self, x, y, path, visit = []) : # outOfIndexif self.is_out_of_index (x, y) : return False # 방문했거나 방문한 경우 wallif [x, y] 또는 self.get_value ([x, y]) == 1 : False 반환


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

분명히 이것은 전형적인 재귀 프로그램입니다. 노드의 좌표가 경계를 벗어난 경우 노드가 방문되었거나 노드가 벽이면 직접 돌아갑니다. 노드는 우리가 찾고있는 경로의 일부가 아니기 때문입니다. 그렇지 않으면 노드가 방문한 노드 및 경로 집합.

그런 다음 노드가 종료이면 프로그램 실행이 종료되고 경로가 발견되었음을 의미합니다. 그렇지 않으면 네 방향 벡터를 횡단하고 노드의 인접 경로를 함수 dfs로 전달한 다음 길을 찾거나 프로그램의 모든 노드가 횡단 될 때까지 위의 단계를 계속합니다.

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]] 미로지도와 경로 경로, 나머지 작업
시각화
이 좌표 점을 렌더링하는 것입니다. 오늘날 우리가 사용하는 시각화 라이브러리는 픽셀 수준 게임을 작성하기위한 Python 라이브러리 인 pyxel입니다.

물론 사용하기 전에이 라이브러리를 설치해야합니다.

Win 사용자는 pip install -U pyxel 명령을 사용하여 직접 설치할 수 있습니다.

Mac 사용자는 다음 명령을 사용하여 설치합니다.

brew install python3 gcc sdl2 sdl2_image gifsicle
pip3 install -U pyxel
먼저 간단한 데모를 살펴 보겠습니다.

워터 마크, size_16, text_QDUxQ1RP5Y2a5a6i, color_FFFFFF, t_100, g_se, x_10, y_10, shadow_90, type_ZmFuZ3poZW5naGVpdGk =

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

App ()
클래스 App의 실행 로직은 update 함수와 draw 함수를 지속적으로 호출하는 것이므로 update 함수에서 객체의 좌표를 업데이트 한 다음 draw 함수에서 화면에 이미지를 그릴 수 있습니다.

이런 식으로 먼저 미로를 그린 다음 dfs 순회 애니메이션을 렌더링합니다.

워터 마크, 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 (width, height)
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) : # 범위 내 x에 대한 미로 그리기 (height) : for y in range (width) :
color = road_color if my_maze.map [x] [y] is 0 else wall_color
pyxel.rect (y * pixel, x * pixel, pixel, pixel, color)
pyxel.rect (0, pixel, pixel, pixel, start_point_color)
pyxel.rect ((width-1) * pixel, (height-2) * pixel, pixel, pixel, end_point_color)

App ()
은 괜찮아 보입니다. 폭과 높이를 각각 37 픽셀과 21 픽셀 그리드로 생성했기 때문에 생성 된 미로는 그리 복잡하지 않습니다. 픽셀이 많으면 복잡 할 것입니다.

다음으로 경로를 렌더링하기 위해 업데이트 기능과 그리기 기능을 수정해야합니다. 작업을 용이하게하기 위해 init 함수에 몇 가지 속성을 추가했습니다.

self.index = 0 self.route = [] # 렌더링 할 경로를 기록하는 데 사용됩니다. self.step = 1 # 단계 길이, 값이 작을수록 속도가 빨라짐, 1 : 매번 그리드 1 개, 10 : 1 / 매번 10 그리드 self .color = start_point_colorself.bfs_route = my_maze.bfs_route ()
여기서 index와 step은 렌더링 속도를 제어하는 ​​데 사용됩니다. draw 함수에서는 index가 매번 1 씩 증가한 다음 나머지 단계가 계산됩니다. 즉, 인덱스가 단계적으로 증가 할 때마다 real_index가 1 씩 증가하고 렌더링 경로가 한 단계 앞으로 나아갑니다.

def draw (self) : # x에 대한 미로 그리기 범위 (높이) : y에 대한 범위 (폭) :
color = road_color if my_maze.map [x] [y]가 0이면 else wall_color
pyxel.rect (y * pixel, x * pixel, pixel, pixel, color)
pyxel.rect (0, pixel, pixel, pixel, start_point_color)
pyxel.rect ((width-1) * pixel, (height-2) * pixel, pixel, pixel, end_point_color) if self.index> 0 : # draw route
offset = pixel / 2 for i in range (len (self.route)-1) :
curr = self.route [i] next = self.route [i + 1] self.color = backtrack_color if curr in self.route [: i] 및 next in self.route [: i] else route_color
pyxel.line (curr [0] + offset, (curr [1] + offset), next [0] + offset, 다음 [1] + 오프셋, 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 = False if not self.death : self.check_death () self.update_route ( ) def check_death (self) : if self.dfs_model and len (self.route) == len (self.dfs_route)-1 : self.death = True
elif not self.dfs_model and 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]]) else : self.route.append ([pixel * self.bfs_route [index] [0], 픽셀 * self.bfs_route [index] [1]])

App () 지금
까지 미로 생성부터 경로 찾기, 경로 시각화에 이르기까지 완전히 깨달았습니다. 주 함수 App ()을 직접 호출하고 S 키보드를 눌러 게임을 시작합니다.

요약
오늘 우리는 깊이 우선 알고리즘을 사용하여 미로를 횡단했습니다. 초보자에게는 재귀 개념이 이해하기 더 어려울 수 있지만 이것은 컴퓨터 사고와 일치합니다. 경험이 깊어짐에 따라 이해는 점점 더 심오합니다.

둘째, 경로 시각화를 실현하기 위해 pyxel 라이브러리를 사용합니다. 어려움은 좌표의 계산 및 업데이트에 있습니다. 세부 정보는 더 복잡하고 복잡합니다. 물론 독자는 다른 라이브러리를 사용하거나 웹 페이지를 직접 사용하여이를 달성 할 수도 있습니다. .
기사 출처 : 웹 게임 http://www.hp91.cn/web game

추천

출처blog.51cto.com/14621511/2679085