大爽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 初步分析

大概步骤

  1. 首先要读取迷宫信息,存到一个二维列表board中,如下
[
    ['#', '#', '#', '#', '#', '#', '#', '#'],
    ['#', '_', '_', '_', '_', '#', '_', 'E'],
    ['#', '_', '#', '_', '_', '#', '_', '#'],
    ['#', '_', '#', '_', '#', '#', '_', '#'],
    ['S', '_', '#', '_', '_', '_', '_', '#'],
    ['#', '#', '#', '#', '#', '#', '#', '#']
]
  1. 要得到起点位置,和终点位置,用其行列序号表示。
    第一个坐标为行序号,第二个坐标为列序号。上面例子中的
    起点位置是[4, 0],
    终点位置是[1, 7]
  2. 要想办法实现移动。
  3. 主函数,通过循环反复接受输入。
  4. 把迷宫面板展示(输出)出来。

函数设计

对应的,需要以下几个函数

  • 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
drdc作为一组,可以存到元组中。

故方向的移动信息DIRECTION可以定义如下

DIRECTION = {
    # direction: (dr, dc)
    "w": (-1, 0),
    "a": (0, -1),
    "s": (1, 0),
    "d": (0, 1),
}

移动命令解析

移动命令分为方位步数

命令的变量名一般叫做command或者cmd(前面的简写)。
这里我们用cmd
方位是命令的第一个字符串。
步数是之后的,通过[1:]切片操作能从命令字符串中取出步骤字符串。

这里有三个要检查的,
0. 检查命令字符串长度是否大于0。

  1. 检查方位字符串是否是wasd之一
  2. 再检查步数字符串是否是数字构成,然后转化成int

这部分对应的代码如下

if len(cmd) > 0:
    direction = cmd[0]
    step = cmd[1:]
    if direction in DIRECTION:
        drc = DIRECTION[direction]
        if step.isdigit():
            step = int(step)

主函数流程逻辑

主函数中要做的

  1. 获取迷宫对应的二维列表
  2. 获取起点和终点坐标
  3. 展示欢迎语
  4. 使用循环,循环中展示面板,接受用户输入,处理用户输入。

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)
posted @ 2021-12-05 22:41  大爽歌python编程辅导  阅读(411)  评论(0编辑  收藏  举报