@pkmn/engine 0.1.0-dev.84cc1a02 → 0.1.0-dev.8b192003

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 (63) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +135 -71
  3. package/build/pkg/addon.d.ts +7 -0
  4. package/build/pkg/addon.js +111 -0
  5. package/build/pkg/addon.js.map +1 -0
  6. package/build/pkg/data/ids.json +12 -12
  7. package/build/pkg/data/layout.json +27 -5
  8. package/build/pkg/data/protocol.json +69 -12
  9. package/build/pkg/data.d.ts +5 -6
  10. package/build/pkg/data.js +3 -5
  11. package/build/pkg/data.js.map +1 -1
  12. package/build/pkg/gen1.d.ts +5 -4
  13. package/build/pkg/gen1.js +49 -33
  14. package/build/pkg/gen1.js.map +1 -1
  15. package/build/pkg/index.d.ts +17 -13
  16. package/build/pkg/index.js.map +1 -1
  17. package/build/pkg/protocol.d.ts +4 -2
  18. package/build/pkg/protocol.js +65 -44
  19. package/build/pkg/protocol.js.map +1 -1
  20. package/build/test/display.js +400 -0
  21. package/build/test/display.js.map +1 -0
  22. package/build/tools/debug.js +134 -0
  23. package/build/tools/debug.js.map +1 -0
  24. package/build.zig +161 -147
  25. package/package.json +86 -46
  26. package/src/bin/install-pkmn-engine +67 -35
  27. package/src/bin/pkmn-debug +62 -0
  28. package/src/lib/common/battle.zig +17 -0
  29. package/src/lib/common/data.zig +19 -31
  30. package/src/lib/common/debug.zig +23 -13
  31. package/src/lib/common/js.zig +118 -0
  32. package/src/lib/common/optional.zig +67 -0
  33. package/src/lib/common/options.zig +14 -3
  34. package/src/lib/common/protocol.zig +942 -403
  35. package/src/lib/common/rational.zig +447 -0
  36. package/src/lib/common/rng.zig +35 -26
  37. package/src/lib/common/util.zig +12 -0
  38. package/src/lib/gen1/calc.zig +812 -0
  39. package/src/lib/gen1/chance.zig +827 -0
  40. package/src/lib/gen1/data/moves.zig +202 -190
  41. package/src/lib/gen1/data/species.zig +5 -5
  42. package/src/lib/gen1/data/types.zig +10 -10
  43. package/src/lib/gen1/data.zig +108 -94
  44. package/src/lib/gen1/helpers.zig +136 -37
  45. package/src/lib/gen1/mechanics.zig +1188 -835
  46. package/src/lib/gen2/calc.zig +143 -0
  47. package/src/lib/gen2/chance.zig +882 -0
  48. package/src/lib/gen2/data/items.zig +14 -20
  49. package/src/lib/gen2/data/moves.zig +664 -570
  50. package/src/lib/gen2/data/species.zig +2 -2
  51. package/src/lib/gen2/data/types.zig +137 -34
  52. package/src/lib/gen2/data.zig +134 -96
  53. package/src/lib/gen2/generated.zig +806 -0
  54. package/src/lib/gen2/helpers.zig +50 -26
  55. package/src/lib/gen2/mechanics.zig +3564 -12
  56. package/src/lib/node.zig +131 -0
  57. package/src/lib/pkmn.zig +86 -12
  58. package/src/lib/wasm.zig +13 -0
  59. package/src/test/display/pkmn.html.tmpl +368 -0
  60. package/src/lib/binding/node.zig +0 -229
  61. package/src/lib/binding/wasm.zig +0 -10
  62. package/src/lib/gen1/README.md +0 -778
  63. package/src/lib/gen2/README.md +0 -309
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/engine",
3
- "version": "0.1.0-dev.84cc1a02",
3
+ "version": "0.1.0-dev.8b192003",
4
4
  "description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
5
5
  "repository": "github:pkmn/engine",
6
6
  "license": "MIT",
@@ -9,86 +9,106 @@
9
9
  "types": "build/pkg/index.d.ts",
