File size: 2,286 Bytes
4334698
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Math problem pool: load, sample (deterministic via seed), and verify.

Per PROJECT.md §14. The placeholder ``data/problems_pool.json`` ships
~10 verifiable arithmetic problems; the real ~500-problem pool is
generated on a parallel track and replaces the JSON without code
changes.
"""

import json
import random
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Union


@dataclass
class Problem:
    id: int
    problem: str
    answer: int
    difficulty: str

    def to_dict(self) -> Dict[str, Any]:
        """JSON-serialisable form matching the §14.2 schema."""
        return {
            "id": self.id,
            "problem": self.problem,
            "answer": self.answer,
            "difficulty": self.difficulty,
        }


@dataclass
class ProblemPool:
    problems: List[Problem] = field(default_factory=list)

    @classmethod
    def load(cls, path: Union[str, Path]) -> "ProblemPool":
        """Load a problem pool from a JSON file at ``path``."""
        path = Path(path)
        with path.open() as f:
            data = json.load(f)
        return cls(problems=[Problem(**item) for item in data])

    def sample(self, n: int = 10, seed: Optional[int] = None) -> List[Problem]:
        """Return ``n`` problems sampled without replacement.

        Deterministic when ``seed`` is given. When ``n`` exceeds the
        pool size, returns the full pool (clamped, not raising).
        """
        rng = random.Random(seed)
        return rng.sample(self.problems, k=min(n, len(self.problems)))

    def __len__(self) -> int:
        return len(self.problems)


def load_problems(path: Optional[Union[str, Path]] = None) -> ProblemPool:
    """Load the bundled problem pool, or a custom path if provided."""
    if path is None:
        path = Path(__file__).parent / "data" / "problems_pool.json"
    return ProblemPool.load(path)


def check_answer(submitted: Any, ground_truth: Any) -> bool:
    """Strict integer equality.

    Rejects type mismatches (str vs int, float vs int, bool vs int)
    explicitly — ``True == 1`` would otherwise pass naive ``==``.
    """
    if type(submitted) is not int or type(ground_truth) is not int:
        return False
    return submitted == ground_truth