Metadata-Version: 2.5
Name: tcframe
Version: 0.1.2
Summary: Python abstraction layer for the tcframe test case generation framework
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# tcframe-python

Python abstraction layer for [tcframe](https://tcframe.toki.id) — a test case generation framework for competitive programming problems.

## What is this?

`tcframe-python` lets you write tcframe test case specs in **Python** instead of C++. It is designed for problem setters who are not fluent in C++ but still want to use the structured test generation workflow that tcframe provides.

Your spec runs entirely in Python (no compilation step), while the solution binary you provide still runs as a native executable.

**Key advantages over writing C++ specs:**

- Native big integers — `10**1000` just works, no overflow concerns
- Full Python standard library available for test case generation
- Cross-platform: Windows, Linux, macOS
- Simple install: `pip install tcframe`

## Install

```bash
pip install tcframe
# or
uv add tcframe
```

Requires Python ≥ 3.10.

## Quick start

Create a `spec.py` next to your compiled solution:

```python
from tcframe import *

class ProblemSpec(BaseProblemSpec):
    A: int
    B: int
    result: int

    def InputFormat(self):
        LINE(self.A, self.B)

    def OutputFormat(self):
        LINE(self.result)

    def Constraints(self):
        CONS(lambda: 1 <= self.A <= 1000)
        CONS(lambda: 1 <= self.B <= 1000)

class TestSpec(BaseTestSpec):
    def SampleTestCase1(self):
        self.Input(["2 8"])
        self.Output(["10"])

    def TestCases(self):
        CASE(A=1, B=1)
        CASE(A=1000, B=1000)
        CASE(A=rnd.nextInt(1, 1000), B=rnd.nextInt(1, 1000))
        # Python big ints work natively:
        CASE(A=10**500, B=10**500)

tcframe.run(ProblemSpec, TestSpec)
```

Run it:

```bash
python spec.py
```

This generates `tc/` containing `.in` and `.out` files (`.out` files are produced by running `./solution`).

## API overview

| Symbol                               | Purpose                                                                                                    |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `BaseProblemSpec`                    | Base class — declare variables as type annotations, implement `InputFormat`, `OutputFormat`, `Constraints` |
| `BaseTestSpec`                       | Base class — implement `SampleTestCaseN`, `TestCases` (or `TestGroupN`)                                    |
| `LINE(a, b, ...)`                    | Write space-separated values on one line                                                                   |
| `LINES(vec) % SIZE(n)`               | Write one value per line                                                                                   |
| `GRID(mat) % SIZE(r, c)`             | Write a 2-D matrix, space-separated rows                                                                   |
| `EMPTY_LINE()`                       | Write a blank line                                                                                         |
| `CONS(lambda: expr)`                 | Declare a constraint — checked for each test case                                                          |
| `CASE(A=1, B=2)`                     | Declare a test case by assigning variable values                                                           |
| `rnd.nextInt(lo, hi)`                | Random integer in [lo, hi] (inclusive)                                                                     |
| `rnd.nextDouble(lo, hi)`             | Random float in [lo, hi]                                                                                   |
| `tcframe.run(ProblemSpec, TestSpec)` | Entry point — call at the bottom of `spec.py`                                                              |

## Options

```python
tcframe.run(
    ProblemSpec,
    TestSpec,
    seed=42,               # random seed (default 0)
    output_dir='tc',       # output directory (default 'tc')
    solution='./solution', # solution command (default './solution')
)
```

## Examples

Working examples are in [`examples/`](examples/):

| Folder | What it shows |
| --- | --- |
| [`aplusb/`](examples/aplusb/) | Basic usage — A+B problem with constraints and random test cases |
| [`aplusb_multi/`](examples/aplusb_multi/) | Multiple test cases per file (`MultipleTestCasesConfig`) |
| [`distinct/`](examples/distinct/) | Subtasks + grading with TLE/MLE verdict detection |

Each folder has a `spec.py` (the generator spec) and one or more `solution_*.cpp` files. To run an example:

```bash
cd examples/aplusb
make          # compiles the solution
python spec.py
```

Generated test cases land in a `tc/` folder next to the spec.

## Full design

See [`DESIGN.md`](DESIGN.md) at the repo root for the full architecture and design decisions.

## Future Works

- **Interactive problems on Windows.** Grading with a communicator (`InteractiveEvaluator()`) currently works only on Linux and macOS, because the solution and communicator talk over file descriptors 3 and 4, which Windows does not support. The plan is to switch to the protocol documented for C++ tcframe: the communicator gets the input file as `argv[1]`, talks to the solution over its stdin/stdout, and writes the verdict (`AC` / `WA`) to stderr. That uses plain pipes, which work on every OS, and makes communicators written for C++ tcframe usable as-is. It will also fix the verdict: today a communicator that reports `WA` is still graded as AC.
- **Tested.** Currently this project still not have been tested yet.

## Changelog

| Version | Changes |
| --- | --- |
| 0.1.2 | Solutions and scorers run the same way on Windows, Linux and macOS (`./solution` also finds `solution.exe` on Windows). A missing, non-executable or wrong-OS solution now stops with a clear error and a compile hint instead of failing every test case with exit code 127. Failed test cases show the solution's stderr. Crashes without a memory limit are reported by signal name (e.g. `SIGSEGV`) instead of "memory limit exceeded". |
| 0.1.1 | Add README on the package. |
| 0.1.0 | Initial version. |
