@pkmn/engine 0.1.0-dev.5ec64ea5 → 0.1.0-dev.5fbc7aef

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 (87) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +141 -76
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +28 -6
  13. package/build/pkg/addon.js +42 -73
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/ids.json +12 -12
  16. package/build/pkg/data/layout.json +40 -5
  17. package/build/pkg/data/protocol.json +84 -13
  18. package/build/pkg/data.d.ts +27 -5
  19. package/build/pkg/data.js.map +1 -1
  20. package/build/pkg/gen1.d.ts +18 -20
  21. package/build/pkg/gen1.js +76 -51
  22. package/build/pkg/gen1.js.map +1 -1
  23. package/build/pkg/index.d.ts +57 -103
  24. package/build/pkg/index.js +23 -70
  25. package/build/pkg/index.js.map +1 -1
  26. package/build/pkg/protocol.d.ts +24 -3
  27. package/build/pkg/protocol.js +127 -104
  28. package/build/pkg/protocol.js.map +1 -1
  29. package/build/tools/debug.js +69 -74
  30. package/build/tools/debug.js.map +1 -1
  31. package/build/tools/display/debug.jsx +152 -0
  32. package/build/tools/display/debug.jsx.map +1 -0
  33. package/build/tools/display/index.js +152 -0
  34. package/build/tools/display/index.js.map +1 -0
  35. package/build/tools/display/ui.jsx +359 -0
  36. package/build/tools/display/ui.jsx.map +1 -0
  37. package/build/tools/display/util.js +33 -0
  38. package/build/tools/display/util.js.map +1 -0
  39. package/build.zig +337 -165
  40. package/package.json +71 -120
  41. package/src/bin/install-pkmn-engine +57 -26
  42. package/src/bin/pkmn-debug +3 -4
  43. package/src/lib/bindings/c.zig +200 -0
  44. package/src/lib/bindings/node.zig +117 -0
  45. package/src/lib/bindings/wasm.zig +50 -0
  46. package/src/lib/common/array.zig +70 -0
  47. package/src/lib/common/battle.zig +17 -0
  48. package/src/lib/common/data.zig +19 -31
  49. package/src/lib/common/debug.zig +51 -20
  50. package/src/lib/common/js.zig +114 -0
  51. package/src/lib/common/optional.zig +61 -0
  52. package/src/lib/common/options.zig +18 -3
  53. package/src/lib/common/protocol.zig +1050 -497
  54. package/src/lib/common/rational.zig +440 -0
  55. package/src/lib/common/rng.zig +31 -24
  56. package/src/lib/common/util.zig +44 -0
  57. package/src/lib/gen1/calc.zig +956 -0
  58. package/src/lib/gen1/chance.zig +1170 -0
  59. package/src/lib/gen1/data/moves.zig +203 -193
  60. package/src/lib/gen1/data/species.zig +6 -8
  61. package/src/lib/gen1/data/types.zig +12 -13
  62. package/src/lib/gen1/data.zig +104 -93
  63. package/src/lib/gen1/helpers.zig +138 -51
  64. package/src/lib/gen1/mechanics.zig +1364 -925
  65. package/src/lib/gen2/calc.zig +150 -0
  66. package/src/lib/gen2/chance.zig +896 -0
  67. package/src/lib/gen2/data/items.zig +15 -23
  68. package/src/lib/gen2/data/moves.zig +665 -573
  69. package/src/lib/gen2/data/species.zig +3 -5
  70. package/src/lib/gen2/data/types.zig +141 -36
  71. package/src/lib/gen2/data.zig +140 -104
  72. package/src/lib/gen2/generated.zig +801 -0
  73. package/src/lib/gen2/helpers.zig +58 -45
  74. package/src/lib/gen2/mechanics.zig +3582 -30
  75. package/src/lib/napi.h +2 -0
  76. package/src/lib/node.zig +14 -0
  77. package/src/lib/pkmn.zig +177 -22
  78. package/src/lib/wasm.zig +13 -0
  79. package/src/tools/display/dom.ts +93 -0
  80. package/src/tools/display/ui.css +354 -0
  81. package/build/test/display.js +0 -382
  82. package/build/test/display.js.map +0 -1
  83. package/src/lib/binding/node.zig +0 -229
  84. package/src/lib/binding/wasm.zig +0 -10
  85. package/src/lib/gen1/README.md +0 -784
  86. package/src/lib/gen2/README.md +0 -309
  87. package/src/test/display/pkmn.html.tmpl +0 -368
package/LICENSE CHANGED
@@ -1,6 +1,4 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2021-2023 pkmn contributors
1
+ Copyright (c) 2021-2026 pkmn contributors
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
4
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <br />
4
4
  <br />
