/
Mihaham
/
Table-Time
Обзор
Документация
Войти
/
Mihaham
/
Table-Time
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
services/api/app/plugins/tabletime_clock/plugin.py
101 строка
4 KB
MihahamYT
feat: full game catalog — 46 plugins, docs nav, web UI kit
14 июн 2026, 12:43
14 июн 2026, 12:43
2cd5f50
Код
Авторство
О чём код?
"""TableTime Clock — timed round buzzer game (Table Time S5).""" from copy import deepcopy from app.plugins.base import GamePlugin, TelegramViewModel, ValidationResult, WebViewModel from app.plugins.shared.timer import phase_deadline, remaining_seconds class TabletimeClockPlugin(GamePlugin): plugin_id = "tabletime_clock" display_name = "TableTime Clock" min_players = 2 max_players = 8 version = "0.1" def initial_state(self, player_ids, player_names, config): duration = config.get("round_seconds", 30) return { "phase": "Buzzing", "player_order": player_ids, "player_names": player_names, "current_player_id": player_ids[0], "round": 1, "max_rounds": config.get("max_rounds", len(player_ids) * 2), "deadline": phase_deadline(duration), "buzzes": [], "scores": {pid: 0 for pid in player_ids}, "winner_id": None, "prompt": config.get("prompt", "Назовите слово на букву А"), } def validate_action(self, state, action_type, payload, player_id): if state.get("winner_id"): return ValidationResult(ok=False, error_code="GAME_FINISHED") if action_type == "buzz": if state.get("phase") != "Buzzing": return ValidationResult(ok=False, error_code="WRONG_PHASE") if any(b["player_id"] == player_id for b in state.get("buzzes", [])): return ValidationResult(ok=False, error_code="ALREADY_BUZZED") return ValidationResult(ok=True) if action_type == "judge_buzz": if state.get("phase") != "Judging": return ValidationResult(ok=False, error_code="WRONG_PHASE") idx = payload.get("index") buzzes = state.get("buzzes", []) if not isinstance(idx, int) or idx < 0 or idx >= len(buzzes): return ValidationResult(ok=False, error_code="INVALID_INDEX") return ValidationResult(ok=True) return ValidationResult(ok=False, error_code="UNKNOWN_ACTION") def apply_action(self, state, action_type, payload, player_id): ns = deepcopy(state) if action_type == "buzz": ns["buzzes"].append({"player_id": player_id, "answer": payload.get("answer", ""), "accepted": None}) if len(ns["buzzes"]) >= len(ns["player_order"]): ns["phase"] = "Judging" return ns entry = ns["buzzes"][payload["index"]] if payload.get("accepted"): pid = entry["player_id"] ns["scores"][pid] = ns["scores"].get(pid, 0) + 1 if ns["scores"][pid] >= 3: ns["winner_id"] = pid ns["phase"] = "GameOver" return ns ns["round"] += 1 ns["buzzes"] = [] ns["phase"] = "Buzzing" ns["deadline"] = phase_deadline(30) if ns["round"] > ns["max_rounds"]: ns["winner_id"] = max(ns["scores"], key=ns["scores"].get) ns["phase"] = "GameOver" return ns def get_current_player(self, state): return None def is_finished(self, state): return state.get("winner_id") is not None def get_winner(self, state): return state.get("winner_id") def allowed_actions(self, state, player_id): if self.is_finished(state): return [] if state.get("phase") == "Buzzing": return ["buzz"] if state.get("phase") == "Judging": return ["judge_buzz"] return [] def render_web(self, state, player_names, viewer_id=None): return WebViewModel( phase=state.get("phase", ""), actions_available=self.allowed_actions(state, viewer_id) if viewer_id else [], extra={"prompt": state.get("prompt"), "remaining": remaining_seconds(state.get("deadline")), "buzzes": state.get("buzzes"), "scores": state.get("scores")}, ) def render_telegram(self, state, player_names): return TelegramViewModel(caption=f"Clock — раунд {state.get('round')} | {remaining_seconds(state.get('deadline'))}с")