@pkmn/engine 0.1.0-dev.ce4c0b2a → 0.1.0-dev.d127cb34

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 (73) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +23 -15
  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 +25 -5
  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/data.js.map +1 -1
  17. package/build/pkg/gen1.d.ts +15 -18
  18. package/build/pkg/gen1.js +28 -19
  19. package/build/pkg/gen1.js.map +1 -1
  20. package/build/pkg/index.d.ts +37 -100
  21. package/build/pkg/index.js +23 -75
  22. package/build/pkg/index.js.map +1 -1
  23. package/build/pkg/protocol.d.ts +11 -2
  24. package/build/pkg/protocol.js.map +1 -1
  25. package/build/tools/debug.js +56 -13
  26. package/build/tools/debug.js.map +1 -1
  27. package/build/tools/display/debug.jsx +19 -81
  28. package/build/tools/display/debug.jsx.map +1 -1
  29. package/build/tools/display/index.js +35 -15
  30. package/build/tools/display/index.js.map +1 -1
  31. package/build/tools/display/ui.jsx +27 -16
  32. package/build/tools/display/ui.jsx.map +1 -1
  33. package/build/tools/display/util.js +141 -2
  34. package/build/tools/display/util.js.map +1 -1
  35. package/build.zig +330 -245
  36. package/package.json +36 -128
  37. package/src/bin/install-pkmn-engine +25 -9
  38. package/src/lib/bindings/c.zig +201 -0
  39. package/src/lib/bindings/node.zig +117 -0
  40. package/src/lib/bindings/wasm.zig +51 -0
  41. package/src/lib/common/array.zig +5 -12
  42. package/src/lib/common/debug.zig +45 -34
  43. package/src/lib/common/js.zig +3 -9
  44. package/src/lib/common/optional.zig +26 -26
  45. package/src/lib/common/options.zig +2 -0
  46. package/src/lib/common/protocol.zig +228 -197
  47. package/src/lib/common/rational.zig +278 -285
  48. package/src/lib/common/rng.zig +1 -3
  49. package/src/lib/common/util.zig +34 -31
  50. package/src/lib/gen1/calc.zig +220 -195
  51. package/src/lib/gen1/chance.zig +184 -108
  52. package/src/lib/gen1/data/moves.zig +2 -4
  53. package/src/lib/gen1/data/species.zig +1 -3
  54. package/src/lib/gen1/data/types.zig +3 -4
  55. package/src/lib/gen1/data.zig +12 -16
  56. package/src/lib/gen1/helpers.zig +35 -40
  57. package/src/lib/gen1/mechanics.zig +147 -127
  58. package/src/lib/gen2/calc.zig +12 -23
  59. package/src/lib/gen2/chance.zig +38 -26
  60. package/src/lib/gen2/data/items.zig +1 -3
  61. package/src/lib/gen2/data/moves.zig +2 -4
  62. package/src/lib/gen2/data/species.zig +1 -3
  63. package/src/lib/gen2/data/types.zig +7 -5
  64. package/src/lib/gen2/data.zig +19 -22
  65. package/src/lib/gen2/generated.zig +18 -23
  66. package/src/lib/gen2/helpers.zig +32 -31
  67. package/src/lib/gen2/mechanics.zig +58 -55
  68. package/src/lib/napi.h +2 -0
  69. package/src/lib/node.zig +4 -121
  70. package/src/lib/pkmn.zig +98 -19
  71. package/src/lib/wasm.zig +9 -9
  72. package/src/tools/display/dom.ts +43 -31
  73. package/src/tools/display/ui.css +12 -9
@@ -1,44 +1,37 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("../pkmn.zig");
4
-
5
- const common = @import("../common/data.zig");
6
1
  const DEBUG = @import("../common/debug.zig").print;
7
- const protocol = @import("../common/protocol.zig");
8
- const rng = @import("../common/rng.zig");
9
2
 
3
+ const builtin = @import("builtin");
4
+ const common = @import("../common/data.zig");
10
5
  const data = @import("data.zig");
11
6
  const generated = @import("generated.zig");
12
-
13
- const assert = std.debug.assert;
14
-
15
- const expectEqual = std.testing.expectEqual;
16
- const expectEqualSlices = std.testing.expectEqualSlices;
17
- const expectEqualStrings = std.testing.expectEqualStrings;
18
-
19
- const showdown = pkmn.options.showdown;
20
-
21
- const Choice = common.Choice;
22
- const ID = common.ID;
23
- const Player = common.Player;
24
- const Result = common.Result;
7
+ const pkmn = @import("../pkmn.zig");
8
+ const protocol = @import("../common/protocol.zig");
9
+ const rng = @import("../common/rng.zig");
10
+ const std = @import("std");
25
11
 
