@pkmn/engine 0.1.0-dev.53787794 → 0.1.0-dev.54a1aa85

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 (57) hide show
  1. package/README.md +5 -4
  2. package/build/pkg/addon/browser.d.ts +1 -1
  3. package/build/pkg/addon/browser.js +2 -1
  4. package/build/pkg/addon/browser.js.map +1 -1
  5. package/build/pkg/addon/node.js +2 -1
  6. package/build/pkg/addon/node.js.map +1 -1
  7. package/build/pkg/addon/wasm.d.ts +2 -0
  8. package/build/pkg/addon/wasm.js +44 -0
  9. package/build/pkg/addon/wasm.js.map +1 -0
  10. package/build/pkg/addon.d.ts +23 -3
  11. package/build/pkg/addon.js +8 -17
  12. package/build/pkg/addon.js.map +1 -1
  13. package/build/pkg/data/layout.json +15 -2
  14. package/build/pkg/data/protocol.json +23 -9
  15. package/build/pkg/gen1.d.ts +13 -17
  16. package/build/pkg/gen1.js +6 -10
  17. package/build/pkg/gen1.js.map +1 -1
  18. package/build/pkg/index.d.ts +35 -100
  19. package/build/pkg/index.js +6 -68
  20. package/build/pkg/index.js.map +1 -1
  21. package/build/tools/debug.js +2 -2
  22. package/build/tools/debug.js.map +1 -1
  23. package/build/tools/display/debug.jsx +21 -8
  24. package/build/tools/display/debug.jsx.map +1 -1
  25. package/build/tools/display/index.js +17 -8
  26. package/build/tools/display/index.js.map +1 -1
  27. package/build/tools/display/ui.jsx +7 -7
  28. package/build/tools/display/ui.jsx.map +1 -1
  29. package/build.zig +112 -68
  30. package/package.json +24 -16
  31. package/src/bin/install-pkmn-engine +1 -1
  32. package/src/lib/common/debug.zig +11 -6
  33. package/src/lib/common/options.zig +2 -0
  34. package/src/lib/common/protocol.zig +4 -8
  35. package/src/lib/common/rational.zig +7 -5
  36. package/src/lib/common/rng.zig +1 -3
  37. package/src/lib/gen1/calc.zig +96 -98
  38. package/src/lib/gen1/chance.zig +37 -21
  39. package/src/lib/gen1/data/moves.zig +2 -4
  40. package/src/lib/gen1/data/species.zig +1 -3
  41. package/src/lib/gen1/data/types.zig +1 -2
  42. package/src/lib/gen1/data.zig +7 -11
  43. package/src/lib/gen1/helpers.zig +5 -18
  44. package/src/lib/gen1/mechanics.zig +108 -90
  45. package/src/lib/gen2/calc.zig +9 -17
  46. package/src/lib/gen2/data/items.zig +1 -3
  47. package/src/lib/gen2/data/moves.zig +2 -4
  48. package/src/lib/gen2/data/species.zig +1 -3
  49. package/src/lib/gen2/data/types.zig +1 -2
  50. package/src/lib/gen2/data.zig +11 -15
  51. package/src/lib/gen2/generated.zig +18 -23
  52. package/src/lib/gen2/helpers.zig +5 -12
  53. package/src/lib/gen2/mechanics.zig +41 -38
  54. package/src/lib/node.zig +2 -4
  55. package/src/lib/pkmn.zig +4 -0
  56. package/src/lib/wasm.zig +36 -1
  57. package/src/tools/display/ui.css +12 -9
@@ -1,44 +1,35 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("../pkmn.zig");
1
+ const DEBUG = @import("../common/debug.zig").print;
4
2
 
3
+ const chance = @import("chance.zig");
5
4
  const common = @import("../common/data.zig");
6
- const DEBUG = @import("../common/debug.zig").print;
5
+ const data = @import("data.zig");
6
+ const helpers = @import("helpers.zig");
7
7
  const optional = @import("../common/optional.zig");
8
+ const pkmn = @import("../pkmn.zig");
8
9
  const protocol = @import("../common/protocol.zig");
9
10
  const rational = @import("../common/rational.zig");
11
+ const std = @import("std");
10
12
  const util = @import("../common/util.zig");
11
13
 
