生成迷宫(maze)的算法有很多种,论坛上有很多这方面的资料可以参考。 这里使用回溯法(backtracking),主要参考Build a 2-player maze game with Python Part 4 - Coding TidBits
用的是迭代函数(recursive function): 不断寻找周边没有走过的网格(cell), 并随机打通任一边的”墙(wall)". 如果设定SHOW_DRAW = True的话, 可以图像显示整个回溯法过程。 迷宫生成结束后, 图像显示, 并加上一个红球代表玩家, 红球可以走过整个迷宫, 玩法方面, 可以计算红球通过整个迷宫的时间。迷宫的复杂程度可以通过设定其行和列数, 如MAZE_COLS=15, MAZE_ROWS=13
全部代码如下:
import random
import pygame
import numpy as np
#Ref;http://www.danduda.com/blog/2018/11/18/Build-a-2-player-maze-game-with-Python-Part-4/
UP,DOWN=-1,1
LEFT,RIGHT=-2,2
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
MAZE_COLS=15
MAZE_ROWS=13
WALL_SIZE = int(120/max(MAZE_COLS,MAZE_ROWS)) # Pixel size/Wall thickness
WT=WALL_SIZE #used in pygame.time.wait(WT)
CELL_SIZE=4*WALL_SIZE #a cell has only 2 walls: DOWN, RIGHT
MAZE_WIDTH =CELL_SIZE * MAZE_COLS +WALL_SIZE
MAZE_HEIGHT = CELL_SIZE *MAZE_ROWS+ WALL_SIZE
TOPLEFT_X,TOPLEFT_Y = (SCREEN_WIDTH // 2 - MAZE_WIDTH // 2, SCREEN_HEIGHT // 2 - MAZE_HEIGHT // 2)
x_offset = TOPLEFT_X + WALL_SIZE
y_offset = TOPLEFT_Y + WALL_SIZE
WALL_COLOR = (18, 94, 32)
MAZE_COLOR = (255, 255, 255)
UNVISITED_COLOR = (0, 0, 0)
PLAYER_COLOR = (255, 0, 0)
SHOW_DRAW = True
class Cell():
def __init__(self,r,c):
self.r=r
self.c=c
self.visited=False #True-just visited, not open any wall
self.walls={UP:False,DOWN:False,LEFT:False,RIGHT:False}
class Maze():
def __init__(self,row=MAZE_ROWS,col=MAZE_COLS):
self.row=row
self.col=col
self.maze= [[Cell(r,c) for c in range(col)] for r in range(row)]
self.make()
self.player= Player(PLAYER_COLOR, (WALL_SIZE * 3) // 2 )
self.player_sprite = pygame.sprite.RenderPlain(self.player)
def make(self,r0=0,c0=0):
direction = {UP: (-1, 0),DOWN: (1, 0),LEFT:(0, -1),RIGHT:(0, 1)}
current_cell=self.maze[r0][c0]
current_cell.visited=True
visited_stack=[current_cell]
while visited_stack:
current_cell=visited_stack[-1]
un_visited=[] #unvisited neighbours
for d, (dr, dc) in direction.items():
r, c = current_cell.r + dr, current_cell.c + dc
if (0 <= r < self.row) and (0 <= c < self.col):
cell = self.maze[r][c]
if not cell.visited:
un_visited.append((d,cell))
if un_visited:
dc,nc=random.choice(un_visited)
current_cell.walls[dc]=True #this direction is open
nc.visited=True
nc.walls[-dc]=True
visited_stack.append(nc)
elif len(visited_stack):
visited_stack.pop()
if SHOW_DRAW:
self.draw_maze()
pygame.display.update()
pygame.time.wait(WT)
pygame.event.pump()
#self.maze[0][0].walls[LEFT]=True
#self.maze[self.row-1][self.col-1].walls[RIGHT] = True
def draw_maze(self):
screen.fill('black')
pygame.draw.rect(screen, WALL_COLOR, (TOPLEFT_X, TOPLEFT_Y, MAZE_WIDTH, MAZE_HEIGHT))
for c in range(MAZE_COLS): #colums 5 x-c
for r in range(MAZE_ROWS): # rows 3 y-r
cell=self.maze[r][c]
x=c*CELL_SIZE+ x_offset
y=r*CELL_SIZE + y_offset
if cell.visited:
pygame.draw.rect(screen, MAZE_COLOR, (x, y,3*WALL_SIZE, 3*WALL_SIZE))
else:
pygame.draw.rect(screen, UNVISITED_COLOR,(x, y,3*WALL_SIZE, 3*WALL_SIZE))
if cell.walls[DOWN]:
pygame.draw.rect(screen, MAZE_COLOR, (x, y+3*WALL_SIZE ,3*WALL_SIZE, WALL_SIZE))
if cell.walls[RIGHT]:
pygame.draw.rect(screen, MAZE_COLOR, (x+3*WALL_SIZE, y, WALL_SIZE, 3*WALL_SIZE))
def can_move(self, direction):
c=self.player.c
r=self.player.r
return self.maze[r][c].walls[direction]
def move_up(self):
if self.can_move(UP):
self.player.r-=1
def move_right(self):
if self.can_move(RIGHT):
self.player.c+=1
def move_down(self):
if self.can_move(DOWN):
self.player.r+=1
def move_left(self):
if self.can_move(LEFT):
self.player.c-=1
def checkEvents(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.move_left()
if keys[pygame.K_RIGHT]:
self.move_right()
if keys[pygame.K_UP]:
self.move_up()
if keys[pygame.K_DOWN]:
self.move_down()
def update(self):
self.draw_maze()
#self.draw_grid()
self.player.update()
self.player_sprite.draw(screen)
class Player(pygame.sprite.Sprite):
def __init__(self, color, radius, r=0, c=0):
# Call the parent class (Sprite) constructor
super().__init__()
self.r = r
self.c = c
self.image = pygame.Surface([radius * 2, radius * 2])
self.image.fill(MAZE_COLOR)
self.image.set_colorkey(MAZE_COLOR)
pygame.draw.circle(self.image, color, (radius, radius), radius)
self.rect = self.image.get_rect()
self.update()
def update(self):
self.rect.x = x_offset+self.c*CELL_SIZE
self.rect.y = y_offset+self.r*CELL_SIZE
pygame.init()
screen=pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
A=Maze()
while 1:
screen.fill((0,0,0))
A.checkEvents()
A.update()
pygame.display.update()