@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,34 +1,27 @@
1
- const std = @import("std");
2
-
3
- const assert = std.debug.assert;
4
- const print = std.debug.print;
5
-
6
- const expect = std.testing.expect;
7
- const expectEqual = std.testing.expectEqual;
8
-
9
1
  const DEBUG = @import("../common/debug.zig").print;
2
+
3
+ const array = @import("../common/array.zig");
4
+ const common = @import("../common/data.zig");
5
+ const data = @import("data.zig");
6
+ const optional = @import("../common/optional.zig");
10
7
  const options = @import("../common/options.zig");
11
8
  const rational = @import("../common/rational.zig");
9
+ const std = @import("std");
12
10
  const util = @import("../common/util.zig");
13
11
 
14
- const Array = @import("../common/array.zig").Array;
15
- const Player = @import("../common/data.zig").Player;
16
- const Optional = @import("../common/optional.zig").Optional;
17
-
18
- const data = @import("data.zig");
19
-
12
+ const Array = array.Array;
13
+ const assert = std.debug.assert;
20
14
  const enabled = options.chance;
21
- const showdown = options.showdown;
22
-
23
- const PointerType = util.PointerType;
15
+ const expect = std.testing.expect;
16
+ const expectEqual = std.testing.expectEqual;
24
17
  const isPointerTo = util.isPointerTo;
25
-
26
18
  const Move = data.Move;
27
19
  const MoveSlot = data.MoveSlot;
28
-
29
- const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
30
- const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
31
- const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
20
+ const Optional = optional.Optional;
21
+ const Player = common.Player;
22
+ const PointerType = util.PointerType;
23
+ const print = std.debug.print;
24
+ const showdown = options.showdown;
32
25
 
33
26
  /// Actions taken by a hypothetical "chance player" that convey information about which RNG events
34
27
  /// were observed during a Generation I battle `update`. This can additionally be provided as input
@@ -59,15 +52,28 @@ pub const Actions = extern struct {
59
52
  /// same shape if they have the same fields set (though those fields need not necessarily be
60
53
  /// set to the same value).
61
54
  pub fn matches(a: Actions, b: Actions) bool {
62
- inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
63
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
64
- const a_val = @field(@field(a, player.name), field.name);
65
- const b_val = @field(@field(b, player.name), field.name);
55
+ inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
56
+ @typeInfo(Actions).@"struct".fields
57
+ else
58
+ @typeInfo(Actions).@"struct".field_names) |player|
59
+ {
60
+ const player_name = if (@hasField(@TypeOf(player), "name"))
61
+ player.name
62
+ else
63
+ player;
64
+ inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
65
+ @typeInfo(Action).@"struct".fields
66
+ else
67
+ @typeInfo(Action).@"struct".field_names) |field|
68
+ {
69
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
70
+ const a_val = @field(@field(a, player_name), field_name);
71
+ const b_val = @field(@field(b, player_name), field_name);
66
72
 
67
73
  switch (@typeInfo(@TypeOf(a_val))) {
68
- Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
74
+ .@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
69
75
  return false,
70
- Int => if ((a_val > 0) != (b_val > 0)) return false,
76
+ .int => if ((a_val > 0) != (b_val > 0)) return false,
71
77
  else => unreachable,
72
78
  }
73
79
  }
@@ -75,7 +81,26 @@ pub const Actions = extern struct {
75
81
  return true;
76
82
  }
77
83
 
78
- pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
84
+ /// TODO
85
+ pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
86
+ for (bs, 0..) |b, j| {
87
+ // TODO: is skipping this redundant check worth it?
88
+ if (i == j) continue;
89
+ if (b.matches(a)) return true;
90
+ }
91
+ return false;
92
+ }
93
+
94
+ /// TODO
95
+ pub fn relax(actions: Actions) Actions {
96
+ if (!options.overwrite) return actions;
97
+ var a = actions;
98
+ if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
99
+ if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
100
+ return a;
101
+ }
102
+
103
+ pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
79
104
  try writer.writeAll("<P1 = ");
