@kradle/challenges-sdk 0.14.1 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -17,6 +17,45 @@ also hand-edit this file or the [Unreleased] section at any time.
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [0.16.0] - 2026-09-24
21
+
22
+ ### Added
23
+
24
+ - `outcome.points(g, player, result)`: the integer a participant earns, evaluated once per player at the end with the frozen result (so `result.isWinner(player)` and the roster are available). Without it a winner earns 1 and anyone else 0. `outcome.pointsDescription` is one line for the menus that offer the challenge ("1 point for a win, plus 10 per extra opponent"), carried by the manifest. A non-integer, or more than 1,000,000 in either direction, fails the run (`bad_points`).
25
+ - `game_over` carries `points` and `achievements` per player (by name). `g.achievement(player, id)` now records the id once per player, readable as `p.achievements` (the log line still goes out on every call); an id is lowercase letters, digits, `_` and `-`, up to 64 characters; a malformed one, or a 65th achievement for one player, fails the run (`bad_achievement`). An achievement granted in `onOver` is included, so "won without a death" works from the frozen result. A failed run pays nobody and keeps the achievements earned before the failure.
26
+ - `MINECRAFT_TARGET` (`"26.1"`), the Minecraft version the SDK builds for, is exported from `@kradle/challenges-sdk` and `@kradle/challenges-sdk/engine`, and the manifest carries it as `minecraft`.
27
+ - `GameRuleId`, exported from `@kradle/challenges-sdk`: the game rule ids Minecraft 1.21.11 and later know.
28
+ - `MIGRATION-0.16.md`, in the package, maps the 0.15 forms to the 26.1 ones row by row.
29
+
30
+ ### Changed
31
+
32
+ - **Breaking:** The SDK builds for Minecraft 26.1 (Paper 26.1.2). Rebuild every challenge with this SDK and activate it again: Kradle refuses a bundle built for another Minecraft version, in both directions.
33
+ - **Breaking:** `g.gamerule(rule, value)` takes the Minecraft 1.21.11 ids (`GameRuleId`), e.g. `minecraft:keep_inventory` instead of `keepInventory`; the old camelCase names are refused. The world defaults (`daylightCycle`, `mobSpawning`, `naturalRegeneration`, `maxHealth`) need no change: the SDK emits the new ids.
34
+ - **Breaking:** An item request carries its metadata as fields (`name`, `lore`, `enchantments`, `damage`) and anything else as `components`, the raw item argument suffix (`"[unbreakable={}]"`). A metadata field wins over the same component.
35
+ - **Breaking:** Attribute ids lost the `generic.` prefix in Minecraft 1.21.2: use `g.attribute(p, "minecraft:max_health", 30)`.
36
+ - **Breaking:** Command strings and the SNBT of `g.summon` and `g.setBlock` follow the 1.20.5+ forms: item components (`give @s stick[custom_name="..."]`, `{Item:{id:"...",count:1}}`), lowercase banner `patterns`, sign text as text components, and `click_event` in a `tellraw`. `MIGRATION-0.16.md` lists them.
37
+ - Releases publish under the npm tag `latest` again: install the SDK with `npm install @kradle/challenges-sdk`, without `@next`. `README.md` says so.
38
+ - `LLM_README.md` names the item `components` field, the game rule ids and the attribute ids without the `generic.` prefix.
39
+
40
+ ### Removed
41
+
42
+ - **Breaking:** `snbt` on an item request, and the `nbt` field of the `give` effect and of a container item on the wire. Use the metadata fields (`name`, `lore`, `enchantments`, `damage`) or `components` for anything else.
43
+
44
+ ## [0.15.0] - 2026-09-18
45
+
46
+ ### Added
47
+
48
+ - Two reads on the test kit's `Game`: `game.participants` gives every participant's record by name (the observations, `state`, `score`) and `game.votes` gives every vote's record by id (`open`, `round`, `cast`). Both are read-only. `scores(game)` is unchanged.
49
+
50
+ ### Changed
51
+
52
+ - **Breaking:** `game.participant(name)` returns a read-only record. A test that wrote through the record no longer compiles; drive the state through the fake player and the events.
53
+ - The guides name what happens, not the machinery that does it: `README.md`, `LLM_README.md` and `MIGRATION-0.13.md`, all three in the package, no longer speak of the arena, the reducer or the sandbox, and the JSDoc on the four entries says the same. A challenge is a set of pure rules that Kradle runs, and `kradle challenge test` runs them against the built bundle, with the limits of a run: no clock, no random, no network. In `LLM_README.md` the "Rules of the Reducer" section is now "Constraints".
54
+
55
+ ### Removed
56
+
57
+ - **Breaking:** The test kit no longer exposes `game.reducerState` or the `ReducerState` type. Read the players through `game.participants` and the votes through `game.votes`; `JSON.parse(game.checkpoint())` gives the whole state. See "The test kit after 0.14.1" in `MIGRATION-0.13.md`.
58
+
20
59
  ## [0.14.1] - 2026-09-18
21
60
 
22
61
  ### Changed
@@ -214,7 +253,9 @@ also hand-edit this file or the [Unreleased] section at any time.
214
253
  - Voting primitives (`votedFor`, `promptVote`, `allVotesIn`, `assignDefaultVotes`) for challenges that use discuss-then-vote-then-act phases as an alternative to pad-based voting.
215
254
  - 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`.
216
255
 
217
- [Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.14.1...HEAD
256
+ [Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.16.0...HEAD
257
+ [0.16.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.15.0...v0.16.0
258
+ [0.15.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.14.1...v0.15.0
218
259
  [0.14.1]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.14.0...v0.14.1
219
260
  [0.14.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.13.1...v0.14.0
220
261
  [0.13.1]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.13.0...v0.13.1
package/LLM_README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # LLM_README.md - @kradle/challenges-sdk API Reference
2
2
 
3
- This document is the API reference for AI agents that write Kradle challenges with `@kradle/challenges-sdk`. A challenge is a reducer: a pure function that the arena runs during the game inside a sandbox. Read this file before you write a challenge. The declarations (`.d.ts`) carry the full JSDoc of every member; this file is the tutorial and the index.
3
+ This document is the API reference for AI agents that write Kradle challenges with `@kradle/challenges-sdk`. A challenge is a set of pure rules that Kradle runs during the game. Read this file before you write a challenge. The declarations (`.d.ts`) carry the full JSDoc of every member; this file is the tutorial and the index.
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -13,7 +13,7 @@ This document is the API reference for AI agents that write Kradle challenges wi
13
13
  7. [The Context](#the-context)
14
14
  8. [Players and Entities](#players-and-entities)
15
15
  9. [Structures](#structures)
16
- 10. [Rules of the Reducer](#rules-of-the-reducer)
16
+ 10. [Constraints](#constraints)
17
17
  11. [Testing](#testing)
18
18
  12. [Build and Run](#build-and-run)
19
19
  13. [Complete Examples](#complete-examples)
@@ -25,11 +25,11 @@ This document is the API reference for AI agents that write Kradle challenges wi
25
25
 
26
26
  ## Overview
27
27
 
28
- `@kradle/challenges-sdk` is a TypeScript SDK for Minecraft challenges that AI agents play on Kradle. A challenge is one root call, `defineChallenge(config, decl)`, plus steps that register the behavior: `.setup(...)`, `.start(...)`, `.phase(name, {...})`, `.on({...})`, `.outcome({...})`. The result is a reducer: `reduce(state, event) -> { state, actions }`. The arena feeds it the facts the `KradleEngine` server plugin reports (players, entities, loaded chunks, block changes, containers, chat, deaths, pickups, votes), every server tick, and applies the effects it asks for (give, fill, teleport, titles, votes, the end of the game). The state is plain JSON, and a run replays from its event log.
28
+ `@kradle/challenges-sdk` is a TypeScript SDK for Minecraft challenges that AI agents play on Kradle. A challenge is one root call, `defineChallenge(config, decl)`, plus steps that register the behavior: `.setup(...)`, `.start(...)`, `.phase(name, {...})`, `.on({...})`, `.outcome({...})`. The game server reports the facts (players, entities, loaded chunks, block changes, containers, chat, deaths, pickups, votes) every server tick, and applies the effects the rules ask for (give, fill, teleport, titles, votes, the end of the game). The state is plain JSON, and a run replays from its event log.
29
29
 
30
- There is no datapack and no mcfunction. The rules are TypeScript, the tests run without Minecraft, and `kradle challenge test` runs them inside the same sandbox as the arena.
30
+ There is no datapack and no mcfunction. The rules are TypeScript, the tests run without Minecraft, and `kradle challenge test` runs them against the built bundle, with the limits of a run.
31
31
 
32
- The package's entries: `@kradle/challenges-sdk` (the author API), `@kradle/challenges-sdk/test` (the test kit), `@kradle/challenges-sdk/engine` (the wire protocol, for the arena and tools).
32
+ The package's entries: `@kradle/challenges-sdk` (the author API), `@kradle/challenges-sdk/test` (the test kit), `@kradle/challenges-sdk/engine` (the wire protocol, for tools).
33
33
 
34
34
  ---
35
35
 
@@ -40,12 +40,12 @@ Use [Kradle's CLI](https://github.com/kradle-ai/cli):
40
40
  ```bash
