]> Untitled Git - axy/ft/pacman.git/commitdiff
fix dockstring, game over in progress
authorThomas Rimelen <trimelen@k1r3p4.42mulhouse.fr>
Wed, 21 Oct 2026 13:26:58 +0000 (15:26 +0200)
committerThomas Rimelen <trimelen@k1r3p4.42mulhouse.fr>
Wed, 21 Oct 2026 13:26:58 +0000 (15:26 +0200)
highscores.json
src/pacman/game/__init__.py

index 2ff216e6bf67fc3f3b7050c345b736ded4a29d03..416a8372f396b3555cf36d0aa1a6ba97595ff5dc 100644 (file)
@@ -2,6 +2,21 @@
     "scores": {
         "Axel": 30,
         "Thomas": 20,
-        "Garobert": 1
+        "Garobert": 1,
+        "agilliar": 5,
+        "agilliaras": 5,
+        "": 7,
+        "sdasd": 7,
+        "sdasdswwsw": 7,
+        "asads": 6,
+        "sswswss": 6,
+        "dds": 7,
+        "s": 8,
+        "sssd": 7,
+        "w": 8,
+        "ssswsws": 7,
+        "sssssaaa": 7,
+        "ssss": 8,
+        "sws": 8
     }
 }
\ No newline at end of file
index 39af5ec7bdd191254de0b16c04792f6ef4089b64..9040306d12f6c0f88eaa087ba24119c12328afdb 100644 (file)
@@ -149,6 +149,8 @@ class GameIsOver(Resource):
 
 @dataclass
 class PlayerName(Resource):
+    """Resource for player name."""
+
     name: str = ""
 
 
@@ -181,8 +183,7 @@ class GameMenu(Component):
 
 
 class GameOver(Component):
-    """Component for game menu items.
-    """
+    """Component for game menu items."""
 
     pass
 
@@ -224,9 +225,7 @@ class GhostStatus(Component):
 
 @dataclass
 class Ghost(Component):
-    """Component for ghost items storing the ghost variant and the home
-    coordinates and entity.
-    """
+    """Component for ghost."""
 
     variant: GhostVariant
     home: tuple[int, int]
@@ -249,9 +248,7 @@ class TilePos(Component):
 
 @dataclass
 class InMotion(Component):
-    """Component for pacman or ghost movement storing the position, the provenance
-    and the direction.
-    """
+    """Component for pacman or ghost movement."""
 
     src: Entity
     pos: Entity
@@ -260,17 +257,16 @@ class InMotion(Component):
 
 @dataclass
 class MoveSpeed(Component):
-    """Component for movement storing the movement speed by pixels per frames."""
+    """Component storing the movement speed by pixels per frames."""
 
     pixels_per_frame: float
 
 
 class Maze(Component):
-    """Component for maze storing the x and y dimension and the cells in a
-    dictionnary (coordonate entity).
-    """
+    """Component for maze storing dimension and cells."""
 
     def __init__(self, x: int, y: int) -> None:
+        """Init the maze with size."""
         self._cells: dict[tuple[int, int], Entity] = {}
         self._x = x
         self._y = y
@@ -278,7 +274,7 @@ class Maze(Component):
 
 @dataclass
 class Cell(Component):
-    """Component for the cells of the maze storing the value and the position."""
+    """Component for the cells of the maze."""
 
     n: int
     grid_pos: tuple[int, int]
@@ -299,9 +295,7 @@ def remove_comment(s: str) -> str:
 
 
 def load_config(world: World) -> None:
