@kradle/challenges-sdk 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/LLM_README.md +285 -192
  3. package/MIGRATION-0.13.md +109 -0
  4. package/README.md +97 -88
  5. package/dist/engine.d.ts +8 -0
  6. package/dist/engine.d.ts.map +1 -0
  7. package/dist/engine.js +17 -0
  8. package/dist/engine.js.map +1 -0
  9. package/dist/index.d.ts +19 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +42 -16
  12. package/dist/index.js.map +1 -1
  13. package/dist/runtime/builder.d.ts +33 -0
  14. package/dist/runtime/builder.d.ts.map +1 -0
  15. package/dist/runtime/builder.js +357 -0
  16. package/dist/runtime/builder.js.map +1 -0
  17. package/dist/runtime/errors.d.ts +61 -0
  18. package/dist/runtime/errors.d.ts.map +1 -0
  19. package/dist/runtime/errors.js +71 -0
  20. package/dist/runtime/errors.js.map +1 -0
  21. package/dist/runtime/reducer.d.ts +205 -0
  22. package/dist/runtime/reducer.d.ts.map +1 -0
  23. package/dist/runtime/reducer.js +2025 -0
  24. package/dist/runtime/reducer.js.map +1 -0
  25. package/dist/runtime/rules.d.ts +29 -0
  26. package/dist/runtime/rules.d.ts.map +1 -0
  27. package/dist/runtime/rules.js +23 -0
  28. package/dist/runtime/rules.js.map +1 -0
  29. package/dist/runtime/sandbox.d.ts +2 -1
  30. package/dist/runtime/sandbox.d.ts.map +1 -1
  31. package/dist/runtime/sandbox.js +7 -4
  32. package/dist/runtime/sandbox.js.map +1 -1
  33. package/dist/runtime/time.d.ts +33 -0
  34. package/dist/runtime/time.d.ts.map +1 -0
  35. package/dist/runtime/time.js +53 -0
  36. package/dist/runtime/time.js.map +1 -0
  37. package/dist/runtime/types.d.ts +963 -0
  38. package/dist/runtime/types.d.ts.map +1 -0
  39. package/dist/runtime/types.js +25 -0
  40. package/dist/runtime/types.js.map +1 -0
  41. package/dist/runtime/wire.d.ts +858 -0
  42. package/dist/runtime/wire.d.ts.map +1 -0
  43. package/dist/runtime/wire.js +28 -0
  44. package/dist/runtime/wire.js.map +1 -0
  45. package/dist/runtime/world.d.ts +58 -3
  46. package/dist/runtime/world.d.ts.map +1 -1
  47. package/dist/runtime/world.js +12 -0
  48. package/dist/runtime/world.js.map +1 -1
  49. package/dist/structures.d.ts +119 -0
  50. package/dist/structures.d.ts.map +1 -0
  51. package/dist/structures.js +279 -0
  52. package/dist/structures.js.map +1 -0
  53. package/dist/test/harness.d.ts +133 -29
  54. package/dist/test/harness.d.ts.map +1 -1
  55. package/dist/test/harness.js +191 -66
  56. package/dist/test/harness.js.map +1 -1
  57. package/dist/test/helpers.d.ts +3 -2
  58. package/dist/test/helpers.d.ts.map +1 -1
  59. package/dist/test/helpers.js +2 -2
  60. package/dist/test/helpers.js.map +1 -1
  61. package/package.json +28 -6
  62. package/dist/runtime/examples/capture-the-flag.d.ts +0 -33
  63. package/dist/runtime/examples/capture-the-flag.d.ts.map +0 -1
  64. package/dist/runtime/examples/capture-the-flag.js +0 -79
  65. package/dist/runtime/examples/capture-the-flag.js.map +0 -1
  66. package/dist/runtime/examples/climb.d.ts +0 -34
  67. package/dist/runtime/examples/climb.d.ts.map +0 -1
  68. package/dist/runtime/examples/climb.js +0 -54
  69. package/dist/runtime/examples/climb.js.map +0 -1
  70. package/dist/runtime/examples/four-bridges.d.ts +0 -107
  71. package/dist/runtime/examples/four-bridges.d.ts.map +0 -1
  72. package/dist/runtime/examples/four-bridges.js +0 -294
  73. package/dist/runtime/examples/four-bridges.js.map +0 -1
  74. package/dist/runtime/examples/herding.d.ts +0 -59
  75. package/dist/runtime/examples/herding.d.ts.map +0 -1
  76. package/dist/runtime/examples/herding.js +0 -181
  77. package/dist/runtime/examples/herding.js.map +0 -1
  78. package/dist/runtime/examples/skywars.d.ts +0 -116
  79. package/dist/runtime/examples/skywars.d.ts.map +0 -1
  80. package/dist/runtime/examples/skywars.js +0 -253
  81. package/dist/runtime/examples/skywars.js.map +0 -1
  82. package/dist/runtime/sdk.d.ts +0 -1038
  83. package/dist/runtime/sdk.d.ts.map +0 -1
  84. package/dist/runtime/sdk.js +0 -1271
  85. package/dist/runtime/sdk.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -17,6 +17,60 @@ also hand-edit this file or the [Unreleased] section at any time.
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [0.13.0] - 2026-09-17
21
+
22
+ ### Added
23
+
24
+ - The builder API: `defineChallenge(config, decl)` returns a challenge, and steps register the behavior (`.setup`, `.start`, `.phase`, `.at`, `.every`, `.on`, `.outcome`, `.onOver`, `.judgeContext`; several `.on()` calls add up). The root declaration carries the data and the state initializers (`state`, `life`, `duration`, `world`, `zones`, `timers`, `votes`, `phases`, `initialPhase`, `markers`), and every literal type (phases, zones, timers, votes, roles, end states, params) comes from it, so you still never write a type argument. The definition locks on the first `init()` or `manifest` read: a later step throws `locked`, and every `init()` is an independent run. `MIGRATION-0.13.md`, in the package, maps the 0.12 API to the new one row by row.
25
+ - The lifecycle is explicit: `CONNECTING` until every participant is connected and positioned, then the initializers run with the observed players and a seeded random source, then the setup effects, then the game. A phase or end request from a callback applies after the callback returns: exit once, enter once, one transition per event group, and a game end wins over a staged phase change. A second request in the same event group throws `phase_twice`, and a request inside `exit` or a pure callback throws. After every event one checkpoint computes the derived scores, the phase end and the game end.
26
+ - Four contexts over the same reads: read-only (the pure callbacks `outcome.*`, `judgeContext`, `until`, a functional `duration` or `next`, `accept`), notification (`onOver`), setup and the full gameplay context. A pure callback cannot write state or queue an effect, and the compiler refuses it. New reads: `g.status`, `g.phaseEntry` (the entry number across all phases), `g.activePlayers`, `p.isActive`, `p.seatNumber`.
27
+ - Phases: `.phase(name, { duration?, next, until?, enter?, exit?, at?, every?, on? })` with a required `next`; `g.startPhase(name)`, `g.endPhase()` (it follows `next`, which may be another phase) and `g.endGame(endState?)` for an unconditional end.
28
+ - Derived scores: `outcome.scores` computes one committed score vector per event. The checkpoint computes a candidate from the previous committed vector, which `until` and `endWhen` read, then commits once after its transition; `exit` and `enter` read the committed scores on every end path. Declare `outcome.scores` or write with `g.setScore`/`g.addScore`, not both.
29
+ - `winBy` winner selectors (`winBy.highestScore`, `winBy.scoreAtLeast`, ...), with an `among` rule for the eligible players.
30
+ - Vote rounds: `g.openVote` opens a round (it throws on an open one), `g.closeVote` closes it and `g.resetVote` reopens it, on a closed round too. A vote declares its `options`, `final`, `allowEliminated` and an `accept` veto in the root declaration; a vote cast against a round that is over is refused.
31
+ - `g.batch(name, fn)` groups effects and `on.batchDone` reports the group; `effectFailed` echoes the failing effect and names the batch while it is pending.
32
+ - Containers need no declaration: `g.containerContents(at)` reads any container the server reports.
33
+ - `g.random` (`int`, `pick`, `shuffle`, `weighted`) and `init.random` in the initializers, seeded per run, so a run replays.
34
+ - Blocks: `g.blockStateAt(at)` returns `{ id, props }`, `g.blockMatches(at, match)` matches a state, and `g.regionLoaded(box)` tells you whether a box is loaded. The block events carry the states and update the mirror before the handler runs, and a `fill` fires one `blocksChanged` entry.
35
+ - `ChallengeError` with a stable `code` and a `category` (`definition`, `api`, `unavailable`). An error in one of your callbacks rolls the reduction back and ends the run as `error_challenge` without another callback; returning a Promise is such an error. An invalid duration is a `bad_duration` error, and a declared one names its path.
36
+ - Every player a callback sees is a read-only view: the observations (`pos`, `slots`, `score`, ...) cannot be written in any mode, and `p.state` is writable in a handler only. A pure or notification callback reads `g.state` and `g.params` through deep read-only views. A write throws `read_only`, and storing a view in the state is refused (`state_not_json`). A parent you froze yourself still hands out views, and a frozen array still enumerates through its view.
37
+ - The published state is deep-frozen, your own shallow freeze included, so a position or a player you kept a reference to cannot change it later. Every context is closed after its reduction.
38
+ - What a callback hands back is copied as plain data: the judge output, `g.log` data and every effect's arguments. So a location or a position read through a view can be logged, placed or reported. Each initializer call gets its own `init` object with copies of `params` and `locations`, and `life`, `duration` and `initialPhase` are captured when the challenge is built, so a later change to your own object changes nothing.
39
+ - New entries: `@kradle/challenges-sdk/engine` (the wire protocol: `Event`, `Effect`, `Action`, `RuntimeManifest`) and `@kradle/challenges-sdk/sandbox`, a shorter name for `@kradle/challenges-sdk/runtime/sandbox`, which stays.
40
+ - `@kradle/challenges-sdk/structures`: `defineStructure` (text layers and a palette, with a rotation table for the directional block properties, a jigsaw's `orientation` included), `compareStructure` (a report with `unknown` cells for the chunks that are not loaded, and `rotation: "any"`), `placeStructure` and `structureBox`. A palette entry may carry a block state (`minecraft:oak_stairs[facing=east]`): its properties rotate with the structure and the comparison checks them one by one, and extra properties on the block in the world are fine. A rotated footprint keeps its minimum corner at `origin`; the origin is not a pivot.
41
+ - The harness (`@kradle/challenges-sdk/test`) reports more: `reducerState`, `result`, `logs`, `metrics` and `batchResult`. `begin` supplies a position per participant and confirms the setup effects, and `confirmSetup` no longer misses a batch when an observation arrives before the confirmation.
42
+ - `MAX_BATCH_EFFECTS`, `MAX_FILL_VOLUME` and `MAX_TRANSITIONS_PER_EVENT` are exported.
43
+ - Every public member of the four entries (`@kradle/challenges-sdk`, `/engine`, `/test`, `/structures`) has a JSDoc comment, the bases an exported interface extends included, so the editor hover on `g.` is complete.
44
+
45
+ ### Changed
46
+
47
+ - **Breaking:** `defineChallenge(config, spec)` with the handlers in one object is replaced by `defineChallenge(config, decl)` plus the steps. Every challenge must be rewritten; `MIGRATION-0.13.md` in the package maps the old API to the new one, row by row.
48
+ - **Breaking:** The custom state moves off the context and the player: `g.game.x` is `g.state.x`, and `p.x` is `p.state.x`. The initializers live in `state: { game, player, players }` and receive `(init)` / `(p, init)` with `init = { params, random, players, locations }` and the observed player (`p.pos` is set).
49
+ - **Breaking:** `p.score = n` is refused. Write with `g.setScore(p, n)` / `g.addScore(p, d)`, or declare `outcome.scores`.
50
+ - **Breaking:** `winBy.highestScore()` gives every eligible player at score 0 the win, where `Win.topScore()` gave no winner below `min: 1`; `winBy.highestScore({ min: 1 })` keeps the old rule. `winBy.scoreAtLeast(n)` is eligible over the active players; `winBy.scoreAtLeast(n, { among: (g) => g.players })` keeps an eliminated player who reached `n`. An old `eligible: (p) => ...` becomes `{ among: (g) => g.players.filter((p) => ...) }`.
51
+ - **Breaking:** `g.status` starts at `CONNECTING`, not `LOBBY`, and a test reads `game.status` instead of `game.state.status`.
52
+ - **Breaking:** A phase without both `duration` and `until` lasts one tick; an unbounded phase needs `until`, or no `.phase()` step at all. After the end the last phase stays in `g.phase` (`result.phase` names it) and the end is `g.status === "OVER"`. `g.phaseRemaining` without a duration is `null`, not `Number.MAX_SAFE_INTEGER`.
53
+ - **Breaking:** Renamed reads and effects: `g.active` is `g.activePlayers`, `g.role`/`g.one` are `g.playersWithRole`/`g.playerWithRole` (which throws unless exactly one), `p.number` is `p.seatNumber`, `g.count` is `g.countItems`, `g.setblock` is `g.setBlock`, `g.rand`/`g.pick`/`g.shuffle` are `g.random.int`/`g.random.pick`/`g.random.shuffle`, and `g.store` is `g.setMetric(name, value, { player? })`.
54
+ - **Breaking:** Changed signatures: `g.give(p, item, count?)` with an id or `{ id, count?, name?, lore?, enchantments?, damage?, snbt? }`; `g.setBlock(at, block)` with an id or `{ id, props?, blockEntity? }`; `g.effect(p, { id, duration, amplifier? })`; `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })`; `g.log(message, { player?, data? })`; `g.findBlocks(box, match, { limit })`; `g.summon(type, at, { nbt })`; `g.worldBorder({ over })`; `g.container(at, [{ id, count, snbt }])`, whose items fill the slots in order, with no slot selection; and `p.slots[i].id` for `p.slots[i].item`.
55
+ - **Breaking:** `g.blockStateAt(at)` returns `{ id, props }` instead of a string, and a `BlockMatch` function receives that object. `g.blockCount` and `g.findBlocks` throw `block_unknown` on a chunk that is not loaded, instead of reporting zero or nothing; pass `{ partial: true }` for the old behavior.
56
+ - **Breaking:** `on.move` carries `from` and `to`, one sample per tick, instead of the `maxY`/`minY` extrema.
57
+ - **Breaking:** The context helper types come from the root const: `CtxOf<typeof challenge>`, `ReadCtxOf`, `SetupCtxOf`, `PlayerIn<G>`. A helper typed from a whole chain expression and used inside that chain is circular, and a root predicate (`votes.room.accept`) cannot call a helper typed from the challenge.
58
+ - **Breaking:** A vote declares its options in the root declaration (`votes: { room: { options: [...] } }`); `votingOptions` leaves `config.ts`.
59
+ - **Breaking:** In the harness, `begin(seed, roster)` supplies a position per participant (`at` to choose); `g.state` is the shared data and `g.reducerState` the whole state; `Fake.has` is `setInventory`; `g.all("store")` is `g.all("metric")`.
60
+ - The compiler now refuses a phase name, a zone, a timer, a vote id, an option or an end state that is not declared.
61
+ - Item names and lore escape backslashes and single quotes in the SNBT, so an apostrophe in a name works.
62
+ - `README.md` and `LLM_README.md`, both in the package, are rewritten for the builder and every example in them compiles against the published entries.
63
+
64
+ ### Removed
65
+
66
+ - **Breaking:** The object-spec API: the `spec` object with `setup`, `start`, `at`, `every`, `on`, `end`, `winners`, `endState`, `judgeContext`, `onOver` and `phases: { [name]: {...} }` at the top level. Use the root declaration and the steps.
67
+ - **Breaking:** `Win` and `End` (`Win.topScore`, `Win.scoreAtLeast`, `End.allEliminated`). Use `winBy` and a predicate (`End.allEliminated` is `(g) => g.activePlayers.length === 0`).
68
+ - **Breaking:** `g.goTo` (use `g.startPhase`, or `g.endGame()` for `g.goTo("END")`), `g.end` (use `g.endGame`), `g.others`, `g.dt` (keep the last tick in the state and use `g.tick` differences), and `g.contents` with the `containers` declaration (use `g.containerContents(at)`, which needs no declaration).
69
+ - **Breaking:** The `announce: { deaths, gameOver }` declaration and the SDK's `=== GAME OVER ===` lines. The SDK prints nothing now: say it in `onOver`.
70
+ - **Breaking:** The `mirror: {...}` declaration. The mirror holds every loaded chunk, every entity and every container the server reports, so nothing is declared for observation; `markers` stays the one pre-game read.
71
+ - **Breaking:** `ContextOf<typeof config, typeof state>`, `ContextOfChallenge` and `typeof challenge.ctx`, replaced by `CtxOf` and friends. `Spec`, `Mirror`, `Observed`, `StatePlayer`, `OverResult` (now `Result`), `TitleTimes` (now `TitleOptions`), `VotingOptionsOf`, `OptionOf`, `Participant` and `Block`/`Item`/`EntityType` (now `BlockId`/`ItemId`/`EntityTypeId`) go with them.
72
+ - **Breaking:** The five reference challenges that shipped under `dist/runtime/examples/` (Climb, Capture the Flag, Four Bridges, Skywars, Sheep Herding). `README.md` and `LLM_README.md` teach the API without them.
73
+
20
74
  ## [0.12.0] - 2026-09-16
21
75
 
22
76
  ### Added
@@ -132,7 +186,8 @@ also hand-edit this file or the [Unreleased] section at any time.
132
186
  - Voting primitives (`votedFor`, `promptVote`, `allVotesIn`, `assignDefaultVotes`) for challenges that use discuss-then-vote-then-act phases as an alternative to pad-based voting.
133
187
  - Build-time validation of vote IDs and options against `KRADLE_CHALLENGE_VOTING_OPTIONS` (when set), matching the existing `getLocation`/`setEndState` pattern. `promptVote` also validates each announced option against the declared `votingOptions`.
134
188
 
135
- [Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.12.0...HEAD
189
+ [Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.13.0...HEAD
190
+ [0.13.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.12.0...v0.13.0
136
191
  [0.12.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.11.0...v0.12.0
137
192
  [0.11.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.10.0...v0.11.0
138
193
  [0.10.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.9.0...v0.10.0