实现路径生成
This commit is contained in:
parent
c7f28e7f88
commit
198b6bd506
@ -1,24 +1,24 @@
|
|||||||
import csv
|
import csv
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
class TreeNode:
|
class TreeNode:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fa = None
|
self.fa = None
|
||||||
self.children = []
|
self.children = []
|
||||||
self.pos = None
|
self.pos = None
|
||||||
|
self.final_pos = None
|
||||||
self.val = 0
|
self.val = 0
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.dp = 0
|
self.dp = 0
|
||||||
self.path = []
|
|
||||||
self.dp_path = []
|
|
||||||
|
|
||||||
class SourceCollector:
|
class SourceCollector:
|
||||||
def __init__(self, filename, maze=None):
|
def __init__(self, filename=None, maze=None):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.maze = maze
|
self.maze = maze
|
||||||
self.start_pos = None
|
self.start_pos = None
|
||||||
self.end_pos = None
|
self.end_pos = None
|
||||||
self.path = []
|
self.path = []
|
||||||
|
self.node_path = []
|
||||||
if self.filename:
|
if self.filename:
|
||||||
self.maze = []
|
self.maze = []
|
||||||
with open(f"{self.filename}",'r') as f:
|
with open(f"{self.filename}",'r') as f:
|
||||||
@ -31,6 +31,8 @@ class SourceCollector:
|
|||||||
else:
|
else:
|
||||||
t.append(i)
|
t.append(i)
|
||||||
self.maze.append(t)
|
self.maze.append(t)
|
||||||
|
else:
|
||||||
|
self.maze = maze
|
||||||
self.rowNums = len(self.maze)
|
self.rowNums = len(self.maze)
|
||||||
self.colNums = len(self.maze[0])
|
self.colNums = len(self.maze[0])
|
||||||
|
|
||||||
@ -44,99 +46,153 @@ class SourceCollector:
|
|||||||
|
|
||||||
def dfs_show(self,u):
|
def dfs_show(self,u):
|
||||||
if u.id != 0:
|
if u.id != 0:
|
||||||
print(f"id: {u.id} , fa:{u.fa.id} , val:{u.val}")
|
print(f"id: {u.id} , fa:{u.fa.id} , val:{u.val} , pos:{u.pos}")
|
||||||
else:
|
else:
|
||||||
print(f"id: {u.id} , val:{u.val}")
|
print(f"id: {u.id} , val:{u.val} , pos:{u.pos}")
|
||||||
print(u.path)
|
|
||||||
for child in u.children:
|
for child in u.children:
|
||||||
|
|
||||||
self.dfs_show(child)
|
self.dfs_show(child)
|
||||||
|
|
||||||
def outofmap(self,x,y):
|
|
||||||
return x < 0 or y < 0 or x > self.rowNums or y > self.colNums
|
|
||||||
def build_a_tree(self):
|
def build_a_tree(self):
|
||||||
sn = TreeNode()
|
cnt = 0
|
||||||
sn.pos = self.start_pos
|
root = TreeNode()
|
||||||
id = 0
|
root.pos = self.start_pos
|
||||||
sn.id = id
|
root.id = 0
|
||||||
sn.val = 0
|
root.val = 0
|
||||||
sn.path = [sn.pos]
|
root.fa = None
|
||||||
|
|
||||||
|
queue = deque([(self.start_pos[0], self.start_pos[1], root)])
|
||||||
st = [[False] * self.colNums for _ in range(self.rowNums)]
|
st = [[False] * self.colNums for _ in range(self.rowNums)]
|
||||||
qsk = []
|
st[self.start_pos[0]][self.start_pos[1]] = True
|
||||||
sx, sy = self.start_pos
|
|
||||||
st[sx][sy] = True
|
|
||||||
qsk.append((self.start_pos[0],self.start_pos[1], sn,[]))
|
|
||||||
dx = [-1, 0, 1, 0]
|
dx = [-1, 0, 1, 0]
|
||||||
dy = [0, -1, 0, 1]
|
dy = [0, -1, 0, 1]
|
||||||
|
|
||||||
while qsk:
|
while queue:
|
||||||
x, y, u,path = qsk.pop()
|
x, y, parent = queue.popleft()
|
||||||
for _x, _y in zip(dx,dy):
|
for i in range(4):
|
||||||
nx, ny = x + _x, y + _y
|
nx, ny = x + dx[i], y + dy[i]
|
||||||
if self.outofmap(nx,ny):
|
if self.outofmap(nx, ny) or st[nx][ny]:
|
||||||
continue
|
|
||||||
if self.maze[nx][ny] == '1' or st[nx][ny]:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if self.maze[nx][ny] != '1':
|
||||||
st[nx][ny] = True
|
st[nx][ny] = True
|
||||||
to = self.maze[nx][ny]
|
|
||||||
new_path = path + [(nx,ny)]
|
|
||||||
if to.startswith('g') or to.startswith('t'):
|
|
||||||
new_node = TreeNode()
|
|
||||||
id+=1
|
|
||||||
new_node.id = id
|
|
||||||
new_node.pos = (nx, ny)
|
|
||||||
new_node.fa = u
|
|
||||||
num_str = to[1:]
|
|
||||||
new_node.path = new_path
|
|
||||||
try:
|
|
||||||
if to.startswith('g'):
|
|
||||||
new_node.val = int(num_str)
|
|
||||||
else:
|
|
||||||
new_node.val = -int(num_str)
|
|
||||||
except ValueError:
|
|
||||||
print("wa ! ")
|
|
||||||
u.children.append(new_node)
|
|
||||||
qsk.append((nx, ny, new_node,[]))
|
|
||||||
else:
|
|
||||||
qsk.append((nx, ny, u,new_path))
|
|
||||||
|
|
||||||
return sn
|
new_node = TreeNode()
|
||||||
|
new_node.pos = (nx, ny)
|
||||||
|
new_node.fa = parent
|
||||||
|
cnt+=1
|
||||||
|
new_node.id = cnt
|
||||||
|
if self.maze[nx][ny].startswith('g'):
|
||||||
|
new_node.val = int(self.maze[nx][ny][1:])
|
||||||
|
elif self.maze[nx][ny].startswith('t'):
|
||||||
|
new_node.val =-1 *int(self.maze[nx][ny][1:])
|
||||||
|
parent.children.append(new_node)
|
||||||
|
queue.append((nx, ny, new_node))
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def outofmap(self,x,y):
|
||||||
|
return x < 0 or y < 0 or x > self.rowNums or y > self.colNums
|
||||||
|
def getlca(self,u, v):
|
||||||
|
def get_path_to_root(node):
|
||||||
|
path = []
|
||||||
|
while node:
|
||||||
|
path.append(node)
|
||||||
|
node = node.fa
|
||||||
|
return path
|
||||||
|
path_u = get_path_to_root(u)
|
||||||
|
path_v = get_path_to_root(v)
|
||||||
|
|
||||||
|
path_u.reverse()
|
||||||
|
path_v.reverse()
|
||||||
|
|
||||||
|
lca = None
|
||||||
|
for i in range(min(len(path_u),len(path_v))):
|
||||||
|
if path_u[i] == path_v[i]:
|
||||||
|
lca = path_u[i]
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if lca is None:
|
||||||
|
return []
|
||||||
|
u_to_lca = []
|
||||||
|
node = u
|
||||||
|
while node != lca:
|
||||||
|
u_to_lca.append(node.pos)
|
||||||
|
node = node.fa
|
||||||
|
|
||||||
|
lca_to_v = []
|
||||||
|
node_list = []
|
||||||
|
node = v
|
||||||
|
while node != lca:
|
||||||
|
node_list.append(node)
|
||||||
|
node = node.fa
|
||||||
|
node_list.append(lca)
|
||||||
|
node_list.reverse()
|
||||||
|
for node in node_list:
|
||||||
|
lca_to_v.append(node.pos)
|
||||||
|
|
||||||
|
full_path = u_to_lca + lca_to_v
|
||||||
|
return full_path
|
||||||
def dfs(self,sn):
|
def dfs(self,sn):
|
||||||
|
|
||||||
sn.dp = sn.val
|
sn.dp = sn.val
|
||||||
for child in sn.children:
|
sn.final_pos = sn.pos
|
||||||
# print(f"cur : {child.pos} , fa : {child.fa.pos} , childrens : {child.path}")
|
sn.path= [sn.pos]
|
||||||
|
cur = None
|
||||||
|
for idx,child in enumerate(sn.children):
|
||||||
self.dfs(child)
|
self.dfs(child)
|
||||||
if child.dp > 0:
|
if child.dp > 0:
|
||||||
sn.dp += child.dp
|
sn.dp += child.dp
|
||||||
sn.dp_path += child.path + child.dp_path + child.path[::-1]
|
if cur != None:
|
||||||
|
sn.path.extend(self.getlca(cur,child))
|
||||||
|
sn.path.extend(child.path)
|
||||||
|
cur = child
|
||||||
|
if idx == len(sn.children)-1:
|
||||||
|
sn.final_pos = cur.final_pos
|
||||||
|
|
||||||
|
|
||||||
def find_path(self):
|
|
||||||
self.path = []
|
def get_path(self):
|
||||||
sn = self.build_a_tree()
|
|
||||||
self.dfs(sn)
|
|
||||||
return self.path
|
return self.path
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
sn = self.build_a_tree()
|
||||||
|
# self.dfs_show(sn)
|
||||||
|
self.dfs(sn)
|
||||||
|
self.path = sn.path
|
||||||
|
|
||||||
|
|
||||||
|
def output_list(self):
|
||||||
|
copy_maze = self.maze
|
||||||
|
for idx, (y, x) in enumerate(self.path):
|
||||||
|
if copy_maze[y][x].startswith('s') | copy_maze[y][x].startswith('e'):
|
||||||
|
continue
|
||||||
|
if copy_maze[y][x].startswith('g') | copy_maze[y][x].startswith('t'):
|
||||||
|
copy_maze[y][x] = f"{copy_maze[y][x]}p{idx}"
|
||||||
|
continue
|
||||||
|
copy_maze[y][x] = f"p{idx}"
|
||||||
|
|
||||||
|
return copy_maze
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
obj = SourceCollector(filename="maze.csv")
|
obj = SourceCollector(filename="maze.csv")
|
||||||
sn = obj.build_a_tree()
|
obj.run()
|
||||||
# obj.dfs_show(sn)
|
path = obj.get_path()
|
||||||
obj.dfs(sn)
|
for i in path:
|
||||||
print(len(sn.dp_path))
|
print(i)
|
||||||
print(sn.pos)
|
# print(sn.pos)
|
||||||
pre = sn.pos
|
# pre = sn.pos
|
||||||
for _ in sn.dp_path:
|
# for _ in sn.dp_path:
|
||||||
dx,dy = _[0] - pre[0],_[1]-pre[1]
|
# dx,dy = _[0] - pre[0],_[1]-pre[1]
|
||||||
if dx > 0:
|
# if dx > 0:
|
||||||
print("down")
|
# print("down")
|
||||||
elif dx < 0:
|
# elif dx < 0:
|
||||||
print("up")
|
# print("up")
|
||||||
elif dy > 0:
|
# elif dy > 0:
|
||||||
print("right")
|
# print("right")
|
||||||
elif dy < 0:
|
# elif dy < 0:
|
||||||
print("left")
|
# print("left")
|
||||||
pre = _
|
# pre = _
|
||||||
|
|
||||||
|
|
6
main.py
6
main.py
@ -7,10 +7,10 @@ from draw import Button, Toast
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
UI_HEIGHT = 800
|
UI_HEIGHT = 1000
|
||||||
UI_WIDTH = 1100
|
UI_WIDTH = 1500
|
||||||
|
|
||||||
MAZE_SIZE = 800
|
MAZE_SIZE = 150
|
||||||
WALL_SIZE = 50
|
WALL_SIZE = 50
|
||||||
FPS = 120
|
FPS = 120
|
||||||
|
|
||||||
|
28
maze.csv
28
maze.csv
@ -1,16 +1,16 @@
|
|||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
1,0,1,0,0,0,1,e,0,0,1,t11,1,0,0,1
|
1,0,0,1,0,0,0,0,0,l25,0,t10,0,1,0,1
|
||||||
1,0,1,1,1,t17,1,0,1,0,1,0,0,0,t6,1
|
1,1,0,1,1,1,1,1,1,1,1,1,l15,1,t7,1
|
||||||
1,0,1,0,1,0,0,0,1,0,1,0,1,0,g25,1
|
1,0,0,g26,0,e,t20,s,0,0,0,0,0,0,l25,1
|
||||||
1,0,1,0,0,0,1,0,1,0,1,0,1,1,1,1
|
1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1
|
||||||
1,0,1,b89,1,0,1,0,1,0,1,0,1,g30,0,1
|
1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1
|
||||||
1,0,0,0,1,0,1,0,1,0,1,0,1,l11,0,1
|
1,0,1,1,1,1,0,1,1,1,1,1,g24,1,0,1
|
||||||
1,g30,1,0,1,0,1,0,1,0,1,0,0,g21,0,1
|
1,0,0,1,0,t16,0,0,1,0,0,0,0,1,l16,1
|
||||||
1,0,1,0,1,s,1,t11,1,1,1,0,1,0,0,1
|
1,1,1,1,1,1,1,1,1,1,1,0,1,1,g15,1
|
||||||
1,0,1,0,1,t14,1,0,0,0,1,0,1,0,0,1
|
1,0,t8,1,0,1,0,0,g12,0,1,0,0,1,0,1
|
||||||
1,1,1,1,1,1,1,0,1,1,1,0,1,0,0,1
|
1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,1
|
||||||
1,0,0,g21,0,0,0,0,0,0,1,0,1,0,0,1
|
1,0,0,0,0,0,0,0,1,0,1,0,t15,1,l16,1
|
||||||
1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1
|
1,0,0,1,0,1,0,t16,1,0,1,0,0,1,0,1
|
||||||
1,0,l23,0,0,0,g17,0,0,g22,1,0,0,l26,0,1
|
1,1,0,1,1,1,1,1,1,0,1,0,0,1,0,1
|
||||||
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
|
1,0,0,0,0,0,0,0,1,0,t5,b89,0,1,0,1
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
|
48
maze.py
48
maze.py
@ -1,5 +1,6 @@
|
|||||||
import pygame
|
import pygame
|
||||||
from maze_generator import MazeGenerator
|
from maze_generator import MazeGenerator
|
||||||
|
from SourceCollector import SourceCollector
|
||||||
from tanxin import *
|
from tanxin import *
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -18,9 +19,10 @@ class Maze:
|
|||||||
def generate(self):
|
def generate(self):
|
||||||
seed = int(time.time() * 1000) % (2**32)
|
seed = int(time.time() * 1000) % (2**32)
|
||||||
self.generater.generate(seed=seed)
|
self.generater.generate(seed=seed)
|
||||||
# player = GreedyPlayer(generater.maze)
|
obj = SourceCollector(maze=self.generater.maze)
|
||||||
# player.find_path()
|
obj.run()
|
||||||
self.grid = self.generater.maze
|
self.grid = obj.output_list()
|
||||||
|
print(self.grid)
|
||||||
|
|
||||||
def export_to_csv(self, filename):
|
def export_to_csv(self, filename):
|
||||||
self.generater.export_to_csv(filename=filename)
|
self.generater.export_to_csv(filename=filename)
|
||||||
@ -39,8 +41,38 @@ class Maze:
|
|||||||
screen.blit(wall_texture, (x * tile_size, y * tile_size))
|
screen.blit(wall_texture, (x * tile_size, y * tile_size))
|
||||||
if self.grid[y][x].startswith('g'):
|
if self.grid[y][x].startswith('g'):
|
||||||
screen.blit(coin_texture, (x * tile_size, y * tile_size))
|
screen.blit(coin_texture, (x * tile_size, y * tile_size))
|
||||||
|
|
||||||
|
font = pygame.font.SysFont(None, tile_size // 2)
|
||||||
|
path = self.grid[y][x].rfind('p')
|
||||||
|
if path == -1:
|
||||||
|
continue
|
||||||
|
path = self.grid[y][x][path+1:]
|
||||||
|
|
||||||
|
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
|
||||||
|
radius = tile_size // 3
|
||||||
|
|
||||||
|
text = font.render(path, True, (255, 255, 255))
|
||||||
|
text_rect = text.get_rect(center=center)
|
||||||
|
screen.blit(text, text_rect)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if self.grid[y][x].startswith('t'):
|
if self.grid[y][x].startswith('t'):
|
||||||
screen.blit(trap_texture, (x * tile_size, y * tile_size))
|
screen.blit(trap_texture, (x * tile_size, y * tile_size))
|
||||||
|
|
||||||
|
font = pygame.font.SysFont(None, tile_size // 2)
|
||||||
|
path = self.grid[y][x].rfind('p')
|
||||||
|
if path == -1:
|
||||||
|
continue
|
||||||
|
path = self.grid[y][x][path+1:]
|
||||||
|
|
||||||
|
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
|
||||||
|
radius = tile_size // 3
|
||||||
|
|
||||||
|
text = font.render(path, True, (255, 255, 255))
|
||||||
|
text_rect = text.get_rect(center=center)
|
||||||
|
screen.blit(text, text_rect)
|
||||||
|
|
||||||
if self.grid[y][x].startswith('|') or self.grid[y][x].startswith('-'):
|
if self.grid[y][x].startswith('|') or self.grid[y][x].startswith('-'):
|
||||||
font = pygame.font.SysFont(None, tile_size // 2)
|
font = pygame.font.SysFont(None, tile_size // 2)
|
||||||
num = 12
|
num = 12
|
||||||
@ -73,6 +105,16 @@ class Maze:
|
|||||||
text = font.render(text, True, (0, 0, 0))
|
text = font.render(text, True, (0, 0, 0))
|
||||||
text_rect = text.get_rect(center=center)
|
text_rect = text.get_rect(center=center)
|
||||||
screen.blit(text, text_rect)
|
screen.blit(text, text_rect)
|
||||||
|
if self.grid[y][x].startswith('p'):
|
||||||
|
font = pygame.font.SysFont(None, tile_size // 2)
|
||||||
|
text = self.grid[y][x][1:]
|
||||||
|
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
|
||||||
|
radius = tile_size // 3
|
||||||
|
pygame.draw.circle(screen, (255, 215, 0), center, radius)
|
||||||
|
if text:
|
||||||
|
text = font.render(text, True, (0, 0, 0))
|
||||||
|
text_rect = text.get_rect(center=center)
|
||||||
|
screen.blit(text, text_rect)
|
||||||
|
|
||||||
|
|
||||||
pygame.draw.line(screen, (0, 0, 0), (self.maze_size, 0), (self.maze_size, self.maze_size), 5)
|
pygame.draw.line(screen, (0, 0, 0), (self.maze_size, 0), (self.maze_size, self.maze_size), 5)
|
||||||
|
@ -22,82 +22,82 @@ class MazeGenerator:
|
|||||||
self.name = name # 迷宫名称
|
self.name = name # 迷宫名称
|
||||||
self.special_elements = [] # 存储特殊元素的位置和值
|
self.special_elements = [] # 存储特殊元素的位置和值
|
||||||
self.history_mazes = []
|
self.history_mazes = []
|
||||||
|
|
||||||
def initialize_maze(self):
|
def initialize_maze(self):
|
||||||
"""初始化迷宫,四周设置为墙"""
|
"""初始化迷宫,全部填充为墙"""
|
||||||
self.maze = [[self.ROUTE for _ in range(self.size)] for _ in range(self.size)]
|
self.maze = [[self.WALL for _ in range(self.size)] for _ in range(self.size)]
|
||||||
for i in range(self.size):
|
|
||||||
self.maze[0][i] = self.WALL
|
|
||||||
self.maze[i][0] = self.WALL
|
|
||||||
self.maze[self.size - 1][i] = self.WALL
|
|
||||||
self.maze[i][self.size - 1] = self.WALL
|
|
||||||
|
|
||||||
def create_maze(self, x1, y1, x2, y2):
|
def create_maze(self, x1, y1, x2, y2):
|
||||||
"""递归分割法生成迷宫"""
|
"""递归分割法生成迷宫,墙壁始终在偶数坐标"""
|
||||||
if x2 - x1 < 2 or y2 - y1 < 2:
|
def getWallIndex(start, length):
|
||||||
return
|
assert length >= 3
|
||||||
|
wall_index = random.randint(start + 1, start + length - 2)
|
||||||
|
if wall_index % 2 == 1:
|
||||||
|
wall_index -= 1
|
||||||
|
return wall_index
|
||||||
|
|
||||||
x = x1 + 1 + random.randint(0, (x2 - x1 - 2))
|
def isValid(x, y):
|
||||||
y = y1 + 1 + random.randint(0, (y2 - y1 - 2))
|
return 0 <= x < self.size and 0 <= y < self.size
|
||||||
|
|
||||||
# 画墙
|
def isMovable(x, y):
|
||||||
for i in range(x1, x2 + 1):
|
return self.maze[y][x] != self.WALL
|
||||||
self.maze[i][y] = self.WALL
|
|
||||||
for i in range(y1, y2 + 1):
|
|
||||||
self.maze[x][i] = self.WALL
|
|
||||||
|
|
||||||
# 递归分割四个区域
|
def generateHoles(x, y, width, height, wall_x, wall_y):
|
||||||
self.create_maze(x1, y1, x - 1, y - 1)
|
holes = []
|
||||||
self.create_maze(x + 1, y + 1, x2, y2)
|
hole_entrys = [
|
||||||
self.create_maze(x + 1, y1, x2, y - 1)
|
(random.randint(x, wall_x - 1), wall_y),
|
||||||
self.create_maze(x1, y + 1, x - 1, y2)
|
(random.randint(wall_x + 1, x + width - 1), wall_y),
|
||||||
|
(wall_x, random.randint(y, wall_y - 1)),
|
||||||
# 随机打通三面墙
|
(wall_x, random.randint(wall_y + 1, y + height - 1))
|
||||||
r = [0, 0, 0, 0]
|
]
|
||||||
r[random.randint(0, 3)] = 1
|
margin_entrys = [
|
||||||
|
(x, wall_y), (x + width - 1, wall_y),
|
||||||
|
(wall_x, y), (wall_x, y + height - 1)
|
||||||
|
]
|
||||||
|
adjacent_entrys = [
|
||||||
|
(x - 1, wall_y), (x + width, wall_y),
|
||||||
|
(wall_x, y - 1), (wall_x, y + height)
|
||||||
|
]
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
if r[i] == 0:
|
adj_x, adj_y = adjacent_entrys[i]
|
||||||
rx, ry = x, y
|
if isValid(adj_x, adj_y) and isMovable(adj_x, adj_y):
|
||||||
if i == 0: # 上方
|
mx, my = margin_entrys[i]
|
||||||
while True:
|
self.maze[my][mx] = self.ROUTE
|
||||||
rx = x1 + random.randint(0, (x - x1 - 1))
|
else:
|
||||||
wall_count = sum([
|
holes.append(hole_entrys[i])
|
||||||
(int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]),
|
ignore_hole = random.randint(0, len(holes) - 1)
|
||||||
(int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1])
|
for i in range(len(holes)):
|
||||||
])
|
if i != ignore_hole:
|
||||||
if wall_count <= 2 * (int)(self.WALL):
|
hx, hy = holes[i]
|
||||||
break
|
self.maze[hy][hx] = self.ROUTE
|
||||||
elif i == 1: # 右侧
|
|
||||||
while True:
|
|
||||||
ry = y + 1 + random.randint(0, (y2 - y - 1))
|
|
||||||
wall_count = sum([
|
|
||||||
(int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]),
|
|
||||||
(int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1])
|
|
||||||
])
|
|
||||||
if wall_count <= 2 * (int)(self.WALL):
|
|
||||||
break
|
|
||||||
elif i == 2: # 下方
|
|
||||||
while True:
|
|
||||||
rx = x + 1 + random.randint(0, (x2 - x - 1))
|
|
||||||
wall_count = sum([
|
|
||||||
(int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]),
|
|
||||||
(int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1])
|
|
||||||
])
|
|
||||||
if wall_count <= 2 * (int)(self.WALL):
|
|
||||||
break
|
|
||||||
elif i == 3: # 左侧
|
|
||||||
while True:
|
|
||||||
ry = y1 + random.randint(0, (y - y1 - 1))
|
|
||||||
wall_count = sum([
|
|
||||||
(int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]),
|
|
||||||
(int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1])
|
|
||||||
])
|
|
||||||
if wall_count <= 2 * (int)(self.WALL):
|
|
||||||
break
|
|
||||||
self.maze[rx][ry] = self.ROUTE
|
|
||||||
self.history_mazes.append(self.maze)
|
|
||||||
|
|
||||||
# self.history_mazes.append(self.maze)
|
def recursiveDivision(x, y, width, height):
|
||||||
|
if width < 3 or height < 3:
|
||||||
|
return
|
||||||
|
wall_x = getWallIndex(x, width)
|
||||||
|
wall_y = getWallIndex(y, height)
|
||||||
|
for i in range(x, x + width):
|
||||||
|
self.maze[wall_y][i] = self.WALL
|
||||||
|
for i in range(y, y + height):
|
||||||
|
self.maze[i][wall_x] = self.WALL
|
||||||
|
generateHoles(x, y, width, height, wall_x, wall_y)
|
||||||
|
recursiveDivision(x, y, wall_x - x, wall_y - y)
|
||||||
|
recursiveDivision(x, wall_y + 1, wall_x - x, y + height - wall_y - 1)
|
||||||
|
recursiveDivision(wall_x + 1, y, x + width - wall_x - 1, wall_y - y)
|
||||||
|
recursiveDivision(wall_x + 1, wall_y + 1, x + width - wall_x - 1, y + height - wall_y - 1)
|
||||||
|
# 先全部通路
|
||||||
|
self.maze = [[self.ROUTE for _ in range(self.size)] for _ in range(self.size)]
|
||||||
|
# 四周加墙
|
||||||
|
for x in range(self.size):
|
||||||
|
self.maze[0][x] = self.WALL
|
||||||
|
self.maze[self.size - 1][x] = self.WALL
|
||||||
|
for y in range(self.size):
|
||||||
|
self.maze[y][0] = self.WALL
|
||||||
|
self.maze[y][self.size - 1] = self.WALL
|
||||||
|
# 递归分割
|
||||||
|
try:
|
||||||
|
recursiveDivision(1, 1, self.size - 2, self.size - 2)
|
||||||
|
except:
|
||||||
|
self.create_maze(x1, y1, x2, y2) # 如果递归失败,重新尝试
|
||||||
def set_random_exits(self):
|
def set_random_exits(self):
|
||||||
"""随机设置迷宫入口和出口"""
|
"""随机设置迷宫入口和出口"""
|
||||||
available = self.get_available_cells()
|
available = self.get_available_cells()
|
||||||
@ -171,6 +171,7 @@ class MazeGenerator:
|
|||||||
random.seed(seed or random.randint(0, 1000))
|
random.seed(seed or random.randint(0, 1000))
|
||||||
self.initialize_maze()
|
self.initialize_maze()
|
||||||
self.create_maze(1, 1, self.size - 2, self.size - 2)
|
self.create_maze(1, 1, self.size - 2, self.size - 2)
|
||||||
|
self.patch_maze_edges() # 自动修正边界
|
||||||
self.place_special_elements(boss_count, traps_range, mechanisms_range, skill_traps)
|
self.place_special_elements(boss_count, traps_range, mechanisms_range, skill_traps)
|
||||||
print(f"成功生成迷宫: {self.name}")
|
print(f"成功生成迷宫: {self.name}")
|
||||||
|
|
||||||
@ -229,27 +230,77 @@ class MazeGenerator:
|
|||||||
for row in reader:
|
for row in reader:
|
||||||
l.append(row)
|
l.append(row)
|
||||||
return l
|
return l
|
||||||
|
def patch_maze_edges(self):
|
||||||
|
|
||||||
|
"""只在不破坏联通性的前提下,修正右侧和下侧边界的多余通路(加墙)"""
|
||||||
|
n = self.size
|
||||||
|
if n %2==1:
|
||||||
|
return
|
||||||
|
candidates = []
|
||||||
|
# 倒数第二列(右侧)
|
||||||
|
x = n - 2
|
||||||
|
for y in range(1, n-1):
|
||||||
|
if self.maze[y][x] == self.ROUTE:
|
||||||
|
right = self.maze[y][x+1] if x+1 < n else self.WALL
|
||||||
|
down = self.maze[y+1][x] if y+1 < n else self.WALL
|
||||||
|
if right == self.ROUTE or down == self.ROUTE:
|
||||||
|
candidates.append((y, x))
|
||||||
|
# 倒数第
|
||||||
|
# 二行(下侧)
|
||||||
|
y = n - 2
|
||||||
|
for x in range(1, n-1):
|
||||||
|
if self.maze[y][x] == self.ROUTE:
|
||||||
|
right = self.maze[y][x+1] if x+1 < n else self.WALL
|
||||||
|
down = self.maze[y+1][x] if y+1 < n else self.WALL
|
||||||
|
if right == self.ROUTE or down == self.ROUTE:
|
||||||
|
candidates.append((y, x))
|
||||||
|
# 逐个尝试加墙,确保联通
|
||||||
|
for y, x in candidates:
|
||||||
|
old = self.maze[y][x]
|
||||||
|
self.maze[y][x] = self.WALL
|
||||||
|
if not self.is_maze_connected():
|
||||||
|
self.maze[y][x] = old # 恢复
|
||||||
|
|
||||||
|
def is_maze_connected(self):
|
||||||
|
"""检查迷宫是否连通(深度优先搜索)"""
|
||||||
|
visited = [[False] * self.size for _ in range(self.size)]
|
||||||
|
|
||||||
|
def dfs(x, y):
|
||||||
|
if x < 0 or x >= self.size or y < 0 or y >= self.size:
|
||||||
|
return
|
||||||
|
if visited[y][x] or self.maze[y][x] == self.WALL:
|
||||||
|
return
|
||||||
|
visited[y][x] = True
|
||||||
|
dfs(x + 1, y)
|
||||||
|
dfs(x - 1, y)
|
||||||
|
dfs(x, y + 1)
|
||||||
|
dfs(x, y - 1)
|
||||||
|
|
||||||
|
# 从左上角开始搜索
|
||||||
|
dfs(1, 1)
|
||||||
|
|
||||||
|
# 检查是否所有通路都被访问
|
||||||
|
for y in range(1, self.size - 1):
|
||||||
|
for x in range(1, self.size - 1):
|
||||||
|
if self.maze[y][x] == self.ROUTE and not visited[y][x]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# 示例1: 生成带技能陷阱的迷宫
|
# 示例1: 生成带技能陷阱的迷宫
|
||||||
generator = MazeGenerator(
|
generator = MazeGenerator(
|
||||||
size=20,
|
size=20,
|
||||||
filename="dungeon_maze.csv",
|
filename="dungeon_maze.csv"
|
||||||
name="龙脊峡谷迷宫"
|
|
||||||
)
|
)
|
||||||
generator.generate(
|
generator.generate(
|
||||||
seed=666,
|
seed=666
|
||||||
boss_count=2,
|
|
||||||
traps_range=(5, 10),
|
|
||||||
mechanisms_range=(3, 7),
|
|
||||||
skill_traps=8
|
|
||||||
)
|
)
|
||||||
generator.print_maze()
|
generator.print_maze()
|
||||||
generator.export_to_csv()
|
generator.export_to_csv("d.csv")
|
||||||
|
for i in generator.history_mazes:
|
||||||
|
print(i[3:])
|
||||||
|
|
||||||
|
reader = MazeGenerator(size=1, filename="d.csv")
|
||||||
reader = MazeGenerator(size=1, filename="dungeon_maze.csv")
|
|
||||||
if reader.read_from_csv():
|
if reader.read_from_csv():
|
||||||
print("\n读取的迷宫:")
|
print("\n读取的迷宫:")
|
||||||
reader.print_maze()
|
reader.print_maze()
|
||||||
|
Loading…
Reference in New Issue
Block a user