项目运行基于
python3.11
,pygame2.5.2
的环境,同时,使用了pyinstaller
进行文件的打包,如果你只是想体验推箱子的乐趣,clone仓库后使用仓库推箱子小游戏.exe
即可
仓库地址:推箱子小游戏
1.地图说明
在文件夹map内,你可以向其中自行添加地图,
- 0:代表外围绿色的背景
- 1:wall
- 2:代表蓝色的floor
- 3:箱子
- 4:目的地
- 5:player
在添加完成后,可以在answer中添加对应的答案,1234分别代表上下左右,这样可以给出地图的参考答案。
2.基本代码思路
(1)关于sokoban的类:
我们新建了一个sokoban类,其中可以通过载入的map得到地图,其次,我们对玩家运行的动作进行了规约
def is_valid_move(self, dx, dy):
new_x = self.playerX + dx
new_y = self.playerY + dy
if self.gameMap[new_y][new_x] == '1': #wall
return False
if self.gameMap[new_y][new_x] == '3': #box
# 箱子更改
box_new_x = new_x + dx
box_new_y = new_y + dy
# 判断:箱子的下一个位置是2还是4,4是destination
if self.gameMap[box_new_y][box_new_x] in '24':
self.gameMap[box_new_y][box_new_x] = '3'
if self.gameMapInit[new_y][new_x] == '4':
self.gameMap[new_y][new_x] = '4'
else:
self.gameMap[new_y][new_x] = '2'
self.move.append([1,dx, dy]) #第二种情况是参与了箱子
return True
return False
# 剩余2和5的情况为真,第一种情况是只有player参与了move
self.move.append([0,dx,dy])
return True
(2)关于其他功能(重置,回退,判断胜利)
- 重置
其实就是把目前要的一些参数全置为初始的状态
def reset_game(self):
#回溯到游戏刚开始的状态
self.gameMap = [list(row) for row in self.gameMapInit]
self.playerX = self.playerXInit
self.playerY = self.playerYInit
self.step = 0
self.move.clear()
- 回退
这里做了一个异常处理,当回退列表为空时,他就不能在回退了
def game_back(self):
try:
listTem = self.move.pop()
# 判断回溯的上一个
if listTem[0] == 1:
self.gameMap[self.playerY][self.playerX] = '3'
temY = self.playerY + listTem[2]
temX = self.playerX + listTem[1]
self.gameMap[temY][temX] = self.gameMapInit[temY][temX] if self.gameMapInit[temY][temX] != '3' else '2'
self.playerX -= listTem[1]
self.playerY -= listTem[2]
self.step -= 1
except:
print("已是最新状态")
- 判断胜利
用一个列表存储他的目的地的位置
def win(self):
for i in range(self.destNumber):
if self.gameMap[int(self.destCoordinate[i][1])][int(self.destCoordinate[i][0])] != '3':
return False
return True
3.index主页构造
(1)读取外来的地图
我们用python中的os库,统计了map中有几个文件数目,就代表有几个地图
files = os.listdir("./map")
# 统计文件数量,其实就是有多少关
map_count = len(files)
(2)对于关卡的绘制
def draw_box(op,location):
# 绘制关卡选择
for i in range(map_count):
if op and i == location:
pygame.draw.ellipse(screen, black, input_box[i]) # 画关卡
print_text(screen, font_theme, 57 + (i % 6) * 70, 235 + (i // 6) * 170, f'{i + 1}',white)
else:
pygame.draw.ellipse(screen, black, input_box[i], width=2) #画关卡
print_text(screen,font_theme,57+(i%6)*70, 235+(i//6)*170,f'{i+1}',black)
(3)在main.py新增了参考答案的功能
def get_reference_answer(sokoban):
ans = ""
with open(f'./answer/answer{goal}.txt','r') as f:
ans += f.read()
f.close()
playerXTem = sokoban.playerX
playerYTem = sokoban.playerY
gameMapTem = tuple(tuple(row) for row in sokoban.gameMap)
move = tuple(sokoban.move)
stepTem = sokoban.step
sokoban.reset_game()
for i in range(len(ans)):
for event in pygame.event.get():
continue
if ans[i]== '1':
if sokoban.is_valid_move(0, -1):
sokoban.playerY -= 1
sokoban.step += 1
elif ans[i]== '2':
if sokoban.is_valid_move(0, 1):
sokoban.playerY += 1
sokoban.step += 1
elif ans[i]== '3':
if sokoban.is_valid_move(-1, 0):
sokoban.playerX -= 1
sokoban.step += 1
elif ans[i]== '4':
if sokoban.is_valid_move(1, 0):
sokoban.playerX += 1
sokoban.step += 1
sokoban.draw_map(screen)
print_text(screen, font_step, 380, 10, f"step:{str(sokoban.step)}", white)
if sokoban.win():
print_text(screen, font_theme, 200, 30, "挑战成功", white)
# update the display
pygame.display.update()
pygame.time.delay(200)
pygame.time.delay(1000)
sokoban.playerX = playerXTem
sokoban.playerY = playerYTem
sokoban.gameMap = [list(row) for row in gameMapTem]
sokoban.move = list(move)
sokoban.step = stepTem
4.通过pyinstaller生成了exe文件
pyinstaller --onefile --windowed main.py
- –onefile 选项将所有内容打包到一个独立的 exe 文件中。
- –windowed 选项用于创建没有控制台窗口的 GUI 应用程序。如果你希望保留控制台窗口,可以省略这个选项。
- 运行上述命令后,PyInstaller 会生成几个文件和文件夹:
一个 build 文件夹,包含临时文件。
一个 dist 文件夹,包含打包后的可执行文件。
一个 game.spec 文件,包含打包的配置。
你的可执行文件会在 dist 文件夹中,
- 重命名参数
-n old_name newname.py
- 图标参数
-i fanvico.ico
- 进一步配置(可选)
你可以通过修改 game.spec 文件来进行更多自定义配置。例如,添加数据文件,设置图标等。
如果你需要将数据文件(如图像、声音等)一起打包,你可以修改 spec 文件中的 datas 参数