You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+57-24Lines changed: 57 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,20 +4,34 @@
4
4
5
5
A .NET library + WASM/Node packages for Balatro seed analysis. It predicts what items, jokers, tags, vouchers, etc. a given seed will produce using Balatro's PRNG system.
6
6
7
-
## Architecture: Two Paths
7
+
## Architecture: Two Thin Hosts, One Brain
8
8
9
9
```
10
-
motely-wasm/ ← Browser WASM output (published to npm)
- There is NO intermediate static class. If you feel the urge to create a "shared interop API" between the two hosts, stop. They both call the orchestrator.
15
31
16
32
### DO NOT EDIT these directories:
17
33
-`motely-wasm/` — generated output
18
34
-`motely-node/` — generated output
19
-
-`motely-wasm/index.js` — generated
20
-
-`motely-node/index.cjs` — generated
21
35
22
36
### DO NOT EDIT core Motely library files:
23
37
-`Motely/MotelySingleSearchContext*.cs` — core PRNG engine, not ours
@@ -27,29 +41,47 @@ Both are **OUTPUT DIRECTORIES**. They are populated by the build/pack/publish pi
27
41
28
42
### Files YOU should edit:
29
43
-`Motely/MotelyGameplayState.cs` — our game state wrapper
-`IMotelyJsUi` — `[JSImport]` interface, events pushed from .NET to JS (`NotifySearchProgress`, `NotifySearchResult`)
56
+
-`Program.cs` — DI wiring: `AddBootsharp()` + `AddSingleton<IMotelyWasmBackend, MotelyWasmBackend>()` + `RunBootsharp()`
57
+
-`Motely.BrowserWasm.csproj` has a `BootsharpLLVM` flag (currently `false`) for future NativeAOT-LLVM
58
+
59
+
Bootsharp output lands in `Motely.BrowserWasm/bin/bootsharp/`. The stage script copies it to `motely-wasm/bootsharp/` (and `bootsharp_st/` for single-thread).
WASM and Node exports must be **one-liner pass-throughs** to `MotelyExports`. All logic lives in orchestration. Never duplicate logic across WASM and Node.
67
+
The script:
68
+
1. Bumps patch version in `Directory.Packages.props` + both `package.json` files
69
+
2. Publishes single-thread WASM → stages to `motely-wasm/bootsharp_st/`
70
+
3. Publishes multi-thread WASM → stages to `motely-wasm/bootsharp/` (falls back to ST copy if MT fails)
Every PRNG stream's state is ultimately a `double`. `MotelySinglePrngStream(double state)` wraps it. Higher-level streams (ShopItemStream, TarotStream, etc.) compose multiple PrngStreams. Each call to get the next item advances the double. That's it.
80
+
Every PRNG stream's state is ultimately a `double`. `MotelySinglePrngStream(double state)` wraps it. Higher-level streams compose multiple PrngStreams. Each call to get the next item advances the double.
49
81
50
82
### ref struct vs struct
51
83
52
-
Some stream types are `ref struct` (stack-only, cannot be stored on the heap). Others are plain `struct`. When you need to store a `ref struct` as a class field, decompose its fields into a storage struct (they're just doubles and strings), then reconstruct on demand. See `StreamCache` in `MotelyGameplayState.cs`.
84
+
Some stream types are `ref struct` (stack-only, cannot be stored on the heap). When you need to store a `ref struct` as a class field, decompose its fields into a storage struct (they're just doubles and strings), then reconstruct on demand. See `StreamCache` in `MotelyGameplayState.cs`.
53
85
54
86
Do NOT change `ref struct` to `struct` in core Motely files. Work around it.
55
87
@@ -63,7 +95,7 @@ Do NOT change `ref struct` to `struct` in core Motely files. Work around it.
63
95
64
96
### One Ante At A Time
65
97
66
-
The game state represents sequential gameplay. One ante at a time. When the ante changes, reset streams. Do NOT use `Dictionary<int, StreamType>` to cache per-ante. Think fibonacci: hold (a, b), advance. Don't cache every step.
98
+
The game state represents sequential gameplay. One ante at a time. When the ante changes, reset streams. Do NOT use `Dictionary<int, StreamType>` to cache per-ante.
67
99
68
100
### MotelyGameplayState IS the Single Seed Context
69
101
@@ -72,13 +104,14 @@ It's a stateful object that wraps `MotelySingleSearchContext` for one seed. You
72
104
## Common Pitfalls
73
105
74
106
1.**"Let me redesign the architecture"** — No. Make the minimal fix. Use what exists.
75
-
2.**"Let me wrap the analyzer output"** — No. The analyzer pre-computes a fixed set. GameState is infinite scroll.
76
-
3.**"Let me add a Dictionary for caching"** — No. One ante at a time. Direct fields, reset on ante change.
77
-
4.**"Let me create a new search/filter"** — No. The parked filter IS the mechanism. Use it.
78
-
5.**"Let me edit MotelySingleSearchContext"** — No. Use Motely. Don't edit it.
79
-
6.**"Let me edit the output JS/CJS files"** — No. They're build output.
80
-
7.**"These stream types should be struct not ref struct"** — Don't change core Motely types. Write storage wrappers if needed.
81
-
8.**"Let me hand-roll PRNG logic"** — No. Motely already provides high-level stream interfaces. Use them.
107
+
2.**"Let me add a shared interop API class"** — No. Both hosts call orchestrator directly.
108
+
3.**"Let me wrap the analyzer output"** — No. The analyzer pre-computes a fixed set. GameState is infinite scroll.
109
+
4.**"Let me add a Dictionary for caching"** — No. One ante at a time. Direct fields, reset on ante change.
110
+
5.**"Let me create a new search/filter"** — No. The parked filter IS the mechanism. Use it.
111
+
6.**"Let me edit MotelySingleSearchContext"** — No. Use Motely. Don't edit it.
112
+
7.**"Let me edit the output JS/CJS files"** — No. They're build output.
113
+
8.**"These stream types should be struct not ref struct"** — Don't change core Motely types.
114
+
9.**"Let me hand-roll PRNG logic"** — No. Motely provides high-level stream interfaces. Use them.
0 commit comments