@pkmn/engine 0.1.0-dev.6d566713 → 0.1.0-dev.6ebbd3bc

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 (76) hide show
  1. package/README.md +5 -4
  2. package/build/pkg/addon/browser.d.ts +3 -0
  3. package/build/pkg/addon/browser.js +44 -0
  4. package/build/pkg/addon/browser.js.map +1 -0
  5. package/build/pkg/addon/node.d.ts +3 -0
  6. package/build/pkg/addon/node.js +97 -0
  7. package/build/pkg/addon/node.js.map +1 -0
  8. package/build/pkg/addon/wasm.d.ts +2 -0
  9. package/build/pkg/addon/wasm.js +53 -0
  10. package/build/pkg/addon/wasm.js.map +1 -0
  11. package/build/pkg/addon.d.ts +25 -3
  12. package/build/pkg/addon.js +34 -65
  13. package/build/pkg/addon.js.map +1 -1
  14. package/build/pkg/data/layout.json +15 -2
  15. package/build/pkg/data/protocol.json +23 -9
  16. package/build/pkg/data.d.ts +23 -1
  17. package/build/pkg/data.js.map +1 -1
  18. package/build/pkg/gen1.d.ts +13 -17
  19. package/build/pkg/gen1.js +6 -10
  20. package/build/pkg/gen1.js.map +1 -1
  21. package/build/pkg/index.d.ts +37 -89
  22. package/build/pkg/index.js +6 -63
  23. package/build/pkg/index.js.map +1 -1
  24. package/build/pkg/protocol.d.ts +20 -1
  25. package/build/pkg/protocol.js +105 -103
  26. package/build/pkg/protocol.js.map +1 -1
  27. package/build/tools/debug.js +35 -69
  28. package/build/tools/debug.js.map +1 -1
  29. package/build/tools/display/debug.jsx +141 -0
  30. package/build/tools/display/debug.jsx.map +1 -0
  31. package/build/tools/display/index.js +142 -0
  32. package/build/tools/display/index.js.map +1 -0
  33. package/build/tools/display/ui.jsx +349 -0
  34. package/build/tools/display/ui.jsx.map +1 -0
  35. package/build/tools/display/util.js +18 -0
  36. package/build/tools/display/util.js.map +1 -0
  37. package/build.zig +150 -71
  38. package/package.json +51 -135
  39. package/src/bin/install-pkmn-engine +2 -2
  40. package/src/bin/pkmn-debug +1 -1
  41. package/src/lib/bindings/c.zig +254 -0
  42. package/src/lib/bindings/node.zig +121 -0
  43. package/src/lib/bindings/wasm.zig +59 -0
  44. package/src/lib/common/array.zig +1 -1
  45. package/src/lib/common/debug.zig +39 -8
  46. package/src/lib/common/optional.zig +1 -1
  47. package/src/lib/common/options.zig +4 -0
  48. package/src/lib/common/protocol.zig +4 -8
  49. package/src/lib/common/rational.zig +7 -5
  50. package/src/lib/common/rng.zig +1 -3
  51. package/src/lib/common/util.zig +1 -1
  52. package/src/lib/gen1/calc.zig +200 -51
  53. package/src/lib/gen1/chance.zig +40 -22
  54. package/src/lib/gen1/data/moves.zig +2 -4
  55. package/src/lib/gen1/data/species.zig +1 -3
  56. package/src/lib/gen1/data/types.zig +1 -2
  57. package/src/lib/gen1/data.zig +7 -11
  58. package/src/lib/gen1/helpers.zig +5 -18
  59. package/src/lib/gen1/mechanics.zig +107 -79
  60. package/src/lib/gen2/calc.zig +9 -17
  61. package/src/lib/gen2/data/items.zig +1 -3
  62. package/src/lib/gen2/data/moves.zig +2 -4
  63. package/src/lib/gen2/data/species.zig +1 -3
  64. package/src/lib/gen2/data/types.zig +1 -2
  65. package/src/lib/gen2/data.zig +11 -15
  66. package/src/lib/gen2/generated.zig +18 -23
  67. package/src/lib/gen2/helpers.zig +5 -12
  68. package/src/lib/gen2/mechanics.zig +41 -38
  69. package/src/lib/node.zig +3 -116
  70. package/src/lib/pkmn.zig +7 -2
  71. package/src/lib/wasm.zig +13 -9
  72. package/src/tools/display/dom.ts +96 -0
  73. package/src/tools/display/ui.css +354 -0
  74. package/build/test/display.js +0 -405
  75. package/build/test/display.js.map +0 -1
  76. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,41 +1,36 @@
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
-
14
+ const Action = chance.Action;
15
+ const Actions = chance.Actions;
16
16
  const assert = std.debug.assert;
