@pkmn/engine 0.1.0-dev.979a393a → 0.1.0-dev.a324c50f
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 +32 -29
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +112 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/layout.json +25 -3
- package/build/pkg/gen1.d.ts +5 -4
- package/build/pkg/gen1.js +47 -31
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +15 -12
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +4 -2
- package/build/pkg/protocol.js +4 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +31 -13
- package/build/test/display.js.map +1 -1
- package/build.zig +49 -39
- package/package.json +18 -14
- package/src/bin/install-pkmn-engine +11 -6
- package/src/bin/pkmn-debug +29 -6
- package/src/lib/binding/node.zig +46 -141
- package/src/lib/binding/wasm.zig +6 -4
- package/src/lib/common/data.zig +11 -11
- package/src/lib/common/debug.zig +21 -11
- package/src/lib/common/options.zig +12 -3
- package/src/lib/common/protocol.zig +239 -178
- package/src/lib/common/rng.zig +10 -10
- package/src/lib/gen1/README.md +111 -81
- package/src/lib/gen1/data/moves.zig +10 -10
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +3 -3
- package/src/lib/gen1/data.zig +64 -43
- package/src/lib/gen1/helpers.zig +439 -32
- package/src/lib/gen1/mechanics.zig +987 -612
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +1 -1
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +3 -3
- package/src/lib/gen2/data.zig +37 -26
- package/src/lib/gen2/helpers.zig +9 -11
- package/src/lib/gen2/mechanics.zig +5 -4
- package/src/lib/pkmn.zig +68 -9
package/build.zig
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const builtin = @import("builtin");
|
|
3
3
|
|
|
4
|
-
pub const Options = struct {
|
|
4
|
+
pub const Options = struct {
|
|
5
|
+
showdown: ?bool = null,
|
|
6
|
+
log: ?bool = null,
|
|
7
|
+
chance: ?bool = null,
|
|
8
|
+
calc: ?bool = null,
|
|
9
|
+
};
|
|
5
10
|
|
|
6
11
|
pub fn module(b: *std.Build, options: Options) *std.Build.Module {
|
|
7
12
|
const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
|
|
8
13
|
const build_options = b.addOptions();
|
|
9
14
|
build_options.addOption(?bool, "showdown", options.showdown);
|
|
10
|
-
build_options.addOption(?bool, "
|
|
15
|
+
build_options.addOption(?bool, "log", options.log);
|
|
16
|
+
build_options.addOption(?bool, "chance", options.chance);
|
|
17
|
+
build_options.addOption(?bool, "calc", options.calc);
|
|
11
18
|
return b.createModule(.{
|
|
12
19
|
.source_file = .{ .path = dirname ++ "/src/lib/pkmn.zig" },
|
|
13
20
|
.dependencies = &.{.{ .name = "build_options", .module = build_options.createModule() }},
|
|
@@ -27,25 +34,29 @@ pub fn build(b: *std.Build) !void {
|
|
|
27
34
|
const dynamic = b.option(bool, "dynamic", "Build a dynamic library") orelse false;
|
|
28
35
|
const strip = b.option(bool, "strip", "Strip debugging symbols from binary") orelse false;
|
|
29
36
|
const pic = b.option(bool, "pic", "Force position independent code") orelse false;
|
|
37
|
+
const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
|
|
30
38
|
|
|
31
39
|
const cmd = b.findProgram(&.{"strip"}, &.{}) catch null;
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
var repository = std.mem.split(u8, tree.root.Object.get("repository").?.String, ":");
|
|
41
|
+
const json = @embedFile("package.json");
|
|
42
|
+
var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
|
|
43
|
+
defer parsed.deinit();
|
|
44
|
+
const version = parsed.value.object.get("version").?.string;
|
|
45
|
+
const description = parsed.value.object.get("description").?.string;
|
|
46
|
+
var repository = std.mem.split(u8, parsed.value.object.get("repository").?.string, ":");
|
|
40
47
|
std.debug.assert(std.mem.eql(u8, repository.first(), "github"));
|
|
41
48
|
|
|
42
49
|
const showdown =
|
|
43
50
|
b.option(bool, "showdown", "Enable Pokémon Showdown compatibility mode") orelse false;
|
|
44
|
-
const
|
|
51
|
+
const log = b.option(bool, "log", "Enable protocol message logging") orelse false;
|
|
52
|
+
const chance = b.option(bool, "chance", "Enable update probability tracking") orelse false;
|
|
53
|
+
const calc = b.option(bool, "calc", "Enable damage calculator support") orelse false;
|
|
45
54
|
|
|
46
55
|
const options = b.addOptions();
|
|
47
56
|
options.addOption(?bool, "showdown", showdown);
|
|
48
|
-
options.addOption(?bool, "
|
|
57
|
+
options.addOption(?bool, "log", log);
|
|
58
|
+
options.addOption(?bool, "chance", chance);
|
|
59
|
+
options.addOption(?bool, "calc", calc);
|
|
49
60
|
|
|
50
61
|
const name = if (showdown) "pkmn-showdown" else "pkmn";
|
|
51
62
|
|
|
@@ -79,7 +90,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
79
90
|
//
|
|
80
91
|
// But ziglang/zig#14647 broke this so we now need to do an install() and then manually
|
|
81
92
|
// rename the file ourself in install-pkmn-engine
|
|
82
|
-
|
|
93
|
+
b.installArtifact(lib);
|
|
83
94
|
} else if (wasm) {
|
|
84
95
|
const lib = b.addSharedLibrary(.{
|
|
85
96
|
.name = name,
|
|
@@ -105,12 +116,12 @@ pub fn build(b: *std.Build) !void {
|
|
|
105
116
|
sh.addFileSourceArg(.{ .path = out });
|
|
106
117
|
b.getInstallStep().dependOn(&sh.step);
|
|
107
118
|
}
|
|
108
|
-
|
|
119
|
+
b.installArtifact(lib);
|
|
109
120
|
} else if (dynamic) {
|
|
110
121
|
const lib = b.addSharedLibrary(.{
|
|
111
122
|
.name = name,
|
|
112
123
|
.root_source_file = .{ .path = "src/lib/binding/c.zig" },
|
|
113
|
-
.version = try std.
|
|
124
|
+
.version = try std.SemanticVersion.parse(version),
|
|
114
125
|
.optimize = optimize,
|
|
115
126
|
.target = target,
|
|
116
127
|
});
|
|
@@ -119,7 +130,7 @@ pub fn build(b: *std.Build) !void {
|
|
|
119
130
|
lib.addIncludePath("src/include");
|
|
120
131
|
maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
|
|
121
132
|
if (pic) lib.force_pic = pic;
|
|
122
|
-
|
|
133
|
+
b.installArtifact(lib);
|
|
123
134
|
c = true;
|
|
124
135
|
} else {
|
|
125
136
|
const lib = b.addStaticLibrary(.{
|
|
@@ -134,7 +145,8 @@ pub fn build(b: *std.Build) !void {
|
|
|
134
145
|
lib.bundle_compiler_rt = true;
|
|
135
146
|
maybeStrip(b, lib, b.getInstallStep(), strip, cmd);
|
|
136
147
|
if (pic) lib.force_pic = pic;
|
|
137
|
-
lib.
|
|
148
|
+
if (emit_asm) lib.emit_asm = .emit;
|
|
149
|
+
b.installArtifact(lib);
|
|
138
150
|
c = true;
|
|
139
151
|
}
|
|
140
152
|
|
|
@@ -176,14 +188,20 @@ pub fn build(b: *std.Build) !void {
|
|
|
176
188
|
.strip = strip,
|
|
177
189
|
.cmd = cmd,
|
|
178
190
|
};
|
|
191
|
+
|
|
192
|
+
// TODO: tests can be run multiple times due to @imports
|
|
179
193
|
const tests = TestStep.create(b, options, config);
|
|
180
194
|
|
|
181
195
|
var exes = std.ArrayList(*std.Build.CompileStep).init(b.allocator);
|
|
182
196
|
const tools: ToolConfig = .{
|
|
197
|
+
.options = .{
|
|
198
|
+
.showdown = showdown,
|
|
199
|
+
.log = log,
|
|
200
|
+
.chance = chance,
|
|
201
|
+
.calc = calc,
|
|
202
|
+
},
|
|
183
203
|
.general = config,
|
|
184
204
|
.tool = .{
|
|
185
|
-
.showdown = showdown,
|
|
186
|
-
.trace = trace,
|
|
187
205
|
.tests = if (tests.build) tests else null,
|
|
188
206
|
.exes = &exes,
|
|
189
207
|
},
|
|
@@ -197,23 +215,25 @@ pub fn build(b: *std.Build) !void {
|
|
|
197
215
|
var fuzz_config = tools;
|
|
198
216
|
fuzz_config.general.strip = false;
|
|
199
217
|
fuzz_config.tool.name = "fuzz";
|
|
200
|
-
const fuzz = tool(b, "src/test/
|
|
218
|
+
const fuzz = tool(b, "src/test/fuzz.zig", fuzz_config);
|
|
201
219
|
|
|
220
|
+
const dump = tool(b, "src/tools/dump.zig", tools);
|
|
202
221
|
const serde = tool(b, "src/tools/serde.zig", tools);
|
|
203
|
-
const
|
|
222
|
+
const transitions = tool(b, "src/tools/transitions.zig", tools);
|
|
204
223
|
|
|
205
224
|
const lint_exe =
|
|
206
225
|
b.addExecutable(.{ .name = "lint", .root_source_file = .{ .path = "src/tools/lint.zig" } });
|
|
207
226
|
if (tests.build) tests.step.dependOn(&lint_exe.step);
|
|
208
|
-
const lint =
|
|
227
|
+
const lint = b.addRunArtifact(lint_exe);
|
|
209
228
|
|
|
210
229
|
b.step("benchmark", "Run benchmark code").dependOn(&benchmark.step);
|
|
230
|
+
b.step("dump", "Run protocol dump tool").dependOn(&dump.step);
|
|
211
231
|
b.step("fuzz", "Run fuzz tester").dependOn(&fuzz.step);
|
|
212
232
|
b.step("lint", "Lint source files").dependOn(&lint.step);
|
|
213
|
-
b.step("protocol", "Run protocol dump tool").dependOn(&protocol.step);
|
|
214
233
|
b.step("serde", "Run serialization/deserialization tool").dependOn(&serde.step);
|
|
215
234
|
b.step("test", "Run all tests").dependOn(&tests.step);
|
|
216
235
|
b.step("tools", "Install tools").dependOn(&ToolsStep.create(b, &exes).step);
|
|
236
|
+
b.step("transitions", "Visualize transitions algorithm search").dependOn(&transitions.step);
|
|
217
237
|
}
|
|
218
238
|
|
|
219
239
|
fn maybeStrip(
|
|
@@ -256,11 +276,8 @@ const TestStep = struct {
|
|
|
256
276
|
pub fn create(b: *std.Build, options: *std.Build.OptionsStep, config: Config) *TestStep {
|
|
257
277
|
const coverage = b.option([]const u8, "test-coverage", "Generate test coverage");
|
|
258
278
|
const test_file = b.option([]const u8, "test-file", "Input file for test");
|
|
259
|
-
const test_bin = b.option([]const u8, "test-bin", "Emit test binary to");
|
|
260
279
|
const test_filter =
|
|
261
280
|
b.option([]const u8, "test-filter", "Skip tests that do not match filter");
|
|
262
|
-
const test_no_exec =
|
|
263
|
-
b.option(bool, "test-no-exec", "Compiles test binary without running it") orelse false;
|
|
264
281
|
|
|
265
282
|
const self = b.allocator.create(TestStep) catch @panic("OOM");
|
|
266
283
|
const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
|
|
@@ -274,31 +291,26 @@ const TestStep = struct {
|
|
|
274
291
|
.root_source_file = .{ .path = path },
|
|
275
292
|
.optimize = config.optimize,
|
|
276
293
|
.target = config.target,
|
|
294
|
+
.filter = test_filter,
|
|
277
295
|
});
|
|
278
296
|
tests.setMainPkgPath("./");
|
|
279
|
-
tests.setFilter(test_filter);
|
|
280
297
|
tests.addOptions("build_options", options);
|
|
281
298
|
tests.single_threaded = true;
|
|
282
299
|
maybeStrip(b, tests, &tests.step, config.strip, config.cmd);
|
|
283
300
|
if (config.pic) tests.force_pic = config.pic;
|
|
284
|
-
if (test_bin) |bin| {
|
|
285
|
-
tests.name = std.fs.path.basename(bin);
|
|
286
|
-
if (std.fs.path.dirname(bin)) |dir| tests.setOutputDir(dir);
|
|
287
|
-
}
|
|
288
301
|
if (coverage) |c| {
|
|
289
302
|
tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
|
|
290
303
|
}
|
|
291
|
-
self.step.dependOn(
|
|
304
|
+
self.step.dependOn(&b.addRunArtifact(tests).step);
|
|
292
305
|
}
|
|
293
306
|
return self;
|
|
294
307
|
}
|
|
295
308
|
};
|
|
296
309
|
|
|
297
310
|
const ToolConfig = struct {
|
|
311
|
+
options: Options,
|
|
298
312
|
general: Config,
|
|
299
313
|
tool: struct {
|
|
300
|
-
showdown: bool,
|
|
301
|
-
trace: bool,
|
|
302
314
|
tests: ?*TestStep,
|
|
303
315
|
name: ?[]const u8 = null,
|
|
304
316
|
exes: *std.ArrayList(*std.Build.CompileStep),
|
|
@@ -309,7 +321,7 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
|
|
|
309
321
|
var name = config.tool.name orelse std.fs.path.basename(path);
|
|
310
322
|
const index = std.mem.lastIndexOfScalar(u8, name, '.');
|
|
311
323
|
if (index) |i| name = name[0..i];
|
|
312
|
-
if (config.
|
|
324
|
+
if (config.options.showdown orelse false) name = b.fmt("{s}-showdown", .{name});
|
|
313
325
|
|
|
314
326
|
const exe = b.addExecutable(.{
|
|
315
327
|
.name = name,
|
|
@@ -317,16 +329,14 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) *std.Build.RunStep
|
|
|
317
329
|
.target = config.general.target,
|
|
318
330
|
.optimize = config.general.optimize,
|
|
319
331
|
});
|
|
320
|
-
exe.addModule("pkmn", module(b, .
|
|
321
|
-
|
|
322
|
-
.trace = config.tool.trace,
|
|
323
|
-
}));
|
|
332
|
+
exe.addModule("pkmn", module(b, config.options));
|
|
333
|
+
|
|
324
334
|
exe.single_threaded = true;
|
|
325
335
|
if (config.general.pic) exe.force_pic = config.general.pic;
|
|
326
336
|
if (config.tool.tests) |ts| ts.step.dependOn(&exe.step);
|
|
327
337
|
config.tool.exes.append(exe) catch @panic("OOM");
|
|
328
338
|
|
|
329
|
-
const run =
|
|
339
|
+
const run = b.addRunArtifact(exe);
|
|
330
340
|
maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
|
|
331
341
|
if (b.args) |args| run.addArgs(args);
|
|
332
342
|
|
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.a324c50f",
|
|
4
4
|
"description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
|
|
5
5
|
"repository": "github:pkmn/engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"build.zig",
|
|
12
12
|
"build/pkg/data.d.ts",
|
|
13
13
|
"build/pkg/data.js.map",
|
|
14
|
+
"build/pkg/addon.d.ts",
|
|
15
|
+
"build/pkg/addon.js.map",
|
|
16
|
+
"build/pkg/addon.js",
|
|
14
17
|
"build/pkg/data.js",
|
|
15
18
|
"build/pkg/data/ids.json",
|
|
16
19
|
"build/pkg/data/layout.json",
|
|
@@ -63,30 +66,30 @@
|
|
|
63
66
|
"access": "public"
|
|
64
67
|
},
|
|
65
68
|
"dependencies": {
|
|
66
|
-
"@pkmn/data": "^0.7.
|
|
67
|
-
"@pkmn/protocol": "^0.6.
|
|
69
|
+
"@pkmn/data": "^0.7.44",
|
|
70
|
+
"@pkmn/protocol": "^0.6.12"
|
|
68
71
|
},
|
|
69
72
|
"devDependencies": {
|
|
70
|
-
"@pkmn/eslint-config": "^3.
|
|
71
|
-
"@pkmn/img": "^0.2.
|
|
73
|
+
"@pkmn/eslint-config": "^3.2.0",
|
|
74
|
+
"@pkmn/img": "^0.2.12",
|
|
72
75
|
"@pkmn/sets": "^5.0.4",
|
|
73
|
-
"@pkmn/sim": "0.7.
|
|
76
|
+
"@pkmn/sim": "0.7.44",
|
|
74
77
|
"@types/html-minifier": "^4.0.2",
|
|
75
|
-
"@types/jest": "^29.5.
|
|
78
|
+
"@types/jest": "^29.5.2",
|
|
76
79
|
"@types/minimist": "^1.2.2",
|
|
77
80
|
"@types/mustache": "^4.2.2",
|
|
78
|
-
"@types/node": "^
|
|
79
|
-
"@vitest/coverage-c8": "^0.
|
|
80
|
-
"binaryen": "^
|
|
81
|
-
"eslint": "^8.
|
|
81
|
+
"@types/node": "^20.3.1",
|
|
82
|
+
"@vitest/coverage-c8": "^0.32.2",
|
|
83
|
+
"binaryen": "^113.0.0",
|
|
84
|
+
"eslint": "^8.43.0",
|
|
82
85
|
"html-minifier": "^4.0.0",
|
|
83
86
|
"json-stringify-pretty-compact": "3.0.0",
|
|
84
87
|
"minimist": "^1.2.8",
|
|
85
88
|
"mustache": "^4.2.0",
|
|
86
89
|
"source-map-support": "^0.5.21",
|
|
87
90
|
"trakr": "^0.2.0",
|
|
88
|
-
"typescript": "^5.
|
|
89
|
-
"vitest": "^0.
|
|
91
|
+
"typescript": "^5.1.3",
|
|
92
|
+
"vitest": "^0.32.2"
|
|
90
93
|
},
|
|
91
94
|
"scripts": {
|
|
92
95
|
"lint": "eslint --cache src --ext ts,js && eslint src/bin/*",
|
|
@@ -158,7 +161,8 @@
|
|
|
158
161
|
},
|
|
159
162
|
{
|
|
160
163
|
"files": [
|
|
161
|
-
"src/test/*.test.ts"
|
|
164
|
+
"src/test/*.test.ts",
|
|
165
|
+
"src/test/regression/*.test.ts"
|
|
162
166
|
],
|
|
163
167
|
"rules": {
|
|
164
168
|
"jest/no-standalone-expect": "off"
|
|
@@ -10,7 +10,7 @@ const path = require('path');
|
|
|
10
10
|
const {pipeline} = require('stream/promises');
|
|
11
11
|
const tty = require('tty');
|
|
12
12
|
|
|
13
|
-
const ZIG_VERSION = {major: 0, minor: 11, patch: 0, dev:
|
|
13
|
+
const ZIG_VERSION = {major: 0, minor: 11, patch: 0, dev: 3859};
|
|
14
14
|
const DEV = ZIG_VERSION.dev ? `-dev.${ZIG_VERSION.dev}` : '';
|
|
15
15
|
const VERSION = `${ZIG_VERSION.major}.${ZIG_VERSION.minor}.${ZIG_VERSION.patch}${DEV}`;
|
|
16
16
|
const INDEX = 'https://ziglang.org/download/index.json';
|
|
@@ -336,14 +336,18 @@ const downloadImportLib = async () => {
|
|
|
336
336
|
node = true;
|
|
337
337
|
if (!options) {
|
|
338
338
|
const addon = require(copy);
|
|
339
|
-
if (addon.options.showdown) opts.push('-Dshowdown');
|
|
340
|
-
if (addon.options.
|
|
339
|
+
if (addon.engine.options.showdown) opts.push('-Dshowdown');
|
|
340
|
+
if (addon.engine.options.log) opts.push('-Dlog');
|
|
341
|
+
if (addon.engine.options.chance) opts.push('-Dchance');
|
|
342
|
+
if (addon.engine.options.calc) opts.push('-Dcalc');
|
|
341
343
|
}
|
|
342
344
|
} else if (!options) {
|
|
343
345
|
const addon = await WebAssembly.instantiate(fs.readFileSync(copy));
|
|
344
346
|
const values = new Uint8Array(addon.instance.exports.memory.buffer);
|
|
345
347
|
if (values[addon.instance.exports.SHOWDOWN.value]) opts.push('-Dshowdown');
|
|
346
|
-
if (values[addon.instance.exports.
|
|
348
|
+
if (values[addon.instance.exports.LOG.value]) opts.push('-Dlog');
|
|
349
|
+
if (values[addon.instance.exports.CHANCE.value]) opts.push('-Dchance');
|
|
350
|
+
if (values[addon.instance.exports.CALC.value]) opts.push('-Dcalc');
|
|
347
351
|
}
|
|
348
352
|
const key = `${type} ${opts.sort().join(' ')}`;
|
|
349
353
|
if (!configs.has(key)) configs.set(key, {type, options: opts});
|
|
@@ -354,8 +358,9 @@ const downloadImportLib = async () => {
|
|
|
354
358
|
// either the default options or whatever was passed in
|
|
355
359
|
if (!configs.size) {
|
|
356
360
|
node = true;
|
|
357
|
-
|
|
358
|
-
configs.set('
|
|
361
|
+
const DEFAULT = ['-Dshowdown', '-Dlog', '-Dchance', '-Dcalc'];
|
|
362
|
+
configs.set('node', {type: 'node', options: options || DEFAULT});
|
|
363
|
+
configs.set('wasm', {type: 'wasm', options: options || DEFAULT});
|
|
359
364
|
}
|
|
360
365
|
|
|
361
366
|
const headers = node ? await downloadHeaders() : undefined;
|
package/src/bin/pkmn-debug
CHANGED
|
@@ -8,31 +8,54 @@ try {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const {execFileSync} = require('child_process');
|
|
11
|
+
const {readFileSync} = require('fs');
|
|
12
|
+
const {join, resolve} = require('path');
|
|
11
13
|
|
|
12
|
-
const
|
|
14
|
+
const ROOT = resolve(__dirname, '..', '..');
|
|
15
|
+
const packageJSON = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
13
16
|
|
|
14
|
-
const
|
|
17
|
+
const missing = [];
|
|
18
|
+
for (const dep of ['@pkmn/img', 'html-minifier', 'mustache']) {
|
|
19
|
+
try {
|
|
20
|
+
require.resolve(dep);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
23
|
+
missing.push(`${dep}@${packageJSON.devDependencies[dep]}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
15
26
|
|
|
16
|
-
let Dex;
|
|
27
|
+
let Dex = undefined;
|
|
17
28
|
try {
|
|
18
29
|
Dex = require('@pkmn/sim').Dex;
|
|
19
30
|
} catch (err) {
|
|
20
31
|
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
21
32
|
try {
|
|
22
|
-
require
|
|
33
|
+
Dex = require('@pkmn/dex').Dex;
|
|
23
34
|
} catch (e) {
|
|
24
35
|
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
25
|
-
|
|
36
|
+
missing.push(`@pkmn/dex@${packageJSON.dependencies['@pkmn/data']}`);
|
|
26
37
|
}
|
|
38
|
+
}
|
|
27
39
|
|
|
40
|
+
if (missing.length) {
|
|
28
41
|
if (+/^v(\d+)/.exec(process.version)[1] < 16) {
|
|
29
42
|
console.error('pkmn-debug requires Node v16+');
|
|
30
43
|
process.exit(1);
|
|
31
44
|
}
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
execFileSync('npm', ['install', ...missing, '--global'], {encoding: 'utf8'});
|
|
47
|
+
|
|
48
|
+
const node_modules = execFileSync('npm', ['root', '--global'], {encoding: 'utf8'}).trim();
|
|
49
|
+
if (!process.env.NODE_PATH || !process.env.NODE_PATH.includes(node_modules)) {
|
|
50
|
+
console.error(`NODE_PATH must include npm's global node_modules directory: '${node_modules}'`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
34
53
|
}
|
|
35
54
|
|
|
55
|
+
const {Generations} = require('@pkmn/data');
|
|
56
|
+
|
|
57
|
+
const {run} = require('../../build/tools/debug');
|
|
58
|
+
|
|
36
59
|
run(new Generations(Dex)).catch(err => {
|
|
37
60
|
console.error(err.message);
|
|
38
61
|
process.exit(1);
|
package/src/lib/binding/node.zig
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const pkmn = @import("../pkmn.zig");
|
|
3
3
|
|
|
4
|
+
const js = @import("../common/js.zig");
|
|
5
|
+
|
|
4
6
|
const assert = std.debug.assert;
|
|
5
7
|
|
|
6
8
|
const c = @cImport({
|
|
@@ -10,38 +12,49 @@ const c = @cImport({
|
|
|
10
12
|
|
|
11
13
|
export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
|
|
12
14
|
const properties = [_]c.napi_property_descriptor{
|
|
13
|
-
Property.init("
|
|
14
|
-
Property.init("bindings", .{ .value = bindings(env) }),
|
|
15
|
+
js.Property.init("engine", .{ .value = register(env) }),
|
|
15
16
|
};
|
|
16
17
|
assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
|
|
17
18
|
return exports;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
pub fn register(env: c.napi_env) c.napi_value {
|
|
22
|
+
var object = js.Object.init(env);
|
|
23
|
+
const properties = [_]c.napi_property_descriptor{
|
|
24
|
+
js.Property.init("options", .{ .value = options(env) }),
|
|
25
|
+
js.Property.init("bindings", .{ .value = bindings(env) }),
|
|
26
|
+
};
|
|
27
|
+
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
28
|
+
return object;
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
fn options(env: c.napi_env) c.napi_value {
|
|
21
|
-
var object = Object.init(env);
|
|
32
|
+
var object = js.Object.init(env);
|
|
22
33
|
const properties = [_]c.napi_property_descriptor{
|
|
23
|
-
Property.init("showdown", .{ .value = Boolean.init(env, pkmn.options.showdown) }),
|
|
24
|
-
Property.init("
|
|
34
|
+
js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
|
|
35
|
+
js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
|
|
36
|
+
js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
|
|
37
|
+
js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
|
|
25
38
|
};
|
|
26
39
|
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
27
40
|
return object;
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
fn bindings(env: c.napi_env) c.napi_value {
|
|
31
|
-
var array = Array.init(env, .{ .length = 1 });
|
|
32
|
-
Array.set(env, array, 0, bind(env, pkmn.gen1));
|
|
44
|
+
var array = js.Array.init(env, .{ .length = 1 });
|
|
45
|
+
js.Array.set(env, array, 0, bind(env, pkmn.gen1));
|
|
33
46
|
return array;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
fn bind(env: c.napi_env, gen: anytype) c.napi_value {
|
|
37
|
-
const
|
|
38
|
-
const logs_size = @intCast(
|
|
39
|
-
var object = Object.init(env);
|
|
50
|
+
const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
|
|
51
|
+
const logs_size: u32 = @intCast(gen.LOGS_SIZE);
|
|
52
|
+
var object = js.Object.init(env);
|
|
40
53
|
const properties = [_]c.napi_property_descriptor{
|
|
41
|
-
Property.init("
|
|
42
|
-
Property.init("LOGS_SIZE", .{ .value = Number.init(env, logs_size) }),
|
|
43
|
-
Property.init("update", .{ .method = update(gen) }),
|
|
44
|
-
Property.init("choices", .{ .method = choices(gen) }),
|
|
54
|
+
js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
|
|
55
|
+
js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
|
|
56
|
+
js.Property.init("update", .{ .method = update(gen) }),
|
|
57
|
+
js.Property.init("choices", .{ .method = choices(gen) }),
|
|
45
58
|
};
|
|
46
59
|
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
47
60
|
return object;
|
|
@@ -61,28 +74,32 @@ fn update(gen: anytype) c.napi_callback {
|
|
|
61
74
|
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
62
75
|
assert(data != null);
|
|
63
76
|
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const c2 = @bitCast(pkmn.Choice, Number.get(env, argv[2], u8));
|
|
77
|
+
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
78
|
+
const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
|
|
79
|
+
const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
|
|
68
80
|
|
|
69
81
|
var vtype: c.napi_valuetype = undefined;
|
|
70
82
|
assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
|
|
71
83
|
const result = switch (vtype) {
|
|
72
|
-
c.napi_undefined, c.napi_null => battle.update(c1, c2,
|
|
84
|
+
c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
|
|
73
85
|
else => result: {
|
|
74
86
|
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
75
87
|
assert(len == gen.LOGS_SIZE);
|
|
76
88
|
assert(data != null);
|
|
77
89
|
|
|
78
|
-
var buf = @
|
|
90
|
+
var buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
|
|
79
91
|
var stream = pkmn.protocol.ByteStream{ .buffer = buf };
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
// TODO: extract out
|
|
93
|
+
var opts = pkmn.battle.options(
|
|
94
|
+
pkmn.protocol.FixedLog{ .writer = stream.writer() },
|
|
95
|
+
gen.chance.NULL,
|
|
96
|
+
gen.calc.NULL,
|
|
97
|
+
);
|
|
98
|
+
break :result battle.update(c1, c2, &opts);
|
|
82
99
|
},
|
|
83
100
|
} catch unreachable;
|
|
84
101
|
|
|
85
|
-
return Number.init(env, @
|
|
102
|
+
return js.Number.init(env, @as(u8, @bitCast(result)));
|
|
86
103
|
}
|
|
87
104
|
}.call;
|
|
88
105
|
}
|
|
@@ -101,129 +118,17 @@ fn choices(gen: anytype) c.napi_callback {
|
|
|
101
118
|
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
102
119
|
assert(data != null);
|
|
103
120
|
|
|
104
|
-
var
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const player = @intToEnum(pkmn.Player, Number.get(env, argv[1], u8));
|
|
108
|
-
const request = @intToEnum(pkmn.Choice.Type, Number.get(env, argv[2], u8));
|
|
121
|
+
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
122
|
+
const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
|
|
123
|
+
const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
|
|
109
124
|
|
|
110
125
|
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
111
|
-
assert(len == gen.
|
|
126
|
+
assert(len == gen.CHOICES_SIZE);
|
|
112
127
|
assert(data != null);
|
|
113
128
|
|
|
114
|
-
var out = @
|
|
129
|
+
var out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
|
|
115
130
|
const n = battle.choices(player, request, out);
|
|
116
|
-
return Number.init(env, @
|
|
131
|
+
return js.Number.init(env, @as(u8, @bitCast(n)));
|
|
117
132
|
}
|
|
118
133
|
}.call;
|
|
119
134
|
}
|
|
120
|
-
|
|
121
|
-
const Array = struct {
|
|
122
|
-
fn init(env: c.napi_env, o: struct { length: ?usize }) c.napi_value {
|
|
123
|
-
var result: c.napi_value = undefined;
|
|
124
|
-
assert(c.napi_ok == if (o.length) |n|
|
|
125
|
-
c.napi_create_array_with_length(env, n, &result)
|
|
126
|
-
else
|
|
127
|
-
c.napi_create_array(env, &result));
|
|
128
|
-
return result;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
fn set(env: c.napi_env, array: c.napi_value, index: u32, value: c.napi_value) void {
|
|
132
|
-
assert(c.napi_set_element(env, array, index, value) == c.napi_ok);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const Boolean = struct {
|
|
137
|
-
fn init(env: c.napi_env, value: bool) c.napi_value {
|
|
138
|
-
var result: c.napi_value = undefined;
|
|
139
|
-
assert(c.napi_get_boolean(env, value, &result) == c.napi_ok);
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const Number = struct {
|
|
145
|
-
fn init(env: c.napi_env, value: anytype) c.napi_value {
|
|
146
|
-
const T = @TypeOf(value);
|
|
147
|
-
var result: c.napi_value = undefined;
|
|
148
|
-
assert(c.napi_ok == switch (@typeInfo(T)) {
|
|
149
|
-
.Int => |info| switch (info.bits) {
|
|
150
|
-
0...32 => switch (info.signedness) {
|
|
151
|
-
.signed => c.napi_create_int32(env, @as(i32, value), &result),
|
|
152
|
-
.unsigned => c.napi_create_uint32(env, @as(u32, value), &result),
|
|
153
|
-
},
|
|
154
|
-
33...52 => c.napi_create_int64(env, @as(i64, value), &result),
|
|
155
|
-
else => @compileError("int can't be represented as JS number"),
|
|
156
|
-
},
|
|
157
|
-
else => @compileError("expected number, got: " ++ @typeName(T)),
|
|
158
|
-
});
|
|
159
|
-
return result;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
fn get(env: c.napi_env, value: c.napi_value, comptime T: type) T {
|
|
163
|
-
switch (@typeInfo(T)) {
|
|
164
|
-
.Int => |info| switch (info.bits) {
|
|
165
|
-
0...32 => switch (info.signedness) {
|
|
166
|
-
.signed => {
|
|
167
|
-
var result: i32 = undefined;
|
|
168
|
-
assert(c.napi_get_value_int32(env, value, &result) == c.napi_ok);
|
|
169
|
-
return if (info.bits == 32) result else @intCast(T, result);
|
|
170
|
-
},
|
|
171
|
-
.unsigned => {
|
|
172
|
-
var result: u32 = undefined;
|
|
173
|
-
assert(c.napi_get_value_uint32(env, value, &result) == c.napi_ok);
|
|
174
|
-
return if (info.bits == 32) result else @intCast(T, result);
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
33...63 => {
|
|
178
|
-
var result: i64 = undefined;
|
|
179
|
-
assert(c.napi_get_value_int64(env, value, &result) == c.napi_ok);
|
|
180
|
-
return @intCast(T, result);
|
|
181
|
-
},
|
|
182
|
-
else => {
|
|
183
|
-
var result: i64 = undefined;
|
|
184
|
-
assert(c.napi_get_value_int64(env, value, &result) == c.napi_ok);
|
|
185
|
-
return switch (info.signedness) {
|
|
186
|
-
.signed => @as(T, value),
|
|
187
|
-
.unsigned => if (0 <= value) @intCast(T, value) else unreachable,
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
else => @compileError("expected number, got: " ++ @typeName(T)),
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const Object = struct {
|
|
197
|
-
fn init(env: c.napi_env) c.napi_value {
|
|
198
|
-
var result: c.napi_value = undefined;
|
|
199
|
-
assert(c.napi_create_object(env, &result) == c.napi_ok);
|
|
200
|
-
return result;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const Property = union(enum) {
|
|
205
|
-
method: c.napi_callback,
|
|
206
|
-
value: c.napi_value,
|
|
207
|
-
|
|
208
|
-
fn init(comptime name: [:0]const u8, property: Property) c.napi_property_descriptor {
|
|
209
|
-
return .{
|
|
210
|
-
.utf8name = name,
|
|
211
|
-
.name = null,
|
|
212
|
-
.method = switch (property) {
|
|
213
|
-
.method => |m| m,
|
|
214
|
-
.value => null,
|
|
215
|
-
},
|
|
216
|
-
.getter = null,
|
|
217
|
-
.setter = null,
|
|
218
|
-
.value = switch (property) {
|
|
219
|
-
.method => null,
|
|
220
|
-
.value => |v| v,
|
|
221
|
-
},
|
|
222
|
-
.attributes = switch (property) {
|
|
223
|
-
.method => c.napi_default,
|
|
224
|
-
.value => c.napi_enumerable,
|
|
225
|
-
},
|
|
226
|
-
.data = null,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
};
|