10
10
  "files": [
11
11
  "build.zig",
12
- "src/bin/install-pkmn-engine",
13
- "src/lib/binding/node.zig",
14
- "src/lib/binding/wasm.zig",
12
+ "build/pkg/data.d.ts",
13
+ "build/pkg/data.js.map",
14
+ "build/pkg/addon.d.ts",
15
+ "build/pkg/addon.js.map",
16
+ "build/pkg/addon.js",
17
+ "build/pkg/data.js",
18
+ "build/pkg/data/ids.json",
19
+ "build/pkg/data/layout.json",
20
+ "build/pkg/data/protocol.json",
21
+ "build/pkg/gen1.d.ts",
22
+ "build/pkg/gen1.js.map",
23
+ "build/pkg/gen1.js",
24
+ "build/pkg/index.d.ts",
25
+ "build/pkg/index.js.map",
26
+ "build/pkg/index.js",
27
+ "build/pkg/protocol.d.ts",
28
+ "build/pkg/protocol.js.map",
29
+ "build/pkg/protocol.js",
30
+ "build/test/display.js.map",
31
+ "build/test/display.js",
32
+ "build/tools/debug.js.map",
33
+ "build/tools/debug.js",
34
+ "src/lib/common/battle.zig",
15
35
  "src/lib/common/data.zig",
16
36
  "src/lib/common/debug.zig",
37
+ "src/lib/common/js.zig",
38
+ "src/lib/common/optional.zig",
17
39
  "src/lib/common/options.zig",
18
40
  "src/lib/common/protocol.zig",
41
+ "src/lib/common/rational.zig",
19
42
  "src/lib/common/rng.zig",
43
+ "src/lib/common/util.zig",
44
+ "src/lib/gen1/calc.zig",
45
+ "src/lib/gen1/chance.zig",
46
+ "src/lib/gen1/data.zig",
20
47
  "src/lib/gen1/data/moves.zig",
21
48
  "src/lib/gen1/data/species.zig",
22
49
  "src/lib/gen1/data/types.zig",
23
- "src/lib/gen1/data.zig",
24
50
  "src/lib/gen1/helpers.zig",
25
51
  "src/lib/gen1/mechanics.zig",
52
+ "src/lib/gen2/calc.zig",
53
+ "src/lib/gen2/chance.zig",
54
+ "src/lib/gen2/data.zig",
26
55
  "src/lib/gen2/data/items.zig",
27
56
  "src/lib/gen2/data/moves.zig",
28
57
  "src/lib/gen2/data/species.zig",
29
58
  "src/lib/gen2/data/types.zig",
30
- "src/lib/gen2/data.zig",
59
+ "src/lib/gen2/generated.zig",
31
60
  "src/lib/gen2/helpers.zig",
32
61
  "src/lib/gen2/mechanics.zig",
62
+ "src/lib/node.zig",
33
63
  "src/lib/pkmn.zig",
34
- "build/pkg/data/ids.json",
35
- "build/pkg/data/layout.json",
36
- "build/pkg/data/protocol.json",
37
- "build/pkg/data.d.ts",
38
- "build/pkg/data.js",
39
- "build/pkg/data.js.map",
40
- "build/pkg/gen1.d.ts",
41
- "build/pkg/gen1.js",
42
- "build/pkg/gen1.js.map",
43
- "build/pkg/index.d.ts",
44
- "build/pkg/index.js",
45
- "build/pkg/index.js.map",
46
- "build/pkg/protocol.d.ts",
47
- "build/pkg/protocol.js",
48
- "build/pkg/protocol.js.map"
64
+ "src/lib/wasm.zig",
65
+ "src/test/display/pkmn.html.tmpl"
49
66
  ],
50
67
  "engines": {
51
68
  "node": ">=16.0.0"
52
69
  },
53
70
  "bin": {
54
- "install-pkmn-engine": "src/bin/install-pkmn-engine"
71
+ "install-pkmn-engine": "src/bin/install-pkmn-engine",
72
+ "pkmn-debug": "src/bin/pkmn-debug"
55
73
  },
56
74
  "publishConfig": {
57
75
  "access": "public"
58
76
  },
