@pkmn/engine 0.1.0-dev.a0167a9d → 0.1.0-dev.a0447192

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 (60) hide show
  1. package/README.md +4 -2
  2. package/build/pkg/addon/browser.d.ts +3 -0
  3. package/build/pkg/addon/browser.js +43 -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 +96 -0
  7. package/build/pkg/addon/node.js.map +1 -0
  8. package/build/pkg/addon.d.ts +2 -0
  9. package/build/pkg/addon.js +29 -51
  10. package/build/pkg/addon.js.map +1 -1
  11. package/build/pkg/data.d.ts +23 -1
  12. package/build/pkg/data.js.map +1 -1
  13. package/build/pkg/gen1.js +1 -1
  14. package/build/pkg/gen1.js.map +1 -1
  15. package/build/pkg/index.d.ts +22 -9
  16. package/build/pkg/index.js +15 -10
  17. package/build/pkg/index.js.map +1 -1
  18. package/build/pkg/protocol.d.ts +20 -1
  19. package/build/pkg/protocol.js +105 -103
  20. package/build/pkg/protocol.js.map +1 -1
  21. package/build/tools/debug.js +35 -68
  22. package/build/tools/debug.js.map +1 -1
  23. package/build/tools/display/debug.jsx +141 -0
  24. package/build/tools/display/debug.jsx.map +1 -0
  25. package/build/tools/display/index.js +132 -0
  26. package/build/tools/display/index.js.map +1 -0
  27. package/build/tools/display/ui.jsx +349 -0
  28. package/build/tools/display/ui.jsx.map +1 -0
  29. package/build/tools/display/util.js +18 -0
  30. package/build/tools/display/util.js.map +1 -0
  31. package/build.zig +199 -66
  32. package/package.json +46 -29
  33. package/src/bin/install-pkmn-engine +7 -5
  34. package/src/bin/pkmn-debug +1 -1
  35. package/src/lib/common/array.zig +77 -0
  36. package/src/lib/common/data.zig +8 -8
  37. package/src/lib/common/debug.zig +39 -8
  38. package/src/lib/common/js.zig +4 -2
  39. package/src/lib/common/optional.zig +13 -9
  40. package/src/lib/common/options.zig +2 -0
  41. package/src/lib/common/protocol.zig +41 -36
  42. package/src/lib/common/rational.zig +53 -51
  43. package/src/lib/common/rng.zig +4 -4
  44. package/src/lib/common/util.zig +50 -1
  45. package/src/lib/gen1/calc.zig +508 -127
  46. package/src/lib/gen1/chance.zig +316 -173
  47. package/src/lib/gen1/data.zig +28 -27
  48. package/src/lib/gen1/helpers.zig +15 -450
  49. package/src/lib/gen1/mechanics.zig +216 -145
  50. package/src/lib/gen2/calc.zig +34 -16
  51. package/src/lib/gen2/chance.zig +90 -592
  52. package/src/lib/gen2/data.zig +15 -14
  53. package/src/lib/gen2/helpers.zig +4 -6
  54. package/src/lib/gen2/mechanics.zig +47 -77
  55. package/src/lib/pkmn.zig +7 -5
  56. package/src/tools/display/dom.ts +96 -0
  57. package/src/tools/display/ui.css +359 -0
  58. package/build/test/display.js +0 -400
  59. package/build/test/display.js.map +0 -1
  60. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -8,9 +8,9 @@ const expectEqual = std.testing.expectEqual;
8
8
 
9
9
  const options = @import("../common/options.zig");
10
10
  const rational = @import("../common/rational.zig");
11
- const rng = @import("../common/rng.zig");
12
11
  const util = @import("../common/util.zig");
13
12
 
13
+ const Array = @import("../common/array.zig").Array;
14
14
  const Player = @import("../common/data.zig").Player;
15
15
  const Optional = @import("../common/optional.zig").Optional;
16
16
 
@@ -19,13 +19,18 @@ const data = @import("data.zig");
19
19
  const enabled = options.chance;
20
20
  const showdown = options.showdown;
21
21
 
22
- const Gen12 = rng.Gen12;
22
+ const PointerType = util.PointerType;
23
+ const isPointerTo = util.isPointerTo;
23
24
 
24
25
  const Move = data.Move;
25
26
  const Type = data.Type;
26
27
  const Effectiveness = data.Effectiveness;
27
28
  const TriAttack = data.TriAttack;
28
29
 
30
+ const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
31
+ const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
32
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
33
+
29
34
  /// Actions taken by a hypothetical "chance player" that convey information about which RNG events
30
35
  /// were observed during a Generation II battle `update`. This can additionally be provided as input
31
36
  /// to the `update` call to override the normal behavior of the RNG in order to force specific
@@ -37,22 +42,11 @@ pub const Actions = extern struct {
37
42
  p2: Action = .{},
38
43
 
39
44
  /// Returns the `Action` for the given `player`.
40
- pub fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Action) {
41
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Actions);
45
+ pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
46
+ assert(isPointerTo(self, Actions));
42
47
  return if (player == .P1) &self.p1 else &self.p2;
43
48
  }
44
49
 
45
- /// Perform a reset by clearing fields which should not persist across updates.
46
- pub fn reset(self: *Actions) void {
47
- self.p1.reset();
48
- self.p2.reset();
49
- }
50
-
51
- /// TODO
52
- pub fn durations(self: Actions) Durations {
53
- return .{ .p1 = self.p1.durations(), .p2 = self.p2.durations() };
54
- }
55
-
56
50
  /// Returns true if `a` is equal to `b`.
