@pkmn/engine 0.1.0-dev.a0447192 → 0.1.0-dev.a15b6c17

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 +22 -12
  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 +55 -12
  26. package/build/tools/debug.js.map +1 -1
  27. package/build/tools/display/debug.jsx +19 -94
  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 +20 -9
  32. package/build/tools/display/ui.jsx.map +1 -1
  33. package/build/tools/display/util.js +140 -1
  34. package/build/tools/display/util.js.map +1 -1
  35. package/build.zig +239 -217
  36. package/package.json +34 -130
  37. package/src/bin/install-pkmn-engine +25 -9
  38. package/src/lib/bindings/c.zig +200 -0
  39. package/src/lib/bindings/node.zig +117 -0
  40. package/src/lib/bindings/wasm.zig +50 -0
  41. package/src/lib/common/array.zig +5 -12
  42. package/src/lib/common/debug.zig +33 -59
  43. package/src/lib/common/js.zig +3 -9
  44. package/src/lib/common/optional.zig +11 -21
  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 +275 -284
  48. package/src/lib/common/rng.zig +1 -3
  49. package/src/lib/common/util.zig +14 -31
  50. package/src/lib/gen1/calc.zig +159 -163
  51. package/src/lib/gen1/chance.zig +146 -93
  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 +12 -20
  57. package/src/lib/gen1/mechanics.zig +103 -78
  58. package/src/lib/gen2/calc.zig +12 -23
  59. package/src/lib/gen2/chance.zig +12 -18
  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 +9 -18
  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 +0 -5
@@ -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,15 @@ 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| {
55
+ inline for (@typeInfo(Actions).@"struct".fields) |player| {
56
+ inline for (@typeInfo(Action).@"struct".fields) |field| {
64
57
  const a_val = @field(@field(a, player.name), field.name);
65
58
  const b_val = @field(@field(b, player.name), field.name);
66
59
 
67
60
  switch (@typeInfo(@TypeOf(a_val))) {
68
- Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
61
+ .@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
69
62
  return false,
70
- Int => if ((a_val > 0) != (b_val > 0)) return false,
63
+ .int => if ((a_val > 0) != (b_val > 0)) return false,
71
64
  else => unreachable,
72
65
  }
73
66
  }
@@ -75,7 +68,26 @@ pub const Actions = extern struct {
75
68
  return true;
76
69
  }
77
70
 
78
- pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
71
+ /// TODO
72
+ pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
73
+ for (bs, 0..) |b, j| {
74
+ // TODO: is skipping this redundant check worth it?
75
+ if (i == j) continue;
76
+ if (b.matches(a)) return true;
77
+ }
78
+ return false;
79
+ }
80
+
81
+ /// TODO
82
+ pub fn relax(actions: Actions) Actions {
83
+ if (!options.overwrite) return actions;
84
+ var a = actions;
85
+ if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
86
+ if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
87
+ return a;
88
+ }
89
+
90
+ pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
79
91
  try writer.writeAll("<P1 = ");
80
92
  try self.p1.fmt(writer, shape);
81
93
  try writer.writeAll(", P2 = ");
@@ -83,8 +95,7 @@ pub const Actions = extern struct {
83
95
  try writer.writeAll(">");
84
96
  }
85
97
 
86
- pub fn format(a: Actions, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
87
- _ = .{ f, o };
98
+ pub fn format(a: Actions, w: *std.Io.Writer) !void {
88
99
  try fmt(a, w, false);
89
100
  }
90
101
  };
@@ -108,18 +119,41 @@ test Actions {
108
119
  }
109
120
 
110
121
  /// 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
- };
122
+ pub fn Observation(comptime field: Duration.Field) type {
123
+ return switch (field) {
124
+ .attacking, .binding => enum(u2) {
125
+ started,
126
+ continuing,
127
+ ended,
128
+ },
129
+ .sleep, .disable => enum(u3) {
130
+ started,
131
+ continuing,
132
+ ended,
133
+ // TODO haze support
134
+ unused1,
135
+ unused2,
136
+ unused3,
137
+ unused4,
138
+ },
139
+ .confusion => enum(u4) {
140
+ started,
141
+ continuing,
142
+ ended,
143
+ /// An indicator that an existing confusion duration has been overwritten by a new
144
+ /// confusion duration due to a Thrashing move ending ("hidden" confusion). This is not
145
+ /// immediately observable to an opponent, to compute probabilities from an opponent's
146
+ /// point of view based solely on public information extra work must be done to correct
147
+ /// for the information leak
148
+ overwritten,
149
+ // TODO haze support
150
+ unused1,
151
+ unused2,
152
+ unused3,
153
+ unused4,
154
+ },
155
+ };
156
+ }
123
157
 
