@pkmn/engine 0.1.0-dev.b3197aa3 → 0.1.0-dev.b396399c

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 (47) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +17 -53
  3. package/build/pkg/addon/wasm.js.map +1 -1
  4. package/build/pkg/addon.d.ts +4 -4
  5. package/build/pkg/addon.js.map +1 -1
  6. package/build/pkg/data.js.map +1 -1
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/protocol.d.ts +11 -2
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build/tools/debug.js +37 -3
  11. package/build/tools/debug.js.map +1 -1
  12. package/build/tools/display/debug.jsx +1 -87
  13. package/build/tools/display/debug.jsx.map +1 -1
  14. package/build/tools/display/ui.jsx +3 -2
  15. package/build/tools/display/ui.jsx.map +1 -1
  16. package/build/tools/display/util.js +125 -1
  17. package/build/tools/display/util.js.map +1 -1
  18. package/build.zig +141 -232
  19. package/package.json +22 -21
  20. package/src/bin/install-pkmn-engine +22 -7
  21. package/src/lib/bindings/c.zig +27 -82
  22. package/src/lib/bindings/node.zig +7 -7
  23. package/src/lib/bindings/wasm.zig +6 -15
  24. package/src/lib/common/array.zig +5 -12
  25. package/src/lib/common/debug.zig +33 -59
  26. package/src/lib/common/js.zig +3 -5
  27. package/src/lib/common/optional.zig +11 -21
  28. package/src/lib/common/protocol.zig +224 -189
  29. package/src/lib/common/rational.zig +18 -38
  30. package/src/lib/common/util.zig +14 -46
  31. package/src/lib/gen1/calc.zig +121 -108
  32. package/src/lib/gen1/chance.zig +112 -75
  33. package/src/lib/gen1/data/types.zig +1 -1
  34. package/src/lib/gen1/data.zig +5 -5
  35. package/src/lib/gen1/helpers.zig +8 -10
  36. package/src/lib/gen1/mechanics.zig +31 -31
  37. package/src/lib/gen2/calc.zig +3 -6
  38. package/src/lib/gen2/chance.zig +12 -18
  39. package/src/lib/gen2/data.zig +5 -5
  40. package/src/lib/gen2/helpers.zig +4 -6
  41. package/src/lib/gen2/mechanics.zig +16 -16
  42. package/src/lib/napi.h +2 -0
  43. package/src/lib/node.zig +1 -1
  44. package/src/lib/pkmn.zig +91 -17
  45. package/src/lib/wasm.zig +2 -6
  46. package/src/tools/display/dom.ts +40 -25
  47. package/src/lib/common/napi.zig +0 -4
@@ -23,10 +23,6 @@ const PointerType = util.PointerType;
23
23
  const print = std.debug.print;
24
24
  const showdown = options.showdown;
25
25
 
26
- const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
27
- const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
28
- const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
29
-
30
26
  /// Actions taken by a hypothetical "chance player" that convey information about which RNG events
31
27
  /// were observed during a Generation I battle `update`. This can additionally be provided as input
32
28
  /// to the `update` call to override the normal behavior of the RNG in order to force specific
