Metadata-Version: 2.4
Name: uhp-utils
Version: 0.1.0
Summary: Reusable Python utilities for Universal Hive Protocol engines
Author: dortojekyll
License-Expression: MIT
Project-URL: Homepage, https://github.com/dortojekyll/UHP-utils
Project-URL: Repository, https://github.com/dortojekyll/UHP-utils
Project-URL: Issues, https://github.com/dortojekyll/UHP-utils/issues
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Dynamic: license-file

# UHP Utils

Dependency-free Python tools for running, testing, and comparing
[Universal Hive Protocol (UHP)](https://github.com/jonthysell/Mzinga/wiki/UniversalHiveProtocol)
engines.

## Installation

Python 3.9 or newer is required. From the repository root, run:

```bash
python3 -m pip install .
```

## Use without installation

Run the provided files from the repository root. They accept the same options
as the installed commands:

| Utility | Without installation |
| --- | --- |
| `uhp-wrapper` | `python3 uhp_wrapper.py` |
| `uhp-arena` | `python3 arena.py` |
| `uhp-compare-validmoves` | `python3 compare_validmoves.py` |
| `uhp-compare-perft` | `python3 compare_perft.py` |
| `uhp-to-pgn` | `python3 uhp_to_pgn.py` |

For example:

```bash
python3 arena.py ./engine-a ./engine-b
```

## Quick example

Start one game between two engines:

```bash
uhp-arena ./engine-a ./engine-b
```

Every command provides its full option list through `--help`.

## Contents

| Utility | Functionality |
| --- | --- |
| [Use without installation](#use-without-installation) | Run each utility from its provided Python file. |
| [Engine inputs](#engine-inputs) | Use native executables or local Docker images. |
| [`uhp-wrapper`](#uhp-wrapper) | Send UHP commands to one engine interactively. |
| [`uhp-arena`](#uhp-arena) | Play matches between two engines and save results. |
| [`uhp-compare-validmoves`](#uhp-compare-validmoves) | Find legal-move differences across recorded positions. |
| [`uhp-compare-perft`](#uhp-compare-perft) | Compare perft counts and isolate the first divergent branch. |
| [`uhp-to-pgn`](#uhp-to-pgn) | Convert a UHP game string to readable PGN-style text. |
| [Python API](#python-api) | Use the same utilities as a library. |

## Engine inputs

Whenever a utility asks for an engine, provide one of these values:

- A local executable: `./engine`
- A command available on `PATH`: `hive-engine`
- A local Docker image: `example/hive-engine:latest`

The utility first looks for an executable or command. If none is found, it
looks for a local Docker image with that name. Images are never pulled: the
image must already exist and its `ENTRYPOINT` must start the UHP engine.

Native engine flags use options such as `--engine-arg`, `--white-arg`, or
`--engine-a-arg`. Write a flag that begins with `-` using the equals form, for
example `--engine-arg=--fast`. Environment variables use `--env KEY=value` or
an engine-specific option such as `--white-env KEY=value`. Docker images accept
environment variables, but not native engine flags.

## `uhp-wrapper`

Open a terminal connected to one engine:

```bash
uhp-wrapper ./engine
```

Type UHP commands at the `UHP>` prompt. The wrapper prints the complete engine
response. Use `restart` to restart the engine, `terminate` to stop it, and
`quit` or `exit` to close the wrapper.

To pass a native engine flag, set an environment variable, and limit how long
each command may run:

```bash
uhp-wrapper ./engine --engine-arg=--fast --env THREADS=4 --timeout 10
```

Use `--startup-timeout` if engine startup needs a separate limit.

## `uhp-arena`

Play one game between two engines:

```bash
uhp-arena ./engine-a ./engine-b
```

By default, each engine gets five seconds per move. Multiple games alternate
which engine plays White. Results are saved to `logs/results.json` with the
final state and complete move list.

To play 20 games using a fixed search depth and a 300-move draw limit:

```bash
uhp-arena ./engine-a ./engine-b --games 20 --depth 6 --max-moves 300
```

Choose either `--depth N` or `--time SECONDS`. The default game type is
`Base+MLP`. Use `--results-file PATH` to change the output file or
`--no-results` to disable it. Optional `--command-timeout` and
`--search-timeout` values set wall-clock limits.

## `uhp-compare-validmoves`

Compare the legal moves reported by two engines:

```bash
uhp-compare-validmoves ./engine-a ./engine-b positions.uhp
```

`positions.uhp` must contain one UHP game string per line. By default, the
utility checks the position before the last move of each game and prints any
moves reported by only one engine. Blank lines and `#` comments are ignored.

To check every position in every game using four workers:

```bash
uhp-compare-validmoves ./engine-a ./engine-b positions.txt \
  --full-game --workers 4
```

Use `-` instead of the file name to read stdin. Differences are also verified
by playing the other engine's moves; `--no-cross-check` skips this. Each worker
starts one copy of both engines.

## `uhp-compare-perft`

Compare perft counts from the initial Base-game position through depth 2:

```bash
uhp-compare-perft ./engine-a ./engine-b 'Base;NotStarted;White[1]' 2
```

The arguments are the two engines, the UHP position, and the maximum depth.
The utility compares every reported count and, when they differ, follows the
tree to find the first divergent position.

To test a position from a game through depth 4 with a 30-second command limit:

```bash
uhp-compare-perft ./engine-a ./engine-b \
  'Base+MLP;InProgress;White[2];wA1;bA1 -wA1' 4 --timeout 30
```

### Expected `perft` command

Both engines must accept this line-oriented command after the position has
been loaded with `newgame`:

```text
perft N
```

`N` is a positive integer. For every depth from `1` through `N`, the engine
must return the number of leaf nodes reachable in exactly that many plies from
the current position. A depth `0` row with count `1` is optional. The response
must finish with `ok` on its own line.

The preferred response format is:

```text
perft 3
1: 37
2: 1,369
3: 65,631
ok
```

The first line above is the command sent to the engine; the remaining lines
are its response. Counts must be decimal integers and may contain comma
separators. `DEPTH COUNT` is also accepted. Engines may instead return a table:

```text
depth  count  time
1      37     5us
2      1369   112us
3      65631  2ms
ok
```

In table output, the first two columns must be the numeric depth and count. A
header and extra columns are ignored. Missing depths, non-numeric counts, or a
missing `ok` terminator are invalid responses.

If counts differ, the utility uses `validmoves`, `play` or `pass`, and `undo 1`
to locate the first divergent branch, so both engines must support those UHP
commands too. Use `--valid-moves-only` to skip `perft` and compare only the
legal moves at the supplied position.

## `uhp-to-pgn`

Convert a UHP game string and print the PGN-style result:

```bash
uhp-to-pgn 'Base;WhiteWins;Black[1];wA1'
```

The output contains game metadata followed by the numbered moves. It is printed
to the terminal by default.

To add player names and save the result to a file:

```bash
uhp-to-pgn 'Base;WhiteWins;Black[1];wA1' \
  --white Alice --black Bob --output game.pgn
```

Input may also come from `--input FILE` or stdin. Use `--site`, `--date`, and
repeatable `--tag KEY=value` options for additional metadata.

## Python API

```python
from uhp_utils import EngineWrapper

with EngineWrapper("./engine") as engine:
    moves = engine.command("validmoves")
```

`EngineWrapper` starts the engine, sends complete UHP commands, and closes the
process automatically. The package also exports `Arena`, `UHPState`,
`compare_valid_moves`, `compare_perft`, `convert_uhp_to_pgn`, structured result
types, Docker helpers, and engine error classes.

## License

MIT


With love *@dortojekyll*
