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 ]