@pkmn/engine 0.1.0-dev.a324c50f → 0.1.0-dev.a93847b2

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 (41) hide show
  1. package/README.md +45 -37
  2. package/build/pkg/data/ids.json +12 -12
  3. package/build/pkg/data/layout.json +2 -2
  4. package/build/pkg/data/protocol.json +69 -12
  5. package/build/pkg/data.d.ts +2 -2
  6. package/build/pkg/gen1.js +2 -2
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/protocol.js +56 -37
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build.zig +53 -44
  11. package/package.json +40 -19
  12. package/src/bin/install-pkmn-engine +2 -2
  13. package/src/lib/binding/node.zig +1 -1
  14. package/src/lib/common/battle.zig +17 -0
  15. package/src/lib/common/data.zig +4 -16
  16. package/src/lib/common/js.zig +118 -0
  17. package/src/lib/common/optional.zig +67 -0
  18. package/src/lib/common/protocol.zig +561 -80
  19. package/src/lib/common/rational.zig +353 -0
  20. package/src/lib/common/rng.zig +6 -6
  21. package/src/lib/common/util.zig +12 -0
  22. package/src/lib/gen1/README.md +273 -193
  23. package/src/lib/gen1/calc.zig +426 -0
  24. package/src/lib/gen1/chance.zig +1060 -0
  25. package/src/lib/gen1/data/moves.zig +183 -171
  26. package/src/lib/gen1/data/species.zig +1 -1
  27. package/src/lib/gen1/data/types.zig +3 -3
  28. package/src/lib/gen1/data.zig +31 -38
  29. package/src/lib/gen1/helpers.zig +241 -103
  30. package/src/lib/gen1/mechanics.zig +111 -166
  31. package/src/lib/gen2/README.md +106 -85
  32. package/src/lib/gen2/calc.zig +143 -0
  33. package/src/lib/gen2/chance.zig +1314 -0
  34. package/src/lib/gen2/data/items.zig +11 -17
  35. package/src/lib/gen2/data/moves.zig +662 -568
  36. package/src/lib/gen2/data/species.zig +1 -1
  37. package/src/lib/gen2/data/types.zig +129 -26
  38. package/src/lib/gen2/data.zig +89 -64
  39. package/src/lib/gen2/generated.zig +857 -0
  40. package/src/lib/gen2/helpers.zig +8 -12
  41. package/src/lib/gen2/mechanics.zig +2994 -9
@@ -1,4 +1,4 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
3
  const std = @import("std");
4
4
 
@@ -1,4 +1,4 @@
1
- //! Code generated by `tools/generate` - manual edits will be overwritten
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
2
 
3
3
  const std = @import("std");
4
4
 