26
12
  const Activate = protocol.Activate;
13
+ const ActivePokemon = data.ActivePokemon;
14
+ const assert = std.debug.assert;
27
15
  const Boost = protocol.Boost;
16
+ const Boosts = data.Boosts;
17
+ const Choice = common.Choice;
28
18
  const Damage = protocol.Damage;
19
+ const Effectiveness = data.Effectiveness;
29
20
  const End = protocol.End;
21
+ const expectEqual = std.testing.expectEqual;
22
+ const expectEqualSlices = std.testing.expectEqualSlices;
23
+ const expectEqualStrings = std.testing.expectEqualStrings;
30
24
  const Fail = protocol.Fail;
31
- const Heal = protocol.Heal;
32
- const Start = protocol.Start;
33
-
34
25
  const Gen12 = rng.Gen12;
35
-
36
- const ActivePokemon = data.ActivePokemon;
37
- const Boosts = data.Boosts;
38
- const Effectiveness = data.Effectiveness;
26
+ const Heal = protocol.Heal;
27
+ const ID = common.ID;
39
28
  const Item = data.Item;
40
29
  const Move = data.Move;
30
+ const Player = common.Player;
31
+ const Result = common.Result;
32
+ const showdown = pkmn.options.showdown;
41
33
  const Side = data.Side;
34
+ const Start = protocol.Start;
42
35
  const Stats = data.Stats;
43
36
  const Status = data.Status;
44
37
  const TriAttack = data.TriAttack;
