大爽Python入门教程 6-8 实例演示二 控制台——走迷宫游戏
大爽Python入门公开课教案 点击查看教程总目录
1 游戏介绍
实现一个控制台版本的走迷宫小游戏。
代码量:100行左右。
迷宫介绍
这是一个迷宫文件maze01.txt
, 如下
########
#____#_E
#_#__#_#
#_#_##_#
S_#____#
########
其中#
是墙,不可穿越,
_
是路,可以在路与路中上下左右移动,不能对角线(斜着)移动。
S
是起点,玩家的初始地点,代表玩家,过程中移动玩家,这个S
也跟着玩家动。
E
是终点,玩家到达这里即为胜利。
进入游戏
运行游戏,
先展示欢迎语。
再展示迷宫面板,
以及提醒用户输入。
如下
Welcome to maze game!
########
#____#_E
#_#__#_#
#_#_##_#
S_#____#
########
Enter command:
游戏中有移动命令和退出游戏命令
移动命令
移动命令的语法为:
方位
+ 步数
:尝试向指定方向移动指定步数,移动中前方有墙则停止。
方位值及意义如下:
w
: 向上移动a
: 向左移动s
: 向下移动d
: 向右移动
步数是一个正整数
举例:
d2
: 尝试向右移动2格。
w3
: 尝试向上移动3格。
对这个迷宫
########
#____#_E
#_#__#_#
#_#_##_#
S_#____#
########
执行d2
命令后如下
########
#____#_E
#_#__#_#
#_#_##_#
_S#____#
########
实际只往右移动了一格,因为再往右就是墙了
再执行w3
命令后如下
########
#S___#_E
#_#__#_#
#_#_##_#
__#____#
########
往上移动了3格
退出命令
退出命令是e
输入该命令,
游戏界面展示结束语并结束
效果如下
Enter command: e
Bye!
找到终点
对于上面展示的迷宫。
找到终点的一系列命令为
d1
w3
d2
s3
d3
w3
d1
找到终点的时候,再次展示面板,
并输出成功信息。
########
#____#_S
#_#__#_#
#_#_##_#
__#____#
########
Win!Successfully walked out of the maze.
整体运行效果
走出该迷宫的命令
Welcome to maze game!
########
#____#_E
#_#__#_#
#_#_##_#
S_#____#
########
Enter command: d1
########
#____#_E
#_#__#_#
#_#_##_#
_S#____#
########
Enter command: w3
########
#S___#_E
#_#__#_#
#_#_##_#
__#____#
########
Enter command: d2
########
#__S_#_E
#_#__#_#
#_#_##_#
__#____#
########
Enter command: s3
########
#____#_E
#_#__#_#
#_#_##_#
__#S___#
########
Enter command: d3
########
#____#_E
#_#__#_#
#_#_##_#
__#___S#
########
Enter command: w3
########
#____#SE
#_#__#_#
#_#_##_#
__#____#
########
Enter command: d1
########
#____#_S
#_#__#_#
#_#_##_#
__#____#
########
Win!Successfully walked out of the maze.
2 初步分析
大概步骤
- 首先要读取迷宫信息,存到一个二维列表
board
中,如下
[
['#', '#', '#', '#', '#', '#', '#', '#'],
['#', '_', '_', '_', '_', '#', '_', 'E'],
['#', '_', '#', '_', '_', '#', '_', '#'],
['#', '_', '#', '_', '#', '#', '_', '#'],
['S', '_', '#', '_', '_', '_', '_', '#'],
['#', '#', '#', '#', '#', '#', '#', '#']
]
- 要得到起点位置,和终点位置,用其行列序号表示。
第一个坐标为行序号,第二个坐标为列序号。上面例子中的
起点位置是[4, 0],
终点位置是[1, 7] - 要想办法实现移动。
- 主函数,通过循环反复接受输入。
- 把迷宫面板展示(输出)出来。
函数设计
对应的,需要以下几个函数
read_maze_file
: 读取文件show_board
: 展示迷宫面板get_rc
: 得到起点或终点的坐标(行列坐标)move
: 实现移动功能main
: 主函数或者说主逻辑
参数分析
这些函数需要哪些参数
read_maze_file(maze_file)
: 迷宫文本文件路径show_board(board)
: 迷宫面板的二位列表get_rc(board, symbol)
:symbol
为起点或终点的符号。move(?)
: 尚不明确。main(maze_file)
: 迷宫文本文件路径。
3 细节分析
移动动作分析
wasd
对应的上下左右的移动怎么实现呢。
w
: 上移,S
的行序号坐标减1,列序号不变。
a
: 左移,S
的行序号不变,列序号坐标减1。
s
: 上移,S
的行序号坐标加1,列序号不变。
d
: 左移,S
的行序号不变,列序号坐标加1。
这些操作本质上就是再S
的原坐标基础上修改行序号和列序号。
如果把一次移动中,行的变化量叫做dr
,列的变化量叫做dc
实现一次变化的代码如下
# 原来的坐标
start = [start_r, start_c]
new_r = start[0] + dr
new_c = start[1] + dc
# 移动后的坐标
new_start = [new_r, new_c]
方向, 和行的变化量dr
,列的变化量dc
之间是一种对应关系。
一个方向,对应一种行变化量dr
和列变化量dc
,
dr
和dc
作为一组,可以存到元组中。
故方向的移动信息DIRECTION
可以定义如下
DIRECTION = {
# direction: (dr, dc)
"w": (-1, 0),
"a": (0, -1),
"s": (1, 0),
"d": (0, 1),
}
移动命令解析
移动命令分为方位
和步数
命令的变量名一般叫做command
或者cmd
(前面的简写)。
这里我们用cmd
方位是命令的第一个字符串。
步数是之后的,通过[1:]
切片操作能从命令字符串中取出步骤字符串。
这里有三个要检查的,
0. 检查命令字符串长度是否大于0。
- 检查方位字符串是否是
wasd
之一 - 再检查步数字符串是否是数字构成,然后转化成
int
。
这部分对应的代码如下
if len(cmd) > 0:
direction = cmd[0]
step = cmd[1:]
if direction in DIRECTION:
drc = DIRECTION[direction]
if step.isdigit():
step = int(step)
主函数流程逻辑
主函数中要做的
- 获取迷宫对应的二维列表
- 获取起点和终点坐标
- 展示欢迎语
- 使用循环,循环中展示面板,接受用户输入,处理用户输入。
4 逐步实现
字符串和其他信息
先在代码开头定义要用的的字符串和和方位移动的信息字典。
如下
WELCOME = "Welcome to maze game!"
ENTER = "Enter command: "
Invalid = "Invalid command"
WIN = "Win!Successfully walked out of the maze."
BYE = "Bye!"
DIRECTION = {
# direction: (dr, dc)
"w": (-1, 0),
"a": (0, -1),
"s": (1, 0),
"d": (0, 1),
}
主函数main
代码如下
def main(maze_file):
# 1. 获取迷宫对应的二维列表
board = read_maze_file(maze_file)
# 2. 获取起点和终点坐标
start = get_rc(board, "S") # r, c
end = get_rc(board, "E") # r, c
# 3. 展示欢迎语
print(WELCOME)
# 4. 使用循环
while True:
# 4-a. 循环中展示面板
show_board(board)
if start == end: # 4-b. 到达终点,胜利并结束游戏
print(WIN)
return
# 4-c. 接受用户输入
cmd = input(ENTER)
cmd = cmd.lower()
# 4-d. 处理用户输入
if len(cmd) > 0:
if cmd[0] == "e": # 4-e. 直接退出游戏
print(BYE)
return
# 4-f. 尝试解析移动命令
direction = cmd[0]
step = cmd[1:]
if direction in DIRECTION:
drc = DIRECTION[direction]
if step.isdigit(): #
# 4-g. 解析成功,移动并直接进入下一轮循环
step = int(step)
move(board, start, drc, step)
continue
# 4-h. 处理中失败了,提醒输入不符合规范。
print(Invalid)
实现到4-g
时,
明确move
函数接受参数为(board, start, drc, step)
。
read_maze_file
代码如下
def read_maze_file(maze_file):
""""""
with open(maze_file, 'r') as f:
fr = f.read()
board = []
lines = fr.split("\n")
for line in lines:
line = line.strip()
if line:
row = [cell for cell in line]
board.append(row)
return board
show_board
代码如下
def show_board(board):
for row in board:
for cell in row:
print(cell, end="")
print()
get_rc
代码如下
def get_rc(board, start_or_end):
r, c = len(board), len(board[1])
for ri in range(r):
for ci in range(c):
if board[ri][ci] == start_or_end:
return [ri, ci]
return None
move
使用循环即可移动多步,但是有个问题要解决。
那就是遇到前方是墙要停止(还有隐藏限制,不能超过迷宫边界),
这个的解决思路为:
先算出下一步位置,再检查下一步是否是墙或边界外,
是墙或边界外,就停止。
否则更新位置为下一步的位置。
移动中,S
字符的移动实现原理为。
移动开始时,把原位置设置为_
。
移动结束时,把当前位置设置为S
。
代码如下
def move(board, start, drc, step):
r, c = len(board), len(board[1])
sr, sc = start
dr, dc = drc
board[sr][sc] = "_"
for i in range(step):
new_r = sr + dr
new_c = sc + dc
if board[new_r][new_c] == "#":
break
if new_r < 0 or new_r >= r or new_c < 0 or new_c >= c:
break
sr = new_r
sc = new_c
start[0] = sr
start[1] = sc
board[sr][sc] = "S"
调用代码
调用代码如下
maze_file = "maze01.txt"
main(maze_file)
5 最终代码
最终代码如下(运行效果第一部分已展示,不再额外展示)。
WELCOME = "Welcome to maze game!"
ENTER = "Enter command: "
Invalid = "Invalid command"
WIN = "Win!Successfully walked out of the maze."
BYE = "Bye!"
DIRECTION = {
# direction: (dr, dc)
"w": (-1, 0),
"a": (0, -1),
"s": (1, 0),
"d": (0, 1),
}
def read_maze_file(maze_file):
""""""
with open(maze_file, 'r') as f:
fr = f.read()
board = []
lines = fr.split("\n")
for line in lines:
line = line.strip()
if line:
row = [cell for cell in line]
board.append(row)
return board
def show_board(board):
for row in board:
for cell in row:
print(cell, end="")
print()
def get_rc(board, start_or_end):
r, c = len(board), len(board[1])
for ri in range(r):
for ci in range(c):
if board[ri][ci] == start_or_end:
return [ri, ci]
return None
def move(board, start, drc, step):
r, c = len(board), len(board[1])
sr, sc = start
dr, dc = drc
board[sr][sc] = "_"
for i in range(step):
new_r = sr + dr
new_c = sc + dc
if board[new_r][new_c] == "#":
break
if new_r < 0 or new_r >= r or new_c < 0 or new_c >= c:
break
sr = new_r
sc = new_c
start[0] = sr
start[1] = sc
board[sr][sc] = "S"
def main(maze_file):
# 1. 获取迷宫对应的二维列表
board = read_maze_file(maze_file)
# 2. 获取起点和终点坐标
start = get_rc(board, "S") # r, c
end = get_rc(board, "E") # r, c
# 3. 展示欢迎语
print(WELCOME)
# 4. 使用循环
while True:
# 4-a. 循环中展示面板
show_board(board)
if start == end: # 4-b. 到达终点,胜利并结束游戏
print(WIN)
return
# 4-c. 接受用户输入
cmd = input(ENTER)
cmd = cmd.lower()
# 4-d. 处理用户输入
if len(cmd) > 0:
if cmd[0] == "e": # 4-e. 直接退出游戏
print(BYE)
return
# 4-f. 尝试解析移动命令
direction = cmd[0]
step = cmd[1:]
if direction in DIRECTION:
drc = DIRECTION[direction]
if step.isdigit(): #
# 4-g. 解析成功,移动并直接进入下一轮循环
step = int(step)
move(board, start, drc, step)
continue
# 4-h. 处理中失败了,提醒输入不符合规范。
print(Invalid)
maze_file = "maze01.txt"
main(maze_file)