diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index e7582a7..aad0937 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -59,3 +59,19 @@ def test_state_shape(client): assert key in state assert state["player"]["score"] == 0.0 assert all(o["board"] is not None for o in state["opponents"]) + + +def test_upgrade_hint(): + from tft.api.app import upgrade_hint + from tft.sim.game import Game + static = make_static() + static["meta"]["patch"] = "t" + game = Game(artifact_mod.build(static), load_constants(17), seed=1) + assert upgrade_hint(game, "U1_0") == 0 + game.bench = [{"api_name": "U1_0", "stars": 1, "items": []} for _ in range(2)] + assert upgrade_hint(game, "U1_0") == 2 + game.bench = [{"api_name": "U1_0", "stars": 2, "items": []} for _ in range(2)] + \ + [{"api_name": "U1_0", "stars": 1, "items": []} for _ in range(2)] + assert upgrade_hint(game, "U1_0") == 3 + game.bench = [{"api_name": "U1_0", "stars": 3, "items": []}] + assert upgrade_hint(game, "U1_0") == 0 diff --git a/backend/tests/test_game.py b/backend/tests/test_game.py index 06289ab..765363c 100644 --- a/backend/tests/test_game.py +++ b/backend/tests/test_game.py @@ -79,3 +79,11 @@ def test_invalid_actions_raise(art, cfg): game.buy_xp() with pytest.raises(InvalidAction): game.sell("bench", 0) + + +def test_shop_lock_survives_step(art, cfg): + game = Game(art, cfg, seed=2) + game.toggle_lock() + before = list(game.shop) + game.step() + assert game.shop == before diff --git a/backend/tft/api/app.py b/backend/tft/api/app.py index 363bbf3..908af4f 100644 --- a/backend/tft/api/app.py +++ b/backend/tft/api/app.py @@ -75,6 +75,8 @@ def action(game_id: str, req: ActionRequest) -> dict: game.equip(req.item_idx, req.board_idx) elif req.action == "pick_augment": game.pick_augment(req.choice) + elif req.action == "lock": + game.toggle_lock() elif req.action == "next": game.step() else: @@ -86,6 +88,18 @@ def action(game_id: str, req: ActionRequest) -> dict: return serialize(game) +def upgrade_hint(game: Game, api: str) -> int: + """0 = kein Upgrade, 2/3 = Kauf ergibt einen 2-/3-Sterner.""" + owned = [u for u in game.board + game.bench + if u["api_name"] == api and u["stars"] < 3] + copies = sum(3 ** (u["stars"] - 1) for u in owned) + if copies + 1 >= 9: + return 3 + if sum(1 for u in owned if u["stars"] == 1) == 2: + return 2 + return 0 + + def unit_view(game: Game, u: dict) -> dict: info = game.artifact["static"]["units"][u["api_name"]] return { @@ -148,12 +162,17 @@ def serialize(game: Game) -> dict: "api_name": api, "name": static["units"][api].get("name", api), "cost": static["units"][api]["cost"], - "traits": [static["traits"][t].get("name", t) for t in static["units"][api]["traits"] - if t in static["traits"]], + "traits": [ + {"name": static["traits"][t].get("name", t), + "icon": static["traits"][t].get("icon")} + for t in static["units"][api]["traits"] if t in static["traits"] + ], "icon": static["units"][api].get("icon"), + "upgrade": upgrade_hint(game, api), } for api in game.shop ], + "shop_locked": game.shop_locked, "traits": traits_panel, "augment_offer": [ {"api_name": a, "name": static["augments"].get(a, {}).get("name", a), diff --git a/backend/tft/sim/game.py b/backend/tft/sim/game.py index 86e122e..3263142 100644 --- a/backend/tft/sim/game.py +++ b/backend/tft/sim/game.py @@ -36,6 +36,7 @@ class Game: self.augments: list[str] = [] self.streak = 0 self.shop: list[str | None] = [] + self.shop_locked = False self.augment_offer: list[str] = [] self.placement: int | None = None self.log: list[str] = [] @@ -166,6 +167,9 @@ class Game: raise InvalidAction("unit has 3 items") self.board[board_idx]["items"].append(self.items.pop(item_idx)) + def toggle_lock(self) -> None: + self.shop_locked = not self.shop_locked + def pick_augment(self, choice: int) -> None: if not self.augment_offer: raise InvalidAction("no augment offer") @@ -210,7 +214,8 @@ class Game: self.idx += 1 if self.round["stage"] != prev_stage: self._refresh_bots() - self._reroll_shop() + if not self.shop_locked: + self._reroll_shop() self._maybe_offer_augment() def _resolve(self, rnd: dict) -> bool: diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 3c6e402..eb19252 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -20,12 +20,10 @@ const kindLabel = { pvp: 'PvP', pve: 'PvE', carousel: 'Carousel' }
Runde {{ s.round.label }} · {{ kindLabel[s.round.kind] }} ❤️ {{ s.player.hp }} - 🪙 {{ s.player.gold }} Lvl {{ s.player.level }} - Streak {{ s.player.streak }} Boardstärke {{ s.player.score }}