@pkmn/engine 0.1.0-dev.0cabf16e → 0.1.0-dev.0f9e9648

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.
Files changed (66) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +47 -4
  3. package/build/pkg/addon/browser.d.ts +1 -1
  4. package/build/pkg/addon/browser.js +6 -2
  5. package/build/pkg/addon/browser.js.map +1 -1
  6. package/build/pkg/addon/node.js +22 -8
  7. package/build/pkg/addon/node.js.map +1 -1
  8. package/build/pkg/addon/wasm.d.ts +2 -0
  9. package/build/pkg/addon/wasm.js +53 -0
  10. package/build/pkg/addon/wasm.js.map +1 -0
  11. package/build/pkg/addon.d.ts +24 -4
  12. package/build/pkg/addon.js +8 -17
  13. package/build/pkg/addon.js.map +1 -1
  14. package/build/pkg/data/layout.json +15 -2
  15. package/build/pkg/data/protocol.json +23 -9
  16. package/build/pkg/gen1.d.ts +15 -18
  17. package/build/pkg/gen1.js +28 -19
  18. package/build/pkg/gen1.js.map +1 -1
  19. package/build/pkg/index.d.ts +37 -100
  20. package/build/pkg/index.js +23 -75
  21. package/build/pkg/index.js.map +1 -1
  22. package/build/tools/debug.js +18 -9
  23. package/build/tools/debug.js.map +1 -1
  24. package/build/tools/display/debug.jsx +18 -7
  25. package/build/tools/display/debug.jsx.map +1 -1
  26. package/build/tools/display/index.js +35 -15
  27. package/build/tools/display/index.js.map +1 -1
  28. package/build/tools/display/ui.jsx +17 -7
  29. package/build/tools/display/ui.jsx.map +1 -1
  30. package/build/tools/display/util.js +16 -1
  31. package/build/tools/display/util.js.map +1 -1
  32. package/build.zig +196 -84
  33. package/package.json +29 -127
  34. package/src/bin/install-pkmn-engine +3 -2
  35. package/src/lib/bindings/c.zig +255 -0
  36. package/src/lib/bindings/node.zig +121 -0
  37. package/src/lib/bindings/wasm.zig +59 -0
  38. package/src/lib/common/array.zig +1 -1
  39. package/src/lib/common/optional.zig +1 -1
  40. package/src/lib/common/options.zig +2 -0
  41. package/src/lib/common/protocol.zig +4 -8
  42. package/src/lib/common/rational.zig +222 -147
  43. package/src/lib/common/rng.zig +1 -3
  44. package/src/lib/common/util.zig +17 -2
  45. package/src/lib/gen1/calc.zig +87 -97
  46. package/src/lib/gen1/chance.zig +34 -18
  47. package/src/lib/gen1/data/moves.zig +2 -4
  48. package/src/lib/gen1/data/species.zig +1 -3
  49. package/src/lib/gen1/data/types.zig +1 -2
  50. package/src/lib/gen1/data.zig +7 -11
  51. package/src/lib/gen1/helpers.zig +4 -11
  52. package/src/lib/gen1/mechanics.zig +66 -50
  53. package/src/lib/gen2/calc.zig +10 -18
  54. package/src/lib/gen2/data/items.zig +1 -3
  55. package/src/lib/gen2/data/moves.zig +2 -4
  56. package/src/lib/gen2/data/species.zig +1 -3
  57. package/src/lib/gen2/data/types.zig +1 -2
  58. package/src/lib/gen2/data.zig +11 -15
  59. package/src/lib/gen2/generated.zig +18 -23
  60. package/src/lib/gen2/helpers.zig +5 -12
  61. package/src/lib/gen2/mechanics.zig +41 -38
  62. package/src/lib/node.zig +3 -116
  63. package/src/lib/pkmn.zig +7 -2
  64. package/src/lib/wasm.zig +13 -9
  65. package/src/tools/display/dom.ts +5 -8
  66. package/src/tools/display/ui.css +0 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/engine",
