mirror of
https://github.com/kristoferssolo/Tetris.git
synced 2025-10-21 20:00:35 +00:00
feat(game): movemnt keys from file
This commit is contained in:
parent
f1e854a38c
commit
8e3ed493e0
@ -1,21 +1,20 @@
|
||||
[keybinds]
|
||||
rotate_colockwise = "up arrow"
|
||||
hard_drop = "space"
|
||||
hold = "shift+c"
|
||||
rotate_counter_colockwise = "ctrl+z"
|
||||
pause = "esc+F1"
|
||||
move_left = "left arrow"
|
||||
move_right = "right arrow"
|
||||
move_down = "down arrow"
|
||||
[General]
|
||||
pause = ["esc", "F1"]
|
||||
quit = ["q"]
|
||||
|
||||
[numpad]
|
||||
hold = "numpad 0"
|
||||
hard_drop = "numpad 5"
|
||||
move_left = "numpad 4"
|
||||
move_right = "numpad 6"
|
||||
soft_drop = "numpad 2"
|
||||
rotate_colockwise_1 = "numpad 1"
|
||||
rotate_colockwise_2 = "numpad 5"
|
||||
rotate_colockwise_3 = "numpad 9"
|
||||
rotate_counter_colockwise_1 = "numpad 3"
|
||||
rotate_counter_colockwise_2 = "numpad 7"
|
||||
[Movement]
|
||||
left = ["left", "kp4"]
|
||||
right = ["right", "kp6"]
|
||||
down = ["down", "kp2"]
|
||||
|
||||
[Rotation]
|
||||
cw = ["x", "up", "kp1", "kp5", "kp9"] # clockwise
|
||||
ccw = ["ctrl", "z", "kp3", "kp7"] # counter-clockwise
|
||||
|
||||
[Action]
|
||||
hold = ["shift", "c", "kp0"]
|
||||
drop = ["space", "kp5"]
|
||||
|
||||
[Sound]
|
||||
music = true
|
||||
volume = 0.5
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import pygame
|
||||
from utils import CONFIG, Figure, GameMode
|
||||
@ -27,8 +27,9 @@ class Game(BaseScreen):
|
||||
music: Music that plays in the background.
|
||||
"""
|
||||
|
||||
def __init__(self, game_mode: GameMode) -> None:
|
||||
def __init__(self, game_mode: GameMode, settings: dict[str, Any]) -> None:
|
||||
self.game_mode = game_mode
|
||||
self.settings = settings
|
||||
self._initialize_game_components()
|
||||
self._start_background_music()
|
||||
|
||||
@ -60,7 +61,9 @@ class Game(BaseScreen):
|
||||
self.clock = pygame.time.Clock()
|
||||
self.next_figure: Figure = self._generate_next_figure()
|
||||
|
||||
self.tetris = Tetris(self._get_next_figure, self._update_score, self.game_mode)
|
||||
self.tetris = Tetris(
|
||||
self._get_next_figure, self._update_score, self.game_mode, self.settings
|
||||
)
|
||||
self.score = Score(self.game_mode)
|
||||
self.preview = Preview()
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import sys
|
||||
from typing import Optional
|
||||
|
||||
import pygame
|
||||
from utils import CONFIG, GameMode
|
||||
from utils import CONFIG, PYGAME_EVENT, GameMode, read_settings
|
||||
|
||||
from game.log import log
|
||||
|
||||
@ -13,13 +13,14 @@ from .game import Game
|
||||
|
||||
class Main(BaseScreen, SceenElement, TextScreen):
|
||||
def __init__(self, mode: GameMode) -> None:
|
||||
# log.info("Initializing the game")
|
||||
log.info("Initializing the game")
|
||||
self._initialize_pygame()
|
||||
self._initialize_surface()
|
||||
self._initialize_rect()
|
||||
self._initialize_font()
|
||||
self._set_buttons()
|
||||
self._initialize_increment_height()
|
||||
self.settings = read_settings()
|
||||
self.game_mode = mode
|
||||
self.game: Optional[Game] = None
|
||||
|
||||
@ -36,7 +37,9 @@ class Main(BaseScreen, SceenElement, TextScreen):
|
||||
if event.type == pygame.QUIT:
|
||||
self.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_q:
|
||||
if event.key in [
|
||||
PYGAME_EVENT[key] for key in self.settings["General"]["quit"]
|
||||
]:
|
||||
self.exit()
|
||||
|
||||
if not self.game:
|
||||
@ -61,13 +64,13 @@ class Main(BaseScreen, SceenElement, TextScreen):
|
||||
|
||||
def exit(self) -> None:
|
||||
"""Exit the game."""
|
||||
# log.info("Exiting the game")
|
||||
log.info("Exiting the game")
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
def play(self) -> "Main":
|
||||
self._draw_background()
|
||||
self.game = Game(self.game_mode)
|
||||
self.game = Game(self.game_mode, self.settings)
|
||||
return self
|
||||
|
||||
def _set_buttons(self) -> None:
|
||||
|
||||
@ -2,7 +2,7 @@ from typing import Any, Callable, Optional
|
||||
|
||||
import numpy as np
|
||||
import pygame
|
||||
from utils import CONFIG, Direction, Figure, GameMode, Rotation
|
||||
from utils import CONFIG, PYGAME_EVENT, Direction, Figure, GameMode, Rotation
|
||||
|
||||
from game.log import log
|
||||
from game.sprites import Block, Tetromino
|
||||
@ -45,10 +45,12 @@ class Tetris(BaseScreen):
|
||||
get_next_figure: Callable[[], Figure],
|
||||
update_score: Callable[[int, int, int], None],
|
||||
game_mode: GameMode,
|
||||
settings: dict[str, Any],
|
||||
) -> None:
|
||||
self._initialize_surface()
|
||||
self._initialize_rect()
|
||||
self._initialize_sprites()
|
||||
self.settings = settings
|
||||
|
||||
self.get_next_figure = get_next_figure
|
||||
self.update_score = update_score
|
||||
@ -150,8 +152,8 @@ class Tetris(BaseScreen):
|
||||
self._check_finished_rows()
|
||||
|
||||
self.game_over: bool = self._check_game_over()
|
||||
# if self.game_over:
|
||||
# self.restart()
|
||||
if self.game_over:
|
||||
self.restart()
|
||||
|
||||
self.tetromino = Tetromino(
|
||||
self.sprites,
|
||||
@ -171,13 +173,13 @@ class Tetris(BaseScreen):
|
||||
"""
|
||||
for block in self.tetromino.blocks:
|
||||
if block.pos.y <= 0:
|
||||
# log.info("Game over!")
|
||||
log.info("Game over!")
|
||||
return True
|
||||
return False
|
||||
|
||||
def restart(self) -> None:
|
||||
"""Restart the game."""
|
||||
# log.info("Restarting the game")
|
||||
log.info("Restarting the game")
|
||||
self._reset_game_state()
|
||||
self._initialize_field_and_tetromino()
|
||||
self.game_over = False
|
||||
@ -356,17 +358,19 @@ class Tetris(BaseScreen):
|
||||
"""
|
||||
Handle movement keys.
|
||||
|
||||
Move right [K_d, K_l].
|
||||
Move left [K_a, K_h].
|
||||
See `settings.toml` for the default key bindings.
|
||||
"""
|
||||
right_keys = keys[pygame.K_d] or keys[pygame.K_l]
|
||||
left_keys = keys[pygame.K_a] or keys[pygame.K_h]
|
||||
right_keys = [PYGAME_EVENT[key] for key in self.settings["Movement"]["right"]]
|
||||
right_key_pressed = any(keys[key] for key in right_keys)
|
||||
|
||||
left_keys = [PYGAME_EVENT[key] for key in self.settings["Movement"]["left"]]
|
||||
left_key_pressed = any(keys[key] for key in left_keys)
|
||||
|
||||
if not self.timers.horizontal.active:
|
||||
if left_keys:
|
||||
if left_key_pressed:
|
||||
self.move_left()
|
||||
self.timers.horizontal.activate()
|
||||
elif right_keys:
|
||||
elif right_key_pressed:
|
||||
self.move_right()
|
||||
self.timers.horizontal.activate()
|
||||
|
||||
@ -374,46 +378,49 @@ class Tetris(BaseScreen):
|
||||
"""
|
||||
Handle rotation keys.
|
||||
|
||||
Rotation clockwise [K_RIGHT, K_UP, K_r, K_w, K_k].
|
||||
Rotation counter-clockwise [K_LEFT, K_e, K_i].
|
||||
See `settings.toml` for the default key bindings.
|
||||
"""
|
||||
clockwise_keys = (
|
||||
keys[pygame.K_r]
|
||||
or keys[pygame.K_UP]
|
||||
or keys[pygame.K_w]
|
||||
or keys[pygame.K_k]
|
||||
or keys[pygame.K_RIGHT]
|
||||
)
|
||||
cw_keys = [PYGAME_EVENT[key] for key in self.settings["Rotation"]["cw"]]
|
||||
cw_key_pressed = any(keys[key] for key in cw_keys)
|
||||
|
||||
counter_clockwise_keys = (
|
||||
keys[pygame.K_e] or keys[pygame.K_i] or keys[pygame.K_LEFT]
|
||||
)
|
||||
ccw_keys = [PYGAME_EVENT[key] for key in self.settings["Rotation"]["ccw"]]
|
||||
ccw_key_pressed = any(keys[key] for key in ccw_keys)
|
||||
|
||||
if not self.timers.rotation.active:
|
||||
if clockwise_keys:
|
||||
if cw_key_pressed:
|
||||
self.rotate()
|
||||
self.timers.rotation.activate()
|
||||
|
||||
if counter_clockwise_keys:
|
||||
if ccw_key_pressed:
|
||||
self.rotate_reverse()
|
||||
self.timers.rotation.activate()
|
||||
|
||||
def _handle_down_key(self, keys: pygame.key.ScancodeWrapper) -> None:
|
||||
"""Handle the down key [K_DOWN, K_s, K_j]."""
|
||||
down_keys = keys[pygame.K_DOWN] or keys[pygame.K_s] or keys[pygame.K_j]
|
||||
if not self.down_pressed and down_keys:
|
||||
"""
|
||||
Handle the down key.
|
||||
|
||||
See `settings.toml` for the default key bindings.
|
||||
"""
|
||||
down_keys = [PYGAME_EVENT[key] for key in self.settings["Movement"]["down"]]
|
||||
down_key_pressed = any(keys[key] for key in down_keys)
|
||||
if not self.down_pressed and down_key_pressed:
|
||||
self.down_pressed = True
|
||||
self.timers.vertical.duration = self.increased_block_speed
|
||||
|
||||
if self.down_pressed and not down_keys:
|
||||
if self.down_pressed and not down_key_pressed:
|
||||
self.down_pressed = False
|
||||
self.timers.vertical.duration = self.initial_block_speed
|
||||
|
||||
def _handle_drop_key(self, keys: pygame.key.ScancodeWrapper) -> None:
|
||||
"""Handle the drop key [K_SPACE]."""
|
||||
drop_keys = keys[pygame.K_SPACE]
|
||||
"""
|
||||
Handle the drop key.
|
||||
|
||||
if not self.timers.drop.active and drop_keys:
|
||||
See `settings.toml` for the default key bindings.
|
||||
"""
|
||||
drop_keys = [PYGAME_EVENT[key] for key in self.settings["Action"]["drop"]]
|
||||
drop_key_pressed = any(keys[key] for key in drop_keys)
|
||||
|
||||
if not self.timers.drop.active and drop_key_pressed:
|
||||
self.drop()
|
||||
self.timers.drop.activate()
|
||||
|
||||
|
||||
@ -203,7 +203,7 @@ class Tetromino:
|
||||
"""
|
||||
return all(
|
||||
0 <= pos.x < CONFIG.game.columns
|
||||
and 0 <= pos.y < CONFIG.game.rows
|
||||
and -2 <= pos.y < CONFIG.game.rows
|
||||
and not self.field[int(pos.y), int(pos.x)]
|
||||
for pos in new_positions
|
||||
)
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
from .config import CONFIG
|
||||
from .enum import Direction, GameMode, Rotation
|
||||
from .events import PYGAME_EVENT
|
||||
from .figure import Figure, FigureConfig
|
||||
from .log import log
|
||||
from .path import BASE_PATH
|
||||
from .settings import read_settings, save_settings
|
||||
from .tuples import BestMove, Size
|
||||
from .weights import Weights
|
||||
from .tuples import Size
|
||||
|
||||
__all__ = [
|
||||
"BASE_PATH",
|
||||
@ -17,8 +17,7 @@ __all__ = [
|
||||
"Direction",
|
||||
"Rotation",
|
||||
"GameMode",
|
||||
"Weights",
|
||||
"BestMove",
|
||||
"read_settings",
|
||||
"save_settings",
|
||||
"PYGAME_EVENT",
|
||||
]
|
||||
|
||||
@ -21,7 +21,7 @@ class Game:
|
||||
size: Size = Size(columns * cell.width, rows * cell.width)
|
||||
pos: Vec2 = Vec2(padding, padding)
|
||||
offset: Vec2 = Vec2(columns // 2, -1)
|
||||
initial_speed: float | int = 100
|
||||
initial_speed: float | int = 300
|
||||
movment_delay: int = 150
|
||||
rotation_delay: int = 200
|
||||
drop_delay: int = 200
|
||||
|
||||
94
src/utils/events.py
Normal file
94
src/utils/events.py
Normal file
@ -0,0 +1,94 @@
|
||||
import pygame
|
||||
|
||||
PYGAME_EVENT: dict[str, int] = {
|
||||
"up": pygame.K_UP,
|
||||
"down": pygame.K_DOWN,
|
||||
"left": pygame.K_LEFT,
|
||||
"right": pygame.K_RIGHT,
|
||||
"space": pygame.K_SPACE,
|
||||
"escape": pygame.K_ESCAPE,
|
||||
"q": pygame.K_q,
|
||||
"w": pygame.K_w,
|
||||
"e": pygame.K_e,
|
||||
"r": pygame.K_r,
|
||||
"t": pygame.K_t,
|
||||
"y": pygame.K_y,
|
||||
"u": pygame.K_u,
|
||||
"i": pygame.K_i,
|
||||
"o": pygame.K_o,
|
||||
"p": pygame.K_p,
|
||||
"a": pygame.K_a,
|
||||
"s": pygame.K_s,
|
||||
"d": pygame.K_d,
|
||||
"f": pygame.K_f,
|
||||
"g": pygame.K_g,
|
||||
"h": pygame.K_h,
|
||||
"j": pygame.K_j,
|
||||
"k": pygame.K_k,
|
||||
"l": pygame.K_l,
|
||||
"z": pygame.K_z,
|
||||
"x": pygame.K_x,
|
||||
"c": pygame.K_c,
|
||||
"v": pygame.K_v,
|
||||
"b": pygame.K_b,
|
||||
"n": pygame.K_n,
|
||||
"m": pygame.K_m,
|
||||
"1": pygame.K_1,
|
||||
"2": pygame.K_2,
|
||||
"3": pygame.K_3,
|
||||
"4": pygame.K_4,
|
||||
"5": pygame.K_5,
|
||||
"6": pygame.K_6,
|
||||
"7": pygame.K_7,
|
||||
"8": pygame.K_8,
|
||||
"9": pygame.K_9,
|
||||
"0": pygame.K_0,
|
||||
"shift": pygame.K_LSHIFT,
|
||||
"ctrl": pygame.K_LCTRL,
|
||||
"alt": pygame.K_LALT,
|
||||
"tab": pygame.K_TAB,
|
||||
"capslock": pygame.K_CAPSLOCK,
|
||||
"return": pygame.K_RETURN,
|
||||
"backspace": pygame.K_BACKSPACE,
|
||||
"insert": pygame.K_INSERT,
|
||||
"delete": pygame.K_DELETE,
|
||||
"home": pygame.K_HOME,
|
||||
"end": pygame.K_END,
|
||||
"pageup": pygame.K_PAGEUP,
|
||||
"pagedown": pygame.K_PAGEDOWN,
|
||||
"numlock": pygame.K_NUMLOCK,
|
||||
"printscreen": pygame.K_PRINTSCREEN,
|
||||
"scrolllock": pygame.K_SCROLLLOCK,
|
||||
"pause": pygame.K_PAUSE,
|
||||
"F1": pygame.K_F1,
|
||||
"F2": pygame.K_F2,
|
||||
"F3": pygame.K_F3,
|
||||
"F4": pygame.K_F4,
|
||||
"F5": pygame.K_F5,
|
||||
"F6": pygame.K_F6,
|
||||
"F7": pygame.K_F7,
|
||||
"F8": pygame.K_F8,
|
||||
"F9": pygame.K_F9,
|
||||
"F10": pygame.K_F10,
|
||||
"F11": pygame.K_F11,
|
||||
"F12": pygame.K_F12,
|
||||
"F13": pygame.K_F13,
|
||||
"F14": pygame.K_F14,
|
||||
"F15": pygame.K_F15,
|
||||
"kp0": pygame.K_KP0,
|
||||
"kp1": pygame.K_KP1,
|
||||
"kp2": pygame.K_KP2,
|
||||
"kp3": pygame.K_KP3,
|
||||
"kp4": pygame.K_KP4,
|
||||
"kp5": pygame.K_KP5,
|
||||
"kp6": pygame.K_KP6,
|
||||
"kp7": pygame.K_KP7,
|
||||
"kp8": pygame.K_KP8,
|
||||
"kp9": pygame.K_KP9,
|
||||
"kp_divide": pygame.K_KP_DIVIDE,
|
||||
"kp_multiply": pygame.K_KP_MULTIPLY,
|
||||
"kp_minus": pygame.K_KP_MINUS,
|
||||
"kp_plus": pygame.K_KP_PLUS,
|
||||
"kp_enter": pygame.K_KP_ENTER,
|
||||
"kp_equals": pygame.K_KP_EQUALS,
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import toml
|
||||
|
||||
from .config import CONFIG, Config
|
||||
from .log import log
|
||||
from .path import BASE_PATH
|
||||
|
||||
|
||||
def save_settings(settings: Config, file_path: Path) -> None:
|
||||
@ -12,7 +13,9 @@ def save_settings(settings: Config, file_path: Path) -> None:
|
||||
toml.dump(settings, file)
|
||||
|
||||
|
||||
def read_settings(file_path: Path) -> Optional[dict[str, str]]:
|
||||
def read_settings(
|
||||
file_path: Path = BASE_PATH / "settings.toml",
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Read and parse a TOML file and return the content as a dictionary.
|
||||
|
||||
@ -27,7 +30,7 @@ def read_settings(file_path: Path) -> Optional[dict[str, str]]:
|
||||
return toml.load(file)
|
||||
except FileNotFoundError:
|
||||
log.error(f"Error: The file '{file_path}' does not exist.")
|
||||
return None
|
||||
return {}
|
||||
except toml.TomlDecodeError as e:
|
||||
log.error(f"rror decoding TOML file: {e}")
|
||||
return None
|
||||
return {}
|
||||
|
||||
@ -11,8 +11,3 @@ class Size(NamedTuple):
|
||||
if isinstance(other, Size):
|
||||
return Size(self.width - other.width, self.height - other.height)
|
||||
return Size(self.width - other, self.height - other)
|
||||
|
||||
|
||||
class BestMove(NamedTuple):
|
||||
rotation: int
|
||||
x: int
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
from attrs import define
|
||||
|
||||
|
||||
@define
|
||||
class Weights:
|
||||
height: float
|
||||
lines: float
|
||||
holes: float
|
||||
bumpiness: float
|
||||
Loading…
Reference in New Issue
Block a user