欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python迷宫冒险游戏

程序员文章站 2022-07-13 08:26:56
...

功能介绍

这是一个迷宫冒险类游戏,玩家走到终点即为胜利。在通往终点的道路上可能会有火焰,玩家需要绕过这些火焰或者用手中的水瓶去浇灭它。玩家可以用过 w,a,s,d 来移动,碰到不同格子会有不同的效果:

Cell Display function
入口 X 迷宫的入口。当游戏开始时,玩家出生在出口位置。
出口 Y 迷宫的出口。玩家到达出口即为胜利,显示出玩家用的步数。
玩家 A 玩家。初始没有水。
* 玩家撞到墙时不能移动。
空气 玩家可移动到的格子。
火焰 F 如果玩家手中有水,可以消耗一瓶水救火。如果玩家没有水,玩家被火烧死,游戏结束。
W 玩家捡起水。
传送门 数字 玩家可以传送到相对应的数字上。玩家按 e 可以再传送原来的传送门。)

迷宫文本:
Python迷宫冒险游戏
游戏开始:
Python迷宫冒险游戏
撞墙:
Python迷宫冒险游戏
传送门:
Python迷宫冒险游戏
玩家捡起水:
Python迷宫冒险游戏
灭火:
Python迷宫冒险游戏
游戏结束:
Python迷宫冒险游戏

功能实现

玩家的移动本质就是交换格子,但由于有的格子有特殊效果,我们可以先step到那个格子,看有没有特殊效果,然后再交换格子。程序可以分为以下几个modules:

run.py
game.py
player.py
cells.py
game_parser.py
grid.py

处理人物以及格子

玩家和格子都需要有display的属性,方便我们把他画出来。在player里,需要记录玩家的行和列,以及移动的动作。

# player.py
class Player:
    def __init__(self):
        """
        display -- the display of the player
        num_water_buckets -- the number of water the player have
        row, col -- the position of the player
        moves -- store the move made by the player
        action -- store the user's input (record how the player move to the teleport)
        """
        self.display = 'A'
        self.num_water_buckets = 0
        self.row = 0
        self.col = 0
        self.moves = []
        self.action = ''

    def move(self, move):
        # Move the player
        if move =='w':
            self.row -= 1
            self.moves.append('w')
        
        if move =='s':
            self.row += 1
            self.moves.append('s')
        
        if move =='d':
            self.col += 1
            self.moves.append('d')
        
        if move =='a':
            self.col -= 1
            self.moves.append('a')
        
        if move == 'e':
            self.moves.append('e')
    
    def moves_made(self):
        """
        Returns:
        A string contains how many moves the player made and their moves
        """
        message = ''
        num_of_move = len(self.moves)
        moves = ', '.join(self.moves)

        if len(self.moves) == 1:
            message += 'You made {} move.\n'.format(num_of_move)
            message += 'Your move: {}\n'.format(moves)
        else:
            message += 'You made {} moves.\n'.format(num_of_move)
            message += 'Your moves: {}\n'.format(moves)
        
        return message
    
    def win(self):
        # Print the winning message and the moves
        print()
        print('\nYou conquer the treacherous maze set up by the Fire Nation and reclaim the Honourable \
Furious Forest Throne, restoring your hometown back to its former glory of rainbow and sunshine! Peace reigns over the lands.\n')
        print(self.moves_made())
        print('''=====================\n====== YOU WIN! =====\n=====================''')
        exit()

    def lose(self):
        # Print the winning message and the moves
        print()
        print('\nYou step into the fires and watch your dreams disappear :(.')
        print('\nThe Fire Nation triumphs! The Honourable Furious Forest is reduced to a pile of \
ash and is scattered to the winds by the next storm... You have been roasted.\n')
        print(self.moves_made())
        print('''=====================\n===== GAME OVER =====\n=====================''')
        exit()

step()记录了每个格子都有特殊的效果,能直接改变game的状态

class Start:
    def __init__(self):
        self.display = 'X'

    def step(self, game):
        """
        If current position is not a teleport, the current position become an Air block and the player is free to move
        If it is a teleport, it stays the same
        """
        if not isinstance(game.grid[game.player.row][game.player.col], Teleport):
            game.grid[game.player.row][game.player.col]=Air()

class End:
    def __init__(self):
        self.display = 'Y'

    def step(self, game):
        """
        If current position is not a teleport, the current position become an Air block and the player is free to move
        If it is a teleport, it stays the same
        Give a game message 'win'
        """
        if not isinstance(game.grid[game.player.row][game.player.col], Teleport):
            game.grid[game.player.row][game.player.col]=Air()
        game.message='win'


class Air:
    def __init__(self):
        self.display = ' '

    def step(self, game):
        """
        If current position is a teleport, current position is the same teleport
        Otherwise, current position become an Air block
        """
        if isinstance(game.grid[game.player.row][game.player.col], Teleport):
            game.grid[game.player.row][game.player.col]=Teleport(game.grid[game.player.row][game.player.col].display)
        else:
            game.grid[game.player.row][game.player.col]=Air()


