zoukankan      html  css  js  c++  java
  • 大爽Python入门教程 68 实例演示二 控制台——走迷宫游戏

    大爽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)
    
  • 相关阅读:
    MySQL ------ 触发器(TRIGGER)(二十七)
    MySQL ------ 游标(CURSOR)(二十六)
    小程序:ajax(页面跳转方式、http、ajax的应用)
    小程序:phpStudy(下载与安装、创建数据库)
    redis:key(常用命令、命名规范)
    redis:基础(内存维护策略、redis的开启与退出、RedisDesktopManager的安装与使用)
    小程序:列表(获取json中的数据)
    svn:IDEA与SVN(IDEA删除、检出、更新项目、解决冲突、恢复历史版本)
    svn:IDEA与SVN(配置SVN、IDEA添加项目到SVN)
    java:面向接口编程(解耦)
  • 原文地址:https://www.cnblogs.com/BigShuang/p/15647497.html
Copyright © 2011-2022 走看看