Metadata-Version: 2.4
Name: daydokuthon
Version: 0.1.0
Summary: Honeycomb puzzle hackathon SDK, trace judge and replay platform
License-Expression: Apache-2.0
Keywords: hackathon,puzzle,honeycomb,calendar,validator
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: server
Requires-Dist: fastapi<1,>=0.115; extra == "server"
Requires-Dist: uvicorn<1,>=0.30; extra == "server"
Provides-Extra: dev
Requires-Dist: daydokuthon[server]; extra == "dev"
Requires-Dist: pytest<10,>=8; extra == "dev"
Requires-Dist: httpx<1,>=0.27; extra == "dev"
Requires-Dist: build<2,>=1.2; extra == "dev"
Requires-Dist: ruff<1,>=0.9; extra == "dev"
Provides-Extra: publish
Requires-Dist: build<2,>=1.2; extra == "publish"
Requires-Dist: twine<8,>=6; extra == "publish"
Dynamic: license-file

# Daydokuthon

**Write the algorithm. Record the moves. Replay the solution.**

Daydokuthon is a Python SDK and hackathon platform for a honeycomb calendar puzzle. It supplies twelve pieces, a 50-cell hexagonal board, rotations/flips, collision checks, a JSON action recorder, an independent validator, and a replay visualizer. Participants implement their own solving strategy; no completed solver is included.

Requires **Python 3.12 or later**. Licensed under **Apache-2.0**.

## Install

```shell
# Participant SDK and validator — no third-party runtime dependencies
python -m pip install daydokuthon

# Add the local visualizer and central organizer server
python -m pip install "daydokuthon[server]"
```

The visualizer is bundled. No Node installation, frontend build, database server, or GitHub access is needed.

## Get started

Create a working directory, download `challenge.json` from your organizer, then run:

```shell
daydokuthon init
```

This creates `solution.py` with the participant interface:

```python
from daydokuthon import PuzzleAlgorithm, Simulation


class MyAlgorithm(PuzzleAlgorithm):
    def solve(self, simulation: Simulation) -> None:
        # Implement your greedy/search/backtracking strategy here.
        # The supplied starter demonstrates placement of one piece only.
        ...
```

Run your implementation to create a trace, validate it, and replay it:

```shell
daydokuthon run --algorithm solution:MyAlgorithm --challenge challenge.json --output submission.json
daydokuthon validate submission.json --challenge challenge.json
daydokuthon replay submission.json --challenge challenge.json
```

`python -m daydokuthon` supports the same commands. Replay requires the `[server]` extra and opens localhost:8002; use `--no-browser` to open it yourself.

### Trying the SDK without an organizer

Create a local `challenge.json` with these contents:

```json
{
  "id": "my-local-challenge",
  "date": "2026-06-01",
  "title": "My local challenge",
  "puzzle_version": "honeycomb-1"
}
```

You can use this for local recording, validation and replay. For competition submissions, download the actual challenge: its ID, date and puzzle version must match the server.

## Participant interfaces

```python
from daydokuthon import BOARD, PIECES, Challenge, HexCoordinate, Simulation
from datetime import date

simulation = Simulation(Challenge("local", date(2026, 6, 1)))
print(len(PIECES))       # 12
print(len(BOARD.cells))  # 50

# Choose candidates in your own algorithm:
piece = PIECES[0]
for orientation in simulation.orientations(piece.id):
    for origin in sorted(BOARD.coordinates):
        if simulation.can_place(piece.id, origin,
                                orientation.rotation, orientation.flipped):
            simulation.place(piece.id, origin,
                             orientation.rotation, orientation.flipped)
            simulation.remove(piece.id)  # Record a backtracking step.
            break
    break
```

| Interface | Purpose |
| --- | --- |
| `PIECES`, `PIECES_BY_ID` | Immutable piece definitions with stable IDs P01–P12 |
| `BOARD.cells`, `.coordinates`, `.by_value` | Corrected board layout and lookup |
| `simulation.date`, `.exposed`, `.occupied`, `.placements` | Puzzle inputs and current state |
| `simulation.orientations(piece_id)` | Deduplicated rotations and flips |
| `simulation.can_place(piece_id, origin, rotation=0, flipped=False)` | Check legality without recording |
| `simulation.place(...)` | Validate and record a whole-piece placement |
| `simulation.remove(piece_id)` | Validate and record a removal |
| `simulation.is_solved` | All twelve pieces cover exactly the 47 target cells |
| `simulation.to_trace()` | JSON-serializable copy of recorded actions |
| `validate_trace(raw, challenge)` | Independent replay and validation report |

Origins are axial `HexCoordinate(q, r)` values, never pixels. Rotation is an integer from 0 to 5 in 60° steps; `flipped` is boolean. Transformation order is reflection, rotation, normalization, then translation. The final two board rows are `22 23 24 25 26 27 Fri` and `28 29 30 31 Sun Sat`.

Failed moves raise `IllegalMove` and do not change the state or history. Repositioning a piece requires removal followed by placement. Exceptions during an algorithm's `solve` method still export the successfully recorded prefix. Incomplete results exit nonzero and can be replayed for debugging.

## Organizer server

Install `daydokuthon[server]`. Configure an organizer password of at least 12 characters and a persistent signing secret of at least 32 characters.

PowerShell:

```powershell
$env:DAYDOKUTHON_ADMIN_PASSWORD = Read-Host 'Organizer password (12+ characters)'
$env:DAYDOKUTHON_SECRET = python -c "import secrets; print(secrets.token_urlsafe(48))"
daydokuthon serve --database ./daydokuthon.db
```

Linux/macOS:

```shell
read -rs -p 'Organizer password: ' DAYDOKUTHON_ADMIN_PASSWORD
export DAYDOKUTHON_ADMIN_PASSWORD
export DAYDOKUTHON_SECRET="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
daydokuthon serve --database ./daydokuthon.db
```

Open **http://127.0.0.1:8001/organizer** to publish challenges and review submissions. Participants use **http://127.0.0.1:8001/** to select a challenge, enter a roll number, and upload their JSON trace.

- Challenges accept any valid date and optional opening/closing times.
- Every bounded attempt is retained, including invalid JSON, incomplete solutions and late attempts.
- The server records UTC receipt time after the complete upload arrives, before validation.
- Private receipt links provide results and replay; only organizers can browse all attempts and export CSV.
- The validator accepts any legal complete arrangement, independently of the solving approach.
- The visualizer supports placement/removal animation, stepping, scrubbing, replay and speed controls.

SQLite persistence is included. Default limits are 2 MiB and 10,000 actions; override using `DAYDOKUTHON_MAX_BYTES` and `DAYDOKUTHON_MAX_ACTIONS`. Set `DAYDOKUTHON_SECURE_COOKIE=true` behind HTTPS. Preserve the signing secret across restarts; protect the database and private receipt links. Roll numbers are self-reported identifiers.

Uploaded files are **JSON data only**. The server never executes participant Python. It independently derives piece cells and checks schemas, types, versions, dates, orientations, board bounds, exposed cells, overlaps, removals and final coverage. Invalid traces identify the first failing action and replay only its legal prefix.

Judging is correctness-only. Uploaded traces do not prove algorithm runtime, search efficiency, originality or participant identity. No public solution gallery or completed solver is bundled.
