mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-06 19:43:19 +02:00
fix: correct mypy ignore comment for attr-defined error
This commit is contained in:
parent
4e178be185
commit
97edab318f
@ -24,8 +24,10 @@ class _HrefParser(HTMLParser):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.hrefs: list[str] = []
|
self.hrefs: list[str] = []
|
||||||
|
|
||||||
def handle_starttag(self, tag: str, attrs): # type: ignore[override]
|
def handle_starttag( # type: ignore[override]
|
||||||
# Collect any href attribute on any tag
|
self, tag: str, attrs: list[tuple[str, str | None]]
|
||||||
|
) -> None:
|
||||||
|
"""Collect href attributes from start tags."""
|
||||||
for k, v in attrs:
|
for k, v in attrs:
|
||||||
if k.lower() == "href" and v is not None:
|
if k.lower() == "href" and v is not None:
|
||||||
self.hrefs.append(v)
|
self.hrefs.append(v)
|
||||||
|
|||||||
@ -11,12 +11,12 @@ if str(ROOT) not in sys.path:
|
|||||||
SCRIPT = ROOT / "main.py"
|
SCRIPT = ROOT / "main.py"
|
||||||
|
|
||||||
|
|
||||||
def read_lines(p: Path):
|
def read_lines(p: Path) -> list[str]:
|
||||||
"""Read lines from a file, stripping newlines."""
|
"""Read lines from a file, stripping newlines."""
|
||||||
return [line.rstrip("\n") for line in p.read_text(encoding="utf-8").splitlines()]
|
return [line.rstrip("\n") for line in p.read_text(encoding="utf-8").splitlines()]
|
||||||
|
|
||||||
|
|
||||||
def test_extract_hosts_function():
|
def test_extract_hosts_function() -> None:
|
||||||
"""Test extract_hosts_from_html extracts unique hosts in order."""
|
"""Test extract_hosts_from_html extracts unique hosts in order."""
|
||||||
from main import extract_hosts_from_html
|
from main import extract_hosts_from_html
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ def test_extract_hosts_function():
|
|||||||
assert hosts == ["wiby.me", "example.com"], hosts
|
assert hosts == ["wiby.me", "example.com"], hosts
|
||||||
|
|
||||||
|
|
||||||
def test_cli_writes_expected_output(tmp_path: Path):
|
def test_cli_writes_expected_output(tmp_path: Path) -> None:
|
||||||
"""Test CLI writes correctly formatted output file."""
|
"""Test CLI writes correctly formatted output file."""
|
||||||
# copy sample1.html to tmpdir and run the script
|
# copy sample1.html to tmpdir and run the script
|
||||||
sample = ROOT / "tests" / "sample1.html"
|
sample = ROOT / "tests" / "sample1.html"
|
||||||
@ -53,7 +53,7 @@ def test_cli_writes_expected_output(tmp_path: Path):
|
|||||||
assert lines == ["*wiby.me*", "*example.com*"], lines
|
assert lines == ["*wiby.me*", "*example.com*"], lines
|
||||||
|
|
||||||
|
|
||||||
def test_cli_default_output_name(tmp_path: Path):
|
def test_cli_default_output_name(tmp_path: Path) -> None:
|
||||||
"""Test CLI generates default output filename from input."""
|
"""Test CLI generates default output filename from input."""
|
||||||
sample = ROOT / "tests" / "sample2.html"
|
sample = ROOT / "tests" / "sample2.html"
|
||||||
html_copy = tmp_path / "sample2.html"
|
html_copy = tmp_path / "sample2.html"
|
||||||
|
|||||||
@ -70,7 +70,7 @@ KEY_ADJACENCY = {
|
|||||||
class KeyboardCoopGame:
|
class KeyboardCoopGame:
|
||||||
"""Main game class for the keyboard cooperative word game."""
|
"""Main game class for the keyboard cooperative word game."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
"""Initialize the game window, fonts, and game state."""
|
"""Initialize the game window, fonts, and game state."""
|
||||||
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
||||||
pygame.display.set_caption("Keyboard Coop Game")
|
pygame.display.set_caption("Keyboard Coop Game")
|
||||||
@ -96,7 +96,7 @@ class KeyboardCoopGame:
|
|||||||
# Key positions
|
# Key positions
|
||||||
self.key_positions = self.calculate_key_positions()
|
self.key_positions = self.calculate_key_positions()
|
||||||
|
|
||||||
def load_dictionary(self):
|
def load_dictionary(self) -> set[str]:
|
||||||
"""Load dictionary from words_dictionary.json file."""
|
"""Load dictionary from words_dictionary.json file."""
|
||||||
try:
|
try:
|
||||||
dictionary_path = os.path.join(
|
dictionary_path = os.path.join(
|
||||||
@ -198,7 +198,7 @@ class KeyboardCoopGame:
|
|||||||
"good",
|
"good",
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_random_keyboard(self):
|
def generate_random_keyboard(self) -> None:
|
||||||
"""Generate a random keyboard layout and calculate adjacencies."""
|
"""Generate a random keyboard layout and calculate adjacencies."""
|
||||||
# All 26 letters
|
# All 26 letters
|
||||||
all_letters = list("abcdefghijklmnopqrstuvwxyz")
|
all_letters = list("abcdefghijklmnopqrstuvwxyz")
|
||||||
@ -217,7 +217,7 @@ class KeyboardCoopGame:
|
|||||||
# Calculate adjacencies based on new layout
|
# Calculate adjacencies based on new layout
|
||||||
self.calculate_adjacencies()
|
self.calculate_adjacencies()
|
||||||
|
|
||||||
def calculate_adjacencies(self):
|
def calculate_adjacencies(self) -> None:
|
||||||
"""Calculate adjacencies based on current keyboard layout."""
|
"""Calculate adjacencies based on current keyboard layout."""
|
||||||
self.key_adjacency = {}
|
self.key_adjacency = {}
|
||||||
|
|
||||||
@ -248,9 +248,9 @@ class KeyboardCoopGame:
|
|||||||
|
|
||||||
self.key_adjacency[letter] = adjacents
|
self.key_adjacency[letter] = adjacents
|
||||||
|
|
||||||
def calculate_key_positions(self):
|
def calculate_key_positions(self) -> dict[str, pygame.Rect]:
|
||||||
"""Calculate the position of each key on screen."""
|
"""Calculate the position of each key on screen."""
|
||||||
positions = {}
|
positions: dict[str, pygame.Rect] = {}
|
||||||
key_width = 60
|
key_width = 60
|
||||||
key_height = 60
|
key_height = 60
|
||||||
key_spacing = 8
|
key_spacing = 8
|
||||||
@ -266,14 +266,14 @@ class KeyboardCoopGame:
|
|||||||
|
|
||||||
return positions
|
return positions
|
||||||
|
|
||||||
def get_key_at_position(self, pos):
|
def get_key_at_position(self, pos: tuple[int, int]) -> str | None:
|
||||||
"""Get the key at the given mouse position."""
|
"""Get the key at the given mouse position."""
|
||||||
for key, rect in self.key_positions.items():
|
for key, rect in self.key_positions.items():
|
||||||
if rect.collidepoint(pos):
|
if rect.collidepoint(pos):
|
||||||
return key
|
return key
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def is_valid_move(self, letter):
|
def is_valid_move(self, letter: str) -> bool:
|
||||||
"""Check if the letter is a valid move."""
|
"""Check if the letter is a valid move."""
|
||||||
if not self.selected_letters:
|
if not self.selected_letters:
|
||||||
return True # First move can be any letter
|
return True # First move can be any letter
|
||||||
@ -281,17 +281,17 @@ class KeyboardCoopGame:
|
|||||||
last_letter = self.selected_letters[-1]
|
last_letter = self.selected_letters[-1]
|
||||||
return letter in self.key_adjacency[last_letter]
|
return letter in self.key_adjacency[last_letter]
|
||||||
|
|
||||||
def is_valid_word(self, word):
|
def is_valid_word(self, word: str) -> bool:
|
||||||
"""Check if the word is in the dictionary."""
|
"""Check if the word is in the dictionary."""
|
||||||
return word.lower() in self.dictionary
|
return word.lower() in self.dictionary
|
||||||
|
|
||||||
def calculate_score(self, word_length):
|
def calculate_score(self, word_length: int) -> int:
|
||||||
"""Calculate score exponentially based on word length."""
|
"""Calculate score exponentially based on word length."""
|
||||||
if word_length < MIN_WORD_LENGTH:
|
if word_length < MIN_WORD_LENGTH:
|
||||||
return 0
|
return 0
|
||||||
return 2 ** (word_length - 2)
|
return 2 ** (word_length - 2)
|
||||||
|
|
||||||
def handle_letter_click(self, letter):
|
def handle_letter_click(self, letter: str) -> None:
|
||||||
"""Handle clicking on a letter."""
|
"""Handle clicking on a letter."""
|
||||||
if letter in self.available_letters and self.is_valid_move(letter):
|
if letter in self.available_letters and self.is_valid_move(letter):
|
||||||
self.selected_letters.append(letter)
|
self.selected_letters.append(letter)
|
||||||
@ -316,7 +316,7 @@ class KeyboardCoopGame:
|
|||||||
if not self.available_letters:
|
if not self.available_letters:
|
||||||
self.submit_word()
|
self.submit_word()
|
||||||
|
|
||||||
def submit_word(self):
|
def submit_word(self) -> None:
|
||||||
"""Submit the current word and check if it's valid."""
|
"""Submit the current word and check if it's valid."""
|
||||||
if len(self.current_word) >= MIN_WORD_LENGTH and self.is_valid_word(
|
if len(self.current_word) >= MIN_WORD_LENGTH and self.is_valid_word(
|
||||||
self.current_word
|
self.current_word
|
||||||
@ -345,7 +345,7 @@ class KeyboardCoopGame:
|
|||||||
self.selected_letters = []
|
self.selected_letters = []
|
||||||
self.current_player = 0
|
self.current_player = 0
|
||||||
|
|
||||||
def reset_game(self):
|
def reset_game(self) -> None:
|
||||||
"""Reset the game to initial state."""
|
"""Reset the game to initial state."""
|
||||||
self.current_player = 0
|
self.current_player = 0
|
||||||
self.current_word = ""
|
self.current_word = ""
|
||||||
@ -358,7 +358,7 @@ class KeyboardCoopGame:
|
|||||||
self.generate_random_keyboard()
|
self.generate_random_keyboard()
|
||||||
self.key_positions = self.calculate_key_positions()
|
self.key_positions = self.calculate_key_positions()
|
||||||
|
|
||||||
def draw_keyboard(self):
|
def draw_keyboard(self) -> None:
|
||||||
"""Draw the virtual keyboard."""
|
"""Draw the virtual keyboard."""
|
||||||
mouse_pos = pygame.mouse.get_pos()
|
mouse_pos = pygame.mouse.get_pos()
|
||||||
|
|
||||||
@ -382,7 +382,7 @@ class KeyboardCoopGame:
|
|||||||
text_rect = text.get_rect(center=rect.center)
|
text_rect = text.get_rect(center=rect.center)
|
||||||
self.screen.blit(text, text_rect)
|
self.screen.blit(text, text_rect)
|
||||||
|
|
||||||
def draw_ui(self):
|
def draw_ui(self) -> tuple[pygame.Rect, pygame.Rect]:
|
||||||
"""Draw the user interface."""
|
"""Draw the user interface."""
|
||||||
# Title
|
# Title
|
||||||
title = self.large_font.render("Keyboard Coop Game", True, TEXT_COLOR)
|
title = self.large_font.render("Keyboard Coop Game", True, TEXT_COLOR)
|
||||||
@ -444,7 +444,7 @@ class KeyboardCoopGame:
|
|||||||
|
|
||||||
return enter_rect, reset_rect
|
return enter_rect, reset_rect
|
||||||
|
|
||||||
def handle_click(self, pos):
|
def handle_click(self, pos: tuple[int, int]) -> None:
|
||||||
"""Handle mouse clicks."""
|
"""Handle mouse clicks."""
|
||||||
# Check if clicked on a key
|
# Check if clicked on a key
|
||||||
key = self.get_key_at_position(pos)
|
key = self.get_key_at_position(pos)
|
||||||
@ -460,7 +460,7 @@ class KeyboardCoopGame:
|
|||||||
elif reset_rect.collidepoint(pos):
|
elif reset_rect.collidepoint(pos):
|
||||||
self.reset_game()
|
self.reset_game()
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
"""Main game loop."""
|
"""Main game loop."""
|
||||||
running = True
|
running = True
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class RandomEngine:
|
|||||||
engine_path: str | None = None,
|
engine_path: str | None = None,
|
||||||
max_time_sec: float = 2.0,
|
max_time_sec: float = 2.0,
|
||||||
depth: int | None = None,
|
depth: int | None = None,
|
||||||
):
|
) -> None:
|
||||||
"""Initialize the engine wrapper with path and time settings."""
|
"""Initialize the engine wrapper with path and time settings."""
|
||||||
self.max_time_sec = max_time_sec
|
self.max_time_sec = max_time_sec
|
||||||
# depth is accepted for compatibility with existing callers but is unused;
|
# depth is accepted for compatibility with existing callers but is unused;
|
||||||
|
|||||||
@ -16,7 +16,7 @@ LICHESS_API = "https://lichess.org"
|
|||||||
class LichessAPI:
|
class LichessAPI:
|
||||||
"""Client for interacting with the Lichess Bot API."""
|
"""Client for interacting with the Lichess Bot API."""
|
||||||
|
|
||||||
def __init__(self, token: str, session: requests.Session | None = None):
|
def __init__(self, token: str, session: requests.Session | None = None) -> None:
|
||||||
"""Initialize the API client with authentication token."""
|
"""Initialize the API client with authentication token."""
|
||||||
self.token = token
|
self.token = token
|
||||||
self.session = session or requests.Session()
|
self.session = session or requests.Session()
|
||||||
@ -29,7 +29,12 @@ class LichessAPI:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _request(
|
def _request(
|
||||||
self, method: str, url: str, *, raise_for_status: bool = False, **kwargs
|
self,
|
||||||
|
method: str,
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
raise_for_status: bool = False,
|
||||||
|
**kwargs: object,
|
||||||
) -> requests.Response:
|
) -> requests.Response:
|
||||||
"""Wrapper around session.request that logs every request/response.
|
"""Wrapper around session.request that logs every request/response.
|
||||||
|
|
||||||
@ -40,7 +45,7 @@ class LichessAPI:
|
|||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
logging.info(f"HTTP {method} {url} -> sending")
|
logging.info(f"HTTP {method} {url} -> sending")
|
||||||
try:
|
try:
|
||||||
r = self.session.request(method, url, **kwargs)
|
r = self.session.request(method, url, **kwargs) # type: ignore[arg-type]
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception(f"HTTP {method} {url} -> exception")
|
logging.exception(f"HTTP {method} {url} -> exception")
|
||||||
raise
|
raise
|
||||||
|
|||||||
@ -38,7 +38,7 @@ def run_bot(log_level: str = "INFO", decline_correspondence: bool = False) -> No
|
|||||||
|
|
||||||
game_threads = {}
|
game_threads = {}
|
||||||
|
|
||||||
def handle_game(game_id: str, my_color: str | None = None):
|
def handle_game(game_id: str, my_color: str | None = None) -> None:
|
||||||
logging.info(f"Starting game thread for {game_id} [bot v{bot_version}]")
|
logging.info(f"Starting game thread for {game_id} [bot v{bot_version}]")
|
||||||
board = chess.Board()
|
board = chess.Board()
|
||||||
color: str | None = my_color
|
color: str | None = my_color
|
||||||
@ -453,7 +453,7 @@ def run_bot(log_level: str = "INFO", decline_correspondence: bool = False) -> No
|
|||||||
backoff = backoff_sleep(backoff)
|
backoff = backoff_sleep(backoff)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
"""Parse arguments and run the Lichess bot."""
|
"""Parse arguments and run the Lichess bot."""
|
||||||
parser = argparse.ArgumentParser(description="Run a minimal Lichess bot")
|
parser = argparse.ArgumentParser(description="Run a minimal Lichess bot")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
# Add repository root to sys.path so 'import PYTHON.*' works when running
|
# Add repository root to sys.path so 'import PYTHON.*' works when running
|
||||||
# pytest with a subdirectory as rootdir.
|
# pytest with a subdirectory as rootdir.
|
||||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
||||||
@ -9,7 +11,7 @@ if ROOT not in sys.path:
|
|||||||
sys.path.insert(0, ROOT)
|
sys.path.insert(0, ROOT)
|
||||||
|
|
||||||
|
|
||||||
def pytest_ignore_collect(collection_path: Path, config):
|
def pytest_ignore_collect(collection_path: Path, config: pytest.Config) -> bool | None:
|
||||||
"""Ignore per-game blunder test files; keep only the unified one.
|
"""Ignore per-game blunder test files; keep only the unified one.
|
||||||
|
|
||||||
This lets us keep historical files in the repo without collecting them.
|
This lets us keep historical files in the repo without collecting them.
|
||||||
|
|||||||
@ -33,7 +33,7 @@ def _load_top_puzzles(csv_path: str, limit: int = 8) -> list[tuple[str, str]]:
|
|||||||
os.path.join(os.path.dirname(__file__), "lichess_db_puzzle.csv"), limit=8
|
os.path.join(os.path.dirname(__file__), "lichess_db_puzzle.csv"), limit=8
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_puzzle_engine_follow_solution(fen: str, moves_str: str):
|
def test_puzzle_engine_follow_solution(fen: str, moves_str: str) -> None:
|
||||||
"""Verify the engine follows puzzle solutions correctly."""
|
"""Verify the engine follows puzzle solutions correctly."""
|
||||||
board = chess.Board(fen)
|
board = chess.Board(fen)
|
||||||
eng = RandomEngine(max_time_sec=1.0)
|
eng = RandomEngine(max_time_sec=1.0)
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
"""Tests for utility functions."""
|
"""Tests for utility functions."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from PYTHON.lichess_bot.utils import backoff_sleep
|
from PYTHON.lichess_bot.utils import backoff_sleep
|
||||||
|
|
||||||
|
|
||||||
def test_backoff_sleep_increments_and_caps(monkeypatch):
|
def test_backoff_sleep_increments_and_caps(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
"""Test that backoff sleep increments and respects the cap."""
|
"""Test that backoff sleep increments and respects the cap."""
|
||||||
slept = []
|
slept: list[float] = []
|
||||||
|
|
||||||
def fake_sleep(sec):
|
def fake_sleep(sec: float) -> None:
|
||||||
slept.append(sec)
|
slept.append(sec)
|
||||||
|
|
||||||
monkeypatch.setattr("time.sleep", fake_sleep)
|
monkeypatch.setattr("time.sleep", fake_sleep)
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
"""Tests for bot version management."""
|
"""Tests for bot version management."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from PYTHON.lichess_bot.utils import get_and_increment_version
|
from PYTHON.lichess_bot.utils import get_and_increment_version
|
||||||
|
|
||||||
|
|
||||||
def test_version_file_increments_and_persists(tmp_path, monkeypatch):
|
def test_version_file_increments_and_persists(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
"""Test that version increments and persists to file."""
|
"""Test that version increments and persists to file."""
|
||||||
version_file = tmp_path / "version.txt"
|
version_file = tmp_path / "version.txt"
|
||||||
monkeypatch.setenv("LICHESS_BOT_VERSION_FILE", str(version_file))
|
monkeypatch.setenv("LICHESS_BOT_VERSION_FILE", str(version_file))
|
||||||
|
|||||||
@ -14,19 +14,28 @@ MAX_IMAGE_SIZE = 1000
|
|||||||
|
|
||||||
|
|
||||||
def generate_bloated_jpeg(
|
def generate_bloated_jpeg(
|
||||||
size, color_list, block_size, output_path, quality, image_index, folder
|
size: int,
|
||||||
):
|
color_list: list[str],
|
||||||
|
block_size: int,
|
||||||
|
output_path: str,
|
||||||
|
quality: int,
|
||||||
|
image_index: int,
|
||||||
|
folder: str,
|
||||||
|
) -> str:
|
||||||
"""Generates a random JPEG image with given size, list of colors, and block size.
|
"""Generates a random JPEG image with given size, list of colors, and block size.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
size (int): Size of the image (both width and height,
|
size: Size of the image (both width and height,
|
||||||
must be divisible by block_size).
|
must be divisible by block_size).
|
||||||
color_list (list of str): List of colors in hex format.
|
color_list: List of colors in hex format.
|
||||||
block_size (int): Size of the pixel blocks.
|
block_size: Size of the pixel blocks.
|
||||||
output_path (str): Output path for the JPEG image.
|
output_path: Output path for the JPEG image.
|
||||||
quality (int): Quality setting for the JPEG image (0-100).
|
quality: Quality setting for the JPEG image (0-100).
|
||||||
image_index (int): Index of the image for unique naming.
|
image_index: Index of the image for unique naming.
|
||||||
folder (str): Folder to save the image.
|
folder: Folder to save the image.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to the generated image.
|
||||||
"""
|
"""
|
||||||
# Ensure size is divisible by block_size and does not exceed MAX_IMAGE_SIZE
|
# Ensure size is divisible by block_size and does not exceed MAX_IMAGE_SIZE
|
||||||
if size > MAX_IMAGE_SIZE or size % block_size != 0:
|
if size > MAX_IMAGE_SIZE or size % block_size != 0:
|
||||||
|
|||||||
@ -13,10 +13,10 @@ DEFAULT_MAX_PERCENTAGE = 20
|
|||||||
|
|
||||||
|
|
||||||
def randomize_numbers(
|
def randomize_numbers(
|
||||||
numbers,
|
numbers: list[float],
|
||||||
min_percentage=DEFAULT_MIN_PERCENTAGE,
|
min_percentage: float = DEFAULT_MIN_PERCENTAGE,
|
||||||
max_percentage=DEFAULT_MAX_PERCENTAGE,
|
max_percentage: float = DEFAULT_MAX_PERCENTAGE,
|
||||||
):
|
) -> list[float]:
|
||||||
"""Apply random percentage variation to a list of numbers."""
|
"""Apply random percentage variation to a list of numbers."""
|
||||||
randomized_numbers = []
|
randomized_numbers = []
|
||||||
for number in numbers:
|
for number in numbers:
|
||||||
@ -29,7 +29,7 @@ def randomize_numbers(
|
|||||||
return randomized_numbers
|
return randomized_numbers
|
||||||
|
|
||||||
|
|
||||||
def parse_input(input_string):
|
def parse_input(input_string: str) -> tuple[list[float], list[int]]:
|
||||||
"""Parse a string of numbers and return floats with decimal counts."""
|
"""Parse a string of numbers and return floats with decimal counts."""
|
||||||
# Replace commas with dots and remove non-numeric characters
|
# Replace commas with dots and remove non-numeric characters
|
||||||
# except dots, commas, and digits
|
# except dots, commas, and digits
|
||||||
@ -37,8 +37,8 @@ def parse_input(input_string):
|
|||||||
# Split the cleaned input into individual numbers
|
# Split the cleaned input into individual numbers
|
||||||
number_strings = cleaned_input.split()
|
number_strings = cleaned_input.split()
|
||||||
# Convert the number strings to floats
|
# Convert the number strings to floats
|
||||||
numbers = []
|
numbers: list[float] = []
|
||||||
decimal_counts = []
|
decimal_counts: list[int] = []
|
||||||
for num in number_strings:
|
for num in number_strings:
|
||||||
try:
|
try:
|
||||||
float_num = float(num)
|
float_num = float(num)
|
||||||
|
|||||||
@ -30,7 +30,7 @@ driver.get(url)
|
|||||||
|
|
||||||
|
|
||||||
# A function to download images by URL
|
# A function to download images by URL
|
||||||
def download_image(url):
|
def download_image(url: str) -> bool:
|
||||||
"""Download an image from a URL and save it locally."""
|
"""Download an image from a URL and save it locally."""
|
||||||
# Extract image name from URL
|
# Extract image name from URL
|
||||||
image_name = os.path.basename(urlparse(url).path)
|
image_name = os.path.basename(urlparse(url).path)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ MAX_WEIGHT_KG = 500
|
|||||||
class ScreenLocker:
|
class ScreenLocker:
|
||||||
"""Screen locker that requires workout logging to unlock."""
|
"""Screen locker that requires workout logging to unlock."""
|
||||||
|
|
||||||
def __init__(self, demo_mode=True):
|
def __init__(self, demo_mode: bool = True) -> None:
|
||||||
"""Initialize screen locker with optional demo mode."""
|
"""Initialize screen locker with optional demo mode."""
|
||||||
# Set up log file path
|
# Set up log file path
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
@ -86,12 +86,12 @@ class ScreenLocker:
|
|||||||
self.root.focus_force()
|
self.root.focus_force()
|
||||||
self.root.grab_set_global()
|
self.root.grab_set_global()
|
||||||
|
|
||||||
def clear_container(self):
|
def clear_container(self) -> None:
|
||||||
"""Remove all widgets from the main container."""
|
"""Remove all widgets from the main container."""
|
||||||
for widget in self.container.winfo_children():
|
for widget in self.container.winfo_children():
|
||||||
widget.destroy()
|
widget.destroy()
|
||||||
|
|
||||||
def ask_workout_done(self):
|
def ask_workout_done(self) -> None:
|
||||||
"""Display the initial workout question dialog."""
|
"""Display the initial workout question dialog."""
|
||||||
self.clear_container()
|
self.clear_container()
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ class ScreenLocker:
|
|||||||
)
|
)
|
||||||
no_btn.pack(side="left", padx=20)
|
no_btn.pack(side="left", padx=20)
|
||||||
|
|
||||||
def lockout(self):
|
def lockout(self) -> None:
|
||||||
"""Display lockout screen with countdown timer."""
|
"""Display lockout screen with countdown timer."""
|
||||||
self.clear_container()
|
self.clear_container()
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ class ScreenLocker:
|
|||||||
self.remaining_time = self.lockout_time
|
self.remaining_time = self.lockout_time
|
||||||
self.update_lockout_countdown()
|
self.update_lockout_countdown()
|
||||||
|
|
||||||
def update_lockout_countdown(self):
|
def update_lockout_countdown(self) -> None:
|
||||||
"""Update the lockout countdown timer display."""
|
"""Update the lockout countdown timer display."""
|
||||||
if self.remaining_time > 0:
|
if self.remaining_time > 0:
|
||||||
self.countdown_label.config(text=str(self.remaining_time))
|
self.countdown_label.config(text=str(self.remaining_time))
|
||||||
@ -165,7 +165,7 @@ class ScreenLocker:
|
|||||||
else:
|
else:
|
||||||
self.ask_workout_done()
|
self.ask_workout_done()
|
||||||
|
|
||||||
def ask_workout_type(self):
|
def ask_workout_type(self) -> None:
|
||||||
"""Display workout type selection dialog."""
|
"""Display workout type selection dialog."""
|
||||||
self.clear_container()
|
self.clear_container()
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ class ScreenLocker:
|
|||||||
)
|
)
|
||||||
strength_btn.pack(side="left", padx=20)
|
strength_btn.pack(side="left", padx=20)
|
||||||
|
|
||||||
def ask_running_details(self):
|
def ask_running_details(self) -> None:
|
||||||
"""Display running workout input form."""
|
"""Display running workout input form."""
|
||||||
self.clear_container()
|
self.clear_container()
|
||||||
self.workout_data["type"] = "running"
|
self.workout_data["type"] = "running"
|
||||||
@ -295,7 +295,7 @@ class ScreenLocker:
|
|||||||
self.submit_command = self.verify_running_data
|
self.submit_command = self.verify_running_data
|
||||||
self.update_submit_timer()
|
self.update_submit_timer()
|
||||||
|
|
||||||
def verify_running_data(self):
|
def verify_running_data(self) -> None:
|
||||||
"""Validate running workout data and unlock if valid."""
|
"""Validate running workout data and unlock if valid."""
|
||||||
try:
|
try:
|
||||||
distance = float(self.distance_entry.get())
|
distance = float(self.distance_entry.get())
|
||||||
@ -337,7 +337,7 @@ class ScreenLocker:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
self.show_error("Please enter valid numbers")
|
self.show_error("Please enter valid numbers")
|
||||||
|
|
||||||
def ask_strength_details(self):
|
def ask_strength_details(self) -> None:
|
||||||
"""Display strength training input form."""
|
"""Display strength training input form."""
|
||||||
self.clear_container()
|
self.clear_container()
|
||||||
self.workout_data["type"] = "strength"
|
self.workout_data["type"] = "strength"
|
||||||
@ -459,7 +459,7 @@ class ScreenLocker:
|
|||||||
self.submit_command = self.verify_strength_data
|
self.submit_command = self.verify_strength_data
|
||||||
self.update_submit_timer()
|
self.update_submit_timer()
|
||||||
|
|
||||||
def verify_strength_data(self):
|
def verify_strength_data(self) -> None:
|
||||||
"""Validate strength workout data and unlock if valid."""
|
"""Validate strength workout data and unlock if valid."""
|
||||||
try:
|
try:
|
||||||
exercises = [e.strip() for e in self.exercises_entry.get().split(",")]
|
exercises = [e.strip() for e in self.exercises_entry.get().split(",")]
|
||||||
@ -513,7 +513,7 @@ class ScreenLocker:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
self.show_error("Please enter valid data in correct format")
|
self.show_error("Please enter valid data in correct format")
|
||||||
|
|
||||||
def update_submit_timer(self):
|
def update_submit_timer(self) -> None:
|
||||||
"""Update countdown timer and check if submit can be enabled."""
|
"""Update countdown timer and check if submit can be enabled."""
|
||||||
# Check if widgets still exist (user might have clicked back)
|
# Check if widgets still exist (user might have clicked back)
|
||||||
try:
|
try:
|
||||||
@ -544,7 +544,7 @@ class ScreenLocker:
|
|||||||
# Widgets were destroyed (user clicked back), stop the timer
|
# Widgets were destroyed (user clicked back), stop the timer
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def check_entries_filled(self):
|
def check_entries_filled(self) -> None:
|
||||||
"""Continuously check if entries are filled after timer expires."""
|
"""Continuously check if entries are filled after timer expires."""
|
||||||
try:
|
try:
|
||||||
all_filled = all(entry.get().strip() for entry in self.entries_to_check)
|
all_filled = all(entry.get().strip() for entry in self.entries_to_check)
|
||||||
@ -564,7 +564,7 @@ class ScreenLocker:
|
|||||||
# Widgets were destroyed (user clicked back), stop checking
|
# Widgets were destroyed (user clicked back), stop checking
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def show_error(self, message):
|
def show_error(self, message: str) -> None:
|
||||||
"""Display error message with retry option."""
|
"""Display error message with retry option."""
|
||||||
self.clear_container()
|
self.clear_container()
|
||||||
|
|
||||||
@ -599,7 +599,7 @@ class ScreenLocker:
|
|||||||
)
|
)
|
||||||
retry_btn.pack(pady=30)
|
retry_btn.pack(pady=30)
|
||||||
|
|
||||||
def unlock_screen(self):
|
def unlock_screen(self) -> None:
|
||||||
"""Save workout log and display success message."""
|
"""Save workout log and display success message."""
|
||||||
# Save workout data to log
|
# Save workout data to log
|
||||||
self.save_workout_log()
|
self.save_workout_log()
|
||||||
@ -626,7 +626,7 @@ class ScreenLocker:
|
|||||||
|
|
||||||
self.root.after(1500, self.close)
|
self.root.after(1500, self.close)
|
||||||
|
|
||||||
def has_logged_today(self):
|
def has_logged_today(self) -> bool:
|
||||||
"""Check if workout has been logged today."""
|
"""Check if workout has been logged today."""
|
||||||
if not os.path.exists(self.log_file):
|
if not os.path.exists(self.log_file):
|
||||||
return False
|
return False
|
||||||
@ -640,7 +640,7 @@ class ScreenLocker:
|
|||||||
except (OSError, json.JSONDecodeError):
|
except (OSError, json.JSONDecodeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def save_workout_log(self):
|
def save_workout_log(self) -> None:
|
||||||
"""Save workout data to log file."""
|
"""Save workout data to log file."""
|
||||||
# Load existing logs
|
# Load existing logs
|
||||||
logs = {}
|
logs = {}
|
||||||
@ -665,12 +665,12 @@ class ScreenLocker:
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
logging.warning(f"Could not save workout log: {e}")
|
logging.warning(f"Could not save workout log: {e}")
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
"""Close the application and exit."""
|
"""Close the application and exit."""
|
||||||
self.root.destroy()
|
self.root.destroy()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
"""Start the Tkinter main event loop."""
|
"""Start the Tkinter main event loop."""
|
||||||
self.root.mainloop()
|
self.root.mainloop()
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,22 @@
|
|||||||
"""Distribute values symmetrically across N parts."""
|
"""Distribute values symmetrically across N parts."""
|
||||||
|
|
||||||
|
|
||||||
def calculate_symmetric_weights(N, middle_weight, factors=None):
|
def calculate_symmetric_weights(
|
||||||
|
n: int, middle_weight: float, factors: list[float] | None = None
|
||||||
|
) -> list[float]:
|
||||||
"""Calculate symmetric weights for both even and odd N.
|
"""Calculate symmetric weights for both even and odd N.
|
||||||
|
|
||||||
N: Number in which to split.
|
Args:
|
||||||
middle_weight: The middle value for symmetry.
|
n: Number of parts to split into.
|
||||||
factors: If provided, controls the difference in weights (used for the
|
middle_weight: The middle value for symmetry.
|
||||||
`split_x_into_n_symmetrically` function).
|
factors: If provided, controls the difference in weights.
|
||||||
Must have length N // 2 or N // 2 - 1 depending on N.
|
Must have length n // 2 or n // 2 - 1 depending on n.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of symmetric weights.
|
||||||
"""
|
"""
|
||||||
half_n = N // 2
|
half_n = n // 2
|
||||||
weights_left = [middle_weight]
|
weights_left: list[float] = [middle_weight]
|
||||||
|
|
||||||
if factors:
|
if factors:
|
||||||
for factor in factors:
|
for factor in factors:
|
||||||
@ -21,7 +26,7 @@ def calculate_symmetric_weights(N, middle_weight, factors=None):
|
|||||||
for i in range(half_n - 1):
|
for i in range(half_n - 1):
|
||||||
weights_left.append(middle_weight - (i + 1))
|
weights_left.append(middle_weight - (i + 1))
|
||||||
|
|
||||||
if N % 2 == 0:
|
if n % 2 == 0:
|
||||||
weights = weights_left[::-1] + weights_left
|
weights = weights_left[::-1] + weights_left
|
||||||
else:
|
else:
|
||||||
weights = [*weights_left[::-1], middle_weight, *weights_left]
|
weights = [*weights_left[::-1], middle_weight, *weights_left]
|
||||||
@ -29,24 +34,28 @@ def calculate_symmetric_weights(N, middle_weight, factors=None):
|
|||||||
return weights
|
return weights
|
||||||
|
|
||||||
|
|
||||||
def scale_to_total(X, weights):
|
def scale_to_total(x: float, weights: list[float]) -> list[float]:
|
||||||
"""Scale the weights so that their sum is proportional to X.
|
"""Scale the weights so that their sum is proportional to X.
|
||||||
|
|
||||||
X: Total value to distribute.
|
Args:
|
||||||
weights: The list of weights to be scaled.
|
x: Total value to distribute.
|
||||||
|
weights: The list of weights to be scaled.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of scaled values summing to x.
|
||||||
"""
|
"""
|
||||||
total_weight = sum(weights)
|
total_weight = sum(weights)
|
||||||
base_unit = X / total_weight
|
base_unit = x / total_weight
|
||||||
return [base_unit * weight for weight in weights]
|
return [base_unit * weight for weight in weights]
|
||||||
|
|
||||||
|
|
||||||
def split_x_into_n_symmetrically(X, N, factors):
|
def split_x_into_n_symmetrically(x: float, n: int, factors: list[float]) -> list[float]:
|
||||||
"""Split X into N parts with symmetric weights controlled by factors."""
|
"""Split X into N parts with symmetric weights controlled by factors."""
|
||||||
weights = calculate_symmetric_weights(N, middle_weight=1, factors=factors)
|
weights = calculate_symmetric_weights(n, middle_weight=1, factors=factors)
|
||||||
return scale_to_total(X, weights)
|
return scale_to_total(x, weights)
|
||||||
|
|
||||||
|
|
||||||
def split_x_into_n_middle(X, N, middle_value):
|
def split_x_into_n_middle(x: float, n: int, middle_value: float) -> list[float]:
|
||||||
"""Split X into N parts with symmetric weights using middle_value as peak."""
|
"""Split X into N parts with symmetric weights using middle_value as peak."""
|
||||||
weights = calculate_symmetric_weights(N, middle_weight=middle_value)
|
weights = calculate_symmetric_weights(n, middle_weight=middle_value)
|
||||||
return scale_to_total(X, weights)
|
return scale_to_total(x, weights)
|
||||||
|
|||||||
@ -193,7 +193,7 @@ def _detect_total_mem_mb() -> int | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _auto_hash_mb(threads_wanted: int, engine_options) -> int:
|
def _auto_hash_mb(threads_wanted: int, engine_options: dict[str, object]) -> int:
|
||||||
total_mb = _detect_total_mem_mb() or 2048
|
total_mb = _detect_total_mem_mb() or 2048
|
||||||
# Heuristic: cap at 4 GiB by default; keep at most half of RAM; ensure >= 64MB
|
# Heuristic: cap at 4 GiB by default; keep at most half of RAM; ensure >= 64MB
|
||||||
half_ram = max(64, total_mb // 2)
|
half_ram = max(64, total_mb // 2)
|
||||||
@ -202,7 +202,7 @@ def _auto_hash_mb(threads_wanted: int, engine_options) -> int:
|
|||||||
opt = engine_options.get("Hash")
|
opt = engine_options.get("Hash")
|
||||||
max_allowed = None
|
max_allowed = None
|
||||||
try:
|
try:
|
||||||
max_allowed = opt.max if opt is not None else None
|
max_allowed = opt.max if opt is not None else None # type: ignore[attr-defined]
|
||||||
except Exception:
|
except Exception:
|
||||||
max_allowed = None
|
max_allowed = None
|
||||||
if isinstance(max_allowed, int):
|
if isinstance(max_allowed, int):
|
||||||
@ -213,7 +213,7 @@ def _auto_hash_mb(threads_wanted: int, engine_options) -> int:
|
|||||||
return max(64, int(target))
|
return max(64, int(target))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
"""Parse arguments and run chess game analysis."""
|
"""Parse arguments and run chess game analysis."""
|
||||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||||
ap = argparse.ArgumentParser(
|
ap = argparse.ArgumentParser(
|
||||||
|
|||||||
@ -7,13 +7,16 @@ from pathlib import Path
|
|||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
from typing import Any
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def _req(url, method="GET", data=None):
|
def _req(
|
||||||
|
url: str, method: str = "GET", data: dict[str, Any] | bytes | None = None
|
||||||
|
) -> tuple[int, bytes]:
|
||||||
"""Send an HTTP request and return status code and body."""
|
"""Send an HTTP request and return status code and body."""
|
||||||
if data is not None and not isinstance(data, bytes | bytearray):
|
if data is not None and not isinstance(data, bytes | bytearray):
|
||||||
data = json.dumps(data).encode("utf-8")
|
data = json.dumps(data).encode("utf-8")
|
||||||
@ -24,7 +27,7 @@ def _req(url, method="GET", data=None):
|
|||||||
return resp.getcode(), body
|
return resp.getcode(), body
|
||||||
|
|
||||||
|
|
||||||
def test_crud_roundtrip(tmp_path):
|
def test_crud_roundtrip(tmp_path: Path) -> None:
|
||||||
"""Test full CRUD lifecycle for articles API."""
|
"""Test full CRUD lifecycle for articles API."""
|
||||||
# Build C server
|
# Build C server
|
||||||
here = Path(__file__).resolve().parent
|
here = Path(__file__).resolve().parent
|
||||||
|
|||||||
@ -9,12 +9,12 @@ HERE = os.path.dirname(__file__)
|
|||||||
SITE_FILE = os.path.join(HERE, "index.html")
|
SITE_FILE = os.path.join(HERE, "index.html")
|
||||||
|
|
||||||
|
|
||||||
def test_site_file_exists():
|
def test_site_file_exists() -> None:
|
||||||
"""Verify the main site HTML file exists."""
|
"""Verify the main site HTML file exists."""
|
||||||
assert os.path.exists(SITE_FILE), f"Missing site file: {SITE_FILE}"
|
assert os.path.exists(SITE_FILE), f"Missing site file: {SITE_FILE}"
|
||||||
|
|
||||||
|
|
||||||
def test_site_size_under_budget():
|
def test_site_size_under_budget() -> None:
|
||||||
"""Verify site size is under the defined budget."""
|
"""Verify site size is under the defined budget."""
|
||||||
size = os.path.getsize(SITE_FILE)
|
size = os.path.getsize(SITE_FILE)
|
||||||
assert size <= BUDGET, f"Site size {size} bytes exceeds budget {BUDGET}"
|
assert size <= BUDGET, f"Site size {size} bytes exceeds budget {BUDGET}"
|
||||||
|
|||||||
@ -11,7 +11,7 @@ logging.basicConfig(level=logging.INFO)
|
|||||||
class PokerModifierApp:
|
class PokerModifierApp:
|
||||||
"""GUI application for poker game modifiers."""
|
"""GUI application for poker game modifiers."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
"""Initialize the poker modifier app with default settings."""
|
"""Initialize the poker modifier app with default settings."""
|
||||||
self.modifiers = [
|
self.modifiers = [
|
||||||
# Hand Bonus Modifiers (Balatro-inspired)
|
# Hand Bonus Modifiers (Balatro-inspired)
|
||||||
@ -519,7 +519,7 @@ class PokerModifierApp:
|
|||||||
|
|
||||||
self.setup_gui()
|
self.setup_gui()
|
||||||
|
|
||||||
def setup_gui(self):
|
def setup_gui(self) -> None:
|
||||||
"""Create and configure the main GUI window."""
|
"""Create and configure the main GUI window."""
|
||||||
# Create main window
|
# Create main window
|
||||||
self.root = tk.Tk()
|
self.root = tk.Tk()
|
||||||
@ -787,16 +787,16 @@ class PokerModifierApp:
|
|||||||
)
|
)
|
||||||
self.phase_label.pack(pady=10)
|
self.phase_label.pack(pady=10)
|
||||||
|
|
||||||
def update_prob_display(self, value):
|
def update_prob_display(self, value: str) -> None:
|
||||||
"""Update the probability percentage display."""
|
"""Update the probability percentage display."""
|
||||||
self.prob_label.config(text=f"{value}%")
|
self.prob_label.config(text=f"{value}%")
|
||||||
|
|
||||||
def update_length_display(self, value):
|
def update_length_display(self, value: str) -> None:
|
||||||
"""Update the game length display."""
|
"""Update the game length display."""
|
||||||
self.length_label.config(text=str(value))
|
self.length_label.config(text=str(value))
|
||||||
self.total_game_rounds = int(value)
|
self.total_game_rounds = int(value)
|
||||||
|
|
||||||
def toggle_debug_mode(self):
|
def toggle_debug_mode(self) -> None:
|
||||||
"""Toggle debug mode and show/hide debug controls."""
|
"""Toggle debug mode and show/hide debug controls."""
|
||||||
self.debug_mode = self.debug_var.get()
|
self.debug_mode = self.debug_var.get()
|
||||||
if self.debug_mode:
|
if self.debug_mode:
|
||||||
@ -807,7 +807,7 @@ class PokerModifierApp:
|
|||||||
self.force_endgame = False
|
self.force_endgame = False
|
||||||
logging.debug("Debug mode disabled")
|
logging.debug("Debug mode disabled")
|
||||||
|
|
||||||
def toggle_force_endgame(self):
|
def toggle_force_endgame(self) -> None:
|
||||||
"""Toggle forced endgame mode for testing."""
|
"""Toggle forced endgame mode for testing."""
|
||||||
self.force_endgame = not self.force_endgame
|
self.force_endgame = not self.force_endgame
|
||||||
if self.force_endgame:
|
if self.force_endgame:
|
||||||
@ -817,7 +817,7 @@ class PokerModifierApp:
|
|||||||
self.force_endgame_button.config(text="Force Endgame", bg="#ff6b6b")
|
self.force_endgame_button.config(text="Force Endgame", bg="#ff6b6b")
|
||||||
logging.debug("Normal modifier selection restored")
|
logging.debug("Normal modifier selection restored")
|
||||||
|
|
||||||
def is_endgame(self):
|
def is_endgame(self) -> bool:
|
||||||
"""Determine if we're in endgame phase."""
|
"""Determine if we're in endgame phase."""
|
||||||
if self.debug_mode and self.force_endgame:
|
if self.debug_mode and self.force_endgame:
|
||||||
return True
|
return True
|
||||||
@ -825,7 +825,7 @@ class PokerModifierApp:
|
|||||||
endgame_round = int(self.total_game_rounds * self.endgame_threshold)
|
endgame_round = int(self.total_game_rounds * self.endgame_threshold)
|
||||||
return self.rounds_played >= endgame_round
|
return self.rounds_played >= endgame_round
|
||||||
|
|
||||||
def start_round(self):
|
def start_round(self) -> None:
|
||||||
"""Start a new poker round and determine if modifier should be applied."""
|
"""Start a new poker round and determine if modifier should be applied."""
|
||||||
# Button animation effect
|
# Button animation effect
|
||||||
self.start_button.config(relief=tk.SUNKEN)
|
self.start_button.config(relief=tk.SUNKEN)
|
||||||
@ -850,7 +850,7 @@ class PokerModifierApp:
|
|||||||
else:
|
else:
|
||||||
self.show_no_modifier()
|
self.show_no_modifier()
|
||||||
|
|
||||||
def update_phase_indicator(self):
|
def update_phase_indicator(self) -> None:
|
||||||
"""Update the game phase indicator based on current round."""
|
"""Update the game phase indicator based on current round."""
|
||||||
if self.is_endgame():
|
if self.is_endgame():
|
||||||
self.phase_label.config(text="Endgame", fg="#ff6b6b")
|
self.phase_label.config(text="Endgame", fg="#ff6b6b")
|
||||||
@ -861,7 +861,7 @@ class PokerModifierApp:
|
|||||||
else:
|
else:
|
||||||
self.phase_label.config(text="Early", fg="#4CAF50")
|
self.phase_label.config(text="Early", fg="#4CAF50")
|
||||||
|
|
||||||
def apply_random_modifier(self):
|
def apply_random_modifier(self) -> None:
|
||||||
"""Apply a random modifier and update display."""
|
"""Apply a random modifier and update display."""
|
||||||
# Update modifier counter
|
# Update modifier counter
|
||||||
self.modifiers_applied += 1
|
self.modifiers_applied += 1
|
||||||
@ -925,7 +925,7 @@ class PokerModifierApp:
|
|||||||
text=modifier_text, fg="#ffd700", bg=bg_color, font=("Arial", 14, "bold")
|
text=modifier_text, fg="#ffd700", bg=bg_color, font=("Arial", 14, "bold")
|
||||||
)
|
)
|
||||||
|
|
||||||
def show_no_modifier(self):
|
def show_no_modifier(self) -> None:
|
||||||
"""Show no modifier message."""
|
"""Show no modifier message."""
|
||||||
# Update result frame styling for no modifier
|
# Update result frame styling for no modifier
|
||||||
self.result_frame.config(
|
self.result_frame.config(
|
||||||
@ -940,7 +940,7 @@ class PokerModifierApp:
|
|||||||
font=("Arial", 14),
|
font=("Arial", 14),
|
||||||
)
|
)
|
||||||
|
|
||||||
def reset_game(self):
|
def reset_game(self) -> None:
|
||||||
"""Reset the game to initial state."""
|
"""Reset the game to initial state."""
|
||||||
self.rounds_played = 0
|
self.rounds_played = 0
|
||||||
self.modifiers_applied = 0
|
self.modifiers_applied = 0
|
||||||
@ -968,11 +968,11 @@ class PokerModifierApp:
|
|||||||
|
|
||||||
logging.info("Game reset to initial state")
|
logging.info("Game reset to initial state")
|
||||||
|
|
||||||
def add_modifier(self, name, description):
|
def add_modifier(self, name: str, description: str) -> None:
|
||||||
"""Add a new modifier to the list."""
|
"""Add a new modifier to the list."""
|
||||||
self.modifiers.append({"name": name, "description": description})
|
self.modifiers.append({"name": name, "description": description})
|
||||||
|
|
||||||
def get_stats(self):
|
def get_stats(self) -> dict[str, int | float | bool]:
|
||||||
"""Get current statistics."""
|
"""Get current statistics."""
|
||||||
modifier_rate = (
|
modifier_rate = (
|
||||||
0
|
0
|
||||||
@ -992,7 +992,7 @@ class PokerModifierApp:
|
|||||||
"force_endgame": self.force_endgame,
|
"force_endgame": self.force_endgame,
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
"""Start the application."""
|
"""Start the application."""
|
||||||
logging.info("Texas Hold'em Modifier App started!")
|
logging.info("Texas Hold'em Modifier App started!")
|
||||||
logging.info(
|
logging.info(
|
||||||
|
|||||||
@ -34,12 +34,6 @@ ignore = [
|
|||||||
"COM812", # Trailing comma missing (conflicts with formatter)
|
"COM812", # Trailing comma missing (conflicts with formatter)
|
||||||
"ISC001", # Implicit string concatenation (conflicts with formatter)
|
"ISC001", # Implicit string concatenation (conflicts with formatter)
|
||||||
# Relaxed for script-heavy repository
|
# Relaxed for script-heavy repository
|
||||||
"ANN001", # Missing type annotation for function argument
|
|
||||||
"ANN002", # Missing type annotation for *args
|
|
||||||
"ANN003", # Missing type annotation for **kwargs
|
|
||||||
"ANN201", # Missing return type annotation for public function
|
|
||||||
"ANN202", # Missing return type annotation for private function
|
|
||||||
"ANN204", # Missing return type annotation for special method
|
|
||||||
"PTH", # Use pathlib instead of os.path - too invasive for existing code
|
"PTH", # Use pathlib instead of os.path - too invasive for existing code
|
||||||
"C901", # Function is too complex - many scripts have complex logic
|
"C901", # Function is too complex - many scripts have complex logic
|
||||||
"PLR0912", # Too many branches - relaxed for scripts
|
"PLR0912", # Too many branches - relaxed for scripts
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user