@@ -137,11 +130,11 @@ fn start(battle: anytype, options: anytype) !Result {
137
130
 
138
131
  const p1_slot = findFirstAlive(p1);
139
132
  assert(!showdown or p1_slot == 1);
140
- if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
133
+ if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
141
134
 
142
135
  const p2_slot = findFirstAlive(p2);
143
136
  assert(!showdown or p2_slot == 1);
144
- if (p2_slot == 0) return Result.Win;
137
+ if (p2_slot == 0) return .Win;
145
138
 
146
139
  try switchIn(battle, .P1, p1_slot, .Initial, options);
147
140
  try switchIn(battle, .P2, p2_slot, .Initial, options);
@@ -689,7 +682,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
689
682
 
690
683
  // Type effectiveness matchup precedence only matters with (NVE, SE)
691
684
  if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
692
- types.type1.precedence() > types.type2.precedence())
685
+ Type.precedence(state.type, types.type1) > Type.precedence(state.type, types.type2))
693
686
  {
694
687
  assert(eff2 != neutral);
695
688
  d = d *% eff2 / 10;
@@ -826,13 +819,13 @@ fn checkFaint(
826
819
 
827
820
  if (player_out and foe_out) {
828
821
  try options.log.tie(.{});
829
- return Result.Tie;
822
+ return .Tie;
830
823
  } else if (player_out) {
831
824
  try options.log.win(.{player.foe()});
832
- return if (player == .P1) Result.Lose else Result.Win;
825
+ return if (player == .P1) .Lose else .Win;
833
826
  } else if (foe_out) {
834
827
  try options.log.win(.{player});
835
- return if (player == .P1) Result.Win else Result.Lose;
828
+ return if (player == .P1) .Win else .Lose;
836
829
  }
837
830
 
838
831
  const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
@@ -1200,16 +1193,16 @@ fn updateSideCondition(
1200
1193
  fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
1201
1194
  battle.turn += 1;
1202
1195
 
1203
- if (showdown and battle.turn >= 1000) {
1196
+ if (pkmn.options.mod and battle.turn >= 1000) {
1204
1197
  try options.log.tie(.{});
1205
- return Result.Tie;
1198
+ return .Tie;
1206
1199
  } else if (battle.turn >= 65535) {
1207
- return Result.Error;
1200
+ return .Error;
1208
1201
  }
1209
1202
 
1210
1203
  try options.log.turn(.{battle.turn});
1211
1204
 
1212
- return Result.Default;
1205
+ return .Default;
1213
1206
  }
1214
1207
 
1215
1208
  pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytype) !void {
@@ -1434,7 +1427,8 @@ pub const Effects = struct {
1434
1427
  if (move.type.special() != (last == .MirrorCoat)) return;
1435
1428
  if (state.damage == 0) return;
1436
1429
 
1437
- state.damage *|= 2;
1430
+ // NOTE: llvm/llvm-project#58557
1431
+ state.damage = smul(state.damage, 2);
1438
1432
  state.miss = false;
1439
1433
  }
1440
1434
 
@@ -1529,15 +1523,18 @@ pub const Effects = struct {
1529
1523
 
1530
1524
  pub const double = struct {
1531
1525
  pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
1532
- if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
1526
+ if (!battle.foe(player).active.volatiles.Flying) return;
1527
+ state.damage = smul(state.damage, 2);
1533
1528
  }
1534
1529
 
1535
1530
  pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
1536
- if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
1531
+ if (!battle.foe(player).active.volatiles.Underground) return;
1532
+ state.damage = smul(state.damage, 2);
1537
1533
  }
1538
1534
 
1539
1535
  pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
1540
- if (battle.foe(player).active.volatiles.minimized) state.damage *|= 2;
1536
+ if (!battle.foe(player).active.volatiles.minimized) return;
1537
+ state.damage = smul(state.damage, 2);
1541
1538
  }
1542
1539
  };
1543
1540
 
@@ -1561,7 +1558,7 @@ pub const Effects = struct {
1561
1558
  const last = battle.foe(player).lastMove(true);
1562
1559
  const failed = volatiles.Encore or
1563
1560
  (last == .None or last == .Struggle or
1564
- last == .Encore or .last == .MirrorMove);
1561
+ last == .Encore or .last == .MirrorMove);
1565
1562
  if (failed) return options.log.fail(.{ foe_ident, .None });
1566
1563
 
1567
1564
  var slot: u3 = 0;
@@ -1706,7 +1703,7 @@ pub const Effects = struct {
1706
1703
  if (!state.proc) return;
1707
1704
  if (foe.conditions.Safeguard) return;
1708
1705
  // Freeze Clause Mod
1709
- if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1706
+ if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1710
1707
 
1711
1708
  foe_stored.status = Status.init(.FRZ);
1712
1709
  try options.log.status(.{ foe_ident, foe_stored.status, .None });
@@ -1735,7 +1732,7 @@ pub const Effects = struct {
1735
1732
  state.bp *= 2;
1736
1733
  assert(state.bp <= 160);
1737
1734
  } else {
1738
- state.damage *|= 2;
1735
+ state.damage = smul(state.damage, 2);
1739
1736
  }
1740
1737
  }
1741
1738
 
@@ -2206,7 +2203,7 @@ pub const Effects = struct {
2206
2203
 
2207
2204
  pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
2208
2205
  // TODO: remove switching variable and just look at Choice saved in Battle struct?
2209
- if (battle.foe(player).active.volatiles.switching) state.damage *|= 2;
2206
+ if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
2210
2207
  }
2211
2208
 
2212
2209
  pub const rage = struct {
@@ -2219,7 +2216,7 @@ pub const Effects = struct {
2219
2216
  const side = battle.side(player);
2220
2217
 
2221
2218
  assert(side.active.volatiles.Rage);
2222
- state.damage *|= (side.active.volatiles.rage +| 1);
2219
+ state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
2223
2220
  }
2224
2221
  };
2225
2222
 
@@ -2314,7 +2311,7 @@ pub const Effects = struct {
2314
2311
  side.active.volatiles.attacks += 1;
2315
2312
  side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
2316
2313
 
2317
- if (side.active.volatiles.DefenseCurl) state.damage *|= 2;
2314
+ if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
2318
2315
  }
2319
2316
  };
2320
2317
 
@@ -2384,7 +2381,7 @@ pub const Effects = struct {
2384
2381
  }
2385
2382
 
2386
2383
  // Sleep Clause Mod