3
- "version": "0.1.0-dev.0cabf16e",
3
+ "version": "0.1.0-dev.0f9e9648",
4
4
  "description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
5
5
  "repository": "github:pkmn/engine",
6
6
  "license": "MIT",
@@ -21,6 +21,9 @@
21
21
  "build/pkg/addon/node.d.ts",
22
22
  "build/pkg/addon/node.js.map",
23
23
  "build/pkg/addon/node.js",
24
+ "build/pkg/addon/wasm.d.ts",
25
+ "build/pkg/addon/wasm.js.map",
26
+ "build/pkg/addon/wasm.js",
24
27
  "build/pkg/data.d.ts",
25
28
  "build/pkg/data.js.map",
26
29
  "build/pkg/data.js",
@@ -46,6 +49,9 @@
46
49
  "build/tools/display/ui.jsx",
47
50
  "build/tools/display/util.js.map",
48
51
  "build/tools/display/util.js",
52
+ "src/lib/bindings/c.zig",
53
+ "src/lib/bindings/node.zig",
54
+ "src/lib/bindings/wasm.zig",
49
55
  "src/lib/common/array.zig",
50
56
  "src/lib/common/battle.zig",
51
57
  "src/lib/common/data.zig",
@@ -92,41 +98,42 @@
92
98
  "access": "public"
93
99
  },
94
100
  "dependencies": {
95
- "@pkmn/data": "^0.9.13",
96
- "@pkmn/protocol": "^0.6.26"
101
+ "@pkmn/data": "^0.9.31",
102
+ "@pkmn/protocol": "^0.6.31"
97
103
  },
98
104
  "devDependencies": {
99
- "@pkmn/eslint-config": "^8.3.0",
100
- "@pkmn/img": "^0.2.32",
105
+ "@pkmn/eslint-config": "^9.7.0",
106
+ "@pkmn/img": "^0.2.36",
101
107
  "@pkmn/sets": "^5.1.2",
102
- "@pkmn/sim": "0.9.13",
108
+ "@pkmn/sim": "0.9.31",
109
+ "@pkmn/smogon": "^0.5.19",
103
110
  "@types/html-minifier": "^4.0.5",
104
- "@types/jest": "^29.5.12",
111
+ "@types/jest": "^29.5.14",
105
112
  "@types/minimist": "^1.2.5",
106
113
  "@types/mustache": "^4.2.5",
107
- "@types/node": "^22.5.3",
108
- "@types/react": "^18.2.45",
114
+ "@types/node": "^22.13.9",
115
+ "@types/react": "<=18",
109
116
  "@types/semver": "^7.5.8",
110
- "@types/vscode": "^1.93.0",
111
- "@vitest/coverage-v8": "^2.0.5",
112
- "@vscode/vsce": "^3.1.0",
113
- "binaryen": "^118.0.0",
114
- "esbuild": "^0.23.1",
115
- "eslint": "<=8",
117
+ "@types/vscode": "^1.97.0",
118
+ "@vitest/coverage-v8": "^3.0.7",
119
+ "@vscode/vsce": "^3.2.2",
120
+ "binaryen": "^122.0.0",
121
+ "esbuild": "^0.25.0",
122
+ "eslint": "^9.21.0",
116
123
  "html-minifier": "^4.0.0",
117
- "json-stringify-pretty-compact": "4.0.0",
124
+ "json-stringify-pretty-compact": "3.0.0",
118
125
  "minimist": "^1.2.8",
119
126
  "mustache": "^4.2.0",
120
- "semver": "^7.6.3",
127
+ "semver": "^7.7.1",
121
128
  "source-map-support": "^0.5.21",
122
- "table": "^6.8.2",
129
+ "table": "^6.9.0",
123
130
  "trakr": "^0.2.0",
124
- "typescript": "^5.5.4",
125
- "vitest": "^2.0.5"
131
+ "typescript": "^5.8.2",
132
+ "vitest": "^3.0.7"
126
133
  },