@@ -30,7 +30,7 @@ pub const Type = enum(u4) {
30
30
  Ice,
31
31
  Dragon,
32
32
 
33
- const CHART = [15][15]Effectiveness{
33
+ const CHART: [15][15]Effectiveness = .{
34
34
  [_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
35
35
  [_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
36
36
  [_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
@@ -99,7 +99,7 @@ pub const Type = enum(u4) {
99
99
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
100
100
  }
101
101
 
102
- /// The precedence order of type `t2` vs. type `t1`.
102
+ /// The precedence order of Type `t2` vs. Type `t1`.
103
103
  pub fn precedence(t1: Type, t2: Type) u8 {
104
104
  for (PRECEDENCE, 0..) |matchup, i| {
105
105
  if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
@@ -65,7 +65,7 @@ pub fn Battle(comptime RNG: anytype) type {
65
65
  /// The last damage dealt by either side.
66
66
  last_damage: u16 = 0,
67
67
  /// Details about the last move selected/used by each player required to detect desyncs.
68
- last_moves: MoveDetails = .{},
68
+ last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
69
69
  /// The pseudo random number generator.
70
70
  rng: RNG,
71
71
 
@@ -86,6 +86,15 @@ pub fn Battle(comptime RNG: anytype) type {
86
86
  return player.ident(@intCast(self.side(player).order[0]));
87
87
  }
88
88
 
89
+ /// Returns the `MoveDetails` for the given `player`.
90
+ pub inline fn lastMove(
91
+ self: anytype,
92
+ player: Player,
93
+ ) PointerType(@TypeOf(self), MoveDetails) {
94
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
95
+ return &self.last_moves[@intFromEnum(player)];
96
+ }
97
+
89
98
  /// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
90
99
  /// battle, optionally writing protocol message logs to `options.log` if `pkmn.options.log`
91
100
  /// is enabled.
@@ -205,7 +214,7 @@ pub const Pokemon = extern struct {
205
214
  }
206
215
  };
207
216
 
208
- /// Representation of a Generation I & II Pokémon's move slot in a battle.
217
+ /// Representation of a Generation I Pokémon's move slot in a battle.
209
218
  pub const MoveSlot = extern struct {
210
219
  /// The identifier for the move.
211
220
  id: Move = .None,
@@ -213,24 +222,16 @@ pub const MoveSlot = extern struct {
213
222
  pp: u8 = 0,
214
223
 
215
224
  comptime {
216
- assert(@sizeOf(MoveSlot) == @sizeOf(u16));
225
+ assert(@sizeOf(MoveSlot) == 2);
217
226
  }
218
227
  };
219
228
 
220
229
  /// Details required to detect desyncs based on the move last selected/executed by players.
221
- pub const MoveDetails = packed struct {
222
- /// The move slot index of the last move selected in the battle menu by Player 1
223
- p1_index: if (showdown) u8 else u4 = 0,
224
- /// Whether the last move executed by Player 1 was counterable.
225
- p1_counterable: if (showdown) u8 else u4 = 0,
226
- /// The move slot index of the last move selected in the battle menu by Player 2
227
- p2_index: if (showdown) u8 else u4 = 0,
228
- /// Whether the last move executed by Player 2 was counterable.
229
- p2_counterable: if (showdown) u8 else u4 = 0,
230
-
231
- comptime {
232
- assert(@sizeOf(MoveDetails) == if (showdown) 4 else 2);
233
- }
230
+ pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
231
+ /// The move slot index of the last move selected in the battle menu.
232
+ index: if (showdown) u8 else u4 = 0,
233
+ /// Whether the last move executed was counterable.
234
+ counterable: if (showdown) u8 else u4 = 0,
234
235
  };
235
236
 
236
237
  /// Bitfield representation of a Generation I & II Pokémon's major status condition.
@@ -324,7 +325,7 @@ test Status {
324
325
  }
325
326
 
326
327
  /// Bitfield representation of volatile statuses and associated data in Generation I.
327
- pub const Volatiles = packed struct {
328
+ pub const Volatiles = packed struct(u64) {
328
329
  /// Whether the "Bide" volatile status is present.
329
330
  Bide: bool = false,
330
331
  /// Whether the "Thrashing" volatile status is present.
@@ -377,19 +378,15 @@ pub const Volatiles = packed struct {
377
378
  /// The identity of whom the active Pokémon is transformed into.
378
379
  transform: u4 = 0,
379
380
  /// The remaining turns the move is disabled.
380
- disabled_duration: u4 = 0,
381
+ disable_duration: u4 = 0,
381
382
  /// The move slot (1-4) that is disabled.
382
- disabled_move: u3 = 0,
383
+ disable_move: u3 = 0,
383
384
  /// The number of turns toxic damage has been accumulating.
384
385
  toxic: u5 = 0,
385
-
386
- comptime {
387
- assert(@sizeOf(Volatiles) == 8);
388
- }
389
386
  };
390
387
 
391
388
  test Volatiles {
392
- var volatiles = Volatiles{};
389
+ var volatiles: Volatiles = .{};
393
390
  volatiles.Confusion = true;
394
391
  volatiles.confusion = 2;
395
392
  volatiles.Thrashing = true;
@@ -398,8 +395,8 @@ test Volatiles {
398
395
  volatiles.Substitute = true;
399
396
  volatiles.substitute = 42;
400
397
  volatiles.toxic = 4;
401
- volatiles.disabled_move = 2;
402
- volatiles.disabled_duration = 4;
398
+ volatiles.disable_move = 2;
399
+ volatiles.disable_duration = 4;
403
400
 
404
401
  try expect(volatiles.Confusion);
405
402
  try expect(volatiles.Thrashing);
@@ -410,8 +407,8 @@ test Volatiles {
410
407
 
411
408
  try expectEqual(@as(u16, 235), volatiles.state);
412
409
  try expectEqual(@as(u8, 42), volatiles.substitute);
413
- try expectEqual(@as(u4, 2), volatiles.disabled_move);
414
- try expectEqual(@as(u4, 4), volatiles.disabled_duration);
410
+ try expectEqual(@as(u4, 2), volatiles.disable_move);
411
+ try expectEqual(@as(u4, 4), volatiles.disable_duration);
415
412
  try expectEqual(@as(u4, 2), volatiles.confusion);
416
413
  try expectEqual(@as(u5, 4), volatiles.toxic);
417
414
  try expectEqual(@as(u4, 3), volatiles.attacks);
@@ -451,7 +448,7 @@ test Stats {
451
448
  }
452
449
 
453
450
  /// Representation of a Pokémon's boosts in Generation I.
454
- pub const Boosts = packed struct {
451
+ pub const Boosts = packed struct(u32) {
455
452
  /// A Pokémon's Attack boosts.
456
453
  atk: i4 = 0,
457
454
  /// A Pokémon's Defense boosts.
@@ -466,14 +463,10 @@ pub const Boosts = packed struct {
466
463
  evasion: i4 = 0,
467
464
 
468
465
  _: u8 = 0,
469
-
470
- comptime {
471
- assert(@sizeOf(Boosts) == 4);
472
- }
473
466
  };
474
467
 
475
468
  test Boosts {
476
- const boosts = Boosts{ .spc = -6 };
469
+ const boosts: Boosts = .{ .spc = -6 };
477
470
  try expectEqual(0, boosts.atk);
478
471
  try expectEqual(-6, boosts.spc);
479
472
  }
@@ -485,7 +478,7 @@ test Move {
485
478
  try expectEqual(2, @intFromEnum(Move.KarateChop));
486
479
  const move = Move.get(.Fissure);
487
480
  try expectEqual(Move.Effect.OHKO, move.effect);
488
- try expectEqual(@as(u8, 30), move.accuracy);
481
+ try expectEqual(rng.Gen12.percent(30), move.accuracy);
489
482
  try expectEqual(Type.Ground, move.type);
490
483
 
491
484
  try expect(!Move.Effect.onBegin(.None));
@@ -623,16 +616,16 @@ pub const DVs = struct {
623
616
  };
624
617
 
625
618
  test DVs {
626
- var dvs = DVs{ .spc = 15, .spe = 15 };
619
+ var dvs: DVs = .{ .spc = 15, .spe = 15 };
627
620
  try expectEqual(@as(u4, 15), dvs.hp());
628
- dvs = DVs{
621
+ dvs = .{
629
622
  .atk = 5,
630
623
  .def = 15,
631
624
  .spe = 13,
632
625
  .spc = 13,
633
626
  };
634
627
  try expectEqual(@as(u4, 15), dvs.hp());
635
- dvs = DVs{
628
+ dvs = .{
636
629
  .def = 3,
637
630
  .spe = 10,
638
631
  .spc = 11,
@@ -350,47 +350,65 @@ pub const Rolls = struct {
350
350
  const U3_NONE = [_]u3{0};
351
351
  const U3_FORCED = [_]u3{1};
352
352
 
353
- /// Returns a slice with a range of values for sleep given the `action` state.
353
+ /// Returns a slice with a range of values for sleep given the `action` and `origin` states.
354
354
  pub inline fn sleep(action: Action, origin: Action) []const u3 {
355
- const turns = @field(action.durations, "sleep");
356
- _ = origin;
357
- return if (turns < 1 or turns >= 7) &U3_NONE else &[_]u3{ 0, turns };
355
+ const a = @field(action.durations, "sleep");
356
+ const o = @field(origin.durations, "sleep");
357
+ if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
358
+ assert(a == 0 or a == o + 1);
359
+ return if (o >= 7) &U3_NONE else &[_]u3{ 0, o + 1 };
358
360
  }
359
361
 
360
362
  const DISABLE_NONE = [_]u4{0};
361
363
 
362
- /// Returns a slice with a range of values for disable given the `action` state
364
+ /// Returns a slice with a range of values for disable given the `action` and `origin` states
363
365
  /// and the state of the `parent` (the player's Pokémon's remaining sleep turns).
364
366
  pub inline fn disable(action: Action, origin: Action, parent: u4) []const u4 {
365
- const turns = @field(action.durations, "disable");
366
- _ = origin;
367
- return if (parent > 0 or turns < 1 or turns >= 8) &DISABLE_NONE else &[_]u4{ 0, turns };
367
+ if (parent > 0) return &DISABLE_NONE;
368
+ const a = @field(action.durations, "disable");
369
+ const o = @field(origin.durations, "disable");
370
+ if (o == a) return if (o == 0) &DISABLE_NONE else &[_]u4{o};
371
+ assert(a == 0 or a == o + 1);
372
+ return if (o >= 8) &DISABLE_NONE else &[_]u4{ 0, o + 1 };
368
373
  }
369
374
 
370
- /// Returns a slice with a range of values for confusion given the `action` state
375
+ /// Returns a slice with a range of values for confusion given the `action` and `origin` states
371
376
  /// and the state of the `parent` (the player's remaining sleep turns).
372
377
  pub inline fn confusion(action: Action, origin: Action, parent: u4) []const u3 {
373
- const turns = @field(action.durations, "confusion");
374
- _ = origin;
375
- if (parent > 0 or turns < 1 or turns >= 5) return &U3_NONE;
376
- return if (turns < 2) &U3_FORCED else &[_]u3{ 0, turns };
378
+ if (parent > 0) return &U3_NONE;
379
+ const a = @field(action.durations, "confusion");
380
+ const o = @field(origin.durations, "confusion");
381
+ if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
382
+ assert(a == 0 or a == o + 1);
383
+ return if (o >= 5) &U3_NONE else if (o < 2)
384
+ if (a == 0) &U3_NONE else &U3_FORCED
385
+ else
386
+ &[_]u3{ 0, o + 1 };
377
387
  }
378
388
 
379
- /// Returns a slice with a range of values for attacking given the `action` state
389
+ /// Returns a slice with a range of values for attacking given the `action` and `origin` states
380
390
  /// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
381
391
  pub inline fn attacking(action: Action, origin: Action, parent: Optional(bool)) []const u3 {
382
- const turns = @field(action.durations, "attacking");
383
- _ = origin;
384
- if (parent == .true or turns < 1 or turns >= 3) return &U3_NONE;
385
- return if (turns < 2) &U3_FORCED else &[_]u3{ 0, turns };
392
+ if (parent == .true) return &U3_NONE;
393
+ const a = @field(action.durations, "attacking");
394
+ const o = @field(origin.durations, "attacking");
395
+ if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
396
+ assert(a == 0 or a == o + 1);
397
+ return if (o >= 3) &U3_NONE else if (o < 2)
398
+ if (a == 0) &U3_NONE else &U3_FORCED
399
+ else
400
+ &[_]u3{ 0, o + 1 };
386
401
  }
387
402
 
388
- /// Returns a slice with a range of values for binding given the `action` state
403
+ /// Returns a slice with a range of values for binding given the `action` and `origin` states
389
404
  /// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
390
405
  pub inline fn binding(action: Action, origin: Action, parent: Optional(bool)) []const u3 {
391
- const turns = @field(action.durations, "binding");
392
- _ = origin;
393
- return if (parent == .true or turns < 1 or turns >= 4) &U3_NONE else &[_]u3{ 0, turns };
406
+ if (parent == .true) return &U3_NONE;
407
+ const a = @field(action.durations, "binding");
408
+ const o = @field(origin.durations, "binding");
409
+ if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
410
+ assert(a == 0 or a == o + 1);
411
+ return if (o >= 4) &U3_NONE else &[_]u3{ 0, o + 1 };
394
412
  }
395
413
 
396
414
  const SLOT_NONE = [_]u4{0};
@@ -437,36 +455,21 @@ pub const Rolls = struct {
437
455
  }
438
456
 
439
457
  const MOVE_NONE = [_]Move{.None};
440
- const MOVES = init: {
441
- var moves: [Move.size - 2]Move = undefined;
442
- var i: usize = 0;
443
- for (@typeInfo(Move).Enum.fields) |f| {
444
- if (!(std.mem.eql(u8, f.name, "None") or
445
- std.mem.eql(u8, f.name, "Metronome") or
446
- std.mem.eql(u8, f.name, "Struggle") or
447
- std.mem.eql(u8, f.name, "SKIP_TURN")))
448
- {
449
- moves[i] = @field(Move, f.name);
450
- i += 1;
451
- }
452
- }
453
- break :init moves;
454
- };
455
458
 
456
459
  /// Returns a slice with the correct range of values for metronome given the `action` state.
457
460
  pub inline fn metronome(action: Action) []const Move {
458
- return if (@field(action, "metronome") == .None) &MOVE_NONE else &MOVES;
461
+ return if (@field(action, "metronome") == .None) &MOVE_NONE else &Move.METRONOME;
459
462
  }
460
463
  };
461
464
 
462
465
  test "Rolls.speedTie" {
463
- const actions = Actions{ .p1 = .{ .speed_tie = .P2 } };
466
+ const actions: Actions = .{ .p1 = .{ .speed_tie = .P2 } };
464
467
  try expectEqualSlices(Optional(Player), &.{ .P1, .P2 }, Rolls.speedTie(actions.p1));
465
468
  try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
466
469
  }
467
470
 
468
471
  test "Rolls.damage" {
469
- const actions = Actions{ .p2 = .{ .damage = 221 } };
472
+ const actions: Actions = .{ .p2 = .{ .damage = 221 } };
470
473
  try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
471
474
  try expectEqual(Rolls.Range{ .min = 217, .max = 256 }, Rolls.damage(actions.p2, .None));
472
475
  try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p2, .false));
@@ -484,7 +487,7 @@ test "Rolls.coalesce" {
484
487
  }
485
488
 
486
489
  test "Rolls.hit" {
487
- const actions = Actions{ .p2 = .{ .hit = .true } };
490
+ const actions: Actions = .{ .p2 = .{ .hit = .true } };
488
491
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p1, .None));
489
492
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.hit(actions.p2, .None));
490
493
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p2, .true));
@@ -492,7 +495,7 @@ test "Rolls.hit" {
492
495
  }
493
496
 
494
497
  test "Rolls.secondaryChance" {
495
- const actions = Actions{ .p1 = .{ .secondary_chance = .true } };
498
+ const actions: Actions = .{ .p1 = .{ .secondary_chance = .true } };
496
499
  try expectEqualSlices(
497
500
  Optional(bool),
498
501
  &.{ .false, .true },
@@ -503,7 +506,7 @@ test "Rolls.secondaryChance" {
503
506
  }
504
507
 
505
508
  test "Rolls.criticalHit" {
506
- const actions = Actions{ .p1 = .{ .critical_hit = .true } };
509
+ const actions: Actions = .{ .p1 = .{ .critical_hit = .true } };
507
510
  try expectEqualSlices(
508
511
  Optional(bool),
509
512
  &.{ .false, .true },
@@ -514,14 +517,14 @@ test "Rolls.criticalHit" {
514
517
  }
515
518
 
516
519
  test "Rolls.confused" {
517
- const actions = Actions{ .p2 = .{ .confused = .true } };
520
+ const actions: Actions = .{ .p2 = .{ .confused = .true } };
518
521
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, 1));
519
522
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.confused(actions.p2, 1));
520
523
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, 0));
521
524
  }
522
525
 
523
526
  test "Rolls.paralyzed" {
524
- const actions = Actions{ .p2 = .{ .paralyzed = .true } };
527
+ const actions: Actions = .{ .p2 = .{ .paralyzed = .true } };
525
528
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.paralyzed(actions.p1, .None));
526
529
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .None));
527
530
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .false));
@@ -529,102 +532,237 @@ test "Rolls.paralyzed" {
529
532
  }
530
533
 
531
534
  test "Rolls.duration" {
532
- const actions = Actions{ .p2 = .{ .duration = 3 } };
535
+ const actions: Actions = .{ .p2 = .{ .duration = 3 } };
533
536
  try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p1, .None));
534
537
  try expectEqualSlices(u1, &.{1}, Rolls.duration(actions.p2, .None));
535
538
  try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p2, .false));
536
539
  }
537
540
 
538
- const FIXME: Action = .{};
539
-
540
541
  test "Rolls.sleep" {
541
- try expectEqualSlices(u3, &.{0}, Rolls.sleep(.{ .durations = .{ .sleep = 0 } }, FIXME));
542
- try expectEqualSlices(u3, &.{0}, Rolls.sleep(.{ .durations = .{ .sleep = 7 } }, FIXME));
543
- try expectEqualSlices(u3, &.{ 0, 4 }, Rolls.sleep(.{ .durations = .{ .sleep = 4 } }, FIXME));
542
+ try expectEqualSlices(u3, &.{0}, Rolls.sleep(
543
+ .{ .durations = .{ .sleep = 0 } },
544
+ .{ .durations = .{ .sleep = 0 } },
545
+ ));
546
+ try expectEqualSlices(u3, &.{ 0, 1 }, Rolls.sleep(
547
+ .{ .durations = .{ .sleep = 1 } },
548
+ .{ .durations = .{ .sleep = 0 } },
549
+ ));
550
+ try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.sleep(
551
+ .{ .durations = .{ .sleep = 0 } },
552
+ .{ .durations = .{ .sleep = 1 } },
553
+ ));
554
+ try expectEqualSlices(u3, &.{1}, Rolls.sleep(
555
+ .{ .durations = .{ .sleep = 1 } },
556
+ .{ .durations = .{ .sleep = 1 } },
557
+ ));
558
+ try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.sleep(
559
+ .{ .durations = .{ .sleep = 0 } },
560
+ .{ .durations = .{ .sleep = 2 } },
561
+ ));
562
+ try expectEqualSlices(u3, &.{ 0, 7 }, Rolls.sleep(
563
+ .{ .durations = .{ .sleep = 7 } },
564
+ .{ .durations = .{ .sleep = 6 } },
565
+ ));
566
+ try expectEqualSlices(u3, &.{0}, Rolls.sleep(
567
+ .{ .durations = .{ .sleep = 0 } },
568
+ .{ .durations = .{ .sleep = 7 } },
569
+ ));
544
570
  }
545
571
 
546
572
  test "Rolls.disable" {
547
- try expectEqualSlices(u4, &.{0}, Rolls.disable(.{ .durations = .{ .disable = 0 } }, FIXME, 0));
548
- try expectEqualSlices(u4, &.{0}, Rolls.disable(.{ .durations = .{ .disable = 8 } }, FIXME, 0));
549
- try expectEqualSlices(u4, &.{0}, Rolls.disable(.{ .durations = .{ .disable = 4 } }, FIXME, 1));
550
- try expectEqualSlices(u4, &.{ 0, 4 }, Rolls.disable(.{
551
- .durations = .{ .disable = 4 },
552
- }, FIXME, 0));
573
+ try expectEqualSlices(u4, &.{0}, Rolls.disable(
574
+ .{ .durations = .{ .disable = 0 } },
575
+ .{ .durations = .{ .disable = 0 } },
576
+ 0,
577
+ ));
578
+ try expectEqualSlices(u4, &.{ 0, 1 }, Rolls.disable(
579
+ .{ .durations = .{ .disable = 1 } },
580
+ .{ .durations = .{ .disable = 0 } },
581
+ 0,
582
+ ));
583
+ try expectEqualSlices(u4, &.{ 0, 2 }, Rolls.disable(
584
+ .{ .durations = .{ .disable = 0 } },
585
+ .{ .durations = .{ .disable = 1 } },
586
+ 0,
587
+ ));
588
+ try expectEqualSlices(u4, &.{1}, Rolls.disable(
589
+ .{ .durations = .{ .disable = 1 } },
590
+ .{ .durations = .{ .disable = 1 } },
591
+ 0,
592
+ ));
593
+ try expectEqualSlices(u4, &.{ 0, 3 }, Rolls.disable(
594
+ .{ .durations = .{ .disable = 0 } },
595
+ .{ .durations = .{ .disable = 2 } },
596
+ 0,
597
+ ));
598
+ try expectEqualSlices(u4, &.{0}, Rolls.disable(
599
+ .{ .durations = .{ .disable = 0 } },
600
+ .{ .durations = .{ .disable = 2 } },
601
+ 1,
602
+ ));
603
+ try expectEqualSlices(u4, &.{ 0, 8 }, Rolls.disable(
604
+ .{ .durations = .{ .disable = 8 } },
605
+ .{ .durations = .{ .disable = 7 } },
606
+ 0,
607
+ ));
608
+ try expectEqualSlices(u4, &.{0}, Rolls.disable(
609
+ .{ .durations = .{ .disable = 0 } },
610
+ .{ .durations = .{ .disable = 8 } },
611
+ 0,
612
+ ));
553
613
  }
554
614
 
555
615
  test "Rolls.confusion" {
556
- try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{
557
- .durations = .{ .confusion = 0 },
558
- }, FIXME, 0));
559
- try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{
560
- .durations = .{ .confusion = 5 },
561
- }, FIXME, 0));
562
- try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{
563
- .durations = .{ .confusion = 3 },
564
- }, FIXME, 1));
565
- try expectEqualSlices(u3, &.{1}, Rolls.confusion(.{
566
- .durations = .{ .confusion = 1 },
567
- }, FIXME, 0));
568
- try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.confusion(.{
569
- .durations = .{ .confusion = 3 },
570
- }, FIXME, 0));
616
+ try expectEqualSlices(u3, &.{0}, Rolls.confusion(
617
+ .{ .durations = .{ .confusion = 0 } },
618
+ .{ .durations = .{ .confusion = 0 } },
619
+ 0,
620
+ ));
621
+ try expectEqualSlices(u3, &.{1}, Rolls.confusion(
622
+ .{ .durations = .{ .confusion = 1 } },
623
+ .{ .durations = .{ .confusion = 0 } },
624
+ 0,
625
+ ));
626
+ try expectEqualSlices(u3, &.{0}, Rolls.confusion(
627
+ .{ .durations = .{ .confusion = 0 } },
628
+ .{ .durations = .{ .confusion = 1 } },
629
+ 0,
630
+ ));
631
+ try expectEqualSlices(u3, &.{1}, Rolls.confusion(
632
+ .{ .durations = .{ .confusion = 1 } },
633
+ .{ .durations = .{ .confusion = 1 } },
634
+ 0,
635
+ ));
636
+ try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.confusion(
637
+ .{ .durations = .{ .confusion = 0 } },
638
+ .{ .durations = .{ .confusion = 2 } },
639
+ 0,
640
+ ));
641
+ try expectEqualSlices(u3, &.{0}, Rolls.confusion(
642
+ .{ .durations = .{ .confusion = 0 } },
643
+ .{ .durations = .{ .confusion = 2 } },
644
+ 1,
645
+ ));
646
+ try expectEqualSlices(u3, &.{ 0, 5 }, Rolls.confusion(
647
+ .{ .durations = .{ .confusion = 5 } },
648
+ .{ .durations = .{ .confusion = 4 } },
649
+ 0,
650
+ ));
651
+ try expectEqualSlices(u3, &.{0}, Rolls.confusion(
652
+ .{ .durations = .{ .confusion = 0 } },
653
+ .{ .durations = .{ .confusion = 5 } },
654
+ 0,
655
+ ));
571
656
  }