2387
- if (showdown) {
2384
+ if (pkmn.options.mod) {
2388
2385
  for (foe.pokemon) |p| {
2389
2386
  if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
2390
2387
  }
@@ -2842,6 +2839,12 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2842
2839
  return t != .@"???" and !active.types.includes(t);
2843
2840
  }
2844
2841
 
2842
+ // NOTE: llvm/llvm-project#58557
2843
+ fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
2844
+ if (!comptime builtin.target.cpu.arch.isWasm()) return a *| b;
2845
+ return @max(a *% b, std.math.maxInt(@TypeOf(a)));
2846
+ }
2847
+
2845
2848
  pub const Rolls = struct {
2846
2849
  fn speedTie(battle: anytype, options: anytype) !bool {
2847
2850
  const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
@@ -3078,7 +3081,7 @@ pub const Rolls = struct {
3078
3081
  }
3079
3082
 
3080
3083
  // Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
3081
- var CONVERSION_2: [7]Type = [_]Type{.@"???"} ** 7;
3084
+ var CONVERSION_2: [7]Type = @splat(.@"???");
3082
3085
 
3083
3086
  fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
3084
3087
  var i: u8 = 0;
@@ -3503,11 +3506,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3503
3506
  },
3504
3507
  .Switch => {
3505
3508
  const side = battle.side(player);
3506
- var slot: u4 = 2;
3509
+ var slot: usize = 2;
3507
3510
  while (slot <= 6) : (slot += 1) {
3508
3511
  const id = side.order[slot - 1];
3509
3512
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3510
- out[n] = .{ .type = .Switch, .data = slot };
3513
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3511
3514
  n += 1;
3512
3515
  }
3513
3516
  if (n == 0) {
@@ -3537,11 +3540,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3537
3540
  return n;
3538
3541
  }
3539
3542
 
3540
- var slot: u4 = 2;
3543
+ var slot: usize = 2;
3541
3544
  while (!active.volatiles.Trapped and slot <= 6) : (slot += 1) {
3542
3545
  const id = side.order[slot - 1];
3543
3546
  if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
3544
- out[n] = .{ .type = .Switch, .data = slot };
3547
+ out[n] = .{ .type = .Switch, .data = @intCast(slot) };
3545
3548
  n += 1;
3546
3549
  }
3547
3550
 
@@ -3574,7 +3577,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3574
3577
  const struggle =
3575
3578
  m.id == .Bide and (m.pp == 0 or active.volatiles.disable.move == slot);
3576
3579
  const s = if (struggle) 0 else slot;
3577
- out[n] = .{ .type = .Move, .data = s };
3580
+ out[n] = .{ .type = .Move, .data = @intCast(s) };
3578
3581
  n += 1;
3579
3582
  return n;
3580
3583
  }
@@ -3588,7 +3591,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
3588
3591
  if (m.id == .None) break;
3589
3592
  if (m.pp == 0) continue;
3590
3593
  if (active.volatiles.disable.move == slot) continue;
3591
- out[n] = .{ .type = .Move, .data = slot };
3594
+ out[n] = .{ .type = .Move, .data = @intCast(slot) };
3592
3595
  n += 1;
3593
3596
  }
3594
3597
  // Struggle (Pokémon Showdown would use 'move 1' here)
package/src/lib/napi.h ADDED
@@ -0,0 +1,2 @@
1
+ #define NAPI_VERSION 8
2
+ #include <node_api.h>
package/src/lib/node.zig CHANGED
@@ -1,131 +1,14 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("pkmn.zig");
4
-
1
+ const c = @import("napi");
5
2
  const js = @import("common/js.zig");
3
+ const node = @import("bindings/node.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
7
 
9
- const c = @cImport({
10
- @cDefine("NAPI_VERSION", "8");
11
- @cInclude("node_api.h");
12
- });
13
-
14
8
  export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
15
9
  const properties = [_]c.napi_property_descriptor{
16
- js.Property.init("engine", .{ .value = register(env) }),
10
+ js.Property.init("engine", .{ .value = node.register(env) }),
17
11
  };
18
12
  assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
19
13
  return exports;
20
14
  }