-    """Load the configuration json file given in first argument and spawn the
-    Config resource.
-    """
+    """Load the configuration json."""
     if len(sys.argv) != 2:
         raise Exception(
             f"Expected exactly 1 argument, got {len(sys.argv) - 1}"
@@ -332,7 +326,7 @@ def save_highscores(config: Config, scores: Highscores) -> None:
 def create_maze(
     world: World, assets: Assets[Sprite2D]
 ) -> Callable[[MazeGenerator], Entity]:
-    """System creating and adding the maze to the world."""
+    """Add the maze to the world."""
 
     def inner(maze: MazeGenerator) -> Entity:
         root_maze = Maze(len(maze.maze[0]), len(maze.maze))
@@ -375,7 +369,7 @@ def create_maze(
 def spawn_superpacgums(
     world: World, mazes: Query[Maze], assets: Assets[Sprite2D]
 ) -> None:
-    """System creating and adding super pacgums to the maze."""
+    """Add super pacgums to the maze."""
     pacgums = []
     for maze in mazes:
         for pos in [
@@ -406,7 +400,7 @@ def spawn_pacgums(
     superpacgums: Query[ChildOf, SuperPacGum],
     assets: Assets[Sprite2D],
 ) -> None:
-    """System creating and adding pacgums to the maze."""
+    """Add pacgums to the maze."""
     ignore = {e.parent for e in superpacgums}
     pacgums = []
     for ent, cell in cells:
@@ -432,7 +426,7 @@ def spawn_pacgums(
 
 
 def setup_maze(world: World) -> None:
-    """System running the 'create_maze' system with a give MazeGenerator."""
+    """Create maze with a given MazeGenerator."""
     world.run_system(create_maze, MazeGenerator((14, 14)))
 
 
@@ -440,7 +434,7 @@ def move_in_motion(
     entities: Query[tuple[InMotion, Coord2D, MoveSpeed]],
     positions: Query[Coord2D],
 ) -> None:
-    """System enabling movement operation for entity wit InMotion component."""
+    """Enable movement operation for entity wit InMotion component."""
     for motion, pos, speed in entities:
         if (dst := positions.get(motion.pos)) is None:
             continue
@@ -455,7 +449,7 @@ def update_tile_pos(
     entities: Query[tuple[InMotion, Coord2D, TilePos]],
     cells: Query[Coord2D],
 ) -> None:
-    """System updating the tile position for entity wit InMotion component."""
+    """Update the tile position for entity with InMotion component."""
     for motion, coord, pos in entities:
         src = cells[motion.src]
         dst = cells[motion.pos]
@@ -471,7 +465,7 @@ def spawn_pacman(
     cells: Query[Cell],
     config: Config,
 ) -> None:
-    """System creating and adding pacman to the maze."""
+    """Add pacman to the maze."""
     if (maze_t := maze_q.single()) is None:
         return
     maze, maze_ent = maze_t
@@ -523,7 +517,7 @@ def spawn_ghosts(
     maze_q: Query[tuple[Maze, Entity]],
     config: Config,
 ) -> None:
-    """System creating and adding ghosts to the maze."""
+    """Add ghosts to the maze."""
     if (maze_t := maze_q.single()) is None:
         return
     maze, maze_ent = maze_t
@@ -588,7 +582,7 @@ def move_pacman(
     cells: Query[tuple[Cell, Coord2D]],
     maze_q: Query[Maze],
 ) -> None:
-    """System giving target movement to pacman with the input given by the user."""
+    """Give target movement to pacman with the input given by the user."""
     if (maze := maze_q.single()) is None:
         return
     for coord, motion, pos in pacman_q:
@@ -617,7 +611,7 @@ def collect_pacgum(
     score: GameState,
     config: Config,
 ) -> None:
-    """System managing the collision and collection of pacgum by pacman."""
+    """Manage the collision and collection of pacgum by pacman."""
     collided = [
         pacgum
         for coord, pacgum in pacgums
@@ -636,9 +630,7 @@ def collect_superpacgum(
     score: GameState,
     config: Config,
 ) -> None:
-    """System managing the collision and collection of superpacgum by pacman and
-    starting the ghost flee state if a superpacgum is collected.
-    """
+    """Manage the collision and collection of superpacgum by pacman."""
     collided = [
         pacgum
         for pacgum_pos, pacgum in pacgums
@@ -660,7 +652,7 @@ def collect_superpacgum(
 
 
 def respawn_pacman(pacmans: Query[tuple[Pacman, Coord2D, InMotion]]) -> None:
-    """System reseting pacman position when touched by ghost."""
+    """Reset pacman position when touched by ghost."""
     for pacman, pos, motion in pacmans:
         pos.xy = Vector2(*pacman.home).elementwise() * 64
         motion.pos = pacman.home_entity
@@ -668,7 +660,7 @@ def respawn_pacman(pacmans: Query[tuple[Pacman, Coord2D, InMotion]]) -> None:
 
 
 def respawn_ghost(ghosts: Query[tuple[Ghost, Coord2D, InMotion]]) -> None:
-    """System reseting ghost fleeing position when touched by pacman."""
+    """Reset ghost fleeing position when touched by pacman."""
     for ghost, pos, motion in ghosts:
         pos.xy = Vector2(*ghost.home).elementwise() * 64
         motion.pos = ghost.home_entity
@@ -685,7 +677,7 @@ def collide_ghost(
     state: GameState,
     config: Config,
 ) -> None:
-    """System managing the collision between by pacman and ghost."""
+    """Manage the collision between by pacman and ghost."""
     for pacman_pos in pacmans:
         for ghost_pos, status, ghost, motion, pos in ghosts:
             if (
@@ -719,7 +711,7 @@ def move_to_target(
     cells: Query[tuple[Cell, Coord2D]],
     maze_q: Query[Maze],
 ) -> None:
-    """System giving target movement to ghost."""
+    """Give target movement to ghost."""
     if (maze := maze_q.single()) is None:
         return
     for target, motion, pos, status in entities:
@@ -759,7 +751,7 @@ def update_ghost_targets(
     pacman_q: Query[tuple[InMotion, TilePos], Pacman],
     cells: Query[Cell],
 ) -> None:
-    """System updating target to ghost."""
+    """Update target to ghost."""
     player = pacman_q.single()
     if player is None:
         return
@@ -786,7 +778,7 @@ def update_ghost_targets(
 
 
 def update_chase_status(status_q: Query[GhostStatus], config: Config) -> None:
-    """System updating ghost status and cycle."""
+    """Update ghost status and cycle."""
     for status in status_q:
         target_time: int
         next_state: GhostState
@@ -823,7 +815,7 @@ def update_chase_status(status_q: Query[GhostStatus], config: Config) -> None:
 def update_ghost_speed(
     ghost: Query[tuple[MoveSpeed, GhostStatus]], config: Config
 ) -> None:
-    """System updating ghost speed according to his state."""
+    """Update ghost speed according to his state."""
     for speed, status in ghost:
         match status.state:
             case GhostState.chase | GhostState.wander:
@@ -835,7 +827,7 @@ def update_ghost_speed(
 def update_pacman_anim(
     pacman_q: Query[tuple[InMotion, AnimationSet], Pacman],
 ) -> None:
-    """System updating pacman animation according to his direction."""
+    """Update pacman animation according to his direction."""
     for motion, anim in pacman_q:
         anim.idx = motion.direction
 
@@ -844,7 +836,7 @@ def update_ghost_anim(
     ghost_q: Query[tuple[InMotion, AnimationSet, GhostStatus], Ghost],
     config: Config,
 ) -> None:
-    """System updating pacman animation according to his direction and state."""
+    """Update ghost animation according to his direction and state."""
     for motion, anim, status in ghost_q:
         match status.state:
             case GhostState.chase | GhostState.wander:
@@ -860,15 +852,12 @@ def update_ghost_anim(
 
 
 def start_pause_timer(timer: "InitialPauseTimer") -> None:
-    """System starting the initial pause timer."""
+    """Start initial pause timer."""
     timer.tick = 0
 
 
 def setup_new_maze(world: World) -> None:
-    """System setting up the maze by running 'setup_maze', 'spawn_superpacgums',
-    'spawn_pacgums', 'spawn_pacman', 'spawn_ghosts' and
-    'start_pause_timer' systems.
-    """
+    """Set up all system to spawn a new maze."""
     for system in (
         setup_maze,
         spawn_superpacgums,
@@ -881,16 +870,17 @@ def setup_new_maze(world: World) -> None:
 
 
 def maze_exists(itr: Query[Maze]) -> bool:
-    """System cheking maze existance."""
+    """Return if maze exist."""
     return next(itr.__iter__(), None) is not None
 
 
 def game_is_over(game_over: Query[GameIsOver]) -> bool:
+    """Return if game is over."""
     return next(game_over.__iter__(), None) is not None
 
 
 def in_game(world: World) -> bool:
-    """System cheking that the game is running."""
+    """Return if the game is running."""
     return world.run_simple_system(
         maze_exists
     ) and not world.run_simple_system(in_game_menu)
@@ -899,7 +889,7 @@ def in_game(world: World) -> bool:
 def level_is_finished(
     pacgums: Query[SuperPacGum | PacGum], _: Not[GameIsOver]
 ) -> bool:
-    """System cheking the level completion."""
+    """Return if level is completed."""
     return next(iter(pacgums), None) is None
 
 
@@ -909,9 +899,7 @@ def victory(
     queue: CommandQueue,
     state: GameState,
 ) -> None:
-    """System called when level is completed, generating a new level or finishing
-    the game if all levels are completed.
-    """
+    """Despawn maze and spawn new maze or init game over."""
     for maze in mazes:
         queue.queue(hierarchy.despawn_with_children(maze))
     if state.level == 2:
@@ -931,56 +919,47 @@ def level_is_lost(
 
 
 def spawn_game_over(world: World, fonts: Assets[Font]) -> None:  # FIXME
+    """Display game over menu."""
+    root = world.spawn(
+        tuple[WindowPositionned, GameOver],
+        (
+            WindowPositionned(Vector2(0.5, 0), Vector2(0, 100)),
+            GameOver(),
+        ),
+    )
+    font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:100")
+    text = "GAME OVER"
     world.spawn(
-        tuple[WindowPositionned, ShouldRender, TextDisplay, GameOver],
+        tuple[ChildOf, ShouldRender, CoordBundle, TextDisplay],
         (
-            WindowPositionned(Vector2(0.5, 0), Vector2(-300, 100)),
+            ChildOf(root),
             ShouldRender(),
-            TextDisplay(
-                "GAME OVER",
-                fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:100"),
-            ),
-            GameOver(),
+            font.center_text_for(text, Vector2(0, 0)),
+            TextDisplay(text, font),
         ),
     )
 
+    font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:40")
     world.spawn(
-        tuple[
-            WindowPositionned,
-            ShouldRender,
-            ComputedText,
-            TextDisplay,
-            GameOver,
-        ],
+        tuple[ChildOf, ShouldRender, CoordBundle, ComputedText, TextDisplay],
         (
-            WindowPositionned(Vector2(0.5, 0), Vector2(-100, 300)),
+            ChildOf(root),
             ShouldRender(),
-            ComputedText(cast(System[str], print_score)),
-            TextDisplay(
-                "Score",
-                fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:40"),
-            ),
-            GameOver(),
+            font.center_text_for("", Vector2(0, 300)),
+            ComputedText(cast(System[str], print_name)),
+            TextDisplay("", font),
         ),
     )
 
+    font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30")
     world.spawn(
-        tuple[
-            WindowPositionned,
-            ShouldRender,
-            ComputedText,
-            TextDisplay,
-            GameOver,
-        ],
+        tuple[ChildOf, ShouldRender, CoordBundle, ComputedText, TextDisplay],
         (
-            WindowPositionned(Vector2(0.5, 0), Vector2(-300, 400)),
+            ChildOf(root),
             ShouldRender(),
-            ComputedText(cast(System[str], print_name)),
-            TextDisplay(
-                "Score",
-                fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30"),
-            ),
-            GameOver(),
+            font.center_text_for("Score = XXX", Vector2(0, 200)),
+            ComputedText(cast(System[str], print_score)),
+            TextDisplay("", font),
         ),
     )
 
@@ -992,7 +971,7 @@ def lose(
     mazes: Query[Entity, Maze],
     queue: CommandQueue,
 ) -> None:
-    """System called when the player lose the game."""
+    """Despawn mane and run the game over."""
     world.despawn_many(list(gui))
     for maze in mazes:
         queue.queue(hierarchy.despawn_with_children(maze))
@@ -1001,20 +980,20 @@ def lose(
 
 
 def game_over(queue: CommandQueue) -> None:
-    """System called when game is finish."""
+    """Spawn resources when game is over."""
     queue.queue(lambda world: world.spawn_resource(GameIsOver()))
     queue.queue(lambda world: world.spawn_resource(PlayerName()))
 
 
 def update_game_timer(world: World, state: GameState) -> None:
-    """System updating the levels timer."""
+    """Update the level timer."""
     if world.run_simple_system(cheat_mode_enabled):
         return
     state.timer += 1
 
 
 def initial_pause_done(timer: InitialPauseTimer, config: Config) -> bool:
-    """System cheking if the inital timer is done."""
+    """Return if the inital timer is done."""
     if timer.tick < config.initial_pos_timer:
         timer.tick += 1
         return False
@@ -1022,8 +1001,7 @@ def initial_pause_done(timer: InitialPauseTimer, config: Config) -> bool:
 
 
 def print_name(name: PlayerName) -> str:
-    """Return a string with the player score.
-    """
+    """Return a string with the player score."""
     return f"Enter your name: {name.name}"
 
 
@@ -1044,9 +1022,7 @@ def print_timer(state: GameState, config: Config) -> str:
 
 
 def spawn_hud(world: World, fonts: Assets[Font]) -> None:
-    """System that print the Heads-Up Display in game with lives, levels and timer
-    informations.
-    """
+    """Draw in game HUD with lives, levels and timer informations."""
     world.spawn_many(
         tuple[
             WindowPositionned,
@@ -1084,7 +1060,7 @@ def start_game(
     hierarchy: Hierarchy[tuple[()]],
     menus: Query[Entity, MainMenu],
 ) -> None:
-    """System closing main menu and starting the game and Heads-Up Display."""
+    """Close main menu and starting the game and HUD."""
     for menu in menus:
         hierarchy.despawn_with_children(menu)(world)
     world.despawn_resource(SelectedButton)
@@ -1095,14 +1071,14 @@ def start_game(
 
 
 def quit_game(world: World) -> None:
-    """System closing the game and quiting the program."""
+    """Close the game and quiting the program."""
     world.spawn_resource(WorldShouldExit())
 
 
 def spawn_button(
     world: World, fonts: Assets[Font], sprites: Assets[Sprite2D]
 ) -> Callable[[WindowPositionned, str, System], Entity]:
-    """System creating and adding a button to the world."""
+    """Add a button to the world."""
 
     def inner(pos: WindowPositionned, s: str, cb: System) -> Entity:
         parent = world.spawn(
@@ -1144,6 +1120,7 @@ def spawn_button(
 
 
 def spawn_cheat_info(world: World, fonts: Assets[Font]) -> None:
+    """Display 'CHEAT MODE ENABLED' on screen."""
     world.spawn(
         tuple[
             WindowPositionned,
@@ -1166,20 +1143,24 @@ def spawn_cheat_info(world: World, fonts: Assets[Font]) -> None:
 
 
 def cheat_mode_enabled(cheat: Query[CheatMode]) -> bool:
+    """Return if cheat mode is emable."""
     return next(cheat.__iter__(), None) is not None
 
 
 def enable_cheat_mode(world: World) -> None:
+    """Enable cheat mod."""
     world.run_simple_system(spawn_cheat_info)
     world.spawn(CheatMode, CheatMode())
 
 
 def disable_cheat_mode(world: World, cheats: Query[Entity, CheatMode]) -> None:
+    """Disable cheat mod."""
     items: list[Entity] = [cheat for cheat in cheats]
     world.despawn_many(items)
 
 
 def toggle_cheat_mode(world: World) -> None:
+    """Enable or disable cheat mod."""
     if world.run_simple_system(cheat_mode_enabled):
         world.run_simple_system(disable_cheat_mode)
     else:
@@ -1193,7 +1174,7 @@ def close_current_game(
     gui: Query[Entity, GameGui],
     queue: CommandQueue,
 ) -> None:
-    """System removing maze with childrens and game graphical user interface."""
+    """Remove maze with childrens and game GUI."""
     world.run_simple_system(close_game_menu)
     world.despawn_resource(GameState)
     world.despawn_many(list(gui))
@@ -1204,18 +1185,18 @@ def close_current_game(
 
 
 def back_to_main_menu(world: World) -> None:
-    """System closing the current game and opening the main menu."""
+    """Close the current game and open the main menu."""
     world.run_simple_system(close_current_game)
     world.run_simple_system(setup_main_menu)
 
 
 def in_game_menu(menus: Query[GameMenu]) -> bool:
-    """System cheking if the main menu is running."""
+    """Check if the main menu is running."""
     return next(menus.__iter__(), None) is not None
 
 
 def toggle_game_menu(world: World) -> None:
-    """System opening or closing the game menu."""
+    """Open or close the game menu."""
     if world.run_simple_system(in_game_menu):
         world.run_simple_system(close_game_menu)
     else:
@@ -1412,36 +1393,50 @@ def setup_main_menu(world: World) -> None:
 
 
 def test(events: WindowEvents) -> None:  # FIXME
+    """Test."""
     for key, state in events.keys_itr():
         if state:
             print(key)
 
 
 def add_score(
-    world: World, name: PlayerName, state: GameState, scores: Highscores
+    world: World,
+    name: PlayerName,
+    state: GameState,
+    scores: Highscores,
+    menus: Query[Entity, GameOver],
+    queue: CommandQueue,
+    hierarchy: Hierarchy[tuple[()]],
 ) -> None:
+    """Add players score to highscores, save it and go to main menu."""
     # FIXME
     scores.scores[name.name] = state.score
     world.run_simple_system(save_highscores)
+    # items = [item for item in menus]
+    # # world.despawn_many(items)
+    for menu in menus:
+        queue.queue(hierarchy.despawn_with_children(menu))
+    world.despawn_resource(GameIsOver)
     world.run_simple_system(setup_main_menu)
 
 
 def get_player_name(
     world: World, events: WindowEvents, name: PlayerName
 ) -> None:
+    """Get the player name."""
     for key, state in events.keys_itr():
         if state and key == K_BACKSPACE and name.name != "":
             name.name = name.name[: len(name.name) - 1]
         if state and key in range(K_a, K_z) and len(name.name) < 10:
             char = "abcdefghijklmnopqrstuvwxyz"[key - K_a]
             name.name += char
-        if state and key == K_RETURN:
+        if state and key == K_RETURN and name.name != "":
             world.run_simple_system(add_score)
             return
 
 
 def run_game() -> None:
-    """Start with all necessary plugins and system to run the game."""
+    """Run the game with all necessary plugins and system."""
     World().with_plugins(graphics_plugins, minimal_plugins).with_systems(
         StartupSchedule,
         Systems(load_config, load_highscores, setup_main_menu)
@@ -1458,7 +1453,6 @@ def run_game() -> None:
             update_chase_status,
             update_ghost_speed,
             update_game_timer,
-            test,
         )
         .chain()
         .run_if(initial_pause_done, in_game)