motely-wasm 17.4.0 → 17.4.4
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/README.md +132 -65
- package/bin/dotnet.native.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# motely-wasm
|
|
2
2
|
|
|
3
|
-
WebAssembly package for [Motely](https://github.com/
|
|
3
|
+
WebAssembly package for [Motely](https://github.com/OptimusPi/MotelyJAML) — the Balatro seed search engine, with filters written in JAML.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -11,87 +11,106 @@ npm install motely-wasm
|
|
|
11
11
|
## Quick start
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
await
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
14
|
+
import bootsharp, { Motely } from "motely-wasm";
|
|
15
|
+
|
|
16
|
+
// Boot the .NET WASM runtime. The argument is the URL path to the package's
|
|
17
|
+
// `bin/` directory (where dotnet.native.wasm is served from).
|
|
18
|
+
await bootsharp.boot("/node_modules/motely-wasm/bin");
|
|
19
|
+
|
|
20
|
+
// A JAML filter — see https://github.com/OptimusPi/MotelyJAML for the language.
|
|
21
|
+
const jaml = `
|
|
22
|
+
name: WeeMonday
|
|
23
|
+
deck: Erratic
|
|
24
|
+
stake: Black
|
|
25
|
+
must:
|
|
26
|
+
- joker: WeeJoker
|
|
27
|
+
antes: [1]
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
// Validate before searching — returns "valid" or an error message.
|
|
31
|
+
const status = Motely.validateJaml(jaml);
|
|
32
|
+
if (status !== "valid") throw new Error(status);
|
|
33
|
+
|
|
34
|
+
// Subscribe to results before starting.
|
|
35
|
+
Motely.onScoredResult.subscribe(r => console.log("match:", r.seed, r.score));
|
|
22
36
|
Motely.onProgress.subscribe(p => console.log(`${p.percentComplete.toFixed(1)}%`));
|
|
23
37
|
|
|
24
|
-
// Build and
|
|
25
|
-
const search = Motely.createSearch(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- Joker
|
|
29
|
-
`).withSequentialSearch()
|
|
30
|
-
.createSearch();
|
|
38
|
+
// Build, start, and await a search.
|
|
39
|
+
const search = Motely.createSearch(jaml)
|
|
40
|
+
.withSequentialSearch()
|
|
41
|
+
.start();
|
|
31
42
|
|
|
32
|
-
search.
|
|
43
|
+
await search.waitForCompletionAsync();
|
|
44
|
+
console.log("done:", search.totalSeedsSearched, "searched,", search.matchingSeeds, "matched");
|
|
33
45
|
```
|
|
34
46
|
|
|
35
47
|
## Booting
|
|
36
48
|
|
|
37
|
-
`boot(
|
|
49
|
+
`bootsharp.boot(binUrl)` initializes the .NET WASM runtime. Call it once before any
|
|
50
|
+
`Motely.*` API. The argument is the URL path to the `bin/` directory that serves
|
|
51
|
+
`dotnet.native.wasm` — e.g. `/node_modules/motely-wasm/bin`, or wherever your bundler
|
|
52
|
+
copies the package's `bin/` folder.
|
|
38
53
|
|
|
39
54
|
```js
|
|
40
|
-
import
|
|
55
|
+
import bootsharp, { BootStatus } from "motely-wasm";
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
await
|
|
57
|
+
if (bootsharp.getStatus() === bootsharp.BootStatus.Standby) {
|
|
58
|
+
await bootsharp.boot("/node_modules/motely-wasm/bin");
|
|
59
|
+
}
|
|
44
60
|
|
|
45
|
-
//
|
|
46
|
-
console.log(motely.getStatus()); // BootStatus.Booted
|
|
61
|
+
console.log(bootsharp.getStatus()); // BootStatus.Booted
|
|
47
62
|
```
|
|
48
63
|
|
|
49
64
|
## JAML API
|
|
50
65
|
|
|
51
66
|
```js
|
|
52
|
-
import { Motely } from "motely-wasm
|
|
67
|
+
import { Motely } from "motely-wasm";
|
|
53
68
|
|
|
54
|
-
// Validate a JAML filter string — returns "" on success, error message on failure
|
|
55
|
-
const
|
|
69
|
+
// Validate a JAML filter string — returns "valid" on success, an error message on failure.
|
|
70
|
+
const status = Motely.validateJaml(jaml);
|
|
56
71
|
|
|
57
|
-
//
|
|
72
|
+
// Human-readable explanation of what a JAML filter does.
|
|
58
73
|
const explanation = Motely.explainJaml(jaml);
|
|
59
74
|
|
|
60
|
-
// Inspect the search plan (tally column count, CSV header, labels)
|
|
75
|
+
// Inspect the search plan (tally column count, CSV header, labels).
|
|
61
76
|
const plan = Motely.createPlan(jaml);
|
|
62
77
|
|
|
63
|
-
// Analyze specific seeds against a JAML filter
|
|
78
|
+
// Analyze specific seeds against a JAML filter.
|
|
64
79
|
const result = Motely.analyzeJamlSeeds(jaml, ["ABCD1234", "XYZ99"]);
|
|
65
80
|
|
|
66
|
-
// Engine version string
|
|
81
|
+
// Engine version string.
|
|
67
82
|
console.log(Motely.version());
|
|
68
83
|
```
|
|
69
84
|
|
|
70
85
|
## Running a search
|
|
71
86
|
|
|
87
|
+
`Motely.createSearch(jaml)` returns a settings builder. Chain a search-mode method,
|
|
88
|
+
then call `.start()` to get a running `IMotelySearch`.
|
|
89
|
+
|
|
72
90
|
```js
|
|
73
|
-
import { Motely } from "motely-wasm
|
|
74
|
-
import { MotelyDeck, MotelyStake } from "motely-wasm/motely";
|
|
91
|
+
import { Motely } from "motely-wasm";
|
|
75
92
|
|
|
76
|
-
Motely.onSeedMatch.subscribe(seed => { /*
|
|
77
|
-
Motely.onScoredResult.subscribe(
|
|
78
|
-
Motely.onProgress.subscribe(
|
|
93
|
+
Motely.onSeedMatch.subscribe(seed => { /* matching seed string */ });
|
|
94
|
+
Motely.onScoredResult.subscribe(r => { /* r.seed, r.score, r.tallies */ });
|
|
95
|
+
Motely.onProgress.subscribe(p => { /* p.percentComplete, p.seedsSearched, p.seedsPerMillisecond, … */ });
|
|
79
96
|
|
|
80
|
-
const settings = Motely.
|
|
81
|
-
.withDeck(MotelyDeck.Red)
|
|
82
|
-
.withStake(MotelyStake.White)
|
|
97
|
+
const settings = Motely.createSearch(jaml)
|
|
83
98
|
.withSequentialSearch() // enumerate all seeds in order
|
|
84
99
|
// .withRandomSearch(10_000) // or pick N random seeds
|
|
85
|
-
// .withListSearch(seeds,
|
|
100
|
+
// .withListSearch(seeds, seeds.length) // or supply a seed list
|
|
101
|
+
// .withAestheticSearch(0) // or a JamlAesthetic mode
|
|
86
102
|
.withProgressReportIntervalMs(500n);
|
|
87
103
|
|
|
88
|
-
const search = settings.
|
|
104
|
+
const search = settings.start();
|
|
89
105
|
|
|
90
|
-
//
|
|
91
|
-
search.
|
|
106
|
+
// Async (yields between batches — good on the main thread or in a Worker)
|
|
107
|
+
await search.waitForCompletionAsync();
|
|
92
108
|
|
|
93
|
-
//
|
|
94
|
-
|
|
109
|
+
// or synchronous (blocks until done — only inside a Worker)
|
|
110
|
+
// search.runSearchUntilCompletion();
|
|
111
|
+
|
|
112
|
+
console.log(search.isCompleted, search.totalSeedsSearched, search.matchingSeeds);
|
|
113
|
+
search.cancel(); // stop early
|
|
95
114
|
```
|
|
96
115
|
|
|
97
116
|
## Events
|
|
@@ -100,45 +119,93 @@ await search.runSearchAsync(undefined);
|
|
|
100
119
|
|---|---|
|
|
101
120
|
| `Motely.onSeedMatch` | `string` — matching seed |
|
|
102
121
|
| `Motely.onScoredResult` | `{ seed, score, tallies }` |
|
|
103
|
-
| `Motely.onProgress` | `MotelyProgress` |
|
|
104
|
-
| `Motely.onFileChanges` | `
|
|
122
|
+
| `Motely.onProgress` | `MotelyProgress` — `percentComplete`, `seedsSearched`, `matchingSeeds`, `seedsPerMillisecond`, `elapsedMilliseconds` |
|
|
123
|
+
| `Motely.onFileChanges` | `Change[]` (browser file-system mounts) |
|
|
105
124
|
|
|
106
125
|
Subscribe and unsubscribe:
|
|
107
126
|
|
|
108
127
|
```js
|
|
109
|
-
const handler =
|
|
110
|
-
Motely.
|
|
111
|
-
Motely.
|
|
128
|
+
const handler = r => console.log(r.seed);
|
|
129
|
+
Motely.onScoredResult.subscribe(handler);
|
|
130
|
+
Motely.onScoredResult.unsubscribe(handler);
|
|
112
131
|
```
|
|
113
132
|
|
|
114
133
|
## Submodule exports
|
|
115
134
|
|
|
116
135
|
| Import path | Contents |
|
|
117
136
|
|---|---|
|
|
118
|
-
| `motely-wasm` | `boot`, `
|
|
119
|
-
| `motely-wasm/
|
|
120
|
-
| `motely-wasm/motely` | Types: `MotelyDeck`, `MotelyStake`, `MotelyTag`, `MotelyVoucher`, `MotelyBossBlind`, `MotelyBoosterPack`, enums |
|
|
137
|
+
| `motely-wasm` | Default export: `boot`, `getStatus`, `BootStatus`. Named export: `Motely` (main API) |
|
|
138
|
+
| `motely-wasm/motely` | Types: `IMotelySearch`, `IMotelySearchSettingsInterop`, `MotelyProgress`, `MotelyScoredSeedResult`, `MotelyDeck`, `MotelyStake`, enums |
|
|
121
139
|
| `motely-wasm/motely/filters` | `JamlAesthetic`, `JamlSearchPlan` |
|
|
122
|
-
| `motely-wasm/motely/analysis` | `MotelyJamlyzerResult` |
|
|
123
|
-
| `motely-wasm/bootsharp/file-system` | File
|
|
140
|
+
| `motely-wasm/motely/analysis` | `MotelyJamlyzerResult`, `MotelySeedAnalysis` |
|
|
141
|
+
| `motely-wasm/bootsharp/file-system` | File-system interop (browser OPFS) — `PermissionMode`, `IFileMounter` |
|
|
124
142
|
|
|
125
143
|
## Using in a Web Worker
|
|
126
144
|
|
|
127
|
-
The WASM runtime is single-threaded. For non-blocking UI,
|
|
145
|
+
The WASM runtime is single-threaded. For a non-blocking UI, boot a runtime inside a
|
|
146
|
+
Worker and drive it with messages. This mirrors the proven setup in the `jaml-ui`
|
|
147
|
+
package's `searchWorker.ts`.
|
|
128
148
|
|
|
129
149
|
```js
|
|
130
|
-
// worker.js
|
|
131
|
-
import
|
|
132
|
-
import { Motely } from "motely-wasm/index";
|
|
133
|
-
|
|
134
|
-
await motely.boot("/assets/motely-wasm");
|
|
150
|
+
// search-worker.js
|
|
151
|
+
import bootsharp, { Motely } from "motely-wasm";
|
|
135
152
|
|
|
136
|
-
|
|
137
|
-
Motely.onProgress.subscribe(p => postMessage({ type: "progress", p }));
|
|
153
|
+
let currentSearch = null;
|
|
138
154
|
|
|
139
155
|
self.onmessage = async ({ data }) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
156
|
+
if (data.type === "stop") {
|
|
157
|
+
currentSearch?.cancel();
|
|
158
|
+
self.postMessage({ type: "cancelled" });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (data.type !== "start") return;
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
if (bootsharp.getStatus() === bootsharp.BootStatus.Standby) {
|
|
165
|
+
await bootsharp.boot("/node_modules/motely-wasm/bin");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const onResult = r =>
|
|
169
|
+
self.postMessage({ type: "result", seed: r.seed, score: r.score });
|
|
170
|
+
const onProgress = p =>
|
|
171
|
+
self.postMessage({ type: "progress", percent: p.percentComplete });
|
|
172
|
+
Motely.onScoredResult.subscribe(onResult);
|
|
173
|
+
Motely.onProgress.subscribe(onProgress);
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
currentSearch = Motely.createSearch(data.jaml)
|
|
177
|
+
.withThreadCount(1)
|
|
178
|
+
.withSequentialSearch()
|
|
179
|
+
.start();
|
|
180
|
+
await currentSearch.waitForCompletionAsync();
|
|
181
|
+
self.postMessage({
|
|
182
|
+
type: "complete",
|
|
183
|
+
total: Number(currentSearch.totalSeedsSearched),
|
|
184
|
+
matched: Number(currentSearch.matchingSeeds),
|
|
185
|
+
});
|
|
186
|
+
} finally {
|
|
187
|
+
Motely.onScoredResult.unsubscribe(onResult);
|
|
188
|
+
Motely.onProgress.unsubscribe(onProgress);
|
|
189
|
+
currentSearch = null;
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
self.postMessage({ type: "error", message: String(error?.message ?? error) });
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
self.postMessage({ type: "ready" });
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
// main thread
|
|
201
|
+
const worker = new Worker(new URL("./search-worker.js", import.meta.url), { type: "module" });
|
|
202
|
+
|
|
203
|
+
worker.onmessage = ({ data }) => {
|
|
204
|
+
if (data.type === "result") console.log("match:", data.seed, data.score);
|
|
205
|
+
if (data.type === "progress") console.log(`${data.percent.toFixed(1)}%`);
|
|
206
|
+
if (data.type === "complete") console.log("done:", data.total, data.matched);
|
|
143
207
|
};
|
|
208
|
+
|
|
209
|
+
worker.postMessage({ type: "start", jaml });
|
|
210
|
+
// worker.postMessage({ type: "stop" }); // cancel early
|
|
144
211
|
```
|
package/bin/dotnet.native.wasm
CHANGED
|
Binary file
|