21
-
22
- pub fn register(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("options", .{ .value = options(env) }),
26
- js.Property.init("bindings", .{ .value = bindings(env) }),
27
- };
28
- assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
29
- return object;
30
- }
31
-
32
- fn options(env: c.napi_env) c.napi_value {
33
- const object = js.Object.init(env);
34
- const properties = [_]c.napi_property_descriptor{
35
- js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
36
- js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
37
- js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
38
- js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
39
- };
40
- assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
41
- return object;
42
- }
43
-
44
- fn bindings(env: c.napi_env) c.napi_value {
45
- const array = js.Array.init(env, .{ .length = 1 });
46
- js.Array.set(env, array, 0, bind(env, pkmn.gen1));
47
- return array;
48
- }
49
-
50
- fn bind(env: c.napi_env, gen: anytype) c.napi_value {
51
- const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
52
- const logs_size: u32 = @intCast(gen.LOGS_SIZE);
53
- const object = js.Object.init(env);
54
- const properties = [_]c.napi_property_descriptor{
55
- js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
56
- js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
57
- js.Property.init("update", .{ .method = update(gen) }),
58
- js.Property.init("choices", .{ .method = choices(gen) }),
59
- };
60
- assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
61
- return object;
62
- }
63
-
64
- fn update(gen: anytype) c.napi_callback {
65
- return struct {
66
- fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
67
- var argc: usize = 4;
68
- var argv: [4]c.napi_value = undefined;
69
- assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
70
- assert(argc == 4);
71
-
72
- var data: ?*anyopaque = undefined;
73
- var len: usize = 0;
74
- assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
75
- assert(len == @sizeOf(gen.Battle(gen.PRNG)));
76
-
77
- var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
78
- const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
79
- const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
80
-
81
- var vtype: c.napi_valuetype = undefined;
82
- assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
83
- const result = switch (vtype) {
84
- c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
85
- else => result: {
86
- assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
87
- assert(len == gen.LOGS_SIZE);
88
-
89
- const buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
90
- var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
91
- // TODO: extract out
92
- var opts = pkmn.battle.options(
93
- pkmn.protocol.FixedLog{ .writer = stream.writer() },
94
- gen.chance.NULL,
95
- gen.calc.NULL,
96
- );
97
- break :result battle.update(c1, c2, &opts);
98
- },
99
- } catch unreachable;
100
-
101
- return js.Number.init(env, @as(u8, @bitCast(result)));
102
- }
103
- }.call;
104
- }
105
-
106
- fn choices(gen: anytype) c.napi_callback {
107
- return struct {
108
- fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
109
- var argc: usize = 4;
110
- var argv: [4]c.napi_value = undefined;
111
- assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
112
- assert(argc == 4);
113
-
114
- var data: ?*anyopaque = undefined;
115
- var len: usize = 0;
116
- assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
117
- assert(len == @sizeOf(gen.Battle(gen.PRNG)));
118
-
119
- var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
120
- const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
121
- const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
122
-
123
- assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
124
- assert(len == gen.CHOICES_SIZE);
125
-
126
- const out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
127
- const n = battle.choices(player, request, out);
128
- return js.Number.init(env, @as(u8, @bitCast(n)));
129
- }
130
- }.call;
131
- }
package/src/lib/pkmn.zig CHANGED
@@ -22,9 +22,9 @@ pub const Choice = @import("common/data.zig").Choice;
22
22
  /// considered terminal.
23
23
  pub const Result = @import("common/data.zig").Result;
24
24
 
25
- // TODO
25
+ /// Helpers for working with bit-packed arrays of non-powers-of-2 types.
26
26
  pub const Array = @import("common/array.zig").Array;
27
- // TODO
27
+ /// Optimized optional representation which stores the empty None value as a sentinel.
28
28
  pub const Optional = @import("common/optional.zig").Optional;
29
29
 
30
30
  /// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
@@ -32,31 +32,78 @@ pub const PSRNG = @import("common/rng.zig").PSRNG;
32
32
 
33
33
  /// Namespace for helpers related to protocol message logging.
34
34
  pub const protocol = if (options.internal) @import("common/protocol.zig") else struct {
35
- /// Logs protocol information to its `Writer` during a battle update when
35
+ /// Logs protocol information to its `WriterT` during a battle update when
36
36
  /// `options.log` is enabled.
37
37
  pub const Log = @import("common/protocol.zig").Log;
38
- /// Stripped down version of `std.io.FixedBufferStream` optimized for
39
- /// efficiently writing the individual protocol bytes. Note that the
40
- /// `ByteStream.Writer` is **not** a `std.io.Writer` and should not be
41
- /// used for general purpose writing.
42
- pub const ByteStream = @import("common/protocol.zig").ByteStream;
43
- /// `Log` type backed by the optimized `ByteStream.Writer`. Intended to be
44
- /// intialized with a `LOGS_SIZE`-sized buffer.
38
+ /// `Log` type backed by the optimized `Writer`. Intended to be intialized with
39
+ /// a `LOGS_SIZE`-sized buffer.
45
40
  pub const FixedLog = @import("common/protocol.zig").FixedLog;
46
- /// Null object pattern implementation of `Log` backed by a
47
- /// `std.io.null_writer`. Ignores anything sent to it, though protocol
48
- /// logging should additionally be turned off entirely with `options.log`.
41
+ /// Minimal logging helper optimized for efficiently writing the individual
42
+ /// protocol bytes into a fixed buffer. Note that this is **not** a
43
+ /// `std.Io.Writer` and should not be used for general purpose writing.
44
+ pub const Writer = @import("common/protocol.zig").Writer;
45
+ /// Null object pattern implementation of `Log` backed by a null writer.
46
+ /// Ignores anything sent to it, though protocol logging should additionally
47
+ /// be turned off entirely with `options.log`.
49
48
  pub const NULL = @import("common/protocol.zig").NULL;
50
49
  };
