@pkmn/engine 0.1.0-dev.f3aab252 → 0.1.0-dev.f3b63b86

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 (80) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +35 -8
  3. package/build/pkg/addon/browser.d.ts +3 -0
  4. package/build/pkg/addon/browser.js +47 -0
  5. package/build/pkg/addon/browser.js.map +1 -0
  6. package/build/pkg/addon/node.d.ts +3 -0
  7. package/build/pkg/addon/node.js +110 -0
  8. package/build/pkg/addon/node.js.map +1 -0
  9. package/build/pkg/addon/wasm.d.ts +2 -0
  10. package/build/pkg/addon/wasm.js +53 -0
  11. package/build/pkg/addon/wasm.js.map +1 -0
  12. package/build/pkg/addon.d.ts +26 -4
  13. package/build/pkg/addon.js +34 -65
  14. package/build/pkg/addon.js.map +1 -1
  15. package/build/pkg/data/layout.json +15 -2
  16. package/build/pkg/data/protocol.json +23 -9
  17. package/build/pkg/data.d.ts +23 -1
  18. package/build/pkg/data.js.map +1 -1
  19. package/build/pkg/gen1.d.ts +15 -18
  20. package/build/pkg/gen1.js +28 -19
  21. package/build/pkg/gen1.js.map +1 -1
  22. package/build/pkg/index.d.ts +39 -89
  23. package/build/pkg/index.js +23 -70
  24. package/build/pkg/index.js.map +1 -1
  25. package/build/pkg/protocol.d.ts +20 -1
  26. package/build/pkg/protocol.js +105 -103
  27. package/build/pkg/protocol.js.map +1 -1
  28. package/build/tools/debug.js +68 -74
  29. package/build/tools/debug.js.map +1 -1
  30. package/build/tools/display/debug.jsx +152 -0
  31. package/build/tools/display/debug.jsx.map +1 -0
  32. package/build/tools/display/index.js +152 -0
  33. package/build/tools/display/index.js.map +1 -0
  34. package/build/tools/display/ui.jsx +359 -0
  35. package/build/tools/display/ui.jsx.map +1 -0
  36. package/build/tools/display/util.js +33 -0
  37. package/build/tools/display/util.js.map +1 -0
  38. package/build.zig +235 -213
  39. package/package.json +59 -141
  40. package/src/bin/install-pkmn-engine +25 -9
  41. package/src/bin/pkmn-debug +1 -1
  42. package/src/lib/bindings/c.zig +200 -0
  43. package/src/lib/bindings/node.zig +117 -0
  44. package/src/lib/bindings/wasm.zig +50 -0
  45. package/src/lib/common/array.zig +5 -12
  46. package/src/lib/common/debug.zig +30 -9
  47. package/src/lib/common/js.zig +3 -9
  48. package/src/lib/common/optional.zig +11 -21
  49. package/src/lib/common/options.zig +4 -0
  50. package/src/lib/common/protocol.zig +180 -171
  51. package/src/lib/common/rational.zig +278 -285
  52. package/src/lib/common/rng.zig +1 -3
  53. package/src/lib/common/util.zig +14 -31
  54. package/src/lib/gen1/calc.zig +258 -106
  55. package/src/lib/gen1/chance.zig +57 -54
  56. package/src/lib/gen1/data/moves.zig +2 -4
  57. package/src/lib/gen1/data/species.zig +1 -3
  58. package/src/lib/gen1/data/types.zig +3 -4
  59. package/src/lib/gen1/data.zig +12 -16
  60. package/src/lib/gen1/helpers.zig +13 -28
  61. package/src/lib/gen1/mechanics.zig +131 -101
  62. package/src/lib/gen2/calc.zig +12 -23
  63. package/src/lib/gen2/chance.zig +12 -18
  64. package/src/lib/gen2/data/items.zig +1 -3
  65. package/src/lib/gen2/data/moves.zig +2 -4
  66. package/src/lib/gen2/data/species.zig +1 -3
  67. package/src/lib/gen2/data/types.zig +7 -5
  68. package/src/lib/gen2/data.zig +19 -22
  69. package/src/lib/gen2/generated.zig +18 -23
  70. package/src/lib/gen2/helpers.zig +9 -18
  71. package/src/lib/gen2/mechanics.zig +50 -47
  72. package/src/lib/napi.h +2 -0
  73. package/src/lib/node.zig +4 -121
  74. package/src/lib/pkmn.zig +98 -19
  75. package/src/lib/wasm.zig +9 -9
  76. package/src/tools/display/dom.ts +93 -0
  77. package/src/tools/display/ui.css +354 -0
  78. package/build/test/display.js +0 -405
  79. package/build/test/display.js.map +0 -1
  80. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -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
  };