5
5
  <a href="https://github.com/pkmn/engine/actions/workflows/test.yml">
6
- <img alt="Test Status" src="https://github.com/pkmn/engine/workflows/Tests/badge.svg" />
6
+ <img alt="Test Status" src="https://github.com/pkmn/engine/actions/workflows/test.yml/badge.svg" />
7
7
  </a>
8
8
  <a href="#status">
9
9
  <img alt="WIP" src="https://img.shields.io/badge/status-WIP-red.svg" />
@@ -12,13 +12,25 @@
12
12
  <img alt="License" src="https://img.shields.io/badge/License-MIT-blue.svg" />
13
13
  </a>
14
14
  </p>
15
+
16
+ > [!WARNING]
17
+ > **This project is under heavy development** and currently the `main` branch contains numerous breaking
18
+ > changes which may not work and which are not fully documented. Please **wait for the forthcoming
19
+ > initial v0.1 release** before depending on this project (code from the
20
+ > [`old`](https://github.com/pkmn/engine/releases/tag/old) tag can also be used to experiment with a
21
+ > more stable version of the codebase).
22
+
23
+ > [!IMPORTANT]
24
+ > While this project aims to support the most recent stable Zig release and the Zig master branch,
25
+ > **only the Zig master branch is currently supported** due to breaking changes upstream.
26
+
15
27
  <hr />
16
28
 
17
29
  A minimal, complete, Pokémon battle simulation engine optimized for performance and
18
30
  [designed](docs/DESIGN.md) for tooling, embedded systems, and [artificial
19
31
  intelligence](https://github.com/pkmn/0-ERROR) use cases. This engine aims to be a frame-accurate and
20
32
  [bug-for-bug compatible](http://www.catb.org/jargon/html/B/bug-for-bug-compatible.html)
21
- implementation of both Pokémon battles as defined by the original game code, and the [Pokémon
33
+ implementation of both Pokémon battles as defined by the original game code and the [Pokémon
22
34
  Showdown](https://pokemonshowdown.com/)[^1] simulator which represents Pokémon battling as practically
23
35
  interpreted online.
24
36
 
@@ -49,14 +61,19 @@ $ zig build --prefix /usr/local -Doptimize=ReleaseFast
49
61
  ```
50
62
 
51
63
  The Zig website has [installation instructions](https://ziglang.org/learn/getting-started/) which
52
- walk through how to install Zig on each platform - the engine code should work on Zig v0.11.0 dev
53
- build 2168 or greater, though tracks Zig's master branch so this may change in the future if
54
- breaking language changes are introduced:
64
+ walk through how to install Zig on each platform - ~~the engine code should work on Zig v0.14.0,
65
+ though tracks Zig's master branch so this may change in the future if breaking language changes are
66
+ introduced~~ the engine code currently only works on the Zig master branch due to breaking language
67
+ changes that have been introduced. Note that **due to a [bug in the Zig
68
+ compiler](https://github.com/ziglang/zig/issues/17768), building a version of [Zig from
69
+ source](https://codeberg.org/ziglang/zig#building-from-source) after having applied a
70
+ [patch](zig.patch) to revert [ziglang/zig#17391](https://github.com/ziglang/zig/pull/17391) is
71
+ recommended for performance**.
55
72
 
56
73
  `libpkmn` can be built with `-Dshowdown` to instead produce the Pokémon Showdown compatible
57
- `libpkmn-showdown` library. Furthermore, trace logging can be enabled through `-Dtrace`. The
58
- `libpkmn` and `libpkmn-showdown` objects available in the binary release are compiled with and
59
- without the `-Dtrace` flag respectively.
74
+ `libpkmn-showdown` library. Furthermore, protocol message logging can be enabled through `-Dlog`.
75
+ The `libpkmn` and `libpkmn-showdown` objects available in the binary release are both compiled with
76
+ the `-Dlog` flag by default.
60
77
 
61
78
  ### `@pkmn/engine`
62
79
 
@@ -67,44 +84,88 @@ $ npm install @pkmn/engine
67
84
  ```
68
85
 
69
86
  The driver depends on being able to find compiled Node/WASM addons in
70
- `node_modules/@pkmn/engine/build/lib` in order to be useful. When you install the package a
71
- [`postinstall` lifecycle script](https://docs.npmjs.com/cli/v8/using-npm/scripts) will run
72
- [`install-pkmn-engine`](src/bin/install-pkmn-engine) which will check for a compatible `zig`
73
- compiler (see above regarding minimum version) and download one to
74
- `node_module/@pkmn/engine/build/bin` if it can't find one, as well as looking for (and downloading,
75
- if necessary) the required Node headers needed to successfully build the addons natively.
87
+ `node_modules/@pkmn/engine/build/lib` to be useful. When you install the package a [`postinstall`
88
+ lifecycle script](https://docs.npmjs.com/cli/v8/using-npm/scripts) runs
89
+ [`install-pkmn-engine`](src/bin/install-pkmn-engine) which checks for a compatible `zig` compiler
90
+ (see earlier regarding minimum version) and download one to `node_module/@pkmn/engine/build/bin` if
91
+ it can't find one, as well as looking for (and downloading, if necessary) the required Node headers
92
+ needed to successfully build the addons natively.
76
93
 
77
94
  **If you have configured `npm` to `--ignore-scripts` you must either run `npx install-pkmn-engine`
78
95
  directly or build the addons manually and place the artifacts in the expected paths.**
79
96
 
80
97
  ### `pkmn`
81
98
 
82
- Until the [Zig package manager](https://github.com/ziglang/zig/projects/4) is completed, the
83
- recommended way of using the `pkmn` package in Zig is by either copying this repository into your
84
- project or by using [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) and then
85
- adding the following to your `build.zig`:
99
+ To include `pkmn`, add it as a dependency in your project's
100
+ [`build.zig.zon`](https://github.com/ziglang/zig/blob/master/doc/build.zig.zon.md) using [Zig's
101
+ Package Management
102
+ system](https://ziglang.org/download/0.11.0/release-notes.html#Package-Management):
103
+
104
+
105
+ ```zig
106
+ .dependencies = .{
107
+ .pkmn = .{
108
+ .url = "https://github.com/pkmn/engine/archive/RELEASE.tar.gz",
109
+ },
110
+ },
111
+ ```
112
+
113
+ Replace `RELEASE` with the tag for the [release](https://github.com/pkmn/engine/releases) you wish
114
+ to use (e.g. `0.1.0` or `nightly`) or the specific hash of any commit. Next, run `zig build` and
115
+ copy the expected hash from the error message into your `build.zig.zon` alongside the `.url`:
116
+
117
+ ```sh
118
+ $ zig build
119
+ error: dependency is missing hash field
120
+ .url = "https://github.com/pkmn/engine/archive/nightly.tar.gz",
121
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122
+ note: expected .hash = "122056b93b403033cb7d9bed96a02c2eb8cc1275515976167726ada6eb4207e8ef8a",
123
+ ```
124
+
125
+ You must add `build.zig.zon` to the engine package to be able to import it in your `build.zig`:
126
+
127
+ ```zig
128
+ .{
129
+ .name = .pkmn,
130
+ .version = "0.1.0-dev",
131
+ .minimum_zig_version = "0.16.0",
132
+ .paths = .{
133
+ "build.zig",
134
+ "docs",
135
+ "LICENSE",
136
+ "README.md",
137
+ "src/include",
138
+ "src/lib",
139
+ },
140
+ .fingerprint = 0x74a002f44f200a47,
141
+ }
142
+ ```
143
+
144
+ Note that this hash is **not** going to be the same as the commit hash. After adding the hash to
145
+ your `build.zig.zon` you will be able to import the `pkmn` package's build helpers into your
146
+ `build.zig`:
86
147
 
87
148
  ```zig
88
149
  const std = @import("std");
89
- const pkmn = @import("lib/pkmn/build.zig");
90
150
 
91
- pub fn build(b: *std.build.Builder) void {
151
+ pub fn build(b: *std.Build) void {
92
152
  ...
93
- exe.addModule("pkmn", pkmn.module(b, .{ .showdown = true, .trace = true }));
153
+ const pkmn = b.dependency("pkmn", .{ .showdown = true, .log = true });
154
+ exe.root_module.addImport("pkmn", pkmn.module("pkmn"));
94
155
  ...
95
156
  }
96
157
  ```
97
158
 
98
- The engine's `build.zig` exposes a `module` function that takes an options structure to allow for
99
- configuring whether or not Pokémon Showdown compatibility mode or trace logs should be enabled.
100
- Alternatively, you may set options via a `pkmn_options` [root source file
159
+ The `pkmn` package can be configured with various options such as whether or not Pokémon Showdown
160
+ compatibility mode or protocol message logging should be enabled. Alternatively, you may set options
161
+ via a `pkmn_options` [root source file
101
162
  declaration](https://ziglang.org/documentation/master/#Root-Source-File). There are several
102
163
  undocumented internal options that can be tweaked as well via build or root options, though these
103
- options are not officially supported, affect correctness, and may change meaning or behavior without
164
+ options aren't officially supported, affect correctness, and may change meaning or behavior without
104
165
  warning. Use at your own risk.
105
166
 
106
167
  ```zig
107
- pub const pkmn_options = .{ .showdown = true, .trace = true };
168
+ pub const pkmn_options = .{ .showdown = true, .log = true };
108
169
  ```
109
170
 
110
171
  ## Usage
@@ -112,99 +173,103 @@ pub const pkmn_options = .{ .showdown = true, .trace = true };
112
173
  For each Pokémon generation, the engine provides a battle structure with two functions - a function
113
174
  to `update` the battle's state based on both players' choices and a function that defines which
114
175
  `choices` are valid at that decision point (at the beginning of a battle both player's must "pass"
115
- as their first choice in order to switch in the first member of their party). If `-Dtrace` logging
116
- is enabled each update will produce logs which can be decoded into a buffer (`LOGS_SIZE` constants
117
- are provided to make it easy to allocate a buffer of the correct size). Each `update` will return a
118
- result to indicate either that the battle has terminated or which types of choices will be available
119
- for each player.
176
+ as their first choice to switch in the first member of their party). If `-Dlog` protocol logging is
177
+ enabled each update produces logs which can be decoded into a buffer (`LOGS_SIZE` constants are
178
+ provided to make it easy to allocate a buffer of the correct size). Each `update` returns a result
179
+ to indicate either that the battle has terminated or which types of choices are available for each
180
+ player.
120
181
 
121
182
  Unlike [Pokémon Showdown's
122
183
  SIM-PROTOCOL](https://github.com/smogon/pokemon-showdown/blob/master/sim/SIM-PROTOCOL.md#choice-requests)
123
184
  which provides a rich request object at each decision point, the pkmn engine computes the possible
124
- choices on demand based on the result of the previous `update` and the battle state (`OPTIONS_SIZE`
185
+ choices on demand based on the result of the previous `update` and the battle state (`CHOICES_SIZE`
125
186
  is a good size to initialize the buffer passed to `choices`). **Attempting to update the battle with
126
187
  a choice not present in the options returned by `choices` is undefined behavior and may corrupt
127
188
  state or cause the engine to crash.**
128
189
 
129
190
  Battles may be played out from any point but to freshly initialize a battle which is yet to start
130
191
  the turn count and active Pokémon should be zeroed out (driver code should handle this for you).
131
- Most driver code should also provide helpers to make initialize a battle convenient - check the
192
+ Most driver code should also provide helpers to make initializing battle convenient - check the
132
193
  respective documentation and examples for the bindings being used. The engine's [protocol
133
194
  documentation](docs/PROTOCOL.md) goes into greater detail on the specifics of updates and the
134
195
  potential logs that may result.
135
196
 
136
197
  *The snippets below are meant to merely illustrate in broad strokes how the pkmn engine can be used
137
- \- the [`examples`](src/examples) directory contains fully commented and runnable code.*
198
+ \- the [`examples`](examples) directory contains fully commented and runnable code.*
138
199
 
139
200
  ### C
140
201
 
141
202
  [`pkmn.h`](src/include/pkmn.h) exports the C API for `libpkmn`. Symbols are all prefixed with
142
- `pkmn_` to avoid name collisions. If `-Dtrace` is enabled and logging throws an error then the error
143
- will be encoded in the `pkmn_result` and can be checked with `pkmn_error`.
203
+ `pkmn_` to avoid name collisions. If `-Dlog` is enabled and logging throws an error then the error
204
+ is encoded in the `pkmn_result` and can be checked with `pkmn_error`.
144
205
 
145
206
  ```c
146
207
  #include <pkmn.h>
147
208
 
209
+ pkmn_battle_options options;
210
+ pkmn_gen1_battle_options_set(&options, ...);
148
211
  pkmn_battle battle = ...;
149
- uint8_t buf[PKMN_LOGS_SIZE];
212
+
150
213
  pkmn_result result;
151
214
  pkmn_choice c1 = 0, c2 = 0;
152
- while (!pkmn_result_type(result = pkmn_battle_update(&battle, c1, c2, buf, PKMN_LOGS_SIZE))) {
215
+ while (!pkmn_result_type(result = pkmn_battle_update(&battle, c1, c2, &options))) {
153
216
  c1 = choose(PKMN_PLAYER_P1, pkmn_result_p1(result));
154
217
  c2 = choose(PKMN_PLAYER_P2, pkmn_result_p2(result));
218
+ pkmn_gen1_battle_options_set(&options, ...);
155
219
  }
156
220
  if (pkmn_error(result)) exit(1);
157
221
  ```
158
222
 
159
- [(full code)](src/examples/c/example.c)
223
+ [(full code)](examples/c/example.c)
160
224
 
161
- The C API does not export any helpers for creating or accessing the opaque battle objects - it is
225
+ The C API doesn't export any helpers for creating or accessing the opaque battle objects - it's
162
226
  instead intended to be used as the foundation for more ergonomic bindings in [other
163
227
  languages](#other) (the lack of namespaces and bit fields having an implementation-defined layout in
164
228
  C are the main contributing factors to the sparseness of what `libpkmn` chooses to expose).
165
229
 
166
230
  ### JavaScript / TypeScript
167
231
 
168
- `@pkmn/engine` depends on the [`@pkmn/data`](https://www.npmjs.com/package/@pkmn/data) which
232
+ `@pkmn/engine` depends on the [`@pkmn/data`](https://www.npmjs.com/package/@pkmn/data) package which
169
233
  requires a `Dex` implementation to be provided as well. The `Battle.create` function can be used to
170
234
  initialize a `Battle` from the beginning, or `Battle.restore` can be used to re-instantiate a battle
171
- which is in already progress. If logging is enabled the output can be turned into Pokémon Showdown
235
+ which is already in progress. If logging is enabled the output can be turned into Pokémon Showdown
172
236
  protocol via `Log.parse`.
173
237
 
174
238
  ```ts
175
239
  import {Dex} from '@pkmn/dex';
176
240
  import {Generations} from '@pkmn/data';
177
- import {Battle} from '@pkmn/engine';
241
+ import {Battle, Choice} from '@pkmn/engine';
178
242
 
179
243
  const gens = new Generations(Dex);
180
244
  const battle = Battle.create(...);
181
245
 
182
- const choose = (n: number) => Math.random() * n;
246
+ const choose = (choices: Choice[]) =>
247
+ choices[Math.floor(Math.random() * choices.length)];
183
248
 
184
249
  let result: Result;
185
250
  let c1: Choice, c2: Choice;
186
251
  while (!(result = battle.update(c1, c2)).type) {
187
- c1 = battle.choose('p1', result, choose);
188
- c2 = battle.choose('p2', result, choose);
252
+ c1 = choose(battle.choices('p1', result));
253
+ c2 = choose(battle.choices('p2', result));
189
254
  }
190
255
 
191
256
  console.log(result);
192
257
  ```
193
258
 
194
- [(full code)](src/examples/js/example.ts)
259
+ [(full code)](examples/js/example.ts)
195
260
 
196
- By default, the `@pkmn/engine` package will compile the engine with `-Dshowdown`, though by running
261
+ By default, the `@pkmn/engine` package compiles the engine with `-Dshowdown`, though by running
197
262
  `install-pkmn-engine` directly and passing in `--options` you can override this default and build
198
263
  different configurations of the extension for the driver to use. The driver can support
199
264
  configurations both with and without Pokémon Showdown compatibility simultaneously if present. On
200
- update, the post-install script will attempt to rebuild whichever extensions it finds with the same
265
+ update, the post-install script attempts to rebuild whichever extensions it finds with the same
201
266
  configuration parameters that were originally used meaning updating to the newest version of the
202
267
  library should be seamless.
203
268
 
204
269
  Despite relying on the native engine code, the `@pkmn/engine` code is designed to also work in
205
270
  browsers which support [WebAssembly](https://webassembly.org/). Running `npm run start:web` from the
206
- [`examples`](src/examples/js) directory will start a server that can be used to demonstrate the
207
- engine running in the browser.
271
+ [`examples`](examples/js) directory starts a server that can be used to demonstrate the engine
272
+ running in the browser.
208
273
 
209
274
  ### Zig
210
275
 
@@ -215,29 +280,29 @@ be used when logging is enabled to allow for easily printing e.g. to standard ou
215
280
  const std = @import("std");
216
281
  const pkmn = @import("pkmn");
217
282
 
218
- var random = std.rand.DefaultPrng.init(seed).random();
219
- var options: [pkmn.OPTIONS_SIZE]pkmn.Choice = undefined;
283
+ var random = std.Random.DefaultPrng.init(seed).random();
284
+ var choices: [pkmn.CHOICES_SIZE]pkmn.Choice = undefined;
220
285
 
221
286
  var battle = ...
222
- var log = ...
287
+ var options = pkmn.battle.options(...);
223
288
 
224
289
  var c1 = pkmn.Choice{};
225
290
  var c2 = pkmn.Choice{};
226
291
 
227
- var result = try battle.update(c1, c2, log);
228
- while (result.type == .None) : (result = try battle.update(c1, c2, log)) {
229
- c1 = options[random.uintLessThan(u8, battle.choices(.P1, result.p1, &options))];
230
- c2 = options[random.uintLessThan(u8, battle.choices(.P2, result.p2, &options))];
292
+ var result = try battle.update(c1, c2, &options);
293
+ while (result.type == .None) : (result = try battle.update(c1, c2, &options)) {
294
+ c1 = choices[random.uintLessThan(u8, battle.choices(.P1, result.p1, &choices))];
295
+ c2 = choices[random.uintLessThan(u8, battle.choices(.P2, result.p2, &choices))];
231
296
  }
232
297
 
233
298
  std.debug.print("{}\n", .{result.type});
234
299
  ```
235
300
 
236
- [(full code)](src/examples/zig/example.zig)
301
+ [(full code)](examples/zig/example.zig)
237
302
 
238
303
  The Zig package also supports some APIs which are difficult to expose elsewhere such as the
239
304
  `FixedRNG` which allows you to fully specify the exact RNG frames (which can be useful for ensuring
240
- certain outcomes/effects always occur) for a battle, though doing so will change the size of the
305
+ certain outcomes/effects always occur) for a battle, though doing so changes the size of the
241
306
  `Battle` object.
242
307
 
243
308
  ### Other
@@ -248,12 +313,12 @@ simplify the process even further [`src/data`](src/data) contains JSON dumps of
248
313
  data, structure sizes and offsets, and protocol information used by the reference driver code. The
249
314
  following is a list of known `libpkmn` bindings written by developers outside of the [pkmn
250
315
  organization](https://github.com/pkmn) that may be helpful (though note that these projects may not
251
- necessarily be up-to-date/complete/correct - inclusion in this list does not imply endorsement):
316
+ necessarily be up-to-date/complete/correct - inclusion in this list doesn't imply endorsement):
252
317
 
253
- | Language | License | URL |
254
- | ---------- | --------- | --------------------------------------- |
255
- | **C++** | `BSL-1.0` | https://github.com/pasyg/engine-wrapper |
256
- | **Python** | `MIT` | https://github.com/AnnikaCodes/PyKMN |
318
+ | Language | License | URL |
319
+ | ---------- | --------- | ------------------------------------ |
320
+ | **C++** | `BSL-1.0` | https://github.com/pasyg/wrapsire |
321
+ | **Python** | `MIT` | https://github.com/AnnikaCodes/PyKMN |
257
322
 
258
323
 
259
324
  #### `pkmn-debug`
@@ -276,7 +341,7 @@ By default, `pkmn-debug` expects to read [binary debug protocol](docs/PROTOCOL.m
276
341
  $ <cmd> | pkmn-debug > index.html
277
342
  ```
278
343
 
279
- Alternatively, if a single filename argument is passed to the `pkmn-debug` it will read from that
344
+ Alternatively, if a single filename argument is passed to the `pkmn-debug` it reads from that
280
345
  instead:
281
346
 
282
347
  ```sh
@@ -298,14 +363,14 @@ Currently, most of the foundational work from stage 0 is done:
298
363
 
299
364
  - [benchmark and integration testing](src/test) infrastructure
300
365
  - [documentation](docs) about design, research, methodology, etc
301
- - definition and implementation of the [protocol](docs/PROTOCOL.md) that will be used by the engine
366
+ - definition and implementation of the [protocol](docs/PROTOCOL.md) that's used by the engine
302
367
 
303
- **Stage 1 is currently in progress** and will see the implementation of the actual Generation I & II
368
+ **Stage 1 is currently in progress** and involves the implementation of the actual Generation I & II
304
369
  battle engines, followed by Generation III & IV in stage 2. The implementation of further Pokémon
305
- generations is in scope for the project but should not be considered as part of the immediate
306
- roadmap (i.e. exploring the options for broadening support for old generation APIs will be given
307
- higher priority than implementing more modern generations). Furthermore, implementation of modern
308
- generations is soft-blocked on the [availability of high quality
370
+ generations is in scope for the project but shouldn't be considered part of the immediate roadmap
371
+ (i.e. exploring the options for broadening support for old generation APIs is given higher priority
372
+ than implementing more modern generations). Furthermore, implementation of modern generations is
373
+ soft-blocked on the [availability of high quality
309
374
  decompilations](https://github.com/orgs/pret/repositories) of the original games in question.
310
375
 
311
376
  Certain features will always be deemed **out of scope**:
@@ -323,10 +388,10 @@ The pkmn engine is distributed under the terms of the [MIT License](LICENSE).
323
388
  [^1]: In the case of Pokémon Showdown, only bugs which stem from a misimplementation of specific
324
389
  effects are reproduced in the engine, bugs which are the result of a misunderstanding of the
325
390
  fundamental mechanics of Pokémon or which simply arise due to specific Pokémon Showdown
326
- implementation details that are not replicable without making the same (incorrect) architectural
327
- choices are not. Furthermore, the "Pokémon Showdown" code referenced by this project includes
391
+ implementation details that aren't replicable without making the same (incorrect) architectural
392
+ choices aren't. Furthermore, the "Pokémon Showdown" code referenced by this project includes
328
393
  several [patches](docs/TESTING.md#patches) to improve accuracy and smooth over some of the more
329
394
  egregious implementation issues. In practical terms, the vast majority of games played out in the
330
- pkmn engine's compatibility mode and on this patched Pokémon Showdown simulator will be the same,
331
- it is only in a well defined and documented set of circumstances where the two implementations
395
+ pkmn engine's compatibility mode and on this patched Pokémon Showdown simulator are the same,
396
+ it's only in a well defined and documented set of circumstances where the two implementations
332
397
  diverge.
@@ -0,0 +1,3 @@
1
+ import type { Argument } from '../addon';
2
+ export declare function load(showdown: boolean, addon?: Argument): Promise<import("../addon").Bindings<boolean>>;
3
+ export declare function loadSync(): void;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.load = load;
4
+ exports.loadSync = loadSync;
5
+ const wasm_1 = require("./wasm");
6
+ async function load(showdown, addon) {
7
+ if (typeof addon === 'string' && addon !== 'wasm') {
8
+ throw new Error('Unable to load native addons in the browser!');
9
+ }
10
+ let wasm;
11
+ if (addon instanceof WebAssembly.Instance) {
12
+ wasm = addon;
13
+ }
14
+ else if (addon instanceof WebAssembly.Module) {
15
+ try {
16
+ wasm = (await WebAssembly.instantiate(addon));
17
+ }
18
+ catch (err) {
19
+ if (!(err instanceof Error))
20
+ throw err;
21
+ throw new Error(`Could not instantiate WASM module!\n${err.message}`);
22
+ }
23
+ }
24
+ else {
25
+ const name = `pkmn${showdown ? '-showdown' : ''}.wasm`;
26
+ try {
27
+ const response = !addon || addon === 'wasm' ? fetch(name)
28
+ : addon instanceof URL ? fetch(addon) : addon;
29
+ wasm = (await WebAssembly.instantiateStreaming(response)).instance;
30
+ }
31
+ catch (err) {
32
+ if (!(err instanceof Error))
33
+ throw err;
34
+ const message = !addon || addon === 'wasm'
35
+ ? `Could not find ${name} - did you run \`npx install-pkmn-engine\`?`
36
+ : addon instanceof URL
37
+ ? `Could not fetch WASM module from '${addon.href}'!`
38
+ : 'Could not instantiate WASM module!';
39
+ throw new Error(`${message}\n${err.message}`);
40
+ }
41
+ }
42
+ return (0, wasm_1.toBindings)(wasm.exports);
43
+ }
44
+ function loadSync() {
45
+ throw new Error('You must `initialize` the WASM addon before using the engine!');
46
+ }
47
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../src/pkg/addon/browser.ts"],"names":[],"mappings":";;AAKA,oBAiCC;AAED,4BAEC;AAvCD,iCAAkC;AAE3B,KAAK,UAAU,IAAI,CAAC,QAAiB,EAAE,KAAgB;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,IAA0B,CAAC;IAC/B,IAAI,KAAK,YAAY,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC1C,IAAI,GAAG,KAAK,CAAC;IACf,CAAC;SAAM,IAAI,KAAK,YAAY,WAAW,CAAC,MAAM,EAAE,CAAC;QAC/C,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;gBAAE,MAAM,GAAG,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,uCAAuC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,GAAG,OAAO,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,CAAC,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;gBACvD,CAAC,CAAE,KAAiC,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAC7E,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;gBAAE,MAAM,GAAG,CAAC;YACvC,MAAM,OAAO,GAAG,CAAC,KAAK,IAAI,KAAK,KAAK,MAAM;gBACxC,CAAC,CAAC,kBAAkB,IAAI,6CAA6C;gBACrE,CAAC,CAAE,KAAiC,YAAY,GAAG;oBACjD,CAAC,CAAC,qCAAsC,KAAa,CAAC,IAAI,IAAI;oBAC9D,CAAC,CAAC,oCAAoC,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,IAAA,iBAAU,EAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,QAAQ;IACtB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Argument } from '../addon';
2
+ export declare function load(showdown: boolean, addon?: Argument): Promise<any>;
3
+ export declare function loadSync(showdown: boolean): any;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.load = load;
37
+ exports.loadSync = loadSync;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const wasm_1 = require("./wasm");
41
+ const ROOT = path.join(__dirname, '..', '..', '..');
42
+ const LIB = path.join(ROOT, 'build', 'lib');
43
+ const NODE = [path.join(LIB, 'pkmn.node'), path.join(LIB, 'pkmn-showdown.node')];
44
+ const WASM = [path.join(LIB, 'pkmn.wasm'), path.join(LIB, 'pkmn-showdown.wasm')];
45
+ async function load(showdown, addon) {
46
+ if (addon === undefined || typeof addon === 'string' && addon !== 'wasm') {
47
+ try {
48
+ return require(!addon || addon === 'node' ? NODE[+showdown] : addon).engine;
49
+ }
50
+ catch (err) {
51
+ if (!(err instanceof Error))
52
+ return err;
53
+ if (addon && addon !== 'node') {
54
+ throw new Error(`Unable to load native addon: '${addon}'\n${err.message}`);
55
+ }
56
+ else {
57
+ throw error(NODE[+showdown], err);
58
+ }
59
+ }
60
+ }
61
+ let wasm;
62
+ if (addon === 'wasm') {
63
+ try {
64
+ wasm = (await WebAssembly.instantiate(fs.readFileSync(WASM[+showdown]))).instance;
65
+ }
66
+ catch (err) {
67
+ throw error(WASM[+showdown], err);
68
+ }
69
+ }
70
+ else if (addon instanceof WebAssembly.Instance) {
71
+ wasm = addon;
72
+ }
73
+ else if (addon instanceof WebAssembly.Module) {
74
+ try {
75
+ wasm = (await WebAssembly.instantiate(addon));
76
+ }
77
+ catch (err) {
78
+ if (!(err instanceof Error))
79
+ throw err;
80
+ throw new Error(`Could not instantiate WASM module!\n${err.message}`);
81
+ }
82
+ }
83
+ else {
84
+ try {
85
+ const response = addon instanceof Promise ? addon : fetch(addon);
86
+ wasm = (await WebAssembly.instantiateStreaming(response)).instance;
87
+ }
88
+ catch (err) {
89
+ if (!(err instanceof Error))
90
+ throw err;
91
+ throw new Error(`Could not instantiate WASM module!\n${err.message}`);
92
+ }
93
+ }
94
+ return (0, wasm_1.toBindings)(wasm.exports);
95
+ }
96
+ function loadSync(showdown) {
97
+ try {
98
+ return require(NODE[+showdown]).engine;
99
+ }
100
+ catch {
101
+ return undefined;
102
+ }
103
+ }
104
+ function error(file, err) {
105
+ if (!(err instanceof Error))
106
+ return err;
107
+ const message = `Could not find ${path.basename(file)} addon`;
108
+ return new Error(`${message} - did you run \`npx install-pkmn-engine\`?\n${err.message}`);
109
+ }
110
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../../src/pkg/addon/node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Bindings } from '../addon';
2
+ export declare function toBindings<T extends boolean>(w: WebAssembly.Exports): Bindings<T>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toBindings = toBindings;
4
+ const data_1 = require("../data");
5
+ function toBindings(w) {
6
+ const memory = new Uint8Array(w.memory.buffer);
7
+ return {
8
+ options: {
9
+ showdown: !!memory[w.SHOWDOWN.valueOf()],
10
+ log: !!memory[w.LOG.valueOf()],
11
+ chance: !!memory[w.CHANCE.valueOf()],
12
+ calc: !!memory[w.CALC.valueOf()],
13
+ },
14
+ bindings: [toBinding(1, w)],
15
+ };
16
+ }
17
+ function toBinding(gen, w) {
18
+ const prefix = `GEN${gen}`;
19
+ const buf = w.memory.buffer;
20
+ const constants = new Uint32Array(buf);
21
+ const CHOICES_SIZE = constants[w[`${prefix}_CHOICES_SIZE`].valueOf() / 4];
22
+ const LOGS_SIZE = constants[w[`${prefix}_LOGS_SIZE`].valueOf() / 4];
23
+ const size = data_1.LAYOUT[gen - 1].sizes.Battle;
24
+ const update = w[`${prefix}_update`];
25
+ const choices = w[`${prefix}_choices`];
26
+ const memory = new Uint8Array(buf);
27
+ return {
28
+ CHOICES_SIZE,
29
+ LOGS_SIZE,
30
+ update(battle, c1, c2, log) {
31
+ const bytes = new Uint8Array(battle);
32
+ memory.set(bytes, 0);
33
+ let result;
34
+ if (log) {
35
+ result = update(0, c1, c2, size);
36
+ new Uint8Array(log).set(memory.subarray(size, size + LOGS_SIZE));
37
+ }
38
+ else {
39
+ result = update(0, c1, c2, 0);
40
+ }
41
+ bytes.set(memory.subarray(0, size));
42
+ return result;
43
+ },
44
+ choices(battle, player, request, options) {
45
+ const opts = new Uint8Array(options);
46
+ memory.set(new Uint8Array(battle));
47
+ const n = choices(0, player, request, size);
48
+ opts.set(memory.subarray(size, size + n));
49
+ return n;
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=wasm.js.map