@pkmn/engine 0.1.0-dev.c4f94762 → 0.1.0-dev.c4ffaf39

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.
@@ -387,6 +387,71 @@ pub fn transitions(
387
387
  return stats;
388
388
  }
389
389
 
390
+ pub fn update(
391
+ battle: anytype,
392
+ c1: Choice,
393
+ c2: Choice,
394
+ options: anytype,
395
+ allocator: std.mem.Allocator,
396
+ writer: anytype,
397
+ transition: bool,
398
+ ) !Result {
399
+ options.chance.reset();
400
+
401
+ if (!pkmn.options.chance or !pkmn.options.calc) return battle.update(c1, c2, options);
402
+
403
+ var copy = battle.*;
404
+ const actions = options.chance.actions;
405
+
406
+ // Perfom the actual update
407
+ const result = battle.update(c1, c2, options);
408
+
409
+ if (transition) {
410
+ // Ensure we can generate all transitions from the same original state
411
+ // (we must change the battle's RNG from a FixedRNG to a PRNG because
412
+ // the transitions function relies on RNG for discovery of states)
413
+ const stats = try transitions(unfix(copy), c1, c2, allocator, writer, .{
414
+ .actions = actions,
415
+ .cap = true,
416
+ });
417
+ DEBUG(stats);
418
+ // TODO try expect(stats.frontier < pkmn.gen1.MAX_FRONTIER_SIZE);
419
+ }
420
+
421
+ // Demonstrate that we can produce the same state by forcing the RNG to behave the
422
+ // same as we observed - note that because we do not pass in a durations override
423
+ // mask none of the durations will be extended.
424
+ var override = pkmn.battle.options(
425
+ protocol.NULL,
426
+ chance.NULL,
427
+ Calc{ .overrides = .{ .actions = actions } },
428
+ );
429
+ const overridden = copy.update(c1, c2, &override);
430
+ try expectEqual(result, overridden);
431
+
432
+ // The actual battle excluding its RNG field should match a copy updated with
433
+ // overridden RNG (the copy RNG may have advanced because of no-ops)
434
+ copy.rng = battle.rng;
435
+ try expectEqual(copy, battle.*);
436
+
437
+ return result;
438
+ }
439
+
440
+ fn unfix(actual: anytype) data.Battle(data.PRNG) {
441
+ return .{
442
+ .sides = actual.sides,
443
+ .turn = actual.turn,
444
+ .last_damage = actual.last_damage,
445
+ .last_moves = actual.last_moves,
446
+ .rng = .{ .src = .{
447
+ .seed = if (pkmn.options.showdown)
448
+ 0x12345678
449
+ else
450
+ .{ 123, 234, 56, 78, 9, 101, 112, 131, 4 },
451
+ } },
452
+ };
453
+ }
454
+
390
455
  fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
391
456
  for (frontier, 0..) |f, j| {
392
457
  // TODO: is skipping this redundant check worth it?
@@ -14,7 +14,7 @@ const util = @import("../common/util.zig");
14
14
  const Player = @import("../common/data.zig").Player;
15
15
  const Optional = @import("../common/optional.zig").Optional;
16
16
 
17
- const data = @import("./data.zig");
17
+ const data = @import("data.zig");
18
18
 
19
19
  const enabled = options.chance;
20
20
  const showdown = options.showdown;
@@ -92,13 +92,14 @@ pub const Actions = extern struct {
92
92
  }
93
93
  };
94
94
 
