@pkmn/engine 0.1.0-dev.2c13d3ec → 0.1.0-dev.38984361
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 +34 -34
- package/build.zig +24 -31
- package/package.json +23 -14
- package/src/bin/install-pkmn-engine +1 -1
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/protocol.zig +3 -0
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +243 -165
- package/src/lib/gen1/calc.zig +427 -0
- package/src/lib/gen1/chance.zig +1044 -0
- package/src/lib/gen1/data.zig +15 -10
- package/src/lib/gen1/helpers.zig +228 -75
- package/src/lib/gen1/mechanics.zig +11 -36
- package/src/lib/gen2/README.md +12 -13
package/README.md
CHANGED
|
@@ -67,12 +67,12 @@ $ npm install @pkmn/engine
|
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
The driver depends on being able to find compiled Node/WASM addons in
|
|
70
|
-
`node_modules/@pkmn/engine/build/lib`
|
|
71
|
-
|
|
72
|
-
[`install-pkmn-engine`](src/bin/install-pkmn-engine) which
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
`node_modules/@pkmn/engine/build/lib` to be useful. When you install the package a [`postinstall`
|
|
71
|
+
lifecycle script](https://docs.npmjs.com/cli/v8/using-npm/scripts) runs
|
|
72
|
+
[`install-pkmn-engine`](src/bin/install-pkmn-engine) which checks for a compatible `zig` compiler
|
|
73
|
+
(see earlier regarding minimum version) and download one to `node_module/@pkmn/engine/build/bin` if
|
|
74
|
+
it can't find one, as well as looking for (and downloading, if necessary) the required Node headers
|
|
75
|
+
needed to successfully build the addons natively.
|
|
76
76
|
|
|
77
77
|
**If you have configured `npm` to `--ignore-scripts` you must either run `npx install-pkmn-engine`
|
|
78
78
|
directly or build the addons manually and place the artifacts in the expected paths.**
|
|
@@ -100,7 +100,7 @@ configuring whether or not Pokémon Showdown compatibility mode or protocol mess
|
|
|
100
100
|
enabled. Alternatively, you may set options via a `pkmn_options` [root source file
|
|
101
101
|
declaration](https://ziglang.org/documentation/master/#Root-Source-File). There are several
|
|
102
102
|
undocumented internal options that can be tweaked as well via build or root options, though these
|
|
103
|
-
options
|
|
103
|
+
options aren't officially supported, affect correctness, and may change meaning or behavior without
|
|
104
104
|
warning. Use at your own risk.
|
|
105
105
|
|
|
106
106
|
```zig
|
|
@@ -112,11 +112,11 @@ pub const pkmn_options = .{ .showdown = true, .log = true };
|
|
|
112
112
|
For each Pokémon generation, the engine provides a battle structure with two functions - a function
|
|
113
113
|
to `update` the battle's state based on both players' choices and a function that defines which
|
|
114
114
|
`choices` are valid at that decision point (at the beginning of a battle both player's must "pass"
|
|
115
|
-
as their first choice
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
as their first choice to switch in the first member of their party). If `-Dlog` protocol logging is
|
|
116
|
+
enabled each update produces logs which can be decoded into a buffer (`LOGS_SIZE` constants are
|
|
117
|
+
provided to make it easy to allocate a buffer of the correct size). Each `update` returns a result
|
|
118
|
+
to indicate either that the battle has terminated or which types of choices are available for each
|
|
119
|
+
player.
|
|
120
120
|
|
|
121
121
|
Unlike [Pokémon Showdown's
|
|
122
122
|
SIM-PROTOCOL](https://github.com/smogon/pokemon-showdown/blob/master/sim/SIM-PROTOCOL.md#choice-requests)
|
|
@@ -128,7 +128,7 @@ state or cause the engine to crash.**
|
|
|
128
128
|
|
|
129
129
|
Battles may be played out from any point but to freshly initialize a battle which is yet to start
|
|
130
130
|
the turn count and active Pokémon should be zeroed out (driver code should handle this for you).
|
|
131
|
-
Most driver code should also provide helpers to make
|
|
131
|
+
Most driver code should also provide helpers to make initializing battle convenient - check the
|
|
132
132
|
respective documentation and examples for the bindings being used. The engine's [protocol
|
|
133
133
|
documentation](docs/PROTOCOL.md) goes into greater detail on the specifics of updates and the
|
|
134
134
|
potential logs that may result.
|
|
@@ -140,7 +140,7 @@ potential logs that may result.
|
|
|
140
140
|
|
|
141
141
|
[`pkmn.h`](src/include/pkmn.h) exports the C API for `libpkmn`. Symbols are all prefixed with
|
|
142
142
|
`pkmn_` to avoid name collisions. If `-Dlog` is enabled and logging throws an error then the error
|
|
143
|
-
|
|
143
|
+
is encoded in the `pkmn_result` and can be checked with `pkmn_error`.
|
|
144
144
|
|
|
145
145
|
```c
|
|
146
146
|
#include <pkmn.h>
|
|
@@ -161,17 +161,17 @@ if (pkmn_error(result)) exit(1);
|
|
|
161
161
|
|
|
162
162
|
[(full code)](src/examples/c/example.c)
|
|
163
163
|
|
|
164
|
-
The C API
|
|
164
|
+
The C API doesn't export any helpers for creating or accessing the opaque battle objects - it's
|
|
165
165
|
instead intended to be used as the foundation for more ergonomic bindings in [other
|
|
166
166
|
languages](#other) (the lack of namespaces and bit fields having an implementation-defined layout in
|
|
167
167
|
C are the main contributing factors to the sparseness of what `libpkmn` chooses to expose).
|
|
168
168
|
|
|
169
169
|
### JavaScript / TypeScript
|
|
170
170
|
|
|
171
|
-
`@pkmn/engine` depends on the [`@pkmn/data`](https://www.npmjs.com/package/@pkmn/data) which
|
|
171
|
+
`@pkmn/engine` depends on the [`@pkmn/data`](https://www.npmjs.com/package/@pkmn/data) package which
|
|
172
172
|
requires a `Dex` implementation to be provided as well. The `Battle.create` function can be used to
|
|
173
173
|
initialize a `Battle` from the beginning, or `Battle.restore` can be used to re-instantiate a battle
|
|
174
|
-
which is in
|
|
174
|
+
which is already in progress. If logging is enabled the output can be turned into Pokémon Showdown
|
|
175
175
|
protocol via `Log.parse`.
|
|
176
176
|
|
|
177
177
|
```ts
|
|
@@ -196,18 +196,18 @@ console.log(result);
|
|
|
196
196
|
|
|
197
197
|
[(full code)](src/examples/js/example.ts)
|
|
198
198
|
|
|
199
|
-
By default, the `@pkmn/engine` package
|
|
199
|
+
By default, the `@pkmn/engine` package compiles the engine with `-Dshowdown`, though by running
|
|
200
200
|
`install-pkmn-engine` directly and passing in `--options` you can override this default and build
|
|
201
201
|
different configurations of the extension for the driver to use. The driver can support
|
|
202
202
|
configurations both with and without Pokémon Showdown compatibility simultaneously if present. On
|
|
203
|
-
update, the post-install script
|
|
203
|
+
update, the post-install script attempts to rebuild whichever extensions it finds with the same
|
|
204
204
|
configuration parameters that were originally used meaning updating to the newest version of the
|
|
205
205
|
library should be seamless.
|
|
206
206
|
|
|
207
207
|
Despite relying on the native engine code, the `@pkmn/engine` code is designed to also work in
|
|
208
208
|
browsers which support [WebAssembly](https://webassembly.org/). Running `npm run start:web` from the
|
|
209
|
-
[`examples`](src/examples/js) directory
|
|
210
|
-
|
|
209
|
+
[`examples`](src/examples/js) directory starts a server that can be used to demonstrate the engine
|
|
210
|
+
running in the browser.
|
|
211
211
|
|
|
212
212
|
### Zig
|
|
213
213
|
|
|
@@ -240,7 +240,7 @@ std.debug.print("{}\n", .{result.type});
|
|
|
240
240
|
|
|
241
241
|
The Zig package also supports some APIs which are difficult to expose elsewhere such as the
|
|
242
242
|
`FixedRNG` which allows you to fully specify the exact RNG frames (which can be useful for ensuring
|
|
243
|
-
certain outcomes/effects always occur) for a battle, though doing so
|
|
243
|
+
certain outcomes/effects always occur) for a battle, though doing so changes the size of the
|
|
244
244
|
`Battle` object.
|
|
245
245
|
|
|
246
246
|
### Other
|
|
@@ -251,7 +251,7 @@ simplify the process even further [`src/data`](src/data) contains JSON dumps of
|
|
|
251
251
|
data, structure sizes and offsets, and protocol information used by the reference driver code. The
|
|
252
252
|
following is a list of known `libpkmn` bindings written by developers outside of the [pkmn
|
|
253
253
|
organization](https://github.com/pkmn) that may be helpful (though note that these projects may not
|
|
254
|
-
necessarily be up-to-date/complete/correct - inclusion in this list
|
|
254
|
+
necessarily be up-to-date/complete/correct - inclusion in this list doesn't imply endorsement):
|
|
255
255
|
|
|
256
256
|
| Language | License | URL |
|
|
257
257
|
| ---------- | --------- | ------------------------------------ |
|
|
@@ -279,7 +279,7 @@ By default, `pkmn-debug` expects to read [binary debug protocol](docs/PROTOCOL.m
|
|
|
279
279
|
$ <cmd> | pkmn-debug > index.html
|
|
280
280
|
```
|
|
281
281
|
|
|
282
|
-
Alternatively, if a single filename argument is passed to the `pkmn-debug` it
|
|
282
|
+
Alternatively, if a single filename argument is passed to the `pkmn-debug` it reads from that
|
|
283
283
|
instead:
|
|
284
284
|
|
|
285
285
|
```sh
|
|
@@ -301,14 +301,14 @@ Currently, most of the foundational work from stage 0 is done:
|
|
|
301
301
|
|
|
302
302
|
- [benchmark and integration testing](src/test) infrastructure
|
|
303
303
|
- [documentation](docs) about design, research, methodology, etc
|
|
304
|
-
- definition and implementation of the [protocol](docs/PROTOCOL.md) that
|
|
304
|
+
- definition and implementation of the [protocol](docs/PROTOCOL.md) that's used by the engine
|
|
305
305
|
|
|
306
|
-
**Stage 1 is currently in progress** and
|
|
306
|
+
**Stage 1 is currently in progress** and involves the implementation of the actual Generation I & II
|
|
307
307
|
battle engines, followed by Generation III & IV in stage 2. The implementation of further Pokémon
|
|
308
|
-
generations is in scope for the project but
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
308
|
+
generations is in scope for the project but shouldn't be considered part of the immediate roadmap
|
|
309
|
+
(i.e. exploring the options for broadening support for old generation APIs is given higher priority
|
|
310
|
+
than implementing more modern generations). Furthermore, implementation of modern generations is
|
|
311
|
+
soft-blocked on the [availability of high quality
|
|
312
312
|
decompilations](https://github.com/orgs/pret/repositories) of the original games in question.
|
|
313
313
|
|
|
314
314
|
Certain features will always be deemed **out of scope**:
|
|
@@ -326,10 +326,10 @@ The pkmn engine is distributed under the terms of the [MIT License](LICENSE).
|
|
|
326
326
|
[^1]: In the case of Pokémon Showdown, only bugs which stem from a misimplementation of specific
|
|
327
327
|
effects are reproduced in the engine, bugs which are the result of a misunderstanding of the
|
|
328
328
|
fundamental mechanics of Pokémon or which simply arise due to specific Pokémon Showdown
|
|
329
|
-
implementation details that
|
|
330
|
-
choices
|
|
329
|
+
implementation details that aren't replicable without making the same (incorrect) architectural
|
|
330
|
+
choices aren't. Furthermore, the "Pokémon Showdown" code referenced by this project includes
|
|
331
331
|
several [patches](docs/TESTING.md#patches) to improve accuracy and smooth over some of the more
|
|
332
332
|
egregious implementation issues. In practical terms, the vast majority of games played out in the
|
|
333
|
-
pkmn engine's compatibility mode and on this patched Pokémon Showdown simulator
|
|
334
|
-
it
|
|
333
|
+
pkmn engine's compatibility mode and on this patched Pokémon Showdown simulator are the same,
|
|
334
|
+
it's only in a well defined and documented set of circumstances where the two implementations
|
|
335
335
|
diverge.
|
package/build.zig
CHANGED
|
@@ -35,6 +35,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
35
35
|
const strip = b.option(bool, "strip", "Strip debugging symbols from binary") orelse false;
|
|
36
36
|
const pic = b.option(bool, "pic", "Force position independent code") orelse false;
|
|
37
37
|
const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
|
|
38
|
+
const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
|
|
38
39
|
|
|
39
40
|
const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
|
|
40
41
|
|
|
@@ -146,6 +147,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
146
147
|
maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
|
|
147
148
|
if (pic) lib.force_pic = pic;
|
|
148
149
|
if (emit_asm) lib.emit_asm = .emit;
|
|
150
|
+
if (emit_ll) lib.emit_llvm_ir = .emit;
|
|
149
151
|
b.installArtifact(lib);
|
|
150
152
|
c = true;
|
|
151
153
|
}
|
|
@@ -217,19 +219,15 @@ pub fn build(b: *std.Build) !void {
|
|
|
217
219
|
fuzz_config.tool.name = "fuzz";
|
|
218
220
|
const fuzz = tool(b, "src/test/fuzz.zig", fuzz_config);
|
|
219
221
|
|
|
222
|
+
const copies = tool(b, "src/tools/copies.zig", tools);
|
|
220
223
|
const dump = tool(b, "src/tools/dump.zig", tools);
|
|
221
224
|
const serde = tool(b, "src/tools/serde.zig", tools);
|
|
222
225
|
const transitions = tool(b, "src/tools/transitions.zig", tools);
|
|
223
226
|
|
|
224
|
-
|
|
225
|
-
b.addExecutable(.{ .name = "lint", .root_source_file = .{ .path = "src/tools/lint.zig" } });
|
|
226
|
-
if (tests.build) tests.step.dependOn(&lint_exe.step);
|
|
227
|
-
const lint = b.addRunArtifact(lint_exe);
|
|
228
|
-
|
|
227
|
+
b.step("copies", "Run copy detector").dependOn(&copies.step);
|
|
229
228
|
b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
|
|
230
229
|
b.step("dump", "Run protocol dump tool").dependOn(&dump.step);
|
|
231
230
|
b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
|
|
232
|
-
b.step("lint", "Lint source files").dependOn(&lint.step);
|
|
233
231
|
b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
|
|
234
232
|
b.step("test", "Run all tests").dependOn(&tests.step);
|
|
235
233
|
b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
|
|
@@ -263,12 +261,6 @@ const Config = struct {
|
|
|
263
261
|
cmd: ?[]const u8,
|
|
264
262
|
};
|
|
265
263
|
|
|
266
|
-
const TESTS = .{
|
|
267
|
-
"src/lib/common/test.zig",
|
|
268
|
-
"src/lib/gen1/test.zig",
|
|
269
|
-
"src/lib/gen2/test.zig",
|
|
270
|
-
};
|
|
271
|
-
|
|
272
264
|
const TestStep = struct {
|
|
273
265
|
step: std.Build.Step,
|
|
274
266
|
build: bool,
|
|
@@ -283,26 +275,27 @@ const TestStep = struct {
|
|
|
283
275
|
const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
|
|
284
276
|
self.* = TestStep{ .step = step, .build = test_filter == null };
|
|
285
277
|
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
self.step.dependOn(&b.addRunArtifact(tests).step);
|
|
278
|
+
const path = test_file orelse "src/lib/test.zig";
|
|
279
|
+
const tests = b.addTest(.{
|
|
280
|
+
.name = b.fmt("{s}-{s}", .{
|
|
281
|
+
std.fs.path.basename(std.fs.path.dirname(path).?),
|
|
282
|
+
std.fs.path.stem(std.fs.path.basename(path)),
|
|
283
|
+
}),
|
|
284
|
+
.root_source_file = .{ .path = path },
|
|
285
|
+
.optimize = config.optimize,
|
|
286
|
+
.target = config.target,
|
|
287
|
+
.filter = test_filter,
|
|
288
|
+
});
|
|
289
|
+
tests.setMainPkgPath("./");
|
|
290
|
+
tests.addOptions("build_options", options);
|
|
291
|
+
tests.single_threaded = true;
|
|
292
|
+
maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
|
|
293
|
+
if (config.pic) tests.force_pic = config.pic;
|
|
294
|
+
if (coverage) |c| {
|
|
295
|
+
tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
|
|
305
296
|
}
|
|
297
|
+
self.step.dependOn(&b.addRunArtifact(tests).step);
|
|
298
|
+
|
|
306
299
|
return self;
|
|
307
300
|
}
|
|
308
301
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pkmn/engine",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.38984361",
|
|
4
4
|
"description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
|
|
5
5
|
"repository": "github:pkmn/engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,11 +34,18 @@
|
|
|
34
34
|
"src/bin/install-pkmn-engine",
|
|
35
35
|
"src/lib/binding/node.zig",
|
|
36
36
|
"src/lib/binding/wasm.zig",
|
|
37
|
+
"src/lib/common/battle.zig",
|
|
37
38
|
"src/lib/common/data.zig",
|
|
38
39
|
"src/lib/common/debug.zig",
|
|
40
|
+
"src/lib/common/js.zig",
|
|
41
|
+
"src/lib/common/optional.zig",
|
|
39
42
|
"src/lib/common/options.zig",
|
|
40
43
|
"src/lib/common/protocol.zig",
|
|
44
|
+
"src/lib/common/rational.zig",
|
|
41
45
|
"src/lib/common/rng.zig",
|
|
46
|
+
"src/lib/common/util.zig",
|
|
47
|
+
"src/lib/gen1/calc.zig",
|
|
48
|
+
"src/lib/gen1/chance.zig",
|
|
42
49
|
"src/lib/gen1/data.zig",
|
|
43
50
|
"src/lib/gen1/data/moves.zig",
|
|
44
51
|
"src/lib/gen1/data/species.zig",
|
|
@@ -66,30 +73,30 @@
|
|
|
66
73
|
"access": "public"
|
|
67
74
|
},
|
|
68
75
|
"dependencies": {
|
|
69
|
-
"@pkmn/data": "^0.7.
|
|
76
|
+
"@pkmn/data": "^0.7.46",
|
|
70
77
|
"@pkmn/protocol": "^0.6.12"
|
|
71
78
|
},
|
|
72
79
|
"devDependencies": {
|
|
73
|
-
"@pkmn/eslint-config": "^
|
|
74
|
-
"@pkmn/img": "^0.2.
|
|
80
|
+
"@pkmn/eslint-config": "^5.0.0",
|
|
81
|
+
"@pkmn/img": "^0.2.13",
|
|
75
82
|
"@pkmn/sets": "^5.0.4",
|
|
76
|
-
"@pkmn/sim": "0.7.
|
|
83
|
+
"@pkmn/sim": "0.7.46",
|
|
77
84
|
"@types/html-minifier": "^4.0.2",
|
|
78
|
-
"@types/jest": "^29.5.
|
|
85
|
+
"@types/jest": "^29.5.3",
|
|
79
86
|
"@types/minimist": "^1.2.2",
|
|
80
87
|
"@types/mustache": "^4.2.2",
|
|
81
|
-
"@types/node": "^20.
|
|
82
|
-
"@vitest/coverage-c8": "^0.
|
|
83
|
-
"binaryen": "^
|
|
84
|
-
"eslint": "^8.
|
|
88
|
+
"@types/node": "^20.4.2",
|
|
89
|
+
"@vitest/coverage-c8": "^0.33.0",
|
|
90
|
+
"binaryen": "^114.0.0",
|
|
91
|
+
"eslint": "^8.44.0",
|
|
85
92
|
"html-minifier": "^4.0.0",
|
|
86
93
|
"json-stringify-pretty-compact": "3.0.0",
|
|
87
94
|
"minimist": "^1.2.8",
|
|
88
95
|
"mustache": "^4.2.0",
|
|
89
96
|
"source-map-support": "^0.5.21",
|
|
90
97
|
"trakr": "^0.2.0",
|
|
91
|
-
"typescript": "^5.1.
|
|
92
|
-
"vitest": "^0.
|
|
98
|
+
"typescript": "^5.1.6",
|
|
99
|
+
"vitest": "^0.33.0"
|
|
93
100
|
},
|
|
94
101
|
"scripts": {
|
|
95
102
|
"lint": "eslint --cache src --ext ts,js && eslint src/bin/*",
|
|
@@ -140,7 +147,8 @@
|
|
|
140
147
|
"rules": {
|
|
141
148
|
"no-control-regex": "off",
|
|
142
149
|
"@typescript-eslint/no-empty-interface": "off",
|
|
143
|
-
"@typescript-eslint/no-shadow": "off"
|
|
150
|
+
"@typescript-eslint/no-shadow": "off",
|
|
151
|
+
"@typescript-eslint/no-unsafe-declaration-merging": "off"
|
|
144
152
|
}
|
|
145
153
|
},
|
|
146
154
|
{
|
|
@@ -156,7 +164,8 @@
|
|
|
156
164
|
"src/test/benchmark*.ts"
|
|
157
165
|
],
|
|
158
166
|
"rules": {
|
|
159
|
-
"@typescript-eslint/no-unused-vars": "off"
|
|
167
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
168
|
+
"@typescript-eslint/no-redundant-type-constituents": "off"
|
|
160
169
|
}
|
|
161
170
|
},
|
|
162
171
|
{
|
|
@@ -358,7 +358,7 @@ const downloadImportLib = async () => {
|
|
|
358
358
|
// either the default options or whatever was passed in
|
|
359
359
|
if (!configs.size) {
|
|
360
360
|
node = true;
|
|
361
|
-
const DEFAULT = ['-Dshowdown'
|
|
361
|
+
const DEFAULT = ['-Dshowdown'];
|
|
362
362
|
configs.set('node', {type: 'node', options: options || DEFAULT});
|
|
363
363
|
configs.set('wasm', {type: 'wasm', options: options || DEFAULT});
|
|
364
364
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/// Stores state required by optional features to the engine that can be passed to a generation's
|
|
2
|
+
/// `Battle.update` function. All generations generically support protocol logging (enabled via
|
|
3
|
+
/// `-Dlog`), chance actions and probability tracking (enabled via `-Dchance`), and damage
|
|
4
|
+
/// computation and chance overrides (`-Dcalc`), though the concrete types for these features are
|
|
5
|
+
/// generation dependent.
|
|
6
|
+
pub fn Options(comptime Log: type, comptime Chance: type, comptime Calc: type) type {
|
|
7
|
+
return struct { log: Log, chance: Chance, calc: Calc };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// Helper to create a battle `Options` object with the provided `log`, `chance`, and `calc`.
|
|
11
|
+
pub fn options(
|
|
12
|
+
log: anytype,
|
|
13
|
+
chance: anytype,
|
|
14
|
+
calc: anytype,
|
|
15
|
+
) Options(@TypeOf(log), @TypeOf(chance), @TypeOf(calc)) {
|
|
16
|
+
return .{ .log = log, .chance = chance, .calc = calc };
|
|
17
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const assert = std.debug.assert;
|
|
4
|
+
|
|
5
|
+
const c = @cImport({
|
|
6
|
+
@cDefine("NAPI_VERSION", "8");
|
|
7
|
+
@cInclude("node_api.h");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
pub const Array = struct {
|
|
11
|
+
pub fn init(env: c.napi_env, o: struct { length: ?usize }) c.napi_value {
|
|
12
|
+
var result: c.napi_value = undefined;
|
|
13
|
+
assert(c.napi_ok == if (o.length) |n|
|
|
14
|
+
c.napi_create_array_with_length(env, n, &result)
|
|
15
|
+
else
|
|
16
|
+
c.napi_create_array(env, &result));
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pub fn set(env: c.napi_env, array: c.napi_value, index: u32, value: c.napi_value) void {
|
|
21
|
+
assert(c.napi_set_element(env, array, index, value) == c.napi_ok);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
pub const Boolean = struct {
|
|
26
|
+
pub fn init(env: c.napi_env, value: bool) c.napi_value {
|
|
27
|
+
var result: c.napi_value = undefined;
|
|
28
|
+
assert(c.napi_get_boolean(env, value, &result) == c.napi_ok);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
pub const Number = struct {
|
|
34
|
+
pub fn init(env: c.napi_env, value: anytype) c.napi_value {
|
|
35
|
+
const T = @TypeOf(value);
|
|
36
|
+
var result: c.napi_value = undefined;
|
|
37
|
+
assert(c.napi_ok == switch (@typeInfo(T)) {
|
|
38
|
+
.Int => |info| switch (info.bits) {
|
|
39
|
+
0...32 => switch (info.signedness) {
|
|
40
|
+
.signed => c.napi_create_int32(env, @as(i32, value), &result),
|
|
41
|
+
.unsigned => c.napi_create_uint32(env, @as(u32, value), &result),
|
|
42
|
+
},
|
|
43
|
+
33...52 => c.napi_create_int64(env, @as(i64, value), &result),
|
|
44
|
+
else => @compileError("int can't be represented as JS number"),
|
|
45
|
+
},
|
|
46
|
+
else => @compileError("expected number, got: " ++ @typeName(T)),
|
|
47
|
+
});
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub fn get(env: c.napi_env, value: c.napi_value, comptime T: type) T {
|
|
52
|
+
switch (@typeInfo(T)) {
|
|
53
|
+
.Int => |info| switch (info.bits) {
|
|
54
|
+
0...32 => switch (info.signedness) {
|
|
55
|
+
.signed => {
|
|
56
|
+
var result: i32 = undefined;
|
|
57
|
+
assert(c.napi_get_value_int32(env, value, &result) == c.napi_ok);
|
|
58
|
+
return if (info.bits == 32) result else @intCast(result);
|
|
59
|
+
},
|
|
60
|
+
.unsigned => {
|
|
61
|
+
var result: u32 = undefined;
|
|
62
|
+
assert(c.napi_get_value_uint32(env, value, &result) == c.napi_ok);
|
|
63
|
+
return if (info.bits == 32) result else @intCast(result);
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
33...63 => {
|
|
67
|
+
var result: i64 = undefined;
|
|
68
|
+
assert(c.napi_get_value_int64(env, value, &result) == c.napi_ok);
|
|
69
|
+
return @intCast(result);
|
|
70
|
+
},
|
|
71
|
+
else => {
|
|
72
|
+
var result: i64 = undefined;
|
|
73
|
+
assert(c.napi_get_value_int64(env, value, &result) == c.napi_ok);
|
|
74
|
+
return switch (info.signedness) {
|
|
75
|
+
.signed => value,
|
|
76
|
+
.unsigned => if (0 <= value) @intCast(value) else unreachable,
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
else => @compileError("expected number, got: " ++ @typeName(T)),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
pub const Object = struct {
|
|
86
|
+
pub fn init(env: c.napi_env) c.napi_value {
|
|
87
|
+
var result: c.napi_value = undefined;
|
|
88
|
+
assert(c.napi_create_object(env, &result) == c.napi_ok);
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
pub const Property = union(enum) {
|
|
94
|
+
method: c.napi_callback,
|
|
95
|
+
value: c.napi_value,
|
|
96
|
+
|
|
97
|
+
pub fn init(comptime name: [:0]const u8, property: Property) c.napi_property_descriptor {
|
|
98
|
+
return .{
|
|
99
|
+
.utf8name = name,
|
|
100
|
+
.name = null,
|
|
101
|
+
.method = switch (property) {
|
|
102
|
+
.method => |m| m,
|
|
103
|
+
.value => null,
|
|
104
|
+
},
|
|
105
|
+
.getter = null,
|
|
106
|
+
.setter = null,
|
|
107
|
+
.value = switch (property) {
|
|
108
|
+
.method => null,
|
|
109
|
+
.value => |v| v,
|
|
110
|
+
},
|
|
111
|
+
.attributes = switch (property) {
|
|
112
|
+
.method => c.napi_default,
|
|
113
|
+
.value => c.napi_enumerable,
|
|
114
|
+
},
|
|
115
|
+
.data = null,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const expect = std.testing.expect;
|
|
4
|
+
const assert = std.debug.assert;
|
|
5
|
+
|
|
6
|
+
// TODO: ziglang/zig#104
|
|
7
|
+
pub fn Optional(comptime T: type) type {
|
|
8
|
+
const fields = std.meta.fields(switch (@typeInfo(T)) {
|
|
9
|
+
.Bool => enum { false, true },
|
|
10
|
+
else => T,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
var enumFields: [fields.len + 1]std.builtin.Type.EnumField = undefined;
|
|
14
|
+
var decls = [_]std.builtin.Type.Declaration{};
|
|
15
|
+
|
|
16
|
+
enumFields[0] = .{ .name = "None", .value = 0 };
|
|
17
|
+
inline for (fields, 1..) |field, i| {
|
|
18
|
+
assert(!std.mem.eql(u8, field.name, "None"));
|
|
19
|
+
enumFields[i] = .{
|
|
20
|
+
.name = field.name,
|
|
21
|
+
.value = i,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return @Type(.{
|
|
26
|
+
.Enum = .{
|
|
27
|
+
.tag_type = std.math.IntFittingRange(0, fields.len + 1),
|
|
28
|
+
.fields = &enumFields,
|
|
29
|
+
.decls = &decls,
|
|
30
|
+
.is_exhaustive = true,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test Optional {
|
|
36
|
+
try expect(@bitSizeOf(Optional(bool)) == 2);
|
|
37
|
+
|
|
38
|
+
const a: Optional(bool) = .true;
|
|
39
|
+
try expect(a != .None);
|
|
40
|
+
try expect(a == .true);
|
|
41
|
+
try expect(a != .false);
|
|
42
|
+
|
|
43
|
+
const b: Optional(bool) = .None;
|
|
44
|
+
try expect(b == .None);
|
|
45
|
+
try expect(b != .true);
|
|
46
|
+
try expect(b != .false);
|
|
47
|
+
|
|
48
|
+
const Player = @import("./data.zig").Player;
|
|
49
|
+
try expect(@bitSizeOf(Optional(Player)) == 2);
|
|
50
|
+
|
|
51
|
+
const p: Optional(Player) = .P2;
|
|
52
|
+
try expect(p != .None);
|
|
53
|
+
try expect(p != .P1);
|
|
54
|
+
try expect(p == .P2);
|
|
55
|
+
|
|
56
|
+
const q: Optional(Player) = .None;
|
|
57
|
+
try expect(q == .None);
|
|
58
|
+
try expect(q != .P1);
|
|
59
|
+
try expect(q != .P2);
|
|
60
|
+
}
|