127
134
  "scripts": {
128
- "lint": "eslint --cache src --ext ts,js && eslint src/bin/*",
129
- "fix": "eslint --fix src --ext ts,js && eslint --fix src/bin/*",
135
+ "lint": "eslint --cache src",
136
+ "fix": "eslint --fix src",
130
137
  "compile": "tsc -p .",
131
138
  "build": "npm run compile",
132
139
  "test": "vitest src/pkg",
@@ -140,110 +147,5 @@
140
147
  "integration": "npm run compile && node build/test/integration",
141
148
  "extension": "npm -C src/tools/vscode run build",
142
149
  "postinstall": "node src/bin/install-pkmn-engine"
143
- },
144
- "eslintConfig": {
145
- "extends": "@pkmn",
146
- "ignorePatterns": [
147
- "dist/",
148
- "node_modules/",
149
- "examples/zig",
150
- "build/",
151
- "extension.js"
152
- ],
153
- "env": {
154
- "es6": true,
155
- "node": true,
156
- "browser": true
157
- },
158
- "overrides": [
159
- {
160
- "files": [
161
- "**/*.ts"
162
- ],
163
- "parserOptions": {
164
- "project": [
165
- "./tsconfig.json",
166
- "./examples/js/tsconfig.json"
167
- ]
168
- }
169
- },
170
- {
171
- "files": [
172
- "src/pkg/index.ts",
173
- "src/test/integration.ts"
174
- ],
175
- "rules": {
176
- "no-control-regex": "off",
177
- "@typescript-eslint/no-empty-interface": "off",
178
- "@typescript-eslint/no-shadow": "off",
179
- "@typescript-eslint/no-unsafe-declaration-merging": "off"
180
- }
181
- },
182
- {
183
- "files": [
184
- "src/pkg/addon/node.ts"
185
- ],
186
- "rules": {
187
- "@typescript-eslint/no-var-requires": "off"
188
- }
189
- },
190
- {
191
- "files": [
192
- "src/test/benchmark*.ts"
193
- ],
194
- "rules": {
195
- "@typescript-eslint/no-unused-vars": "off",
196
- "@typescript-eslint/no-redundant-type-constituents": "off"
197
- }
198
- },
199
- {
200
- "files": [
201
- "src/pkg/protocol.test.ts"
202
- ],
203
- "rules": {
204
- "jest/no-standalone-expect": "off",
205
- "jest/no-conditional-expect": "off"
206
- }
207
- },
208
- {
209
- "files": [
210
- "src/test/*.test.ts",
211
- "src/test/regression/*.test.ts"
212
- ],
213
- "rules": {
214
- "jest/no-standalone-expect": "off"
215
- }
216
- },
217
- {
218
- "files": [
219
- "src/test/showdown/**"
220
- ],
221
- "rules": {
222
- "jest/expect-expect": [
223
- "warn",
224
- {
225
- "assertFunctionNames": [
226
- "expect",
227
- "expectLog",
228
- "verify"
229
- ]
230
- }
231
- ]
232
- }
233
- },
234
- {
235
- "files": [
236
- "src/tools/generate.ts"
237
- ],
238
- "rules": {
239
- "@typescript-eslint/restrict-template-expressions": [
240
- "error",
241
- {
242
- "allowBoolean": true
243
- }
244
- ]
245
- }
246
- }
247
- ]
248
150
  }
249
151
  }
@@ -257,7 +257,7 @@ const downloadImportLib = async () => {
257
257
  }
258
258
 
259
259
  const dir = path.join(ROOT, 'build', 'lib');
260
- const url = new URL('./win-x64/node.lib', process.release.headersUrl).href;
260
+ const url = process.release.libUrl;
261
261
  const dest = path.join(dir, path.basename(url));