572
657
 
573
658
  test "Rolls.attacking" {
574
- try expectEqualSlices(u3, &.{0}, Rolls.attacking(.{
575
- .durations = .{ .attacking = 0 },
576
- }, FIXME, .false));
577
- try expectEqualSlices(u3, &.{0}, Rolls.attacking(.{
578
- .durations = .{ .attacking = 3 },
579
- }, FIXME, .false));
580
- try expectEqualSlices(u3, &.{0}, Rolls.attacking(.{
581
- .durations = .{ .attacking = 2 },
582
- }, FIXME, .true));
583
- try expectEqualSlices(u3, &.{1}, Rolls.attacking(.{
584
- .durations = .{ .attacking = 1 },
585
- }, FIXME, .false));
586
- try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.attacking(.{
587
- .durations = .{ .attacking = 2 },
588
- }, FIXME, .false));
659
+ try expectEqualSlices(u3, &.{0}, Rolls.attacking(
660
+ .{ .durations = .{ .attacking = 0 } },
661
+ .{ .durations = .{ .attacking = 0 } },
662
+ .false,
663
+ ));
664
+ try expectEqualSlices(u3, &.{1}, Rolls.attacking(
665
+ .{ .durations = .{ .attacking = 1 } },
666
+ .{ .durations = .{ .attacking = 0 } },
667
+ .false,
668
+ ));
669
+ try expectEqualSlices(u3, &.{0}, Rolls.attacking(
670
+ .{ .durations = .{ .attacking = 0 } },
671
+ .{ .durations = .{ .attacking = 1 } },
672
+ .false,
673
+ ));
674
+ try expectEqualSlices(u3, &.{1}, Rolls.attacking(
675
+ .{ .durations = .{ .attacking = 1 } },
676
+ .{ .durations = .{ .attacking = 1 } },
677
+ .false,
678
+ ));
679
+ try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.attacking(
680
+ .{ .durations = .{ .attacking = 0 } },
681
+ .{ .durations = .{ .attacking = 2 } },
682
+ .false,
683
+ ));
684
+ try expectEqualSlices(u3, &.{0}, Rolls.attacking(
685
+ .{ .durations = .{ .attacking = 0 } },
686
+ .{ .durations = .{ .attacking = 2 } },
687
+ .true,
688
+ ));
689
+ try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.attacking(
690
+ .{ .durations = .{ .attacking = 3 } },
691
+ .{ .durations = .{ .attacking = 2 } },
692
+ .false,
693
+ ));
694
+ try expectEqualSlices(u3, &.{0}, Rolls.attacking(
695
+ .{ .durations = .{ .attacking = 0 } },
696
+ .{ .durations = .{ .attacking = 3 } },
697
+ .false,
698
+ ));
589
699
  }
