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 .spawnPointBlock import SpawnPointBlock
|
||||
from .finishBlock import FinishBlock
|
||||
from .coinBlock import CoinBlock
|
||||
|
||||
BLOCKS = [
|
||||
StoneBlock,
|
||||
SpawnPointBlock,
|
||||
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 "")
|
||||
+39
-31
@@ -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 provides the gameState object to access the current state of the game.
|
||||
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 change the current map.
|
||||
"""
|
||||
@@ -20,16 +20,21 @@ class Game:
|
||||
self.map = map
|
||||
self.gameState = gameState
|
||||
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
|
||||
|
||||
def set_position_update_callback(self, callback):
|
||||
def set_position_update_callback(self, callback: callable):
|
||||
self.positionUpdateCallback = callback
|
||||
|
||||
def _position_update(self):
|
||||
if self.positionUpdateCallback:
|
||||
self.positionUpdateCallback()
|
||||
|
||||
def set_game_stop_callback(self, callback: callable):
|
||||
self.gameStopCallback = callback
|
||||
|
||||
def start_game(self):
|
||||
self.gameState.set_stop_game(False)
|
||||
@@ -48,13 +53,15 @@ class Game:
|
||||
self.gameState.add_move_count()
|
||||
|
||||
# Try to move the player according to his velocity
|
||||
block_at_velocity = self._get_block_at_velocity()
|
||||
if not block_at_velocity or not block_at_velocity.solid:
|
||||
velocity_position = self.get_position_at_velocity() # TODO : optimize
|
||||
velocity_position = self.get_position_at_velocity()
|
||||
block_at_velocity = None
|
||||
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:
|
||||
self.gameState.set_player_position(velocity_position)
|
||||
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
|
||||
player_position = self.gameState.get_player_position()
|
||||
block_at_player_position = self.map.get_block_at(player_position[0], player_position[1] + 1)
|
||||
@@ -62,7 +69,10 @@ class Game:
|
||||
self.gameState.set_player_velocity(-1) # Stop the player from falling
|
||||
|
||||
self._update_player_velocity()
|
||||
|
||||
|
||||
if self.gameStopCallback and self.gameStopCallback(self.gameState):
|
||||
self.gameState.set_stop_game(True)
|
||||
break
|
||||
|
||||
def _update_player_velocity(self):
|
||||
current_velocity = self.gameState.playerVelocity
|
||||
@@ -100,29 +110,27 @@ class Game:
|
||||
player_position = self.gameState.get_player_position()
|
||||
current_velocity = self.gameState.playerVelocity
|
||||
|
||||
if current_velocity == -1:
|
||||
return None
|
||||
|
||||
if current_velocity == "down":
|
||||
return (player_position[0], player_position[1] + 1)
|
||||
|
||||
if current_velocity == "left_down":
|
||||
return (player_position[0] - 1, player_position[1] + 1)
|
||||
|
||||
if current_velocity == "right_down":
|
||||
return (player_position[0] + 1, player_position[1] + 1)
|
||||
|
||||
if current_velocity == "left":
|
||||
return (player_position[0] - 1, player_position[1])
|
||||
|
||||
if current_velocity == "right":
|
||||
return (player_position[0] + 1, player_position[1])
|
||||
|
||||
if current_velocity == "left_up":
|
||||
return (player_position[0] - 1, player_position[1] - 1)
|
||||
|
||||
if current_velocity == "right_up":
|
||||
return (player_position[0] + 1, player_position[1] - 1)
|
||||
match current_velocity:
|
||||
case -1:
|
||||
return None
|
||||
case "down":
|
||||
return (player_position[0], player_position[1] + 1)
|
||||
case "up":
|
||||
return (player_position[0], player_position[1] - 1)
|
||||
case "left_down":
|
||||
return (player_position[0] - 1, player_position[1] + 1)
|
||||
case "right_down":
|
||||
return (player_position[0] + 1, player_position[1] + 1)
|
||||
case "left":
|
||||
return (player_position[0] - 1, player_position[1])
|
||||
case "right":
|
||||
return (player_position[0] + 1, player_position[1])
|
||||
case "left_up":
|
||||
return (player_position[0] - 1, player_position[1] - 1)
|
||||
case "right_up":
|
||||
return (player_position[0] + 1, player_position[1] - 1)
|
||||
case _:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Represents the current state of the game.
|
||||
Contains :
|
||||
- Player score
|
||||
- Player coins
|
||||
- stopGame flag
|
||||
- Player position
|
||||
"""
|
||||
@@ -10,15 +10,12 @@ class GameState:
|
||||
PLAYER_VELOCITIES = [*PLAYER_MOVEMENTS, "down"]
|
||||
|
||||
def __init__(self):
|
||||
self.score = 0
|
||||
self.playerPosition = (0, 0)
|
||||
self.stopGame = False
|
||||
self.movesCount = 0
|
||||
self.playerVelocity = -1 # -1 means no velocity
|
||||
self.gameWon = False
|
||||
|
||||
def add_score(self, points):
|
||||
self.score += points
|
||||
self.coins = 0
|
||||
|
||||
def set_player_position(self, x, y):
|
||||
self.playerPosition = (x, y)
|
||||
@@ -43,4 +40,7 @@ class GameState:
|
||||
self.stopGame = stop
|
||||
|
||||
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
|
||||
"""
|
||||
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)
|
||||
|
||||
def on_input_request(self) -> str:
|
||||
"""
|
||||
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.height = int(map_data.get('height', 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', [])
|
||||
|
||||
def get_block_at(self, x, y) -> Block | None:
|
||||
@@ -38,6 +39,9 @@ class Map:
|
||||
return block.type
|
||||
return None
|
||||
|
||||
def get_min_moves(self):
|
||||
return self.min_moves
|
||||
|
||||
def to_string_dict(self):
|
||||
"""
|
||||
Returns a string matrix representation of the map layout.
|
||||
|
||||
@@ -36,6 +36,9 @@ class MapReader:
|
||||
'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)
|
||||
|
||||
def _get_block_characters_mapping(self):
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from game.game import Game
|
||||
from game.gameState import GameState
|
||||
from game.handlers import GAME_HANDLERS
|
||||
from game.map import Map
|
||||
from game.mapReader import MapReader
|
||||
from game.mapSet import MapSet
|
||||
|
||||
# loading variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
if __name__ == "__main__":
|
||||
mapReader = MapReader()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
width = 12
|
||||
height = 6
|
||||
difficulty = 10
|
||||
min_moves = 6
|
||||
layout = """\
|
||||
############
|
||||
# E#
|
||||
|
||||
Reference in New Issue
Block a user