12
- const chance = @import("chance.zig");
13
- const data = @import("data.zig");
14
- const helpers = @import("helpers.zig");
15
-
16
- const assert = std.debug.assert;
17
-
18
- const expect = std.testing.expect;
19
- const expectEqual = std.testing.expectEqual;
20
- const expectEqualSlices = std.testing.expectEqualSlices;
21
-
22
- const enabled = pkmn.options.calc;
23
-
24
- const Player = common.Player;
25
- const Result = common.Result;
26
- const Choice = common.Choice;
27
-
28
- const Optional = optional.Optional;
29
-
30
- const Rational = rational.Rational;
31
-
32
- const PointerType = util.PointerType;
33
- const isPointerTo = util.isPointerTo;
34
-
35
- const Actions = chance.Actions;
36
14
  const Action = chance.Action;
15
+ const Actions = chance.Actions;
16
+ const assert = std.debug.assert;
37
17
  const Chance = chance.Chance;
18
+ const Choice = common.Choice;
38
19
  const Confusion = chance.Confusion;
39
20
  const Duration = chance.Duration;
40
21
  const Durations = chance.Durations;
22
+ const enabled = pkmn.options.calc;
23
+ const expect = std.testing.expect;
24
+ const expectEqual = std.testing.expectEqual;
25
+ const expectEqualSlices = std.testing.expectEqualSlices;
26
+ const isPointerTo = util.isPointerTo;
41
27
  const Observation = chance.Observation;
28
+ const Optional = optional.Optional;
29
+ const Player = common.Player;
30
+ const PointerType = util.PointerType;
31
+ const Rational = rational.Rational;
32
+ const Result = common.Result;
42
33
  const Sleeps = chance.Sleeps;
43
34
 
44
35
  const tty = true; // DEBUG
@@ -119,6 +110,14 @@ pub const Calc = struct {
119
110
  }) val else null;
120
111
  }
121
112
 