262
262
  if (fs.existsSync(path.join(dest))) {
263
263
  log('Node import library:', dest);
@@ -325,6 +325,7 @@ const downloadImportLib = async () => {
325
325
  const libs = path.join(ROOT, 'build', 'lib');
326
326
  if (fs.existsSync(libs)) {
327
327
  for (const file of fs.readdirSync(libs)) {
328
+ if (!file.startsWith('pkmn')) continue;
328
329
  const type = path.extname(file).slice(1);
329
330
  if (type !== 'node' && type !== 'wasm') continue;
330
331
  // If we don't copy the addon before loading it Windows will return an
@@ -393,7 +394,7 @@ const downloadImportLib = async () => {
393
394
  try {
394
395
  const actual = path.join(ROOT, 'build', 'lib', `${prefix}pkmn${suffix}.node.${ext}`);
395
396
  fs.renameSync(actual, artifact);
396
- } catch (err) {
397
+ } catch {
397
398
  // ziglang/zig#19743 changed this path
398
399
  const actual = path.join(ROOT, 'build', 'bin', `${prefix}pkmn${suffix}.node.${ext}`);
399
400
  fs.renameSync(actual, artifact);
@@ -0,0 +1,255 @@
1
+ const pkmn = @import("../pkmn.zig");
2
+ const std = @import("std");
3
+
4
+ const assert = std.debug.assert;
5
+
6
+ const ERROR: u8 = 0b1100;
7
+
8
+ const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
9
+ const Strong = if (@hasField(std.builtin.GlobalLinkage, "strong")) .strong else .Strong;
10
+
11
+ pub const OPTIONS: extern struct {
12
+ showdown: bool,
13
+ log: bool,
14
+ chance: bool,
15
+ calc: bool,
16
+ } = .{
17
+ .showdown = pkmn.options.showdown,
18
+ .log = pkmn.options.log,
19
+ .chance = pkmn.options.chance,
20
+ .calc = pkmn.options.calc,
21
+ };
22
+
23
+ pub const MAX_CHOICES = pkmn.MAX_CHOICES;
24
+ pub const CHOICES_SIZE = pkmn.CHOICES_SIZE;
25
+ pub const MAX_LOGS = pkmn.MAX_LOGS;
26
+ pub const LOGS_SIZE = pkmn.LOGS_SIZE;
27
+
28
+ pub fn choice_init(choice: u8, data: u8) callconv(.C) u8 {
29
+ assert(choice <= @field(@typeInfo(pkmn.Choice.Type), @tagName(Enum)).fields.len);
30
+ assert(data <= 6);
31
+ return @bitCast(pkmn.Choice{ .type = @enumFromInt(choice), .data = @intCast(data) });
32
+ }
33
+
34
+ pub fn choice_type(choice: u8) callconv(.C) u8 {
35
+ return @intFromEnum(@as(pkmn.Choice, @bitCast(choice)).type);
36
+ }
37
+
38
+ pub fn choice_data(choice: u8) callconv(.C) u8 {
39
+ return @as(u8, @as(pkmn.Choice, @bitCast(choice)).data);
40
+ }
41
+
42
+ pub fn result_type(result: u8) callconv(.C) u8 {
43
+ return @intFromEnum(@as(pkmn.Result, @bitCast(result)).type);
44
+ }
45
+
46
+ pub fn result_p1(result: u8) callconv(.C) u8 {
47
+ assert(!err(result));
48
+ return @intFromEnum(@as(pkmn.Result, @bitCast(result)).p1);
49
+ }
50
+
51
+ pub fn result_p2(result: u8) callconv(.C) u8 {
52
+ assert(!err(result));
53
+ return @intFromEnum(@as(pkmn.Result, @bitCast(result)).p2);
54
+ }
55
+
56
+ pub fn err(result: u8) callconv(.C) bool {
57
+ return result == ERROR;
58
+ }
59
+
60
+ pub fn psrng_init(prng: *pkmn.PSRNG, seed: u64) callconv(.C) void {
61
+ prng.src = .{ .seed = seed };
62
+ }
63
+
64
+ pub fn psrng_next(prng: *pkmn.PSRNG) callconv(.C) u32 {
65
+ return prng.next();
66
+ }
67
+
68
+ pub fn rational_init(rational: *pkmn.Rational(f64)) callconv(.C) void {
69
+ rational.reset();
70
+ }
71
+
72
+ pub fn rational_reduce(rational: *pkmn.Rational(f64)) callconv(.C) void {
73
+ rational.reduce();
74
+ }
75
+
76
+ pub fn rational_numerator(rational: *const pkmn.Rational(f64)) callconv(.C) f64 {
77
+ return rational.p;
78
+ }
79
+
80
+ pub fn rational_denominator(rational: *const pkmn.Rational(f64)) callconv(.C) f64 {
81
+ return rational.q;
82
+ }
83
+
84
+ const log_options = extern struct {
85
+ buf: [*]u8,
86
+ len: usize,
87
+ };
88
+
89
+ pub fn gen(comptime num: comptime_int) type {
90
+ const g = @field(pkmn, "gen" ++ std.fmt.comptimePrint("{d}", .{num}));
91
+
92
+ return struct {
93
+ pub const MAX_CHOICES = g.MAX_CHOICES;
94
+ pub const CHOICES_SIZE = g.CHOICES_SIZE;
95
+ pub const MAX_LOGS = g.MAX_LOGS;
96
+ pub const LOGS_SIZE = g.LOGS_SIZE;
97
+
98
+ const chance_options = extern struct {
99
+ probability: pkmn.Rational(f64),
100
+ actions: g.chance.Actions,
101
+ durations: g.chance.Durations,
102
+ };
103
+
104
+ const calc_options = extern struct {
105
+ overrides: g.chance.Actions,
106
+ };
107
+
108
+ const battle_options = struct {
109
+ stream: pkmn.protocol.ByteStream,
110
+ log: pkmn.protocol.FixedLog,
111
+ chance: g.Chance(pkmn.Rational(f64)),
112
+ calc: g.Calc,
113
+
114
+ comptime {
115
+ assert(@sizeOf(battle_options) <= 128);
116
+ }
117
+ };
118
+
119
+ pub fn battle_options_set(
120
+ options: *battle_options,
121
+ log: ?*const log_options,
122
+ chance: ?*const chance_options,
123
+ calc: ?*const calc_options,
124
+ ) callconv(.C) void {
125
+ if (pkmn.options.log) {
126
+ if (log) |l| {
127
+ options.stream = .{ .buffer = l.buf[0..l.len] };
128
+ options.log = .{ .writer = options.stream.writer() };
129
+ } else {
130
+ options.stream.reset();
131
+ }
132
+ }
133
+ if (pkmn.options.chance) {
134
+ if (chance) |c| {
135
+ options.chance = .{
136
+ .probability = c.probability,
137
+ .actions = c.actions,
138
+ .durations = c.durations,
139
+ };
140
+ } else {
141
+ options.chance.reset();
142
+ }
143
+ }
144
+ if (pkmn.options.calc) {
145
+ if (calc) |c| {
146
+ options.calc = .{ .overrides = c.overrides };
147
+ } else {
148
+ options.calc = .{};
149
+ }
150
+ }
151
+ }
152
+
153
+ pub fn battle_options_chance_probability(
154
+ options: *battle_options,
155
+ ) callconv(.C) *pkmn.Rational(f64) {
156
+ return &options.chance.probability;
157
+ }
158
+
159
+ pub fn battle_options_chance_actions(
160
+ options: *battle_options,
161
+ ) callconv(.C) *g.chance.Actions {
162
+ return &options.chance.actions;
163
+ }
164
+
165
+ pub fn battle_options_chance_durations(
166
+ options: *battle_options,
167
+ ) callconv(.C) *g.chance.Durations {
168
+ return &options.chance.durations;
169
+ }
170
+
171
+ pub fn battle_options_calc_summaries(
172
+ options: *battle_options,
173
+ ) callconv(.C) *g.calc.Summaries {
174
+ return &options.calc.summaries;
175
+ }
176
+
177
+ pub fn battle_update(
178
+ battle: *g.Battle(g.PRNG),
179
+ c1: pkmn.Choice,
180
+ c2: pkmn.Choice,
181
+ opts: ?*battle_options,
182
+ ) callconv(.C) pkmn.Result {
183
+ if ((pkmn.options.log or pkmn.options.chance or pkmn.options.calc) and opts != null) {
184
+ return battle.update(c1, c2, opts.?) catch return @bitCast(ERROR);
185
+ }
186
+ return battle.update(c1, c2, &g.NULL) catch unreachable;
187
+ }
188
+
189
+ pub fn battle_choices(
190
+ battle: *const g.Battle(g.PRNG),
191
+ player: u8,
192
+ request: u8,
193
+ out: [*]u8,
194
+ n: usize,
195
+ ) callconv(.C) u8 {
196
+ assert(player <= @field(@typeInfo(pkmn.Player), @tagName(Enum)).fields.len);
197
+ assert(request <= @field(@typeInfo(pkmn.Choice.Type), @tagName(Enum)).fields.len);
198
+
199
+ assert(!pkmn.options.showdown or n > 0);
200
+ return battle.choices(@enumFromInt(player), @enumFromInt(request), @ptrCast(out[0..n]));
201
+ }
202
+ };
203
+ }
204
+
205
+ pub fn exports() type {
206
+ @export(choice_init, .{ .name = "pkmn_choice_init", .linkage = Strong });
207
+ @export(choice_type, .{ .name = "pkmn_choice_type", .linkage = Strong });
208
+ @export(choice_data, .{ .name = "pkmn_choice_data", .linkage = Strong });
209
+
210
+ @export(result_type, .{ .name = "pkmn_result_type", .linkage = Strong });
211
+ @export(result_p1, .{ .name = "pkmn_result_p1", .linkage = Strong });
212
+ @export(result_p2, .{ .name = "pkmn_result_p2", .linkage = Strong });
213
+
214
+ @export(err, .{ .name = "pkmn_error", .linkage = Strong });
215
+
216
+ @export(psrng_init, .{ .name = "pkmn_psrng_init", .linkage = Strong });
217
+ @export(psrng_next, .{ .name = "pkmn_psrng_next", .linkage = Strong });
218
+
219
+ @export(rational_init, .{ .name = "pkmn_rational_init", .linkage = Strong });
220
+ @export(rational_reduce, .{ .name = "pkmn_rational_reduce", .linkage = Strong });
221
+ @export(rational_numerator, .{ .name = "pkmn_rational_numerator", .linkage = Strong });
222
+ @export(rational_denominator, .{ .name = "pkmn_rational_denominator", .linkage = Strong });
223
+
224
+ @export(
225
+ gen(1).battle_options_set,
226
+ .{ .name = "pkmn_gen1_battle_options_set", .linkage = Strong },
227
+ );
228
+ @export(
229
+ gen(1).battle_options_chance_probability,
230
+ .{ .name = "pkmn_gen1_battle_options_chance_probability", .linkage = Strong },
231
+ );
232
+ @export(
233
+ gen(1).battle_options_chance_actions,
234
+ .{ .name = "pkmn_gen1_battle_options_chance_actions", .linkage = Strong },
235
+ );
236
+ @export(
237
+ gen(1).battle_options_chance_durations,
238
+ .{ .name = "pkmn_gen1_battle_options_chance_durations", .linkage = Strong },
239
+ );
240
+ @export(
241
+ gen(1).battle_options_calc_summaries,
242
+ .{ .name = "pkmn_gen1_battle_options_calc_summaries", .linkage = Strong },
243
+ );
244
+
245
+ @export(
246
+ gen(1).battle_update,
247
+ .{ .name = "pkmn_gen1_battle_update", .linkage = Strong },
248
+ );
249
+ @export(
250
+ gen(1).battle_choices,
251
+ .{ .name = "pkmn_gen1_battle_choices", .linkage = Strong },
252
+ );
253
+
254
+ return struct {};
255
+ }
@@ -0,0 +1,121 @@
1
+ const js = @import("../common/js.zig");
2
+ const pkmn = @import("../pkmn.zig");
3
+ const std = @import("std");
4
+
5
+ const assert = std.debug.assert;
6
+
7
+ const c = @cImport({
8
+ @cDefine("NAPI_VERSION", "8");
9
+ @cInclude("node_api.h");
10
+ });
11
+
12
+ pub fn register(env: c.napi_env) c.napi_value {
13
+ const object = js.Object.init(env);
14
+ const properties = [_]c.napi_property_descriptor{
15
+ js.Property.init("options", .{ .value = options(env) }),
16
+ js.Property.init("bindings", .{ .value = bindings(env) }),
17
+ };
18
+ assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
19
+ return object;
20
+ }
21
+
22
+ fn options(env: c.napi_env) c.napi_value {
23
+ const object = js.Object.init(env);
24
+ const properties = [_]c.napi_property_descriptor{
25
+ js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
26
+ js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
27
+ js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
28
+ js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
29
+ };
30
+ assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
31
+ return object;
32
+ }
33
+
34
+ fn bindings(env: c.napi_env) c.napi_value {
35
+ const array = js.Array.init(env, .{ .length = 1 });
36
+ js.Array.set(env, array, 0, bind(env, pkmn.gen1));
37
+ return array;
38
+ }
39
+
40
+ fn bind(env: c.napi_env, gen: anytype) c.napi_value {
41
+ const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
42
+ const logs_size: u32 = @intCast(gen.LOGS_SIZE);
43
+ const object = js.Object.init(env);
44
+ const properties = [_]c.napi_property_descriptor{
45
+ js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
46
+ js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
47
+ js.Property.init("update", .{ .method = update(gen) }),
48
+ js.Property.init("choices", .{ .method = choices(gen) }),
49
+ };
50
+ assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
51
+ return object;
52
+ }
53
+
54
+ fn update(gen: anytype) c.napi_callback {
55
+ return struct {
56
+ fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
57
+ var argc: usize = 4;
58
+ var argv: [4]c.napi_value = undefined;
59
+ assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
60
+ assert(argc == 4);
61
+
62
+ var data: ?*anyopaque = undefined;
63
+ var len: usize = 0;
64
+ assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
65
+ assert(len == @sizeOf(gen.Battle(gen.PRNG)));
66
+
67
+ var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
68
+ const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
69
+ const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
70
+
71
+ var vtype: c.napi_valuetype = undefined;
72
+ assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
73
+ const result = switch (vtype) {
74
+ c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
75
+ else => result: {
76
+ assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
77
+ assert(len == gen.LOGS_SIZE);
78
+
79
+ const buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
80
+ var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
81
+ // TODO: extract out
82
+ var opts = pkmn.battle.options(
83
+ pkmn.protocol.FixedLog{ .writer = stream.writer() },
84
+ gen.chance.NULL,
85
+ gen.calc.NULL,
86
+ );
87
+ break :result battle.update(c1, c2, &opts);
88
+ },
89
+ } catch unreachable;
90
+
91
+ return js.Number.init(env, @as(u8, @bitCast(result)));
92
+ }
93
+ }.call;
94
+ }
95
+
96
+ fn choices(gen: anytype) c.napi_callback {
97
+ return struct {
98
+ fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
99
+ var argc: usize = 4;
100
+ var argv: [4]c.napi_value = undefined;
101
+ assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
102
+ assert(argc == 4);
103
+
104
+ var data: ?*anyopaque = undefined;
105
+ var len: usize = 0;
106
+ assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
107
+ assert(len == @sizeOf(gen.Battle(gen.PRNG)));
108
+
109
+ var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
110
+ const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
111
+ const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
112
+
113
+ assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
114
+ assert(len == gen.CHOICES_SIZE);
115
+
116
+ const out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
117
+ const n = battle.choices(player, request, out);
118
+ return js.Number.init(env, @as(u8, @bitCast(n)));
119
+ }
120
+ }.call;
121
+ }
@@ -0,0 +1,59 @@
1
+ const pkmn = @import("../pkmn.zig");
2
+ const std = @import("std");
3
+
4
+ const assert = std.debug.assert;
5
+
6
+ const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
7
+ const Strong = if (@hasField(std.builtin.GlobalLinkage, "strong")) .strong else .Strong;
8
+
9
+ pub const options = pkmn.options;
10
+
11
+ pub fn gen(comptime num: comptime_int) type {
12
+ const g = @field(pkmn, "gen" ++ std.fmt.comptimePrint("{d}", .{num}));
13
+ return struct {
14
+ pub const CHOICES_SIZE = size(g.CHOICES_SIZE);
15
+ pub const LOGS_SIZE = size(g.LOGS_SIZE);
16
+
17
+ pub fn update(
18
+ battle: *g.Battle(g.PRNG),
19
+ c1: pkmn.Choice,
20
+ c2: pkmn.Choice,
21
+ options_: ?[*]u8,
22
+ ) callconv(.C) pkmn.Result {
23
+ return (if (options_) |o| result: {
24
+ const buf = @as([*]u8, @ptrCast(o))[0..g.LOGS_SIZE];
25
+ var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
26
+ // TODO: extract out
27
+ var opts = pkmn.battle.options(
28
+ pkmn.protocol.FixedLog{ .writer = stream.writer() },
29
+ g.chance.NULL,
30
+ g.calc.NULL,
31
+ );
32
+ break :result battle.update(c1, c2, &opts);
33
+ } else battle.update(c1, c2, &g.NULL)) catch unreachable;
34
+ }
35
+
36
+ pub fn choices(
37
+ battle: *g.Battle(g.PRNG),
38
+ player: u8,
39
+ request: u8,
40
+ buf: [*]u8,
41
+ ) callconv(.C) u8 {
42
+ assert(player <= @field(@typeInfo(pkmn.Player), @tagName(Enum)).fields.len);
43
+ assert(request <= @field(@typeInfo(pkmn.Choice.Type), @tagName(Enum)).fields.len);
44
+
45
+ const n = CHOICES_SIZE;
46
+ return battle.choices(@enumFromInt(player), @enumFromInt(request), @ptrCast(buf[0..n]));
47
+ }
48
+ };
49
+ }
50
+
51
+ pub fn exports() type {
52
+ @export(gen(1).update, .{ .name = "GEN1_update", .linkage = Strong });
53
+ @export(gen(1).choices, .{ .name = "GEN1_choices", .linkage = Strong });
54
+ return struct {};
55
+ }
56
+
57
+ fn size(n: usize) u32 {
58
+ return std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(n))) catch unreachable;
59
+ }
@@ -11,7 +11,7 @@ const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
11
11
  pub fn Array(comptime n: comptime_int, comptime U: type) type {
12
12
  return struct {
13
13
  const size = @bitSizeOf(U);
14
- const options = .{ .signedness = .unsigned, .bits = size * n };
14
+ const options: std.builtin.Type.Int = .{ .signedness = .unsigned, .bits = size * n };
15
15
  pub const T = @Type(if (@hasField(std.builtin.Type, "int"))
16
16
  .{ .int = options }
17
17
  else
@@ -25,7 +25,7 @@ pub fn Optional(comptime T: type) type {
25
25
  };
26
26
  }
27
27
 
28
- const options = .{
28
+ const options: std.builtin.Type.Enum = .{
29
29
  .tag_type = std.math.IntFittingRange(0, fields.len),
30
30
  .fields = &enumFields,
31
31
  .decls = &decls,