@@ -175,18 +186,17 @@ pub const Action = packed struct(u64) {
175
186
 
176
187
  pub const Field = std.meta.FieldEnum(Action);
177
188
 
178
- pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
179
- _ = .{ f, o };
189
+ pub fn format(a: Action, w: *std.Io.Writer) !void {
180
190
  try fmt(a, w, false);
181
191
  }
182
192
 
183
- pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
193
+ pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
184
194
  try writer.writeByte('(');
185
195
  var printed = false;
186
- inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
196
+ inline for (@typeInfo(Action).@"struct".fields) |field| {
187
197
  const val = @field(self, field.name);
188
198
  switch (@typeInfo(@TypeOf(val))) {
189
- Enum => if (val != .None) {
199
+ .@"enum" => if (val != .None) {
190
200
  if (printed) try writer.writeAll(", ");
191
201
  if (shape) {
192
202
  try writer.print("{s}:?", .{field.name});
@@ -207,7 +217,7 @@ pub const Action = packed struct(u64) {
207
217
  }
208
218
  printed = true;
209
219
  },
210
- Int => if (val != 0) {
220
+ .int => if (val != 0) {
211
221
  if (printed) try writer.writeAll(", ");
212
222
  if (shape) {
213
223
  try writer.print("{s}:?", .{field.name});
@@ -224,7 +234,7 @@ pub const Action = packed struct(u64) {
224
234
  };
225
235
 
226
236
  /// TODO
227
- pub const Durations = struct {
237
+ pub const Durations = extern struct {
228
238
  /// TODO
229
239
  p1: Duration = .{},
230
240
  /// TODO
@@ -240,18 +250,12 @@ pub const Durations = struct {
240
250
  return if (player == .P1) &self.p1 else &self.p2;
241
251
  }
242
252
 
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 });
253
+ pub fn format(self: Durations, writer: *std.Io.Writer) !void {
254
+ try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
251
255
  }
252
256
  };
253
257
 
254
- const Sleeps = Array(6, u3);
258
+ pub const Sleeps = Array(6, u3);
255
259
 
256
260
  /// TODO
257
261
  pub const Duration = packed struct(u32) {
@@ -265,17 +269,14 @@ pub const Duration = packed struct(u32) {
265
269
 
266
270
  pub fn format(
267
271
  self: Duration,
268
- comptime fmt: []const u8,
269
- opts: std.fmt.FormatOptions,
270
- writer: anytype,
272
+ writer: *std.Io.Writer,
271
273
  ) !void {
272
- _ = .{ fmt, opts };
273
274
  try writer.writeByte('(');
274
275
  var printed = false;
275
- inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
276
+ inline for (@typeInfo(Duration).@"struct".fields) |field| {
276
277
  const val = @field(self, field.name);
277
278
  switch (@typeInfo(@TypeOf(val))) {
278
- Int => if (val != 0) {
279
+ .int => if (val != 0) {
279
280
  if (printed) try writer.writeAll(", ");
280
281
  if (comptime std.mem.eql(u8, field.name, "sleeps")) {
281
282
  try writer.print("{s}:[", .{field.name});
@@ -375,7 +376,7 @@ pub fn Chance(comptime Rational: type) type {
375
376
 
376
377
  // If the move actually lands we can commit any past critical hit / damage rolls. Note
377
378
  // 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
379
+ // "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
379
380
  // damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
380
381
  // passed to the critical hit helper).
381
382
  //
@@ -391,7 +392,7 @@ pub fn Chance(comptime Rational: type) type {
391
392
  action.critical_hit = if (self.pending.crit) .true else .false;
392
393
  } else assert(!self.pending.glitch);
393
394
 
394
- if (self.pending.damage_roll > 0) {
395
+ if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
395
396
  try self.probability.update(1, 39);
396
397
  action.damage = self.pending.damage_roll;
397
398
  }
@@ -656,6 +657,7 @@ pub fn Chance(comptime Rational: type) type {
656
657
  var d = self.durations.get(player);
657
658
  const n = d.attacking;
658
659
  if (obs == .ended) {
660
+ // "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
659
661
  assert(n >= 2 and n <= 3);
660
662
  if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
661
663
  d.attacking = 0;
@@ -682,6 +684,7 @@ pub fn Chance(comptime Rational: type) type {
682
684
  const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
683
685
 
684
686
  if (obs == .ended) {
687
+ // "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
685
688
  assert(n >= 1 and n <= 4);
686
689
  if (n != 4) try self.probability.update(p, q);
687
690
  d.binding = 0;
@@ -1049,7 +1052,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
1049
1052
 
1050
1053
  r.reduce();
1051
1054
  if (r.p != p or r.q != q) {
1052
- print("expected {d}/{d}, found {}\n", .{ p, q, r });
1055
+ print("expected {d}/{d}, found {f}\n", .{ p, q, r });
1053
1056
  return error.TestExpectedEqual;
1054
1057
  }
1055
1058
  }
@@ -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;
@@ -266,14 +258,7 @@ fn blocked(m: Move) bool {
266
258
  fn durations(m: Move) bool {
267
259
  // Any moves that have effects that result in duration tracking with -Dchance
268
260
  return switch (Move.get(m).effect) {
269
- .Confusion,
270
- .Bide,
271
- .Sleep,
272
- .Rage,
273
- .Binding,
274
- .Thrashing,
275
- .ConfusionChance,
276
- .Disable,
261
+ .Haze,
277
262
  .Metronome,
278
263
  => true,
279
264
  else => false,
@@ -305,23 +290,23 @@ else
305
290
  pub fn diff(
306
291
  a: *const data.Battle(data.PRNG),
307
292
  b: *const data.Battle(data.PRNG),
308
- w: protocol.ByteStream.Writer,
293
+ w: *protocol.Writer,
309
294
  ) !usize {
310
- const pos = w.stream.pos;
295
+ const pos = w.pos;
311
296
  const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
312
297
  assert(size % 2 == 0);
313
298
  assert(size / 2 < 255);
314
299
 
315
300
  var i: usize = 0;
316
301
  while (i < size) : (i += 2) {
317
- const val = util.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
318
- 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]).*) {
319
304
  try w.writeByte(@intCast(i / 2));
320
305
  try w.writeInt(u16, val, endian);
321
306
  }
322
307
  }
323
308
 
324
- return w.stream.pos - pos;
309
+ return w.pos - pos;
325
310
  }
326
311
 
327
312
  /// TODO