| """The Level schema shared by every board. |
| |
| A level is self-contained: the grid geometry the browser draws plus the |
| candidate `labels` the judge scores among. Adding a board is one file under |
| levels/ that defines `LEVEL = Level(...)` β see levels/__init__.py for the |
| auto-discovery. |
| |
| Grid words are plain strings, with four reserved values: |
| "start" the home tile (rendered blank, appends no word) |
| "" a walkable empty tile (rendered blank, appends no word) |
| "β" a submit tile β hopping onto one sends the sentence to the judge |
| "portal" a spinning-spiral teleport tile (appends no word); hopping onto one |
| whisks the doodle to the next portal clockwise around the board. |
| A board needs >= 2 portals for the links to mean anything. |
| A board may have several "β" tiles; every other cell appends its word. |
| """ |
|
|
| from dataclasses import dataclass, field |
|
|
|
|
| @dataclass(frozen=True) |
| class Level: |
| id: str |
| title: str |
| grid: list[list[str]] |
| start: tuple[int, int] |
| targets: list[str] |
| labels: list[str] |
| budget: int |
| order: int = 100 |
| home: bool = False |
| glitch: bool = False |
| |
| portal_to: str = "" |
| |
| solutions: dict[str, str] = field(default_factory=dict) |
| |
| |
| music: str = "" |
| |
| |
| music_gain: float = 1.0 |
| |
| |
|
|
| def client_value(self) -> dict: |
| """The subset shipped to the browser β no judge internals leave the server.""" |
| return { |
| "id": self.id, |
| "title": self.title, |
| "grid": self.grid, |
| "start": list(self.start), |
| "targets": self.targets, |
| "labels": self.labels, |
| "budget": self.budget, |
| "glitch": self.glitch, |
| "portal_to": self.portal_to, |
| "solutions": self.solutions, |
| "music": self.music, |
| "music_gain": self.music_gain, |
| } |
|
|