File size: 2,846 Bytes
50f2856
 
4de8125
50f2856
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4de8125
 
 
50f2856
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""Build the fixed task splits in data/tasks/.

    python -m scott_wu_math.scripts.build_tasks   # or: python scripts/build_tasks.py from the env dir

test:  50 deals per level (200), seed 2026
train: 1,000 deals per level (4,000), seed 7, none sharing an id with test
video: the two hands from the video, 2025 from eight cards and 6,843 from nine
"""

from __future__ import annotations

import json
import random
import sys
from pathlib import Path

HERE = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(HERE.parent))
from scott_wu_math.game.cards import Card  # noqa: E402
from scott_wu_math.game.expr import parse, steps, to_text  # noqa: E402
from scott_wu_math.game.tasks import LEVELS, make_task  # noqa: E402

OUT = HERE / "data" / "tasks"

# As played in the video (the suits are not all visible, so they are filled in).
VIDEO = [
    {
        "id": "video-2025",
        "target": 2025,
        "cards": [(13, "S"), (12, "H"), (11, "D"), (10, "C"), (7, "S"), (9, "H"), (5, "D"), (5, "C")],
        "solution": "((13 - 12) + (11 - 10) + 7) * 9 * 5 * 5",
    },
    {
        "id": "video-6843",
        "target": 6843,
        "cards": [(6, "S"), (4, "H"), (12, "D"), (10, "C"), (10, "S"), (8, "H"), (11, "D"), (7, "C"), (3, "S")],
        "solution": "(((12 - 10) * (6 + 4) * 10 + 8) * 11 - 7) * 3",
    },
]


def build(seed: int, per_level: int, prefix: str, skip: set[str]) -> list[dict]:
    rng = random.Random(seed)
    tasks: list[dict] = []
    for level in LEVELS:
        made = 0
        while made < per_level:
            task = make_task(rng, level=level)
            key = task["id"]
            if key in skip:
                continue
            skip.add(key)
            task["id"] = f"{prefix}-{level}-{made:04d}"
            task["key"] = key
            tasks.append(task)
            made += 1
    return tasks


def write(name: str, tasks: list[dict]) -> None:
    path = OUT / f"{name}.jsonl"
    path.write_text("".join(json.dumps(t, ensure_ascii=False) + "\n" for t in tasks))
    print(f"{path.relative_to(HERE)}: {len(tasks)} tasks")


def main() -> None:
    OUT.mkdir(parents=True, exist_ok=True)
    seen: set[str] = set()
    test = build(2026, 50, "test", seen)
    train = build(7, 1000, "train", seen)
    video = []
    for v in VIDEO:
        cards = [Card(r, s) for r, s in v["cards"]]
        tree = parse(v["solution"])
        video.append(
            {
                "id": v["id"],
                "level": "year" if len(cards) == 8 else "four-digit",
                "target": v["target"],
                "cards": [c.to_dict() for c in cards],
                "solution": to_text(tree),
                "solution_steps": steps(tree),
            }
        )
    write("test", test)
    write("train", train)
    write("video", video)


if __name__ == "__main__":
    main()