59
77
  "dependencies": {
60
- "@pkmn/data": "^0.7.35",
61
- "@pkmn/protocol": "^0.6.11"
78
+ "@pkmn/data": "^0.9.11",
79
+ "@pkmn/protocol": "^0.6.25"
62
80
  },
63
81
  "devDependencies": {
64
- "@pkmn/eslint-config": "^3.0.0",
65
- "@pkmn/img": "^0.2.8",
66
- "@pkmn/sets": "^5.0.4",
67
- "@pkmn/sim": "0.7.35",
68
- "@types/html-minifier": "^4.0.2",
69
- "@types/jest": "^29.5.0",
70
- "@types/minimist": "^1.2.2",
71
- "@types/mustache": "^4.2.2",
72
- "@types/node": "^18.15.11",
73
- "@vitest/coverage-c8": "^0.29.8",
74
- "binaryen": "^112.0.0",
75
- "eslint": "^8.37.0",
82
+ "@pkmn/eslint-config": "^8.1.0",
83
+ "@pkmn/img": "^0.2.31",
84
+ "@pkmn/sets": "^5.1.2",
85
+ "@pkmn/sim": "0.9.11",
86
+ "@types/html-minifier": "^4.0.5",
87
+ "@types/jest": "^29.5.12",
88
+ "@types/minimist": "^1.2.5",
89
+ "@types/mustache": "^4.2.5",
90
+ "@types/node": "^22.0.2",
91
+ "@vitest/coverage-v8": "^2.0.5",
92
+ "binaryen": "^118.0.0",
93
+ "eslint": "<=8",
76
94
  "html-minifier": "^4.0.0",
77
- "json-stringify-pretty-compact": "3.0.0",
95
+ "json-stringify-pretty-compact": "4.0.0",
78
96
  "minimist": "^1.2.8",
79
97
  "mustache": "^4.2.0",
80
98
  "source-map-support": "^0.5.21",
99
+ "table": "^6.8.2",
81
100
  "trakr": "^0.2.0",
82
- "typescript": "^5.0.3",
83
- "vitest": "^0.29.8"
101
+ "typescript": "^5.5.4",
102
+ "vitest": "^2.0.5"
84
103
  },
