大爽Python入门练习题 52 实践练习二 推箱子游戏(控制台实现)

大爽Python入门练习题总目录

第五章 中期实战 项目练习 二

题目

情景介绍

推箱子游戏(英文一般叫sokoban)
实现一个控制台版本的推箱子游戏。
推箱子游戏地图信息存在sokoban.txt中,具体如下

#######
#.....#
#...+.#
##..+.#
#--.#P#
#######

其中

  • P代表玩家,可以自由移动。

  • +代表箱子,可以被玩家推动。

  • -代表目的地,要把箱子移动到目的地。
    -无法移动,被箱子覆盖后,该处变成O
    (箱子被移开后,原地再次显示-)

  • .代表路,玩家可以在路与路之间上下左右移动(无法斜着移动)

  • #代表墙,无法穿越。

补充说明:
玩家移动时,前面有箱子则可以向前推动箱子。
如果箱子前面有墙或者其他箱子,则无法推动。

主界面

展示欢迎语,展示游戏面板,读取用户输入。

英文版本

---------------
Welcome to Sokoban!
#######
#.....#
#...+.#
##..+.#
#--.#P#
#######
Enter command:

游戏中有移动命令和退出游戏命令

移动命令

移动命令的语法为:
方位 + 步数:尝试向指定方向移动指定步数,移动中前方有墙则停止。

方位值及意义如下:

  • w: 向上移动
  • a: 向左移动
  • s: 向下移动
  • d: 向右移动

步数是一个正整数

举例:
d2: 尝试向右移动2格。
w3: 尝试向上移动3格。

退出命令

退出命令是e

输入该命令,
游戏界面展示结束语并结束

效果如下

Enter command: e
Bye!

找到终点

对于上面展示的情况。
找到终点的一系列命令为

w2
a2
w1
a1
s2
d1
s1
a1
w2
d3
s1
a2
w1
a1
s1

找到终点的时候,再次展示游戏面板,
并输出成功信息。

#######
#.....#
#.....#
##P...#
#OO.#.#
#######
Win!

整体运行效果

使用上一部分的正确命令,运行效果如下

Welcome to Sokoban!
#######
#.....#
#...+.#
##..+.#
#--.#P#
#######
Enter command: w2
#######
#.....#
#...+P#
##..+.#
#--.#.#
#######
Enter command: a2
#######
#.....#
#.+P..#
##..+.#
#--.#.#
#######
Enter command: w1
#######
#..P..#
#.+...#
##..+.#
#--.#.#
#######
Enter command: a1
#######
#.P...#
#.+...#
##..+.#
#--.#.#
#######
Enter command: s2
#######
#.....#
#.....#
##P.+.#
#-O.#.#
#######
Enter command: d1
#######
#.....#
#.....#
##.P+.#
#-O.#.#
#######
Enter command: s1
#######
#.....#
#.....#
##..+.#
#-OP#.#
#######
Enter command: a1
#######
#.....#
#.....#
##..+.#
#OP.#.#
#######
Enter command: w2
#######
#.....#
#.P...#
##..+.#
#O-.#.#
#######
Enter command: d3
#######
#.....#
#....P#
##..+.#
#O-.#.#
#######
Enter command: s1
#######
#.....#
#.....#
##..+P#
#O-.#.#
#######
Enter command: a2
#######
#.....#
#.....#
##+P..#
#O-.#.#
#######
Enter command: w1
#######
#.....#
#..P..#
##+...#
#O-.#.#
#######
Enter command: a1
#######
#.....#
#.P...#
##+...#
#O-.#.#
#######
Enter command: s1
#######
#.....#
#.....#
##P...#
#OO.#.#
#######
Win!

分割线

本小段没有实际意义,
仅用于分隔题目和答案。
防止学生无意中直接看到答案,
影响思路。



















答案

WELCOME = "Welcome to Sokoban!"
ENTER = "Enter command: "
WIN = "Win!"
BYE = "Bye!"
INVALID = "Invalid command"


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


def read_txt(filename):
    print(filename)
    with open(filename, "r") as f:
        fr = f.read()

    lines = fr.split("\n")

    board = []
    for line in lines:
        line = line.strip()
        if line:
            board.append(list(line))

    return board


def get_info(board):
    info = {
        "player": [],
        "boxes": [],
    }

    r, c = len(board), len(board[0])

    for ri in range(r):
        for ci in range(c):
            cell = board[ri][ci]
            if cell == "+":
                info["boxes"].append([ri, ci])
                board[ri][ci] = "."
            elif cell == "P":
                info["player"] = [ri, ci]
                board[ri][ci] = "."

    return info


def show_board(board, info):
    r, c = len(board), len(board[0])
    player = tuple(info["player"])
    boxes = info["boxes"]


    for ri in range(r):
        for ci in range(c):
            cell = board[ri][ci]
            if (ri, ci) == player:
                cell = "P"
            elif [ri, ci] in boxes:
                if cell == "-":
                    cell = "O"
                else:
                    cell = "+"

            print(cell, end="")

        print()


def check_win(board, info):
    for box in info["boxes"]:
        br, bc = box
        if board[br][bc] != "-":
            return False

    return True


def move(board, info, drc, step):
    r, c = len(board), len(board[1])
    pr, pc = info["player"]
    dr, dc = drc

    for i in range(step):
        new_r = pr + dr
        new_c = pc + 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


        new_rc = [new_r, new_c]
        if new_rc in info["boxes"]:
            index = info["boxes"].index(new_rc)
            next_r, next_c = new_r + dr, new_c + dc
            if board[next_r][next_c] == "#":
                break

            if next_r < 0 or next_r >= r or next_c < 0 or next_c >= c:
                break

            if [next_r, next_c] in info["boxes"]:
                break

            info["boxes"][index] = [next_r, next_c]

        pr = new_r
        pc = new_c

    info["player"] = [pr, pc]


def main(txt_file):
    board = read_txt(txt_file)  # 1. 获取推箱子文件对应的二维列表
    info = get_info(board)  # 2. 解析出关键信息: 玩家位置,箱子位置
    print(WELCOME)  # 3. 展示欢迎语

    # 4. 使用循环
    while True:
        # 4-a. 循环中展示面板
        show_board(board, info)

        if check_win(board, info):  # 4-b. 所有箱子都到达目标点,胜利并结束游戏
            print(WIN)
            return

        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, info, drc, step)
                    continue

        # 4-h. 处理中失败了,提醒输入不符合规范。
        print(INVALID)


txt_file = "sokoban.txt"
main(txt_file)
原文地址:https://www.cnblogs.com/BigShuang/p/15695521.html