@kradle/challenges 0.1.0 → 0.2.1

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/AGENTS.md ADDED
@@ -0,0 +1,283 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to LLMs when working with code in this repository.
4
+
5
+ ## ⚠️ Mandatory Documentation Updates
6
+
7
+ **ALL AGENTS MUST FOLLOW THESE RULES:**
8
+
9
+ 1. **After making any code changes**, verify that AGENTS.md and README.md accurately reflect your changes. Update them if they don't.
10
+
11
+ 2. **When adding new features**, add documentation for them in both files:
12
+ - AGENTS.md: Add to the appropriate section with implementation details - it will be used by future agents for improving this repository
13
+ - README.md: Add user-facing documentation with usage examples (more like a guide) - this is for humans, so it must be clear, concise, and follow README best practices
14
+ - LLM_README.md: Add LLM-facing documentation with exhaustive detailed usage and examples - it will be used by user agents that will leverage the API, so it should not contain implementation details
15
+
16
+ 3. **When modifying existing features**, update their documentation if the behavior changes (or anything else).
17
+
18
+ 4. **When adding new libraries or modules**, document them in the Architecture Overview section of AGENTS.md.
19
+
20
+ 5. **When changing project structure** (new folders), update the Project Structure section in README.md. It must only surface the folders, the files is too detailed.
21
+
22
+ 6. **Before completing a task**, run a quick check:
23
+ - Do AGENTS.md, README.md and FEATURES.md reflect the current state of the code?
24
+ - Are all features documented?
25
+ - Is the project structure accurate?
26
+
27
+ Failure to keep documentation up-to-date creates confusion for future agents and developers. This is a **mandatory** part of every code change.
28
+
29
+ ---
30
+
31
+ ## Architecture Overview
32
+
33
+ ### Project Structure
34
+
35
+ ```
36
+ /src
37
+ ├── index.ts # Public API exports
38
+ ├── challenge.ts # Core ChallengeBase class and createChallenge()
39
+ ├── actions.ts # Actions library (give, teleport, announce, etc.)
40
+ ├── types.ts # TypeScript type definitions
41
+ ├── constants.ts # Shared constants (objectives, selectors, built-in vars)
42
+ ├── commands.ts # Watcher integration commands
43
+ ├── api_utils.ts # Utility functions (forEveryPlayer)
44
+ ├── testmode.ts # Test/debug mode support
45
+ └── utils.ts # Internal utilities
46
+ ```
47
+
48
+ ### Key Dependencies
49
+
50
+ - **Sandstone (0.14.0-alpha.13)**: Minecraft datapack generator. Provides MCFunction, Objective, Selector, execute commands, etc. All generated code compiles to Minecraft datapacks via Sandstone's `savePack()`.
51
+
52
+ ### Core Components
53
+
54
+ #### 1. ChallengeBase Class (`challenge.ts`)
55
+
56
+ The main engine that:
57
+ - Manages variable creation and objective assignment
58
+ - Generates MCFunctions for lifecycle events (start, init, tick, end)
59
+ - Processes custom events (score-based and advancement-based)
60
+ - Handles win condition evaluation and game state management
61
+
62
+ **Key Methods:**
63
+ - `constructor(config)`: Sets up objectives, variables, and built-in score events
64
+ - `events()`: Registers lifecycle event callbacks
65
+ - `custom_events()`: Registers triggered events
66
+ - `end_condition()`: Sets game termination condition
67
+ - `win_conditions()`: Sets per-role win conditions and triggers `build()`
68
+ - `build()`: Generates all MCFunctions and calls `savePack()`
69
+
70
+ **Generated MCFunctions:**
71
+ 1. `start_challenge` - Initial setup, tags players, sets game state (runs globally)
72
+ 2. `init_participants` - Player setup (runs globally, delayed 1 second via schedule)
73
+ 3. `on_tick` - Main loop: updates variables, processes events, checks end condition (runs globally every tick)
74
+ 4. `end_challenge` - Announces winners, saves game state (runs globally)
75
+
76
+ #### 2. Actions Library (`actions.ts`)
77
+
78
+ Pre-built game operations. Each action is a function that generates Sandstone commands.
79
+
80
+ **Categories:**
81
+ - Communication: `announce`, `tellraw`
82
+ - Items: `give`, `giveLoot`, `clear`, `countItems`, `summonItem`
83
+ - Entities: `summonMultiple`, `kill`, `teleport`
84
+ - World: `setBlock`, `fill`, `setTime`, `gamerule`
85
+ - Scores: `set`, `increment`, `decrement`
86
+ - Attributes: `setAttribute`
87
+ - Custom: `custom` (arbitrary Sandstone code)
88
+ - Logging: `log_variable`
89
+
90
+ **Implementation Pattern:**
91
+ ```typescript
92
+ export const Actions = {
93
+ give: ({ item, target, count = 1 }: { item: ITEMS; target: TargetNames; count?: number }) => {
94
+ give(mapTarget(target), item, count);
95
+ },
96
+ // ...
97
+ };
98
+ ```
99
+
100
+ **Target Mapping:**
101
+ The `mapTarget()` function converts `TargetNames` to selectors:
102
+ - `"all"` → `@a[tag=kradle_participant]`
103
+ - `"self"` → `"@s"`
104
+ - `SelectorClass` → passed through
105
+ - `"minecraft:entity_type"` → `@e[type=minecraft:entity_type]`
106
+ - Other strings → `@a[tag=<string>]` (treated as team names)
107
+
108
+ #### 3. Variable System (`types.ts`, `challenge.ts`)
109
+
110
+ **Variable Types:**
111
+ - `individual` + specific `objective_type`: Uses Minecraft's built-in stat tracking
112
+ - `individual` + `"dummy"`: Manual/computed values per player
113
+ - `global`: Shared values stored on a constant entity
114
+
115
+ **Objective Naming:**
116
+ - Uses counter-based suffixes to ensure unique names under 16 chars
117
+ - Format: `kradle.${hash}` where hash is derived from challenge name + counter
118
+
119
+ **Updater Execution:**
120
+ - All updaters run every tick in `on_tick` MCFunction
121
+ - Updaters execute as/at each participant for individual variables
122
+ - Global updaters run once per tick
123
+
124
+ #### 4. Event System (`challenge.ts`)
125
+
126
+ **Score Events:**
127
+ - Track previous tick values to detect changes
128
+ - Fire when score reaches target (fire_once) or while condition holds (repeatable)
129
+ - For individual variables: events fire per-player
130
+ - For global variables: events fire globally
131
+
132
+ **Advancement Events:**
133
+ - Always fire per-player (advancement triggers are inherently per-player)
134
+ - Converted to score events internally
135
+ - Advancement grants → score increment → event fires
136
+ - Advancement revoked after triggering
137
+
138
+ #### 5. Watcher Integration (`commands.ts`)
139
+
140
+ Communicates with external "Kradle Watcher" system via JSON tellraw commands:
141
+ ```json
142
+ {
143
+ "type": "kradle_command",
144
+ "command": "game_over",
145
+ "arguments": { "winners": [...], "end_state": "..." }
146
+ }
147
+ ```
148
+
149
+ ### Constants (`constants.ts`)
150
+
151
+ **Objectives:**
152
+ - `VISIBLE_OBJECTIVE` = "kradle.board" (sidebar display)
153
+ - `HIDDEN_OBJECTIVE` = hidden scoreboard for internal data
154
+
155
+ **Entity Selectors:**
156
+ - `ALL` = `@a[tag=kradle_participant]`
157
+ - `KRADLE_PARTICIPANT_TAG` = "kradle_participant"
158
+ - `WINNER_TAG` = "kradle_winner"
159
+
160
+ **Built-in Variables (8):**
161
+ 1. `death_count` - Minecraft deathCount objective
162
+ 2. `has_never_died` - 1 if alive, 0 if died
163
+ 3. `alive_players` - Global count
164
+ 4. `main_score` - Primary display score
165
+ 5. `game_timer` - Tick counter
166
+ 6. `game_state` - State enum (CREATED=0, OFF=1, ON=2)
167
+ 7. `player_count` - Total players
168
+ 8. `player_number` - Unique ID (1-N)
169
+
170
+ ### Build Process
171
+
172
+ 1. User calls `createChallenge(config)` → returns builder
173
+ 2. Chain: `.events()` → `.custom_events()` → `.end_condition()` → `.win_conditions()`
174
+ 3. `.win_conditions()` triggers `build()`:
175
+ - Creates all MCFunctions
176
+ - Generates advancements for advancement-based events
177
+ - Creates loot tables (hashed for deduplication)
178
+ - Calls `savePack()` to write datapack files
179
+
180
+ ---
181
+
182
+ ## Development Guidelines
183
+
184
+ ### Adding a New Action
185
+
186
+ 1. Add to `Actions` object in `src/actions.ts`
187
+ 2. Define parameter interface with required/optional fields
188
+ 3. Implement using Sandstone commands
189
+ 4. Update LLM_README.md with full signature and examples
190
+ 5. Update README.md with brief usage example
191
+
192
+ ### Adding a New Built-in Variable
193
+
194
+ 1. Add to `BUILTIN_VARIABLES` in `src/constants.ts`
195
+ 2. Define type, objective_type, and updater
196
+ 3. Update LLM_README.md built-in variables table
197
+ 4. Update README.md built-in variables table
198
+
199
+ ### Adding a New Event Type
200
+
201
+ 1. Update `_InputCustomEventType` in `src/types.ts`
202
+ 2. Handle in `processCustomEvents()` in `src/challenge.ts`
203
+ 3. Document in LLM_README.md Events section
204
+ 4. Add example to README.md
205
+
206
+ ### Testing Changes
207
+
208
+ ```bash
209
+ npm run build # Compile TypeScript
210
+ npm run lint # Run Biome linter
211
+ ```
212
+
213
+ Test by creating a challenge using the library and verifying generated datapack.
214
+
215
+ ---
216
+
217
+ ## Common Implementation Patterns
218
+
219
+ ### Objective Name Generation
220
+
221
+ ```typescript
222
+ private getNewObjectiveName(): string {
223
+ const hash = createHash("sha256")
224
+ .update(this.config.name + this.objectiveCounter++)
225
+ .digest("hex")
226
+ .slice(0, 8);
227
+ return `kradle.${hash}`;
228
+ }
229
+ ```
230
+
231
+ ### Variable Registration
232
+
233
+ ```typescript
234
+ // Individual with specific objective
235
+ const obj = Objective.create(name, objectiveType);
236
+ variables[varName] = obj("@s");
237
+
238
+ // Global dummy
239
+ const obj = Objective.create(name, "dummy");
240
+ variables[varName] = obj(GLOBAL_ENTITY);
241
+ ```
242
+
243
+ ### Event Processing (on_tick)
244
+
245
+ ```typescript
246
+ // 1. Update all variable values
247
+ for (const updater of updaters) {
248
+ updater(value, allVariables);
249
+ }
250
+
251
+ // 2. Process score events
252
+ for (const event of scoreEvents) {
253
+ _.if(score.greaterOrEqualThan(target), () => {
254
+ if (mode === "fire_once") {
255
+ _.if(prevScore.lowerThan(target), () => {
256
+ actions();
257
+ });
258
+ } else {
259
+ actions();
260
+ }
261
+ });
262
+ }
263
+
264
+ // 3. Update previous values
265
+ for (const [current, prev] of previousValuePairs) {
266
+ prev.set(current);
267
+ }
268
+
269
+ // 4. Check end condition
270
+ _.if(endCondition, () => {
271
+ endChallenge();
272
+ });
273
+ ```
274
+
275
+ ### Loot Table Deduplication
276
+
277
+ Loot tables are hashed by content to avoid duplicates:
278
+ ```typescript
279
+ const hash = createHash("sha256")
280
+ .update(JSON.stringify(lootTable))
281
+ .digest("hex")
282
+ .slice(0, 16);
283
+ ```
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ @AGENTS.md