File size: 4,461 Bytes
d45f009
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""Generate replayable opponent-policy cache for official evaluation.

The cache can be generated with `--mode llm_live` when API credentials are
available, or with `--mode heuristic` for deterministic local/dev packs.
"""

import argparse
import json
import random
import sys
from pathlib import Path

_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(_ROOT))

try:
    from server.opponent_policy import create_opponent_policy
except ImportError:
    from cricket_captain.server.opponent_policy import create_opponent_policy


def _cache_key(kind: str, context: dict) -> str:
    parts = [
        kind,
        str(context.get("eval_pack_id", "default")),
        str(context.get("scenario_id", "")),
        str(context.get("innings", "first")),
        str(context.get("game_state", "")),
        f"{context.get('over', 0)}.{context.get('ball', 0)}",
        f"{context.get('score', 0)}/{context.get('wickets', 0)}",
        str(context.get("target", "")),
        str(context.get("phase", "")),
    ]
    return "|".join(parts)


def _phase(over: int) -> str:
    if over <= 5:
        return "powerplay"
    if over <= 15:
        return "middle"
    return "death"


def _contexts_from_pack(pack: dict, split: str) -> list[dict]:
    contexts = []
    scenarios = pack.get("splits", {}).get(split, []) or pack.get("match_starts", [])
    for idx, scenario in enumerate(scenarios):
        game_state = scenario.get("start_state", "batting")
        over = int(scenario.get("over", 0))
        context = {
            "eval_pack_id": pack.get("eval_pack_id", "default"),
            "scenario_id": f"{split}:{idx}",
            "game_state": game_state,
            "strategic_phase": "pre_ball",
            "innings": scenario.get("innings_type", "second" if scenario.get("target") else "first"),
            "over": over,
            "ball": int(scenario.get("ball", 0)),
            "score": int(scenario.get("score", 0)),
            "wickets": int(scenario.get("wickets", 0)),
            "target": scenario.get("target"),
            "phase": _phase(over),
            "field_setting": "Balanced",
            "current_batter": {"name": "Benchmark Batter", "style": "balanced", "aggression": 0.5},
            "current_bowler": {"name": "Benchmark Bowler", "type": "pace", "style": "stock"},
            "batting_strategy": {},
            "bowling_strategy": {},
            "shot_plan": {},
            "delivery_plan": {},
        }
        contexts.append(context)
    return contexts


def generate_cache(eval_pack_path: str, output_path: str, mode: str, split: str, seed: int):
    rng = random.Random(seed)
    with open(eval_pack_path) as f:
        pack = json.load(f)

    policy = create_opponent_policy(mode, rng)
    records = []
    for context in _contexts_from_pack(pack, split):
        if context["game_state"] == "batting":
            kind = "bowling"
            plan = policy.bowling_plan(context)
        else:
            kind = "batting"
            plan = policy.batting_plan(context)
        records.append({
            "key": _cache_key(kind, context),
            "kind": kind,
            "context": context,
            "plan": plan,
        })

        reflection = policy.reflect_after_ball(context, {"runs": 0, "wicket": False, "extra": False})
        records.append({
            "key": _cache_key("reflection", {**context, "last_outcome": {"runs": 0, "wicket": False}}),
            "kind": "reflection",
            "context": context,
            "plan": reflection,
        })

    output = Path(output_path)
    output.parent.mkdir(parents=True, exist_ok=True)
    with output.open("w") as f:
        for record in records:
            f.write(json.dumps(record) + "\n")
    print(f"Wrote {len(records)} opponent cache records -> {output}")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--eval-pack", default=str(_ROOT / "data" / "eval_packs" / "adaptive_t20_v1.json"))
    parser.add_argument("--output", default=str(_ROOT / "data" / "opponent_cache" / "adaptive_t20_v1.jsonl"))
    parser.add_argument("--mode", default="heuristic", choices=["heuristic", "llm_live"])
    parser.add_argument("--split", default="official", choices=["dev", "official"])
    parser.add_argument("--seed", type=int, default=42)
    args = parser.parse_args()
    generate_cache(args.eval_pack, args.output, args.mode, args.split, args.seed)


if __name__ == "__main__":
    main()