@pkmn/engine 0.1.0-dev.cbdbb719 → 0.1.0-dev.cdc837af

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 (67) hide show
  1. package/build/pkg/addon/browser.d.ts +3 -0
  2. package/build/pkg/addon/browser.js +44 -0
  3. package/build/pkg/addon/browser.js.map +1 -0
  4. package/build/pkg/addon/node.d.ts +3 -0
  5. package/build/pkg/addon/node.js +97 -0
  6. package/build/pkg/addon/node.js.map +1 -0
  7. package/build/pkg/addon/wasm.d.ts +2 -0
  8. package/build/pkg/addon/wasm.js +21 -0
  9. package/build/pkg/addon/wasm.js.map +1 -0
  10. package/build/pkg/addon.d.ts +22 -0
  11. package/build/pkg/addon.js +29 -51
  12. package/build/pkg/addon.js.map +1 -1
  13. package/build/pkg/data.d.ts +23 -1
  14. package/build/pkg/data.js.map +1 -1
  15. package/build/pkg/gen1.js +1 -1
  16. package/build/pkg/gen1.js.map +1 -1
  17. package/build/pkg/index.d.ts +21 -8
  18. package/build/pkg/index.js +15 -10
  19. package/build/pkg/index.js.map +1 -1
  20. package/build/pkg/protocol.d.ts +20 -1
  21. package/build/pkg/protocol.js +105 -103
  22. package/build/pkg/protocol.js.map +1 -1
  23. package/build/tools/debug.js +35 -68
  24. package/build/tools/debug.js.map +1 -1
  25. package/build/tools/display/debug.jsx +141 -0
  26. package/build/tools/display/debug.jsx.map +1 -0
  27. package/build/tools/display/index.js +141 -0
  28. package/build/tools/display/index.js.map +1 -0
  29. package/build/tools/display/ui.jsx +349 -0
  30. package/build/tools/display/ui.jsx.map +1 -0
  31. package/build/tools/display/util.js +18 -0
  32. package/build/tools/display/util.js.map +1 -0
  33. package/build.zig +89 -70
  34. package/package.json +49 -32
  35. package/src/bin/install-pkmn-engine +4 -2
  36. package/src/bin/pkmn-debug +1 -1
  37. package/src/lib/common/debug.zig +39 -8
  38. package/src/lib/common/options.zig +2 -0
  39. package/src/lib/common/protocol.zig +4 -8
  40. package/src/lib/common/rational.zig +7 -5
  41. package/src/lib/common/rng.zig +1 -3
  42. package/src/lib/gen1/calc.zig +200 -53
  43. package/src/lib/gen1/chance.zig +40 -22
  44. package/src/lib/gen1/data/moves.zig +2 -4
  45. package/src/lib/gen1/data/species.zig +1 -3
  46. package/src/lib/gen1/data/types.zig +1 -2
  47. package/src/lib/gen1/data.zig +9 -13
  48. package/src/lib/gen1/helpers.zig +5 -21
  49. package/src/lib/gen1/mechanics.zig +55 -61
  50. package/src/lib/gen2/calc.zig +10 -18
  51. package/src/lib/gen2/chance.zig +8 -13
  52. package/src/lib/gen2/data/items.zig +1 -3
  53. package/src/lib/gen2/data/moves.zig +2 -4
  54. package/src/lib/gen2/data/species.zig +1 -3
  55. package/src/lib/gen2/data/types.zig +1 -2
  56. package/src/lib/gen2/data.zig +11 -15
  57. package/src/lib/gen2/generated.zig +18 -23
  58. package/src/lib/gen2/helpers.zig +5 -16
  59. package/src/lib/gen2/mechanics.zig +22 -36
  60. package/src/lib/node.zig +2 -4
  61. package/src/lib/pkmn.zig +4 -0
  62. package/src/lib/wasm.zig +1 -2
  63. package/src/tools/display/dom.ts +96 -0
  64. package/src/tools/display/ui.css +354 -0
  65. package/build/test/display.js +0 -405
  66. package/build/test/display.js.map +0 -1
  67. 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;
