zoukankan      html  css  js  c++  java
  • 大爽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)
    
  • 相关阅读:
    ural 1146. Maximum Sum(动态规划)
    ural 1119. Metro(动态规划)
    ural 1013. K-based Numbers. Version 3(动态规划)
    Floyd算法
    杭电21题 Palindrome
    杭电20题 Human Gene Functions
    杭电15题 The Cow Lexicon
    杭电三部曲一、基本算法;19题 Cow Bowling
    杭电1002 Etaoin Shrdlu
    Qt 学习之路 2(37):文本文件读写
  • 原文地址:https://www.cnblogs.com/BigShuang/p/15695521.html
Copyright © 2011-2022 走看看