class Wall:
    def __init__(self):
        self.display = '*'

    def step(self, game):
        """
        The player will not move
        Give a game message of step on a wall
        """
        game.is_player_move = False
        game.message = '\nYou walked into a wall. Oof!\n'


class Fire:
    def __init__(self):
        self.display = 'F'

    def step(self, game):
        """
        If player have more than one water, one water is used and give a message of extinguish fire and current position become an Air block
        If player donnot have water, give a message of lose and current position become an Air block
        """
        if game.player.num_water_buckets >= 1:
            game.player.num_water_buckets -= 1
            game.message = '\nWith your strong acorn arms, you throw a water bucket at the fire. You acorn roll your way through the extinguished flames!\n'
            game.grid[game.player.row][game.player.col]=Air()
        else:
            game.message = 'lose'
            game.grid[game.player.row][game.player.col]=Air()

class Water:
    def __init__(self):
        self.display = 'W'

    def step(self, game):
        """
        player gain one water and give a message of gaining water and current position become an Air block
        """
        game.player.num_water_buckets += 1
        game.message = "\nThank the Honourable Furious Forest, you\'ve found a bucket of water!\n"
        game.grid[game.player.row][game.player.col]=Air()


class Teleport:
    def __init__(self,cell):
        self.display = cell

    def step(self, game):
        """
        Update how the player came to current teleport
        Find the corresponding teleport and move player to there, the player are not allowed to move then
        Give a game message of teleport
        """
        game.player.moves.append(game.player.action)
        display = game.grid[game.next_row][game.next_col].display
        game.player.row, game.player.col = game.find(display, game.next_row, game.next_col)
        game.is_player_move = False
        game.message = '\nWhoosh! The magical gates break Physics as we know it and opens a wormhole through space and time.\n'

处理迷宫

生成迷宫。读取一个txt文件,把每个cell变成各自的对象,输出一个二维列表,里面为对象

# game_parser.py
from cells import (
    Start,
    End,
    Air,
    Wall,
    Fire,
    Water,
    Teleport
)


def read_lines(filename):
    """
    Read in a file, process them using parse(),
    and return the contents as a list of list of cells.
    """
    try:
        f = open(filename)
    except:
        print('{} does not exist!'.format(filename))
        exit()
    
    lines = f.readlines()
    f.close()
    return parse(lines)

def parse(lines):
    """Transform the input into a grid.

    Arguments:
        lines -- list of strings representing the grid

    Returns:
        list -- contains list of lists of Cells
    """
    
    #只能有一个入口,一个出口, 传送门必须=对应
    
    count_x = 0
    count_y = 0
    count_tele = {'1':0,'2':0,'3':0,'4':0,'5':0,'6':0,'7':0,'8':0,'9':0}
    grid = []
    
    for line in lines:
        str_cells = list(line.rstrip())
        obj_cells = []
        
        for cell in str_cells:
            if cell == 'X':
                obj_cells.append(Start())
                count_x+=1
            
            elif cell == 'Y':
                obj_cells.append(End())
                count_y+=1
            
            elif cell == ' ':
                obj_cells.append(Air())
            
            elif cell == '*':
                obj_cells.append(Wall())
            
            elif cell == 'F':
                obj_cells.append(Fire())
            
            elif cell == 'W':
                obj_cells.append(Water())
            
            elif cell.isdigit():
                if int(cell) in [i for i in range(1,10)]:
                    obj_cells.append(Teleport(cell))
                    count_tele[cell] += 1
                else:
                    raise ValueError('Bad letter in configuration file: {}.'.format(cell))
            
            else:
                raise ValueError('Bad letter in configuration file: {}.'.format(cell))
        
        grid.append(obj_cells)
    
    # Check there is one start, end, teleport in pairs
    if count_x != 1:
        raise ValueError('Expected 1 starting position, got {}.'.format(count_x))
    if count_y != 1:
        raise ValueError('Expected 1 ending position, got {}.'.format(count_y))
    for [key,value] in count_tele.items():
        if value == 1 or value > 2:
            raise ValueError('Teleport pad {} does not have an exclusively matching pad.'.format(key))
    
    return grid

第二步是把他们画出来,输出是一个字符串

# grid.py
def grid_to_string(grid, player):
    """Turns a grid and player into a string

    Arguments:
        grid -- list of list of Cells
        player -- a Player with water buckets

    Returns:
        string: A string representation of the grid and player.
    """
    strn = ''
    row = 0
    
    while row < len(grid):
        col = 0
        while col < len(grid[row]):
            if row == player.row and col == player.col:
                strn += player.display
            else:
                strn += grid[row][col].display
            col += 1
        strn+='\n'
        row += 1

    if player.num_water_buckets == 1:
        strn += '\nYou have {} water bucket.'.format(player.num_water_buckets)
    else:
        strn += '\nYou have {} water buckets.'.format(player.num_water_buckets)
    return strn

游戏的引擎

