@pkmn/engine 0.1.0-dev.a324c50f → 0.1.0-dev.a77ab8e2

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 (57) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +87 -52
  3. package/build/pkg/addon.js +6 -7
  4. package/build/pkg/addon.js.map +1 -1
  5. package/build/pkg/data/ids.json +12 -12
  6. package/build/pkg/data/layout.json +2 -2
  7. package/build/pkg/data/protocol.json +69 -12
  8. package/build/pkg/data.d.ts +4 -4
  9. package/build/pkg/data.js.map +1 -1
  10. package/build/pkg/gen1.js +2 -2
  11. package/build/pkg/gen1.js.map +1 -1
  12. package/build/pkg/index.d.ts +3 -2
  13. package/build/pkg/index.js.map +1 -1
  14. package/build/pkg/protocol.js +61 -42
  15. package/build/pkg/protocol.js.map +1 -1
  16. package/build/test/display.js +8 -3
  17. package/build/test/display.js.map +1 -1
  18. package/build/tools/debug.js +5 -4
  19. package/build/tools/debug.js.map +1 -1
  20. package/build.zig +259 -117
  21. package/package.json +57 -25
  22. package/src/bin/install-pkmn-engine +31 -21
  23. package/src/lib/common/array.zig +77 -0
  24. package/src/lib/common/battle.zig +17 -0
  25. package/src/lib/common/data.zig +12 -24
  26. package/src/lib/common/debug.zig +2 -2
  27. package/src/lib/common/js.zig +120 -0
  28. package/src/lib/common/optional.zig +71 -0
  29. package/src/lib/common/options.zig +2 -0
  30. package/src/lib/common/protocol.zig +810 -327
  31. package/src/lib/common/rational.zig +447 -0
  32. package/src/lib/common/rng.zig +22 -13
  33. package/src/lib/common/util.zig +61 -0
  34. package/src/lib/gen1/calc.zig +806 -0
  35. package/src/lib/gen1/chance.zig +1167 -0
  36. package/src/lib/gen1/data/moves.zig +192 -180
  37. package/src/lib/gen1/data/species.zig +3 -3
  38. package/src/lib/gen1/data/types.zig +7 -7
  39. package/src/lib/gen1/data.zig +58 -64
  40. package/src/lib/gen1/helpers.zig +95 -397
  41. package/src/lib/gen1/mechanics.zig +587 -550
  42. package/src/lib/gen2/calc.zig +161 -0
  43. package/src/lib/gen2/chance.zig +905 -0
  44. package/src/lib/gen2/data/items.zig +12 -18
  45. package/src/lib/gen2/data/moves.zig +663 -569
  46. package/src/lib/gen2/data/species.zig +1 -1
  47. package/src/lib/gen2/data/types.zig +134 -31
  48. package/src/lib/gen2/data.zig +107 -79
  49. package/src/lib/gen2/generated.zig +806 -0
  50. package/src/lib/gen2/helpers.zig +49 -21
  51. package/src/lib/gen2/mechanics.zig +3563 -9
  52. package/src/lib/{binding/node.zig → node.zig} +10 -13
  53. package/src/lib/pkmn.zig +32 -15
  54. package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
  55. package/src/test/display/pkmn.html.tmpl +5 -5
  56. package/src/lib/gen1/README.md +0 -814
  57. package/src/lib/gen2/README.md +0 -309
@@ -1,4 +1,4 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
3
  const std = @import("std");
4
4
 