80
105
  try self.p1.fmt(writer, shape);
81
106
  try writer.writeAll(", P2 = ");
@@ -83,8 +108,7 @@ pub const Actions = extern struct {
83
108
  try writer.writeAll(">");
84
109
  }
85
110
 
86
- pub fn format(a: Actions, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
87
- _ = .{ f, o };
111
+ pub fn format(a: Actions, w: *std.Io.Writer) !void {
88
112
  try fmt(a, w, false);
89
113
  }
90
114
  };
@@ -108,18 +132,41 @@ test Actions {
108
132
  }
109
133
 
110
134
  /// Observation made about a duration - whether the duration has started, been continued, or ended.
111
- pub const Observation = enum { started, continuing, ended };
112
- /// An extension of Observation to account for "hidden" confusion.
113
- pub const Confusion = enum {
114
- started,
115
- continuing,
116
- ended,
117
- /// An indicator that an existing confusion duration has been overwritten by a new confusion
118
- /// duration due to a Thrashing move ending. This is not immediately observable to an opponent,
119
- /// to compute probabilities from an opponent's point of view based solely on public information
120
- /// extra work must be done to correct for the information leak
121
- overwritten,
122
- };
135
+ pub fn Observation(comptime field: Duration.Field) type {
136
+ return switch (field) {
137
+ .attacking, .binding => enum(u2) {
138
+ started,
139
+ continuing,
140
+ ended,
141
+ },
142
+ .sleep, .disable => enum(u3) {
143
+ started,
144
+ continuing,
145
+ ended,
146
+ // TODO haze support
147
+ unused1,
148
+ unused2,
149
+ unused3,
150
+ unused4,
151
+ },
152
+ .confusion => enum(u4) {
153
+ started,
154
+ continuing,
155
+ ended,
156
+ /// An indicator that an existing confusion duration has been overwritten by a new
157
+ /// confusion duration due to a Thrashing move ending ("hidden" confusion). This is not
158
+ /// immediately observable to an opponent, to compute probabilities from an opponent's
159
+ /// point of view based solely on public information extra work must be done to correct
160
+ /// for the information leak
161
+ overwritten,
162
+ // TODO haze support
163
+ unused1,
164
+ unused2,
165
+ unused3,
166
+ unused4,
167
+ },
168
+ };
169
+ }
123
170
 
124
171
  /// Information about the RNG that was observed during a Generation I battle `update` for a
125
172
  /// single player.