@@ -577,8 +593,6 @@ fn format(writer: anytype, actions: Actions, p1: u9, p2: u9) !void {
577
593
  try writer.writeAll(output[0..len]);
578
594
  }
579
595
 
580
- const NONE = @intFromEnum(Optional(bool).None);
581
-
582
596
  /// Helper functions that efficiently return valid ranges for various RNG events based on the
583
597
  /// state of an `Action` and other events to be used to construct a "transitions" function.
584
598
  pub const Rolls = struct {
@@ -590,6 +604,108 @@ pub const Rolls = struct {
590
604
  return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
591
605
  }
592
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
+
593
709
  const BOOL_NONE = [_]Optional(bool){.None};
594
710
  const BOOLS = [_]Optional(bool){ .false, .true };
595
711
 
@@ -603,7 +719,7 @@ pub const Rolls = struct {
603
719
  /// Returns a slice with the correct range of values for critical hits given the `action` state
604
720
  /// and the state of the `parent` (whether the player's Pokémon's move hit).
605
721
  pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
606
- if (parent == .false) return &BOOL_NONE;
722
+ if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
607
723
  return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
608
724
  }
609
725
 
@@ -638,9 +754,11 @@ pub const Rolls = struct {
638
754
  return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
639
755
  }
640
756
 
641
- /// Returns a slice with the correct range of values for confused.
642
- pub fn confused(action: Action) []const Optional(bool) {
643
- 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;
644
762
  }
645
763
 
646
764
  /// Returns a slice with the correct range of values for paralysis given the `action` state
@@ -707,6 +825,26 @@ test "Rolls.speedTie" {
707
825
  try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
708
826
  }
709
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
+
710
848
  test "Rolls.damage" {
711
849
  const actions: Actions = .{ .p2 = .{ .damage = 221 } };
712
850
  try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
@@ -750,14 +888,23 @@ test "Rolls.criticalHit" {
750
888
  &.{ .false, .true },
751
889
  Rolls.criticalHit(actions.p1, .None),
752
890
  );
753
- 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
+ );
754
896
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
755
897
  }
756
898
 
757
899
  test "Rolls.confused" {
758
900
  const actions: Actions = .{ .p2 = .{ .confused = .true } };
759
- try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
760
- 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));
761
908
  }
762
909
 
763
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;
@@ -256,7 +252,7 @@ pub const Status = enum(u8) {
256
252
 
257
253
  pub const TOX = 0b10001000;
258
254
 
259
- const SLP = 0b111;
255
+ const SLEEP = 0b111;
260
256
 
261
257
  /// Whether or not the status `num` is the same as `status`.
262
258
  pub fn is(num: u8, status: Status) bool {
@@ -284,7 +280,7 @@ pub const Status = enum(u8) {
284
280
 
285
281
  /// Returns the duration of a sleep status.
286
282
  pub fn duration(num: u8) u3 {
287
- return @intCast(num & SLP);
283
+ return @intCast(num & SLEEP);
288
284
  }
289
285
 
290
286
  /// Returns whether `num` reflects any status.
@@ -1,30 +1,21 @@
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
- const Player = common.Player;
16
-
17
- const showdown = options.showdown;
18
- const chance = options.chance;
19
-
20
- const PSRNG = rng.PSRNG;
21
-
22
12
  const DVs = data.DVs;
23
13
  const Move = data.Move;
24
14
  const MoveSlot = data.MoveSlot;
15
+ const PSRNG = rng.PSRNG;
16
+ const showdown = options.showdown;
25
17
  const Species = data.Species;
26
18
  const Stats = data.Stats;
27
- const Status = data.Status;
28
19
 
29
20
  const endian = builtin.cpu.arch.endian();
30
21
 
@@ -269,14 +260,7 @@ fn blocked(m: Move) bool {
269
260
  fn durations(m: Move) bool {
270
261
  // Any moves that have effects that result in duration tracking with -Dchance
271
262
  return switch (Move.get(m).effect) {
272
- .Confusion,
273
- .Bide,
274
- .Sleep,
275
- .Rage,
276
- .Binding,
277
- .Thrashing,
278
- .ConfusionChance,
279
- .Disable,
263
+ .Haze,
280
264
  .Metronome,
281
265
  => true,
282
266
  else => false,