95
+ // TODO: ziglang/zig#19730
95
96
  test Actions {
96
97
  const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
97
98
  const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
98
99
  const c: Actions = .{ .p1 = .{ .hit = .true } };
99
- const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
100
- const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
101
- const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
100
+ // const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
101
+ // const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
102
+ // const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
102
103
 
103
104
  try expect(a.eql(a));
104
105
  try expect(!a.eql(b));
@@ -111,8 +112,8 @@ test Actions {
111
112
  try expect(b.matches(a));
112
113
  try expect(!a.matches(c));
113
114
  try expect(!c.matches(a));
114
- try expect(d.matches(e));
115
- try expect(!d.matches(f));
115
+ // try expect(d.matches(e));
116
+ // try expect(!d.matches(f));
116
117
  }
117
118
 
118
119
  /// Information about the RNG that was observed during a Generation I battle `update` for a
@@ -254,6 +255,8 @@ pub const Duration = packed struct(u16) {
254
255
 
255
256
  pub const Commit = enum { hit, miss, binding };
256
257
 
258
+ const uN = if (options.miss) u8 else u9;
259
+
257
260
  /// Tracks chance actions and their associated probability during a Generation I battle update when
258
261
  /// `options.chance` is enabled.
259
262
  pub fn Chance(comptime Rational: type) type {
@@ -292,13 +295,13 @@ pub fn Chance(comptime Rational: type) type {
292
295
  // shared structure to track this information (though it needs to be cleared appropriately)
293
296
  pending: if (showdown) struct {
294
297
  hit: bool = false,
295
- hit_probablity: u8 = 0,
298
+ hit_probablity: uN = 0,
296
299
  } else struct {
297
300
  crit: bool = false,
298
301
  crit_probablity: u8 = 0,
299
302
  damage_roll: u8 = 0,
300
303
  hit: bool = false,
301
- hit_probablity: u8 = 0,
304
+ hit_probablity: uN = 0,
302
305
  binding: u8 = 0,
303
306
  } = .{},
304
307
 
@@ -397,7 +400,7 @@ pub fn Chance(comptime Rational: type) type {
397
400
  self.actions.p2.speed_tie = self.actions.p1.speed_tie;
398
401
  }
399
402
 
400
- pub fn hit(self: *Self, ok: bool, accuracy: u8) void {
403
+ pub fn hit(self: *Self, ok: bool, accuracy: uN) void {
401
404
  if (!enabled) return;
402
405
 
403
406
  const p = if (ok) accuracy else @as(u8, @intCast(256 - @as(u9, accuracy)));
@@ -706,19 +709,19 @@ test "Chance.damage" {
706
709
 
707
710
  try chance.damage(.P1, 217);
708
711
  if (showdown) {
709
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
712
+ try expectValue(217, chance.actions.p1.damage);
710
713
  try expectProbability(&chance.probability, 1, 39);
711
714
  } else {
712
715
  try expectProbability(&chance.probability, 1, 1);
713
- try expectValue(@as(u8, 0), chance.actions.p1.damage);
716
+ try expectValue(0, chance.actions.p1.damage);
714
717
 
715
718
  try chance.commit(.P1, .miss);
716
719
  try expectProbability(&chance.probability, 1, 1);
717
- try expectValue(@as(u8, 0), chance.actions.p1.damage);
720
+ try expectValue(0, chance.actions.p1.damage);
718
721
 
719
722
  try chance.commit(.P1, .hit);
720
723
  try expectProbability(&chance.probability, 1, 39);
721
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
724
+ try expectValue(217, chance.actions.p1.damage);
722
725
  }
723
726
  }
724
727
 
@@ -770,13 +773,13 @@ test "Chance.moveSlot" {
770
773
 
771
774
  try chance.moveSlot(.P2, 2, &ms, 2);
772
775
  try expectProbability(&chance.probability, 1, 2);
773
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
776
+ try expectValue(2, chance.actions.p2.move_slot);
774
777
 
775
778
  chance.reset();
776
779
 
777
780
  try chance.moveSlot(.P1, 1, &ms, 0);
778
781
  try expectProbability(&chance.probability, 1, 3);
779
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
782
+ try expectValue(1, chance.actions.p1.move_slot);
780
783
  }
781
784
 
782
785
  test "Chance.multiHit" {
@@ -784,32 +787,32 @@ test "Chance.multiHit" {
784
787
 
785
788
  try chance.multiHit(.P1, 3);
786
789
  try expectProbability(&chance.probability, 3, 8);
787
- try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
790
+ try expectValue(3, chance.actions.p1.multi_hit);
788
791
 
789
792
  chance.reset();
790
793
 
791
794
  try chance.multiHit(.P2, 5);
792
795
  try expectProbability(&chance.probability, 1, 8);
793
- try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
796
+ try expectValue(5, chance.actions.p2.multi_hit);
794
797
  }
795
798
 
796
799
  test "Chance.duration" {
797
800
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
798
801
 
799
802
  chance.duration(.sleep, .P1, .P2, 2);
800
- try expectValue(@as(u4, 2), chance.actions.p1.duration);
801
- try expectValue(@as(u3, 1), chance.actions.p2.durations.sleep);
803
+ try expectValue(2, chance.actions.p1.duration);
804
+ try expectValue(1, chance.actions.p2.durations.sleep);
802
805
 
803
806
  chance.reset();
804
807
 
805
808
  chance.duration(.binding, .P2, .P2, 4);
806
809
  if (!showdown) {
807
- try expectValue(@as(u4, 0), chance.actions.p2.duration);
808
- try expectValue(@as(u3, 0), chance.actions.p2.durations.binding);
810
+ try expectValue(0, chance.actions.p2.duration);
811
+ try expectValue(0, chance.actions.p2.durations.binding);
809
812
  try chance.commit(.P2, .hit);
810
813
  }
811
- try expectValue(@as(u4, 4), chance.actions.p2.duration);
812
- try expectValue(@as(u3, 1), chance.actions.p2.durations.binding);
814
+ try expectValue(4, chance.actions.p2.duration);
815
+ try expectValue(1, chance.actions.p2.durations.binding);
813
816
  }
814
817
 
815
818
  test "Chance.sleep" {
@@ -819,7 +822,7 @@ test "Chance.sleep" {
819
822
  chance.actions.p1.durations.sleep = @intCast(i);
820
823
  try chance.sleep(.P1, 0);
821
824
  try expectProbability(&chance.probability, 1, d);
822
- try expectValue(@as(u3, 0), chance.actions.p1.durations.sleep);
825
+ try expectValue(0, chance.actions.p1.durations.sleep);
823
826
 
824
827
  chance.reset();
825
828
 
@@ -842,7 +845,7 @@ test "Chance.confusion" {
842
845
  chance.actions.p2.durations.confusion = @intCast(i);
843
846
  try chance.confusion(.P2, 0);
844
847
  try expectProbability(&chance.probability, 1, d);
845
- try expectValue(@as(u3, 0), chance.actions.p2.durations.confusion);
848
+ try expectValue(0, chance.actions.p2.durations.confusion);
846
849
 
847
850
  chance.reset();
848
851
  }
@@ -865,7 +868,7 @@ test "Chance.disable" {
865
868
  chance.actions.p1.durations.disable = @intCast(i);
866
869
  try chance.disable(.P1, 0);
867
870
  try expectProbability(&chance.probability, 1, d);
868
- try expectValue(@as(u4, 0), chance.actions.p1.durations.disable);
871
+ try expectValue(0, chance.actions.p1.durations.disable);
869
872
 
870
873
  chance.reset();
871
874
 
@@ -888,7 +891,7 @@ test "Chance.attacking" {
888
891
  chance.actions.p2.durations.attacking = @intCast(i);
889
892
  try chance.attacking(.P2, 0);
890
893
  try expectProbability(&chance.probability, 1, d);
891
- try expectValue(@as(u3, 0), chance.actions.p2.durations.attacking);
894
+ try expectValue(0, chance.actions.p2.durations.attacking);
892
895
 
893
896
  chance.reset();
894
897
  }
@@ -914,7 +917,7 @@ test "Chance.binding" {
914
917
  chance.actions.p1.durations.binding = @intCast(i);
915
918
  try chance.binding(.P1, 0);
916
919
  try expectProbability(&chance.probability, p, q);
917
- try expectValue(@as(u3, 0), chance.actions.p1.durations.binding);
920
+ try expectValue(0, chance.actions.p1.durations.binding);
918
921
 
919
922
  chance.reset();
920
923
 
@@ -934,7 +937,7 @@ test "Chance.psywave" {
934
937
 
935
938
  try chance.psywave(.P2, 100, 150);
936
939
  try expectProbability(&chance.probability, 1, 150);
937
- try expectValue(@as(u8, 101), chance.actions.p2.psywave);
940
+ try expectValue(101, chance.actions.p2.psywave);
938
941
  }
939
942
 
940
943
  test "Chance.metronome" {
@@ -988,7 +991,7 @@ const Null = struct {
988
991
  _ = .{ self, p1 };
989
992
  }
990
993
 
991
- pub fn hit(self: Null, ok: bool, accuracy: u8) void {
994
+ pub fn hit(self: Null, ok: bool, accuracy: uN) void {
992
995
  _ = .{ self, ok, accuracy };
993
996
  }
994
997
 
@@ -1091,7 +1091,7 @@ pub const Species = enum(u8) {
1091
1091
  /// The number of Pokémon species in this generation.
1092
1092
  pub const size = 151;
1093
1093
 
1094
- /// The Pokémon's critical hit rate=io out of 256.
1094
+ /// The Pokémon's critical hit ratio out of 256.
1095
1095
  pub fn chance(id: Species) u8 {
1096
1096
  assert(id != .None);
1097
1097
  return CHANCES[@intFromEnum(id) - 1];
@@ -112,7 +112,7 @@ pub fn Battle(comptime RNG: anytype) type {
112
112
  /// make (i.e. on the cartridge a soft-lock occurs).
113
113
  ///
114
114
  /// This function will always return choices > 0 if `pkmn.options.showdown` is true.
115
- pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
115
+ pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
116
116
  return mechanics.choices(self, player, request, out);
117
117
  }
118
118
  };
@@ -316,12 +316,12 @@ test Status {
316
316
  try expect(Status.is(Status.slp(5), .SLP));
317
317
  try expect(!Status.is(Status.slp(5), .EXT));
318
318
  try expect(!Status.is(Status.slp(7), .PSN));
319
- try expectEqual(@as(u3, 5), Status.duration(Status.slp(5)));
319
+ try expectEqual(5, Status.duration(Status.slp(5)));
320
320
  try expect(Status.is(Status.slf(2), .SLP));
321
321
  try expect(Status.is(Status.slf(2), .EXT));
322
322
  try expect(Status.is(Status.TOX, .PSN));
323
323
  try expect(Status.is(Status.TOX, .EXT));
324
- try expectEqual(@as(u3, 1), Status.duration(Status.slp(1)));
324
+ try expectEqual(1, Status.duration(Status.slp(1)));
325
325
  }
326
326
 
327
327
  /// Bitfield representation of volatile statuses and associated data in Generation I.
@@ -405,13 +405,13 @@ test Volatiles {
405
405
  try expect(!volatiles.Transform);
406
406
  try expect(!volatiles.MultiHit);
407
407
 
408
- try expectEqual(@as(u16, 235), volatiles.state);
409
- try expectEqual(@as(u8, 42), volatiles.substitute);
410
- try expectEqual(@as(u4, 2), volatiles.disable_move);
411
- try expectEqual(@as(u4, 4), volatiles.disable_duration);
412
- try expectEqual(@as(u4, 2), volatiles.confusion);
413
- try expectEqual(@as(u5, 4), volatiles.toxic);
414
- try expectEqual(@as(u4, 3), volatiles.attacks);
408
+ try expectEqual(235, volatiles.state);
409
+ try expectEqual(42, volatiles.substitute);
410
+ try expectEqual(2, volatiles.disable_move);
411
+ try expectEqual(4, volatiles.disable_duration);
412
+ try expectEqual(2, volatiles.confusion);
413
+ try expectEqual(4, volatiles.toxic);
414
+ try expectEqual(3, volatiles.attacks);
415
415
  }
416
416
 
417
417
  /// Representaiton of a Pokémon's stats in Generation I.
@@ -442,9 +442,9 @@ test Stats {
442
442
  try expectEqual(0, stats.def);
443
443
 
444
444
  var base = Species.get(.Gyarados).stats;
445
- try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
445
+ try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
446
446
  base = Species.get(.Pidgeot).stats;
447
- try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
447
+ try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
448
448
  }
449
449
 
450
450
  /// Representation of a Pokémon's boosts in Generation I.
@@ -537,7 +537,7 @@ pub const Species = species.Species;
537
537
 
538
538
  test Species {
539
539
  try expectEqual(2, @intFromEnum(Species.Ivysaur));
540
- try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
540
+ try expectEqual(100, Species.get(.Mew).stats.def);
541
541
  }
542
542
 
543
543
  /// Representation of a Generation I type in Pokémon.
@@ -576,8 +576,8 @@ test Types {
576
576
  try expectEqual(Effectiveness.Resisted, Type.effectiveness(.Fire, .Water));
577
577
  try expectEqual(Effectiveness.Neutral, Type.effectiveness(.Normal, .Grass));
578
578
 
579
- try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
580
- try expectEqual(@as(u8, 26), Type.precedence(.Bug, .Ghost));
579
+ try expectEqual(3, Type.precedence(.Water, .Water));
580
+ try expectEqual(26, Type.precedence(.Bug, .Ghost));
581
581
  try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
582
582
 
583
583
  const t: Types = .{ .type1 = .Rock, .type2 = .Ground };
@@ -617,18 +617,18 @@ pub const DVs = struct {
617
617
 
618
618
  test DVs {
619
619
  var dvs: DVs = .{ .spc = 15, .spe = 15 };
620
- try expectEqual(@as(u4, 15), dvs.hp());
620
+ try expectEqual(15, dvs.hp());
621
621
  dvs = .{
622
622
  .atk = 5,
623
623
  .def = 15,
624
624
  .spe = 13,
625
625
  .spc = 13,
626
626
  };
627
- try expectEqual(@as(u4, 15), dvs.hp());
627
+ try expectEqual(15, dvs.hp());
628
628
  dvs = .{
629
629
  .def = 3,
630
630
  .spe = 10,
631
631
  .spc = 11,
632
632
  };
633
- try expectEqual(@as(u4, 13), dvs.hp());
633
+ try expectEqual(13, dvs.hp());
634
634
  }
@@ -43,6 +43,8 @@ pub const Options = struct {
43
43
  /// Whether to generate Pokémon with moves that contain bugs on Pokémon Showdown that are
44
44
  /// unimplementable in the engine.
45
45
  block: bool = showdown,
46
+ // FIXME: remove once durations work with -Dchance
47
+ durations: bool = false,
46
48
  };
47
49
 
48
50
  /// Helpers to simplify initialization of a Generation I battle.
@@ -86,18 +88,38 @@ pub const Battle = struct {
86
88
  };
87
89
 
88
90
  fn prng(rand: *PSRNG) data.PRNG {
89
- // GLITCH: initial bytes in seed can only range from 0-252, not 0-255
90
- const max: u8 = 253;
91
91
  return .{
92
92
  .src = .{
93
93
  .seed = if (showdown)
94
94
  rand.newSeed()
95
- else
96
- .{
97
- rand.range(u8, 0, max), rand.range(u8, 0, max), rand.range(u8, 0, max),
98
- rand.range(u8, 0, max), rand.range(u8, 0, max), rand.range(u8, 0, max),
99
- rand.range(u8, 0, max), rand.range(u8, 0, max), rand.range(u8, 0, max),
100
- },
95
+ else seed: {
96
+ // GLITCH: The initial RNG seed in Gen I link battles is heavily constrained
97
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
98
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
99
+
100
+ // Bytes in seed can only range from 0-252, not 0-255
101
+ const max: u8 = 253;
102
+ // Instructions take multiples of 4 cycles to execute
103
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
104
+ var add: u8 = rand.range(u8, 0, 256);
105
+ var carry: u8 = 0;
106
+
107
+ var i: u8 = 0;
108
+ while (i < seed.len) {
109
+ const div: u8 = @intCast(timer >> 8);
110
+ add +%= div +% carry;
111
+ if (add < max) {
112
+ seed[i] = add;
113
+ i += 1;
114
+ timer +%= 472;
115
+ carry = 1;
116
+ } else {
117
+ timer +%= 452;
118
+ carry = 0;
119
+ }
120
+ }
121
+ break :seed seed;
122
+ },
101
123
  },
102
124
  };
103
125
  }
@@ -210,6 +232,7 @@ pub const Pokemon = struct {
210
232
  var m: Move = .None;
211
233
  sample: while (true) {
212
234
  m = @enumFromInt(rand.range(u8, 1, Move.size - 1 + 1));
235
+ if (opt.durations and durations(m)) continue :sample;
213
236
  if (opt.block and blocked(m)) continue :sample;
214
237
  for (0..i) |j| if (ms[j].id == m) continue :sample;
215
238
  break;
@@ -247,6 +270,23 @@ fn blocked(m: Move) bool {
247
270
  };
248
271
  }
249
272
 
273
+ fn durations(m: Move) bool {
274
+ // Any moves that have effects that result in duration tracking with -Dchance
275
+ return switch (Move.get(m).effect) {
276
+ .Confusion,
277
+ .Bide,
278
+ .Sleep,
279
+ .Rage,
280
+ .Binding,
281
+ .Thrashing,
282
+ .ConfusionChance,
283
+ .Disable,
284
+ .Metronome,
285
+ => true,
286
+ else => false,
287
+ };
288
+ }
289
+
250
290
  /// Convenience helper to create a move-type choice for the provided move slot.
251
291
  pub fn move(slot: u4) Choice {
252
292
  return .{ .type = .Move, .data = slot };
@@ -478,12 +518,12 @@ test "Rolls.damage" {
478
518
  test "Rolls.coalesce" {
479
519
  var summaries =
480
520
  calc.Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
481
- try expectEqual(@as(u8, 0), try Rolls.coalesce(.P2, 0, &summaries, false));
482
- try expectEqual(@as(u8, 241), try Rolls.coalesce(.P2, 238, &summaries, false));
483
- try expectEqual(@as(u8, 255), try Rolls.coalesce(.P2, 238, &summaries, true));
521
+ try expectEqual(0, try Rolls.coalesce(.P2, 0, &summaries, false));
522
+ try expectEqual(241, try Rolls.coalesce(.P2, 238, &summaries, false));
523
+ try expectEqual(255, try Rolls.coalesce(.P2, 238, &summaries, true));
484
524
  summaries.p1.damage.final = 74;
485
- try expectEqual(@as(u8, 217), try Rolls.coalesce(.P2, 217, &summaries, false));
486
- try expectEqual(@as(u8, 255), try Rolls.coalesce(.P2, 217, &summaries, true));
525
+ try expectEqual(217, try Rolls.coalesce(.P2, 217, &summaries, false));
526
+ try expectEqual(255, try Rolls.coalesce(.P2, 217, &summaries, true));
487
527
  }
488
528
 
489
529
  test "Rolls.hit" {
@@ -771,9 +811,9 @@ test "Rolls.psywave" {
771
811
 
772
812
  try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
773
813
  var rolls = Rolls.psywave(actions.p2, &side, .None);
774
- try expectEqual(@as(u8, 150), rolls[rolls.len - 1]);
814
+ try expectEqual(150, rolls[rolls.len - 1]);
775
815
  side.stored().level = 81;
776
816
  rolls = Rolls.psywave(actions.p2, &side, .None);
777
- try expectEqual(@as(u8, 121), rolls[rolls.len - 1]);
817
+ try expectEqual(121, rolls[rolls.len - 1]);
778
818
  try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
779
819
  }