113
+ pub fn confusion(self: *Calc, player: Player) void {
114
+ if (!enabled) return;
115
+
116
+ if (self.overrides.get(player).confusion == .started) {
117
+ self.overrides.get(player).confusion = .None;
118
+ }
119
+ }
120
+
122
121
  pub fn base(self: *Calc, player: Player, val: u16) void {
123
122
  if (!enabled) return;
124
123
 
@@ -152,6 +151,10 @@ const Null = struct {
152
151
  return null;
153
152
  }
154
153
 
154
+ pub fn confusion(self: Null, player: Player) void {
155
+ _ = .{ self, player };
156
+ }
157
+
155
158
  pub fn base(self: Null, player: Player, val: u16) void {
156
159
  _ = .{ self, player, val };
157
160
  }
@@ -179,7 +182,7 @@ pub const Options = struct {
179
182
  metronome: bool = false,
180
183
  };
181
184
 
182
- pub const MAX_FRONTIER = 49;
185
+ pub const MAX_FRONTIER = 81;
183
186
 
184
187
  pub fn transitions(
185
188
  battle: anytype,
@@ -198,11 +201,11 @@ pub fn transitions(
198
201
  var frontier = std.ArrayList(Actions).init(allocator);
199
202
  defer frontier.deinit();
200
203
 
201
- const durations = options.durations;
204
+ const d = options.durations;
202
205
 
203
206
  var opts = pkmn.battle.options(
204
207
  protocol.NULL,
205
- Chance(Rational(u128)){ .probability = .{}, .durations = durations },
208
+ Chance(Rational(u128)){ .probability = .{}, .durations = d },
206
209
  Calc{},
207
210
  );
208
211
 
@@ -242,20 +245,20 @@ pub fn transitions(
242
245
  };
243
246
 
244
247
  for (Rolls.speedTie(f.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
245
- for (Rolls.sleep(f.p1, durations.p1)) |p1_slp| { a.p1.sleep = p1_slp;
246
- for (Rolls.sleep(f.p2, durations.p2)) |p2_slp| { a.p2.sleep = p2_slp;
247
- for (Rolls.disable(f.p1, durations.p1, p1_slp)) |p1_dis| { a.p1.disable = p1_dis;
248
- for (Rolls.disable(f.p2, durations.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
249
- for (Rolls.confusion(f.p1, durations.p1, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
250
- for (Rolls.confusion(f.p2, durations.p2, p1_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
248
+ for (Rolls.sleep(f.p1, d.p1)) |p1_slp| { a.p1.sleep = p1_slp;
249
+ for (Rolls.sleep(f.p2, d.p2)) |p2_slp| { a.p2.sleep = p2_slp;
250
+ for (Rolls.disable(f.p1, d.p1, p1_slp)) |p1_dis| { a.p1.disable = p1_dis;
251
+ for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
252
+ for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
253
+ for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
254
+ for (Rolls.confusion(f.p1, d.p1, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
255
+ for (Rolls.confusion(f.p2, d.p2, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
251
256
  for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
252
257
  for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
253
258
  for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
254
259
  for (Rolls.paralyzed(f.p2, p2_cfzd)) |p2_par| { a.p2.paralyzed = p2_par;
255
- for (Rolls.attacking(f.p1, durations.p1, p1_par)) |p1_atk| { a.p1.attacking = p1_atk;
256
- for (Rolls.attacking(f.p2, durations.p2, p2_par)) |p2_atk| { a.p2.attacking = p2_atk;
257
- for (Rolls.binding(f.p1, durations.p1, p1_par)) |p1_bind| { a.p1.binding = p1_bind;
258
- for (Rolls.binding(f.p2, durations.p2, p2_par)) |p2_bind| { a.p2.binding = p2_bind;
260
+ for (Rolls.binding(f.p1, d.p1, p1_par)) |p1_bind| { a.p1.binding = p1_bind;
261
+ for (Rolls.binding(f.p2, d.p2, p2_par)) |p2_bind| { a.p2.binding = p2_bind;
259
262
  for (Rolls.hit(f.p1, p1_par)) |p1_hit| { a.p1.hit = p1_hit;
260
263
  for (Rolls.hit(f.p2, p2_par)) |p2_hit| { a.p2.hit = p2_hit;
261
264
  for (Rolls.psywave(f.p1, p1, p1_hit)) |p1_psywave| { a.p1.psywave = p1_psywave;
@@ -283,7 +286,7 @@ pub fn transitions(
283
286
 
284
287
  opts.calc.overrides = a;
285
288
  opts.calc.summaries = .{};
286
- opts.chance = .{ .probability = .{}, .durations = durations };
289
+ opts.chance = .{ .probability = .{}, .durations = d };
287
290
  const q = &opts.chance.probability;
288
291
 
289
292
  b = battle;
@@ -300,7 +303,7 @@ pub fn transitions(
300
303
  try Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
301
304
 
302
305
  if (opts.chance.actions.matches(f)) {
303
- if (!map(opts.chance.actions).eql(a)) {
306
+ if (!opts.chance.actions.relax().eql(a)) {
304
307
  if (!summary) {
305
308
  try debug(writer, opts.chance.actions, .{
306
309
  .p1_max = p1_max,
@@ -334,10 +337,13 @@ pub fn transitions(
334
337
  }
335
338
  }
336
339
  }
337
-
338
340
  if (p1_max != p1_min) try q.update(p1_max - p1_min + 1, 1);
339
341
  if (p2_max != p2_dmg.min) try q.update(p2_max - p2_dmg.min + 1, 1);
342
+
343
+ q.reduce();
340
344
  try p.add(q);
345
+ p.reduce();
346
+
341
347
  stats.saved += 1;
342
348
 
343
349
  if (p.q < p.p) {
@@ -345,7 +351,7 @@ pub fn transitions(
345
351
  return error.TestUnexpectedResult;
346
352
  }
347
353
  } else {
348
- if (!matches(opts.chance.actions, i, frontier.items)) {
354
+ if (!opts.chance.actions.matchesAny(frontier.items, i)) {
349
355
  try frontier.append(opts.chance.actions);
350
356
 
351
357
  try debug(writer, opts.chance.actions, .{
@@ -515,23 +521,6 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
515
521
  };
516
522
  }
517
523
 
518
- fn map(actions: Actions) Actions {
519
- if (!pkmn.options.overwrite) return actions;
520
- var a = actions;
521
- if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
522
- if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
523
- return a;
524
- }
525
-
526
- fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
527
- for (frontier, 0..) |f, j| {
528
- // TODO: is skipping this redundant check worth it?
529
- if (i == j) continue;
530
- if (f.matches(actions)) return true;
531
- }
532
- return false;
533
- }
534
-
535
524
  fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
536
525
  const w = std.io.getStdErr().writer();
537
526
  w.print(fmt, v) catch return;
@@ -641,65 +630,67 @@ pub const Rolls = struct {
641
630
  duration: Duration,
642
631
  parent: Optional(Observation),
643
632
  ) []const Optional(Observation) {
644
- // if (parent == .started or parent == .continuing) return &OBS_NONE;
645
- _ = parent; // FIXME
646
633
  if (action.disable == .None) return &OBS_NONE;
647
634
  return if (duration.disable == 0 and action.speed_tie != .None)
648
635
  &OBS_ALL
649
636
  else switch (action.disable) {
650
637
  .started => &OBS_STARTED,
651
- else => if (duration.disable >= 8) &OBS_ENDED else &OBS,
638
+ else => if (duration.disable >= 8)
639
+ &OBS_ENDED
640
+ else if (parent != .None and parent != .started)
641
+ &OBS_CONTINUING
642
+ else
643
+ &OBS,
644
+ };
645
+ }
646
+
647
+ /// FIXME
648
+ pub fn attacking(
649
+ action: Action,
650
+ duration: Duration,
651
+ parent: Optional(Observation),
652
+ ) []const Optional(Observation) {
653
+ return switch (action.attacking) {
654
+ .None => &OBS_NONE,
655
+ .started => &OBS_STARTED,
656
+ else => if (duration.attacking >= 3)
657
+ &OBS_ENDED
658
+ else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
659
+ &OBS_CONTINUING
660
+ else
661
+ &OBS,
652
662
  };
653
663
  }
654
664
 
655
665
  const CFZ_NONE = [_]Optional(Confusion){.None};
656
666
  const CFZ_STARTED = [_]Optional(Confusion){.started};
667
+ const CFZ_OVERWRITTEN = [_]Optional(Confusion){ .started, .overwritten };
657
668
  const CFZ_CONTINUING = [_]Optional(Confusion){.continuing};
658
669
  const CFZ_ENDED = [_]Optional(Confusion){.ended};
659
670
  const CFZ = [_]Optional(Confusion){ .continuing, .ended };
660
671
 
661
- /// Returns a slice with a range of values for confusion given the `action`, observed `durations`,
662
- /// and the state of the `parent` (observation of the player's Pokémon sleep status).
672
+ /// FIXME
663
673
  pub fn confusion(
664
674
  action: Action,
665
675
  duration: Duration,
676
+ sibling: Optional(Observation),
666
677
  parent: Optional(Observation),
667
678
  ) []const Optional(Confusion) {
668
- // if (parent == .started or parent == .continuing) return &CFZ_NONE;
669
- _ = parent; // FIXME
670
679
  return switch (action.confusion) {
671
680
  .None => &CFZ_NONE,
672
- .started => &CFZ_STARTED,
681
+ .started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
682
+ &CFZ_OVERWRITTEN
683
+ else
684
+ &CFZ_STARTED,
673
685
  else => if (duration.confusion >= 5)
674
686
  &CFZ_ENDED
675
- else if (duration.confusion < 2)
687
+ else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
676
688
  &CFZ_CONTINUING
677
689
  else
678
690
  &CFZ,
679
691
  };
680
692
  }
681
693
 
682
- /// Returns a slice with a range of values for attacking given the `action`, observed
683
- /// `durations`, and the state of the `parent` (whether the player's Pokémon was fully
684
- /// paralyzed).
685
- pub fn attacking(
686
- action: Action,
687
- duration: Duration,
688
- parent: Optional(bool),
689
- ) []const Optional(Observation) {
690
- if (parent == .true) return &OBS_NONE;
691
- return switch (action.attacking) {
692
- .None => &OBS_NONE,
693
- .started => &OBS_STARTED,
694
- else => if (duration.attacking >= 3)
695
- &OBS_ENDED
696
- else if (duration.attacking < 2)
697
- &OBS_CONTINUING
698
- else
699
- &OBS,
700
- };
701
- }
702
-
703
694
  /// Returns a slice with a range of values for binding given the `action`, observed `durations`,
704
695
  /// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
705
696
  pub fn binding(
@@ -728,7 +719,7 @@ pub const Rolls = struct {
728
719
  /// Returns a slice with the correct range of values for critical hits given the `action` state
729
720
  /// and the state of the `parent` (whether the player's Pokémon's move hit).
730
721
  pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
731
- if (parent == .false) return &BOOL_NONE;
722
+ if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
732
723
  return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
733
724
  }
734
725
 
@@ -766,8 +757,7 @@ pub const Rolls = struct {
766
757
  /// Returns a slice with the correct range of values for confused given the `action` state and
767
758
  /// the state of the `parent` (observation of the player's Pokémon confusion status).
768
759
  pub fn confused(action: Action, parent: Optional(Confusion)) []const Optional(bool) {
769
- const done = false; // parent == .None or parent == .ended;
770
- _ = parent; // FIXME
760
+ const done = parent == .None or parent == .ended;
771
761
  return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
772
762
  }
773
763
 
@@ -898,15 +888,23 @@ test "Rolls.criticalHit" {
898
888
  &.{ .false, .true },
899
889
  Rolls.criticalHit(actions.p1, .None),
900
890
  );
901
- try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p1, .false));
891
+ try expectEqualSlices(
892
+ Optional(bool),
893
+ if (pkmn.options.showdown) &.{.None} else &.{ .false, .true },
894
+ Rolls.criticalHit(actions.p1, .false),
895
+ );
902
896
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
903
897
  }
904
898
 
905
899
  test "Rolls.confused" {
906
900
  const actions: Actions = .{ .p2 = .{ .confused = .true } };
907
901
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, .None));
908
- try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.confused(actions.p2, .continuing));
909
- // FIXME try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, .ended));
902
+ try expectEqualSlices(
903
+ Optional(bool),
904
+ &.{ .false, .true },
905
+ Rolls.confused(actions.p2, .continuing),
906
+ );
907
+ try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, .ended));
910
908
  }
911
909
 
912
910
  test "Rolls.paralyzed" {
@@ -1,30 +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;
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;
28
25
 
29
26
  const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
30
27
  const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
@@ -75,6 +72,25 @@ pub const Actions = extern struct {
75
72
  return true;
76
73
  }
77
74
 
75
+ /// TODO
76
+ pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
77
+ for (bs, 0..) |b, j| {
78
+ // TODO: is skipping this redundant check worth it?
79
+ if (i == j) continue;
80
+ if (b.matches(a)) return true;
81
+ }
82
+ return false;
83
+ }
84
+
85
+ /// TODO
86
+ pub fn relax(actions: Actions) Actions {
87
+ if (!options.overwrite) return actions;
88
+ var a = actions;
89
+ if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
90
+ if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
91
+ return a;
92
+ }
93
+
78
94
  pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
79
95
  try writer.writeAll("<P1 = ");
80
96
  try self.p1.fmt(writer, shape);
@@ -224,7 +240,7 @@ pub const Action = packed struct(u64) {
224
240
  };
225
241
 
226
242
  /// TODO
227
- pub const Durations = struct {
243
+ pub const Durations = extern struct {
228
244
  /// TODO
229
245
  p1: Duration = .{},
230
246
  /// TODO
@@ -375,7 +391,7 @@ pub fn Chance(comptime Rational: type) type {
375
391
 
376
392
  // If the move actually lands we can commit any past critical hit / damage rolls. Note
377
393
  // 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
394
+ // "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
379
395
  // damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
380
396
  // passed to the critical hit helper).
381
397
  //
@@ -391,7 +407,7 @@ pub fn Chance(comptime Rational: type) type {
391
407
  action.critical_hit = if (self.pending.crit) .true else .false;
392
408
  } else assert(!self.pending.glitch);
393
409
 
394
- if (self.pending.damage_roll > 0) {
410
+ if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
395
411
  try self.probability.update(1, 39);
396
412
  action.damage = self.pending.damage_roll;
397
413
  }
@@ -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;
@@ -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;
@@ -1,25 +1,19 @@
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
 
@@ -266,14 +260,7 @@ fn blocked(m: Move) bool {
266
260
  fn durations(m: Move) bool {
267
261
  // Any moves that have effects that result in duration tracking with -Dchance
268
262
  return switch (Move.get(m).effect) {
269
- .Confusion,
270
- .Bide,
271
- .Sleep,
272
- .Rage,
273
- .Binding,
274
- .Thrashing,
275
- .ConfusionChance,
276
- .Disable,
263
+ .Haze,
277
264
  .Metronome,
278
265
  => true,
279
266
  else => false,