Spikes, better gameHandler, map width and height computed automatically & big peanut hard map

This commit is contained in:
2026-10-10 16:21:11 +02:00
parent 14a39b7154
commit 17f00ebca2
12 changed files with 131 additions and 37 deletions
+3 -1
View File
@@ -2,10 +2,12 @@ from .stoneBlock import StoneBlock
from .spawnPointBlock import SpawnPointBlock from .spawnPointBlock import SpawnPointBlock
from .finishBlock import FinishBlock from .finishBlock import FinishBlock
from .coinBlock import CoinBlock from .coinBlock import CoinBlock
from .spikesBlock import SpikesBlock
BLOCKS = [ BLOCKS = [
StoneBlock, StoneBlock,
SpawnPointBlock, SpawnPointBlock,
FinishBlock, FinishBlock,
CoinBlock CoinBlock,
SpikesBlock,
] ]
+1 -1
View File
@@ -16,8 +16,8 @@ class FinishBlock(Block):
return False return False
def on_collision(self, gameState: GameState): def on_collision(self, gameState: GameState):
gameState.set_stop_game(True)
gameState.set_game_won(True) gameState.set_game_won(True)
gameState.set_stop_game(True)
def to_string(self): def to_string(self):
return "🏁" return "🏁"
+24
View File
@@ -0,0 +1,24 @@
import os
from game.blocks.block import Block
from game.gameState import GameState
class SpikesBlock(Block):
@property
def character(self):
return '^'
@property
def type(self):
return 'spikes'
@property
def solid(self):
return False
def on_collision(self, gameState: GameState):
gameState.set_game_won(False)
gameState.set_stop_game(True)
def to_string(self):
return "🔥"
+3 -2
View File
@@ -44,10 +44,9 @@ class Game:
if spawn_point_position is None: if spawn_point_position is None:
raise ValueError("No spawn point found in the map.") raise ValueError("No spawn point found in the map.")
self.gameState.set_player_position(spawn_point_position) self.gameState.set_player_position(spawn_point_position)
self._position_update()
while not self.gameState.stopGame: while not self.gameState.stopGame:
self._position_update() # Update the screen for example
if self.gameState.playerVelocity == -1 and self.waitingForInputCallback: if self.gameState.playerVelocity == -1 and self.waitingForInputCallback:
self.gameState.set_player_velocity(self.waitingForInputCallback()) self.gameState.set_player_velocity(self.waitingForInputCallback())
self.gameState.add_move_count() self.gameState.add_move_count()
@@ -70,6 +69,8 @@ class Game:
self._update_player_velocity() self._update_player_velocity()
self._position_update() # Update the screen for example
if self.gameStopCallback and self.gameStopCallback(self.gameState): if self.gameStopCallback and self.gameStopCallback(self.gameState):
self.gameState.set_stop_game(True) self.gameState.set_stop_game(True)
break break
+9 -5
View File
@@ -1,12 +1,14 @@
from abc import abstractmethod from abc import abstractmethod
from game.game import Game
class GameHandler: class GameHandler:
def __init__(self, game: Game): def __init__(self):
self.game = game pass
self.game.set_waiting_for_input_callback(self.on_input_request)
self.game.set_position_update_callback(self.on_position_update) @abstractmethod
def run(self) -> None:
pass
@abstractmethod @abstractmethod
def on_position_update(self) -> None: def on_position_update(self) -> None:
@@ -15,3 +17,5 @@ class GameHandler:
@abstractmethod @abstractmethod
def on_input_request(self) -> str: def on_input_request(self) -> str:
pass pass
+39 -5
View File
@@ -1,14 +1,48 @@
from time import sleep from time import sleep
from game.game import Game
from game.handlers.gameHandler import GameHandler
from game.handlers.gameHandler import GameHandler
from game.game import Game
from game.gameState import GameState
from game.mapReader import MapReader
from game.mapSet import MapSet
class UserPlayerGameHandler(GameHandler): class UserPlayerGameHandler(GameHandler):
KEYBOARD_INPUTS = ["w", "q", "a", "z", "e", "d", "c"] KEYBOARD_INPUTS = ["w", "q", "a", "z", "e", "d", "c"]
def __init__(self, game: Game): def __init__(self):
super().__init__(game) super().__init__()
self.mapReader = MapReader()
self.mapSet = MapSet(self.mapReader, self.mapReader.get_map_names())
self.map = self._choose_map()
self.gameState = GameState()
self.game = Game(self.map, self.gameState)
self.game.set_waiting_for_input_callback(self.on_input_request)
self.game.set_position_update_callback(self.on_position_update)
def _choose_map(self):
"""
Prompt the user to choose a map from the available maps
"""
print("\nAvailable maps:")
for i, map_name in enumerate(self.mapReader.get_map_names()):
print(f"{i + 1}. {map_name.replace("_", " ").title()}")
while True:
try:
choice = int(input("Choose a map by number: ")) - 1
if 0 <= choice < len(self.mapReader.get_map_names()):
return self.mapSet.get_map(choice)
else:
print("Invalid choice. Please try again.")
except ValueError:
print("Invalid input. Please enter a number.")
def run(self) -> None:
"""
Run the game loop
"""
self.game.start_game()
print("Game over : You " + ("Won!" if self.game.gameState.gameWon else "Lost!"))
def on_position_update(self) -> None: def on_position_update(self) -> None:
""" """
+2 -2
View File
@@ -13,11 +13,11 @@ from game.blocks.block import Block
class Map: class Map:
def __init__(self, map_data: dict): def __init__(self, map_data: dict):
self.name = map_data.get('name', 'Unknown') self.name = map_data.get('name', 'Unknown')
self.width = int(map_data.get('width', 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.min_moves = map_data.get('min_moves', None)
self.layout = map_data.get('layout', []) self.layout = map_data.get('layout', [])
self.width = max(len(row) for row in self.layout) if self.layout else 0
self.height = len(self.layout) if self.layout else 0
def get_block_at(self, x, y) -> Block | None: def get_block_at(self, x, y) -> Block | None:
if 0 <= x < self.width and 0 <= y < self.height: if 0 <= x < self.width and 0 <= y < self.height:
+12 -8
View File
@@ -28,12 +28,12 @@ class MapReader:
with open(map_path, 'rb') as f: with open(map_path, 'rb') as f:
config = tomllib.load(f) config = tomllib.load(f)
layout = config.get('layout', '')
map_data = { map_data = {
'name': map_name, 'name': map_name,
'width': config.get('width', 0),
'height': config.get('height', 0),
'difficulty': config.get('difficulty', 0.0), 'difficulty': config.get('difficulty', 0.0),
'layout': self._parse_layout(config.get('layout', '')) 'layout': self._parse_layout(layout)
} }
if config.get('min_moves') is not None: if config.get('min_moves') is not None:
@@ -48,16 +48,20 @@ class MapReader:
def _parse_layout(self, mapString): def _parse_layout(self, mapString):
# Takes the blocks and translates their characters into the corresponding block objects. # Takes the blocks and translates their characters into the corresponding block objects.
block_characters_mapping = self._get_block_characters_mapping() # Makes sure tthe width is consistent across all rows by padding with None if necessary.
block_mapping = self._get_block_characters_mapping()
layout = [] layout = []
for line in mapString.strip().splitlines(): for line in mapString.splitlines():
row = [] row = []
for char in line: for char in line:
block_class = block_characters_mapping.get(char) block_class = block_mapping.get(char)
if block_class: if block_class:
row.append(block_class()) row.append(block_class())
else: else:
row.append(None) row.append(None) # Unknown character, treat as empty space
layout.append(row) layout.append(row)
max_width = max(len(row) for row in layout)
for row in layout:
while len(row) < max_width:
row.append(None) # Pad with None to ensure consistent width
return layout return layout
+6
View File
@@ -10,6 +10,12 @@ class MapSet:
self.mapReader = mapReader self.mapReader = mapReader
self.maps = [self.mapReader.read_map(name) for name in mapNames] self.maps = [self.mapReader.read_map(name) for name in mapNames]
def get_map_by_name(self, name: str):
for map in self.maps:
if map.name == name:
return map
raise ValueError(f"Map with name '{name}' not found in the MapSet.")
def get_map(self, index: int): def get_map(self, index: int):
if index < 0 or index >= len(self.maps): if index < 0 or index >= len(self.maps):
raise IndexError("Map index out of range.") raise IndexError("Map index out of range.")
+8 -9
View File
@@ -11,12 +11,11 @@ from game.mapSet import MapSet
load_dotenv() load_dotenv()
if __name__ == "__main__": if __name__ == "__main__":
mapReader = MapReader() # Make the user choose the game handler
mapSet = MapSet(mapReader, mapReader.get_map_names()) print("Choose a game handler:")
map = mapSet.get_maps_by_difficulty(0, 1)[0] for i, handler_name in enumerate(GAME_HANDLERS.keys()):
gameState = GameState() print(f"[{i + 1}] {handler_name}")
game = Game(map, gameState) choice = int(input("Enter the number of the game handler: ")) - 1
gameHandler = GAME_HANDLERS["user player"](game) handler_name = list(GAME_HANDLERS.keys())[choice]
game.start_game() game_handler_class = GAME_HANDLERS[handler_name]()
game_handler_class.run()
# run_game()
+22
View File
@@ -0,0 +1,22 @@
difficulty = 90
min_moves = 42
layout = """
#####
###### ########
## ####
### E##
### °#####
## ° ## ######
#°####### ## # # ### ######
# # ##
## ##° ##° ^###
# ### #### ##
# #####^##### ##
# ##### ####° #
# # #
# °# #####^^##
## ° ###^########
## # ##########
#S ##^#####
#########
"""
-2
View File
@@ -1,5 +1,3 @@
width = 12
height = 6
difficulty = 10 difficulty = 10
min_moves = 6 min_moves = 6
layout = """\ layout = """\