原生Python完成迷宫冒险游戏

功能介绍

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

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

功能实现

  1. 用read file的知识去读取迷宫,同时创建cell instance。
  2. 玩家的移动本质就是交换格子,但由于有的格子有特殊效果,我们可以先step到那个格子,看有没有特殊效果,然后再交换格子。
  3. run.py控制玩家的输入,并把命令传给game.py。game.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)

源码

源码戳我

posted @ 2020-07-13 16:57  AKAjojo  阅读(453)  评论(0编辑  收藏  举报