feat(game): movemnt keys from file

This commit is contained in:
Kristofers Solo 2024-01-07 16:47:24 +02:00
parent f1e854a38c
commit 8e3ed493e0
11 changed files with 178 additions and 84 deletions

View File

@ -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

View File

@ -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()

View File

@ -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:

View File

@ -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()

View File

@ -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
)

View File

@ -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",
]

View File

@ -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
View 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,
}

View File

@ -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 {}

View File

@ -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

View File

@ -1,9 +0,0 @@
from attrs import define
@define
class Weights:
height: float
lines: float
holes: float
bumpiness: float