590
700
 
591
701
  test "Rolls.binding" {
592
- try expectEqualSlices(u3, &.{0}, Rolls.binding(.{
593
- .durations = .{ .binding = 0 },
594
- }, FIXME, .false));
595
- try expectEqualSlices(u3, &.{0}, Rolls.binding(.{
596
- .durations = .{ .binding = 4 },
597
- }, FIXME, .false));
598
- try expectEqualSlices(u3, &.{0}, Rolls.binding(.{
599
- .durations = .{ .binding = 2 },
600
- }, FIXME, .true));
601
- try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.binding(.{
602
- .durations = .{ .binding = 2 },
603
- }, FIXME, .false));
702
+ try expectEqualSlices(u3, &.{0}, Rolls.binding(
703
+ .{ .durations = .{ .binding = 0 } },
704
+ .{ .durations = .{ .binding = 0 } },
705
+ .false,
706
+ ));
707
+ try expectEqualSlices(u3, &.{ 0, 1 }, Rolls.binding(
708
+ .{ .durations = .{ .binding = 1 } },
709
+ .{ .durations = .{ .binding = 0 } },
710
+ .false,
711
+ ));
712
+ try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.binding(
713
+ .{ .durations = .{ .binding = 0 } },
714
+ .{ .durations = .{ .binding = 1 } },
715
+ .false,
716
+ ));
717
+ try expectEqualSlices(u3, &.{1}, Rolls.binding(
718
+ .{ .durations = .{ .binding = 1 } },
719
+ .{ .durations = .{ .binding = 1 } },
720
+ .false,
721
+ ));
722
+ try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.binding(
723
+ .{ .durations = .{ .binding = 0 } },
724
+ .{ .durations = .{ .binding = 2 } },
725
+ .false,
726
+ ));
727
+ try expectEqualSlices(u3, &.{0}, Rolls.binding(
728
+ .{ .durations = .{ .binding = 0 } },
729
+ .{ .durations = .{ .binding = 2 } },
730
+ .true,
731
+ ));
732
+ try expectEqualSlices(u3, &.{ 0, 4 }, Rolls.binding(
733
+ .{ .durations = .{ .binding = 4 } },
734
+ .{ .durations = .{ .binding = 3 } },
735
+ .false,
736
+ ));
737
+ try expectEqualSlices(u3, &.{0}, Rolls.binding(
738
+ .{ .durations = .{ .binding = 0 } },
739
+ .{ .durations = .{ .binding = 4 } },
740
+ .false,
741
+ ));
604
742
  }
