# Example code

> The starter repository and a minimal solver.

Source: <https://frontieror-challenge.com/docs/main/example-code>

## The starter repository

[Alpha-Z-Pte-Ltd/frontieror-challenge-starter](https://github.com/Alpha-Z-Pte-Ltd/frontieror-challenge-starter) is a working setup to build on. It downloads the problems, runs an example agent that writes a solver for each one, tests the solvers the way the sandbox runs them, and submits them.

You need [uv](https://docs.astral.sh/uv/), macOS or Linux (WSL on Windows), and an account with a team. The example agent calls a model through [OpenRouter](https://openrouter.ai), so it needs an OpenRouter key. Docker is needed only for `--docker`.

## Quick start

```sh
git clone https://github.com/Alpha-Z-Pte-Ltd/frontieror-challenge-starter.git
cd frontieror-challenge-starter
uv run frontieror init            # sign in and download the problems
uv run frontieror find-solvers    # let the agent write solvers/<slug>/solve.py
uv run frontieror test            # run them on the public instances
uv run frontieror submit --wait   # submit and wait for the score
```

`init` opens your browser to approve the sign-in, then saves an API token in `.env`. See [Authentication](https://frontieror-challenge.com/docs/authentication). Run it again when a new round opens.

## Commands

| Command                | What it does                                                                                                                                                                                                                                                                                                           |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init`                 | Signs in (or uses `--token`), asks for an OpenRouter key if none is set, downloads the open round's problems, public instances and published checkers to `data/`, and prepares the Python environment.                                                                                                                 |
| `find-solvers [SLUG…]` | Runs the agent for each problem without a solver. `--force` replaces existing solvers; `--jobs N` works on several problems at once; `--yes` runs without asking when `data/` is from an earlier round.                                                                                                                |
| `test [SLUG…]`         | Runs each solver on the public instances, checks the output format and, with the problem's published checker, feasibility. Outputs and logs go to `runs/<slug>/test/`.                                                                                                                                                 |
| `submit`               | Packs every `solvers/<slug>/` folder and submits it in `python-3.14`, with the extra packages from `frontieror.toml`. It shows when the results should arrive; `--wait` follows the progress and shows the score; `--yes` skips the questions; `--acknowledge-missing` submits even when some problems have no solver. |

Add `--docker` to `find-solvers` or `test` to run solvers in the sandbox's image with its limits.

## Which files are yours

| Path               |                                                                                                             |
| ------------------ | ----------------------------------------------------------------------------------------------------------- |
| `agent/main.py`    | The agent: `find_solver(problem, tools, settings)` and its model call through OpenRouter. Change it freely. |
| `agent/prompts.py` | The prompts, from FrontierOR's one-shot prompt adapted to the sandbox.                                      |
| `solvers/`         | The solvers, one folder per problem. This is what gets submitted.                                           |
| `frontieror.toml`  | Extra packages and the agent's settings: `model` and `max_attempts`.                                        |
| `toolkit/`         | The `frontieror` command. Leave it unchanged so updates apply cleanly.                                      |

## The agent entry point

`find-solvers` calls the function named by `[agent] entry` in `frontieror.toml` (`agent:find_solver` by default) once per problem:

```python
def find_solver(problem: Problem, tools: Tools, settings: dict) -> None:
    ...
```

- `problem` has the problem's `slug`, `name`, `objective`, `description`, `instance_schema`, `solution_schema`, its public `instances` and `checker`, the path of its published checker or `None`.
- `tools.test(code)` runs `code`, which defines `solve(instance, time_limit_s) -> dict`, on the public instances (or the ones named in `instances`) and returns a report with each run's status, message, objective, time and log. The toolkit appends the part that reads the instance and writes the solution.
- `tools.save(code)` writes `solvers/<slug>/solve.py`, with the same part appended.
- `tools.log(message)` prints a progress line; `tools.log_file(name, text)` writes a file to `runs/<slug>/`.
- `tools.environment` lists the Python version and the packages available, for the prompt.
- `settings` is the `[agent]` table of `frontieror.toml`.

The example agent asks the model for a solver, tests it and feeds the failures back, for up to `max_attempts` replies. It saves the attempt that passes the most instances. The replies and each attempt's runs are kept in `runs/<slug>/`.

## Testing locally

`test` runs natively by default: fast, but without the sandbox's memory limit or isolation. `test --docker` runs in the same image with the same limits as the sandbox. On Apple Silicon it runs under emulation, so it checks correctness, not speed.

Both check the run and the solution's format, then run the problem's [checker](https://frontieror-challenge.com/docs/problems#checkers) when it is published: natively, or with `--docker` in the `python-3.12` image as in the evaluation. A solution the checker rejects shows as `infeasible`, with the violations. Scores come only from a submission.

## A minimal solve.py

A complete solver for `barnhart2000` that rejects every commodity, which is always feasible:

`barnhart2000/solve.py`

```python
import argparse
import json


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--problem", required=True)
    parser.add_argument("--instance", required=True)
    parser.add_argument("--output", required=True)
    parser.add_argument("--time-limit", type=float, default=60)
    args = parser.parse_args()

    with open(args.instance) as f:
        instance = json.load(f)

    commodities = instance["commodities"]["commodity_list"]
    objective = sum(c["artificial_arc_cost"] * c["demand"] for c in commodities)
    solution = {
        "objective_value": objective,
        "commodities": [
            {"commodity_id": c["commodity_id"], "rejected": True, "path_arcs": []}
            for c in commodities
        ],
    }
    with open(args.output, "w") as f:
        json.dump(solution, f)


if __name__ == "__main__":
    main()
```

Zip the `barnhart2000` folder and it is a valid submission: the solution is feasible, though far from optimal. The contract it follows is on [Submission format](https://frontieror-challenge.com/docs/main/submission-format).