124
158
  /// Information about the RNG that was observed during a Generation I battle `update` for a
125
159
  /// single player.
@@ -146,26 +180,24 @@ pub const Action = packed struct(u64) {
146
180
  duration: u4 = 0,
147
181
 
148
182
  /// TODO
149
- sleep: Optional(Observation) = .None,
183
+ sleep: Optional(Observation(.sleep)) = .None,
150
184
  /// TODO
151
- confusion: Optional(Confusion) = .None,
185
+ confusion: Optional(Observation(.confusion)) = .None,
152
186
  /// TODO
153
- disable: Optional(Observation) = .None,
187
+ disable: Optional(Observation(.disable)) = .None,
154
188
  /// TODO
155
- attacking: Optional(Observation) = .None,
189
+ attacking: Optional(Observation(.attacking)) = .None,
156
190
  /// TODO
157
- binding: Optional(Observation) = .None,
158
-
159
- _: u1 = 0,
191
+ binding: Optional(Observation(.binding)) = .None,
160
192
 
161
193
  /// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
162
194
  /// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
163
- move_slot: u4 = 0,
195
+ move_slot: u3 = 0,
196
+ /// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
197
+ multi_hit: u3 = 0,
164
198
 
165
199
  /// TODO
166
200
  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
201
 
170
202
  /// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
171
203
  psywave: u8 = 0,
@@ -175,18 +207,19 @@ pub const Action = packed struct(u64) {
175
207
 
176
208
  pub const Field = std.meta.FieldEnum(Action);
177
209
 
178
- pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
179
- _ = .{ f, o };
210
+ pub fn format(a: Action, w: *std.Io.Writer) !void {
180
211
  try fmt(a, w, false);
181
212
  }
182
213
 
183
- pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
214
+ const SYMBOLS = [_][]const u8{ "+", "", "-", "#" };
215
+
216
+ pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
184
217
  try writer.writeByte('(');
185
218
  var printed = false;
186
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
219
+ inline for (@typeInfo(Action).@"struct".fields) |field| {
187
220
  const val = @field(self, field.name);
188
221
  switch (@typeInfo(@TypeOf(val))) {
189
- Enum => if (val != .None) {
222
+ .@"enum" => if (val != .None) {
190
223
  if (printed) try writer.writeAll(", ");
191
224
  if (shape) {
192
225
  try writer.print("{s}:?", .{field.name});
@@ -195,19 +228,14 @@ pub const Action = packed struct(u64) {
195
228
  if (val == .false) "!" else "",
196
229
  field.name,
197
230
  });
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,
204
- });
231
+ } else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
232
+ try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field.name });
205
233
  } else {
206
234
  try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
207
235
  }
208
236
  printed = true;
209
237
  },
210
- Int => if (val != 0) {
238
+ .int => if (val != 0) {
211
239
  if (printed) try writer.writeAll(", ");
212
240
  if (shape) {
213
241
  try writer.print("{s}:?", .{field.name});
@@ -240,14 +268,8 @@ pub const Durations = extern struct {
240
268
  return if (player == .P1) &self.p1 else &self.p2;
241
269
  }
242
270
 
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 });
271
+ pub fn format(self: Durations, writer: *std.Io.Writer) !void {
272
+ try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
251
273
  }
252
274
  };
253
275
 