@@ -56,15 +52,15 @@ pub const Actions = extern struct {
56
52
  /// same shape if they have the same fields set (though those fields need not necessarily be
57
53
  /// set to the same value).
58
54
  pub fn matches(a: Actions, b: Actions) bool {
59
- inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
60
- 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| {
61
57
  const a_val = @field(@field(a, player.name), field.name);
62
58
  const b_val = @field(@field(b, player.name), field.name);
63
59
 
64
60
  switch (@typeInfo(@TypeOf(a_val))) {
65
- Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
61
+ .@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
66
62
  return false,
67
- Int => if ((a_val > 0) != (b_val > 0)) return false,
63
+ .int => if ((a_val > 0) != (b_val > 0)) return false,
68
64
  else => unreachable,
69
65
  }
70
66
  }
@@ -91,7 +87,7 @@ pub const Actions = extern struct {
91
87
  return a;
92
88
  }
93
89
 
94
- pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
90
+ pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
95
91
  try writer.writeAll("<P1 = ");
96
92
  try self.p1.fmt(writer, shape);
97
93
  try writer.writeAll(", P2 = ");
@@ -99,8 +95,7 @@ pub const Actions = extern struct {
99
95
  try writer.writeAll(">");
100
96
  }
101
97
 
102
- pub fn format(a: Actions, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
103
- _ = .{ f, o };
98
+ pub fn format(a: Actions, w: *std.Io.Writer) !void {
104
99
  try fmt(a, w, false);
105
100
  }
106
101
  };
@@ -124,18 +119,41 @@ test Actions {
124
119
  }
125
120
 
126
121
  /// Observation made about a duration - whether the duration has started, been continued, or ended.
127
- pub const Observation = enum { started, continuing, ended };
128
- /// An extension of Observation to account for "hidden" confusion.
129
- pub const Confusion = enum {
130
- started,
131
- continuing,
132
- ended,
133
- /// An indicator that an existing confusion duration has been overwritten by a new confusion
134
- /// duration due to a Thrashing move ending. This is not immediately observable to an opponent,
135
- /// to compute probabilities from an opponent's point of view based solely on public information
136
- /// extra work must be done to correct for the information leak
137
- overwritten,
138
- };
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
+ }
139
157
 
140
158
  /// Information about the RNG that was observed during a Generation I battle `update` for a
141
159
  /// single player.
@@ -162,26 +180,24 @@ pub const Action = packed struct(u64) {
162
180
  duration: u4 = 0,
163
181
 
164
182
  /// TODO
165
- sleep: Optional(Observation) = .None,
183
+ sleep: Optional(Observation(.sleep)) = .None,
166
184
  /// TODO
167
- confusion: Optional(Confusion) = .None,
185
+ confusion: Optional(Observation(.confusion)) = .None,
168
186
  /// TODO
169
- disable: Optional(Observation) = .None,
187
+ disable: Optional(Observation(.disable)) = .None,
170
188
  /// TODO
171
- attacking: Optional(Observation) = .None,
189
+ attacking: Optional(Observation(.attacking)) = .None,
172
190
  /// TODO
173
- binding: Optional(Observation) = .None,
174
-
175
- _: u1 = 0,
191
+ binding: Optional(Observation(.binding)) = .None,
176
192
 
177
193
  /// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
178
194
  /// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
179
- 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,
180
198
 
181
199
  /// TODO
182
200
  pp: u4 = 0,
183
- /// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
184
- multi_hit: u4 = 0,
185
201
 
186
202
  /// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
187
203
  psywave: u8 = 0,
@@ -191,18 +207,19 @@ pub const Action = packed struct(u64) {
191
207
 
192
208
  pub const Field = std.meta.FieldEnum(Action);
193
209
 
194
- pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
195
- _ = .{ f, o };
210
+ pub fn format(a: Action, w: *std.Io.Writer) !void {
196
211
  try fmt(a, w, false);
197
212
  }
198
213
 
199
- 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 {
200
217
  try writer.writeByte('(');
201
218
  var printed = false;
202
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
219
+ inline for (@typeInfo(Action).@"struct".fields) |field| {
203
220
  const val = @field(self, field.name);
204
221
  switch (@typeInfo(@TypeOf(val))) {
205
- Enum => if (val != .None) {
222
+ .@"enum" => if (val != .None) {
206
223
  if (printed) try writer.writeAll(", ");
207
224
  if (shape) {
208
225
  try writer.print("{s}:?", .{field.name});
@@ -211,19 +228,14 @@ pub const Action = packed struct(u64) {
211
228
  if (val == .false) "!" else "",
212
229
  field.name,
213
230
  });
214
- } else if (@TypeOf(val) == Optional(Observation) or
215
- @TypeOf(val) == Optional(Confusion))
216
- {
217
- try writer.print("{s}{s}", .{
218
- ([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
219
- field.name,
220
- });
231
+ } else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
232
+ try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field.name });
221
233
  } else {
222
234
  try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
223
235
  }
224
236
  printed = true;
225
237
  },
226
- Int => if (val != 0) {
238
+ .int => if (val != 0) {
227
239
  if (printed) try writer.writeAll(", ");
228
240
  if (shape) {
229
241
  try writer.print("{s}:?", .{field.name});
@@ -256,14 +268,8 @@ pub const Durations = extern struct {
256
268
  return if (player == .P1) &self.p1 else &self.p2;
257
269
  }
258
270
 
259
- pub fn format(
260
- self: Durations,
261
- comptime fmt: []const u8,
262
- opts: std.fmt.FormatOptions,
263
- writer: anytype,
264
- ) !void {
265
- _ = .{ fmt, opts };
266
- 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 });
267
273
  }
268
274
  };
269
275
 
@@ -279,19 +285,18 @@ pub const Duration = packed struct(u32) {
279
285
 
280
286
  _: u1 = 0,
281
287
 
288
+ pub const Field = enum { attacking, binding, sleep, disable, confusion };
289
+
282
290
  pub fn format(
283
291
  self: Duration,
284
- comptime fmt: []const u8,
285
- opts: std.fmt.FormatOptions,
286
- writer: anytype,
292
+ writer: *std.Io.Writer,
287
293
  ) !void {
288
- _ = .{ fmt, opts };
289
294
  try writer.writeByte('(');
290
295
  var printed = false;
291
- inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
296
+ inline for (@typeInfo(Duration).@"struct".fields) |field| {
292
297
  const val = @field(self, field.name);
293
298
  switch (@typeInfo(@TypeOf(val))) {
294
- Int => if (val != 0) {
299
+ .int => if (val != 0) {
295
300
  if (printed) try writer.writeAll(", ");
296
301
  if (comptime std.mem.eql(u8, field.name, "sleeps")) {
297
302
  try writer.print("{s}:[", .{field.name});
@@ -552,7 +557,7 @@ pub fn Chance(comptime Rational: type) type {
552
557
  self: *Self,
553
558
  comptime field: Action.Field,
554
559
  player: Player,
555
- obs: Optional(Confusion),
560
+ obs: Optional(Observation(.confusion)),
556
561
  ) void {
557
562
  if (!enabled) return;
558
563
 
@@ -594,7 +599,7 @@ pub fn Chance(comptime Rational: type) type {
594
599
  }
595
600
  }
596
601
 
597
- 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 {
598
603
  if (!enabled) return;
599
604
 
600
605
  var a = self.actions.get(player);
@@ -618,7 +623,11 @@ pub fn Chance(comptime Rational: type) type {
618
623
  }
619
624
  }
620
625
 
621
- 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 {
622
631
  if (!enabled) return;
623
632
 
624
633
  var a = self.actions.get(player);
@@ -640,7 +649,11 @@ pub fn Chance(comptime Rational: type) type {
640
649
  }
641
650
  }
642
651
 
643
- 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 {
644
657
  if (!enabled) return;
645
658
 
646
659
  var a = self.actions.get(player);
@@ -662,7 +675,11 @@ pub fn Chance(comptime Rational: type) type {
662
675
  }
663
676
  }
664
677
 
665
- 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 {
666
683
  if (!enabled) return;
667
684
 
668
685
  var a = self.actions.get(player);
@@ -684,7 +701,11 @@ pub fn Chance(comptime Rational: type) type {
684
701
  }
685
702
  }
686
703
 
687
- 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 {
688
709
  if (!enabled) return;
689
710
 
690
711
  var a = self.actions.get(player);
@@ -894,13 +915,13 @@ test "Chance.moveSlot" {
894
915
 
895
916
  try chance.moveSlot(.P2, 2, &ms, 2);
896
917
  try expectProbability(&chance.probability, 1, 2);
897
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
918
+ try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
898
919
 
899
920
  chance.reset();
900
921
 
901
922
  try chance.moveSlot(.P1, 1, &ms, 0);
902
923
  try expectProbability(&chance.probability, 1, 3);
903
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
924
+ try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
904
925
  }
905
926
 
906
927
  test "Chance.multiHit" {
@@ -1067,7 +1088,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
1067
1088
 
1068
1089
  r.reduce();
1069
1090
  if (r.p != p or r.q != q) {
1070
- print("expected {d}/{d}, found {}\n", .{ p, q, r });
1091
+ print("expected {d}/{d}, found {f}\n", .{ p, q, r });
1071
1092
  return error.TestExpectedEqual;
1072
1093
  }
1073
1094
  }
@@ -1145,28 +1166,44 @@ const Null = struct {
1145
1166
  self: Null,
1146
1167
  comptime field: Action.Field,
1147
1168
  player: Player,
1148
- obs: Optional(Confusion),
1169
+ obs: Optional(Observation(.confusion)),
1149
1170
  ) void {
1150
1171
  _ = .{ self, field, player, obs };
1151
1172
  }
1152
1173
 
1153
- 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 {
1154
1175
  _ = .{ self, player, obs };
1155
1176
  }
1156
1177
 
1157
- 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 {
1158
1183
  _ = .{ self, player, obs };
1159
1184
  }
1160
1185
 
1161
- 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 {
1162
1191
  _ = .{ self, player, obs };
1163
1192
  }
1164
1193
 
1165
- 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 {
1166
1199
  _ = .{ self, player, obs };
1167
1200
  }
1168
1201
 
1169
- 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 {
1170
1207
  _ = .{ self, player, obs };
1171
1208
  }
1172
1209
 
@@ -108,7 +108,7 @@ pub const Type = enum(u4) {
108
108
  };
109
109
 
110
110
  /// Representation of a Pokémon's typing.
111
- pub const Types = packed struct {
111
+ pub const Types = packed struct(u8) {
112
112
  /// A Pokémon's primary type.
113
113
  type1: Type = .Normal,
114
114
  /// A Pokémon's secondary type (may be identical to its primary type).
@@ -62,7 +62,7 @@ pub fn Battle(comptime RNG: anytype) type {
62
62
  /// The last damage dealt by either side.
63
63
  last_damage: u16 = 0,
64
64
  /// Details about the last move selected/used by each player required to detect desyncs.
65
- last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
65
+ last_moves: [2]MoveDetails = @splat(.{}),
66
66
  /// The pseudo random number generator.
67
67
  rng: RNG,
68
68
 
@@ -122,12 +122,12 @@ test Battle {
122
122
  /// Representation of one side of a Generation I Pokémon battle.
123
123
  pub const Side = extern struct {
124
124
  /// The player's party in its original order
125
- pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
125
+ pokemon: [6]Pokemon = @splat(.{}),
126
126
  /// The active Pokémon for the side, zero initialized if the battle has yet to start.
127
127
  /// Note that fainted Pokémon are still consider "active" until their replacement switches in.
128
128
  active: ActivePokemon = .{},
129
129
  /// One-based slot indexes reflecting the current order of the player's party.
130
- order: [6]u8 = [_]u8{0} ** 6,
130
+ order: [6]u8 = @splat(0),
131
131
  /// The last move the player selected.
132
132
  last_selected_move: Move = .None,
133
133
  /// The last move the player used.
@@ -166,7 +166,7 @@ pub const ActivePokemon = extern struct {
166
166
  /// The active Pokémon's volatile statuses and associated data.
167
167
  volatiles: Volatiles = .{},
168
168
  /// The active Pokémon's current move slots.
169
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
169
+ moves: [4]MoveSlot = @splat(.{}),
170
170
 
171
171
  comptime {
172
172
  assert(@sizeOf(ActivePokemon) == 32);
@@ -186,7 +186,7 @@ pub const Pokemon = extern struct {
186
186
  /// The Pokémon's unmodified stats.
187
187
  stats: Stats(u16) = .{},
188
188
  /// The Pokémon's original stored move slots.
189
- moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
189
+ moves: [4]MoveSlot = @splat(.{}),
190
190
  /// The Pokémon's current HP.
191
191
  hp: u16 = 0,
192
192
  /// The Pokémon's current status.
@@ -19,8 +19,6 @@ const Stats = data.Stats;
19
19
 
20
20
  const endian = builtin.cpu.arch.endian();
21
21
 
22
- const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
23
-
24
22
  /// Configuration options controlling the domain of valid Pokémon that can be generated by the
25
23
  /// random generator helpers.
26
24
  pub const Options = struct {
@@ -163,7 +161,7 @@ pub const Pokemon = struct {
163
161
  var pokemon = data.Pokemon{};
164
162
  pokemon.species = p.species;
165
163
  const species = Species.get(p.species);
166
- inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
164
+ inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields) |field| {
167
165
  const hp = comptime std.mem.eql(u8, field.name, "hp");
168
166
  const spc =
169
167
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -199,7 +197,7 @@ pub const Pokemon = struct {
199
197
  const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
200
198
  var stats: Stats(u16) = .{};
201
199
  const dvs = DVs.random(rand);
202
- inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
200
+ inline for (@typeInfo(@TypeOf(stats)).@"struct".fields) |field| {
203
201
  @field(stats, field.name) = Stats(u16).calc(
204
202
  field.name,
205
203
  @field(species.stats, field.name),
@@ -212,7 +210,7 @@ pub const Pokemon = struct {
212
210
  );
213
211
  }
214
212
 
215
- var ms = [_]MoveSlot{.{}} ** 4;
213
+ var ms: [4]MoveSlot = @splat(.{});
216
214
  const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
217
215
  for (0..n) |i| {
218
216
  var m: Move = .None;
@@ -292,23 +290,23 @@ else
292
290
  pub fn diff(
293
291
  a: *const data.Battle(data.PRNG),
294
292
  b: *const data.Battle(data.PRNG),
295
- w: protocol.ByteStream.Writer,
293
+ w: *protocol.Writer,
296
294
  ) !usize {
297
- const pos = w.stream.pos;
295
+ const pos = w.pos;
298
296
  const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
299
297
  assert(size % 2 == 0);
300
298
  assert(size / 2 < 255);
301
299
 
302
300
  var i: usize = 0;
303
301
  while (i < size) : (i += 2) {
304
- const val = util.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
305
- 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]).*) {
306
304
  try w.writeByte(@intCast(i / 2));
307
305
  try w.writeInt(u16, val, endian);
308
306
  }
309
307
  }
310
308
 
311
- return w.stream.pos - pos;
309
+ return w.pos - pos;
312
310
  }
313
311
 
314
312
  /// TODO