# game.py
from game_parser import read_lines
from grid import grid_to_string
from player import Player
from cells import (
    Start,
    Wall,
    Teleport
)

class Game:
    player = Player()

    def __init__(self, filename):
        """
        Arguments:
        filename -- the name of board  going to play

        Instance variable:
        grid -- returns a two dimension list of cells
        next_row, next_col -- the player's next position in the grid
        start.row, start.col -- the start position of the game
        player.row, player.col -- set the player to the start
        message : store any messages from the game
        is_player_move : controlling whether to move the player's position. If True, the player can move one step. If Flase, the player cannot move one step.
        """
        self.grid = read_lines(filename)
        self.next_row , self.next_col = 0 , 0
        self.start_row, self.start_col = self.find('X', -1, -1)
        self.player.row, self.player.col = self.start_row, self.start_col
        self.message = ''
        self.is_player_move = True

    def find(self, display, current_row, current_col):
        """
        find the position of a cell
        
        Arguments:
        display -- the display of the cell want to find
        current_row, current_col -- the current position of player

        Returns:
        a  list contains row and column of the cell
        """
        valid_display = ['X', 'Y', ' ', '*', 'F', 'W', 'A', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        if display not in valid_display:
            raise ValueError('Please enter a valid display')
        
        row = 0
        result = []
        
        while row < len(self.grid):
            col = 0
            while col < len(self.grid[row]):
                if self.grid[row][col].display == display \
                        and (row != current_row or col != current_col):
                    result.append(row)
                    result.append(col)
                col += 1
            row += 1
        
        return result

    def draw(self):
        # draw the grid
        return grid_to_string(self.grid,self.player)

    def in_bound(self,row,col):
        """
        Check if the position is in the bound of grid
        
        Arguments:
        row, col -- the position going to check
        
        Returns:
        True if the position is in bound
        False if the position is out of bound, then player act as step on a wall
        """
        if type(row) != int or type(col) != int:
            raise ValueError('Please enter an integer row and col')
        
        if row <0 or row > len(self.grid)-1 or col < 0 or col > len(self.grid[0])-1:
            wall = Wall()
            wall.step(self)
            self.is_player_move = False
            return False
        else:
            return True

    def game_move(self,move):
        """
        Before move the player, check any special effect if move the player

        Arguments:
        move -- input from user (one of w, s, d, a, e, q, otherwise raise warning)
        """
        cr = self.player.row
        cl = self.player.col
        self.player.action = move
        
        if move == 'w':
            #If the next position is in the bound, step the next position to see if there is any special effect.
            if self.in_bound(cr-1,cl):
                self.next_row, self.next_col = cr-1, cl
                self.grid[cr-1][cl].step(self)
        
        elif move == 's':
            if self.in_bound(cr+1,cl):
                self.next_row, self.next_col = cr+1, cl
                self.grid[cr+1][cl].step(self)
        
        elif move == 'd':
            if self.in_bound(cr,cl+1):
                self.next_row, self.next_col = cr, cl+1
                self.grid[cr][cl+1].step(self)
        
        elif move == 'a':
            if self.in_bound(cr,cl-1):
                self.next_row, self.next_col = cr, cl-1
                self.grid[cr][cl-1].step(self)
        
        elif move == 'q':
            print('\nBye!')
            exit()
        
        elif move == 'e':
            # if player is on a teleport now and user input 'e', find the position of the other teleport and transit the player to there\
            # the player will not move one step since it is transit by the teleport
            if isinstance(self.grid[self.player.row][self.player.col], Teleport):
                display = self.grid[self.player.row][self.player.col].display
                self.player.row, self.player.col = self.find(display, self.player.row, self.player.col)
                self.is_player_move = False
                self.player.moves.append('e')
                self.message = '\nWhoosh! The magical gates break Physics as we know it and opens a wormhole through space and time.\n'
        
        else:
            self.message = '\nPlease enter a valid move (w, a, s, d, e, q).\n'
            self.is_player_move = False
        
        # Set the display of start position to X
        self.grid[self.start_row][self.start_col]=Start()

    def message_parse(self):
        # parse the message from the game
        if self.message =='win':
            self.player.win()
        
        elif self.message == 'lose':
            self.player.lose()
        
        else:
            print(self.message)
        
        self.message = ''
# run.py
from game import Game
import os
import sys

if len(sys.argv) < 2 or len(sys.argv) > 3:
    print('Usage: python3 run.py <filename> [play]')
    exit()

def play_mode():
    # If the mode is play, the system will clean screen automatic
    if len(sys.argv) == 3 and sys.argv[2] == 'play':
        return os.system('cls')
    else:
        return None

filename = sys.argv[1]
game = Game(filename)

while True:
    game.is_player_move = True
    
    # draw the grid and print any message
    play_mode()
    print(game.draw())
    game.message_parse()
    
    # ask for input and check if there is any special effect
    move = input('Input a move: ').lower()
    game.game_move(move)
    
    # if player is free to move, move the player
    if game.is_player_move:
        game.player.move(move)

源码

点一个小星星吧么么哒