@@ -263,19 +285,18 @@ pub const Duration = packed struct(u32) {
263
285
 
264
286
  _: u1 = 0,
265
287
 
288
+ pub const Field = enum { attacking, binding, sleep, disable, confusion };
289
+
266
290
  pub fn format(
267
291
  self: Duration,
268
- comptime fmt: []const u8,
269
- opts: std.fmt.FormatOptions,
270
- writer: anytype,
292
+ writer: *std.Io.Writer,
271
293
  ) !void {
272
- _ = .{ fmt, opts };
273
294
  try writer.writeByte('(');
274
295
  var printed = false;
275
- inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
296
+ inline for (@typeInfo(Duration).@"struct".fields) |field| {
276
297
  const val = @field(self, field.name);
277
298
  switch (@typeInfo(@TypeOf(val))) {
278
- Int => if (val != 0) {
299
+ .int => if (val != 0) {
279
300
  if (printed) try writer.writeAll(", ");
280
301
  if (comptime std.mem.eql(u8, field.name, "sleeps")) {
281
302
  try writer.print("{s}:[", .{field.name});
@@ -536,7 +557,7 @@ pub fn Chance(comptime Rational: type) type {
536
557
  self: *Self,
537
558
  comptime field: Action.Field,
538
559
  player: Player,
539
- obs: Optional(Confusion),
560
+ obs: Optional(Observation(.confusion)),
540
561
  ) void {
541
562
  if (!enabled) return;
542
563
 
@@ -578,7 +599,7 @@ pub fn Chance(comptime Rational: type) type {
578
599
  }
579
600
  }
580
601
 
581
- pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
602
+ pub fn sleep(self: *Self, player: Player, obs: Optional(Observation(.sleep))) Error!void {
582
603
  if (!enabled) return;
583
604
 
584
605
  var a = self.actions.get(player);
@@ -602,7 +623,11 @@ pub fn Chance(comptime Rational: type) type {
602
623
  }
603
624
  }
604
625
 
605
- pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
626
+ pub fn confusion(
627
+ self: *Self,
628
+ player: Player,
629
+ obs: Optional(Observation(.confusion)),
630
+ ) Error!void {
606
631
  if (!enabled) return;
607
632
 
608
633
  var a = self.actions.get(player);
@@ -624,7 +649,11 @@ pub fn Chance(comptime Rational: type) type {
624
649
  }
625
650
  }
626
651
 
627
- pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
652
+ pub fn disable(
653
+ self: *Self,
654
+ player: Player,
655
+ obs: Optional(Observation(.disable)),
656
+ ) Error!void {
628
657
  if (!enabled) return;
629
658
 
630
659
  var a = self.actions.get(player);
@@ -646,7 +675,11 @@ pub fn Chance(comptime Rational: type) type {
646
675
  }
647
676
  }
648
677
 
649
- pub fn attacking(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
678
+ pub fn attacking(
679
+ self: *Self,
680
+ player: Player,
681
+ obs: Optional(Observation(.attacking)),
682
+ ) Error!void {
650
683
  if (!enabled) return;
651
684
 
652
685
  var a = self.actions.get(player);
@@ -668,7 +701,11 @@ pub fn Chance(comptime Rational: type) type {
668
701
  }
669
702
  }
670
703
 
671
- pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
704
+ pub fn binding(
705
+ self: *Self,
706
+ player: Player,
707
+ obs: Optional(Observation(.binding)),
708
+ ) Error!void {
672
709
  if (!enabled) return;
673
710
 
674
711
  var a = self.actions.get(player);
@@ -878,13 +915,13 @@ test "Chance.moveSlot" {
878
915
 
879
916
  try chance.moveSlot(.P2, 2, &ms, 2);
880
917
  try expectProbability(&chance.probability, 1, 2);
881
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
918
+ try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
882
919
 
883
920
  chance.reset();
884
921
 
885
922
  try chance.moveSlot(.P1, 1, &ms, 0);
886
923
  try expectProbability(&chance.probability, 1, 3);
887
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
924
+ try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
888
925
  }
889
926
 
890
927
  test "Chance.multiHit" {
@@ -1051,7 +1088,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
1051
1088
 
1052
1089
  r.reduce();
1053
1090
  if (r.p != p or r.q != q) {
1054
- print("expected {d}/{d}, found {}\n", .{ p, q, r });
1091
+ print("expected {d}/{d}, found {f}\n", .{ p, q, r });
1055
1092
  return error.TestExpectedEqual;
1056
1093
  }
1057
1094
  }
@@ -1129,28 +1166,44 @@ const Null = struct {
1129
1166
  self: Null,
1130
1167
  comptime field: Action.Field,
1131
1168
  player: Player,
1132
- obs: Optional(Confusion),
1169
+ obs: Optional(Observation(.confusion)),
1133
1170
  ) void {
1134
1171
  _ = .{ self, field, player, obs };
1135
1172
  }
1136
1173
 
1137
- pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1174
+ pub fn sleep(self: Null, player: Player, obs: Optional(Observation(.sleep))) Error!void {
1138
1175
  _ = .{ self, player, obs };
1139
1176
  }
1140
1177
 
1141
- pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
1178
+ pub fn confusion(
1179
+ self: Null,
1180
+ player: Player,
1181
+ obs: Optional(Observation(.confusion)),
1182
+ ) Error!void {
1142
1183
  _ = .{ self, player, obs };
1143
1184
  }
1144
1185
 
1145
- pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1186
+ pub fn disable(
1187
+ self: Null,
1188
+ player: Player,
1189
+ obs: Optional(Observation(.disable)),
1190
+ ) Error!void {
1146
1191
  _ = .{ self, player, obs };
1147
1192
  }
1148
1193
 
1149
- pub fn attacking(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1194
+ pub fn attacking(
1195
+ self: Null,
1196
+ player: Player,
1197
+ obs: Optional(Observation(.attacking)),
1198
+ ) Error!void {
1150
1199
  _ = .{ self, player, obs };
1151
1200
  }
1152
1201
 
1153
- pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
1202
+ pub fn binding(
1203
+ self: Null,
1204
+ player: Player,
1205
+ obs: Optional(Observation(.binding)),
1206
+ ) Error!void {
1154
1207
  _ = .{ self, player, obs };
1155
1208
  }
1156
1209
 
@@ -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.
@@ -1,32 +1,24 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
3
-
4
2
  const common = @import("../common/data.zig");
3
+ const data = @import("data.zig");
5
4
  const options = @import("../common/options.zig");
6
5
  const protocol = @import("../common/protocol.zig");
7
6
  const rng = @import("../common/rng.zig");
7
+ const std = @import("std");
8
8
  const util = @import("../common/util.zig");
9
9
 
10
- const data = @import("data.zig");
11
-
12
10
  const assert = std.debug.assert;
13
-
14
11
  const Choice = common.Choice;
15
-
16
- const showdown = options.showdown;
17
-
18
- const PSRNG = rng.PSRNG;
19
-
20
12
  const DVs = data.DVs;
21
13
  const Move = data.Move;
22
14
  const MoveSlot = data.MoveSlot;
15
+ const PSRNG = rng.PSRNG;
16
+ const showdown = options.showdown;
23
17
  const Species = data.Species;
24
18
  const Stats = data.Stats;
25
19
 
26
20
  const endian = builtin.cpu.arch.endian();
27
21
 
28
- const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
29
-
30
22
  /// Configuration options controlling the domain of valid Pokémon that can be generated by the
31
23
  /// random generator helpers.
32
24
  pub const Options = struct {
@@ -169,7 +161,7 @@ pub const Pokemon = struct {
169
161
  var pokemon = data.Pokemon{};
170
162
  pokemon.species = p.species;
171
163
  const species = Species.get(p.species);
172
- inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
164
+ inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields) |field| {
173
165
  const hp = comptime std.mem.eql(u8, field.name, "hp");
174
166
  const spc =
175
167
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -205,7 +197,7 @@ pub const Pokemon = struct {
205
197
  const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
206
198
  var stats: Stats(u16) = .{};
207
199
  const dvs = DVs.random(rand);
208
- inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
200
+ inline for (@typeInfo(@TypeOf(stats)).@"struct".fields) |field| {
209
201
  @field(stats, field.name) = Stats(u16).calc(
210
202
  field.name,
211
203
  @field(species.stats, field.name),
@@ -218,7 +210,7 @@ pub const Pokemon = struct {
218
210
  );
219
211
  }
220
212
 
221
- var ms = [_]MoveSlot{.{}} ** 4;
213
+ var ms: [4]MoveSlot = @splat(.{});
222
214
  const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
223
215
  for (0..n) |i| {
224
216
  var m: Move = .None;
@@ -298,23 +290,23 @@ else
298
290
  pub fn diff(
299
291
  a: *const data.Battle(data.PRNG),
300
292
  b: *const data.Battle(data.PRNG),
301
- w: protocol.ByteStream.Writer,
293
+ w: *protocol.Writer,
302
294
  ) !usize {
303
- const pos = w.stream.pos;
295
+ const pos = w.pos;
304
296
  const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
305
297
  assert(size % 2 == 0);
306
298
  assert(size / 2 < 255);
307
299
 
308
300
  var i: usize = 0;
309
301
  while (i < size) : (i += 2) {
310
- const val = util.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
311
- if (val != util.bytesAsValue(u16, std.mem.asBytes(b)[i .. i + 2]).*) {
302
+ const val = std.mem.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
303
+ if (val != std.mem.bytesAsValue(u16, std.mem.asBytes(b)[i .. i + 2]).*) {
312
304
  try w.writeByte(@intCast(i / 2));
313
305
  try w.writeInt(u16, val, endian);
314
306
  }
315
307
  }
316
308
 
317
- return w.stream.pos - pos;
309
+ return w.pos - pos;
318
310
  }
319
311
 
320
312
  /// TODO