51
50
 
52
- pub usingnamespace @import("common/rational.zig");
51
+ /// Specialization of a rational number used by the engine to compute probabilties.
52
+ pub const Rational = @import("common/rational.zig").Rational;
53
53
 
54
54
  /// Namespace for cross-generation battle-related types.
55
55
  pub const battle = @import("common/battle.zig");
56
56
 
57
57
  /// Namespace for Generation I Pokémon.
58
58
  pub const gen1 = struct {
59
- pub usingnamespace @import("gen1/data.zig");
59
+ const data = @import("gen1/data.zig");
60
+ /// The minimum size in bytes required to hold all Generation I choice options.
61
+ pub const MAX_CHOICES = data.MAX_CHOICES;
62
+ /// The maximum number of bytes possibly logged by a single Generation I update.
63
+ pub const MAX_LOGS = data.MAX_LOGS;
64
+ /// The optimal size in bytes required to hold all Generation I choice options.
65
+ /// At least as large as MAX_CHOICES.
66
+ pub const CHOICES_SIZE = data.CHOICES_SIZE;
67
+ /// The optimal size in bytes required to hold the largest amount of log data possible from a
68
+ /// single Generation I update. At least as large as MAX_LOGS.
69
+ pub const LOGS_SIZE = data.LOGS_SIZE;
70
+ /// Null object pattern implementation of pkmn.battle.Options for Generation I.
71
+ pub const NULL = data.NULL;
72
+ /// The pseudo random number generator used by Generation I.
73
+ pub const PRNG = data.PRNG;
74
+ /// Representation of a Generation I battle.
75
+ pub const Battle = data.Battle;
76
+ /// Representation of one side of a Generation I Pokémon battle.
77
+ pub const Side = data.Side;
78
+ /// Representation of the state for single Generation I Pokémon while active in battle.
79
+ pub const ActivePokemon = data.ActivePokemon;
80
+ /// Representation of the state for single Generation I Pokémon while inactive in the party.
81
+ pub const Pokemon = data.Pokemon;
82
+ /// Representation of a Generation I Pokémon's move slot in a battle.
83
+ pub const MoveSlot = data.MoveSlot;
84
+ /// Details required to detect desyncs based on the move last selected/executed by players.
85
+ pub const MoveDetails = data.MoveDetails;
86
+ /// Bitfield representation of a Generation I & II Pokémon's major status condition.
87
+ pub const Status = data.Status;
88
+ /// Bitfield representation of volatile statuses and associated data in Generation I.
89
+ pub const Volatiles = data.Volatiles;
90
+ /// Representation of a Pokémon's stats in Generation I.
91
+ pub const Stats = data.Stats;
92
+ /// Representation of a Pokémon's boosts in Generation I.
93
+ pub const Boosts = data.Boosts;
94
+ /// Representation of a Generation I Pokémon move.
95
+ pub const Move = data.Move;
96
+ /// Representation of a Generation I Pokémon species.
97
+ pub const Species = data.Species;
98
+ /// Representation of a Generation I type in Pokémon.
99
+ pub const Type = data.Type;
100
+ /// Representation of a Generation I Pokémon's typing.
101
+ pub const Types = data.Types;
102
+ /// Modifiers for the effectiveness of a type vs. another type in Pokémon.
103
+ pub const Effectiveness = data.Effectiveness;
104
+ /// Representation of a Generation I Pokémon's determinant values.
105
+ pub const DVs = data.DVs;
106
+
60
107
  /// Tracks chance actions and their associated probability during a
61
108
  /// Generation I battle update when `options.chance` is enabled.
62
109
  pub const Chance = @import("gen1/chance.zig").Chance;
@@ -96,6 +143,10 @@ pub const gen1 = struct {
96
143
  /// though damage calculator support should additionally be turned off
97
144
  /// entirely via `options.calc`.
98
145
  pub const NULL = @import("gen1/calc.zig").NULL;
146
+ /// TODO
147
+ pub const MAX_FRONTIER = @import("gen1/calc.zig").MAX_FRONTIER;
148
+ /// TODO
149
+ pub const Rolls = @import("gen1/calc.zig").Rolls;
99
150
  };
100
151
  /// Provides helpers for initializing Generation I Pokémon battles.
101
152
  pub const helpers = @import("gen1/helpers.zig");
@@ -103,7 +154,34 @@ pub const gen1 = struct {
103
154
 
104
155
  /// TODO
105
156
  pub const gen2 = struct {
106
- pub usingnamespace @import("gen2/data.zig");
157
+ const data = @import("gen2/data.zig");
158
+ pub const MAX_CHOICES = data.MAX_CHOICES;
159
+ pub const MAX_LOGS = data.MAX_LOGS;
160
+ pub const CHOICES_SIZE = data.CHOICES_SIZE;
161
+ pub const LOGS_SIZE = data.LOGS_SIZE;
162
+ pub const NULL = data.NULL;
163
+ pub const PRNG = data.PRNG;
164
+ pub const Battle = data.Battle;
165
+ pub const Field = data.Field;
166
+ pub const Weather = data.Weather;
167
+ pub const Side = data.Side;
168
+ pub const ActivePokemon = data.ActivePokemon;
169
+ pub const Pokemon = data.Pokemon;
170
+ pub const Gender = data.Gender;
171
+ pub const DVs = data.DVs;
172
+ pub const MoveSlot = data.MoveSlot;
173
+ pub const Status = data.Status;
174
+ pub const TriAttack = data.TriAttack;
175
+ pub const Volatiles = data.Volatiles;
176
+ pub const Stats = data.Stats;
177
+ pub const Boosts = data.Boosts;
178
+ pub const Item = data.Item;
179
+ pub const Move = data.Move;
180
+ pub const Species = data.Species;
181
+ pub const Type = data.Type;
182
+ pub const Types = data.Types;
183
+ pub const Effectiveness = data.Effectiveness;
184
+
107
185
  pub const Chance = @import("gen2/chance.zig").Chance;
108
186
  pub const chance = struct {
109
187
  pub const Actions = @import("gen2/chance.zig").Actions;
@@ -122,9 +200,10 @@ pub const gen2 = struct {
122
200
  };
123
201
 
124
202
  // Internal APIs used by other pkmn libraries, not actually part of the public API.
125
- // NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
126
203
  pub const js = if (options.internal) @import("common/js.zig") else {};
204
+ pub const util = if (options.internal) @import("common/util.zig") else {};
127
205
  pub const bindings = if (options.internal) struct {
128
- pub const node = @import("node.zig");
129
- pub const wasm = @import("wasm.zig");
206
+ pub const c = @import("bindings/c.zig");
207
+ pub const node = @import("bindings/node.zig");
208
+ pub const wasm = @import("bindings/wasm.zig");
130
209
  } else {};
package/src/lib/wasm.zig CHANGED
@@ -1,13 +1,13 @@
1
1
  const std = @import("std");
2
+ const wasm = @import("bindings/wasm.zig");
2
3
 
3
- const pkmn = @import("pkmn.zig");
4
+ export const SHOWDOWN = wasm.options.showdown;
5
+ export const LOG = wasm.options.log;
6
+ export const CHANCE = wasm.options.chance;
7
+ export const CALC = wasm.options.calc;
4
8
 
5
- export const SHOWDOWN = pkmn.options.showdown;
6
- export const LOG = pkmn.options.log;
7
- export const CHANCE = pkmn.options.chance;
8
- export const CALC = pkmn.options.calc;
9
+ export const GEN1_CHOICES_SIZE = wasm.gen(1).CHOICES_SIZE;
10
+ export const GEN1_LOGS_SIZE = wasm.gen(1).LOGS_SIZE;
9
11
 
10
- export const GEN1_CHOICES_SIZE =
11
- std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(pkmn.gen1.CHOICES_SIZE))) catch unreachable;
12
- export const GEN1_LOGS_SIZE =
13
- std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(pkmn.gen1.LOGS_SIZE))) catch unreachable;
12
+ export const GEN1_update = wasm.gen(1).update;
13
+ export const GEN1_choices = wasm.gen(1).choices;