@pkmn/engine 0.1.0-dev.46307385 → 0.1.0-dev.49f6bf26

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 (48) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +37 -14
  3. package/build/pkg/addon.js.map +1 -1
  4. package/build/pkg/data/ids.json +12 -12
  5. package/build/pkg/data/layout.json +2 -2
  6. package/build/pkg/data/protocol.json +69 -12
  7. package/build/pkg/data.d.ts +3 -2
  8. package/build/pkg/data.js.map +1 -1
  9. package/build/pkg/gen1.js +2 -2
  10. package/build/pkg/gen1.js.map +1 -1
  11. package/build/pkg/index.d.ts +2 -1
  12. package/build/pkg/index.js.map +1 -1
  13. package/build/pkg/protocol.js +57 -38
  14. package/build/pkg/protocol.js.map +1 -1
  15. package/build/test/display.js.map +1 -1
  16. package/build/tools/debug.js +3 -1
  17. package/build/tools/debug.js.map +1 -1
  18. package/build.zig +84 -64
  19. package/package.json +43 -22
  20. package/src/bin/install-pkmn-engine +14 -14
  21. package/src/lib/common/data.zig +12 -24
  22. package/src/lib/common/optional.zig +9 -2
  23. package/src/lib/common/protocol.zig +792 -319
  24. package/src/lib/common/rational.zig +12 -8
  25. package/src/lib/common/rng.zig +20 -11
  26. package/src/lib/gen1/README.md +31 -29
  27. package/src/lib/gen1/calc.zig +9 -10
  28. package/src/lib/gen1/chance.zig +74 -58
  29. package/src/lib/gen1/data/moves.zig +192 -180
  30. package/src/lib/gen1/data/species.zig +2 -2
  31. package/src/lib/gen1/data/types.zig +7 -7
  32. package/src/lib/gen1/data.zig +47 -59
  33. package/src/lib/gen1/helpers.zig +33 -48
  34. package/src/lib/gen1/mechanics.zig +317 -368
  35. package/src/lib/gen2/README.md +93 -72
  36. package/src/lib/gen2/calc.zig +143 -0
  37. package/src/lib/gen2/chance.zig +1314 -0
  38. package/src/lib/gen2/data/items.zig +12 -18
  39. package/src/lib/gen2/data/moves.zig +663 -569
  40. package/src/lib/gen2/data/species.zig +1 -1
  41. package/src/lib/gen2/data/types.zig +134 -31
  42. package/src/lib/gen2/data.zig +106 -79
  43. package/src/lib/gen2/generated.zig +806 -0
  44. package/src/lib/gen2/helpers.zig +8 -12
  45. package/src/lib/gen2/mechanics.zig +3502 -9
  46. package/src/lib/{binding/node.zig → node.zig} +10 -13
  47. package/src/lib/pkmn.zig +26 -3
  48. package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