57
51
  pub fn eql(a: Actions, b: Actions) bool {
58
52
  return @as(u128, @bitCast(a.p1)) == @as(u128, @bitCast(b.p1)) and
@@ -63,26 +57,15 @@ pub const Actions = extern struct {
63
57
  /// same shape if they have the same fields set (though those fields need not necessarily be
64
58
  /// set to the same value).
65
59
  pub fn matches(a: Actions, b: Actions) bool {
66
- inline for (@typeInfo(Actions).Struct.fields) |player| {
67
- inline for (@typeInfo(Action).Struct.fields) |field| {
60
+ inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
61
+ inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
68
62
  const a_val = @field(@field(a, player.name), field.name);
69
63
  const b_val = @field(@field(b, player.name), field.name);
70
64
 
71
65
  switch (@typeInfo(@TypeOf(a_val))) {
72
- // TODO: ziglang/zig#19730
73
- // .Struct => inline for (@typeInfo(@TypeOf(a_val)).Struct.fields) |f| {
74
- // const a_val_f = @field(a_val, f.name);
75
- // const b_val_f = @field(b_val, f.name);
76
- // switch (@typeInfo(@TypeOf(a_val_f))) {
77
- // .Int => if ((a_val_f > 0) != (b_val_f > 0)) return false,
78
- // .Bool => if (a_val_f != b_val_f) return false,
79
- // else => unreachable,
80
- // }
81
- // },
82
- .Bool => if (a_val != b_val) return false, // TODO
83
- .Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
66
+ Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
84
67
  return false,
85
- .Int => if ((a_val > 0) != (b_val > 0)) return false,
68
+ Int => if ((a_val > 0) != (b_val > 0)) return false,
86
69
  else => unreachable,
87
70
  }
88
71
  }
@@ -104,17 +87,10 @@ pub const Actions = extern struct {
104
87
  }
105
88
  };
106
89
 
107
- // TODO: ziglang/zig#19730
108
90
  test Actions {
109
- const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
110
- const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
91
+ const a: Actions = .{ .p1 = .{ .hit = .true, .confused = .false, .damages = 245 } };
92
+ const b: Actions = .{ .p1 = .{ .hit = .false, .confused = .true, .damages = 246 } };
111
93
  const c: Actions = .{ .p1 = .{ .hit = .true } };
112
- const d: Actions = .{ .p2 = .{ .hit = .true, .durations_sleep = 2 } };
113
- const e: Actions = .{ .p2 = .{ .hit = .false, .durations_sleep = 4 } };
114
- const f: Actions = .{ .p1 = .{ .hit = .false, .durations_sleep = 4 } };
115
- const g: Actions = .{ .p1 = .{ .hit = .false, .durations_confusion = 1 } };
116
- const h: Actions =
117
- .{ .p1 = .{ .hit = .false, .durations_confusion = 1, .durations_thrash = true } };
118
94
 
119
95
  try expect(a.eql(a));
120
96
  try expect(!a.eql(b));
@@ -127,17 +103,21 @@ test Actions {
127
103
  try expect(b.matches(a));
128
104
  try expect(!a.matches(c));
129
105
  try expect(!c.matches(a));
130
- try expect(d.matches(e));
131
- try expect(!d.matches(f));
132
- try expect(!g.matches(h));
133
- try expect(h.matches(h));
134
106
  }
135
107
 
108
+ /// Observation made about a duration - whether the duration has started, been continued, or ended.
109
+ pub const Observation = enum { started, continuing, ended };
110
+
111
+ pub const Damages = Array(5, u8);
112
+ pub const Criticals = Array(5, Optional(bool));
113
+
136
114
  /// Information about the RNG that was observed during a Generation II battle `update` for a
137
115
  /// single player.
138
116
  pub const Action = packed struct(u128) {
139
- /// If not 0, the roll to be returned Rolls.damage.
140
- damage: u8 = 0,
117
+ /// TODO
118
+ damages: Damages.T = 0,
119
+ // TODO
120
+ critical_hits: Criticals.T = 0,
141
121
 
142
122
  /// If not None, the Player to be returned by Rolls.speedTie.
143
123
  speed_tie: Optional(Player) = .None,
@@ -145,50 +125,58 @@ pub const Action = packed struct(u128) {
145
125
  quick_claw: Optional(bool) = .None,
146
126
  /// If not None, the value to return for Rolls.hit.
147
127
  hit: Optional(bool) = .None,
148
- /// If not None, the value to be returned by Rolls.criticalHit.
149
- critical_hit: Optional(bool) = .None,
150
-
151
128
  /// If not None, the value to return for Rolls.confused.
152
129
  confused: Optional(bool) = .None,
130
+
153
131
  /// If not None, the value to return for Rolls.attract.
154
132
  attract: Optional(bool) = .None,
155
133
  /// If not None, the value to return for Rolls.paralyzed.
156
134
  paralyzed: Optional(bool) = .None,
157
135
  /// If not None, the value to return for Rolls.defrost.
158
136
  defrost: Optional(bool) = .None,
159
-
160
137
  /// If not None, the value to be returned for Rolls.secondaryChance.
161
138
  secondary_chance: Optional(bool) = .None,
139
+
162
140
  /// If not None, the value to be returned for Rolls.item (Focus Band / King's Rock).
163
141
  item: Optional(bool) = .None,
164
142
  /// If not None, the value to be returned for Rolls.protect.
165
143
  protect: Optional(bool) = .None,
166
144
  /// If not None, the value to return for Rolls.triAttack.
167
145
  tri_attack: Optional(TriAttack) = .None,
146
+ /// If not 0, the value to return for Rolls.tripleKick.
147
+ triple_kick: u2 = 0,
168
148
 
169
149
  /// If not 0, (present - 1) * 40 should be returned as the base power for Rolls.present.
170
150
  present: u3 = 0,
171
151
  /// If not 0, magnitude + 3 should be returned as the number for Rolls.magnitude.
172
152
  magnitude: u3 = 0,
173
- /// If not 0, the value to return for Rolls.tripleKick.
174
- triple_kick: u2 = 0,
175
-
176
- /// If not 0, the amount of PP to deduct for Rolls.spite.
153
+ /// If not 0, the amount of PP to deduct for Rolls.spite.
177
154
  spite: u3 = 0,
178
- /// If not None, the value to return for Rolls.conversion2.
179
- conversion_2: Optional(Type) = .None,
180
-
181
155
  /// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
182
156
  /// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
183
- move_slot: u4 = 0,
157
+ move_slot: u3 = 0,
184
158
  /// If not 0, the party slot (1-6) to return in Rolls.forceSwitch. If present as an override,
185
159
  /// invalid values (eg. due to empty party slots or fainted members) will be ignored.
186
- force_switch: u4 = 0,
187
-
160
+ force_switch: u3 = 0,
188
161
  /// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
189
- multi_hit: u4 = 0,
162
+ multi_hit: u3 = 0,
163
+
164
+ sleep: Optional(Observation) = .None,
165
+ /// TODO
166
+ confusion: Optional(Observation) = .None,
167
+ /// TODO
168
+ disable: Optional(Observation) = .None,
169
+ /// TODO
170
+ attacking: Optional(Observation) = .None,
171
+ /// TODO
172
+ binding: Optional(Observation) = .None,
173
+ /// TODO
174
+ encore: Optional(Observation) = .None,
175
+
190
176
  /// If not 0, the value to by one of the Rolls.*Duration rolls.
191
- duration: u4 = 0,
177
+ duration: u3 = 0,
178
+ /// If not None, the value to return for Rolls.conversion2.
179
+ conversion_2: Optional(Type) = .None,
192
180
 
193
181
  /// If not 0, psywave should be returned as the damage roll for Rolls.psywave.
194
182
  psywave: u8 = 0,
@@ -196,49 +184,8 @@ pub const Action = packed struct(u128) {
196
184
  /// If not None, the Move to return for Rolls.metronome.
197
185
  metronome: Move = .None,
198
186
 
199
- _: u28 = 0,
200
-
201
- // TODO: ziglang/zig#19730
202
- /// Observed values of various durations_ Does not influence future RNG calls. TODO
203
- // durations: Duration = .{},
204
-
205
- /// The number of turns a Pokémon has been observed to be sleeping.
206
- durations_sleep: u3 = 0,
207
- /// The number of turns a Pokémon has been observed to be confused.
208
- durations_confusion: u3 = 0,
209
- /// TODO
210
- durations_thrash: bool = false,
211
- /// The number of turns a Pokémon has been observed to be disabled.
212
- durations_disable: u4 = 0,
213
- /// The number of turns a Pokémon has been observed to be attacking.
214
- durations_attacking: u3 = 0,
215
- /// The number of turns a Pokémon has been observed to be binding their opponent.
216
- durations_binding: u3 = 0,
217
- /// The number of turns a Pokémon has been observed to be encored.
218
- durations_encore: u3 = 0,
219
-
220
- pub const DURATIONS: u128 = 0xFFFFFFFF00000000_0000000000000000;
221
-
222
187
  pub const Field = std.meta.FieldEnum(Action);
223
188
 
224
- /// Perform a reset by clearing fields which should not persist across updates.
225
- pub fn reset(self: *Action) void {
226
- self.* = @bitCast(@as(u128, @bitCast(self.*)) & DURATIONS);
227
- }
228
-
229
- // TODO: ziglang/zig#19730
230
- pub fn durations(self: Action) Duration {
231
- return .{
232
- .sleep = self.durations_sleep,
233
- .confusion = self.durations_confusion,
234
- .thrash = self.durations_thrash,
235
- .disable = self.durations_disable,
236
- .attacking = self.durations_attacking,
237
- .binding = self.durations_binding,
238
- .encore = self.durations_encore,
239
- };
240
- }
241
-
242
189
  pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
243
190
  _ = .{ f, o };
244
191
  try fmt(a, w, false);
@@ -247,23 +194,10 @@ pub const Action = packed struct(u128) {
247
194
  pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
248
195
  try writer.writeByte('(');
249
196
  var printed = false;
250
- inline for (@typeInfo(Action).Struct.fields) |field| {
197
+ inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
251
198
  const val = @field(self, field.name);
252
199
  switch (@typeInfo(@TypeOf(val))) {
253
- // TODO: ziglang/zig#19730
254
- // .Struct => inline for (@typeInfo(@TypeOf(val)).Struct.fields) |f| {
255
- // const v = @field(val, f.name);
256
- // if (v != 0) {
257
- // if (printed) try writer.writeAll(", ");
258
- // if (shape) {
259
- // try writer.print("{s}:?", .{f.name});
260
- // } else {
261
- // try writer.print("{s}:{d}", .{ f.name, v });
262
- // }
263
- // printed = true;
264
- // }
265
- // },
266
- .Enum => if (val != .None) {
200
+ Enum => if (val != .None) {
267
201
  if (printed) try writer.writeAll(", ");
268
202
  if (shape) {
269
203
  try writer.print("{s}:?", .{field.name});
@@ -277,7 +211,7 @@ pub const Action = packed struct(u128) {
277
211
  }
278
212
  printed = true;
279
213
  },
280
- .Int => if (val != 0) {
214
+ Int => if (val != 0) {
281
215
  if (printed) try writer.writeAll(", ");
282
216
  if (shape) {
283
217
  try writer.print("{s}:?", .{field.name});
@@ -293,48 +227,6 @@ pub const Action = packed struct(u128) {
293
227
  }
294
228
  };
295
229
 
296
- test Action {
297
- var a: Action = .{ .hit = .true, .durations_sleep = 3, .damage = 225 };
298
- a.reset();
299
-
300
- try expectEqual(Action{ .hit = .None, .durations_sleep = 3, .damage = 0 }, a);
301
- }
302
-
303
- /// TODO
304
- pub const Durations = extern struct {
305
- /// Information about the durations for Player 1.
306
- p1: Duration = .{},
307
- /// Information about the durations for Player 2.
308
- p2: Duration = .{},
309
-
310
- comptime {
311
- assert(@sizeOf(Durations) == 4);
312
- }
313
- };
314
-
315
- /// Observed values for various durations that need to be tracked in order to properly
316
- /// deduplicate transitions with a primary key. TODO
317
- pub const Duration = packed struct(u32) {
318
- /// The number of turns a Pokémon has been observed to be sleeping.
319
- sleep: u3 = 0,
320
- /// The number of turns a Pokémon has been observed to be confused.
321
- confusion: u3 = 0,
322
- /// TODO
323
- thrash: bool = false,
324
- /// The number of turns a Pokémon has been observed to be disabled.
325
- disable: u4 = 0,
326
- /// The number of turns a Pokémon has been observed to be attacking.
327
- attacking: u3 = 0,
328
- /// The number of turns a Pokémon has been observed to be binding their opponent.
329
- binding: u3 = 0,
330
- /// The number of turns a Pokémon has been observed to be encored.
331
- encore: u3 = 0,
332
-
333
- _: u12 = 0,
334
-
335
- pub const Field = std.meta.FieldEnum(Duration);
336
- };
337
-
338
230
  /// Tracks chance actions and their associated probability during a Generation II battle update when
339
231
  /// `options.chance` is enabled.
340
232
  pub fn Chance(comptime Rational: type) type {
@@ -347,45 +239,15 @@ pub fn Chance(comptime Rational: type) type {
347
239
  /// RNG events were observed during a battle `update`.
348
240
  actions: Actions = .{},
349
241
 
350
- // Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
351
- // other durations which are all tied to volatiles, sleep's counter persists through
352
- // switching and so we must store it here. The indices of these arrays correspond to the
353
- // `order` field of a Side. This information could be stored in actions but size is a
354
- // concern so this is tracked separately as actions only purports to track RNG events
355
- // observed during a single `update` (not across updates).
356
- sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
357
-
358
242
  /// Possible error returned by operations tracking chance probability.
359
243
  pub const Error = Rational.Error;
360
244
 
361
245
  /// Convenience helper to clear fields which typically should be cleared between updates.
362
246
  pub fn reset(self: *Self) void {
363
- self.probability.reset();
364
- self.actions.reset();
365
- // TODO: self.pending = .{};
366
- }
367
-
368
- pub fn switched(self: *Self, player: Player, in: u8, out: u8) void {
369
247
  if (!enabled) return;
370
248
 
371
- assert(in >= 1 and in <= 6);
372
- assert(out >= 1 and out <= 6);
373
-
374
- var action = self.actions.get(player);
375
-
376
- const slp = action.durations_sleep;
377
- // TODO: ziglang/zig#19730
378
- // action.durations = .{};
379
- action.durations_sleep = 0;
380
- action.durations_confusion = 0;
381
- action.durations_thrash = false;
382
- action.durations_disable = 0;
383
- action.durations_attacking = 0;
384
- action.durations_binding = 0;
385
- action.durations_encore = 0;
386
-
387
- self.sleeps[@intFromEnum(player)][out - 1] = slp;
388
- action.durations_sleep = @intCast(self.sleeps[@intFromEnum(player)][in - 1]);
249
+ self.probability.reset();
250
+ self.actions = .{};
389
251
  }
390
252
 
391
253
  pub fn speedTie(self: *Self, p1: bool) Error!void {
@@ -411,19 +273,21 @@ pub fn Chance(comptime Rational: type) type {
411
273
  self.actions.get(player).hit = if (ok) .true else .false;
412
274
  }
413
275
 
414
- pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
276
+ pub fn criticalHit(self: *Self, player: Player, i: u3, crit: bool, rate: u8) Error!void {
415
277
  if (!enabled) return;
416
278
 
417
279
  const n = if (crit) rate else @as(u8, @intCast(256 - @as(u9, rate)));
418
280
  try self.probability.update(n, 256);
419
- self.actions.get(player).critical_hit = if (crit) .true else .false;
281
+ var a = self.actions.get(player);
282
+ a.critical_hits = Criticals.set(a.critical_hits, i, if (crit) .true else .false);
420
283
  }
421
284
 
422
- pub fn damage(self: *Self, player: Player, roll: u8) Error!void {
285
+ pub fn damage(self: *Self, player: Player, i: u3, roll: u8) Error!void {
423
286
  if (!enabled) return;
424
287
 
425
288
  try self.probability.update(1, 39);
426
- self.actions.get(player).damage = roll;
289
+ var a = self.actions.get(player);
290
+ a.damages = Damages.set(a.damages, i, roll);
427
291
  }
428
292
 
429
293
  pub fn confused(self: *Self, player: Player, cfz: bool) Error!void {
@@ -575,154 +439,10 @@ pub fn Chance(comptime Rational: type) type {
575
439
  self.actions.get(player).multi_hit = n;
576
440
  }
577
441
 
578
- pub fn duration(
579
- self: *Self,
580
- comptime field: Duration.Field,
581
- player: Player,
582
- target: Player,
583
- turns: u4,
584
- ) void {
585
- if (!enabled) return;
586
-
587
- // TODO: ziglang/zig#19730
588
- // var durations = &self.actions.get(target).durations;
589
- // assert(@field(durations, @tagName(field)) == 0 or
590
- // (field == .confusion and player == target));
591
- // @field(durations, @tagName(field)) = 1;
592
- // if (field == .confusion) durations_thrash = player == target;
593
- switch (field) {
594
- .sleep => {
595
- assert(self.actions.get(target).durations_sleep == 0);
596
- self.actions.get(target).durations_sleep = 1;
597
- },
598
- .confusion => {
599
- assert(self.actions.get(target).durations_confusion == 0 or
600
- player == target);
601
- self.actions.get(target).durations_thrash = player == target;
602
- self.actions.get(target).durations_confusion = 1;
603
- },
604
- .thrash => unreachable,
605
- .disable => {
606
- assert(self.actions.get(target).durations_disable == 0);
607
- self.actions.get(target).durations_disable = 1;
608
- },
609
- .attacking => {
610
- assert(self.actions.get(target).durations_attacking == 0);
611
- self.actions.get(target).durations_attacking = 1;
612
- },
613
- .binding => {
614
- assert(self.actions.get(target).durations_binding == 0);
615
- self.actions.get(target).durations_binding = 1;
616
- },
617
- .encore => {
618
- assert(self.actions.get(target).durations_encore == 0);
619
- self.actions.get(target).durations_encore = 1;
620
- },
621
- ._ => unreachable,
622
- }
623
-
624
- self.actions.get(player).duration = if (options.key) 1 else turns;
625
- }
626
-
627
- pub fn sleep(self: *Self, player: Player, turns: u4) Error!void {
628
- if (!enabled) return;
629
-
630
- const durations_sleep = &self.actions.get(player).durations_sleep;
631
- const n = durations_sleep.*;
632
- if (turns == 0) {
633
- assert(n >= 1 and n <= 6);
634
- if (n != 6) try self.probability.update(1, 7 - @as(u4, n));
635
- durations_sleep.* = 0;
636
- } else {
637
- assert(n >= 1 and n < 6);
638
- try self.probability.update(7 - @as(u4, n) - 1, 7 - @as(u4, n));
639
- durations_sleep.* += 1;
640
- }
641
- }
642
-
643
- pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
644
- if (!enabled) return;
645
-
646
- const durations_confusion = &self.actions.get(player).durations_confusion;
647
- const n = durations_confusion.*;
648
- const hi: u8 = if (self.actions.get(player).durations_thrash) 3 else 5;
649
- const p = hi + 1;
650
- if (turns == 0) {
651
- assert(n >= 2 and n <= hi);
652
- if (n != hi) try self.probability.update(1, p - @as(u4, n));
653
- durations_confusion.* = 0;
654
- } else {
655
- assert(n >= 1 and n < hi);
656
- if (n > 1) try self.probability.update(p - @as(u4, n) - 1, p - @as(u4, n));
657
- durations_confusion.* += 1;
658
- }
659
- }
660
-
661
- pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
662
- if (!enabled) return;
663
-
664
- const durations_disable = &self.actions.get(player).durations_disable;
665
- const n = durations_disable.*;
666
- const hi = if (showdown) 5 else 8;
667
- const p = hi + 1;
668
- if (turns == 0) {
669
- assert(n >= 2 and n <= hi);
670
- if (n != hi) try self.probability.update(1, p - n);
671
- durations_disable.* = 0;
672
- } else {
673
- assert(n >= 1 and n < hi);
674
- if (n > 1) try self.probability.update(p - n - 1, p - n);
675
- durations_disable.* += 1;
676
- }
677
- }
678
-
679
- pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
680
- if (!enabled) return;
681
-
682
- const durations_attacking = &self.actions.get(player).durations_attacking;
683
- const n = durations_attacking.*;
684
- if (turns == 0) {
685
- assert(n >= 2 and n <= 3);
686
- if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
687
- durations_attacking.* = 0;
688
- } else {
689
- assert(n >= 1 and n < 3);
690
- if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
691
- durations_attacking.* += 1;
692
- }
693
- }
694
-
695
- pub fn binding(self: *Self, player: Player, turns: u4) Error!void {
696
- if (!enabled) return;
697
-
698
- const durations_binding = &self.actions.get(player).durations_binding;
699
- const n = durations_binding.*;
700
- if (turns == 0) {
701
- assert(n >= 2 and n <= 5);
702
- if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
703
- durations_binding.* = 0;
704
- } else {
705
- assert(n >= 1 and n < 5);
706
- if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
707
- durations_binding.* += 1;
708
- }
709
- }
710
-
711
- // TODO: consider sharing implementation with binding
712
- pub fn encore(self: *Self, player: Player, turns: u4) Error!void {
442
+ pub fn duration(self: *Self, player: Player, turns: u3) void {
713
443
  if (!enabled) return;
714
444
 
715
- const durations_encore = &self.actions.get(player).durations_encore;
716
- const n = durations_encore.*;
717
- if (turns == 0) {
718
- assert(n >= 2 and n <= 5);
719
- if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
720
- durations_encore.* = 0;
721
- } else {
722
- assert(n >= 1 and n < 5);
723
- if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
724
- durations_encore.* += 1;
725
- }
445
+ self.actions.get(player).duration = if (options.key) 2 else turns;
726
446
  }
727
447
  };
728
448
  }
@@ -774,22 +494,22 @@ test "Chance.hit" {
774
494
  test "Chance.criticalHit" {
775
495
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
776
496
 
777
- try chance.criticalHit(.P1, true, 17);
778
- try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
497
+ try chance.criticalHit(.P1, 0, true, 17);
498
+ try expectValue(Optional(bool).true, Criticals.get(chance.actions.p1.critical_hits, 0));
779
499
  try expectProbability(&chance.probability, 17, 256);
780
500
 
781
501
  chance.reset();
782
502
 
783
- try chance.criticalHit(.P2, false, 5);
503
+ try chance.criticalHit(.P2, 1, false, 5);
784
504
  try expectProbability(&chance.probability, 251, 256);
785
- try expectValue(Optional(bool).false, chance.actions.p2.critical_hit);
505
+ try expectValue(Optional(bool).false, Criticals.get(chance.actions.p2.critical_hits, 1));
786
506
  }
787
507
 
788
508
  test "Chance.damage" {
789
509
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
790
510
 
791
- try chance.damage(.P1, 217);
792
- try expectValue(217, chance.actions.p1.damage);
511
+ try chance.damage(.P1, 0, 219);
512
+ try expectValue(@as(u8, 219), Damages.get(chance.actions.p1.damages, 0));
793
513
  try expectProbability(&chance.probability, 1, 39);
794
514
  }
795
515
 
@@ -914,7 +634,7 @@ test "Chance.present" {
914
634
  } else {
915
635
  try expectProbability(&chance.probability, 25, 256);
916
636
  }
917
- try expectValue(4, chance.actions.p2.present);
637
+ try expectValue(@as(u3, 4), chance.actions.p2.present);
918
638
 
919
639
  chance.reset();
920
640
 
@@ -924,7 +644,7 @@ test "Chance.present" {
924
644
  } else {
925
645
  try expectProbability(&chance.probability, 51, 256);
926
646
  }
927
- try expectValue(1, chance.actions.p1.present);
647
+ try expectValue(@as(u3, 1), chance.actions.p1.present);
928
648
  }
929
649
 
930
650
  test "Chance.magnitude" {
@@ -936,7 +656,7 @@ test "Chance.magnitude" {
936
656
  } else {
937
657
  try expectProbability(&chance.probability, 13, 256);
938
658
  }
939
- try expectValue(7, chance.actions.p2.magnitude);
659
+ try expectValue(@as(u3, 7), chance.actions.p2.magnitude);
940
660
 
941
661
  chance.reset();
942
662
 
@@ -946,7 +666,7 @@ test "Chance.magnitude" {
946
666
  } else {
947
667
  try expectProbability(&chance.probability, 51, 256);
948
668
  }
949
- try expectValue(5, chance.actions.p1.magnitude);
669
+ try expectValue(@as(u3, 5), chance.actions.p1.magnitude);
950
670
  }
951
671
 
952
672
  test "Chance.tripleKick" {
@@ -954,7 +674,7 @@ test "Chance.tripleKick" {
954
674
 
955
675
  try chance.tripleKick(.P1, 2);
956
676
  try expectProbability(&chance.probability, 1, 3);
957
- try expectValue(2, chance.actions.p1.triple_kick);
677
+ try expectValue(@as(u2, 2), chance.actions.p1.triple_kick);
958
678
  }
959
679
 
960
680
  test "Chance.spite" {
@@ -962,7 +682,7 @@ test "Chance.spite" {
962
682
 
963
683
  try chance.spite(.P1, 3);
964
684
  try expectProbability(&chance.probability, 1, 4);
965
- try expectValue(3, chance.actions.p1.spite);
685
+ try expectValue(@as(u3, 3), chance.actions.p1.spite);
966
686
  }
967
687
 
968
688
  test "Chance.conversion2" {
@@ -989,7 +709,7 @@ test "Chance.psywave" {
989
709
 
990
710
  try chance.psywave(.P2, 100, 150);
991
711
  try expectProbability(&chance.probability, 1, 150);
992
- try expectValue(100, chance.actions.p2.psywave);
712
+ try expectValue(@as(u8, 100), chance.actions.p2.psywave);
993
713
  }
994
714
 
995
715
  test "Chance.metronome" {
@@ -1005,19 +725,19 @@ test "Chance.moveSlot" {
1005
725
 
1006
726
  try chance.moveSlot(.P2, 2, 2);
1007
727
  try expectProbability(&chance.probability, 1, 2);
1008
- try expectValue(2, chance.actions.p2.move_slot);
728
+ try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
1009
729
 
1010
730
  chance.reset();
1011
731
 
1012
732
  try chance.moveSlot(.P1, 1, 3);
1013
733
  try expectProbability(&chance.probability, 1, 3);
1014
- try expectValue(1, chance.actions.p1.move_slot);
734
+ try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
1015
735
 
1016
736
  chance.reset();
1017
737
 
1018
738
  try chance.moveSlot(.P1, 4, 1);
1019
739
  try expectProbability(&chance.probability, 1, 1);
1020
- try expectValue(4, chance.actions.p1.move_slot);
740
+ try expectValue(@as(u3, 4), chance.actions.p1.move_slot);
1021
741
  }
1022
742
 
1023
743
  test "Chance.forceSwitch" {
@@ -1025,19 +745,19 @@ test "Chance.forceSwitch" {
1025
745
 
1026
746
  try chance.forceSwitch(.P2, 2, 2);
1027
747
  try expectProbability(&chance.probability, 1, 2);
1028
- try expectValue(2, chance.actions.p2.force_switch);
748
+ try expectValue(@as(u3, 2), chance.actions.p2.force_switch);
1029
749
 
1030
750
  chance.reset();
1031
751
 
1032
752
  try chance.forceSwitch(.P1, 1, 3);
1033
753
  try expectProbability(&chance.probability, 1, 3);
1034
- try expectValue(1, chance.actions.p1.force_switch);
754
+ try expectValue(@as(u3, 1), chance.actions.p1.force_switch);
1035
755
 
1036
756
  chance.reset();
1037
757
 
1038
758
  try chance.forceSwitch(.P1, 4, 1);
1039
759
  try expectProbability(&chance.probability, 1, 1);
1040
- try expectValue(4, chance.actions.p1.force_switch);
760
+ try expectValue(@as(u3, 4), chance.actions.p1.force_switch);
1041
761
  }
1042
762
 
1043
763
  test "Chance.multiHit" {
@@ -1045,212 +765,20 @@ test "Chance.multiHit" {
1045
765
 
1046
766
  try chance.multiHit(.P1, 3);
1047
767
  try expectProbability(&chance.probability, 3, 8);
1048
- try expectValue(3, chance.actions.p1.multi_hit);
768
+ try expectValue(@as(u3, 3), chance.actions.p1.multi_hit);
1049
769
 
1050
770
  chance.reset();
1051
771
 
1052
772
  try chance.multiHit(.P2, 5);
1053
773
  try expectProbability(&chance.probability, 1, 8);
1054
- try expectValue(5, chance.actions.p2.multi_hit);
774
+ try expectValue(@as(u3, 5), chance.actions.p2.multi_hit);
1055
775
  }
1056
776
 
1057
777
  test "Chance.duration" {
1058
778
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1059
779
 
1060
- chance.duration(.sleep, .P1, .P2, 2);
1061
- try expectValue(2, chance.actions.p1.duration);
1062
- try expectValue(1, chance.actions.p2.durations_sleep);
1063
- }
1064
-
1065
- test "Chance.sleep" {
1066
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1067
-
1068
- for ([_]u8{ 6, 5, 4, 3, 2, 1 }, 1..7) |d, i| {
1069
- chance.actions.p1.durations_sleep = @intCast(i);
1070
- try chance.sleep(.P1, 0);
1071
- try expectProbability(&chance.probability, 1, d);
1072
- try expectValue(0, chance.actions.p1.durations_sleep);
1073
-
1074
- chance.reset();
1075
-
1076
- if (i < 6) {
1077
- chance.actions.p1.durations_sleep = @intCast(i);
1078
- try chance.sleep(.P1, 1);
1079
- try expectProbability(&chance.probability, d - 1, d);
1080
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p1.durations_sleep);
1081
-
1082
- chance.reset();
1083
- }
1084
- }
1085
- }
1086
-
1087
- test "Chance.confusion" {
1088
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1089
-
1090
- chance.actions.p2.durations_thrash = false;
1091
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1092
- if (i > 1) {
1093
- chance.actions.p2.durations_confusion = @intCast(i);
1094
- try chance.confusion(.P2, 0);
1095
- try expectProbability(&chance.probability, 1, d);
1096
- try expectValue(0, chance.actions.p2.durations_confusion);
1097
- try expectValue(false, chance.actions.p2.durations_thrash);
1098
-
1099
- chance.reset();
1100
- }
1101
-
1102
- if (i < 5) {
1103
- chance.actions.p2.durations_confusion = @intCast(i);
1104
- try chance.confusion(.P2, 1);
1105
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1106
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations_confusion);
1107
- try expectValue(false, chance.actions.p2.durations_thrash);
1108
-
1109
- chance.reset();
1110
- }
1111
- }
1112
-
1113
- chance.reset();
1114
-
1115
- chance.actions.p2.durations_thrash = true;
1116
- for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
1117
- if (i > 1) {
1118
- chance.actions.p2.durations_confusion = @intCast(i);
1119
- try chance.confusion(.P2, 0);
1120
- try expectProbability(&chance.probability, 1, d);
1121
- try expectValue(0, chance.actions.p2.durations_confusion);
1122
- try expectValue(true, chance.actions.p2.durations_thrash);
1123
-
1124
- chance.reset();
1125
- }
1126
-
1127
- if (i < 3) {
1128
- chance.actions.p2.durations_confusion = @intCast(i);
1129
- try chance.confusion(.P2, 1);
1130
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1131
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations_confusion);
1132
- try expectValue(true, chance.actions.p2.durations_thrash);
1133
-
1134
- chance.reset();
1135
- }
1136
- }
1137
- }
1138
-
1139
- test "Chance.disable" {
1140
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1141
- if (showdown) {
1142
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1143
- if (i > 1) {
1144
- chance.actions.p2.durations_disable = @intCast(i);
1145
- try chance.disable(.P2, 0);
1146
- try expectProbability(&chance.probability, 1, d);
1147
- try expectValue(0, chance.actions.p2.durations_disable);
1148
-
1149
- chance.reset();
1150
- }
1151
-
1152
- if (i < 5) {
1153
- chance.actions.p2.durations_disable = @intCast(i);
1154
- try chance.disable(.P2, 1);
1155
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1156
- try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations_disable);
1157
-
1158
- chance.reset();
1159
- }
1160
- }
1161
- } else {
1162
- for ([_]u8{ 1, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
1163
- if (i > 1) {
1164
- chance.actions.p2.durations_disable = @intCast(i);
1165
- try chance.disable(.P2, 0);
1166
- try expectProbability(&chance.probability, 1, d);
1167
- try expectValue(0, chance.actions.p2.durations_disable);
1168
-
1169
- chance.reset();
1170
- }
1171
-
1172
- if (i < 8) {
1173
- chance.actions.p2.durations_disable = @intCast(i);
1174
- try chance.disable(.P2, 1);
1175
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1176
- try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations_disable);
1177
-
1178
- chance.reset();
1179
- }
1180
- }
1181
- }
1182
- }
1183
-
1184
- test "Chance.attacking" {
1185
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1186
-
1187
- for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
1188
- if (i > 1) {
1189
- chance.actions.p2.durations_attacking = @intCast(i);
1190
- try chance.attacking(.P2, 0);
1191
- try expectProbability(&chance.probability, 1, d);
1192
- try expectValue(0, chance.actions.p2.durations_attacking);
1193
-
1194
- chance.reset();
1195
- }
1196
-
1197
- if (i < 3) {
1198
- chance.actions.p2.durations_attacking = @intCast(i);
1199
- try chance.attacking(.P2, 1);
1200
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1201
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations_attacking);
1202
-
1203
- chance.reset();
1204
- }
1205
- }
1206
- }
1207
-
1208
- test "Chance.binding" {
1209
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1210
-
1211
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1212
- if (i > 1) {
1213
- chance.actions.p2.durations_binding = @intCast(i);
1214
- try chance.binding(.P2, 0);
1215
- try expectProbability(&chance.probability, 1, d);
1216
- try expectValue(0, chance.actions.p2.durations_binding);
1217
-
1218
- chance.reset();
1219
- }
1220
-
1221
- if (i < 5) {
1222
- chance.actions.p2.durations_binding = @intCast(i);
1223
- try chance.binding(.P2, 1);
1224
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1225
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations_binding);
1226
-
1227
- chance.reset();
1228
- }
1229
- }
1230
- }
1231
-
1232
- test "Chance.encore" {
1233
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1234
-
1235
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1236
- if (i > 1) {
1237
- chance.actions.p2.durations_encore = @intCast(i);
1238
- try chance.encore(.P2, 0);
1239
- try expectProbability(&chance.probability, 1, d);
1240
- try expectValue(0, chance.actions.p2.durations_encore);
1241
-
1242
- chance.reset();
1243
- }
1244
-
1245
- if (i < 5) {
1246
- chance.actions.p2.durations_encore = @intCast(i);
1247
- try chance.encore(.P2, 1);
1248
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1249
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations_encore);
1250
-
1251
- chance.reset();
1252
- }
1253
- }
780
+ chance.duration(.P1, 2);
781
+ try expectValue(@as(u3, 2), chance.actions.p1.duration);
1254
782
  }
1255
783
 
1256
784
  pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
@@ -1292,12 +820,12 @@ const Null = struct {
1292
820
  _ = .{ self, player, ok, accuracy };
1293
821
  }
1294
822
 
1295
- pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
1296
- _ = .{ self, player, crit, rate };
823
+ pub fn criticalHit(self: Null, player: Player, i: u3, crit: bool, rate: u8) Error!void {
824
+ _ = .{ self, player, i, crit, rate };
1297
825
  }
1298
826
 
1299
- pub fn damage(self: Null, player: Player, roll: u8) Error!void {
1300
- _ = .{ self, player, roll };
827
+ pub fn damage(self: Null, player: Player, i: u3, roll: u8) Error!void {
828
+ _ = .{ self, player, i, roll };
1301
829
  }
1302
830
 
1303
831
  pub fn confused(self: Null, player: Player, ok: bool) Error!void {
@@ -1368,37 +896,7 @@ const Null = struct {
1368
896
  _ = .{ self, player, move, n };
1369
897
  }
1370
898
 
1371
- pub fn duration(
1372
- self: Null,
1373
- comptime field: Duration.Field,
1374
- player: Player,
1375
- target: Player,
1376
- turns: u4,
1377
- ) void {
1378
- _ = .{ self, field, player, target, turns };
1379
- }
1380
-
1381
- pub fn sleep(self: Null, player: Player, turns: u4) Error!void {
1382
- _ = .{ self, player, turns };
1383
- }
1384
-
1385
- pub fn confusion(self: Null, player: Player, turns: u4) Error!void {
1386
- _ = .{ self, player, turns };
1387
- }
1388
-
1389
- pub fn disable(self: Null, player: Player, turns: u4) Error!void {
1390
- _ = .{ self, player, turns };
1391
- }
1392
-
1393
- pub fn attacking(self: Null, player: Player, turns: u4) Error!void {
1394
- _ = .{ self, player, turns };
1395
- }
1396
-
1397
- pub fn binding(self: Null, player: Player, turns: u4) Error!void {
1398
- _ = .{ self, player, turns };
1399
- }
1400
-
1401
- pub fn encore(self: Null, player: Player, turns: u4) Error!void {
899
+ pub fn duration(self: Null, player: Player, turns: u3) void {
1402
900
  _ = .{ self, player, turns };
1403
901
  }
1404
902
  };