41
41
  kradle init # a project with the SDK installed
42
42
  kradle challenge create my-challenge # challenges/my-challenge/{challenge.ts,config.ts,challenge.test.ts}
43
- kradle challenge test my-challenge # the tests, inside the arena's sandbox
43
+ kradle challenge test my-challenge # the tests, against the built bundle
44
44
  kradle challenge build my-challenge # the bundle, uploaded and activated
45
45
  kradle challenge run my-challenge # a run in the cloud
46
46
  ```
47
47
 
48
- A manual installation is `npm install @kradle/challenges-sdk@next` with `"type": "module"` in `package.json`. Node 22 or later.
48
+ A manual installation is `npm install @kradle/challenges-sdk` with `"type": "module"` in `package.json`. Node 22 or later.
49
49
 
50
50
  A challenge folder holds `challenge.ts` (the rules), `config.ts` (the metadata and the prompts, `export const config`), and `challenge.test.ts`. `kradle ai-docs challenge-config` documents `config.ts`.
51
51
 
@@ -53,7 +53,7 @@ A challenge folder holds `challenge.ts` (the rules), `config.ts` (the metadata a
53
53
 
54
54
  ## Core Concepts
55
55
 
56
- **The reducer.** Every event from the engine goes through `reduce(state, event)`. The reducer returns the next state and a list of actions. It never keeps state outside the returned state, never mutates its input, and never touches the clock, the network or the file system. The arena runs it inside a SES compartment: `Date.now()`, `Math.random()`, timers, `process` and `fetch` do not exist there.
56
+ **The rules.** Every event from the game server goes through the challenge's rules, which return the next state and a list of actions. The rules never keep state outside the returned state, never mutate their input, and never touch the clock, the network or the file system: during a run, `Date.now()`, `Math.random()`, timers, `process` and `fetch` do not exist.
57
57
 
58
58
  **The state.** `g.state` (shared) and `p.state` (per player). Both come from the initializers in the root declaration (`state: { game: (init) => ({...}), player: (p, init) => ({...}) }`). The SDK keeps its own fields beside them: `p.score`, `p.pos`, `p.inventory`, `p.isActive`... The whole state is JSON.
59
59
 
@@ -126,7 +126,7 @@ The steps register behavior and return the same challenge object. Use one chain
126
126
  | `.at([{ at, run }])` | gameplay | One-shot steps after game start, `>=` semantics. |
127
127
  | `.every([{ interval, run }])` | gameplay | Repeating steps; after a gap they run once. |
128
128
  | `.on({...})` | gameplay | Event handlers. Several calls add up, in call order. |
129
- | `.outcome({ scores?, endWhen?, winners, endState? })` | read-only | The result rules. Required. |
129
+ | `.outcome({ scores?, endWhen?, winners, endState?, points?, pointsDescription? })` | read-only | The result rules. Required. |
130
130
  | `.onOver(fn)` | notification: messages and metrics | Once the result is frozen. |
131
131
  | `.judgeContext(fn)` | read-only | An object sent with `game_over` for the LLM judge. |
132
132
 
@@ -136,7 +136,9 @@ The steps register behavior and return the same challenge object. Use one chain
136
136
 
137
137
  **The checkpoint.** After the handlers of every event: the derived scores, the phase's `until`, the phase timeout, the game duration, `outcome.endWhen`. A game end wins over a staged phase change.
138
138
 
139
- **Finalization.** The phase's `exit` (full context), the final scores, `outcome.winners`, the end state (`endGame(endState)` or `outcome.endState`), the frozen result, `onOver`, `judgeContext`, `game_over`.
139
+ **Finalization.** The phase's `exit` (full context), the final scores, `outcome.winners`, the end state (`endGame(endState)` or `outcome.endState`), the frozen result, the points, `onOver`, `judgeContext`, `game_over`.
140
+
141
+ **Points.** `outcome.points(g, player, result)` returns the integer a participant earns; it runs once per player with the frozen result, so `result.isWinner(player)` and the other players' roles are available. Without it a winner earns 1 and anyone else 0. `outcome.pointsDescription` is one line for the menus that offer the challenge ("1 point for a win, plus 10 per extra opponent"), carried by the manifest. Kradle records the points and the achievements (`g.achievement`) per participant; a judged run may override the points.
140
142
 
141
143
  ---
142
144
 
@@ -181,11 +183,11 @@ The steps register behavior and return the same challenge object. Use one chain
181
183
 
182
184
  **Votes.** `g.votes(id)` (voter to option), `g.vote(player, id)`, `g.voteOpen(id)`, `g.tally(id)` (`{ counts, leaders, abstaining }`); `g.openVote(id)` (a new round; throws `vote_open` on an open round), `g.closeVote(id)`, `g.resetVote(id)`. A round is closed until opened. An eliminated player's vote is refused unless `allowEliminated`; the `player` vote (`skills.voteForPlayer`) excludes the voter unless `allowSelf`.
183
185
 
184
- **Messages and metrics.** `g.say(message)`, `g.tell(to, message)`, `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })`, `g.log(message, { player?, data? })`, `g.setMetric(name, value, { player? })` (the last value per name reaches the run result), `g.achievement(player, id)`.
186
+ **Messages and metrics.** `g.say(message)`, `g.tell(to, message)`, `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })`, `g.log(message, { player?, data? })`, `g.setMetric(name, value, { player? })` (the last value per name reaches the run result), `g.achievement(player, id)` (an id is lowercase letters, digits, `_` and `-`; recorded once per player, listed on `game_over` and readable as `p.achievements`).
185
187
 
186
188
  **Scores and roster.** `g.setScore(player, n)`, `g.addScore(player, delta)` (refused when `outcome.scores` exists), `g.eliminate(player)`, `g.random.int(lo, hi)`, `g.random.pick(items)`, `g.random.shuffle(items)`, `g.random.weighted(items)`.
187
189
 
188
- **Effects.** `g.give(player, item, count?)` (`item` is an id or `{ id, count?, name?, lore?, enchantments?, damage?, snbt? }`), `g.clear(player, item?, count?)`, `g.effect(player, { id, duration, amplifier? })`, `g.teleport(player, to)`, `g.kill(player)`, `g.gamemode(player, mode)`, `g.attribute(player, attribute, value)`, `g.fill(from, to, block)`, `g.setBlock(at, block)` (`block` is an id or `{ id, props?, blockEntity? }`), `g.summon(entity, at, { nbt? })`, `g.killEntities(types?)`, `g.time(value)`, `g.gamerule(rule, value)`, `g.setSpawn(at)`, `g.worldBorder({ center?, size?, over? })`, `g.container(at, items)`, `g.command(cmd)` (the escape hatch: gameplay commands only).
190
+ **Effects.** `g.give(player, item, count?)` (`item` is an id or `{ id, count?, name?, lore?, enchantments?, damage?, components? }`; `components` is the raw item argument suffix such as `[enchantments={sharpness:1}]`, and a metadata field wins over the same component), `g.clear(player, item?, count?)`, `g.effect(player, { id, duration, amplifier? })`, `g.teleport(player, to)`, `g.kill(player)`, `g.gamemode(player, mode)`, `g.attribute(player, attribute, value)` (e.g. `minecraft:max_health`; no `generic.` prefix since Minecraft 1.21.2), `g.fill(from, to, block)`, `g.setBlock(at, block)` (`block` is an id or `{ id, props?, blockEntity? }`), `g.summon(entity, at, { nbt? })`, `g.killEntities(types?)`, `g.time(value)`, `g.gamerule(rule, value)` (`rule` is a Minecraft 1.21.11+ id such as `minecraft:keep_inventory`; the old camelCase names are refused), `g.setSpawn(at)`, `g.worldBorder({ center?, size?, over? })`, `g.container(at, items)`, `g.command(cmd)` (the escape hatch: gameplay commands only).
189
191
 
190
192
  **Flow.** `g.startPhase(name)`, `g.endPhase()`, `g.endGame(endState?)`, `g.schedule(timer, after)`, `g.cancel(timer)`, `g.walk(player, to, { fallback? })`, `g.batch(name, fn)` (the effects inside `fn` go out as one named batch; `on.batchDone` reports).
191
193
 
@@ -195,7 +197,7 @@ Durations are `"35s"`, `"2m"`, `"40t"` or `seconds(35)` / `minutes(2)` / `ticks(
195
197
 
196
198
  ## Players and Entities
197
199
 
198
- A player (`g.players[i]`) carries the roster (`name`, `role`, `seatNumber`, `human`), the observations (`connected`, `alive`, `eliminated`, `isActive`, `deaths`, `kills`, `score`, `pos`, `yaw`, `pitch`, `headYaw`, `health`, `food`, `gamemode`, `held`, `slots`, `inventory`, `equipment`, `invisible`, `zones`) and its own `state`. The observations are read-only for a rule; `p.state` is the challenge's; the score changes through `g.setScore` / `g.addScore` or `outcome.scores`. `p.pos` is set from the initializers onward: a disconnected player keeps the last position.
200
+ A player (`g.players[i]`) carries the roster (`name`, `role`, `seatNumber`, `human`), the observations (`connected`, `alive`, `eliminated`, `isActive`, `deaths`, `kills`, `score`, `achievements`, `pos`, `yaw`, `pitch`, `headYaw`, `health`, `food`, `gamemode`, `held`, `slots`, `inventory`, `equipment`, `invisible`, `zones`) and its own `state`. The observations are read-only for a rule; `p.state` is the challenge's; the score changes through `g.setScore` / `g.addScore` or `outcome.scores`. `p.pos` is set from the initializers onward: a disconnected player keeps the last position.
199
201
 
200
202
  `p.role === "seeker"` narrows the player to that role's state. `g.playersWithRole("seeker")` is typed the same way.
201
203
 
@@ -249,7 +251,7 @@ export default challenge;
249
251
 
250
252
  ---
251
253
 
252
- ## Rules of the Reducer
254
+ ## Constraints
253
255
 
254
256
  - Every callback is synchronous. A callback that returns a Promise is an error that ends the run.
255
257
  - A phase request (`g.startPhase`, `g.endPhase`) applies once the event's handler group returned: every global `on` in order, then the phase's. A later handler of the same event still sees the current phase; a second request in the group throws `phase_twice`. `g.endGame` skips the later handlers of the event; the current callback continues.
@@ -260,21 +262,20 @@ export default challenge;
260
262
  - The observations are read-only everywhere: `p.pos.y = 1`, `p.score = 3` or `g.params.x = 1` throws `read_only` (the state is written through `p.state` and `g.state`; the score through `g.setScore` / `g.addScore`).
261
263
  - The pure callbacks (`outcome.*`, `judgeContext`, `until`, a functional `duration` or `next`, a vote's `accept`) read only: a write or an effect does not compile, and throws `read_only` at run time; so does a write in `onOver`.
262
264
  - Derived scores (`outcome.scores`) commit once per reduction, after the handlers and after the transition the checkpoint made; every evaluation reads the previous committed scores, so `p.score + 1` adds one. `until` and `endWhen` read the vector under evaluation; `exit` and `enter` read the committed one.
263
- - A vote request carries the round the arena saw (`vote_round`); a request without a round, or with an earlier one, is refused with feedback. The harness's `Fake.vote` stamps the current round.
265
+ - A vote request carries the round it saw (`vote_round`); a request without a round, or with an earlier one, is refused with feedback. The harness's `Fake.vote` stamps the current round.
264
266
  - The engine confirms the setup batches in order; an acknowledgement that is not for the pending batch is logged and ignored.
265
267
 
266
268
  ---
267
269
 
268
270
  ## Testing
269
271
 
270
- `@kradle/challenges-sdk/test` is a stand-in arena. No test framework dependency: it throws plain errors.
272
+ `@kradle/challenges-sdk/test` is a stand-in for the run. No test framework dependency: it throws plain errors.
271
273
 
272
274
  ```ts
273
275
  import { describe, expect, test } from "bun:test";
274
276
  import { Game, sandboxed, scores } from "@kradle/challenges-sdk/test";
275
277
  import source from "./challenge";
276
278
 
277
- // Under `kradle challenge test`, the built bundle inside the arena's sandbox; the source otherwise.
278
279
  const challenge = await sandboxed(source);
279
280
 
280
281
  test("a kill scores", () => {
@@ -291,13 +292,13 @@ test("a kill scores", () => {
291
292
 
292
293
  `Game.begin(seed, roster, opts)` sends `run_started`, the joins, one tick with every participant's position (a default row unless `at`), and confirms the setup batches. `game.player(name)` is a scripted player: `moveTo`, `says`, `vote(id, option)`, `hurt`, `dies`, `respawns`, `picksUp`, `setInventory`, `enters(zone)`, `exits(zone)`, `uses`, `breaks`, `places`, `joins`, `leaves`. The game feeds the mirror: `terrain`, `setBlocks`, `fillBlocks`, `spawnEntity`, `changeEntity`, `moveEntities`, `removeEntity`, `container`, `sample`, `other`; and answers the engine's side: `walkResult`, `batchResult`, `effectFailed`, `confirmSetup`. Time: `tick(n)`, `after(duration)`, `at(duration)`.
293
294
 
294
- Reads: `game.state` (the shared data), `game.phase`, `game.status`, `game.participant(name)`, `game.reducerState` (everything), `game.of(type)` / `game.all(type)` (actions), `game.effects(kind)` / `game.effectsAll(kind)`, `game.said()`, `game.announcements(to?)`, `game.result()`, `game.logs()`, `game.metrics()`; `scores(g)` and `near(a, b, blocks)` from the same entry. `game.replay()` re-runs the log and compares; `game.checkpoint()` / `Game.resume(challenge, json)` cross a JSON round trip.
295
+ Reads: `game.state` (the shared data), `game.phase`, `game.status`, `game.participant(name)`, `game.participants`, `game.votes`, `game.of(type)` / `game.all(type)` (actions), `game.effects(kind)` / `game.effectsAll(kind)`, `game.said()`, `game.announcements(to?)`, `game.result()`, `game.logs()`, `game.metrics()`; `scores(g)` and `near(a, b, blocks)` from the same entry. `game.replay()` re-runs the log and compares; `game.checkpoint()` / `Game.resume(challenge, json)` cross a JSON round trip.
295
296
 
296
297
  ---
297
298
 
298
299
  ## Build and Run
299
300
 
300
- `kradle challenge build <slug>` type-checks the project, bundles the challenge, loads the bundle in the arena's sandbox, reads its manifest and runs one synthetic start (a definition or API error fails the build; a throw from the challenge's own code that needs the world is a warning), then uploads and activates it. `kradle challenge run <slug>` starts a run in the cloud.
301
+ `kradle challenge build <slug>` type-checks the project, bundles the challenge, loads the bundle the way a run loads it, reads its manifest and runs one synthetic start (a definition or API error fails the build; a throw from the challenge's own code that needs the world is a warning), then uploads and activates it. `kradle challenge run <slug>` starts a run in the cloud.
301
302
 
302
303
  The manifest the bundle exports (`runtime: 2`) carries the name, the life, the roles, the locations, the votes and the params. The backend checks the roles against the challenge's configuration at activation and stores the votes with the artifact.
303
304
 
@@ -439,4 +440,4 @@ export default challenge
439
440
  - Use one chain by default, followed by `export default challenge;`. Split only to resolve a circular type; do not copy a split into every challenge.
440
441
  - Prefer `outcome.scores` over manual score writes: the score is then a function of the state, computed after every event.
441
442
  - Keep `p.state` small and JSON. Copy what you keep from an event (`{ ...e.pos }`).
442
- - Test with `Game` before you build: a test is faster than a run, and `kradle challenge test` runs the same file under the arena's sandbox.
443
+ - Test with `Game` before you build: a test is faster than a run, and `kradle challenge test` runs the same file against the built bundle.
package/MIGRATION-0.13.md CHANGED
@@ -61,7 +61,7 @@ The root declaration holds data and the state initializers: `state`, `life`, `du
61
61
  | `g.contents(name \| at)`, `containers: {...}` in the spec | `g.containerContents(at)` for any container; no declaration |
62
62
  | `g.rand(lo, hi)`, `g.pick`, `g.shuffle` | `g.random.int(lo, hi)`, `g.random.pick`, `g.random.shuffle`, `g.random.weighted`; `init.random` in the initializers |
63
63
  | `g.setblock(at, block, nbt?)` | `g.setBlock(at, block)` with `block` an id or `{ id, props?, blockEntity? }` |
64
- | `g.give(p, item, count, nbt)` | `g.give(p, item, count?)` with `item` an id or `{ id, count?, name?, lore?, enchantments?, damage?, snbt? }` |
64
+ | `g.give(p, item, count, nbt)` | `g.give(p, item, count?)` with `item` an id or `{ id, count?, name?, lore?, enchantments?, damage?, components? }` |
65
65
  | `g.effect(p, id, seconds, amplifier)` | `g.effect(p, { id, duration, amplifier? })` |
66
66
  | `g.title(to, title, subtitle, times)` | `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })` |
67
67
  | `g.log(player \| null, message, data)` | `g.log(message, { player?, data? })` |
@@ -70,7 +70,7 @@ The root declaration holds data and the state initializers: `state`, `life`, `du
70
70
  | `g.findBlocks(box, match, limit)` | `g.findBlocks(box, match, { limit })` |
71
71
  | `g.summon(type, at, nbt)` | `g.summon(type, at, { nbt })` (a string third argument is ignored) |
72
72
  | `g.worldBorder({ seconds: n })` | `g.worldBorder({ over: seconds(n) })` or `{ over: "10s" }` (`seconds` is ignored) |
73
- | `g.container(at, [{ item, count, nbt, slot? }])` | `g.container(at, [{ id, count, snbt }])`: the items fill the slots in order; there is no slot selection |
73
+ | `g.container(at, [{ item, count, nbt, slot? }])` | `g.container(at, [{ id, count, components? }])`: the items fill the slots in order; there is no slot selection |
74
74
  | `p.slots[i].item` | `p.slots[i].id` |
75
75
  | `onOver(g, { winnerPlayers })` | `result.isWinner(p)` over `g.players` |
76
76
  | `g.dt` | removed: keep the last tick in the state and use `g.tick` differences |
@@ -85,7 +85,7 @@ The root declaration holds data and the state initializers: `state`, `life`, `du
85
85
  | `effectFailed { kind, detail }` | `{ kind, detail, effect?, batch? }`; `g.batch(name, fn)` + `on.batchDone` |
86
86
  | `ContextOf<typeof config, typeof state>`, `typeof challenge.ctx` | `CtxOf<typeof challenge>`, `ReadCtxOf`, `SetupCtxOf`, `PlayerIn<G>`; split the root call only to resolve a circular type |
87
87
  | `game.state.status === "LOBBY"` (in a test) | `game.status === "CONNECTING"` (the author context has no `status` field in 0.12; a rule reads `g.status` now) |
88
- | `Game.begin(seed, roster)` without positions | `begin` supplies a position per participant (`at` to choose); `g.state` is the shared data, `g.reducerState` the whole state; `Fake.has` is `setInventory`; `g.all("store")` is `g.all("metric")` |
88
+ | `Game.begin(seed, roster)` without positions | `begin` supplies a position per participant (`at` to choose); `g.state` is the shared data, `g.participants` every player's record; `Fake.has` is `setInventory`; `g.all("store")` is `g.all("metric")` |
89
89
 
90
90
  ## Helpers
91
91
 
@@ -126,3 +126,7 @@ A root predicate (`votes.room.accept`) cannot call a helper typed from the chall
126
126
  Remove the `markers` declaration. Replace `g.markers(tag)` with `g.entities({ tag })` and `marker.at` with `entity.pos`.
127
127
  All non-player entities in loaded chunks are available before setup. Minecraft controls chunk loading; the SDK does not preload a box. Keep entities needed for setup in spawn chunks or near the players' initial positions. Distant entities appear only after Minecraft loads their chunks.
128
128
  In tests, use `game.spawnEntity(id, type, pos, { tags })` before `game.begin(...)` instead of `begin(..., { markers })`.
129
+
130
+ ## The test kit after 0.14.1
131
+
132
+ `game.reducerState` and the `ReducerState` type are gone. Read the players through `game.participants` (every record, by name) or `game.participant(name)`, the votes through `game.votes` (by id), and the whole state, when a test needs it, through `JSON.parse(game.checkpoint())`. `scores(game)` is unchanged.
@@ -0,0 +1,16 @@
1
+ # Migration to the Minecraft 26.1 SDK
2
+
3
+ The SDK builds for Minecraft 26.1 (Paper 26.1.2). A bundle from an older SDK does not run on a 26.1 arena, and a bundle from this SDK does not run on a 1.20.4 arena: the manifest carries the target (`minecraft: "26.1"`) and the arena refuses another version. Rebuild every challenge with this SDK and activate it again.
4
+
5
+ | Before | Now |
6
+ |---|---|
7
+ | `g.give(p, { id, snbt: "{Enchantments:[{id:\"minecraft:sharpness\",lvl:1s}]}" })` | `g.give(p, { id, enchantments: { "minecraft:sharpness": 1 } })` |
8
+ | `snbt` on an item request (SNBT compound) | `components` on an item request: the item argument suffix, e.g. `"[unbreakable={}]"`. A metadata field (`name`, `lore`, `enchantments`, `damage`) wins over the same component. |
9
+ | `g.gamerule("keepInventory", true)` | `g.gamerule("minecraft:keep_inventory", true)` (`GameRuleId`; Minecraft 1.21.11 renamed every rule, e.g. `doDaylightCycle` = `minecraft:advance_time`, `doMobSpawning` = `minecraft:spawn_mobs`, `doTileDrops` = `minecraft:block_drops`, `doMobLoot` = `minecraft:mob_drops`, `doWeatherCycle` = `minecraft:advance_weather`, `mobGriefing` = `minecraft:mob_griefing`, `naturalRegeneration` = `minecraft:natural_health_regeneration`) |
10
+ | `g.attribute(p, "minecraft:generic.max_health", 30)` | `g.attribute(p, "minecraft:max_health", 30)` (no `generic.` prefix since 1.21.2) |
11
+ | Item NBT in `g.command` strings: `give @s stick{display:{Name:'...'}}`, `{Item:{id:"minecraft:diamond",Count:1b}}` | Item components: `give @s stick[custom_name="..."]`, `{Item:{id:"minecraft:diamond",count:1}}` (1.20.5) |
12
+ | `blockEntity: '{Patterns:[{Pattern:"cs",Color:4}]}'` on a banner | `blockEntity: '{patterns:[{pattern:"minecraft:stripe_center",color:"yellow"}]}'` (1.20.5) |
13
+ | Sign text as JSON strings: `messages:['{"text":"go"}']` | Sign text as text components: `messages:[{text:"go"}]` or `messages:["go"]` (1.21.5: a JSON string shows literally) |
14
+ | `clickEvent:{action:"run_command",value:"/x"}` in a `tellraw` | `click_event:{action:"run_command",command:"/x"}` (1.21.5) |
15
+
16
+ The world defaults (`daylightCycle`, `mobSpawning`, `naturalRegeneration`, `maxHealth`) need no change: the SDK emits the new ids.
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  # @kradle/challenges-sdk
4
4
 
5
- Write a Minecraft challenge for Kradle as a reducer: a pure TypeScript function
6
- that reads the world and the players, updates a JSON state, and asks the server
7
- for effects. The arena runs it during the game inside a sandbox; the
8
- `KradleEngine` server plugin reports the facts and applies the effects.
5
+ Write a Minecraft challenge for Kradle as a set of pure rules: TypeScript
6
+ functions that read the world and the players, update a JSON state, and ask
7
+ the game server for effects. The server reports the facts and applies the
8
+ effects; the rules run with no clock, no `Math.random()` and no network.
9
9
 
10
10
  ## Getting started
11
11
 
@@ -15,13 +15,12 @@ the CLI's README first.
15
15
  ```bash
16
16
  kradle init
17
17
  kradle challenge create my-challenge # challenges/my-challenge/{challenge.ts,config.ts,challenge.test.ts}
18
- kradle challenge test my-challenge # the tests, inside the arena's sandbox
18
+ kradle challenge test my-challenge # the tests, against the built bundle
19
19
  kradle challenge build my-challenge # the bundle, uploaded and activated
20
20
  kradle challenge run my-challenge # a run in the cloud
21
21
  ```
22
22
 
23
- A manual installation is one dependency: `npm install @kradle/challenges-sdk@next`
24
- (the reducer SDK stays under the npm tag `next` until the CLI release installs it).
23
+ A manual installation is one dependency: `npm install @kradle/challenges-sdk`.
25
24
  Add `"type": "module"` to the project's `package.json`.
26
25
 
27
26
  ## Local development
@@ -92,6 +91,13 @@ const challenge = defineChallenge(config, {
92
91
  export default challenge;
93
92
  ```
94
93
 
94
+ The outcome can also price the game: `points: (g, p, result) => number` is
95
+ what a participant earns (an integer, evaluated at the end with the frozen
96
+ result; without it a winner earns 1 and anyone else 0), and
97
+ `pointsDescription` is the one line the menus show for it. `g.achievement(player, id)`
98
+ records an achievement once per player (`p.achievements`); Kradle records
99
+ both with the run.
100
+
95
101
  Helpers can use `CtxOf<typeof challenge>` for their context type.
96
102
  Split the root call from the steps only when TypeScript requires it to resolve
97
103
  a circular type. This can occur when a step receives a helper typed from the
@@ -144,15 +150,15 @@ export const pillarDone = (g: StructureReads, at: { x: number; y: number; z: num
144
150
 
145
151
  ## Testing
146
152
 
147
- `@kradle/challenges-sdk/test` is a stand-in arena. A test feeds events, reads
148
- the actions, and checks the state; `replay()` proves determinism.
153
+ `@kradle/challenges-sdk/test` is a stand-in for the run. A test feeds events,
154
+ reads the actions, and checks the state; `replay()` proves determinism.
149
155
 
150
156
  ```ts
151
157
  import { describe, expect, test } from "bun:test";
152
158
  import { Game, sandboxed, scores } from "@kradle/challenges-sdk/test";
153
159
  import source from "./challenge";
154
160
 
155
- const challenge = await sandboxed(source); // the bundle under `kradle challenge test`, the source otherwise
161
+ const challenge = await sandboxed(source);
156
162
 
157
163
  test("a tag scores", () => {
158
164
  const g = new Game(challenge).begin(1, { Alice: "seeker", Bob: "hider" });
@@ -169,16 +175,14 @@ joins and confirms the setup batches. `g.player(name)` scripts a player
169
175
  (`moveTo`, `says`, `vote`, `hurt`, `dies`, `setInventory`, `places`, ...); the
170
176
  game feeds the mirror (`terrain`, `setBlocks`, `spawnEntity`, `container`) and
171
177
  answers the engine's side (`walkResult`, `batchResult`). `kradle challenge test
172
- <slug>` runs the same file against the built bundle under the arena's sandbox.
178
+ <slug>` runs the same file against the built bundle, with the limits of a run.
173
179
 
174
180
  ## Build and run
175
181
 
176
182
  `kradle challenge build <slug>` bundles `challenge.ts` into one file, loads it
177
- once inside the sandbox, uploads it and activates it with the challenge's
178
- configuration. The server runs the `KradleEngine` plugin; the arena connects
179
- the reducer to it. The run's event log is uploaded as the `runtime` recording.
180
-
181
- Reference challenges: `src/runtime/examples/` (Climb, Capture the Flag, Four
182
- Bridges, Skywars, Sheep Herding). `src/runtime/type-checks.ts` shows what the
183
- types catch. The engine contract (`Event`, `Effect`, `Action`) is in
184
- `src/runtime/sdk.ts`; the server side is `backend/engine-plugin`.
183
+ once the way a run loads it, uploads it and activates it with the challenge's
184
+ configuration. The run's event log is uploaded as the `runtime` recording.
185
+
186
+ The wire contract (`Event`, `Effect`, `Action`) is the
187
+ `@kradle/challenges-sdk/engine` entry; `test/types/` shows what the types
188
+ catch.
package/dist/engine.d.ts CHANGED
@@ -3,6 +3,6 @@ export { ChallengeError, type ErrorCategory, categoryOf } from "./runtime/errors
3
3
  export { MAX_BATCH_EFFECTS, MAX_FILL_VOLUME, type State, assertJson, parseBlockState, splitBatches } from "./runtime/reducer.js";
4
4
  export { type Duration, type Ticks, toTicks } from "./runtime/time.js";
5
5
  export type { Action, BlockId, Box, Capability, ContainerItem, Effect, EffectId, EndReason, EngineChallenge, EntityTypeId, Event, GameMode, ItemId, ItemMeta, ItemStack, Json, ManifestVote, Participant, PlayerSample, RuntimeManifest, SdkEndState, SlotChange, Status, TitleTimes, Vec3, } from "./runtime/wire.js";
6
- export { ALL_CAPABILITIES } from "./runtime/wire.js";
6
+ export { ALL_CAPABILITIES, MINECRAFT_TARGET } from "./runtime/wire.js";
7
7
  export type { ChunkMessage, Entity, EntityMove, Equipment, EquipmentChange, EquipmentSlot, Equipped, WorldState } from "./runtime/world.js";
8
8
  //# sourceMappingURL=engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjI,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EACX,MAAM,EACN,OAAO,EACP,GAAG,EACH,UAAU,EACV,aAAa,EACb,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,MAAM,EACN,UAAU,EACV,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjI,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EACX,MAAM,EACN,OAAO,EACP,GAAG,EACH,UAAU,EACV,aAAa,EACb,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,MAAM,EACN,UAAU,EACV,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/engine.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ALL_CAPABILITIES = exports.toTicks = exports.splitBatches = exports.parseBlockState = exports.assertJson = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.categoryOf = exports.ChallengeError = void 0;
3
+ exports.MINECRAFT_TARGET = exports.ALL_CAPABILITIES = exports.toTicks = exports.splitBatches = exports.parseBlockState = exports.assertJson = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.categoryOf = exports.ChallengeError = void 0;
4
4
  var errors_js_1 = require("./runtime/errors.js");
5
5
  Object.defineProperty(exports, "ChallengeError", { enumerable: true, get: function () { return errors_js_1.ChallengeError; } });
6
6
  Object.defineProperty(exports, "categoryOf", { enumerable: true, get: function () { return errors_js_1.categoryOf; } });
@@ -14,4 +14,5 @@ var time_js_1 = require("./runtime/time.js");
14
14
  Object.defineProperty(exports, "toTicks", { enumerable: true, get: function () { return time_js_1.toTicks; } });
15
15
  var wire_js_1 = require("./runtime/wire.js");
16
16
  Object.defineProperty(exports, "ALL_CAPABILITIES", { enumerable: true, get: function () { return wire_js_1.ALL_CAPABILITIES; } });
17
+ Object.defineProperty(exports, "MINECRAFT_TARGET", { enumerable: true, get: function () { return wire_js_1.MINECRAFT_TARGET; } });
17
18
  //# sourceMappingURL=engine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":";;;AAGA,iDAAqF;AAA5E,2GAAA,cAAc,OAAA;AAAsB,uGAAA,UAAU,OAAA;AACvD,mDAAiI;AAAxH,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAc,wGAAA,UAAU,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,0GAAA,YAAY,OAAA;AAClG,6CAAuE;AAAnC,kGAAA,OAAO,OAAA;AA4B3C,6CAAqD;AAA5C,2GAAA,gBAAgB,OAAA"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":";;;AAGA,iDAAqF;AAA5E,2GAAA,cAAc,OAAA;AAAsB,uGAAA,UAAU,OAAA;AACvD,mDAAiI;AAAxH,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAc,wGAAA,UAAU,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,0GAAA,YAAY,OAAA;AAClG,6CAAuE;AAAnC,kGAAA,OAAO,OAAA;AA4B3C,6CAAuE;AAA9D,2GAAA,gBAAgB,OAAA;AAAE,2GAAA,gBAAgB,OAAA"}
package/dist/index.d.ts CHANGED
@@ -3,8 +3,9 @@ export { ChallengeError, type ErrorCategory } from "./runtime/errors.js";
3
3
  export { MAX_BATCH_EFFECTS, MAX_FILL_VOLUME, MAX_TRANSITIONS_PER_EVENT } from "./runtime/reducer.js";
4
4
  export { winBy } from "./runtime/rules.js";
5
5
  export { addDuration, asMinutes, asSeconds, type Duration, minutes, seconds, subtractDuration, TICKS_PER_SECOND, type Ticks, ticks, toTicks, } from "./runtime/time.js";
6
- export type { BlockMatch, BlockPos, BlockSpec, BlockState, ChallengeApi, ChallengeConfig, Ctx, CtxOf, Decl, DeepReadonly, EndStateOf, Handlers, Init, ItemMatch, ItemRequest, LocationOf, NotifyCtx, NotifyCtxOf, ObservedPlayer, OptionFor, Outcome, ParamSpec, ParamsOf, PhaseSpec, PhaseStep, Player, PlayerIn, PlayerOf, Random, ReadCtx, ReadCtxOf, ReadPlayer, ReadPlayerOf, Result, RoleOf, Roster, SetupCtx, SetupCtxOf, StateDecl, TitleOptions, To, VoteIdOf, VoteRule, World, } from "./runtime/types.js";
6
+ export type { BlockMatch, BlockPos, BlockSpec, BlockState, ChallengeApi, ChallengeConfig, Ctx, CtxOf, Decl, DeepReadonly, EndStateOf, GameRuleId, Handlers, Init, ItemMatch, ItemRequest, LocationOf, NotifyCtx, NotifyCtxOf, ObservedPlayer, OptionFor, Outcome, ParamSpec, ParamsOf, PhaseSpec, PhaseStep, Player, PlayerIn, PlayerOf, Random, ReadCtx, ReadCtxOf, ReadPlayer, ReadPlayerOf, Result, RoleOf, Roster, SetupCtx, SetupCtxOf, StateDecl, TitleOptions, To, VoteIdOf, VoteRule, World, } from "./runtime/types.js";
7
7
  export { PLAYER_VOTE, WORLD_DEFAULTS } from "./runtime/types.js";
8
+ export { MINECRAFT_TARGET } from "./runtime/wire.js";
8
9
  export type { BlockId, Box, EffectId, EndReason, EntityTypeId, GameMode, ItemId, ItemMeta, ItemStack, Json, SdkEndState, Status, Vec3, } from "./runtime/wire.js";
9
10
  export { blockIdOf, bounds, type Entity, type EntityFilter, type Equipment, inBox } from "./runtime/world.js";
10
11
  import type { BlockPos } from "./runtime/types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACN,WAAW,EACX,SAAS,EACT,SAAS,EACT,KAAK,QAAQ,EACb,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,KAAK,EACV,KAAK,EACL,OAAO,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,EACZ,eAAe,EACf,GAAG,EACH,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,YAAY,EACZ,MAAM,EACN,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,EAAE,EACF,QAAQ,EACR,QAAQ,EACR,KAAK,GACL,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EACX,OAAO,EACP,GAAG,EACH,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,WAAW,EACX,MAAM,EACN,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,8EAA8E;AAC9E,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,KAAG,QAA4E,CAAC;AAClH,iGAAiG;AACjG,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,wCAAwC;AACxC,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,sCAAsC;AACtC,eAAO,MAAM,MAAM,GAAI,IAAI,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAA8E,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACN,WAAW,EACX,SAAS,EACT,SAAS,EACT,KAAK,QAAQ,EACb,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,KAAK,EACV,KAAK,EACL,OAAO,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,EACZ,eAAe,EACf,GAAG,EACH,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,YAAY,EACZ,MAAM,EACN,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,EAAE,EACF,QAAQ,EACR,QAAQ,EACR,KAAK,GACL,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EACX,OAAO,EACP,GAAG,EACH,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,WAAW,EACX,MAAM,EACN,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,8EAA8E;AAC9E,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,KAAG,QAA4E,CAAC;AAClH,iGAAiG;AACjG,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,wCAAwC;AACxC,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,sCAAsC;AACtC,eAAO,MAAM,MAAM,GAAI,IAAI,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAA8E,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.offset = exports.above = exports.below = exports.toBlockPos = exports.inBox = exports.bounds = exports.blockIdOf = exports.WORLD_DEFAULTS = exports.PLAYER_VOTE = exports.toTicks = exports.ticks = exports.TICKS_PER_SECOND = exports.subtractDuration = exports.seconds = exports.minutes = exports.asSeconds = exports.asMinutes = exports.addDuration = exports.winBy = exports.MAX_TRANSITIONS_PER_EVENT = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.ChallengeError = exports.defineChallenge = void 0;
3
+ exports.offset = exports.above = exports.below = exports.toBlockPos = exports.inBox = exports.bounds = exports.blockIdOf = exports.MINECRAFT_TARGET = exports.WORLD_DEFAULTS = exports.PLAYER_VOTE = exports.toTicks = exports.ticks = exports.TICKS_PER_SECOND = exports.subtractDuration = exports.seconds = exports.minutes = exports.asSeconds = exports.asMinutes = exports.addDuration = exports.winBy = exports.MAX_TRANSITIONS_PER_EVENT = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.ChallengeError = exports.defineChallenge = void 0;
4
4
  // The author API: `@kradle/challenges-sdk`. The engine protocol is `@kradle/challenges-sdk/engine`,
5
5
  // the test kit `@kradle/challenges-sdk/test`.
6
6
  var builder_js_1 = require("./runtime/builder.js");
@@ -26,6 +26,8 @@ Object.defineProperty(exports, "toTicks", { enumerable: true, get: function () {
26
26
  var types_js_1 = require("./runtime/types.js");
27
27
  Object.defineProperty(exports, "PLAYER_VOTE", { enumerable: true, get: function () { return types_js_1.PLAYER_VOTE; } });
28
28
  Object.defineProperty(exports, "WORLD_DEFAULTS", { enumerable: true, get: function () { return types_js_1.WORLD_DEFAULTS; } });
29
+ var wire_js_1 = require("./runtime/wire.js");
30
+ Object.defineProperty(exports, "MINECRAFT_TARGET", { enumerable: true, get: function () { return wire_js_1.MINECRAFT_TARGET; } });
29
31
  var world_js_1 = require("./runtime/world.js");
30
32
  Object.defineProperty(exports, "blockIdOf", { enumerable: true, get: function () { return world_js_1.blockIdOf; } });
31
33
  Object.defineProperty(exports, "bounds", { enumerable: true, get: function () { return world_js_1.bounds; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oGAAoG;AACpG,8CAA8C;AAC9C,mDAA0F;AAA9C,6GAAA,eAAe,OAAA;AAC3D,iDAAyE;AAAhE,2GAAA,cAAc,OAAA;AACvB,mDAAqG;AAA5F,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,uHAAA,yBAAyB,OAAA;AACtE,+CAA2C;AAAlC,iGAAA,KAAK,OAAA;AACd,6CAY2B;AAX1B,sGAAA,WAAW,OAAA;AACX,oGAAA,SAAS,OAAA;AACT,oGAAA,SAAS,OAAA;AAET,kGAAA,OAAO,OAAA;AACP,kGAAA,OAAO,OAAA;AACP,2GAAA,gBAAgB,OAAA;AAChB,2GAAA,gBAAgB,OAAA;AAEhB,gGAAA,KAAK,OAAA;AACL,kGAAA,OAAO,OAAA;AAgDR,+CAAiE;AAAxD,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AAgBpC,+CAA8G;AAArG,qGAAA,SAAS,OAAA;AAAE,kGAAA,MAAM,OAAA;AAAkD,iGAAA,KAAK,OAAA;AAIjF,8EAA8E;AACvE,MAAM,UAAU,GAAG,CAAC,CAAO,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAArG,QAAA,UAAU,cAA2F;AAClH,iGAAiG;AAC1F,MAAM,KAAK,GAAG,CAAC,EAAQ,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAAxG,QAAA,KAAK,SAAmG;AACrH,wCAAwC;AACjC,MAAM,KAAK,GAAG,CAAC,EAAQ,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAAxG,QAAA,KAAK,SAAmG;AACrH,sCAAsC;AAC/B,MAAM,MAAM,GAAG,CAAC,EAAQ,EAAE,CAAgB,EAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAAtH,QAAA,MAAM,UAAgH"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oGAAoG;AACpG,8CAA8C;AAC9C,mDAA0F;AAA9C,6GAAA,eAAe,OAAA;AAC3D,iDAAyE;AAAhE,2GAAA,cAAc,OAAA;AACvB,mDAAqG;AAA5F,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,uHAAA,yBAAyB,OAAA;AACtE,+CAA2C;AAAlC,iGAAA,KAAK,OAAA;AACd,6CAY2B;AAX1B,sGAAA,WAAW,OAAA;AACX,oGAAA,SAAS,OAAA;AACT,oGAAA,SAAS,OAAA;AAET,kGAAA,OAAO,OAAA;AACP,kGAAA,OAAO,OAAA;AACP,2GAAA,gBAAgB,OAAA;AAChB,2GAAA,gBAAgB,OAAA;AAEhB,gGAAA,KAAK,OAAA;AACL,kGAAA,OAAO,OAAA;AAiDR,+CAAiE;AAAxD,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AACpC,6CAAqD;AAA5C,2GAAA,gBAAgB,OAAA;AAgBzB,+CAA8G;AAArG,qGAAA,SAAS,OAAA;AAAE,kGAAA,MAAM,OAAA;AAAkD,iGAAA,KAAK,OAAA;AAIjF,8EAA8E;AACvE,MAAM,UAAU,GAAG,CAAC,CAAO,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAArG,QAAA,UAAU,cAA2F;AAClH,iGAAiG;AAC1F,MAAM,KAAK,GAAG,CAAC,EAAQ,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAAxG,QAAA,KAAK,SAAmG;AACrH,wCAAwC;AACjC,MAAM,KAAK,GAAG,CAAC,EAAQ,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAAxG,QAAA,KAAK,SAAmG;AACrH,sCAAsC;AAC/B,MAAM,MAAM,GAAG,CAAC,EAAQ,EAAE,CAAgB,EAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAAtH,QAAA,MAAM,UAAgH"}
@@ -1,9 +1,9 @@
1
1
  import { type State } from "./reducer.js";
2
2
  import type { ChallengeApi, ChallengeConfig, Decl, Handlers, Outcome, PhaseSpec, PlayerMap, ResolvePlayers, VotesDecl } from "./types.js";
3
3
  import type { Action, Event, RuntimeManifest } from "./wire.js";
4
- /** A challenge as the arena and the harness use it: the API plus `init`, `reduce` and the manifest. */
4
+ /** A challenge as a run and the harness use it: the API plus `init`, `reduce` and the manifest. */
5
5
  export interface Challenge<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> extends ChallengeApi<C, G, PM, Ph, Z, T, V> {
6
- /** The manifest the arena and the backend read. Reading it locks the definition. */
6
+ /** The manifest Kradle reads before a run. Reading it locks the definition. */
7
7
  readonly manifest: RuntimeManifest;
8
8
  /** True once the definition is locked (`init()` or `manifest` was used). */
9
9
  readonly locked: boolean;
@@ -13,7 +13,7 @@ export interface Challenge<C extends ChallengeConfig, G, PM extends PlayerMap<C>
13
13
  reduce(state: State, event: Event): {
14
14
  /** The next state, frozen; the input is never changed. */
15
15
  state: State;
16
- /** What the engine and the arena apply, in order. */
16
+ /** What the game server applies, in order. */
17
17
  actions: Action[];
18
18
  };
19
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/runtime/builder.ts"],"names":[],"mappings":"AAYA,OAAO,EAAwF,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAEhI,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EAET,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,MAAM,EAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAIrE,uGAAuG;AACvG,MAAM,WAAW,SAAS,CACzB,CAAC,SAAS,eAAe,EACzB,CAAC,EACD,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EACvB,EAAE,SAAS,MAAM,EACjB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,SAAS,CAClB,SAAQ,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5C,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,iEAAiE;IACjE,IAAI,IAAI,KAAK,CAAC;IACd,2FAA2F;IAC3F,MAAM,CACL,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,GACV;QACF,0DAA0D;QAC1D,KAAK,EAAE,KAAK,CAAC;QACb,qDAAqD;QACrD,OAAO,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;AAyH9I;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,KAAK,CAAC,CAAC,SAAS,eAAe,EAC/B,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACrE,KAAK,CAAC,EAAE,SAAS,MAAM,GAAG,KAAK,EAC/B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAC9B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAC9B,KAAK,CAAC,CAAC,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC/C,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAiMzG;AAED,KAAK,OAAO,CAAC,CAAC,SAAS,eAAe,IAAI,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;AAGpE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/runtime/builder.ts"],"names":[],"mappings":"AAYA,OAAO,EAAwF,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAEhI,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EAET,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,MAAM,EAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAKrE,mGAAmG;AACnG,MAAM,WAAW,SAAS,CACzB,CAAC,SAAS,eAAe,EACzB,CAAC,EACD,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EACvB,EAAE,SAAS,MAAM,EACjB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,SAAS,CAClB,SAAQ,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5C,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,iEAAiE;IACjE,IAAI,IAAI,KAAK,CAAC;IACd,2FAA2F;IAC3F,MAAM,CACL,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,GACV;QACF,0DAA0D;QAC1D,KAAK,EAAE,KAAK,CAAC;QACb,8CAA8C;QAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;AA4H9I;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,KAAK,CAAC,CAAC,SAAS,eAAe,EAC/B,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACrE,KAAK,CAAC,EAAE,SAAS,MAAM,GAAG,KAAK,EAC/B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAC9B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAC9B,KAAK,CAAC,CAAC,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC/C,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAmNzG;AAED,KAAK,OAAO,CAAC,CAAC,SAAS,eAAe,IAAI,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;AAGpE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -15,6 +15,7 @@ const errors_js_1 = require("./errors.js");
15
15
  const reducer_js_1 = require("./reducer.js");
16
16
  const time_js_1 = require("./time.js");
17
17
  const types_js_1 = require("./types.js");
18
+ const wire_js_1 = require("./wire.js");
18
19
  const TAG_SAFE = /^[a-z0-9_.-]+$/;
19
20
  /** Deep copy of plain data (objects, arrays, primitives); functions stay by reference. */
20
21
  function copyData(value) {
@@ -143,6 +144,8 @@ function validatePhaseSpec(name, spec, phases) {
143
144
  });
144
145
  (spec.every ?? []).forEach((ev, i) => checkDuration(ev.interval, `phases.${name}.every.${i}.interval`));
145
146
  }
147
+ /** The cap on `outcome.pointsDescription`: one line for a menu. */
148
+ const POINTS_DESCRIPTION_MAX = 200;
146
149
  /**
147
150
  * Defines a challenge. The root declaration carries the data and the state initializers; the
148
151
  * steps carry the behavior. `init()` or the first `manifest` read locks the definition.
@@ -207,6 +210,7 @@ function defineChallenge(config, decl) {
207
210
  const declaredLocations = Object.keys(ownConfig.challengeConfig?.locations ?? {});
208
211
  const manifest = {
209
212
  runtime: 2,
213
+ minecraft: wire_js_1.MINECRAFT_TARGET,
210
214
  name: ownConfig.name,
211
215
  life,
212
216
  roles: Object.keys(ownConfig.roles),
@@ -228,6 +232,8 @@ function defineChallenge(config, decl) {
228
232
  }
229
233
  if (declaredLocations.length > 0)
230
234
  manifest.locations = declaredLocations;
235
+ if (outcome.pointsDescription !== undefined)
236
+ manifest.pointsDescription = outcome.pointsDescription.trim();
231
237
  locked = {
232
238
  config: ownConfig,
233
239
  state: ownState,
@@ -319,6 +325,18 @@ function defineChallenge(config, decl) {
319
325
  assertOpen("outcome");
320
326
  if (outcome)
321
327
  duplicates.push("outcome");
328
+ if (o.points !== undefined && typeof o.points !== "function") {
329
+ throw (0, errors_js_1.definitionError)("bad_outcome", "outcome.points must be a function (g, player, result) => integer", {
330
+ path: "outcome.points",
331
+ value: o.points,
332
+ });
333
+ }
334
+ if (o.pointsDescription !== undefined) {
335
+ const d = o.pointsDescription;
336
+ if (typeof d !== "string" || d.trim().length === 0 || d.length > POINTS_DESCRIPTION_MAX) {
337
+ throw (0, errors_js_1.definitionError)("bad_outcome", `outcome.pointsDescription must be a non-empty string of at most ${POINTS_DESCRIPTION_MAX} characters`, { path: "outcome.pointsDescription", value: d });
338
+ }
339
+ }
322
340
  outcome = { ...o };
323
341
  return challenge;
324
342
  },