From cea1f058e06cf08afc82aa8c05bbb251235a1631 Mon Sep 17 00:00:00 2001 From: Fred Z Date: Thu, 5 Apr 2018 01:53:30 +0200 Subject: [PATCH] Creates a new object : Player Splits player's related code in a new class Renames some var, const, attr and methods Done some cleaning in ELEMENTS --- conf.py | 48 +++++--------- func.py | 7 -- main.py | 26 ++++---- maze.py | 189 ++++++++++++------------------------------------------ player.py | 120 ++++++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+), 200 deletions(-) delete mode 100644 func.py create mode 100644 player.py diff --git a/conf.py b/conf.py index fff9bbe..86fee90 100644 --- a/conf.py +++ b/conf.py @@ -9,38 +9,22 @@ from math import floor import pygame ELEMENTS = ( - { - 'symbol': 'n', 'name': 'needle', 'cross': True, - 'ressurect': False, 'collect': True, 'tile': 'img/3-30.png' - }, - { - 'symbol': 't', 'name': 'tube', 'cross': True, - 'ressurect': False, 'collect': True, 'tile': 'img/1-30.png' - }, - { - 'symbol': 'e', 'name': 'ether', 'cross': True, - 'ressurect': False, 'collect': True, 'tile': 'img/2-30.png' - }, - { - 'symbol': 'E', 'name': 'exit', 'cross': False, - 'ressurect': False, 'collect': False, 'tile': 'img/g-30.png' - }, - { - 'symbol': ' ', 'name': 'void', 'cross': True, - 'ressurect': True, 'collect': False, 'tile': 'img/void-30.png' - }, - { - 'symbol': '.', 'name': 'wall', 'cross': False, - 'ressurect': False, 'collect': False, 'tile': 'img/wall-30.png' - }, - { - 'symbol': 'X', 'name': 'player', 'cross': False, - 'ressurect': False, 'collect': False, 'tile': 'img/player-30.png' - }, - { - 'symbol': '\n', 'name': 'nlin', 'cross': False, - 'ressurect': False, 'collect': False, 'tile': False - }, + {'symbol': 'n', 'name': 'needle', 'cross': True, 'item': True, + 'tile': 'img/3-30.png'}, + {'symbol': 't', 'name': 'tube', 'cross': True, 'item': True, + 'tile': 'img/1-30.png'}, + {'symbol': 'e', 'name': 'ether', 'cross': True, 'item': True, + 'tile': 'img/2-30.png'}, + {'symbol': 'E', 'name': 'exit', 'cross': False, 'item': False, + 'tile': 'img/g-30.png'}, + {'symbol': ' ', 'name': 'void', 'cross': True, 'item': False, + 'tile': 'img/void-30.png'}, + {'symbol': '.', 'name': 'wall', 'cross': False, 'item': False, + 'tile': 'img/wall-30.png'}, + {'symbol': 'X', 'name': 'player', 'cross': False, 'item': False, + 'tile': 'img/player-30.png'}, + {'symbol': '\n', 'name': 'nlin', 'cross': False, 'item': False, + 'tile': False}, ) CELL_SIZE = 30 # Size of the tiles, in pixels diff --git a/func.py b/func.py deleted file mode 100644 index 024a14c..0000000 --- a/func.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Author: freezed 2018-02-11 -Version: 0.1 -Licence: `GNU GPL v3` GNU GPL v3: http://www.gnu.org/licenses/ - -This file is part of [_ocp3_ project](https://github.com/freezed/ocp3) -""" diff --git a/main.py b/main.py index 91f5858..f4c3f43 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ from pygame.locals import ( K_UP, K_DOWN, K_RIGHT, K_LEFT, KEYDOWN, QUIT ) from maze import Maze +from player import Player from conf import ( BACKGRND_FILE, CAPTION, MAZE_FILE, HEAD_SIZE_H, maze_draw, MSG_END, MSG_QUIT, set_header, WIN_DIM @@ -23,10 +24,11 @@ from conf import ( GAME_KEYS = [K_UP, K_DOWN, K_RIGHT, K_LEFT] last_message = False # Do not execute last message loop -# Loading maze -MAZE_GAME = Maze(MAZE_FILE) +# initialize maze and player +game_maze = Maze(MAZE_FILE) +macgyver = Player(game_maze) -if MAZE_GAME.status: +if game_maze.status: pygame.init() pygame.time.Clock().tick(25) pygame.display.set_caption(CAPTION) @@ -34,27 +36,27 @@ if MAZE_GAME.status: WINDOW.blit(pygame.image.load(BACKGRND_FILE).convert(), (0, HEAD_SIZE_H)) # Game loop -while MAZE_GAME.status: - set_header(WINDOW, MAZE_GAME.status_message) - maze_draw(WINDOW, MAZE_GAME.maze_print()) +while game_maze.status: + set_header(WINDOW, macgyver.status_message) + maze_draw(WINDOW, game_maze.maze_print()) for event in pygame.event.get(): if event.type == QUIT: - MAZE_GAME.status = False + game_maze.status = False last_message = False if event.type == KEYDOWN: last_message = True # Execute last_message loop if event.key in GAME_KEYS: - MAZE_GAME.move_to(event.key) + macgyver.move_to(event.key) else: - MAZE_GAME.status_message['status'] = MSG_QUIT - MAZE_GAME.status = False + macgyver.status_message['status'] = MSG_QUIT + game_maze.status = False # Allows reading the last_message (won, lost or quit) while last_message: - MAZE_GAME.status_message['title'] = MSG_END - set_header(WINDOW, MAZE_GAME.status_message) + macgyver.status_message['title'] = MSG_END + set_header(WINDOW, macgyver.status_message) pygame.display.flip() for event in pygame.event.get(): if event.type == KEYDOWN: diff --git a/maze.py b/maze.py index c7dac5b..05c5e52 100644 --- a/maze.py +++ b/maze.py @@ -7,11 +7,7 @@ This file is part of [_ocp3_ project](https://github.com/freezed/ocp3) """ import os import random -from pygame.locals import K_UP, K_DOWN, K_RIGHT, K_LEFT -from conf import ( - elmt_val, ERR_FILE, ERR_LINE, MSG_COLLECT, MSG_LOSER, MSG_OK, - MSG_WALL, MSG_WINNER, HEAD_MESSAGES, MAZE_SIZE -) +from conf import elmt_val, ERR_FILE, ERR_LINE, MAZE_SIZE class Maze: @@ -21,71 +17,51 @@ class Maze: Moves the player to it """ - def __init__(self, maze_file): + def __init__(self, filename): """ Initialise maze The Maze object has given attributes: - :var int status: move status (what append after a move command) - :var str status_message: feedback message for player - :var lst splited_maze: splited maze in a list - :var str _maze_in_a_string: maze string - :var str _element_under_player: Element under player - :var int _player_position: Player index in _maze_in_a_string + :var bool status: False = End of game (file error, end of game or quit) + :var str ground: Element under player + :var int position: Player index in string + :var int COL_NB: column number of the maze + :var int RANGE: string range - :param maze_file: maze filename - :rtype maze: str() - :return: None + :param filename: maze filename """ # Loading maze file - if os.path.isfile(maze_file) is False: + if os.path.isfile(filename) is False: self.status = False - print(ERR_FILE.format(maze_file)) + print(ERR_FILE.format(filename)) else: - with open(maze_file, "r") as maze_data: + with open(filename, "r") as maze_data: splited_maze = maze_data.read().splitlines() if self.check_file(splited_maze): - # Builds a square maze (if end-line spaces are missing in the file) - self._maze_in_a_string = '\n'.join( + # Builds a square maze (end-line spaces are missing in file) + self.string = '\n'.join( (self.check_line(line) for line in splited_maze) ) - # Gets player initial position - self._player_position = self._maze_in_a_string.find( - elmt_val('symbol', 'name', 'player', 0) - ) - - # Defines Element under player at start - self._element_under_player = elmt_val('symbol', 'name', 'void', 0) - - # Place collectables on the maze - for symbol_to_place in elmt_val('symbol', 'collect', True): + # Place randomly 'item' on the maze + for symbol_to_place in elmt_val('symbol', 'item', True): position = random.choice( - [idx for (idx, value) in enumerate( - self._maze_in_a_string - ) if value == elmt_val( - 'symbol', 'name', 'void', 0 - )]) - self.place_element(symbol_to_place, pos=position) + [idx for (idx, value) in enumerate(self.string) + if value == elmt_val('symbol', 'name', 'void', 0)] + ) - self.MAX_ITEMS = sum(1 for _ in elmt_val('name', 'collect', True)) - self._COLUM = MAZE_SIZE + 1 # List starts to zero - self._MAXIM = (self._COLUM * MAZE_SIZE) - 1 + self.set_symbol(symbol_to_place, position) - self.status = True - self.collected_items = [] - self.collected_items_num = 0 - - self.status_message = {} - self.status_message['title'] = HEAD_MESSAGES['title'] - self.status_message['status'] = HEAD_MESSAGES['status'] - self.status_message['items'] = HEAD_MESSAGES['items'].format( - self.collected_items_num, self.MAX_ITEMS + self.MAX_ITEMS = sum( + 1 for _ in elmt_val('name', 'item', True) ) + self.COL_NB = MAZE_SIZE + 1 # List starts to zero + self.RANGE = range(self.COL_NB * MAZE_SIZE - 1) # last EOL + self.status = True else: self.status = False @@ -105,107 +81,6 @@ class Maze: else: return True - def maze_print(self): - """ Return a string of the maze state """ - return self._maze_in_a_string.replace('\n', '') - - def move_to(self, pressed_key): - """ - Move the player on the maze - - :param str pressed_key: direction (pygame const) - """ - # Replace player on the maze by the under-element - self._maze_in_a_string = self._maze_in_a_string.replace( - elmt_val('symbol', 'name', 'player', 0), - self._element_under_player - ) - - # Recupere la position suivante - if pressed_key == K_UP: - next_position = self._player_position - self._COLUM - - elif pressed_key == K_DOWN: - next_position = self._player_position + self._COLUM - - elif pressed_key == K_RIGHT: - next_position = self._player_position + 1 - - elif pressed_key == K_LEFT: - next_position = self._player_position - 1 - - # Next position treatment - if next_position >= 0 and next_position <= self._MAXIM: - next_char = self._maze_in_a_string[next_position] - - if next_char == elmt_val('symbol', 'name', 'void', 0): - self._player_position = next_position - self.status_message['status'] = MSG_OK - - elif next_char in elmt_val('symbol', 'collect', True): - self._player_position = next_position - self._element_under_player = elmt_val( - 'symbol', 'name', 'void', 0 - ) - self.collected_items.append( - elmt_val('name', 'symbol', next_char, 0) - ) - self.collected_items_num += 1 - self.status_message['status'] = MSG_COLLECT.format( - elmt_val('name', 'symbol', next_char, 0) - ) - self.status_message['items'] \ - = HEAD_MESSAGES['items'].format( - self.collected_items_num, self.MAX_ITEMS - ) - - elif next_char == elmt_val('symbol', 'name', 'exit', 0): - self.status = False - if sorted(self.collected_items) == sorted( - elmt_val('name', 'collect', True) - ): - self.status_message['status'] = MSG_WINNER - else: - missed_item_flist = ', '.join( - (item for item in elmt_val( - 'name', 'collect', True - ) if item not in self.collected_items) - ) - self.status_message['status'] = MSG_LOSER.format( - missed_item_flist - ) - - else: # wall or nline - self.status_message['status'] = MSG_WALL - else: - self.status_message['status'] = MSG_WALL - - # Set the player on position - self.place_element(elmt_val('symbol', 'name', 'player', 0)) - - def place_element(self, element, **kwargs): - """ - Set an element on the maze - - The position used is in ._player_position attribute - Used for player and void after collecting items - - :param str element: the string of the element to place - """ - # FIXME cannot find a way to define default value to the - # method's arguments with class attributes - if 'pos' in kwargs: - pos = kwargs['pos'] - else: - pos = self._player_position - - if 'txt' in kwargs: - txt = kwargs['txt'] - else: - txt = self._maze_in_a_string - - self._maze_in_a_string = txt[:pos] + element + txt[pos + 1:] - @staticmethod def check_line(line): """ @@ -219,3 +94,19 @@ class Maze: return line + (differance * elmt_val('symbol', 'name', 'void', 0)) else: return line + + def maze_print(self): + """ Return a string of the maze state """ + return self.string.replace('\n', '') + + def set_symbol(self, symbol, pos): + """ + Set an symbol on the maze + + Used for 'player' and 'void' after collecting items + + :param str symbol: the symbol to set + :param str pos: index in the string + """ + txt = self.string + self.string = txt[:pos] + symbol + txt[pos + 1:] diff --git a/player.py b/player.py new file mode 100644 index 0000000..00f5fe7 --- /dev/null +++ b/player.py @@ -0,0 +1,120 @@ +""" +Author: freezed 2018-04-04 +Version: 0.1 +Licence: `GNU GPL v3` GNU GPL v3: http://www.gnu.org/licenses/ + +This file is part of [_ocp3_ project](https://github.com/freezed/ocp3) +""" +from conf import elmt_val, MSG_COLLECT, MSG_LOSER, MSG_OK, MSG_WALL, MSG_WINNER, HEAD_MESSAGES +from pygame.locals import K_UP, K_DOWN, K_RIGHT, K_LEFT + + +class Player: + """ + Managing the player movement + """ + + def __init__(self, maze): + """ Constructor """ + self.maze = maze + self.position = maze.string.find(elmt_val('symbol', 'name', 'player', 0)) + # Element under player, default 'void' + self.ground = elmt_val('symbol', 'name', 'void', 0) + + self.stock = [] + self.stock_num = 0 + + self.status_message = {} + self.status_message['title'] = HEAD_MESSAGES['title'] + self.status_message['status'] = HEAD_MESSAGES['status'] + self.status_message['items'] = HEAD_MESSAGES['items'].format( + self.stock_num, maze.MAX_ITEMS + ) + + def move_to(self, pressed_key): + """ + Move the player on the maze + + :param str pressed_key: direction (pygame const) + """ + # Replace player symbol on the maze by 'ground' + self.maze.string = self.maze.string.replace( + elmt_val('symbol', 'name', 'player', 0), self.ground + ) + + if pressed_key == K_UP: + self.next_pos(self.position - self.maze.COL_NB) + + elif pressed_key == K_DOWN: + self.next_pos(self.position + self.maze.COL_NB) + + elif pressed_key == K_RIGHT: + self.next_pos(self.position + 1) + + elif pressed_key == K_LEFT: + self.next_pos(self.position - 1) + + def next_pos(self, next_position): + """ + Next position treatment + + For each movement, it checks the next symbol on the maze and apply the corresponding rule: + - set the new position + - updates messages + - collect item (if any) + - set symbol of the leaved position + - stop the game (win or lose) + + :param int next_position: index in self.maze.string + """ + # is in the string range + if next_position in self.maze.RANGE: + next_symbol = self.maze.string[next_position] + + # is a 'void' element + if next_symbol == elmt_val('symbol', 'name', 'void', 0): + self.position = next_position + self.status_message['status'] = MSG_OK + + # is a 'item' element + elif next_symbol in elmt_val('symbol', 'item', True): + self.position = next_position + self.ground = elmt_val('symbol', 'name', 'void', 0) + self.stock.append( + elmt_val('name', 'symbol', next_symbol, 0) + ) + self.stock_num += 1 + self.status_message['status'] = MSG_COLLECT.format( + elmt_val('name', 'symbol', next_symbol, 0) + ) + self.status_message['items'] \ + = HEAD_MESSAGES['items'].format( + self.stock_num, self.maze.MAX_ITEMS + ) + + # is an 'exit' element (aka the guard) + elif next_symbol == elmt_val('symbol', 'name', 'exit', 0): + self.maze.status = False + + # all 'item' are collected : player wins + if sorted(self.stock) == sorted(elmt_val('name', 'item', True)): + self.status_message['status'] = MSG_WINNER + + # player lose + else: + missed_item_flist = ', '.join((item for item in elmt_val( + 'name', 'item', True + ) if item not in self.stock)) + self.status_message['status'] = MSG_LOSER.format( + missed_item_flist + ) + + # is all other element (wall or nline) + else: + self.status_message['status'] = MSG_WALL + + else: + self.status_message['status'] = MSG_WALL + + # Sets the player's new position + self.maze.set_symbol(elmt_val('symbol', 'name', 'player', 0), self.position)