@@ -1091,8 +1091,8 @@ pub const Species = enum(u8) {
1091
1091
  /// The number of Pokémon species in this generation.
1092
1092
  pub const size = 151;
1093
1093
 
1094
- /// The Pokémon's critical hit rate=io out of 256.
1095
- pub inline fn chance(id: Species) u8 {
1094
+ /// The Pokémon's critical hit ratio out of 256.
1095
+ pub fn chance(id: Species) u8 {
1096
1096
  assert(id != .None);
1097
1097
  return CHANCES[@intFromEnum(id) - 1];
1098
1098
  }
@@ -1,4 +1,4 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
3
  const std = @import("std");
4
4
 
@@ -30,7 +30,7 @@ pub const Type = enum(u4) {
30
30
  Ice,
31
31
  Dragon,
32
32
 
33
- const CHART = [15][15]Effectiveness{
33
+ const CHART: [15][15]Effectiveness = .{
34
34
  [_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
35
35
  [_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
36
36
  [_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
@@ -90,16 +90,16 @@ pub const Type = enum(u4) {
90
90
  pub const size = 15;
91
91
 
92
92
  /// Whether or not this type is considered to be special as opposed to physical.
93
- pub inline fn special(self: Type) bool {
93
+ pub fn special(self: Type) bool {
94
94
  return @intFromEnum(self) >= @intFromEnum(Type.Fire);
95
95
  }
96
96
 
97
97
  /// The `Effectiveness` of type `t2` vs. type `t1`.
98
- pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
98
+ pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
99
99
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
100
100
  }
101
101
 
102
- /// The precedence order of type `t2` vs. type `t1`.
102
+ /// The precedence order of Type `t2` vs. Type `t1`.
103
103
  pub fn precedence(t1: Type, t2: Type) u8 {
104
104
  for (PRECEDENCE, 0..) |matchup, i| {
105
105
  if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
@@ -120,12 +120,12 @@ pub const Types = packed struct {
120
120
  }
121
121
 
122
122
  /// Whether this typing is immune to type `t`.
123
- pub inline fn immune(self: Types, t: Type) bool {
123
+ pub fn immune(self: Types, t: Type) bool {
124
124
  return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
125
125
  }
126
126
 
127
127
  /// Whether this typing includes type `t`.
128
- pub inline fn includes(self: Types, t: Type) bool {
128
+ pub fn includes(self: Types, t: Type) bool {
129
129
  return self.type1 == t or self.type2 == t;
130
130
  }
131
131
  };
@@ -22,11 +22,11 @@ const expect = std.testing.expect;
22
22
 
23
23
  /// The minimum size in bytes required to hold all Generation I choice options.
24
24
  pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
25
- /// The optimal size in bytes required to hold all Generation I choice options.
26
- /// At least as large as MAX_CHOICES.
25
+ /// The maximum number of bytes possibly logged by a single Generation I update.
27
26
  pub const MAX_LOGS: usize = 180;
28
27
 
29
- /// The maximum number of bytes possibly logged by a single Generation I update.
28
+ /// The optimal size in bytes required to hold all Generation I choice options.
29
+ /// At least as large as MAX_CHOICES.
30
30
  pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
31
31
  MAX_CHOICES
32
32
  else
@@ -47,6 +47,7 @@ const Player = data.Player;
47
47
  const Result = data.Result;
48
48
 
49
49
  const PointerType = util.PointerType;
50
+ const isPointerTo = util.isPointerTo;
50
51
 
51
52
  const showdown = pkmn.options.showdown;
52
53
 
@@ -65,27 +66,36 @@ pub fn Battle(comptime RNG: anytype) type {
65
66
  /// The last damage dealt by either side.
66
67
  last_damage: u16 = 0,
67
68
  /// Details about the last move selected/used by each player required to detect desyncs.
68
- last_moves: MoveDetails = .{},
69
+ last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
69
70
  /// The pseudo random number generator.
70
71
  rng: RNG,
71
72
 
72
73
  /// Returns the `Side` for the given `player`.
73
- pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
74
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
74
+ pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
75
+ assert(isPointerTo(self, Self));
75
76
  return &self.sides[@intFromEnum(player)];
76
77
  }
77
78
 
78
79
  /// Returns the `Side` of the opponent for the given `player`
79
- pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
80
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
80
+ pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
81
+ assert(isPointerTo(self, Self));
81
82
  return &self.sides[@intFromEnum(player.foe())];
82
83
  }
83
84
 
84
85
  /// Returns an identifier for the active Pokémon of `player`.
85
- pub inline fn active(self: *const Self, player: Player) ID {
86
+ pub fn active(self: *const Self, player: Player) ID {
86
87
  return player.ident(@intCast(self.side(player).order[0]));
87
88
  }
88
89
 
90
+ /// Returns the `MoveDetails` for the given `player`.
91
+ pub fn lastMove(
92
+ self: anytype,
93
+ player: Player,
94
+ ) PointerType(@TypeOf(self), MoveDetails) {
95
+ assert(isPointerTo(self, Self));
96
+ return &self.last_moves[@intFromEnum(player)];
97
+ }
98
+
89
99
  /// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
90
100
  /// battle, optionally writing protocol message logs to `options.log` if `pkmn.options.log`
91
101
  /// is enabled.
@@ -103,7 +113,7 @@ pub fn Battle(comptime RNG: anytype) type {
103
113
  /// make (i.e. on the cartridge a soft-lock occurs).
104
114
  ///
105
115
  /// This function will always return choices > 0 if `pkmn.options.showdown` is true.
106
- pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
116
+ pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
107
117
  return mechanics.choices(self, player, request, out);
108
118
  }
109
119
  };
@@ -132,8 +142,8 @@ pub const Side = extern struct {
132
142
  }
133
143
 
134
144
  /// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
135
- pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
136
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
145
+ pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
146
+ assert(isPointerTo(self, Side));
137
147
  assert(slot > 0 and slot <= 6);
138
148
  const id = self.order[slot - 1];
139
149
  assert(id > 0 and id <= 6);
@@ -141,8 +151,8 @@ pub const Side = extern struct {
141
151
  }
142
152
 
143
153
  /// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
144
- pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
145
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
154
+ pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
155
+ assert(isPointerTo(self, Side));
146
156
  return self.get(1);
147
157
  }
148
158
  };
@@ -167,8 +177,8 @@ pub const ActivePokemon = extern struct {
167
177
  }
168
178
 
169
179
  /// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
170
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
171
- assert(@typeInfo(@TypeOf(self)).Pointer.child == ActivePokemon);
180
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
181
+ assert(isPointerTo(self, ActivePokemon));
172
182
  assert(mslot > 0 and mslot <= 4);
173
183
  assert(self.moves[mslot - 1].id != .None);
174
184
  return &self.moves[mslot - 1];
@@ -197,15 +207,15 @@ pub const Pokemon = extern struct {
197
207
  }
198
208
 
199
209
  /// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
200
- pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
201
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
210
+ pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
211
+ assert(isPointerTo(self, Pokemon));
202
212
  assert(mslot > 0 and mslot <= 4);
203
213
  assert(self.moves[mslot - 1].id != .None);
204
214
  return &self.moves[mslot - 1];
205
215
  }
206
216
  };
207
217
 
208
- /// Representation of a Generation I & II Pokémon's move slot in a battle.
218
+ /// Representation of a Generation I Pokémon's move slot in a battle.
209
219
  pub const MoveSlot = extern struct {
210
220
  /// The identifier for the move.
211
221
  id: Move = .None,
@@ -213,24 +223,16 @@ pub const MoveSlot = extern struct {
213
223
  pp: u8 = 0,
214
224
 
215
225
  comptime {
216
- assert(@sizeOf(MoveSlot) == @sizeOf(u16));
226
+ assert(@sizeOf(MoveSlot) == 2);
217
227
  }
218
228
  };
219
229
 
220
230
  /// Details required to detect desyncs based on the move last selected/executed by players.
221
- pub const MoveDetails = packed struct {
222
- /// The move slot index of the last move selected in the battle menu by Player 1
223
- p1_index: if (showdown) u8 else u4 = 0,
224
- /// Whether the last move executed by Player 1 was counterable.
225
- p1_counterable: if (showdown) u8 else u4 = 0,
226
- /// The move slot index of the last move selected in the battle menu by Player 2
227
- p2_index: if (showdown) u8 else u4 = 0,
228
- /// Whether the last move executed by Player 2 was counterable.
229
- p2_counterable: if (showdown) u8 else u4 = 0,
230
-
231
- comptime {
232
- assert(@sizeOf(MoveDetails) == if (showdown) 4 else 2);
233
- }
231
+ pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
232
+ /// The move slot index of the last move selected in the battle menu.
233
+ index: if (showdown) u8 else u4 = 0,
234
+ /// Whether the last move executed was counterable.
235
+ counterable: if (showdown) u8 else u4 = 0,
234
236
  };
235
237
 
236
238
  /// Bitfield representation of a Generation I & II Pokémon's major status condition.
@@ -257,36 +259,36 @@ pub const Status = enum(u8) {
257
259
  const SLP = 0b111;
258
260
 
259
261
  /// Whether or not the status `num` is the same as `status`.
260
- pub inline fn is(num: u8, status: Status) bool {
262
+ pub fn is(num: u8, status: Status) bool {
261
263
  if (status == .SLP) return Status.duration(num) > 0;
262
264
  return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
263
265
  }
264
266
 
265
267
  /// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
266
- pub inline fn init(status: Status) u8 {
268
+ pub fn init(status: Status) u8 {
267
269
  assert(status != .SLP and status != .EXT);
268
270
  return @as(u8, 1) << @intCast(@intFromEnum(status));
269
271
  }
270
272
 
271
273
  /// Initializes a non self-inflicted sleep status with duration `dur`.
272
- pub inline fn slp(dur: u3) u8 {
274
+ pub fn slp(dur: u3) u8 {
273
275
  assert(dur > 0);
274
276
  return @as(u8, dur);
275
277
  }
276
278
 
277
279
  /// Initializes a self-inflicted sleep status with duration `dur`.
278
- pub inline fn slf(dur: u3) u8 {
280
+ pub fn slf(dur: u3) u8 {
279
281
  assert(dur > 0);
280
282
  return 0x80 | slp(dur);
281
283
  }
282
284
 
283
285
  /// Returns the duration of a sleep status.
284
- pub inline fn duration(num: u8) u3 {
286
+ pub fn duration(num: u8) u3 {
285
287
  return @intCast(num & SLP);
286
288
  }
287
289
 
288
290
  /// Returns whether `num` reflects any status.
289
- pub inline fn any(num: u8) bool {
291
+ pub fn any(num: u8) bool {
290
292
  return num > 0;
291
293
  }
292
294
 
@@ -324,7 +326,7 @@ test Status {
324
326
  }
325
327
 
326
328
  /// Bitfield representation of volatile statuses and associated data in Generation I.
327
- pub const Volatiles = packed struct {
329
+ pub const Volatiles = packed struct(u64) {
328
330
  /// Whether the "Bide" volatile status is present.
329
331
  Bide: bool = false,
330
332
  /// Whether the "Thrashing" volatile status is present.
@@ -377,19 +379,15 @@ pub const Volatiles = packed struct {
377
379
  /// The identity of whom the active Pokémon is transformed into.
378
380
  transform: u4 = 0,
379
381
  /// The remaining turns the move is disabled.
380
- disabled_duration: u4 = 0,
382
+ disable_duration: u4 = 0,
381
383
  /// The move slot (1-4) that is disabled.
382
- disabled_move: u3 = 0,
384
+ disable_move: u3 = 0,
383
385
  /// The number of turns toxic damage has been accumulating.
384
386
  toxic: u5 = 0,
385
-
386
- comptime {
387
- assert(@sizeOf(Volatiles) == 8);
388
- }
389
387
  };
390
388
 
391
389
  test Volatiles {
392
- var volatiles = Volatiles{};
390
+ var volatiles: Volatiles = .{};
393
391
  volatiles.Confusion = true;
394
392
  volatiles.confusion = 2;
395
393
  volatiles.Thrashing = true;
@@ -398,8 +396,8 @@ test Volatiles {
398
396
  volatiles.Substitute = true;
399
397
  volatiles.substitute = 42;
400
398
  volatiles.toxic = 4;
401
- volatiles.disabled_move = 2;
402
- volatiles.disabled_duration = 4;
399
+ volatiles.disable_move = 2;
400
+ volatiles.disable_duration = 4;
403
401
 
404
402
  try expect(volatiles.Confusion);
405
403
  try expect(volatiles.Thrashing);
@@ -410,14 +408,14 @@ test Volatiles {
410
408
 
411
409
  try expectEqual(@as(u16, 235), volatiles.state);
412
410
  try expectEqual(@as(u8, 42), volatiles.substitute);
413
- try expectEqual(@as(u4, 2), volatiles.disabled_move);
414
- try expectEqual(@as(u4, 4), volatiles.disabled_duration);
415
- try expectEqual(@as(u4, 2), volatiles.confusion);
411
+ try expectEqual(@as(u4, 2), volatiles.disable_move);
412
+ try expectEqual(@as(u4, 4), volatiles.disable_duration);
413
+ try expectEqual(@as(u3, 2), volatiles.confusion);
416
414
  try expectEqual(@as(u5, 4), volatiles.toxic);
417
- try expectEqual(@as(u4, 3), volatiles.attacks);
415
+ try expectEqual(@as(u3, 3), volatiles.attacks);
418
416
  }
419
417
 
420
- /// Representaiton of a Pokémon's stats in Generation I.
418
+ /// Representation of a Pokémon's stats in Generation I.
421
419
  pub fn Stats(comptime T: type) type {
422
420
  return extern struct {
423
421
  hp: T = 0,
@@ -451,7 +449,7 @@ test Stats {
451
449
  }
452
450
 
453
451
  /// Representation of a Pokémon's boosts in Generation I.
454
- pub const Boosts = packed struct {
452
+ pub const Boosts = packed struct(u32) {
455
453
  /// A Pokémon's Attack boosts.
456
454
  atk: i4 = 0,
457
455
  /// A Pokémon's Defense boosts.
@@ -466,14 +464,10 @@ pub const Boosts = packed struct {
466
464
  evasion: i4 = 0,
467
465
 
468
466
  _: u8 = 0,
469
-
470
- comptime {
471
- assert(@sizeOf(Boosts) == 4);
472
- }
473
467
  };
474
468
 
475
469
  test Boosts {
476
- const boosts = Boosts{ .spc = -6 };
470
+ const boosts: Boosts = .{ .spc = -6 };
477
471
  try expectEqual(0, boosts.atk);
478
472
  try expectEqual(-6, boosts.spc);
479
473
  }
@@ -485,7 +479,7 @@ test Move {
485
479
  try expectEqual(2, @intFromEnum(Move.KarateChop));
486
480
  const move = Move.get(.Fissure);
487
481
  try expectEqual(Move.Effect.OHKO, move.effect);
488
- try expectEqual(@as(u8, 30), move.accuracy);
482
+ try expectEqual(rng.Gen12.percent(30), move.accuracy);
489
483
  try expectEqual(Type.Ground, move.type);
490
484
 
491
485
  try expect(!Move.Effect.onBegin(.None));
@@ -623,16 +617,16 @@ pub const DVs = struct {
623
617
  };
624
618
 
625
619
  test DVs {
626
- var dvs = DVs{ .spc = 15, .spe = 15 };
620
+ var dvs: DVs = .{ .spc = 15, .spe = 15 };
627
621
  try expectEqual(@as(u4, 15), dvs.hp());
628
- dvs = DVs{
622
+ dvs = .{
629
623
  .atk = 5,
630
624
  .def = 15,
631
625
  .spe = 13,
632
626
  .spc = 13,
633
627
  };
634
628
  try expectEqual(@as(u4, 15), dvs.hp());
635
- dvs = DVs{
629
+ dvs = .{
636
630
  .def = 3,
637
631
  .spe = 10,
638
632
  .spc = 11,