@@ -44,7 +44,7 @@ pub fn Rational(comptime T: type) type {
44
44
  /// Both p and q must be >= 1, and if computable at comptime must have no common factors.
45
45
  pub fn update(r: *Self, p: anytype, q: anytype) Error!void {
46
46
  // FIXME
47
- // std.debug.print("({d}/{d}) * ", .{p, q});
47
+ // std.debug.print("({d}/{d}) * ", .{ p, q });
48
48
  assert(p >= 1);
49
49
  assert(q >= 1);
50
50
 
@@ -185,16 +185,16 @@ pub fn Rational(comptime T: type) type {
185
185
  try writer.print("{d}/{d}", .{ self.p, self.q });
186
186
  }
187
187
 
188
- inline fn multiplication(r: *Self, p: anytype, q: anytype) !void {
188
+ fn multiplication(r: *Self, p: anytype, q: anytype) !void {
189
189
  r.p = try std.math.mul(T, r.p, p);
190
190
  r.q = try std.math.mul(T, r.q, q);
191
191
  }
192
192
 
193
- inline fn addition(r: *Self, p: anytype, q: anytype) !void {
193
+ fn addition(r: *Self, p: anytype, q: anytype) !void {
194
194
  // (a/b) + (c/d) = (ad+bc)/(bd)
195
- var d = try std.math.mul(T, r.q, q);
196
- var n1 = try std.math.mul(T, r.p, q);
197
- var n2 = try std.math.mul(T, r.q, p);
195
+ const d = try std.math.mul(T, r.q, q);
196
+ const n1 = try std.math.mul(T, r.p, q);
197
+ const n2 = try std.math.mul(T, r.q, p);
198
198
 
199
199
  r.p = try std.math.add(T, n1, n2);
200
200
  r.q = d;
@@ -203,7 +203,7 @@ pub fn Rational(comptime T: type) type {
203
203
  }
204
204
 
205
205
  // https://en.wikipedia.org/wiki/Euclidean_algorithm
206
- inline fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
206
+ fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
207
207
  assert(p >= 1);
208
208
  assert(q >= 1);
209
209
 
@@ -234,6 +234,8 @@ test Rational {
234
234
  var r: Rational(t) = .{};
235
235
 
236
236
  var c: t = 128;
237
+ _ = &c;
238
+
237
239
  try r.update(c, 256);
238
240
  try doTurn(&r);
239
241
 
@@ -275,7 +277,7 @@ pub const BigRational = struct {
275
277
  /// Create a new BigRational wrapper. A small amount of memory will be allocated on
276
278
  /// initialization. Not default initialized to 1 - you must explicitly `reset` first.
277
279
  pub fn init(alloc: std.mem.Allocator) !BigRational {
278
- return BigRational{ .val = try std.math.big.Rational.init(alloc) };
280
+ return .{ .val = try std.math.big.Rational.init(alloc) };
279
281
  }
280
282
 
281
283
  /// Frees all memory associated with a rational.
@@ -320,6 +322,8 @@ test BigRational {
320
322
  try r.reset();
321
323
 
322
324
  var c: u8 = 128;
325
+ _ = &c;
326
+
323
327
  try r.update(c, 256);
324
328
  try doTurn(&r);
325
329
 
@@ -1,6 +1,6 @@
1
1
  const std = @import("std");
2
2
 
3
- const options = @import("./options.zig");
3
+ const options = @import("options.zig");
4
4
 
5
5
  const assert = std.debug.assert;
6
6
  const expectEqual = std.testing.expectEqual;
@@ -25,6 +25,11 @@ pub fn PRNG(comptime gen: comptime_int) type {
25
25
  pub fn next(self: *Self) Output(gen) {
26
26
  return self.src.next();
27
27
  }
28
+
29
+ pub fn advance(self: *Self, n: usize) void {
30
+ if (!options.advance) return;
31
+ for (0..n) |_| self.src.advance();
32
+ }
28
33
  };
29
34
  }
30
35
 
@@ -64,8 +69,8 @@ pub const PSRNG = extern struct {
64
69
  };
65
70
 
66
71
  test PSRNG {
67
- var psrng = PSRNG{ .src = .{ .seed = 0x0001000200030004 } };
68
- try expectEqual(@as(u8, 121), psrng.range(u8, 0, 256));
72
+ var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
73
+ try expectEqual(121, psrng.range(u8, 0, 256));
69
74
  try expectEqual(false, psrng.chance(u8, 128, 256)); // 226 < 128
70
75
  }
71
76
 
@@ -79,7 +84,7 @@ pub const Gen12 = extern struct {
79
84
  assert(@sizeOf(Gen12) == 10);
80
85
  }
81
86
 
82
- pub fn percent(p: u8) u8 {
87
+ pub fn percent(comptime p: comptime_int) u8 {
83
88
  return @intCast((@as(u16, p) * 0xFF) / 100);
84
89
  }
85
90
 
@@ -89,18 +94,22 @@ pub const Gen12 = extern struct {
89
94
  self.index = (self.index + 1) % 9;
90
95
  return val;
91
96
  }
97
+
98
+ pub fn advance(self: *Gen12) void {
99
+ _ = self.next();
100
+ }
92
101
  };
93
102
 
94
103
  test Gen12 {
95
104
  const expected = [_]u8{ 6, 11, 16, 21, 26, 31, 36, 41, 46, 31, 56, 81 };
96
- var rng = Gen12{ .seed = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } };
105
+ var rng: Gen12 = .{ .seed = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } };
97
106
  for (expected) |e| {
98
107
  try expectEqual(e, rng.next());
99
108
  }
100
109
 
101
- try expectEqual(@as(u8, 16), Gen12.percent(6) + 1);
102
- try expectEqual(@as(u8, 16), Gen12.percent(7) - 1);
103
- try expectEqual(@as(u8, 128), Gen12.percent(50) + 1);
110
+ try expectEqual(16, Gen12.percent(6) + 1);
111
+ try expectEqual(16, Gen12.percent(7) - 1);
112
+ try expectEqual(128, Gen12.percent(50) + 1);
104
113
  }
105
114
 
106
115
  // https://pkmn.cc/pokeemerald/src/random.c
@@ -129,7 +138,7 @@ test Gen34 {
129
138
  .{ 0x80000000, 5, 0x0E425287 }, .{ 0x80000000, 10, 0x6F2CF4B2 },
130
139
  };
131
140
  for (data) |d| {
132
- var rng = Gen34{ .seed = d[0] };
141
+ var rng: Gen34 = .{ .seed = d[0] };
133
142
  for (0..d[1]) |_| _ = rng.next();
134
143
  try expectEqual(d[2], rng.seed);
135
144
  }
@@ -161,7 +170,7 @@ test Gen56 {
161
170
  .{ 0x8000000000000000, 10, 0xE7795501267F125A },
162
171
  };
163
172
  for (data) |d| {
164
- var rng = Gen56{ .seed = d[0] };
173
+ var rng: Gen56 = .{ .seed = d[0] };
165
174
  for (0..d[1]) |_| _ = rng.next();
166
175
  try expectEqual(d[2], rng.seed);
167
176
  }
@@ -213,7 +222,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
213
222
  test FixedRNG {
214
223
  const Type = if (showdown) u32 else u8;
215
224
  const expected = [_]Type{ 42, 255, 0 };
216
- var rng = FixedRNG(1, expected.len){ .rolls = expected };
225
+ var rng: FixedRNG(1, expected.len) = .{ .rolls = expected };
217
226
  for (expected) |e| {
218
227
  try expectEqual(e, rng.next());
219
228
  }
@@ -427,34 +427,34 @@ data in the browser for ease of debugging.
427
427
 
428
428
  > **NOTE:** The offsets in the following table represent *bits* and **not** bytes.
429
429
 
430
- | Start | End | Data | Description |
431
- | ----- | --- | ------------------- | ----------------------------------------------------------- |
432
- | 0 | 1 | `Bide` | Whether the "Bide" volatile status is present |
433
- | 1 | 2 | `Thrashing` | Whether the "Thrashing" volatile status is present |
434
- | 2 | 3 | `MultiHit` | Whether the "MultiHit" volatile status is present |
435
- | 3 | 4 | `Flinch` | Whether the "Flinch" volatile status is present |
436
- | 4 | 5 | `Charging` | Whether the "Charging" volatile status is present |
437
- | 5 | 6 | `Binding` | Whether the "Binding" volatile status is present |
438
- | 6 | 7 | `Invulnerable` | Whether the "Invulnerable" volatile status is present |
439
- | 7 | 8 | `Confusion` | Whether the "Confusion" volatile status is present |
440
- | 8 | 9 | `Mist` | Whether the "Mist" volatile status is present |
441
- | 9 | 10 | `FocusEnergy` | Whether the "FocusEnergy" volatile status is present |
442
- | 10 | 11 | `Substitute` | Whether the "Substitute" volatile status is present |
443
- | 11 | 12 | `Recharging` | Whether the "Recharging" volatile status is present |
444
- | 12 | 13 | `Rage` | Whether the "Rage" volatile status is present |
445
- | 13 | 14 | `LeechSeed` | Whether the "LeechSeed" volatile status is present |
446
- | 14 | 15 | `Toxic` | Whether the "Toxic" volatile status is present |
447
- | 15 | 16 | `LightScreen` | Whether the "LightScreen" volatile status is present |
448
- | 16 | 17 | `Reflect` | Whether the "Reflect" volatile status is present |
449
- | 17 | 18 | `Transform` | Whether the "Transform" volatile status is present |
450
- | 18 | 21 | `confusion` | The remaining turns of confusion |
451
- | 21 | 24 | `attacks` | The number of attacks remaining |
452
- | 24 | 40 | `state` | A union of either: <ul><li>the total accumulated damage from Bide</li><li>the overwritten accuracy of certain moves</li></ul> |
453
- | 40 | 48 | `substitute` | The remaining HP of the Substitute |
454
- | 48 | 52 | `transform` | The identity of whom the active Pokémon is transformed into |
455
- | 52 | 56 | `disabled_duration` | The remaining turns the move is disabled |
456
- | 56 | 59 | `disabled_move` | The move slot (1-4) that's disabled |
457
- | 59 | 64 | `toxic` | The number of turns toxic damage has been accumulating |
430
+ | Start | End | Data | Description |
431
+ | ----- | --- | -------------------| ----------------------------------------------------------- |
432
+ | 0 | 1 | `Bide` | Whether the "Bide" volatile status is present |
433
+ | 1 | 2 | `Thrashing` | Whether the "Thrashing" volatile status is present |
434
+ | 2 | 3 | `MultiHit` | Whether the "MultiHit" volatile status is present |
435
+ | 3 | 4 | `Flinch` | Whether the "Flinch" volatile status is present |
436
+ | 4 | 5 | `Charging` | Whether the "Charging" volatile status is present |
437
+ | 5 | 6 | `Binding` | Whether the "Binding" volatile status is present |
438
+ | 6 | 7 | `Invulnerable` | Whether the "Invulnerable" volatile status is present |
439
+ | 7 | 8 | `Confusion` | Whether the "Confusion" volatile status is present |
440
+ | 8 | 9 | `Mist` | Whether the "Mist" volatile status is present |
441
+ | 9 | 10 | `FocusEnergy` | Whether the "FocusEnergy" volatile status is present |
442
+ | 10 | 11 | `Substitute` | Whether the "Substitute" volatile status is present |
443
+ | 11 | 12 | `Recharging` | Whether the "Recharging" volatile status is present |
444
+ | 12 | 13 | `Rage` | Whether the "Rage" volatile status is present |
445
+ | 13 | 14 | `LeechSeed` | Whether the "LeechSeed" volatile status is present |
446
+ | 14 | 15 | `Toxic` | Whether the "Toxic" volatile status is present |
447
+ | 15 | 16 | `LightScreen` | Whether the "LightScreen" volatile status is present |
448
+ | 16 | 17 | `Reflect` | Whether the "Reflect" volatile status is present |
449
+ | 17 | 18 | `Transform` | Whether the "Transform" volatile status is present |
450
+ | 18 | 21 | `confusion` | The remaining turns of confusion |
451
+ | 21 | 24 | `attacks` | The number of attacks remaining |
452
+ | 24 | 40 | `state` | A union of either: <ul><li>the total accumulated damage from Bide</li><li>the overwritten accuracy of certain moves</li></ul> |
453
+ | 40 | 48 | `substitute` | The remaining HP of the Substitute |
454
+ | 48 | 52 | `transform` | The identity of whom the active Pokémon is transformed into |
455
+ | 52 | 56 | `disable_duration` | The remaining turns the move is disabled |
456
+ | 56 | 59 | `disable_move` | The move slot (1-4) that's disabled |
457
+ | 59 | 64 | `toxic` | The number of turns toxic damage has been accumulating |
458
458
 
459
459
  ### `Pokemon`
460
460
 
@@ -612,7 +612,8 @@ engine, but the following moves have their broken behavior preserved in `-Dshowd
612
612
  - **Roar** / **Whirlwind**: these moves can miss on Pokémon Showdown (and advance the RNG when
613
613
  checking) which is incorrect (these moves should always fail, but don't check accuracy or advance
614
614
  the RNG). More importantly, these moves should *not* cause the tracked last battle damage to be
615
- zeroed, but on Pokémon Showdown they do.
615
+ zeroed, but on Pokémon Showdown they do. These should also `|-fail|...|[still]` instead of doing
616
+ nothing.
616
617
  - **Substitute**: in addition to the [Substitute + Confusion
617
618
  glitch](https://pkmn.cc/bulba-glitch-1#Substitute_.2B_Confusion_glitch) not being implemented
618
619
  correctly (covered earlier), the [Substitute 1/4
@@ -634,6 +635,7 @@ only pedantic UI correctness):
634
635
 
635
636
  - **Rage**: Rage should report the `|-boost|` before the Disable `|-miss|`, not after.
636
637
  - **Haze**: Haze clears the status/volatiles in an incorrect order.
638
+ - **Teleport**: Teleport should `|-fail|...|[still]` instead of doing nothing.
637
639
  - **Twineedle**: the `|-hitcount|` ("Hit 2 times") should come before the `|-status|` message, not
638
640
  after.
639
641
  - **Thrash** / **Petal Dance**: confusion should silently get applied before the `|move|` message,
@@ -29,7 +29,6 @@ const Optional = optional.Optional;
29
29
  const Rational = rational.Rational;
30
30
 
31
31
  const PointerType = util.PointerType;
32
- const FieldType = util.FieldType;
33
32
 
34
33
  const Actions = chance.Actions;
35
34
  const Action = chance.Action;
@@ -51,7 +50,7 @@ pub const Summaries = extern struct {
51
50
  }
52
51
 
53
52
  /// Returns the `Summary` for the given `player`.
54
- pub inline fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
53
+ pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
55
54
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Summaries);
56
55
  return if (player == .P1) &self.p1 else &self.p2;
57
56
  }
@@ -158,7 +157,7 @@ pub const Calc = struct {
158
157
 
159
158
  /// Null object pattern implementation of Generation I `Calc` which does nothing, though damage
160
159
  /// calculator support should additionally be turned off entirely via `options.calc`.
161
- pub const NULL = Null{};
160
+ pub const NULL: Null = .{};
162
161
 
163
162
  const Null = struct {
164
163
  pub fn overridden(
@@ -212,8 +211,8 @@ pub fn transitions(
212
211
  ) !?Stats {
213
212
  var stats: Stats = .{};
214
213
 
215
- var actions = options.actions;
216
- var cap = options.cap;
214
+ const actions = options.actions;
215
+ const cap = options.cap;
217
216
 
218
217
  var seen = std.AutoHashMap(Actions, void).init(allocator);
219
218
  defer seen.deinit();
@@ -233,8 +232,8 @@ pub fn transitions(
233
232
  _ = try b.update(c1, c2, &opts);
234
233
  stats.updates += 1;
235
234
 
236
- var p1 = b.side(.P1);
237
- var p2 = b.side(.P2);
235
+ const p1 = b.side(.P1);
236
+ const p2 = b.side(.P2);
238
237
 
239
238
  var p: Rational(u128) = .{ .p = 0, .q = 1 };
240
239
  try frontier.append(opts.chance.actions);
@@ -248,7 +247,7 @@ pub fn transitions(
248
247
  var i: usize = 0;
249
248
  assert(frontier.items.len == 1);
250
249
  while (i < frontier.items.len) : (i += 1) {
251
- var template = frontier.items[i];
250
+ const template = frontier.items[i];
252
251
  opts.calc.overrides.durations = .{
253
252
  .p1 = template.p1.durations,
254
253
  .p2 = template.p2.durations,
@@ -256,7 +255,7 @@ pub fn transitions(
256
255
 
257
256
  try debug(writer, template, true, .{ .color = i, .bold = true, .background = true });
258
257
 
259
- var a = Actions{ .p1 = .{ .metronome = p1_move }, .p2 = .{ .metronome = p2_move } };
258
+ var a: Actions = .{ .p1 = .{ .metronome = p1_move }, .p2 = .{ .metronome = p2_move } };
260
259
 
261
260
  for (Rolls.speedTie(template.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
262
261
  for (Rolls.sleep(template.p1, actions.p1)) |p1_slp| { a.p1.durations.sleep = p1_slp;
@@ -388,7 +387,7 @@ pub fn transitions(
388
387
  return stats;
389
388
  }
390
389
 
391
- inline fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
390
+ fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
392
391
  for (frontier, 0..) |f, j| {
393
392
  // TODO: is skipping this redundant check worth it?
394
393
  if (i == j) continue;
@@ -14,12 +14,14 @@ const util = @import("../common/util.zig");
14
14
  const Player = @import("../common/data.zig").Player;
15
15
  const Optional = @import("../common/optional.zig").Optional;
16
16
 
17
- const Move = @import("data.zig").Move;
18
- const MoveSlot = @import("data.zig").MoveSlot;
17
+ const data = @import("data.zig");
19
18
 
20
19
  const enabled = options.chance;
21
20
  const showdown = options.showdown;
22
21
 
22
+ const Move = data.Move;
23
+ const MoveSlot = data.MoveSlot;
24
+
23
25
  /// Actions taken by a hypothetical "chance player" that convey information about which RNG events
24
26
  /// were observed during a Generation I battle `update`. This can additionally be provided as input
25
27
  /// to the `update` call to override the normal behavior of the RNG in order to force specific
@@ -35,19 +37,19 @@ pub const Actions = extern struct {
35
37
  }
36
38
 
37
39
  /// Returns the `Action` for the given `player`.
38
- pub inline fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Action) {
40
+ pub fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Action) {
39
41
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Actions);
40
42
  return if (player == .P1) &self.p1 else &self.p2;
41
43
  }
42
44
 
43
45
  /// Perform a reset by clearing fields which should not persist across updates.
44
- pub inline fn reset(self: *Actions) void {
46
+ pub fn reset(self: *Actions) void {
45
47
  self.p1.reset();
46
48
  self.p2.reset();
47
49
  }
48
50
 
49
51
  /// Returns true if `a` is equal to `b`.
50
- pub inline fn eql(a: Actions, b: Actions) bool {
52
+ pub fn eql(a: Actions, b: Actions) bool {
51
53
  return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
52
54
  }
53
55
 
@@ -115,7 +117,7 @@ test Actions {
115
117
 
116
118
  /// Information about the RNG that was observed during a Generation I battle `update` for a
117
119
  /// single player.
118
- pub const Action = packed struct {
120
+ pub const Action = packed struct(u64) {
119
121
  /// If not 0, the roll to be returned Rolls.damage.
120
122
  damage: u8 = 0,
121
123
 
@@ -156,12 +158,8 @@ pub const Action = packed struct {
156
158
 
157
159
  pub const Field = std.meta.FieldEnum(Action);
158
160
 
159
- comptime {
160
- assert(@sizeOf(Action) == 8);
161
- }
162
-
163
161
  /// Perform a reset by clearing fields which should not persist across updates.
164
- pub inline fn reset(self: *Action) void {
162
+ pub fn reset(self: *Action) void {
165
163
  self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
166
164
  }
167
165
 
@@ -219,7 +217,7 @@ pub const Action = packed struct {
219
217
  };
220
218
 
221
219
  test Action {
222
- var a = Action{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
220
+ var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
223
221
  a.reset();
224
222
 
225
223
  try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
@@ -239,7 +237,7 @@ pub const Durations = extern struct {
239
237
 
240
238
  /// Observed values for various durations that need to be tracked in order to properly
241
239
  /// deduplicate transitions with a primary key. TODO
242
- pub const Duration = packed struct {
240
+ pub const Duration = packed struct(u16) {
243
241
  /// The number of turns a Pokémon has been observed to be sleeping.
244
242
  sleep: u3 = 0,
245
243
  /// The number of turns a Pokémon has been observed to be confused.
@@ -252,10 +250,6 @@ pub const Duration = packed struct {
252
250
  binding: u3 = 0,
253
251
 
254
252
  pub const Field = std.meta.FieldEnum(Duration);
255
-
256
- comptime {
257
- assert(@sizeOf(Duration) == 2);
258
- }
259
253
  };
260
254
 
261
255
  pub const Commit = enum { hit, miss, binding };
@@ -522,22 +516,6 @@ pub fn Chance(comptime Rational: type) type {
522
516
  }
523
517
  }
524
518
 
525
- pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
526
- if (!enabled) return;
527
-
528
- var durations = &self.actions.get(player).durations;
529
- const n = durations.disable;
530
- if (turns == 0) {
531
- assert(n >= 1 and n <= 8);
532
- if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
533
- durations.disable = 0;
534
- } else {
535
- assert(n >= 1 and n < 8);
536
- try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
537
- durations.disable += 1;
538
- }
539
- }
540
-
541
519
  pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
542
520
  if (!enabled) return;
543
521
 
@@ -554,6 +532,22 @@ pub fn Chance(comptime Rational: type) type {
554
532
  }
555
533
  }
556
534
 
535
+ pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
536
+ if (!enabled) return;
537
+
538
+ var durations = &self.actions.get(player).durations;
539
+ const n = durations.disable;
540
+ if (turns == 0) {
541
+ assert(n >= 1 and n <= 8);
542
+ if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
543
+ durations.disable = 0;
544
+ } else {
545
+ assert(n >= 1 and n < 8);
546
+ try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
547
+ durations.disable += 1;
548
+ }
549
+ }
550
+
557
551
  pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
558
552
  if (!enabled) return;
559
553
 
@@ -601,7 +595,7 @@ pub fn Chance(comptime Rational: type) type {
601
595
  pub fn metronome(self: *Self, player: Player, move: Move) Error!void {
602
596
  if (!enabled) return;
603
597
 
604
- try self.probability.update(1, Move.size - 2);
598
+ try self.probability.update(1, Move.METRONOME.len);
605
599
  self.actions.get(player).metronome = move;
606
600
  }
607
601
  };
@@ -712,19 +706,19 @@ test "Chance.damage" {
712
706
 
713
707
  try chance.damage(.P1, 217);
714
708
  if (showdown) {
715
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
709
+ try expectValue(217, chance.actions.p1.damage);
716
710
  try expectProbability(&chance.probability, 1, 39);
717
711
  } else {
718
712
  try expectProbability(&chance.probability, 1, 1);
719
- try expectValue(@as(u8, 0), chance.actions.p1.damage);
713
+ try expectValue(0, chance.actions.p1.damage);
720
714
 
721
715
  try chance.commit(.P1, .miss);
722
716
  try expectProbability(&chance.probability, 1, 1);
723
- try expectValue(@as(u8, 0), chance.actions.p1.damage);
717
+ try expectValue(0, chance.actions.p1.damage);
724
718
 
725
719
  try chance.commit(.P1, .hit);
726
720
  try expectProbability(&chance.probability, 1, 39);
727
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
721
+ try expectValue(217, chance.actions.p1.damage);
728
722
  }
729
723
  }
730
724
 
@@ -776,13 +770,13 @@ test "Chance.moveSlot" {
776
770
 
777
771
  try chance.moveSlot(.P2, 2, &ms, 2);
778
772
  try expectProbability(&chance.probability, 1, 2);
779
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
773
+ try expectValue(2, chance.actions.p2.move_slot);
780
774
 
781
775
  chance.reset();
782
776
 
783
777
  try chance.moveSlot(.P1, 1, &ms, 0);
784
778
  try expectProbability(&chance.probability, 1, 3);
785
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
779
+ try expectValue(1, chance.actions.p1.move_slot);
786
780
  }
787
781
 
788
782
  test "Chance.multiHit" {
@@ -790,32 +784,32 @@ test "Chance.multiHit" {
790
784
 
791
785
  try chance.multiHit(.P1, 3);
792
786
  try expectProbability(&chance.probability, 3, 8);
793
- try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
787
+ try expectValue(3, chance.actions.p1.multi_hit);
794
788
 
795
789
  chance.reset();
796
790
 
797
791
  try chance.multiHit(.P2, 5);
798
792
  try expectProbability(&chance.probability, 1, 8);
799
- try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
793
+ try expectValue(5, chance.actions.p2.multi_hit);
800
794
  }
801
795
 
802
796
  test "Chance.duration" {
803
797
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
804
798
 
805
799
  chance.duration(.sleep, .P1, .P2, 2);
806
- try expectValue(@as(u4, 2), chance.actions.p1.duration);
807
- try expectValue(@as(u3, 1), chance.actions.p2.durations.sleep);
800
+ try expectValue(2, chance.actions.p1.duration);
801
+ try expectValue(1, chance.actions.p2.durations.sleep);
808
802
 
809
803
  chance.reset();
810
804
 
811
805
  chance.duration(.binding, .P2, .P2, 4);
812
806
  if (!showdown) {
813
- try expectValue(@as(u4, 0), chance.actions.p2.duration);
814
- try expectValue(@as(u3, 0), chance.actions.p2.durations.binding);
807
+ try expectValue(0, chance.actions.p2.duration);
808
+ try expectValue(0, chance.actions.p2.durations.binding);
815
809
  try chance.commit(.P2, .hit);
816
810
  }
817
- try expectValue(@as(u4, 4), chance.actions.p2.duration);
818
- try expectValue(@as(u3, 1), chance.actions.p2.durations.binding);
811
+ try expectValue(4, chance.actions.p2.duration);
812
+ try expectValue(1, chance.actions.p2.durations.binding);
819
813
  }
820
814
 
821
815
  test "Chance.sleep" {
@@ -825,7 +819,7 @@ test "Chance.sleep" {
825
819
  chance.actions.p1.durations.sleep = @intCast(i);
826
820
  try chance.sleep(.P1, 0);
827
821
  try expectProbability(&chance.probability, 1, d);
828
- try expectValue(@as(u3, 0), chance.actions.p1.durations.sleep);
822
+ try expectValue(0, chance.actions.p1.durations.sleep);
829
823
 
830
824
  chance.reset();
831
825
 
@@ -848,7 +842,7 @@ test "Chance.confusion" {
848
842
  chance.actions.p2.durations.confusion = @intCast(i);
849
843
  try chance.confusion(.P2, 0);
850
844
  try expectProbability(&chance.probability, 1, d);
851
- try expectValue(@as(u3, 0), chance.actions.p2.durations.confusion);
845
+ try expectValue(0, chance.actions.p2.durations.confusion);
852
846
 
853
847
  chance.reset();
854
848
  }
@@ -864,6 +858,28 @@ test "Chance.confusion" {
864
858
  }
865
859
  }
866
860
 
861
+ test "Chance.disable" {
862
+ var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
863
+
864
+ for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
865
+ chance.actions.p1.durations.disable = @intCast(i);
866
+ try chance.disable(.P1, 0);
867
+ try expectProbability(&chance.probability, 1, d);
868
+ try expectValue(0, chance.actions.p1.durations.disable);
869
+
870
+ chance.reset();
871
+
872
+ if (i < 8) {
873
+ chance.actions.p1.durations.disable = @intCast(i);
874
+ try chance.disable(.P1, 1);
875
+ try expectProbability(&chance.probability, d - 1, d);
876
+ try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p1.durations.disable);
877
+
878
+ chance.reset();
879
+ }
880
+ }
881
+ }
882
+
867
883
  test "Chance.attacking" {
868
884
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
869
885
 
@@ -872,7 +888,7 @@ test "Chance.attacking" {
872
888
  chance.actions.p2.durations.attacking = @intCast(i);
873
889
  try chance.attacking(.P2, 0);
874
890
  try expectProbability(&chance.probability, 1, d);
875
- try expectValue(@as(u3, 0), chance.actions.p2.durations.attacking);
891
+ try expectValue(0, chance.actions.p2.durations.attacking);
876
892
 
877
893
  chance.reset();
878
894
  }
@@ -898,7 +914,7 @@ test "Chance.binding" {
898
914
  chance.actions.p1.durations.binding = @intCast(i);
899
915
  try chance.binding(.P1, 0);
900
916
  try expectProbability(&chance.probability, p, q);
901
- try expectValue(@as(u3, 0), chance.actions.p1.durations.binding);
917
+ try expectValue(0, chance.actions.p1.durations.binding);
902
918
 
903
919
  chance.reset();
904
920
 
@@ -918,7 +934,7 @@ test "Chance.psywave" {
918
934
 
919
935
  try chance.psywave(.P2, 100, 150);
920
936
  try expectProbability(&chance.probability, 1, 150);
921
- try expectValue(@as(u8, 101), chance.actions.p2.psywave);
937
+ try expectValue(101, chance.actions.p2.psywave);
922
938
  }
923
939
 
924
940
  test "Chance.metronome" {
@@ -947,7 +963,7 @@ pub fn expectValue(a: anytype, b: anytype) !void {
947
963
 
948
964
  /// Null object pattern implementation of Generation I `Chance` which does nothing, though chance
949
965
  /// tracking should additionally be turned off entirely via `options.chance`.
950
- pub const NULL = Null{};
966
+ pub const NULL: Null = .{};
951
967
 
952
968
  const Null = struct {
953
969
  pub const Error = error{};
@@ -984,10 +1000,6 @@ const Null = struct {
984
1000
  _ = .{ self, player, roll };
985
1001
  }
986
1002
 
987
- pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
988
- _ = .{ self, player, proc, rate };
989
- }
990
-
991
1003
  pub fn confused(self: Null, player: Player, ok: bool) Error!void {
992
1004
  _ = .{ self, player, ok };
993
1005
  }
@@ -996,6 +1008,10 @@ const Null = struct {
996
1008
  _ = .{ self, player, ok };
997
1009
  }
998
1010
 
1011
+ pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
1012
+ _ = .{ self, player, proc, rate };
1013
+ }
1014
+
999
1015
  pub fn moveSlot(self: Null, player: Player, slot: u4, ms: []const MoveSlot, n: u4) Error!void {
1000
1016
  _ = .{ self, player, slot, ms, n };
1001
1017
  }