Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14a39b7154 | |||
| 10a3d93821 | |||
| 9030146ec8 |
@@ -0,0 +1,2 @@
|
|||||||
|
# show extra spaces after certains emojis to align them in the terminal
|
||||||
|
SHOW_EXTRA_EMOJI_SPACES=true
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# show extra spaces after certains emojis to align them in the terminal
|
||||||
|
SHOW_EXTRA_EMOJI_SPACES=true
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
from .stoneBlock import StoneBlock
|
from .stoneBlock import StoneBlock
|
||||||
from .spawnPointBlock import SpawnPointBlock
|
from .spawnPointBlock import SpawnPointBlock
|
||||||
from .finishBlock import FinishBlock
|
from .finishBlock import FinishBlock
|
||||||
|
from .coinBlock import CoinBlock
|
||||||
|
|
||||||
BLOCKS = [
|
BLOCKS = [
|
||||||
StoneBlock,
|
StoneBlock,
|
||||||
SpawnPointBlock,
|
SpawnPointBlock,
|
||||||
FinishBlock,
|
FinishBlock,
|
||||||
|
CoinBlock
|
||||||
]
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from game.blocks.block import Block
|
||||||
|
from game.gameState import GameState
|
||||||
|
|
||||||
|
class CoinBlock(Block):
|
||||||
|
def __init__(self):
|
||||||
|
self.collected = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def character(self):
|
||||||
|
return '°'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return 'coin'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def solid(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def on_collision(self, gameState: GameState):
|
||||||
|
if not self.collected:
|
||||||
|
gameState.add_coin()
|
||||||
|
self.collected = True
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
return " " if self.collected else "🪙" + (" " if os.getenv("SHOW_EXTRA_EMOJI_SPACES", "false").lower() == "true" else "")
|
||||||
+30
-22
@@ -4,7 +4,7 @@ This class is responsible for managing the game's logic.
|
|||||||
It offers to set a callback function that will be called when waiting for a player input.
|
It offers to set a callback function that will be called when waiting for a player input.
|
||||||
It provides the gameState object to access the current state of the game.
|
It provides the gameState object to access the current state of the game.
|
||||||
It provides the Map object to access the map.
|
It provides the Map object to access the map.
|
||||||
It provides some stopGame conditions to stop the game like the max number of moves or the player score.
|
TODO: It provides some stopGame conditions to stop the game like the max number of moves.
|
||||||
It provides public methods to start, stop and reset the game.
|
It provides public methods to start, stop and reset the game.
|
||||||
It provides public methods to change the current map.
|
It provides public methods to change the current map.
|
||||||
"""
|
"""
|
||||||
@@ -20,17 +20,22 @@ class Game:
|
|||||||
self.map = map
|
self.map = map
|
||||||
self.gameState = gameState
|
self.gameState = gameState
|
||||||
self.waitingForInputCallback = None
|
self.waitingForInputCallback = None
|
||||||
|
self.positionUpdateCallback = None
|
||||||
|
self.gameStopCallback = None
|
||||||
|
|
||||||
def set_waiting_for_input_callback(self, callback):
|
def set_waiting_for_input_callback(self, callback: callable):
|
||||||
self.waitingForInputCallback = callback
|
self.waitingForInputCallback = callback
|
||||||
|
|
||||||
def set_position_update_callback(self, callback):
|
def set_position_update_callback(self, callback: callable):
|
||||||
self.positionUpdateCallback = callback
|
self.positionUpdateCallback = callback
|
||||||
|
|
||||||
def _position_update(self):
|
def _position_update(self):
|
||||||
if self.positionUpdateCallback:
|
if self.positionUpdateCallback:
|
||||||
self.positionUpdateCallback()
|
self.positionUpdateCallback()
|
||||||
|
|
||||||
|
def set_game_stop_callback(self, callback: callable):
|
||||||
|
self.gameStopCallback = callback
|
||||||
|
|
||||||
def start_game(self):
|
def start_game(self):
|
||||||
self.gameState.set_stop_game(False)
|
self.gameState.set_stop_game(False)
|
||||||
|
|
||||||
@@ -48,13 +53,15 @@ class Game:
|
|||||||
self.gameState.add_move_count()
|
self.gameState.add_move_count()
|
||||||
|
|
||||||
# Try to move the player according to his velocity
|
# Try to move the player according to his velocity
|
||||||
block_at_velocity = self._get_block_at_velocity()
|
velocity_position = self.get_position_at_velocity()
|
||||||
if not block_at_velocity or not block_at_velocity.solid:
|
block_at_velocity = None
|
||||||
velocity_position = self.get_position_at_velocity() # TODO : optimize
|
if velocity_position is not None:
|
||||||
|
block_at_velocity = self.map.get_block_at(velocity_position[0], velocity_position[1])
|
||||||
|
if not block_at_velocity or not block_at_velocity.solid: # Empty space or non-solid block → Move the player to the new position
|
||||||
if velocity_position:
|
if velocity_position:
|
||||||
self.gameState.set_player_position(velocity_position)
|
self.gameState.set_player_position(velocity_position)
|
||||||
if block_at_velocity:
|
if block_at_velocity:
|
||||||
block_at_velocity.on_collision(self.gameState) # Do something when the player is on the block, like add score or stop the game
|
block_at_velocity.on_collision(self.gameState) # Do something when the player is on the block, like add coins or stop the game
|
||||||
# Check if the player is on a solid block
|
# Check if the player is on a solid block
|
||||||
player_position = self.gameState.get_player_position()
|
player_position = self.gameState.get_player_position()
|
||||||
block_at_player_position = self.map.get_block_at(player_position[0], player_position[1] + 1)
|
block_at_player_position = self.map.get_block_at(player_position[0], player_position[1] + 1)
|
||||||
@@ -63,6 +70,9 @@ class Game:
|
|||||||
|
|
||||||
self._update_player_velocity()
|
self._update_player_velocity()
|
||||||
|
|
||||||
|
if self.gameStopCallback and self.gameStopCallback(self.gameState):
|
||||||
|
self.gameState.set_stop_game(True)
|
||||||
|
break
|
||||||
|
|
||||||
def _update_player_velocity(self):
|
def _update_player_velocity(self):
|
||||||
current_velocity = self.gameState.playerVelocity
|
current_velocity = self.gameState.playerVelocity
|
||||||
@@ -100,29 +110,27 @@ class Game:
|
|||||||
player_position = self.gameState.get_player_position()
|
player_position = self.gameState.get_player_position()
|
||||||
current_velocity = self.gameState.playerVelocity
|
current_velocity = self.gameState.playerVelocity
|
||||||
|
|
||||||
if current_velocity == -1:
|
match current_velocity:
|
||||||
|
case -1:
|
||||||
return None
|
return None
|
||||||
|
case "down":
|
||||||
if current_velocity == "down":
|
|
||||||
return (player_position[0], player_position[1] + 1)
|
return (player_position[0], player_position[1] + 1)
|
||||||
|
case "up":
|
||||||
if current_velocity == "left_down":
|
return (player_position[0], player_position[1] - 1)
|
||||||
|
case "left_down":
|
||||||
return (player_position[0] - 1, player_position[1] + 1)
|
return (player_position[0] - 1, player_position[1] + 1)
|
||||||
|
case "right_down":
|
||||||
if current_velocity == "right_down":
|
|
||||||
return (player_position[0] + 1, player_position[1] + 1)
|
return (player_position[0] + 1, player_position[1] + 1)
|
||||||
|
case "left":
|
||||||
if current_velocity == "left":
|
|
||||||
return (player_position[0] - 1, player_position[1])
|
return (player_position[0] - 1, player_position[1])
|
||||||
|
case "right":
|
||||||
if current_velocity == "right":
|
|
||||||
return (player_position[0] + 1, player_position[1])
|
return (player_position[0] + 1, player_position[1])
|
||||||
|
case "left_up":
|
||||||
if current_velocity == "left_up":
|
|
||||||
return (player_position[0] - 1, player_position[1] - 1)
|
return (player_position[0] - 1, player_position[1] - 1)
|
||||||
|
case "right_up":
|
||||||
if current_velocity == "right_up":
|
|
||||||
return (player_position[0] + 1, player_position[1] - 1)
|
return (player_position[0] + 1, player_position[1] - 1)
|
||||||
|
case _:
|
||||||
|
return None
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Represents the current state of the game.
|
Represents the current state of the game.
|
||||||
Contains :
|
Contains :
|
||||||
- Player score
|
- Player coins
|
||||||
- stopGame flag
|
- stopGame flag
|
||||||
- Player position
|
- Player position
|
||||||
"""
|
"""
|
||||||
@@ -10,15 +10,12 @@ class GameState:
|
|||||||
PLAYER_VELOCITIES = [*PLAYER_MOVEMENTS, "down"]
|
PLAYER_VELOCITIES = [*PLAYER_MOVEMENTS, "down"]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.score = 0
|
|
||||||
self.playerPosition = (0, 0)
|
self.playerPosition = (0, 0)
|
||||||
self.stopGame = False
|
self.stopGame = False
|
||||||
self.movesCount = 0
|
self.movesCount = 0
|
||||||
self.playerVelocity = -1 # -1 means no velocity
|
self.playerVelocity = -1 # -1 means no velocity
|
||||||
self.gameWon = False
|
self.gameWon = False
|
||||||
|
self.coins = 0
|
||||||
def add_score(self, points):
|
|
||||||
self.score += points
|
|
||||||
|
|
||||||
def set_player_position(self, x, y):
|
def set_player_position(self, x, y):
|
||||||
self.playerPosition = (x, y)
|
self.playerPosition = (x, y)
|
||||||
@@ -44,3 +41,6 @@ class GameState:
|
|||||||
|
|
||||||
def set_game_won(self, won):
|
def set_game_won(self, won):
|
||||||
self.gameWon = won
|
self.gameWon = won
|
||||||
|
|
||||||
|
def add_coin(self):
|
||||||
|
self.coins += 1
|
||||||
@@ -14,11 +14,23 @@ class UserPlayerGameHandler(GameHandler):
|
|||||||
"""
|
"""
|
||||||
Refresh the screen
|
Refresh the screen
|
||||||
"""
|
"""
|
||||||
print("\033[2J\033[H" + self.game.to_string())
|
print("\n\033[2J") # Add margin for stupid vscode terminal
|
||||||
|
|
||||||
|
game_string = self.game.to_string()
|
||||||
|
for i, line in enumerate(game_string.splitlines()):
|
||||||
|
if i == 0:
|
||||||
|
line += "\tCoins: " + str(self.game.gameState.coins)
|
||||||
|
elif i == 1:
|
||||||
|
line += "\tMoves: " + str(self.game.gameState.movesCount)
|
||||||
|
print(line)
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
def on_input_request(self) -> str:
|
def on_input_request(self) -> str:
|
||||||
"""
|
"""
|
||||||
Listen for keyboard input, and map it to a gameState PLAYER_MOVEMENTS
|
Listen for keyboard input, and map it to a gameState PLAYER_MOVEMENTS
|
||||||
"""
|
"""
|
||||||
return self.game.gameState.PLAYER_MOVEMENTS[self.KEYBOARD_INPUTS.index(input(f"Enter a movement ({', '.join(self.KEYBOARD_INPUTS)}): "))]
|
user_input = input(f"Enter a movement ({', '.join(self.KEYBOARD_INPUTS)}): ")
|
||||||
|
if user_input not in self.KEYBOARD_INPUTS:
|
||||||
|
print(f"Invalid input: {user_input}. Please enter one of {', '.join(self.KEYBOARD_INPUTS)}.")
|
||||||
|
return self.on_input_request()
|
||||||
|
return self.game.gameState.PLAYER_MOVEMENTS[self.KEYBOARD_INPUTS.index(user_input)]
|
||||||
@@ -16,6 +16,7 @@ class Map:
|
|||||||
self.width = int(map_data.get('width', 0))
|
self.width = int(map_data.get('width', 0))
|
||||||
self.height = int(map_data.get('height', 0))
|
self.height = int(map_data.get('height', 0))
|
||||||
self.difficulty = float(map_data.get('difficulty', 0.0))
|
self.difficulty = float(map_data.get('difficulty', 0.0))
|
||||||
|
self.min_moves = map_data.get('min_moves', None)
|
||||||
self.layout = map_data.get('layout', [])
|
self.layout = map_data.get('layout', [])
|
||||||
|
|
||||||
def get_block_at(self, x, y) -> Block | None:
|
def get_block_at(self, x, y) -> Block | None:
|
||||||
@@ -38,6 +39,9 @@ class Map:
|
|||||||
return block.type
|
return block.type
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_min_moves(self):
|
||||||
|
return self.min_moves
|
||||||
|
|
||||||
def to_string_dict(self):
|
def to_string_dict(self):
|
||||||
"""
|
"""
|
||||||
Returns a string matrix representation of the map layout.
|
Returns a string matrix representation of the map layout.
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ class MapReader:
|
|||||||
'layout': self._parse_layout(config.get('layout', ''))
|
'layout': self._parse_layout(config.get('layout', ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.get('min_moves') is not None:
|
||||||
|
map_data['min_moves'] = config.get('min_moves')
|
||||||
|
|
||||||
return Map(map_data)
|
return Map(map_data)
|
||||||
|
|
||||||
def _get_block_characters_mapping(self):
|
def _get_block_characters_mapping(self):
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from game.game import Game
|
from game.game import Game
|
||||||
from game.gameState import GameState
|
from game.gameState import GameState
|
||||||
from game.handlers import GAME_HANDLERS
|
from game.handlers import GAME_HANDLERS
|
||||||
@@ -5,6 +7,9 @@ from game.map import Map
|
|||||||
from game.mapReader import MapReader
|
from game.mapReader import MapReader
|
||||||
from game.mapSet import MapSet
|
from game.mapSet import MapSet
|
||||||
|
|
||||||
|
# loading variables from .env file
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mapReader = MapReader()
|
mapReader = MapReader()
|
||||||
mapSet = MapSet(mapReader, mapReader.get_map_names())
|
mapSet = MapSet(mapReader, mapReader.get_map_names())
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
width = 12
|
width = 12
|
||||||
height = 6
|
height = 6
|
||||||
difficulty = 10
|
difficulty = 10
|
||||||
|
min_moves = 6
|
||||||
layout = """\
|
layout = """\
|
||||||
############
|
############
|
||||||
# E#
|
# E#
|
||||||
|
|||||||
Reference in New Issue
Block a user