# Andrews–Curtis Challenge — Discovery

Competition ID: `acc`. Submission kind: `acc-solutions`.

## Endpoint map

**Competition endpoints**

- [Get competition detail](./acc/get-competition-detail.md)
- [Get submission spec](./acc/get-submission-spec.md)
- [Get my participation](./acc/get-my-participation.md)
- [Submit solutions](./acc/submit-solutions.md)
- [List my submissions](./acc/list-my-submissions.md)
- [Get a submission](./acc/get-submission.md)
- [Download a submission](./acc/download-submission.md)
- [Get leaderboard](./acc/get-leaderboard.md)
- [Get my standing](./acc/get-my-standing.md)
- [Get team scoring challenges](./acc/get-team-challenges.md)
- [Get challenge progress](./acc/get-challenge-progress.md)
- [Get complete challenge snapshot](./acc/get-challenge-snapshot.md)

## Workflow

1. Read the submission spec and participation state before submitting. Eligibility is checked again at admission and does not reserve quota.
2. Send UTF-8 TXT unchanged in `payload.text` using `application/json`; add an optional note in `meta.description`.
3. Save the `submissionId` returned with `202 Accepted` and poll until `complete` or `failed`. A complete batch can include unsuccessful solutions.

See the [official challenge repository](https://github.com/SAIRcompetition/Andrews-Curtis/tree/main/competition) for challenge data, move definitions, mathematical rules, and a local verifier. The submission spec defines API limits.

## Team ownership

ACC enrollment and active team membership are required to submit; team ownership is not required. Complete enrollment and team setup on the [competition website](https://competition.sair.foundation/competitions/acc/overview). An invitation grants no membership or private access until accepted after enrollment.

Submissions belong to the team recorded when they are accepted. All current active members can read that team’s private history, verification results and original files, including queued, running, unsuccessful and unpublished records. An individual account’s history is not a substitute for team history.

Joining another team does not transfer submissions or scores. Former-team records are no longer accessible after the change.

## Problems

| Problem     | Challenge IDs           | Move IDs | Maximum rank | Solution target |
| :---------- | :---------------------- | :------- | :----------- | :-------------- |
| `ac`        | `ac-00001`–`ac-10115`   | 0–13     | 2            | `[[1],[2]]`     |
| `stable_ac` | `sac-00001`–`sac-10115` | 0–256    | 8            | `[]`            |

A normal batch may mix both problems. Each has an independent leaderboard; there is no combined ranking. The three leaderboard operations, `/discoveries`, and `/discoveries/snapshot` require exactly one `problem=ac|stable_ac`.

## Submission

| Field               | Type and meaning                                                                                                                                                                                                          |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `submissionId`      | `string` — Immutable batch ID. Preserve it as text.                                                                                                                                                                       |
| `competitionId`     | `"acc"`.                                                                                                                                                                                                                  |
| `teamId`            | `string` — Immutable owning team ID recorded at admission. Later membership changes do not transfer the submission or its score.                                                                                          |
| `submittedBySairId` | `string` — Submitting account ID; private team data.                                                                                                                                                                      |
| `status`            | `"queued"`, `"running"`, `"complete"`, or `"failed"`.                                                                                                                                                                     |
| `receivedAt`        | UTC ISO 8601 `string` — Receipt time, which can contain fractional seconds. Used to determine scoring order.                                                                                                              |
| `updatedAt`         | UTC ISO 8601 `string` — Last batch update.                                                                                                                                                                                |
| `solutionCount`     | Positive `integer` — Number of solution entries in the admitted document.                                                                                                                                                 |
| `meta`              | `{ "description": string }` or `null` — Independent submission note, trimmed at admission. Always returned to the owning team.                                                                                            |
| `results`           | [`SolutionResult[]`](#solutionresult) — Empty while `queued` or `running`. When `complete`, contains one result for each input solution, in input order. |
| `errorMessage`      | `string` or `null` — Batch processing failure message; otherwise `null`.                                                                                                                                                  |

`complete` means mathematical verification finished, even when every solution was unsuccessful. Inspect each result's `ok` value and feedback. For a `failed` batch, read `errorMessage`. Scoring and public publication are separate from verification. Accepted batches consume quota regardless of the verification or processing outcome.

### SolutionResult

Per-solution results use **snake_case**. Batch and challenge-progress responses use **camelCase**. Preserve the field names documented for each response.

Successful result (`ok: true`):

| Field          | Type and meaning                                                                                     |
| :------------- | :--------------------------------------------------------------------------------------------------- |
| `challenge_id` | `string` — Exact input challenge ID.                                                                 |
| `problem`      | `"ac"` or `"stable_ac"`.                                                                             |
| `ok`           | `true`.                                                                                              |
| `length`       | Non-negative `integer` — Verified move count. Zero is valid when the input already meets the target. |

Unsuccessful result (`ok: false`):

| Field          | Type and meaning                                                                                                 |
| :------------- | :--------------------------------------------------------------------------------------------------------------- |
| `challenge_id` | `string` — Exact input challenge ID.                                                                             |
| `problem`      | `"ac"` or `"stable_ac"`; `null` only for `E_UNKNOWN_CHALLENGE`.                                                  |
| `ok`           | `false`.                                                                                                         |
| `code`         | `"E_UNKNOWN_CHALLENGE"`, `"E_PATH_TOO_LONG"`, `"E_BAD_MOVE_ID"`, `"E_MOVE_NOT_APPLICABLE"`, or `"E_NOT_TARGET"`. |
| `move_index`   | Non-negative `integer` or `null` — Zero-based failing move when a particular move fails; otherwise `null`.       |
| `move`         | Integer move ID for `E_MOVE_NOT_APPLICABLE`; otherwise `null`.                                                   |
| `reason`       | Applicability reason for `E_MOVE_NOT_APPLICABLE`; otherwise `null`.                                              |
| `final_shape`  | Variable-length non-negative `integer[]` or `null` — Final relator lengths for `E_NOT_TARGET`; otherwise `null`. |

These are result codes inside a successful `200` detail response, not HTTP errors. Unknown or unscored training IDs produce `E_UNKNOWN_CHALLENGE`. Move values must be integer JSON literals, AC `0`–`13` or Stable AC `0`–`256`; decimal/exponent literals, booleans, and out-of-range values fail verification. A rejected solution has no success-only `length` field.

## PublicTeam

| Field        | Type and meaning                                        |
| :----------- | :------------------------------------------------------ |
| `teamId`     | `string` — Stable team ID.                              |
| `teamName`   | `string` — Public team name in the scoring snapshot.    |
| `teamNumber` | `string` or `null` — Public team number, when assigned. |

Leaderboard entries expose this team projection and include a separate nullable [teamDetails](./acc/get-leaderboard.md#teammemberdetails) with currently permitted shared member information. These responses do not expose private account IDs, unshared member identities, moves, intermediate states, or submitted notes.

## Scoring and snapshots

Only teams holding the current shortest verified solution for a scored challenge receive points for it. With base score `V` and `k` teams tied at the shortest length, each receives `V × 2^(1−k)`. For `V = 1`, one team gets `1`, two tied teams each get `0.5`, and three each get `0.25`. A later shorter solution replaces the previous minimum; equal or longer resubmissions by the same team do not create another tied team.

Teams are ranked by exact descending score, then by the earliest scoring event at which the team reached its current total, then by team ID. Scoring events follow receipt time, submission ID, and solution input order. Display scores are strings rounded to four decimal places using round-half-even. Render `rank` and `score` as returned; sorting by rounded display scores loses the official order.

Challenge progress reports the number of teams tied at the current best length as `kTeams`; it does not return a representative or full tied-team list.

Leaderboard operations use `snapshotId` to read the same score version across rankings, team standings, and scoring challenges. Keep it with associated cursors; on `SNAPSHOT_EXPIRED`, discard both and fetch a new version.

[Challenge progress](./acc/get-challenge-progress.md) uses `pageIndex + pageSize` and a signed `snapshotId`; pages from one version can be read in parallel. The [complete challenge snapshot](./acc/get-challenge-snapshot.md) returns all solved and unsolved scored challenges without pagination or snapshot selection.

Both progress endpoints omit original words by default. Add `include=initialRelators` to include them; use `challengeId` on the paginated endpoint to select one challenge.

`generatedAt` can be `null` when publication is allowed but no scoring run exists yet. The complete snapshot still contains the entire scored pool in that case, with every challenge marked unsolved. `published: false` always has empty items, `totalCount: 0`, and a null timestamp; leaderboard operations additionally return `nextCursor: null`, while paginated challenge progress returns `snapshotId: null` and the requested `pageIndex` and `pageSize`. This is an unpublished result, not evidence that nobody has submitted.

## Shared errors

All JSON responses use the [envelope](../../conventions.md#envelope). All documented endpoints require their documented API-key scope. Read endpoints consume the account read tier. The submission endpoint has its own [team quota behavior](./acc/submit-solutions.md#quota-and-retries).

|         HTTP          | Code                                                 | Meaning                                                                                                                                                                                                                      |
| :-------------------: | :--------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401` / `403` / `429` | Shared authentication, scope, and account-read codes | See [authentication errors](../../errors.md#authentication-and-scopes) and [rate limiting](../../rate-limiting.md).                                                                                                          |
|         `404`         | `NOT_FOUND`                                          | Competition not visible, or a private submission absent or inaccessible.                                                                                                                                                     |
|         `503`         | `ACC_NOT_READY`                                      | ACC submissions or published results are temporarily unavailable. Retry reads later; check participation before submitting again. Applies to the submission spec, submission, leaderboard, progress, and snapshot endpoints. |
|         `409`         | `ACC_FREEZE_CONFLICT`                                | The submission could not be accepted against the current competition rules. Read the submission spec again before deciding whether to resubmit.                                                                              |

ACC operation pages define validation, eligibility, and cursor errors that differ from the generic competition submission wrapper. `submitBlockedReason` is human-readable text or `null`; it is not a stable machine code and does not include remaining quota. Branch on the POST error code after a rejected submission.
