@pkmn/engine 0.1.0-dev.724afcb1 → 0.1.0-dev.72514829
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 +7 -8
- package/build.zig +112 -37
- package/package.json +13 -13
- package/src/lib/bindings/c.zig +4 -3
- package/src/lib/bindings/wasm.zig +3 -2
- package/src/lib/common/debug.zig +14 -3
- package/src/lib/common/optional.zig +20 -10
- package/src/lib/common/util.zig +21 -1
- package/src/lib/gen1/chance.zig +42 -18
- package/src/lib/gen1/helpers.zig +24 -14
- package/src/lib/gen2/chance.zig +29 -11
- package/src/lib/gen2/helpers.zig +25 -15
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</a>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
|
-
> [!WARNING]
|
|
16
|
+
> [!WARNING]
|
|
17
17
|
> **This project is under heavy development** and currently the `main` branch contains numerous breaking
|
|
18
18
|
> changes which may not work and which are not fully documented. Please **wait for the forthcoming
|
|
19
19
|
> initial v0.1 release** before depending on this project.
|
|
@@ -57,11 +57,7 @@ $ zig build --prefix /usr/local -Doptimize=ReleaseFast
|
|
|
57
57
|
The Zig website has [installation instructions](https://ziglang.org/learn/getting-started/) which
|
|
58
58
|
walk through how to install Zig on each platform - the engine code should work on Zig v0.16.0,
|
|
59
59
|
though tracks Zig's master branch so this may change in the future if breaking language changes are
|
|
60
|
-
introduced.
|
|
61
|
-
compiler](https://github.com/ziglang/zig/issues/17768), building a version of [Zig from
|
|
62
|
-
source](https://codeberg.org/ziglang/zig#building-from-source) after having applied a
|
|
63
|
-
[patch](zig.patch) to revert [ziglang/zig#17391](https://github.com/ziglang/zig/pull/17391) is
|
|
64
|
-
recommended for performance**.
|
|
60
|
+
introduced.
|
|
65
61
|
|
|
66
62
|
`libpkmn` can be built with `-Dshowdown` to instead produce the Pokémon Showdown compatible
|
|
67
63
|
`libpkmn-showdown` library. Furthermore, protocol message logging can be enabled through `-Dlog`.
|
|
@@ -84,8 +80,11 @@ lifecycle script](https://docs.npmjs.com/cli/v8/using-npm/scripts) runs
|
|
|
84
80
|
it can't find one, as well as looking for (and downloading, if necessary) the required Node headers
|
|
85
81
|
needed to successfully build the addons natively.
|
|
86
82
|
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
> [!IMPORTANT]
|
|
84
|
+
> If you have configured `npm` to `--ignore-scripts` (or are using [npm v12+ and have not
|
|
85
|
+
> approved](https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/)
|
|
86
|
+
> `@pkmn/engine` via `npm approve-scripts @pkmn/engine`) **you must either run `npx install-pkmn-engine`
|
|
87
|
+
> directly or build the addons manually and place the artifacts in the expected paths.**
|
|
89
88
|
|
|
90
89
|
### `pkmn`
|
|
91
90
|
|
package/build.zig
CHANGED
|
@@ -55,7 +55,10 @@ pub fn build(b: *std.Build) !void {
|
|
|
55
55
|
const emit_asm = b.option(bool, "emit-asm", "Output .s (assembly code)") orelse false;
|
|
56
56
|
const emit_ll = b.option(bool, "emit-ll", "Output .ll (LLVM IR)") orelse false;
|
|
57
57
|
|
|
58
|
-
const cmd =
|
|
58
|
+
const cmd = if (@hasDecl(std.Build, "FindProgramOptions"))
|
|
59
|
+
b.findProgram(.{ .names = &.{"strip"} })
|
|
60
|
+
else
|
|
61
|
+
b.findProgram(&.{"strip"}, &.{}) catch null;
|
|
59
62
|
|
|
60
63
|
const json = @embedFile("package.json");
|
|
61
64
|
var parsed = try std.json.parseFromSlice(std.json.Value, b.allocator, json, .{});
|
|
@@ -109,7 +112,8 @@ pub fn build(b: *std.Build) !void {
|
|
|
109
112
|
const translate_c = b.addTranslateC(.{
|
|
110
113
|
.root_source_file = b.path("src/lib/napi.h"),
|
|
111
114
|
.target = target,
|
|
112
|
-
|
|
115
|
+
// TODO: workaround for ziglang/zig#35515
|
|
116
|
+
.optimize = .Debug,
|
|
113
117
|
});
|
|
114
118
|
translate_c.addSystemIncludePath(b.path(headers));
|
|
115
119
|
const addon = b.fmt("{s}.node", .{name});
|
|
@@ -205,7 +209,9 @@ pub fn build(b: *std.Build) !void {
|
|
|
205
209
|
b.fmt("{s}.ll", .{name}),
|
|
206
210
|
).step);
|
|
207
211
|
}
|
|
208
|
-
b.
|
|
212
|
+
const install = b.addInstallArtifact(lib, .{});
|
|
213
|
+
b.getInstallStep().dependOn(&install.step);
|
|
214
|
+
maybeRanlib(b, lib, &install.step);
|
|
209
215
|
c = true;
|
|
210
216
|
}
|
|
211
217
|
|
|
@@ -214,34 +220,45 @@ pub fn build(b: *std.Build) !void {
|
|
|
214
220
|
const header = b.addInstallFileWithDir(path, .header, "pkmn.h");
|
|
215
221
|
b.getInstallStep().dependOn(&header.step);
|
|
216
222
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
const file = try std.Io.Dir.path.relative(
|
|
220
|
-
b.allocator,
|
|
221
|
-
cwd,
|
|
222
|
-
&b.graph.environ_map,
|
|
223
|
-
cwd,
|
|
224
|
-
try b.cache_root.join(b.allocator, &.{pc}),
|
|
225
|
-
);
|
|
226
|
-
const pkgconfig_file = try std.Io.Dir.cwd().createFile(b.graph.io, file, .{});
|
|
227
|
-
defer pkgconfig_file.close(b.graph.io);
|
|
228
|
-
|
|
229
|
-
const dirname = comptime std.fs.path.dirname(@src().file) orelse ".";
|
|
230
|
-
var writer = pkgconfig_file.writer(b.graph.io, &.{});
|
|
231
|
-
try writer.interface.print(
|
|
232
|
-
\\prefix={0s}/{1s}
|
|
223
|
+
const content = try std.fmt.allocPrint(b.allocator,
|
|
224
|
+
\\prefix=${{pcfiledir}}/../..
|
|
233
225
|
\\includedir=${{prefix}}/include
|
|
234
226
|
\\libdir=${{prefix}}/lib
|
|
235
227
|
\\
|
|
236
|
-
\\Name: lib{
|
|
237
|
-
\\URL: https://github.com/{
|
|
238
|
-
\\Description: {
|
|
239
|
-
\\Version: {
|
|
228
|
+
\\Name: lib{0s}
|
|
229
|
+
\\URL: https://github.com/{1s}
|
|
230
|
+
\\Description: {2s}
|
|
231
|
+
\\Version: {3s}
|
|
240
232
|
\\Cflags: -I${{includedir}}
|
|
241
|
-
\\Libs: -L${{libdir}} -l{
|
|
242
|
-
, .{
|
|
233
|
+
\\Libs: -L${{libdir}} -l{0s}
|
|
234
|
+
, .{ name, repository.next().?, description, version });
|
|
243
235
|
|
|
244
|
-
|
|
236
|
+
const pc = b.fmt("lib{s}.pc", .{name});
|
|
237
|
+
if (@hasDecl(std.Build, "FindProgramOptions")) {
|
|
238
|
+
const write_file = b.addWriteFiles();
|
|
239
|
+
const pkgconfig = write_file.add(pc, content);
|
|
240
|
+
b.getInstallStep().dependOn(&b.addInstallFileWithDir(
|
|
241
|
+
pkgconfig,
|
|
242
|
+
.prefix,
|
|
243
|
+
b.fmt("share/pkgconfig/{s}", .{pc}),
|
|
244
|
+
).step);
|
|
245
|
+
} else {
|
|
246
|
+
const cwd = try std.process.currentPathAlloc(b.graph.io, b.allocator);
|
|
247
|
+
const file = try std.Io.Dir.path.relative(
|
|
248
|
+
b.allocator,
|
|
249
|
+
cwd,
|
|
250
|
+
&b.graph.environ_map,
|
|
251
|
+
cwd,
|
|
252
|
+
try b.cache_root.join(b.allocator, &.{pc}),
|
|
253
|
+
);
|
|
254
|
+
const pkgconfig = try std.Io.Dir.cwd().createFile(b.graph.io, file, .{});
|
|
255
|
+
defer pkgconfig.close(b.graph.io);
|
|
256
|
+
|
|
257
|
+
var writer = pkgconfig.writer(b.graph.io, &.{});
|
|
258
|
+
try writer.interface.writeAll(content);
|
|
259
|
+
|
|
260
|
+
b.installFile(file, b.fmt("share/pkgconfig/{s}", .{pc}));
|
|
261
|
+
}
|
|
245
262
|
}
|
|
246
263
|
|
|
247
264
|
const config: Config = .{
|
|
@@ -294,8 +311,8 @@ pub fn build(b: *std.Build) !void {
|
|
|
294
311
|
if (dump) |t| b.step("dump", "Run protocol dump tool").dependOn(&t.step);
|
|
295
312
|
if (fuzz) |t| b.step("fuzz", "Run fuzz tester").dependOn(&t.step);
|
|
296
313
|
if (serde) |t| b.step("serde", "Run serialization/deserialization tool").dependOn(&t.step);
|
|
297
|
-
b.step("test", "Run all tests").dependOn(
|
|
298
|
-
b.step("tools", "Install tools").dependOn(
|
|
314
|
+
b.step("test", "Run all tests").dependOn(tests.step);
|
|
315
|
+
b.step("tools", "Install tools").dependOn(ToolsStep.create(b, &exes).step);
|
|
299
316
|
if (transitions) |t| {
|
|
300
317
|
b.step("transitions", "Visualize transitions algorithm search").dependOn(&t.step);
|
|
301
318
|
}
|
|
@@ -365,7 +382,12 @@ fn buildWasm(
|
|
|
365
382
|
exe.stack_size = wasm_stack_size;
|
|
366
383
|
exe.root_module.addOptions("build_options", options);
|
|
367
384
|
|
|
368
|
-
const opt =
|
|
385
|
+
const opt = if (@hasDecl(std.Build, "FindProgramOptions")) blk: {
|
|
386
|
+
if (exists(b, "./node_modules/.bin/wasm-opt") catch false) {
|
|
387
|
+
break :blk "./node_modules/.bin/wasm-opt";
|
|
388
|
+
}
|
|
389
|
+
break :blk b.findProgram(.{ .names = &.{"wasm-opt"} });
|
|
390
|
+
} else b.findProgram(&.{"wasm-opt"}, &.{"./node_modules/.bin"}) catch null;
|
|
369
391
|
if (optimize != .Debug and opt != null) {
|
|
370
392
|
const out = b.fmt("build/lib/{s}.wasm", .{name});
|
|
371
393
|
const sh = b.addSystemCommand(&.{ opt.?, "--enable-bulk-memory", "--enable-simd", "-O4" });
|
|
@@ -407,7 +429,7 @@ const Config = struct {
|
|
|
407
429
|
};
|
|
408
430
|
|
|
409
431
|
const TestStep = struct {
|
|
410
|
-
step: std.Build.Step,
|
|
432
|
+
step: *std.Build.Step,
|
|
411
433
|
build: bool,
|
|
412
434
|
|
|
413
435
|
pub fn create(b: *std.Build, options: *std.Build.Step.Options, config: Config) *TestStep {
|
|
@@ -416,8 +438,6 @@ const TestStep = struct {
|
|
|
416
438
|
b.option([]const u8, "test-filter", "Skip tests that do not match filter");
|
|
417
439
|
|
|
418
440
|
const self = b.allocator.create(TestStep) catch @panic("OOM");
|
|
419
|
-
const step = std.Build.Step.init(.{ .id = .custom, .name = "Run all tests", .owner = b });
|
|
420
|
-
self.* = .{ .step = step, .build = test_filter == null };
|
|
421
441
|
|
|
422
442
|
const path = b.path("src/lib/test.zig");
|
|
423
443
|
const tests = b.addTest(.{
|
|
@@ -437,7 +457,20 @@ const TestStep = struct {
|
|
|
437
457
|
if (coverage) |c| {
|
|
438
458
|
tests.setExecCmd(&.{ "kcov", "--include-pattern=src/lib", c, null });
|
|
439
459
|
}
|
|
440
|
-
|
|
460
|
+
|
|
461
|
+
if (@hasDecl(std.Build, "FindProgramOptions")) {
|
|
462
|
+
const run_step = b.addRunArtifact(tests);
|
|
463
|
+
self.* = .{ .step = &run_step.step, .build = test_filter == null };
|
|
464
|
+
} else {
|
|
465
|
+
const custom_step = b.allocator.create(std.Build.Step) catch @panic("OOM");
|
|
466
|
+
custom_step.* = std.Build.Step.init(.{
|
|
467
|
+
.id = .custom,
|
|
468
|
+
.name = "Run all tests",
|
|
469
|
+
.owner = b,
|
|
470
|
+
});
|
|
471
|
+
custom_step.dependOn(&b.addRunArtifact(tests).step);
|
|
472
|
+
self.* = .{ .step = custom_step, .build = test_filter == null };
|
|
473
|
+
}
|
|
441
474
|
|
|
442
475
|
return self;
|
|
443
476
|
}
|
|
@@ -482,7 +515,11 @@ fn tool(b: *std.Build, path: []const u8, config: ToolConfig) !?*std.Build.Step.R
|
|
|
482
515
|
|
|
483
516
|
const run = b.addRunArtifact(exe);
|
|
484
517
|
maybeStrip(b, exe, &run.step, config.general.strip, config.general.cmd);
|
|
485
|
-
if (
|
|
518
|
+
if (@hasDecl(std.Build, "FindProgramOptions")) {
|
|
519
|
+
run.addPassthruArgs();
|
|
520
|
+
} else {
|
|
521
|
+
if (b.args) |args| run.addArgs(args);
|
|
522
|
+
}
|
|
486
523
|
|
|
487
524
|
return run;
|
|
488
525
|
}
|
|
@@ -496,13 +533,33 @@ fn exists(b: *std.Build, path: []const u8) !bool {
|
|
|
496
533
|
}
|
|
497
534
|
|
|
498
535
|
const ToolsStep = struct {
|
|
499
|
-
step: std.Build.Step,
|
|
536
|
+
step: *std.Build.Step,
|
|
500
537
|
|
|
501
538
|
pub fn create(b: *std.Build, exes: *ArrayList(*std.Build.Step.Compile)) *ToolsStep {
|
|
502
539
|
const self = b.allocator.create(ToolsStep) catch @panic("OOM");
|
|
503
|
-
|
|
504
|
-
|
|
540
|
+
|
|
541
|
+
if (@hasDecl(std.Build, "FindProgramOptions")) {
|
|
542
|
+
const step = b.allocator.create(std.Build.Step.TopLevel) catch @panic("OOM");
|
|
543
|
+
step.* = .{
|
|
544
|
+
.step = std.Build.Step.init(.{
|
|
545
|
+
.tag = .top_level,
|
|
546
|
+
.name = "Install tools",
|
|
547
|
+
.owner = b,
|
|
548
|
+
}),
|
|
549
|
+
.description = "Install tools",
|
|
550
|
+
};
|
|
551
|
+
self.* = .{ .step = &step.step };
|
|
552
|
+
} else {
|
|
553
|
+
const step = b.allocator.create(std.Build.Step) catch @panic("OOM");
|
|
554
|
+
step.* = std.Build.Step.init(.{
|
|
555
|
+
.id = .custom,
|
|
556
|
+
.name = "Install tools",
|
|
557
|
+
.owner = b,
|
|
558
|
+
});
|
|
559
|
+
self.* = .{ .step = step };
|
|
560
|
+
}
|
|
505
561
|
for (exes.items) |t| self.step.dependOn(&b.addInstallArtifact(t, .{}).step);
|
|
562
|
+
|
|
506
563
|
return self;
|
|
507
564
|
}
|
|
508
565
|
};
|
|
@@ -524,3 +581,21 @@ pub fn exports(b: *std.Build, bytes: []const u8) ![][]const u8 {
|
|
|
524
581
|
|
|
525
582
|
return symbols.items;
|
|
526
583
|
}
|
|
584
|
+
|
|
585
|
+
fn maybeRanlib(
|
|
586
|
+
b: *std.Build,
|
|
587
|
+
artifact: *std.Build.Step.Compile,
|
|
588
|
+
install_step: *std.Build.Step,
|
|
589
|
+
) void {
|
|
590
|
+
if (builtin.os.tag != .macos) return;
|
|
591
|
+
if (artifact.linkage != .static) return;
|
|
592
|
+
|
|
593
|
+
const ranlib = if (comptime @hasDecl(std.Build, "FindProgramOptions"))
|
|
594
|
+
b.findProgram(.{ .names = &.{"ranlib"} }) orelse return
|
|
595
|
+
else
|
|
596
|
+
b.findProgram(&[_][]const u8{"ranlib"}, &[_][]const u8{}) catch return;
|
|
597
|
+
|
|
598
|
+
const sh = b.addSystemCommand(&[_][]const u8{ranlib});
|
|
599
|
+
sh.addFileArg(artifact.getEmittedBin());
|
|
600
|
+
install_step.dependOn(&sh.step);
|
|
601
|
+
}
|
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.72514829",
|
|
4
4
|
"description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
|
|
5
5
|
"repository": "github:pkmn/engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -100,12 +100,12 @@
|
|
|
100
100
|
},
|
|
101
101
|
"dependencies": {
|
|
102
102
|
"@pkmn/data": "0.9.35",
|
|
103
|
-
"@pkmn/protocol": "^0.7.
|
|
103
|
+
"@pkmn/protocol": "^0.7.3"
|
|
104
104
|
},
|
|
105
105
|
"devDependencies": {
|
|
106
106
|
"@pkmn/dex-types": "0.9.35",
|
|
107
|
-
"@pkmn/eslint-config": "^9.
|
|
108
|
-
"@pkmn/img": "^0.3.
|
|
107
|
+
"@pkmn/eslint-config": "^9.28.0",
|
|
108
|
+
"@pkmn/img": "^0.3.4",
|
|
109
109
|
"@pkmn/sets": "^5.2.0",
|
|
110
110
|
"@pkmn/sim": "0.9.31",
|
|
111
111
|
"@pkmn/smogon": "0.5.21",
|
|
@@ -113,25 +113,25 @@
|
|
|
113
113
|
"@types/jest": "^30.0.0",
|
|
114
114
|
"@types/minimist": "^1.2.5",
|
|
115
115
|
"@types/mustache": "^4.2.6",
|
|
116
|
-
"@types/node": "^
|
|
117
|
-
"@types/react": "^19.2.
|
|
116
|
+
"@types/node": "^26.0.0",
|
|
117
|
+
"@types/react": "^19.2.17",
|
|
118
118
|
"@types/semver": "^7.7.1",
|
|
119
|
-
"@types/vscode": "^1.
|
|
120
|
-
"@vitest/coverage-v8": "^4.1.
|
|
121
|
-
"@vscode/vsce": "^3.9.
|
|
122
|
-
"binaryen": "^
|
|
123
|
-
"esbuild": "^0.28.
|
|
119
|
+
"@types/vscode": "^1.125.0",
|
|
120
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
121
|
+
"@vscode/vsce": "^3.9.2",
|
|
122
|
+
"binaryen": "^130.0.0",
|
|
123
|
+
"esbuild": "^0.28.1",
|
|
124
124
|
"eslint": "<10",
|
|
125
125
|
"html-minifier": "^4.0.0",
|
|
126
126
|
"json-stringify-pretty-compact": "3.0.0",
|
|
127
127
|
"minimist": "^1.2.8",
|
|
128
128
|
"mustache": "^4.2.0",
|
|
129
|
-
"semver": "^7.
|
|
129
|
+
"semver": "^7.8.5",
|
|
130
130
|
"source-map-support": "^0.5.21",
|
|
131
131
|
"table": "^6.9.0",
|
|
132
132
|
"trakr": "^0.2.0",
|
|
133
133
|
"typescript": "^6.0.3",
|
|
134
|
-
"vitest": "^4.1.
|
|
134
|
+
"vitest": "^4.1.9"
|
|
135
135
|
},
|
|
136
136
|
"scripts": {
|
|
137
137
|
"lint": "eslint --cache src",
|
package/src/lib/bindings/c.zig
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const pkmn = @import("../pkmn.zig");
|
|
2
2
|
const std = @import("std");
|
|
3
|
+
const util = @import("../common/util.zig");
|
|
3
4
|
|
|
4
5
|
const assert = std.debug.assert;
|
|
5
6
|
|
|
@@ -23,7 +24,7 @@ pub const MAX_LOGS = pkmn.MAX_LOGS;
|
|
|
23
24
|
pub const LOGS_SIZE = pkmn.LOGS_SIZE;
|
|
24
25
|
|
|
25
26
|
pub fn choice_init(choice: u8, data: u8) callconv(.c) u8 {
|
|
26
|
-
assert(choice <=
|
|
27
|
+
assert(choice <= util.EnumFieldsLen(pkmn.Choice.Type));
|
|
27
28
|
assert(data <= 6);
|
|
28
29
|
return @bitCast(pkmn.Choice{ .type = @enumFromInt(choice), .data = @intCast(data) });
|
|
29
30
|
}
|
|
@@ -190,8 +191,8 @@ pub fn gen(comptime num: comptime_int) type {
|
|
|
190
191
|
out: [*]u8,
|
|
191
192
|
n: usize,
|
|
192
193
|
) callconv(.c) u8 {
|
|
193
|
-
assert(player <=
|
|
194
|
-
assert(request <=
|
|
194
|
+
assert(player <= util.EnumFieldsLen(pkmn.Player));
|
|
195
|
+
assert(request <= util.EnumFieldsLen(pkmn.Choice.Type));
|
|
195
196
|
|
|
196
197
|
assert(!pkmn.options.showdown or n > 0);
|
|
197
198
|
return battle.choices(@enumFromInt(player), @enumFromInt(request), @ptrCast(out[0..n]));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const pkmn = @import("../pkmn.zig");
|
|
2
2
|
const std = @import("std");
|
|
3
|
+
const util = @import("../common/util.zig");
|
|
3
4
|
|
|
4
5
|
const assert = std.debug.assert;
|
|
5
6
|
|
|
@@ -36,8 +37,8 @@ pub fn gen(comptime num: comptime_int) type {
|
|
|
36
37
|
request: u8,
|
|
37
38
|
buf: [*]u8,
|
|
38
39
|
) callconv(.c) u8 {
|
|
39
|
-
assert(player <=
|
|
40
|
-
assert(request <=
|
|
40
|
+
assert(player <= util.EnumFieldsLen(pkmn.Player));
|
|
41
|
+
assert(request <= util.EnumFieldsLen(pkmn.Choice.Type));
|
|
41
42
|
|
|
42
43
|
const n = CHOICES_SIZE;
|
|
43
44
|
return battle.choices(@enumFromInt(player), @enumFromInt(request), @ptrCast(buf[0..n]));
|
package/src/lib/common/debug.zig
CHANGED
|
@@ -24,9 +24,20 @@ pub fn print(value: anytype) void {
|
|
|
24
24
|
switch (@typeInfo(@TypeOf(value))) {
|
|
25
25
|
.@"struct" => |info| {
|
|
26
26
|
if (info.is_tuple) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (@hasField(@TypeOf(info), "fields")) {
|
|
28
|
+
inline for (info.fields, 0..) |f, i| {
|
|
29
|
+
inspect(fw, @field(value, f.name));
|
|
30
|
+
if (i < info.fields.len - 1) {
|
|
31
|
+
fw.interface.writeAll(" ") catch return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
inline for (info.field_names, 0..) |name, i| {
|
|
36
|
+
inspect(fw, @field(value, name));
|
|
37
|
+
if (i < info.field_names.len - 1) {
|
|
38
|
+
fw.interface.writeAll(" ") catch return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
30
41
|
}
|
|
31
42
|
} else {
|
|
32
43
|
inspect(fw, value);
|
|
@@ -6,21 +6,31 @@ const assert = std.debug.assert;
|
|
|
6
6
|
/// Optimized optional representation which stores the empty None value as a sentinel.
|
|
7
7
|
/// NOTE: ziglang/zig#104
|
|
8
8
|
pub fn Optional(comptime T: type) type {
|
|
9
|
-
const
|
|
10
|
-
.
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const names = if (@hasDecl(std.meta, "fieldNames"))
|
|
10
|
+
std.meta.fieldNames(switch (@typeInfo(T)) {
|
|
11
|
+
.bool => enum { false, true },
|
|
12
|
+
else => T,
|
|
13
|
+
})
|
|
14
|
+
else blk: {
|
|
15
|
+
const fields = std.meta.fields(switch (@typeInfo(T)) {
|
|
16
|
+
.bool => enum { false, true },
|
|
17
|
+
else => T,
|
|
18
|
+
});
|
|
19
|
+
var ns: [fields.len][:0]const u8 = undefined;
|
|
20
|
+
inline for (fields, 0..) |f, i| ns[i] = f.name;
|
|
21
|
+
break :blk &ns;
|
|
22
|
+
};
|
|
13
23
|
|
|
14
|
-
const TagType = std.math.IntFittingRange(0,
|
|
15
|
-
var fieldNames: [
|
|
16
|
-
var fieldValues: [
|
|
24
|
+
const TagType = std.math.IntFittingRange(0, names.len);
|
|
25
|
+
var fieldNames: [names.len + 1][]const u8 = undefined;
|
|
26
|
+
var fieldValues: [names.len + 1]TagType = undefined;
|
|
17
27
|
|
|
18
28
|
fieldNames[0] = "None";
|
|
19
29
|
fieldValues[0] = 0;
|
|
20
30
|
|
|
21
|
-
inline for (
|
|
22
|
-
assert(!std.mem.eql(u8,
|
|
23
|
-
fieldNames[i] =
|
|
31
|
+
inline for (names, 1..) |name, i| {
|
|
32
|
+
assert(!std.mem.eql(u8, name, "None"));
|
|
33
|
+
fieldNames[i] = name;
|
|
24
34
|
fieldValues[i] = i;
|
|
25
35
|
}
|
|
26
36
|
|
package/src/lib/common/util.zig
CHANGED
|
@@ -4,7 +4,12 @@ const expect = std.testing.expect;
|
|
|
4
4
|
const expectEqual = std.testing.expectEqual;
|
|
5
5
|
|
|
6
6
|
pub fn PointerType(comptime P: type, comptime C: type) type {
|
|
7
|
-
|
|
7
|
+
const ptr_info = @typeInfo(P).pointer;
|
|
8
|
+
const is_const = if (@hasField(@TypeOf(ptr_info), "attrs"))
|
|
9
|
+
ptr_info.attrs.@"const"
|
|
10
|
+
else
|
|
11
|
+
ptr_info.is_const;
|
|
12
|
+
return if (is_const) *const C else *C;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
test PointerType {
|
|
@@ -12,6 +17,21 @@ test PointerType {
|
|
|
12
17
|
try expectEqual(*const f64, PointerType(*const i32, f64));
|
|
13
18
|
}
|
|
14
19
|
|
|
20
|
+
pub fn EnumFieldsLen(comptime E: type) usize {
|
|
21
|
+
const info = @typeInfo(E).@"enum";
|
|
22
|
+
return if (@hasField(@TypeOf(info), "fields"))
|
|
23
|
+
info.fields.len
|
|
24
|
+
else
|
|
25
|
+
info.field_names.len;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test EnumFieldsLen {
|
|
29
|
+
const E1 = enum { A, B, C };
|
|
30
|
+
const E2 = enum(u8) { A = 0, B = 10, C = 20, D = 30 };
|
|
31
|
+
try expectEqual(3, EnumFieldsLen(E1));
|
|
32
|
+
try expectEqual(4, EnumFieldsLen(E2));
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
pub fn isPointerTo(p: anytype, comptime P: type) bool {
|
|
16
36
|
const info = @typeInfo(@TypeOf(p));
|
|
17
37
|
return switch (info) {
|
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -45,17 +45,31 @@ pub const Actions = extern struct {
|
|
|
45
45
|
|
|
46
46
|
/// Returns true if `a` is equal to `b`.
|
|
47
47
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
48
|
-
return @as(
|
|
48
|
+
return @as(u64, @bitCast(a.p1)) == @as(u64, @bitCast(b.p1)) and
|
|
49
|
+
@as(u64, @bitCast(a.p2)) == @as(u64, @bitCast(b.p2));
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/// Returns true if `a` has the same "shape" as `b`, where `Actions` are defined to have the
|
|
52
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
53
54
|
/// set to the same value).
|
|
54
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
55
|
-
inline for (@typeInfo(Actions).@"struct"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
57
|
+
@typeInfo(Actions).@"struct".fields
|
|
58
|
+
else
|
|
59
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
60
|
+
{
|
|
61
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
62
|
+
player.name
|
|
63
|
+
else
|
|
64
|
+
player;
|
|
65
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
66
|
+
@typeInfo(Action).@"struct".fields
|
|
67
|
+
else
|
|
68
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
69
|
+
{
|
|
70
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
71
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
72
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
59
73
|
|
|
60
74
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
61
75
|
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
@@ -216,31 +230,36 @@ pub const Action = packed struct(u64) {
|
|
|
216
230
|
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
217
231
|
try writer.writeByte('(');
|
|
218
232
|
var printed = false;
|
|
219
|
-
inline for (@typeInfo(Action).@"struct"
|
|
220
|
-
|
|
233
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
234
|
+
@typeInfo(Action).@"struct".fields
|
|
235
|
+
else
|
|
236
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
237
|
+
{
|
|
238
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
239
|
+
const val = @field(self, field_name);
|
|
221
240
|
switch (@typeInfo(@TypeOf(val))) {
|
|
222
241
|
.@"enum" => if (val != .None) {
|
|
223
242
|
if (printed) try writer.writeAll(", ");
|
|
224
243
|
if (shape) {
|
|
225
|
-
try writer.print("{s}:?", .{
|
|
244
|
+
try writer.print("{s}:?", .{field_name});
|
|
226
245
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
227
246
|
try writer.print("{s}{s}", .{
|
|
228
247
|
if (val == .false) "!" else "",
|
|
229
|
-
|
|
248
|
+
field_name,
|
|
230
249
|
});
|
|
231
250
|
} else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
|
|
232
|
-
try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1],
|
|
251
|
+
try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field_name });
|
|
233
252
|
} else {
|
|
234
|
-
try writer.print("{s}:{s}", .{
|
|
253
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
235
254
|
}
|
|
236
255
|
printed = true;
|
|
237
256
|
},
|
|
238
257
|
.int => if (val != 0) {
|
|
239
258
|
if (printed) try writer.writeAll(", ");
|
|
240
259
|
if (shape) {
|
|
241
|
-
try writer.print("{s}:?", .{
|
|
260
|
+
try writer.print("{s}:?", .{field_name});
|
|
242
261
|
} else {
|
|
243
|
-
try writer.print("{s}:{d}", .{
|
|
262
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
244
263
|
}
|
|
245
264
|
printed = true;
|
|
246
265
|
},
|
|
@@ -293,20 +312,25 @@ pub const Duration = packed struct(u32) {
|
|
|
293
312
|
) !void {
|
|
294
313
|
try writer.writeByte('(');
|
|
295
314
|
var printed = false;
|
|
296
|
-
inline for (@typeInfo(Duration).@"struct"
|
|
297
|
-
|
|
315
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Duration).@"struct"), "fields"))
|
|
316
|
+
@typeInfo(Duration).@"struct".fields
|
|
317
|
+
else
|
|
318
|
+
@typeInfo(Duration).@"struct".field_names) |field|
|
|
319
|
+
{
|
|
320
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
321
|
+
const val = @field(self, field_name);
|
|
298
322
|
switch (@typeInfo(@TypeOf(val))) {
|
|
299
323
|
.int => if (val != 0) {
|
|
300
324
|
if (printed) try writer.writeAll(", ");
|
|
301
|
-
if (comptime std.mem.eql(u8,
|
|
302
|
-
try writer.print("{s}:[", .{
|
|
325
|
+
if (comptime std.mem.eql(u8, field_name, "sleeps")) {
|
|
326
|
+
try writer.print("{s}:[", .{field_name});
|
|
303
327
|
for (0..6) |i| {
|
|
304
328
|
if (i != 0) try writer.writeByte(',');
|
|
305
329
|
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
306
330
|
}
|
|
307
331
|
try writer.writeAll("]");
|
|
308
332
|
} else {
|
|
309
|
-
try writer.print("{s}:{d}", .{
|
|
333
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
310
334
|
}
|
|
311
335
|
printed = true;
|
|
312
336
|
},
|
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -161,15 +161,20 @@ pub const Pokemon = struct {
|
|
|
161
161
|
var pokemon = data.Pokemon{};
|
|
162
162
|
pokemon.species = p.species;
|
|
163
163
|
const species = Species.get(p.species);
|
|
164
|
-
inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct"
|
|
165
|
-
|
|
164
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(pokemon.stats)).@"struct"), "fields"))
|
|
165
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields
|
|
166
|
+
else
|
|
167
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".field_names) |field|
|
|
168
|
+
{
|
|
169
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
170
|
+
const hp = comptime std.mem.eql(u8, field_name, "hp");
|
|
166
171
|
const spc =
|
|
167
|
-
comptime std.mem.eql(u8,
|
|
168
|
-
@field(pokemon.stats,
|
|
169
|
-
|
|
170
|
-
@field(species.stats,
|
|
171
|
-
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs,
|
|
172
|
-
@field(p.stats,
|
|
172
|
+
comptime std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
|
|
173
|
+
@field(pokemon.stats, field_name) = Stats(u16).calc(
|
|
174
|
+
field_name,
|
|
175
|
+
@field(species.stats, field_name),
|
|
176
|
+
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field_name),
|
|
177
|
+
@field(p.stats, field_name),
|
|
173
178
|
p.level,
|
|
174
179
|
);
|
|
175
180
|
}
|
|
@@ -197,14 +202,19 @@ pub const Pokemon = struct {
|
|
|
197
202
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
198
203
|
var stats: Stats(u16) = .{};
|
|
199
204
|
const dvs = DVs.random(rand);
|
|
200
|
-
inline for (@typeInfo(@TypeOf(stats)).@"struct"
|
|
201
|
-
@
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(stats)).@"struct"), "fields"))
|
|
206
|
+
@typeInfo(@TypeOf(stats)).@"struct".fields
|
|
207
|
+
else
|
|
208
|
+
@typeInfo(@TypeOf(stats)).@"struct".field_names) |field|
|
|
209
|
+
{
|
|
210
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
211
|
+
@field(stats, field_name) = Stats(u16).calc(
|
|
212
|
+
field_name,
|
|
213
|
+
@field(species.stats, field_name),
|
|
214
|
+
if (comptime std.mem.eql(u8, field_name, "hp"))
|
|
205
215
|
dvs.hp()
|
|
206
216
|
else
|
|
207
|
-
@field(dvs,
|
|
217
|
+
@field(dvs, field_name),
|
|
208
218
|
if (rand.chance(u8, 1, 20)) rand.range(u16, 0, EXP + 1) else EXP,
|
|
209
219
|
lvl,
|
|
210
220
|
);
|
package/src/lib/gen2/chance.zig
CHANGED
|
@@ -53,10 +53,23 @@ pub const Actions = extern struct {
|
|
|
53
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
54
54
|
/// set to the same value).
|
|
55
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
56
|
-
inline for (@typeInfo(Actions).@"struct"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
57
|
+
@typeInfo(Actions).@"struct".fields
|
|
58
|
+
else
|
|
59
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
60
|
+
{
|
|
61
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
62
|
+
player.name
|
|
63
|
+
else
|
|
64
|
+
player;
|
|
65
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
66
|
+
@typeInfo(Action).@"struct".fields
|
|
67
|
+
else
|
|
68
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
69
|
+
{
|
|
70
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
71
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
72
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
60
73
|
|
|
61
74
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
62
75
|
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
@@ -188,29 +201,34 @@ pub const Action = packed struct(u128) {
|
|
|
188
201
|
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
189
202
|
try writer.writeByte('(');
|
|
190
203
|
var printed = false;
|
|
191
|
-
inline for (@typeInfo(Action).@"struct"
|
|
192
|
-
|
|
204
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
205
|
+
@typeInfo(Action).@"struct".fields
|
|
206
|
+
else
|
|
207
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
208
|
+
{
|
|
209
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
210
|
+
const val = @field(self, field_name);
|
|
193
211
|
switch (@typeInfo(@TypeOf(val))) {
|
|
194
212
|
.@"enum" => if (val != .None) {
|
|
195
213
|
if (printed) try writer.writeAll(", ");
|
|
196
214
|
if (shape) {
|
|
197
|
-
try writer.print("{s}:?", .{
|
|
215
|
+
try writer.print("{s}:?", .{field_name});
|
|
198
216
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
199
217
|
try writer.print("{s}{s}", .{
|
|
200
218
|
if (val == .false) "!" else "",
|
|
201
|
-
|
|
219
|
+
field_name,
|
|
202
220
|
});
|
|
203
221
|
} else {
|
|
204
|
-
try writer.print("{s}:{s}", .{
|
|
222
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
205
223
|
}
|
|
206
224
|
printed = true;
|
|
207
225
|
},
|
|
208
226
|
.int => if (val != 0) {
|
|
209
227
|
if (printed) try writer.writeAll(", ");
|
|
210
228
|
if (shape) {
|
|
211
|
-
try writer.print("{s}:?", .{
|
|
229
|
+
try writer.print("{s}:?", .{field_name});
|
|
212
230
|
} else {
|
|
213
|
-
try writer.print("{s}:{d}", .{
|
|
231
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
214
232
|
}
|
|
215
233
|
printed = true;
|
|
216
234
|
},
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -140,15 +140,20 @@ pub const Pokemon = struct {
|
|
|
140
140
|
var pokemon = data.Pokemon{};
|
|
141
141
|
pokemon.species = p.species;
|
|
142
142
|
const species = Species.get(p.species);
|
|
143
|
-
inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct"
|
|
144
|
-
|
|
143
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(pokemon.stats)).@"struct"), "fields"))
|
|
144
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields
|
|
145
|
+
else
|
|
146
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".field_names) |field|
|
|
147
|
+
{
|
|
148
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
149
|
+
const hp = comptime std.mem.eql(u8, field_name, "hp");
|
|
145
150
|
const spc =
|
|
146
|
-
comptime std.mem.eql(u8,
|
|
147
|
-
@field(pokemon.stats,
|
|
148
|
-
|
|
149
|
-
@field(species.stats,
|
|
150
|
-
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs,
|
|
151
|
-
@field(p.stats,
|
|
151
|
+
comptime std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
|
|
152
|
+
@field(pokemon.stats, field_name) = Stats(u16).calc(
|
|
153
|
+
field_name,
|
|
154
|
+
@field(species.stats, field_name),
|
|
155
|
+
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field_name),
|
|
156
|
+
@field(p.stats, field_name),
|
|
152
157
|
p.level,
|
|
153
158
|
);
|
|
154
159
|
}
|
|
@@ -181,13 +186,18 @@ pub const Pokemon = struct {
|
|
|
181
186
|
|
|
182
187
|
var stats: Stats(u16) = .{};
|
|
183
188
|
const dvs = gen1.DVs.random(rand);
|
|
184
|
-
inline for (@typeInfo(@TypeOf(stats)).@"struct"
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
@
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(stats)).@"struct"), "fields"))
|
|
190
|
+
@typeInfo(@TypeOf(stats)).@"struct".fields
|
|
191
|
+
else
|
|
192
|
+
@typeInfo(@TypeOf(stats)).@"struct".field_names) |field|
|
|
193
|
+
{
|
|
194
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
195
|
+
const hp = std.mem.eql(u8, field_name, "hp");
|
|
196
|
+
const spc = std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
|
|
197
|
+
@field(stats, field_name) = Stats(u16).calc(
|
|
198
|
+
field_name,
|
|
199
|
+
@field(species.stats, field_name),
|
|
200
|
+
if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field_name),
|
|
191
201
|
if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
|
|
192
202
|
lvl,
|
|
193
203
|
);
|