605
743
 
606
744
  test "Rolls.moveSlot" {
607
- const actions = Actions{ .p2 = .{ .move_slot = 3 } };
745
+ const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
608
746
  try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p1, .None));
609
747
  try expectEqualSlices(u4, &.{ 1, 2, 3, 4 }, Rolls.moveSlot(actions.p2, .None));
610
748
  try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p2, .false));
611
749
  }
612
750
 
613
751
  test "Rolls.multiHit" {
614
- const actions = Actions{ .p2 = .{ .multi_hit = 3 } };
752
+ const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
615
753
  try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p1, .None));
616
754
  try expectEqualSlices(u4, &.{ 2, 3, 4, 5 }, Rolls.multiHit(actions.p2, .None));
617
755
  try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p2, .false));
618
756
  }
619
757
 
620
758
  test "Rolls.metronome" {
621
- const actions = Actions{ .p2 = .{ .metronome = .Surf } };
759
+ const actions: Actions = .{ .p2 = .{ .metronome = .Surf } };
622
760
  try expectEqualSlices(Move, &.{.None}, Rolls.metronome(actions.p1));
623
761
  try expectEqual(@as(Move, @enumFromInt(24)), Rolls.metronome(actions.p2)[23]);
624
762
  }
625
763
 
626
764
  test "Rolls.psywave" {
627
- const actions = Actions{ .p2 = .{ .psywave = 79 } };
765
+ const actions: Actions = .{ .p2 = .{ .psywave = 79 } };
628
766
  var side = Side.init(&[_]Pokemon{.{
629
767
  .species = .Bulbasaur,
630
768
  .level = 100,