17
-
17
+ const Chance = chance.Chance;
18
+ const Choice = common.Choice;
19
+ const Confusion = chance.Confusion;
20
+ const Duration = chance.Duration;
21
+ const Durations = chance.Durations;
22
+ const enabled = pkmn.options.calc;
18
23
  const expect = std.testing.expect;
19
24
  const expectEqual = std.testing.expectEqual;
20
25
  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
-
26
+ const isPointerTo = util.isPointerTo;
27
+ const Observation = chance.Observation;
28
28
  const Optional = optional.Optional;
29
-
30
- const Rational = rational.Rational;
31
-
29
+ const Player = common.Player;
32
30
  const PointerType = util.PointerType;
33
- const isPointerTo = util.isPointerTo;
34
-
35
- const Actions = chance.Actions;
36
- const Action = chance.Action;
37
- const Chance = chance.Chance;
38
- const Durations = chance.Durations;
31
+ const Rational = rational.Rational;
32
+ const Result = common.Result;
33
+ const Sleeps = chance.Sleeps;
39
34
 
40
35
  const tty = true; // DEBUG
41
36
  const summary = false; // DEBUG
@@ -115,6 +110,14 @@ pub const Calc = struct {
115
110
  }) val else null;
116
111
  }
117
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
+
118
121
  pub fn base(self: *Calc, player: Player, val: u16) void {
119
122
  if (!enabled) return;
120
123
 
@@ -148,6 +151,10 @@ const Null = struct {
148
151
  return null;
149
152
  }
150
153
 
154
+ pub fn confusion(self: Null, player: Player) void {
155
+ _ = .{ self, player };
156
+ }
157
+
151
158
  pub fn base(self: Null, player: Player, val: u16) void {
152
159
  _ = .{ self, player, val };
153
160
  }
@@ -175,7 +182,7 @@ pub const Options = struct {
175
182
  metronome: bool = false,
176
183
  };
177
184
 
178
- pub const MAX_FRONTIER = 19;
185
+ pub const MAX_FRONTIER = 81;
179
186
 
180
187
  pub fn transitions(
181
188
  battle: anytype,
@@ -194,9 +201,11 @@ pub fn transitions(
194
201
  var frontier = std.ArrayList(Actions).init(allocator);
195
202
  defer frontier.deinit();
196
203
 
204
+ const d = options.durations;
205
+
197
206
  var opts = pkmn.battle.options(
198
207
  protocol.NULL,
199
- Chance(Rational(u128)){ .probability = .{}, .durations = options.durations },
208
+ Chance(Rational(u128)){ .probability = .{}, .durations = d },
200
209
  Calc{},
201
210
  );
202
211
 
@@ -220,6 +229,7 @@ pub fn transitions(
220
229
  assert(frontier.items.len == 1);
221
230
  while (i < frontier.items.len) : (i += 1) {
222
231
  const f = frontier.items[i];
232
+ const saved = stats.saved;
223
233
 
224
234
  try debug(writer, f, .{
225
235
  .shape = true,
@@ -235,10 +245,20 @@ pub fn transitions(
235
245
  };
236
246
 
237
247
  for (Rolls.speedTie(f.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
238
- for (Rolls.confused(f.p1)) |p1_cfzd| { a.p1.confused = p1_cfzd;
239
- for (Rolls.confused(f.p2)) |p2_cfzd| { a.p2.confused = p2_cfzd;
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;
256
+ for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
257
+ for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
240
258
  for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
241
259
  for (Rolls.paralyzed(f.p2, p2_cfzd)) |p2_par| { a.p2.paralyzed = p2_par;
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;
242
262
  for (Rolls.hit(f.p1, p1_par)) |p1_hit| { a.p1.hit = p1_hit;
243
263
  for (Rolls.hit(f.p2, p2_par)) |p2_hit| { a.p2.hit = p2_hit;
244
264
  for (Rolls.psywave(f.p1, p1, p1_hit)) |p1_psywave| { a.p1.psywave = p1_psywave;
@@ -266,7 +286,7 @@ pub fn transitions(
266
286
 
267
287
  opts.calc.overrides = a;
268
288
  opts.calc.summaries = .{};
269
- opts.chance = .{ .probability = .{}, .durations = options.durations };
289
+ opts.chance = .{ .probability = .{}, .durations = d };
270
290
  const q = &opts.chance.probability;
271
291
 
272
292
  b = battle;
@@ -283,7 +303,7 @@ pub fn transitions(
283
303
  try Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
284
304
 
285
305
  if (opts.chance.actions.matches(f)) {
286
- if (!opts.chance.actions.eql(a)) {
306
+ if (!opts.chance.actions.relax().eql(a)) {
287
307
  if (!summary) {
288
308
  try debug(writer, opts.chance.actions, .{
289
309
  .p1_max = p1_max,
@@ -317,10 +337,13 @@ pub fn transitions(
317
337
  }
318
338
  }
319
339
  }
320
-
321
340
  if (p1_max != p1_min) try q.update(p1_max - p1_min + 1, 1);
322
341
  if (p2_max != p2_dmg.min) try q.update(p2_max - p2_dmg.min + 1, 1);
342
+
343
+ q.reduce();
323
344
  try p.add(q);
345
+ p.reduce();
346
+
324
347
  stats.saved += 1;
325
348
 
326
349
  if (p.q < p.p) {
@@ -328,7 +351,7 @@ pub fn transitions(
328
351
  return error.TestUnexpectedResult;
329
352
  }
330
353
  } else {
331
- if (!matches(opts.chance.actions, i, frontier.items)) {
354
+ if (!opts.chance.actions.matchesAny(frontier.items, i)) {
332
355
  try frontier.append(opts.chance.actions);
333
356
 
334
357
  try debug(writer, opts.chance.actions, .{
@@ -358,9 +381,11 @@ pub fn transitions(
358
381
  p2_dmg.min = p2_max;
359
382
  }
360
383
 
361
- }}}}}}}}}}}}}}}}}}
384
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}
362
385
 
363
- if (@TypeOf(writer) != @TypeOf(std.io.null_writer)) {
386
+ assert(stats.saved > saved);
387
+ const old = @hasDecl(std.fmt, "formatFloatDecimal");
388
+ if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
364
389
  p.reduce();
365
390
  try writer.print(
366
391
  " = {s} ({d:.2}%)\n\n",
@@ -496,15 +521,6 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
496
521
  };
497
522
  }
498
523
 
499
- fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
500
- for (frontier, 0..) |f, j| {
501
- // TODO: is skipping this redundant check worth it?
502
- if (i == j) continue;
503
- if (f.matches(actions)) return true;
504
- }
505
- return false;
506
- }
507
-
508
524
  fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
509
525
  const w = std.io.getStdErr().writer();
510
526
  w.print(fmt, v) catch return;
@@ -588,6 +604,108 @@ pub const Rolls = struct {
588
604
  return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
589
605
  }
590
606
 
607
+ const OBS_NONE = [_]Optional(Observation){.None};
608
+ const OBS_STARTED = [_]Optional(Observation){.started};
609
+ const OBS_CONTINUING = [_]Optional(Observation){.continuing};
610
+ const OBS_ENDED = [_]Optional(Observation){.ended};
611
+ const OBS_ALL = [_]Optional(Observation){ .started, .continuing, .ended };
612
+ const OBS = [_]Optional(Observation){ .continuing, .ended };
613
+
614
+ /// Returns a slice with a range of values for sleep given the `action` state and observed
615
+ /// `durations`.
616
+ pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
617
+ if (action.sleep == .None) return &OBS_NONE;
618
+ return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
619
+ &OBS_ALL
620
+ else switch (action.sleep) {
621
+ .started => &OBS_STARTED,
622
+ else => if (Sleeps.get(duration.sleeps, 0) >= 7) &OBS_ENDED else &OBS,
623
+ };
624
+ }
625
+
626
+ /// Returns a slice with a range of values for disable given the `action`, observed `durations`,
627
+ /// and the state of the `parent` (observation of the player's Pokémon sleep status).
628
+ pub fn disable(
629
+ action: Action,
630
+ duration: Duration,
631
+ parent: Optional(Observation),
632
+ ) []const Optional(Observation) {
633
+ if (action.disable == .None) return &OBS_NONE;
634
+ return if (duration.disable == 0 and action.speed_tie != .None)
635
+ &OBS_ALL
636
+ else switch (action.disable) {
637
+ .started => &OBS_STARTED,
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,
662
+ };
663
+ }
664
+
665
+ const CFZ_NONE = [_]Optional(Confusion){.None};
666
+ const CFZ_STARTED = [_]Optional(Confusion){.started};
667
+ const CFZ_OVERWRITTEN = [_]Optional(Confusion){ .started, .overwritten };
668
+ const CFZ_CONTINUING = [_]Optional(Confusion){.continuing};
669
+ const CFZ_ENDED = [_]Optional(Confusion){.ended};
670
+ const CFZ = [_]Optional(Confusion){ .continuing, .ended };
671
+
672
+ /// FIXME
673
+ pub fn confusion(
674
+ action: Action,
675
+ duration: Duration,
676
+ sibling: Optional(Observation),
677
+ parent: Optional(Observation),
678
+ ) []const Optional(Confusion) {
679
+ return switch (action.confusion) {
680
+ .None => &CFZ_NONE,
681
+ .started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
682
+ &CFZ_OVERWRITTEN
683
+ else
684
+ &CFZ_STARTED,
685
+ else => if (duration.confusion >= 5)
686
+ &CFZ_ENDED
687
+ else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
688
+ &CFZ_CONTINUING
689
+ else
690
+ &CFZ,
691
+ };
692
+ }
693
+
694
+ /// Returns a slice with a range of values for binding given the `action`, observed `durations`,
695
+ /// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
696
+ pub fn binding(
697
+ action: Action,
698
+ duration: Duration,
699
+ parent: Optional(bool),
700
+ ) []const Optional(Observation) {
701
+ if (parent == .true) return &OBS_NONE;
702
+ return switch (action.binding) {
703
+ .None => &OBS_NONE,
704
+ .started => &OBS_STARTED,
705
+ else => if (duration.binding >= 4) &OBS_ENDED else &OBS,
706
+ };
707
+ }
708
+
591
709
  const BOOL_NONE = [_]Optional(bool){.None};
592
710
  const BOOLS = [_]Optional(bool){ .false, .true };
593
711
 
@@ -601,7 +719,7 @@ pub const Rolls = struct {
601
719
  /// Returns a slice with the correct range of values for critical hits given the `action` state
602
720
  /// and the state of the `parent` (whether the player's Pokémon's move hit).
603
721
  pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
604
- if (parent == .false) return &BOOL_NONE;
722
+ if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
605
723
  return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
606
724
  }
607
725
 
@@ -636,9 +754,11 @@ pub const Rolls = struct {
636
754
  return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
637
755
  }
638
756
 
639
- /// Returns a slice with the correct range of values for confused.
640
- pub fn confused(action: Action) []const Optional(bool) {
641
- return if (action.confused == .None) &BOOL_NONE else &BOOLS;
757
+ /// Returns a slice with the correct range of values for confused given the `action` state and
758
+ /// the state of the `parent` (observation of the player's Pokémon confusion status).
759
+ pub fn confused(action: Action, parent: Optional(Confusion)) []const Optional(bool) {
760
+ const done = parent == .None or parent == .ended;
761
+ return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
642
762
  }
643
763
 
644
764
  /// Returns a slice with the correct range of values for paralysis given the `action` state
@@ -705,6 +825,26 @@ test "Rolls.speedTie" {
705
825
  try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
706
826
  }
707
827
 
828
+ test "Rolls.sleep" {
829
+ return error.SkipZigTest; // TODO
830
+ }
831
+
832
+ test "Rolls.disable" {
833
+ return error.SkipZigTest; // TODO
834
+ }
835
+
836
+ test "Rolls.confusion" {
837
+ return error.SkipZigTest; // TODO
838
+ }
839
+
840
+ test "Rolls.atacking" {
841
+ return error.SkipZigTest; // TODO
842
+ }
843
+
844
+ test "Rolls.binding" {
845
+ return error.SkipZigTest; // TODO
846
+ }
847
+
708
848
  test "Rolls.damage" {
709
849
  const actions: Actions = .{ .p2 = .{ .damage = 221 } };
710
850
  try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
@@ -748,14 +888,23 @@ test "Rolls.criticalHit" {
748
888
  &.{ .false, .true },
749
889
  Rolls.criticalHit(actions.p1, .None),
750
890
  );
751
- 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
+ );
752
896
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
753
897
  }
754
898
 
755
899
  test "Rolls.confused" {
756
900
  const actions: Actions = .{ .p2 = .{ .confused = .true } };
757
- try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
758
- try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.confused(actions.p2));
901
+ try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, .None));
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));
759
908
  }
760
909
 
761
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
@@ -251,7 +267,7 @@ pub const Durations = struct {
251
267
  }
252
268
  };
253
269
 
254
- const Sleeps = Array(6, u3);
270
+ pub const Sleeps = Array(6, u3);
255
271
 
256
272
  /// TODO
257
273
  pub const Duration = packed struct(u32) {
@@ -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
  }
@@ -656,6 +672,7 @@ pub fn Chance(comptime Rational: type) type {
656
672
  var d = self.durations.get(player);
657
673
  const n = d.attacking;
658
674
  if (obs == .ended) {
675
+ // "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
659
676
  assert(n >= 2 and n <= 3);
660
677
  if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
661
678
  d.attacking = 0;
@@ -682,6 +699,7 @@ pub fn Chance(comptime Rational: type) type {
682
699
  const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
683
700
 
684
701
  if (obs == .ended) {
702
+ // "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
685
703
  assert(n >= 1 and n <= 4);
686
704
  if (n != 4) try self.probability.update(p, q);
687
705
  d.binding = 0;
@@ -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,