/
Mihaham
/
Table-Time
Обзор
Документация
Войти
/
Mihaham
/
Table-Time
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
services/api/app/plugins/karty/plugin.py
94 строки
4 KB
MihahamYT
feat: full game catalog — 46 plugins, docs nav, web UI kit
14 июн 2026, 12:43
14 июн 2026, 12:43
2cd5f50
Код
Авторство
О чём код?
"""Карты — higher or lower card guessing (Table Time S4).""" from copy import deepcopy from app.plugins.base import GamePlugin, TelegramViewModel, ValidationResult, WebViewModel from app.plugins.shared.deck import fisher_yates_shuffle, make_number_pool TARGET_SCORE = 5 class KartyPlugin(GamePlugin): plugin_id = "karty" display_name = "Карты" min_players = 2 max_players = 4 version = "0.1" def initial_state(self, player_ids: list[str], player_names: dict[str, str], config: dict) -> dict: deck = fisher_yates_shuffle(make_number_pool(1, 13), seed=config.get("seed")) return { "phase": "AwaitGuess", "deck": deck, "deck_index": 0, "current_card": deck[0] if deck else 1, "scores": {pid: 0 for pid in player_ids}, "current_player_id": player_ids[0], "player_order": player_ids, "player_names": player_names, "target_score": config.get("target_score", TARGET_SCORE), "winner_id": None, } def validate_action(self, state: dict, action_type: str, payload: dict, player_id: str) -> ValidationResult: if state.get("winner_id"): return ValidationResult(ok=False, error_code="GAME_FINISHED") if action_type != "guess": return ValidationResult(ok=False, error_code="UNKNOWN_ACTION") if player_id != state.get("current_player_id"): return ValidationResult(ok=False, error_code="NOT_YOUR_TURN") if payload.get("direction") not in ("higher", "lower"): return ValidationResult(ok=False, error_code="INVALID_DIRECTION") if state.get("deck_index", 0) >= len(state.get("deck", [])) - 1: return ValidationResult(ok=False, error_code="DECK_EMPTY") return ValidationResult(ok=True) def apply_action(self, state: dict, action_type: str, payload: dict, player_id: str) -> dict: new_state = deepcopy(state) direction = payload["direction"] idx = new_state["deck_index"] current = new_state["current_card"] next_card = new_state["deck"][idx + 1] new_state["deck_index"] = idx + 1 new_state["current_card"] = next_card correct = (direction == "higher" and next_card > current) or (direction == "lower" and next_card < current) if next_card == current: correct = True if correct: new_state["scores"][player_id] += 1 if new_state["scores"][player_id] >= new_state.get("target_score", TARGET_SCORE): new_state["winner_id"] = player_id new_state["phase"] = "GameOver" return new_state order = new_state["player_order"] cur = order.index(player_id) new_state["current_player_id"] = order[(cur + 1) % len(order)] if new_state["deck_index"] >= len(new_state["deck"]) - 1: new_state["winner_id"] = max(new_state["scores"], key=new_state["scores"].get) new_state["phase"] = "GameOver" return new_state def get_current_player(self, state: dict) -> str | None: return state.get("current_player_id") def is_finished(self, state: dict) -> bool: return state.get("phase") == "GameOver" def get_winner(self, state: dict) -> str | None: return state.get("winner_id") def allowed_actions(self, state: dict, player_id: str) -> list[str]: if self.is_finished(state) or player_id != state.get("current_player_id"): return [] return ["guess"] def render_web(self, state: dict, player_names: dict[str, str], viewer_id: str | None = None) -> WebViewModel: return WebViewModel( phase=state.get("phase", ""), current_player_name=player_names.get(state.get("current_player_id", ""), ""), actions_available=self.allowed_actions(state, viewer_id) if viewer_id else [], extra={"current_card": state.get("current_card"), "scores": state.get("scores")}, ) def render_telegram(self, state: dict, player_names: dict[str, str]) -> TelegramViewModel: return TelegramViewModel(caption=f"Карты — карта: {state.get('current_card')}")