85
104
  "scripts": {
86
- "lint": "eslint --cache src --ext ts,js && eslint src/bin/install-pkmn-engine",
87
- "fix": "eslint --fix src --ext ts,js && eslint --fix src/bin/install-pkmn-engine",
105
+ "lint": "eslint --cache src --ext ts,js && eslint src/bin/*",
106
+ "fix": "eslint --fix src --ext ts,js && eslint --fix src/bin/*",
88
107
  "compile": "tsc -p .",
89
108
  "build": "npm run compile",
90
109
  "test": "vitest src/pkg",
91
110
  "test:integration": "vitest src/test",
111
+ "debug": "npm run compile && node build/tools/debug",
92
112
  "fuzz": "npm run compile && node build/test/fuzz",
93
113
  "generate": "npm run compile && node build/tools/generate",
94
114
  "release": "npm run compile && node build/tools/release",
@@ -101,7 +121,7 @@
101
121
  "ignorePatterns": [
102
122
  "dist/",
103
123
  "node_modules/",
104
- "src/examples/zig",
124
+ "examples/zig",
105
125
  "build/"
106
126
  ],
107
127
  "env": {
@@ -117,10 +137,18 @@
117
137
  "parserOptions": {
118
138
  "project": [
119
139
  "./tsconfig.json",
120
- "./src/examples/js/tsconfig.json"
140
+ "./examples/js/tsconfig.json"
121
141
  ]
122
142
  }
123
143
  },
144
+ {
145
+ "files": [
146
+ "src/test/display.ts"
147
+ ],
148
+ "rules": {
149
+ "@typescript-eslint/no-unnecessary-type-assertion": "off"
150
+ }
151
+ },
124
152
  {
125
153
  "files": [
126
154
  "src/pkg/index.ts",
@@ -130,7 +158,8 @@
130
158
  "rules": {
131
159
  "no-control-regex": "off",
132
160
  "@typescript-eslint/no-empty-interface": "off",
133
- "@typescript-eslint/no-shadow": "off"
161
+ "@typescript-eslint/no-shadow": "off",
162
+ "@typescript-eslint/no-unsafe-declaration-merging": "off"
134
163
  }
135
164
  },
136
165
  {
@@ -146,12 +175,23 @@
146
175
  "src/test/benchmark*.ts"
147
176
  ],
148
177
  "rules": {
149
- "@typescript-eslint/no-unused-vars": "off"
178
+ "@typescript-eslint/no-unused-vars": "off",
179
+ "@typescript-eslint/no-redundant-type-constituents": "off"
180
+ }
181
+ },
182
+ {
183
+ "files": [
184
+ "src/pkg/protocol.test.ts"
185
+ ],
186
+ "rules": {
187
+ "jest/no-standalone-expect": "off",
188
+ "jest/no-conditional-expect": "off"
150
189
  }
151
190
  },
152
191
  {
153
192
  "files": [
154
- "src/test/*.test.ts"
193
+ "src/test/*.test.ts",
194
+ "src/test/regression/*.test.ts"
155
195
  ],
156
196
  "rules": {
157
197
  "jest/no-standalone-expect": "off"
@@ -10,13 +10,15 @@ 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: 2168};
13
+ const ZIG_VERSION = {major: 0, minor: 13, patch: 0};
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';
17
17
  const ROOT = path.resolve(__dirname, '..', '..');
18
+ let SILENT = false;
18
19
 
19
20
  const sh = (cmd, args) => execFileSync(cmd, args, {encoding: 'utf8', cwd: ROOT});
21
+ const log = (...args) => { if (!SILENT) console.log(...args); };
20
22
 
21
23
  // This is hardly the browser fetch API, but it works for our primitive needs
22
24
  const fetch = url => new Promise((resolve, reject) => {
@@ -61,7 +63,7 @@ const unpack7 = (input, output) => new Promise((resolve, reject) => {
61
63
  require.resolve('7zip-min');
62
64
  } catch (err) {
63
65
  if (err.code !== 'MODULE_NOT_FOUND') throw err;
64
- console.log('Installing 7zip-min package locally to unpack the archive...');
66
+ log('Installing 7zip-min package locally to unpack the archive...');
65
67
  sh('npm', ['install', '7zip-min', '--no-audit', '--no-save']);
66
68
  }
67
69
 
@@ -83,7 +85,7 @@ const unpack = async (input, output) => {
83
85
  sh('tar', ['xf', input, '-C', output]);
84
86
  return;
85
87
  } catch {
86
- console.log('tar command not found, falling back to 7z');
88
+ log('tar command not found, falling back to 7z');
87
89
  }
88
90
  }
89
91
  // Try to use a 7z binary if we can, and only install 7zip-min as a last resort
@@ -120,10 +122,10 @@ const find = zig => {
120
122
  (!ZIG_VERSION.dev || (dev && dev >= ZIG_VERSION.dev)));
121
123
 
122
124
  if (compatible) {
123
- console.log('Found existing compatible Zig executable:', zig_exe);
125
+ log('Found existing compatible Zig executable:', zig_exe);
124
126
  return zig_exe;
125
127
  } else {
126
- console.log(`Existing Zig executable is not compatible (${version} < ${VERSION}):`, zig_exe);
128
+ log(`Existing Zig executable is not compatible (${version} < ${VERSION}):`, zig_exe);
127
129
  }
128
130
  } catch (err) {
129
131
  if (err.code !== 'ENOENT') throw err;
@@ -168,11 +170,11 @@ const install = async () => {
168
170
  let base = path.basename(url);
169
171
  const archive = path.join(dir, path.basename(url));
170
172
 
171
- console.log(`Downloading Zig tarball from ${url} to ${archive}`);
173
+ log(`Downloading Zig tarball from ${url} to ${archive}`);
172
174
  try {
173
175
  await download(url, archive, release.shasum);
174
176
 
175
- console.log(`Extracting tarball and installing to ${dir}`);
177
+ log(`Extracting tarball and installing to ${dir}`);
176
178
  await unpack(archive, dir);
177
179
  if (archive.endsWith('.zip')) {
178
180
  base = base.slice(0, -4);
@@ -187,17 +189,17 @@ const install = async () => {
187
189
  } catch (err) {
188
190
  if (err.code !== 'ENOENT') throw err;
189
191
  }
190
- console.log(`Zig${version === 'master' ? '' : ` v${version}`} executable:`, zig,
192
+ log(`Zig${version === 'master' ? '' : ` v${version}`} executable:`, zig,
191
193
  '(this version of Zig will be retained to speed up future updates)');
192
194
  const [pre, post] = tty.isatty(1) ? ['\x1b[1m\x1b[35m', '\x1b[0m'] : ['', ''];
193
- console.log(
195
+ log(
194
196
  `${pre}Consider moving the ${path.dirname(zig)} directory and adding it to your PATH`,
195
197
  `to use Zig on other projects, or delete this if you wish to save space.${post}`
196
198
  );
197
199
 
198
200
  return zig;
199
201
  } finally {
200
- console.log(`Cleaning up ${archive}`);
202
+ log(`Cleaning up ${archive}`);
201
203
  try { fs.rmSync(archive); } catch {}
202
204
  }
203
205
  };
@@ -206,17 +208,17 @@ const install = async () => {
206
208
  const downloadHeaders = async () => {
207
209
  const include = path.resolve(process.execPath, '..', '..', 'include', 'node');
208
210
  if (fs.existsSync(path.join(include, 'node.h'))) {
209
- console.log('Node headers:', include);
211
+ log('Node headers:', include);
210
212
  return include;
211
213
  }
212
214
 
213
215
  const dir = path.join(ROOT, 'build', 'include');
214
216
  const dest = path.join(dir, 'node');
215
217
  if (fs.existsSync(path.join(dest, 'node.h'))) {
216
- console.log('Node headers:', dest);
218
+ log('Node headers:', dest);
217
219
  return dest;
218
220
  } else {
219
- console.log(`Could not find Node headers at ${include}, downloading...`);
221
+ log(`Could not find Node headers at ${include}, downloading...`);
220
222
  }
221
223
 
222
224
  try {
@@ -229,14 +231,14 @@ const downloadHeaders = async () => {
229
231
  const targz = path.join(dir, path.basename(url));
230
232
  const unpacked = path.join(dir, `node-${process.version}`, 'include');
231
233
 
232
- console.log(`Downloading Node headers from ${url} to ${targz}`);
234
+ log(`Downloading Node headers from ${url} to ${targz}`);
233
235
 
234
236
  try {
235
237
  await download(url, targz);
236
238
  await unpack(targz, dir);
237
239
  fs.renameSync(path.join(unpacked, 'node'), dest);
238
240
 
239
- console.log('Node headers:', dest);
241
+ log('Node headers:', dest);
240
242
 
241
243
  return dest;
242
244
  } finally {
@@ -248,7 +250,7 @@ const downloadHeaders = async () => {
248
250
  const downloadImportLib = async () => {
249
251
  const library = path.resolve(process.execPath, '..', 'node.lib');
250
252
  if (fs.existsSync(path.join(library, 'node.h'))) {
251
- console.log('Node import library:', library);
253
+ log('Node import library:', library);
252
254
  return library;
253
255
  }
254
256
 
@@ -256,10 +258,10 @@ const downloadImportLib = async () => {
256
258
  const url = new URL('./win-x64/node.lib', process.release.headersUrl).href;
257
259
  const dest = path.join(dir, path.basename(url));
258
260
  if (fs.existsSync(path.join(dest))) {
259
- console.log('Node import library:', dest);
261
+ log('Node import library:', dest);
260
262
  return dest;
261
263
  } else {
262
- console.log(`Could not find Node import library at ${library}, downloading...`);
264
+ log(`Could not find Node import library at ${library}, downloading...`);
263
265
  }
264
266
 
265
267
  try {
@@ -268,24 +270,41 @@ const downloadImportLib = async () => {
268
270
  if (err.code !== 'EEXIST') throw err;
269
271
  }
270
272
 
271
- console.log(`Downloading Node import library from ${url} to ${dest}`);
273
+ log(`Downloading Node import library from ${url} to ${dest}`);
272
274
  await download(url, dest);
273
- console.log('Node import library:', dest);
275
+ log('Node import library:', dest);
274
276
 
275
277
  return dest;
276
278
  };
277
279
 
278
280
  (async () => {
279
- const options = (process.argv[2] || '').startsWith('--options=')
280
- ? process.argv[2].slice(10).split(' ') : undefined;
281
- if (process.argv.length > 3 ||
282
- process.argv.length === 3 && !(process.argv[2] === '--zig' || options)) {
281
+ let only = false;
282
+ let options = undefined;
283
+ let error = false;
284
+ for (let i = 2; i < process.argv.length; i++) {
285
+ switch (process.argv[i]) {
286
+ case "--silent": {
287
+ SILENT = true;
288
+ break;
289
+ }
290
+ case "--zig": {
291
+ if (options || only) error = true;
292
+ only = true;
293
+ break;
294
+ }
295
+ default: {
296
+ if (options || only || !process.argv[i].startsWith('--options=')) error = true;
297
+ options = process.argv[i].slice(10).split(' ');
298
+ }
299
+ }
300
+ }
301
+ if (error) {
283
302
  console.error('Invalid arguments');
284
303
  process.exit(1);
285
304
  }
286
305
 
287
306
  const zig = await install();
288
- if (process.argv[2] === '--zig') process.exit(0);
307
+ if (only) process.exit(0);
289
308
 
290
309
  let tmp;
291
310
  try {
@@ -317,14 +336,18 @@ const downloadImportLib = async () => {
317
336
  node = true;
318
337
  if (!options) {
319
338
  const addon = require(copy);
320
- if (addon.options.showdown) opts.push('-Dshowdown');
321
- if (addon.options.trace) opts.push('-Dtrace');
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');
322
343
  }
323
344
  } else if (!options) {
324
345
  const addon = await WebAssembly.instantiate(fs.readFileSync(copy));
325
346
  const values = new Uint8Array(addon.instance.exports.memory.buffer);
326
347
  if (values[addon.instance.exports.SHOWDOWN.value]) opts.push('-Dshowdown');
327
- if (values[addon.instance.exports.TRACE.value]) opts.push('-Dtrace');
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');
328
351
  }
329
352
  const key = `${type} ${opts.sort().join(' ')}`;
330
353
  if (!configs.has(key)) configs.set(key, {type, options: opts});
@@ -335,8 +358,9 @@ const downloadImportLib = async () => {
335
358
  // either the default options or whatever was passed in
336
359
  if (!configs.size) {
337
360
  node = true;
338
- configs.set('node', {type: 'node', options: options || ['-Dshowdown']});
339
- configs.set('wasm', {type: 'wasm', options: options || ['-Dshowdown']});
361
+ const DEFAULT = ['-Dshowdown'];
362
+ configs.set('node', {type: 'node', options: options || DEFAULT});
363
+ configs.set('wasm', {type: 'wasm', options: options || DEFAULT});
340
364
  }
341
365
 
342
366
  const headers = node ? await downloadHeaders() : undefined;
@@ -346,8 +370,10 @@ const downloadImportLib = async () => {
346
370
  const args = ['build', '-p', 'build', ...opts];
347
371
  if (!process.env.DEBUG_PKMN_ENGINE) args.push('-Doptimize=ReleaseFast', '-Dstrip');
348
372
  if (config.type === 'node') {
349
- args.push(`-Dnode-headers=${headers}`);
350
- if (process.platform === 'win32') args.push(`-Dnode-import-library=${lib}`);
373
+ args.push(`-Dnode-headers=${path.relative(ROOT, headers)}`);
374
+ if (process.platform === 'win32') {
375
+ args.push(`-Dnode-import-library=${path.relative(ROOT, lib)}`);
376
+ }
351
377
  } else {
352
378
  args.push('-Dwasm');
353
379
  }
@@ -355,15 +381,21 @@ const downloadImportLib = async () => {
355
381
  opts.includes('-Dshowdown') || opts.includes('-Dshowdown=true') ? '-showdown' : '';
356
382
  const flags = opts.length ? ` (${opts.join(' ')})` : '';
357
383
  const artifact = path.join('build', 'lib', `pkmn${suffix}.${config.type}`);
358
- console.log(`Building ${artifact}${flags}`);
384
+ log(`Building ${artifact}${flags}`);
359
385
  sh(zig, args);
360
386
  // TODO: ziglang/zig#14647 broke emit_to so we get to rename the file ourselves...
361
387
  if (config.type === 'node') {
362
388
  const prefix = process.platform === 'win32' ? '' : 'lib';
363
389
  const ext = process.platform === 'darwin'
364
390
  ? 'dylib' : process.platform === 'win32' ? 'dll' : 'so';
365
- const actual = path.join('build', 'lib', `${prefix}pkmn${suffix}.node.${ext}`);
366
- fs.renameSync(path.join(ROOT, actual), path.join(ROOT, artifact));
391
+ try {
392
+ const actual = path.join('build', 'lib', `${prefix}pkmn${suffix}.node.${ext}`);
393
+ fs.renameSync(actual, artifact);
394
+ } catch (err) {
395
+ // ziglang/zig#19743 changed this path
396
+ const actual = path.join('build', 'bin', `${prefix}pkmn${suffix}.node.${ext}`);
397
+ fs.renameSync(actual, artifact);
398
+ }
367
399
  }
368
400
  }
369
401
  })().catch(e => {
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ try {
5
+ require('source-map-support').install();
6
+ } catch (err) {
7
+ if (err.code !== 'MODULE_NOT_FOUND') throw err;
8
+ }
9
+
10
+ const {execFileSync} = require('child_process');
11
+ const {readFileSync} = require('fs');
12
+ const {join, resolve} = require('path');
13
+
14
+ const ROOT = resolve(__dirname, '..', '..');
15
+ const packageJSON = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
16
+
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
+ }
26
+
27
+ let Dex = undefined;
28
+ try {
29
+ Dex = require('@pkmn/sim').Dex;
30
+ } catch (err) {
31
+ if (err.code !== 'MODULE_NOT_FOUND') throw err;
32
+ try {
33
+ Dex = require('@pkmn/dex').Dex;
34
+ } catch (e) {
35
+ if (e.code !== 'MODULE_NOT_FOUND') throw e;
36
+ missing.push(`@pkmn/dex@${packageJSON.dependencies['@pkmn/data']}`);
37
+ }
38
+ }
39
+
40
+ if (missing.length) {
41
+ if (+/^v(\d+)/.exec(process.version)[1] < 16) {
42
+ console.error('pkmn-debug requires Node v16+');
43
+ process.exit(1);
44
+ }
45
+
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
+ }
53
+ }
54
+
55
+ const {Generations} = require('@pkmn/data');
56
+
57
+ const {run} = require('../../build/tools/debug');
58
+
59
+ run(new Generations(Dex)).catch(err => {
60
+ console.error(err.message);
61
+ process.exit(1);
62
+ });
@@ -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
+ }
@@ -9,12 +9,12 @@ pub const Player = enum(u1) {
9
9
  P2,
10
10
 
11
11
  /// Returns a player's opponent.
12
- pub inline fn foe(self: Player) Player {
13
- return @intToEnum(Player, ~@enumToInt(self));
12
+ pub fn foe(self: Player) Player {
13
+ return @enumFromInt(~@intFromEnum(self));
14
14
  }
15
15
 
16
16
  /// Return's an identifier for the player's Pokémon at the one-indexed `id`.
17
- pub inline fn ident(self: Player, id: u3) ID {
17
+ pub fn ident(self: Player, id: u3) ID {
18
18
  assert(id > 0 and id <= 6);
19
19
  return .{ .id = id, .player = self };
20
20
  }
@@ -22,42 +22,38 @@ pub const Player = enum(u1) {
22
22
 
23
23
  test Player {
24
24
  try expectEqual(Player.P2, Player.P1.foe());
25
- try expectEqual(@as(u8, 0b0001), @bitCast(u8, Player.P1.ident(1)));
26
- try expectEqual(@as(u8, 0b1101), @bitCast(u8, Player.P2.ident(5)));
25
+ try expectEqual(0b0001, @as(u8, @bitCast(Player.P1.ident(1))));
26
+ try expectEqual(0b1101, @as(u8, @bitCast(Player.P2.ident(5))));
27
27
  }
28
28
 
29
29
  /// An identifier for a specific Pokémon in battle.
30
- pub const ID = packed struct {
30
+ pub const ID = packed struct(u8) {
31
31
  /// The one-indexed team slot of the Pokémon
32
32
  id: u3 = 0,
33
33
  /// The Pokémon's trainer.
34
34
  player: Player = .P1,
35
35
  _: u4 = 0,
36
36
 
37
- comptime {
38
- assert(@sizeOf(ID) == 1);
39
- }
40
-
41
37
  /// Converts the identifier into a number.
42
- pub inline fn int(self: ID) u4 {
43
- return @intCast(u4, @bitCast(u8, self));
38
+ pub fn int(self: ID) u4 {
39
+ return @intCast(@as(u8, @bitCast(self)));
44
40
  }
45
41
 
46
42
  /// Decodes the identifier from a number.
47
- pub inline fn from(id: u4) ID {
48
- return @bitCast(ID, @as(u8, id));
43
+ pub fn from(id: u4) ID {
44
+ return @bitCast(@as(u8, id));
49
45
  }
50
46
  };
51
47
 
52
48
  test ID {
53
- try expectEqual(@as(u8, 0b0001), @bitCast(u8, ID{ .player = .P1, .id = 1 }));
54
- try expectEqual(@as(u8, 0b1101), @bitCast(u8, ID{ .player = .P2, .id = 5 }));
55
- const id = ID{ .player = .P2, .id = 4 };
49
+ try expectEqual(0b0001, @as(u8, @bitCast(ID{ .player = .P1, .id = 1 })));
50
+ try expectEqual(0b1101, @as(u8, @bitCast(ID{ .player = .P2, .id = 5 })));
51
+ const id: ID = .{ .player = .P2, .id = 4 };
56
52
  try expectEqual(id, ID.from(id.int()));
57
53
  }
58
54
 
59
55
  /// A choice made by a player during battle.
60
- pub const Choice = packed struct {
56
+ pub const Choice = packed struct(u8) {
61
57
  /// The choice type.
62
58
  type: Choice.Type = .Pass,
63
59
  /// The choice data:
@@ -73,10 +69,6 @@ pub const Choice = packed struct {
73
69
  Move,
74
70
  Switch,
75
71
  };
76
-
77
- comptime {
78
- assert(@sizeOf(Choice) == 1);
79
- }
80
72
  };
81
73
 
82
74
  test Choice {
@@ -84,12 +76,12 @@ test Choice {
84
76
  const p2: Choice = .{ .type = .Switch, .data = 5 };
85
77
  try expectEqual(5, p2.data);
86
78
  try expectEqual(Choice.Type.Move, p1.type);
87
- try expectEqual(0b0001_0001, @bitCast(u8, p1));
88
- try expectEqual(0b0001_0110, @bitCast(u8, p2));
79
+ try expectEqual(0b0001_0001, @as(u8, @bitCast(p1)));
80
+ try expectEqual(0b0001_0110, @as(u8, @bitCast(p2)));
89
81
  }
90
82
 
91
83
  /// The result of the battle - all results other than 'None' should be considered terminal.
92
- pub const Result = packed struct {
84
+ pub const Result = packed struct(u8) {
93
85
  /// The type of result from the perspective of Player 1.
94
86
  /// `Error` is not possible when in Pokémon Showdown compatibility mode.
95
87
  type: Result.Type = .None,
@@ -117,13 +109,9 @@ pub const Result = packed struct {
117
109
  pub const Error: Result = .{ .type = .Error };
118
110
  /// The default result.
119
111
  pub const Default: Result = .{ .p1 = .Move, .p2 = .Move };
120
-
121
- comptime {
122
- assert(@sizeOf(Result) == 1);
123
- }
124
112
  };
125
113
 
126
114
  test Result {
127
- try expectEqual(0b0101_0000, @bitCast(u8, Result.Default));
128
- try expectEqual(0b1000_0000, @bitCast(u8, Result{ .p2 = .Switch }));
115
+ try expectEqual(0b0101_0000, @as(u8, @bitCast(Result.Default)));
116
+ try expectEqual(0b1000_0000, @as(u8, @bitCast(Result{ .p2 = .Switch })));
129
117
  }