@@ -146,26 +193,24 @@ pub const Action = packed struct(u64) {
146
193
  duration: u4 = 0,
147
194
 
148
195
  /// TODO
149
- sleep: Optional(Observation) = .None,
196
+ sleep: Optional(Observation(.sleep)) = .None,
150
197
  /// TODO
151
- confusion: Optional(Confusion) = .None,
198
+ confusion: Optional(Observation(.confusion)) = .None,
152
199
  /// TODO
153
- disable: Optional(Observation) = .None,
200
+ disable: Optional(Observation(.disable)) = .None,
154
201
  /// TODO
155
- attacking: Optional(Observation) = .None,
202
+ attacking: Optional(Observation(.attacking)) = .None,
156
203
  /// TODO
157
- binding: Optional(Observation) = .None,
158
-
159
- _: u1 = 0,
204
+ binding: Optional(Observation(.binding)) = .None,
160
205
 
161
206
  /// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
162
207
  /// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
163
- move_slot: u4 = 0,
208
+ move_slot: u3 = 0,
209
+ /// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
210
+ multi_hit: u3 = 0,
164
211
 
165
212
  /// TODO
166
213
  pp: u4 = 0,
167
- /// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
168
- multi_hit: u4 = 0,
169
214
 
170
215
  /// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
171
216
  psywave: u8 = 0,
@@ -175,44 +220,45 @@ pub const Action = packed struct(u64) {
175
220
 
176
221
  pub const Field = std.meta.FieldEnum(Action);
177
222
 
178
- pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
179
- _ = .{ f, o };
223
+ pub fn format(a: Action, w: *std.Io.Writer) !void {
180
224
  try fmt(a, w, false);
181
225
  }
182
226
 
183
- pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
227
+ const SYMBOLS = [_][]const u8{ "+", "", "-", "#" };
228
+
229
+ pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
184
230
  try writer.writeByte('(');
185
231
  var printed = false;
186
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
187
- const val = @field(self, field.name);
232
+ inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
233
+ @typeInfo(Action).@"struct".fields
234
+ else
235
+ @typeInfo(Action).@"struct".field_names) |field|
236
+ {
237
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
238
+ const val = @field(self, field_name);
188
239
  switch (@typeInfo(@TypeOf(val))) {
189
- Enum => if (val != .None) {
240
+ .@"enum" => if (val != .None) {
190
241
  if (printed) try writer.writeAll(", ");
191
242
  if (shape) {
192
- try writer.print("{s}:?", .{field.name});
243
+ try writer.print("{s}:?", .{field_name});
193
244
  } else if (@TypeOf(val) == Optional(bool)) {
194
245
  try writer.print("{s}{s}", .{
195
246
  if (val == .false) "!" else "",
196
- field.name,
197
- });
198
- } else if (@TypeOf(val) == Optional(Observation) or
199
- @TypeOf(val) == Optional(Confusion))
200
- {
201
- try writer.print("{s}{s}", .{
202
- ([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
203
- field.name,
247
+ field_name,
204
248
  });
249
+ } else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
250
+ try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field_name });
205
251
  } else {
206
- try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
252
+ try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
207
253
  }
208
254
  printed = true;
209
255
  },
210
- Int => if (val != 0) {
256
+ .int => if (val != 0) {
211
257
  if (printed) try writer.writeAll(", ");
212
258
  if (shape) {
213
- try writer.print("{s}:?", .{field.name});
259
+ try writer.print("{s}:?", .{field_name});
214
260
  } else {
215
- try writer.print("{s}:{d}", .{ field.name, val });
261
+ try writer.print("{s}:{d}", .{ field_name, val });
216
262
  }
217
263
  printed = true;
218
264
  },
@@ -224,7 +270,7 @@ pub const Action = packed struct(u64) {
224
270
  };
225
271
 
226
272
  /// TODO
227
- pub const Durations = struct {
273
+ pub const Durations = extern struct {
228
274
  /// TODO
229
275
  p1: Duration = .{},
230
276
  /// TODO
@@ -240,14 +286,8 @@ pub const Durations = struct {
240
286
  return if (player == .P1) &self.p1 else &self.p2;
241
287
  }
242
288
 
243
- pub fn format(
244
- self: Durations,
245
- comptime fmt: []const u8,
246
- opts: std.fmt.FormatOptions,
247
- writer: anytype,
248
- ) !void {
249
- _ = .{ fmt, opts };
250
- try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
289
+ pub fn format(self: Durations, writer: *std.Io.Writer) !void {
290
+ try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
251
291
  }
252
292
  };
253
293
 
@@ -263,29 +303,33 @@ pub const Duration = packed struct(u32) {
263
303
 
264
304
  _: u1 = 0,
265
305
 
306
+ pub const Field = enum { attacking, binding, sleep, disable, confusion };
307
+
266
308
  pub fn format(
267
309
  self: Duration,
268
- comptime fmt: []const u8,
269
- opts: std.fmt.FormatOptions,
270
- writer: anytype,
310
+ writer: *std.Io.Writer,
271
311
  ) !void {
272
- _ = .{ fmt, opts };
273
312
  try writer.writeByte('(');
274
313
  var printed = false;
275
- inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
276
- const val = @field(self, field.name);
314
+ inline for (if (@hasField(@TypeOf(@typeInfo(Duration).@"struct"), "fields"))
315
+ @typeInfo(Duration).@"struct".fields
316
+ else
317
+ @typeInfo(Duration).@"struct".field_names) |field|
318
+ {
319
+ const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
320
+ const val = @field(self, field_name);
277
321
  switch (@typeInfo(@TypeOf(val))) {
278
- Int => if (val != 0) {
322
+ .int => if (val != 0) {
279
323
  if (printed) try writer.writeAll(", ");
280
- if (comptime std.mem.eql(u8, field.name, "sleeps")) {
281
- try writer.print("{s}:[", .{field.name});
324
+ if (comptime std.mem.eql(u8, field_name, "sleeps")) {
325
+ try writer.print("{s}:[", .{field_name});
282
326
  for (0..6) |i| {
283
327
  if (i != 0) try writer.writeByte(',');
284
328
  try writer.print("{d}", .{Sleeps.get(val, i)});
285
329
  }
286
330
  try writer.writeAll("]");
287
331
  } else {
288
- try writer.print("{s}:{d}", .{ field.name, val });
332
+ try writer.print("{s}:{d}", .{ field_name, val });
289
333
  }
290
334
  printed = true;
291
335
  },
@@ -375,7 +419,7 @@ pub fn Chance(comptime Rational: type) type {
375
419
 
376
420
  // If the move actually lands we can commit any past critical hit / damage rolls. Note
377
421
  // we can't rely on the presence or absence of a damage roll to determine whether a
378
- // "critical hit" was spurious or not as moves that do 1 or less damage don't rol for
422
+ // "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
379
423
  // damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
380
424
  // passed to the critical hit helper).
381
425
  //
@@ -391,7 +435,7 @@ pub fn Chance(comptime Rational: type) type {
391
435
  action.critical_hit = if (self.pending.crit) .true else .false;
392
436
  } else assert(!self.pending.glitch);
393
437
 
394
- if (self.pending.damage_roll > 0) {
438
+ if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
395
439
  try self.probability.update(1, 39);
396
440
  action.damage = self.pending.damage_roll;
397
441
  }
@@ -536,7 +580,7 @@ pub fn Chance(comptime Rational: type) type {
536
580
  self: *Self,
537
581
  comptime field: Action.Field,
538
582
  player: Player,
539
- obs: Optional(Confusion),
583
+ obs: Optional(Observation(.confusion)),
540
584
  ) void {
541
585
  if (!enabled) return;
542
586
 
@@ -578,7 +622,7 @@ pub fn Chance(comptime Rational: type) type {
578
622
  }
579
623
  }
580
624
 
581
- pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
625
+ pub fn sleep(self: *Self, player: Player, obs: Optional(Observation(.sleep))) Error!void {
582
626
  if (!enabled) return;
583
627
 
584
628
  var a = self.actions.get(player);
@@ -602,7 +646,11 @@ pub fn Chance(comptime Rational: type) type {
602
646
  }
603
647
  }
604
648
 
605
- pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
649
+ pub fn confusion(
650
+ self: *Self,
651
+ player: Player,
652
+ obs: Optional(Observation(.confusion)),
653
+ ) Error!void {
606
654
  if (!enabled) return;
607
655
 
608
656
  var a = self.actions.get(player);
@@ -624,7 +672,11 @@ pub fn Chance(comptime Rational: type) type {
624
672
  }
625
673
  }
626
674
 
627
- pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
675
+ pub fn disable(
676
+ self: *Self,
677
+ player: Player,
678
+ obs: Optional(Observation(.disable)),
679
+ ) Error!void {
628
680
  if (!enabled) return;
629
681
 
630
682
  var a = self.actions.get(player);
@@ -646,7 +698,11 @@ pub fn Chance(comptime Rational: type) type {
646
698
  }
647
699
  }
648
700
 
649
- pub fn attacking(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
701
+ pub fn attacking(
702
+ self: *Self,
703
+ player: Player,
704
+ obs: Optional(Observation(.attacking)),
705
+ ) Error!void {
650
706
  if (!enabled) return;
651
707
 
652
708
  var a = self.actions.get(player);
@@ -668,7 +724,11 @@ pub fn Chance(comptime Rational: type) type {
668
724
  }
669
725
  }
670
726
 
671
- pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
727
+ pub fn binding(
728
+ self: *Self,
729
+ player: Player,
730
+ obs: Optional(Observation(.binding)),
731
+ ) Error!void {
672
732
  if (!enabled) return;
673
733
 
674
734
  var a = self.actions.get(player);
@@ -878,13 +938,13 @@ test "Chance.moveSlot" {
878
938
 
879
939
  try chance.moveSlot(.P2, 2, &ms, 2);
880
940
  try expectProbability(&chance.probability, 1, 2);
881
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
941
+ try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
882
942
 
883
943
  chance.reset();
884
944
 
885
945
  try chance.moveSlot(.P1, 1, &ms, 0);
886
946
  try expectProbability(&chance.probability, 1, 3);
887
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
947
+ try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
888
948
  }
889
949
 
890
950
  test "Chance.multiHit" {
@@ -1051,7 +1111,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
1051
1111
 
1052
1112
  r.reduce();
1053
1113
  if (r.p != p or r.q != q) {
1054
- print("expected {d}/{d}, found {}\n", .{ p, q, r });
1114
+ print("expected {d}/{d}, found {f}\n", .{ p, q, r });
1055
1115
  return error.TestExpectedEqual;
1056
1116
  }
1057
1117
  }
@@ -1129,28 +1189,44 @@ const Null = struct {
1129
1189
  self: Null,
1130
1190
  comptime field: Action.Field,
1131
1191
  player: Player,
1132
- obs: Optional(Confusion),
1192
+ obs: Optional(Observation(.confusion)),
1133
1193
  ) void {
1134
1194
  _ = .{ self, field, player, obs };
1135
1195
  }
1136
1196
 
1137
- pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1197
+ pub fn sleep(self: Null, player: Player, obs: Optional(Observation(.sleep))) Error!void {
1138
1198
  _ = .{ self, player, obs };
1139
1199
  }
1140
1200
 
1141
- pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
1201
+ pub fn confusion(
1202
+ self: Null,
1203
+ player: Player,
1204
+ obs: Optional(Observation(.confusion)),
1205
+ ) Error!void {
1142
1206
  _ = .{ self, player, obs };
1143
1207
  }
1144
1208
 
1145
- pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1209
+ pub fn disable(
1210
+ self: Null,
1211
+ player: Player,
1212
+ obs: Optional(Observation(.disable)),
1213
+ ) Error!void {
1146
1214
  _ = .{ self, player, obs };
1147
1215
  }
1148
1216
 
1149
- pub fn attacking(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1217
+ pub fn attacking(
1218
+ self: Null,
1219
+ player: Player,
1220
+ obs: Optional(Observation(.attacking)),
1221
+ ) Error!void {
1150
1222
  _ = .{ self, player, obs };
1151
1223
  }
1152
1224
 
1153
- pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1225
+ pub fn binding(
1226
+ self: Null,
1227
+ player: Player,
1228
+ obs: Optional(Observation(.binding)),
1229
+ ) Error!void {
1154
1230
  _ = .{ self, player, obs };
1155
1231
  }
1156
1232
 
@@ -1,12 +1,10 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
- const rng = @import("../../common/rng.zig");
6
3
  const gen1 = @import("../../gen1/data.zig");
4
+ const rng = @import("../../common/rng.zig");
5
+ const std = @import("std");
7
6
 
8
7
  const assert = std.debug.assert;
9
-
10
8
  const percent = rng.Gen12.percent;
11
9
  const Type = gen1.Type;
12
10
 
@@ -1,11 +1,9 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
3
  const gen1 = @import("../../gen1/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
-
9
7
  const Stats = gen1.Stats;
10
8
  const Types = gen1.Types;
11
9
 
@@ -1,8 +1,7 @@
1
1
  //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
- const std = @import("std");
4
-
5
3
  const gen1 = @import("../../gen1/data.zig");
4
+ const std = @import("std");
6
5
 
7
6
  const assert = std.debug.assert;
8
7
  const Effectiveness = gen1.Effectiveness;
@@ -99,7 +98,7 @@ pub const Type = enum(u4) {
99
98
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
100
99
  }
101
100
 
102
- /// The precedence order of Type `t2` vs. Type `t1`.
101
+ /// The precedence order of Type `t1` vs. Type `t2`.
103
102
  pub fn precedence(t1: Type, t2: Type) u8 {
104
103
  for (PRECEDENCE, 0..) |matchup, i| {
105
104
  if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
@@ -109,7 +108,7 @@ pub const Type = enum(u4) {
109
108
  };
110
109
 
111
110
  /// Representation of a Pokémon's typing.
112
- pub const Types = packed struct {
111
+ pub const Types = packed struct(u8) {
113
112
  /// A Pokémon's primary type.
114
113
  type1: Type = .Normal,
115
114
  /// A Pokémon's secondary type (may be identical to its primary type).
@@ -1,20 +1,16 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
3
-
4
- const pkmn = @import("../pkmn.zig");
5
-
2
+ const calc = @import("calc.zig");
3
+ const chance = @import("chance.zig");
6
4
  const data = @import("../common/data.zig");
5
+ const mechanics = @import("mechanics.zig");
6
+ const moves = @import("data/moves.zig");
7
+ const pkmn = @import("../pkmn.zig");
7
8
  const protocol = @import("../common/protocol.zig");
8
9
  const rng = @import("../common/rng.zig");
9
- const util = @import("../common/util.zig");
10
-
11
- const moves = @import("data/moves.zig");
12
10
  const species = @import("data/species.zig");
11
+ const std = @import("std");
13
12
  const types = @import("data/types.zig");
14
-
15
- const calc = @import("calc.zig");
16
- const chance = @import("chance.zig");
17
- const mechanics = @import("mechanics.zig");
13
+ const util = @import("../common/util.zig");
18
14
 
19
15
  const assert = std.debug.assert;
20
16
  const expectEqual = std.testing.expectEqual;
@@ -66,7 +62,7 @@ pub fn Battle(comptime RNG: anytype) type {
66
62
  /// The last damage dealt by either side.
67
63
  last_damage: u16 = 0,
68
64
  /// Details about the last move selected/used by each player required to detect desyncs.
69
- last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
65
+ last_moves: [2]MoveDetails = @splat(.{}),
70
66
  /// The pseudo random number generator.
71
67
  rng: RNG,
72
68
 
@@ -126,12 +122,12 @@ test Battle {
126
122
  /// Representation of one side of a Generation I Pokémon battle.
127
123
  pub const Side = extern struct {
128
124
  /// The player's party in its original order
129
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
125
+ pokemon: [6]Pokemon = @splat(.{}),
130
126
  /// The active Pokémon for the side, zero initialized if the battle has yet to start.
131
127
  /// Note that fainted Pokémon are still consider "active" until their replacement switches in.
132
128
  active: ActivePokemon = .{},
133
129
  /// One-based slot indexes reflecting the current order of the player's party.
134
- order: [6]u8 = [_]u8{0} ** 6,
130
+ order: [6]u8 = @splat(0),
135
131
  /// The last move the player selected.
136
132
  last_selected_move: Move = .None,
137
133
  /// The last move the player used.
@@ -170,7 +166,7 @@ pub const ActivePokemon = extern struct {
170
166
  /// The active Pokémon's volatile statuses and associated data.
171
167
  volatiles: Volatiles = .{},
172
168
  /// The active Pokémon's current move slots.
173
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
169
+ moves: [4]MoveSlot = @splat(.{}),
174
170
 
175
171
  comptime {
176
172
  assert(@sizeOf(ActivePokemon) == 32);
@@ -190,7 +186,7 @@ pub const Pokemon = extern struct {
190
186
  /// The Pokémon's unmodified stats.
191
187
  stats: Stats(u16) = .{},
192
188
  /// The Pokémon's original stored move slots.
193
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
189
+ moves: [4]MoveSlot = @splat(.{}),
194
190
  /// The Pokémon's current HP.
195
191
  hp: u16 = 0,
196
192
  /// The Pokémon's current status.