nodes/card_data.pyΒΆ

Part of Casual Crusade.

 1"""Card data record and deck-generation helpers.
 2
 3A card has 1-4 directions ("u","r","d","l") and an optional gem (the Casual
 4Crusade ``Gem`` is a ``GemColor`` letter, so we keep the letter and look up
 5display fields by table when needed).
 6"""
 7
 8from __future__ import annotations
 9
10import random
11from dataclasses import dataclass, field
12
13from .constants import DIRECTIONS, GEM_TYPES
14
15
16@dataclass
17class CardData:
18    directions: list[str] = field(default_factory=list)
19    gem: str | None = None  # GemColor letter or None
20
21    def has(self, d: str) -> bool:
22        return d in self.directions
23
24    def to_dict(self) -> dict:
25        return {"directions": list(self.directions), "gem": self.gem}
26
27    @classmethod
28    def from_dict(cls, d: dict) -> CardData:
29        return cls(directions=list(d.get("directions") or []), gem=d.get("gem"))
30
31
32def random_card(gem_chance: float = 1.0, can_have_gem: bool = True, dirs: list[str] | None = None) -> CardData:
33    """Random card matching upstream's distribution."""
34    count = 4 if random.random() < 0.1 else (1 + random.randint(0, 2))
35    if dirs is None:
36        ds = list(DIRECTIONS)
37        random.shuffle(ds)
38        ds = ds[:count]
39    else:
40        ds = list(dirs)
41    one = len(ds) == 1
42    p = (0.6 if one else 0.2) * gem_chance
43    gem = random.choice(GEM_TYPES) if (can_have_gem and random.random() < p) else None
44    return CardData(directions=ds, gem=gem)
45
46
47def starter_deck() -> list[CardData]:
48    """The five-card opening deck, matching upstream `Game.init`."""
49    return [
50        CardData(directions=["u", "d"]),
51        CardData(directions=["u", "d"]),
52        CardData(directions=["l", "r"]),
53        CardData(directions=["l", "r"]),
54        random_card(1.0, True),
55    ]