@pkmn/engine 0.1.0-dev.62399ed5 → 0.1.0-dev.6be9a775

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.
@@ -11,6 +11,7 @@ const rational = @import("../common/rational.zig");
11
11
  const rng = @import("../common/rng.zig");
12
12
  const util = @import("../common/util.zig");
13
13
 
14
+ const Array = @import("../common/array.zig").Array;
14
15
  const Player = @import("../common/data.zig").Player;
15
16
  const Optional = @import("../common/optional.zig").Optional;
16
17
 
@@ -21,11 +22,18 @@ const showdown = options.showdown;
21
22
 
22
23
  const Gen12 = rng.Gen12;
23
24
 
25
+ const PointerType = util.PointerType;
26
+ const isPointerTo = util.isPointerTo;
27
+
24
28
  const Move = data.Move;
25
29
  const Type = data.Type;
26
30
  const Effectiveness = data.Effectiveness;
27
31
  const TriAttack = data.TriAttack;
28
32
 
33
+ const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
34
+ const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
35
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
36
+
29
37
  /// Actions taken by a hypothetical "chance player" that convey information about which RNG events
30
38
  /// were observed during a Generation II battle `update`. This can additionally be provided as input
31
39
  /// to the `update` call to override the normal behavior of the RNG in order to force specific
@@ -37,17 +45,11 @@ pub const Actions = extern struct {
37
45
  p2: Action = .{},
38
46
 
39
47
  /// 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);
48
+ pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
49
+ assert(isPointerTo(self, Actions));
42
50
  return if (player == .P1) &self.p1 else &self.p2;
43
51
  }
44
52
 
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
53
  /// Returns true if `a` is equal to `b`.
52
54
  pub fn eql(a: Actions, b: Actions) bool {
53
55
  return @as(u128, @bitCast(a.p1)) == @as(u128, @bitCast(b.p1)) and
@@ -58,24 +60,15 @@ pub const Actions = extern struct {
58
60
  /// same shape if they have the same fields set (though those fields need not necessarily be
59
61
  /// set to the same value).
60
62
  pub fn matches(a: Actions, b: Actions) bool {
61
- inline for (@typeInfo(Actions).Struct.fields) |player| {
62
- inline for (@typeInfo(Action).Struct.fields) |field| {
63
+ inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
64
+ inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
63
65
  const a_val = @field(@field(a, player.name), field.name);
64
66
  const b_val = @field(@field(b, player.name), field.name);
65
67
 
66
68
  switch (@typeInfo(@TypeOf(a_val))) {
67
- .Struct => inline for (@typeInfo(@TypeOf(a_val)).Struct.fields) |f| {
68
- const a_val_f = @field(a_val, f.name);
69
- const b_val_f = @field(b_val, f.name);
70
- switch (@typeInfo(@TypeOf(a_val_f))) {
71
- .Int => if ((a_val_f > 0) != (b_val_f > 0)) return false,
72
- .Bool => if (a_val_f != b_val_f) return false,
73
- else => unreachable,
74
- }
75
- },
76
- .Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
69
+ Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
77
70
  return false,
78
- .Int => if ((a_val > 0) != (b_val > 0)) return false,
71
+ Int => if ((a_val > 0) != (b_val > 0)) return false,
79
72
  else => unreachable,
80
73
  }
81
74
  }
@@ -97,17 +90,10 @@ pub const Actions = extern struct {
97
90
  }
98
91
  };
99
92
 
100
- // TODO: ziglang/zig#19730
101
93
  test Actions {
102
- const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
103
- const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
94
+ const a: Actions = .{ .p1 = .{ .hit = .true, .confused = .false, .damages = 245 } };
95
+ const b: Actions = .{ .p1 = .{ .hit = .false, .confused = .true, .damages = 246 } };
104
96
  const c: Actions = .{ .p1 = .{ .hit = .true } };
105
- // const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
106
- // const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
107
- // const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
108
- // const g: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1 } } };
109
- // const h: Actions =
110
- // .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1, .thrash = true } } };
111
97
 
112
98
  try expect(a.eql(a));
113
99
  try expect(!a.eql(b));
@@ -120,17 +106,21 @@ test Actions {
120
106
  try expect(b.matches(a));
121
107
  try expect(!a.matches(c));
122
108
  try expect(!c.matches(a));
123
- // try expect(d.matches(e));
124
- // try expect(!d.matches(f));
125
- // try expect(!g.matches(h));
126
- // try expect(h.matches(h));
127
109
  }
128
110
 
111
+ /// Observation made about a duration - whether the duration has started, been continued, or ended.
112
+ pub const Observation = enum { started, continuing, ended };
113
+
114
+ pub const Damages = Array(5, u8);
115
+ pub const Criticals = Array(5, Optional(bool));
116
+
129
117
  /// Information about the RNG that was observed during a Generation II battle `update` for a
130
118
  /// single player.
131
119
  pub const Action = packed struct(u128) {
132
- /// If not 0, the roll to be returned Rolls.damage.
133
- damage: u8 = 0,
120
+ /// TODO
121
+ damages: Damages.T = 0,
122
+ // TODO
123
+ critical_hits: Criticals.T = 0,
134
124
 
135
125
  /// If not None, the Player to be returned by Rolls.speedTie.
136
126
  speed_tie: Optional(Player) = .None,
@@ -138,50 +128,58 @@ pub const Action = packed struct(u128) {
138
128
  quick_claw: Optional(bool) = .None,
139
129
  /// If not None, the value to return for Rolls.hit.
140
130
  hit: Optional(bool) = .None,
141
- /// If not None, the value to be returned by Rolls.criticalHit.
142
- critical_hit: Optional(bool) = .None,
143
-
144
131
  /// If not None, the value to return for Rolls.confused.
145
132
  confused: Optional(bool) = .None,
133
+
146
134
  /// If not None, the value to return for Rolls.attract.
147
135
  attract: Optional(bool) = .None,
148
136
  /// If not None, the value to return for Rolls.paralyzed.
149
137
  paralyzed: Optional(bool) = .None,
150
138
  /// If not None, the value to return for Rolls.defrost.
151
139
  defrost: Optional(bool) = .None,
152
-
153
140
  /// If not None, the value to be returned for Rolls.secondaryChance.
154
141
  secondary_chance: Optional(bool) = .None,
142
+
155
143
  /// If not None, the value to be returned for Rolls.item (Focus Band / King's Rock).
156
144
  item: Optional(bool) = .None,
157
145
  /// If not None, the value to be returned for Rolls.protect.
158
146
  protect: Optional(bool) = .None,
159
147
  /// If not None, the value to return for Rolls.triAttack.
160
148
  tri_attack: Optional(TriAttack) = .None,
149
+ /// If not 0, the value to return for Rolls.tripleKick.
150
+ triple_kick: u2 = 0,
161
151
 
162
152
  /// If not 0, (present - 1) * 40 should be returned as the base power for Rolls.present.
163
153
  present: u3 = 0,
164
154
  /// If not 0, magnitude + 3 should be returned as the number for Rolls.magnitude.
165
155
  magnitude: u3 = 0,
166
- /// If not 0, the value to return for Rolls.tripleKick.
167
- triple_kick: u2 = 0,
168
-
169
- /// If not 0, the amount of PP to deduct for Rolls.spite.
156
+ /// If not 0, the amount of PP to deduct for Rolls.spite.
170
157
  spite: u3 = 0,
171
- /// If not None, the value to return for Rolls.conversion2.
172
- conversion_2: Optional(Type) = .None,
173
-
174
158
  /// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
175
159
  /// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
176
- move_slot: u4 = 0,
160
+ move_slot: u3 = 0,
177
161
  /// If not 0, the party slot (1-6) to return in Rolls.forceSwitch. If present as an override,
178
162
  /// invalid values (eg. due to empty party slots or fainted members) will be ignored.
179
- force_switch: u4 = 0,
180
-
163
+ force_switch: u3 = 0,
181
164
  /// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
182
- multi_hit: u4 = 0,
165
+ multi_hit: u3 = 0,
166
+
167
+ sleep: Optional(Observation) = .None,
168
+ /// TODO
169
+ confusion: Optional(Observation) = .None,
170
+ /// TODO
171
+ disable: Optional(Observation) = .None,
172
+ /// TODO
173
+ attacking: Optional(Observation) = .None,
174
+ /// TODO
175
+ binding: Optional(Observation) = .None,
176
+ /// TODO
177
+ encore: Optional(Observation) = .None,
178
+
183
179
  /// If not 0, the value to by one of the Rolls.*Duration rolls.
184
- duration: u4 = 0,
180
+ duration: u3 = 0,
181
+ /// If not None, the value to return for Rolls.conversion2.
182
+ conversion_2: Optional(Type) = .None,
185
183
 
186
184
  /// If not 0, psywave should be returned as the damage roll for Rolls.psywave.
187
185
  psywave: u8 = 0,
@@ -189,20 +187,8 @@ pub const Action = packed struct(u128) {
189
187
  /// If not None, the Move to return for Rolls.metronome.
190
188
  metronome: Move = .None,
191
189
 
192
- _: u16 = 0,
193
-
194
- /// Observed values of various durations. Does not influence future RNG calls. TODO
195
- durations: Duration = .{},
196
-
197
- pub const DURATIONS: u128 = 0xFFFFFFFF00000000_0000000000000000;
198
-
199
190
  pub const Field = std.meta.FieldEnum(Action);
200
191
 
201
- /// Perform a reset by clearing fields which should not persist across updates.
202
- pub fn reset(self: *Action) void {
203
- self.* = @bitCast(@as(u128, @bitCast(self.*)) & DURATIONS);
204
- }
205
-
206
192
  pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
207
193
  _ = .{ f, o };
208
194
  try fmt(a, w, false);
@@ -211,22 +197,10 @@ pub const Action = packed struct(u128) {
211
197
  pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
212
198
  try writer.writeByte('(');
213
199
  var printed = false;
214
- inline for (@typeInfo(Action).Struct.fields) |field| {
200
+ inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
215
201
  const val = @field(self, field.name);
216
202
  switch (@typeInfo(@TypeOf(val))) {
217
- .Struct => inline for (@typeInfo(@TypeOf(val)).Struct.fields) |f| {
218
- const v = @field(val, f.name);
219
- if (v != 0) {
220
- if (printed) try writer.writeAll(", ");
221
- if (shape) {
222
- try writer.print("{s}:?", .{f.name});
223
- } else {
224
- try writer.print("{s}:{d}", .{ f.name, v });
225
- }
226
- printed = true;
227
- }
228
- },
229
- .Enum => if (val != .None) {
203
+ Enum => if (val != .None) {
230
204
  if (printed) try writer.writeAll(", ");
231
205
  if (shape) {
232
206
  try writer.print("{s}:?", .{field.name});
@@ -240,7 +214,7 @@ pub const Action = packed struct(u128) {
240
214
  }
241
215
  printed = true;
242
216
  },
243
- .Int => if (val != 0) {
217
+ Int => if (val != 0) {
244
218
  if (printed) try writer.writeAll(", ");
245
219
  if (shape) {
246
220
  try writer.print("{s}:?", .{field.name});
@@ -256,36 +230,6 @@ pub const Action = packed struct(u128) {
256
230
  }
257
231
  };
258
232
 
259
- test Action {
260
- var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
261
- a.reset();
262
-
263
- try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
264
- }
265
-
266
- /// Observed values for various durations that need to be tracked in order to properly
267
- /// deduplicate transitions with a primary key. TODO
268
- pub const Duration = packed struct(u32) {
269
- /// The number of turns a Pokémon has been observed to be sleeping.
270
- sleep: u3 = 0,
271
- /// The number of turns a Pokémon has been observed to be confused.
272
- confusion: u3 = 0,
273
- /// TODO
274
- thrash: bool = false,
275
- /// The number of turns a Pokémon has been observed to be disabled.
276
- disable: u4 = 0,
277
- /// The number of turns a Pokémon has been observed to be attacking.
278
- attacking: u3 = 0,
279
- /// The number of turns a Pokémon has been observed to be binding their opponent.
280
- binding: u3 = 0,
281
- /// The number of turns a Pokémon has been observed to be encored.
282
- encore: u3 = 0,
283
-
284
- _: u12 = 0,
285
-
286
- pub const Field = std.meta.FieldEnum(Duration);
287
- };
288
-
289
233
  /// Tracks chance actions and their associated probability during a Generation II battle update when
290
234
  /// `options.chance` is enabled.
291
235
  pub fn Chance(comptime Rational: type) type {
@@ -298,37 +242,15 @@ pub fn Chance(comptime Rational: type) type {
298
242
  /// RNG events were observed during a battle `update`.
299
243
  actions: Actions = .{},
300
244
 
301
- // Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
302
- // other durations which are all tied to volatiles, sleep's counter persists through
303
- // switching and so we must store it here. The indices of these arrays correspond to the
304
- // `order` field of a Side. This information could be stored in actions but size is a
305
- // concern so this is tracked separately as actions only purports to track RNG events
306
- // observed during a single `update` (not across updates).
307
- sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
308
-
309
245
  /// Possible error returned by operations tracking chance probability.
310
246
  pub const Error = Rational.Error;
311
247
 
312
248
  /// Convenience helper to clear fields which typically should be cleared between updates.
313
249
  pub fn reset(self: *Self) void {
314
- self.probability.reset();
315
- self.actions.reset();
316
- // TODO: self.pending = .{};
317
- }
318
-
319
- pub fn switched(self: *Self, player: Player, in: u8, out: u8) void {
320
250
  if (!enabled) return;
321
251
 
322
- assert(in >= 1 and in <= 6);
323
- assert(out >= 1 and out <= 6);
324
-
325
- var action = self.actions.get(player);
326
-
327
- const slp = action.durations.sleep;
328
- action.durations = .{};
329
-
330
- self.sleeps[@intFromEnum(player)][out - 1] = slp;
331
- action.durations.sleep = @intCast(self.sleeps[@intFromEnum(player)][in - 1]);
252
+ self.probability.reset();
253
+ self.actions = .{};
332
254
  }
333
255
 
334
256
  pub fn speedTie(self: *Self, p1: bool) Error!void {
@@ -354,19 +276,21 @@ pub fn Chance(comptime Rational: type) type {
354
276
  self.actions.get(player).hit = if (ok) .true else .false;
355
277
  }
356
278
 
357
- pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
279
+ pub fn criticalHit(self: *Self, player: Player, i: u3, crit: bool, rate: u8) Error!void {
358
280
  if (!enabled) return;
359
281
 
360
282
  const n = if (crit) rate else @as(u8, @intCast(256 - @as(u9, rate)));
361
283
  try self.probability.update(n, 256);
362
- self.actions.get(player).critical_hit = if (crit) .true else .false;
284
+ var a = self.actions.get(player);
285
+ a.critical_hits = Criticals.set(a.critical_hits, i, if (crit) .true else .false);
363
286
  }
364
287
 
365
- pub fn damage(self: *Self, player: Player, roll: u8) Error!void {
288
+ pub fn damage(self: *Self, player: Player, i: u3, roll: u8) Error!void {
366
289
  if (!enabled) return;
367
290
 
368
291
  try self.probability.update(1, 39);
369
- self.actions.get(player).damage = roll;
292
+ var a = self.actions.get(player);
293
+ a.damages = Damages.set(a.damages, i, roll);
370
294
  }
371
295
 
372
296
  pub fn confused(self: *Self, player: Player, cfz: bool) Error!void {
@@ -518,122 +442,10 @@ pub fn Chance(comptime Rational: type) type {
518
442
  self.actions.get(player).multi_hit = n;
519
443
  }
520
444
 
521
- pub fn duration(
522
- self: *Self,
523
- comptime field: Duration.Field,
524
- player: Player,
525
- target: Player,
526
- turns: u4,
527
- ) void {
528
- if (!enabled) return;
529
-
530
- var durations = &self.actions.get(target).durations;
531
- assert(@field(durations, @tagName(field)) == 0 or
532
- (field == .confusion and player == target));
533
- @field(durations, @tagName(field)) = 1;
534
- if (field == .confusion) durations.thrash = player == target;
535
- self.actions.get(player).duration = if (options.key) 1 else turns;
536
- }
537
-
538
- pub fn sleep(self: *Self, player: Player, turns: u4) Error!void {
445
+ pub fn duration(self: *Self, player: Player, turns: u3) void {
539
446
  if (!enabled) return;
540
447
 
541
- var durations = &self.actions.get(player).durations;
542
- const n = durations.sleep;
543
- if (turns == 0) {
544
- assert(n >= 1 and n <= 6);
545
- if (n != 6) try self.probability.update(1, 7 - @as(u4, n));
546
- durations.sleep = 0;
547
- } else {
548
- assert(n >= 1 and n < 6);
549
- try self.probability.update(7 - @as(u4, n) - 1, 7 - @as(u4, n));
550
- durations.sleep += 1;
551
- }
552
- }
553
-
554
- pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
555
- if (!enabled) return;
556
-
557
- var durations = &self.actions.get(player).durations;
558
- const n = durations.confusion;
559
- const hi: u8 = if (durations.thrash) 3 else 5;
560
- const p = hi + 1;
561
- if (turns == 0) {
562
- assert(n >= 2 and n <= hi);
563
- if (n != hi) try self.probability.update(1, p - @as(u4, n));
564
- durations.confusion = 0;
565
- } else {
566
- assert(n >= 1 and n < hi);
567
- if (n > 1) try self.probability.update(p - @as(u4, n) - 1, p - @as(u4, n));
568
- durations.confusion += 1;
569
- }
570
- }
571
-
572
- pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
573
- if (!enabled) return;
574
-
575
- var durations = &self.actions.get(player).durations;
576
- const n = durations.disable;
577
- const hi = if (showdown) 5 else 8;
578
- const p = hi + 1;
579
- if (turns == 0) {
580
- assert(n >= 2 and n <= hi);
581
- if (n != hi) try self.probability.update(1, p - n);
582
- durations.disable = 0;
583
- } else {
584
- assert(n >= 1 and n < hi);
585
- if (n > 1) try self.probability.update(p - n - 1, p - n);
586
- durations.disable += 1;
587
- }
588
- }
589
-
590
- pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
591
- if (!enabled) return;
592
-
593
- var durations = &self.actions.get(player).durations;
594
- const n = durations.attacking;
595
- if (turns == 0) {
596
- assert(n >= 2 and n <= 3);
597
- if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
598
- durations.attacking = 0;
599
- } else {
600
- assert(n >= 1 and n < 3);
601
- if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
602
- durations.attacking += 1;
603
- }
604
- }
605
-
606
- pub fn binding(self: *Self, player: Player, turns: u4) Error!void {
607
- if (!enabled) return;
608
-
609
- var durations = &self.actions.get(player).durations;
610
- const n = durations.binding;
611
- if (turns == 0) {
612
- assert(n >= 2 and n <= 5);
613
- if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
614
- durations.binding = 0;
615
- } else {
616
- assert(n >= 1 and n < 5);
617
- if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
618
- durations.binding += 1;
619
- }
620
- }
621
-
622
- // TODO: consider sharing implementation with binding
623
- pub fn encore(self: *Self, player: Player, turns: u4) Error!void {
624
- if (!enabled) return;
625
-
626
- var durations = &self.actions.get(player).durations;
627
- const n = durations.encore;
628
- if (turns == 0) {
629
- assert(n >= 2 and n <= 5);
630
- if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
631
- durations.encore = 0;
632
- } else {
633
- assert(n >= 1 and n < 5);
634
- if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
635
- durations.encore += 1;
636
- }
448
+ self.actions.get(player).duration = if (options.key) 2 else turns;
637
449
  }
638
450
  };
639
451
  }
@@ -685,22 +497,22 @@ test "Chance.hit" {
685
497
  test "Chance.criticalHit" {
686
498
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
687
499
 
688
- try chance.criticalHit(.P1, true, 17);
689
- try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
500
+ try chance.criticalHit(.P1, 0, true, 17);
501
+ try expectValue(Optional(bool).true, Criticals.get(chance.actions.p1.critical_hits, 0));
690
502
  try expectProbability(&chance.probability, 17, 256);
691
503
 
692
504
  chance.reset();
693
505
 
694
- try chance.criticalHit(.P2, false, 5);
506
+ try chance.criticalHit(.P2, 1, false, 5);
695
507
  try expectProbability(&chance.probability, 251, 256);
696
- try expectValue(Optional(bool).false, chance.actions.p2.critical_hit);
508
+ try expectValue(Optional(bool).false, Criticals.get(chance.actions.p2.critical_hits, 1));
697
509
  }
698
510
 
699
511
  test "Chance.damage" {
700
512
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
701
513
 
702
- try chance.damage(.P1, 217);
703
- try expectValue(217, chance.actions.p1.damage);
514
+ try chance.damage(.P1, 0, 219);
515
+ try expectValue(@as(u8, 219), Damages.get(chance.actions.p1.damages, 0));
704
516
  try expectProbability(&chance.probability, 1, 39);
705
517
  }
706
518
 
@@ -825,7 +637,7 @@ test "Chance.present" {
825
637
  } else {
826
638
  try expectProbability(&chance.probability, 25, 256);
827
639
  }
828
- try expectValue(4, chance.actions.p2.present);
640
+ try expectValue(@as(u3, 4), chance.actions.p2.present);
829
641
 
830
642
  chance.reset();
831
643
 
@@ -835,7 +647,7 @@ test "Chance.present" {
835
647
  } else {
836
648
  try expectProbability(&chance.probability, 51, 256);
837
649
  }
838
- try expectValue(1, chance.actions.p1.present);
650
+ try expectValue(@as(u3, 1), chance.actions.p1.present);
839
651
  }
840
652
 
841
653
  test "Chance.magnitude" {
@@ -847,7 +659,7 @@ test "Chance.magnitude" {
847
659
  } else {
848
660
  try expectProbability(&chance.probability, 13, 256);
849
661
  }
850
- try expectValue(7, chance.actions.p2.magnitude);
662
+ try expectValue(@as(u3, 7), chance.actions.p2.magnitude);
851
663
 
852
664
  chance.reset();
853
665
 
@@ -857,7 +669,7 @@ test "Chance.magnitude" {
857
669
  } else {
858
670
  try expectProbability(&chance.probability, 51, 256);
859
671
  }
860
- try expectValue(5, chance.actions.p1.magnitude);
672
+ try expectValue(@as(u3, 5), chance.actions.p1.magnitude);
861
673
  }
862
674
 
863
675
  test "Chance.tripleKick" {
@@ -865,7 +677,7 @@ test "Chance.tripleKick" {
865
677
 
866
678
  try chance.tripleKick(.P1, 2);
867
679
  try expectProbability(&chance.probability, 1, 3);
868
- try expectValue(2, chance.actions.p1.triple_kick);
680
+ try expectValue(@as(u2, 2), chance.actions.p1.triple_kick);
869
681
  }
870
682
 
871
683
  test "Chance.spite" {
@@ -873,7 +685,7 @@ test "Chance.spite" {
873
685
 
874
686
  try chance.spite(.P1, 3);
875
687
  try expectProbability(&chance.probability, 1, 4);
876
- try expectValue(3, chance.actions.p1.spite);
688
+ try expectValue(@as(u3, 3), chance.actions.p1.spite);
877
689
  }
878
690
 
879
691
  test "Chance.conversion2" {
@@ -900,7 +712,7 @@ test "Chance.psywave" {
900
712
 
901
713
  try chance.psywave(.P2, 100, 150);
902
714
  try expectProbability(&chance.probability, 1, 150);
903
- try expectValue(100, chance.actions.p2.psywave);
715
+ try expectValue(@as(u8, 100), chance.actions.p2.psywave);
904
716
  }
905
717
 
906
718
  test "Chance.metronome" {
@@ -916,19 +728,19 @@ test "Chance.moveSlot" {
916
728
 
917
729
  try chance.moveSlot(.P2, 2, 2);
918
730
  try expectProbability(&chance.probability, 1, 2);
919
- try expectValue(2, chance.actions.p2.move_slot);
731
+ try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
920
732
 
921
733
  chance.reset();
922
734
 
923
735
  try chance.moveSlot(.P1, 1, 3);
924
736
  try expectProbability(&chance.probability, 1, 3);
925
- try expectValue(1, chance.actions.p1.move_slot);
737
+ try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
926
738
 
927
739
  chance.reset();
928
740
 
929
741
  try chance.moveSlot(.P1, 4, 1);
930
742
  try expectProbability(&chance.probability, 1, 1);
931
- try expectValue(4, chance.actions.p1.move_slot);
743
+ try expectValue(@as(u3, 4), chance.actions.p1.move_slot);
932
744
  }
933
745
 
934
746
  test "Chance.forceSwitch" {
@@ -936,19 +748,19 @@ test "Chance.forceSwitch" {
936
748
 
937
749
  try chance.forceSwitch(.P2, 2, 2);
938
750
  try expectProbability(&chance.probability, 1, 2);
939
- try expectValue(2, chance.actions.p2.force_switch);
751
+ try expectValue(@as(u3, 2), chance.actions.p2.force_switch);
940
752
 
941
753
  chance.reset();
942
754
 
943
755
  try chance.forceSwitch(.P1, 1, 3);
944
756
  try expectProbability(&chance.probability, 1, 3);
945
- try expectValue(1, chance.actions.p1.force_switch);
757
+ try expectValue(@as(u3, 1), chance.actions.p1.force_switch);
946
758
 
947
759
  chance.reset();
948
760
 
949
761
  try chance.forceSwitch(.P1, 4, 1);
950
762
  try expectProbability(&chance.probability, 1, 1);
951
- try expectValue(4, chance.actions.p1.force_switch);
763
+ try expectValue(@as(u3, 4), chance.actions.p1.force_switch);
952
764
  }
953
765
 
954
766
  test "Chance.multiHit" {
@@ -956,212 +768,20 @@ test "Chance.multiHit" {
956
768
 
957
769
  try chance.multiHit(.P1, 3);
958
770
  try expectProbability(&chance.probability, 3, 8);
959
- try expectValue(3, chance.actions.p1.multi_hit);
771
+ try expectValue(@as(u3, 3), chance.actions.p1.multi_hit);
960
772
 
961
773
  chance.reset();
962
774
 
963
775
  try chance.multiHit(.P2, 5);
964
776
  try expectProbability(&chance.probability, 1, 8);
965
- try expectValue(5, chance.actions.p2.multi_hit);
777
+ try expectValue(@as(u3, 5), chance.actions.p2.multi_hit);
966
778
  }
967
779
 
968
780
  test "Chance.duration" {
969
781
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
970
782
 
971
- chance.duration(.sleep, .P1, .P2, 2);
972
- try expectValue(2, chance.actions.p1.duration);
973
- try expectValue(1, chance.actions.p2.durations.sleep);
974
- }
975
-
976
- test "Chance.sleep" {
977
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
978
-
979
- for ([_]u8{ 6, 5, 4, 3, 2, 1 }, 1..7) |d, i| {
980
- chance.actions.p1.durations.sleep = @intCast(i);
981
- try chance.sleep(.P1, 0);
982
- try expectProbability(&chance.probability, 1, d);
983
- try expectValue(0, chance.actions.p1.durations.sleep);
984
-
985
- chance.reset();
986
-
987
- if (i < 6) {
988
- chance.actions.p1.durations.sleep = @intCast(i);
989
- try chance.sleep(.P1, 1);
990
- try expectProbability(&chance.probability, d - 1, d);
991
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p1.durations.sleep);
992
-
993
- chance.reset();
994
- }
995
- }
996
- }
997
-
998
- test "Chance.confusion" {
999
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1000
-
1001
- chance.actions.p2.durations.thrash = false;
1002
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1003
- if (i > 1) {
1004
- chance.actions.p2.durations.confusion = @intCast(i);
1005
- try chance.confusion(.P2, 0);
1006
- try expectProbability(&chance.probability, 1, d);
1007
- try expectValue(0, chance.actions.p2.durations.confusion);
1008
- try expectValue(false, chance.actions.p2.durations.thrash);
1009
-
1010
- chance.reset();
1011
- }
1012
-
1013
- if (i < 5) {
1014
- chance.actions.p2.durations.confusion = @intCast(i);
1015
- try chance.confusion(.P2, 1);
1016
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1017
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
1018
- try expectValue(false, chance.actions.p2.durations.thrash);
1019
-
1020
- chance.reset();
1021
- }
1022
- }
1023
-
1024
- chance.reset();
1025
-
1026
- chance.actions.p2.durations.thrash = true;
1027
- for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
1028
- if (i > 1) {
1029
- chance.actions.p2.durations.confusion = @intCast(i);
1030
- try chance.confusion(.P2, 0);
1031
- try expectProbability(&chance.probability, 1, d);
1032
- try expectValue(0, chance.actions.p2.durations.confusion);
1033
- try expectValue(true, chance.actions.p2.durations.thrash);
1034
-
1035
- chance.reset();
1036
- }
1037
-
1038
- if (i < 3) {
1039
- chance.actions.p2.durations.confusion = @intCast(i);
1040
- try chance.confusion(.P2, 1);
1041
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1042
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
1043
- try expectValue(true, chance.actions.p2.durations.thrash);
1044
-
1045
- chance.reset();
1046
- }
1047
- }
1048
- }
1049
-
1050
- test "Chance.disable" {
1051
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1052
- if (showdown) {
1053
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1054
- if (i > 1) {
1055
- chance.actions.p2.durations.disable = @intCast(i);
1056
- try chance.disable(.P2, 0);
1057
- try expectProbability(&chance.probability, 1, d);
1058
- try expectValue(0, chance.actions.p2.durations.disable);
1059
-
1060
- chance.reset();
1061
- }
1062
-
1063
- if (i < 5) {
1064
- chance.actions.p2.durations.disable = @intCast(i);
1065
- try chance.disable(.P2, 1);
1066
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1067
- try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations.disable);
1068
-
1069
- chance.reset();
1070
- }
1071
- }
1072
- } else {
1073
- for ([_]u8{ 1, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
1074
- if (i > 1) {
1075
- chance.actions.p2.durations.disable = @intCast(i);
1076
- try chance.disable(.P2, 0);
1077
- try expectProbability(&chance.probability, 1, d);
1078
- try expectValue(0, chance.actions.p2.durations.disable);
1079
-
1080
- chance.reset();
1081
- }
1082
-
1083
- if (i < 8) {
1084
- chance.actions.p2.durations.disable = @intCast(i);
1085
- try chance.disable(.P2, 1);
1086
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1087
- try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations.disable);
1088
-
1089
- chance.reset();
1090
- }
1091
- }
1092
- }
1093
- }
1094
-
1095
- test "Chance.attacking" {
1096
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1097
-
1098
- for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
1099
- if (i > 1) {
1100
- chance.actions.p2.durations.attacking = @intCast(i);
1101
- try chance.attacking(.P2, 0);
1102
- try expectProbability(&chance.probability, 1, d);
1103
- try expectValue(0, chance.actions.p2.durations.attacking);
1104
-
1105
- chance.reset();
1106
- }
1107
-
1108
- if (i < 3) {
1109
- chance.actions.p2.durations.attacking = @intCast(i);
1110
- try chance.attacking(.P2, 1);
1111
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1112
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.attacking);
1113
-
1114
- chance.reset();
1115
- }
1116
- }
1117
- }
1118
-
1119
- test "Chance.binding" {
1120
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1121
-
1122
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1123
- if (i > 1) {
1124
- chance.actions.p2.durations.binding = @intCast(i);
1125
- try chance.binding(.P2, 0);
1126
- try expectProbability(&chance.probability, 1, d);
1127
- try expectValue(0, chance.actions.p2.durations.binding);
1128
-
1129
- chance.reset();
1130
- }
1131
-
1132
- if (i < 5) {
1133
- chance.actions.p2.durations.binding = @intCast(i);
1134
- try chance.binding(.P2, 1);
1135
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1136
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.binding);
1137
-
1138
- chance.reset();
1139
- }
1140
- }
1141
- }
1142
-
1143
- test "Chance.encore" {
1144
- var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
1145
-
1146
- for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
1147
- if (i > 1) {
1148
- chance.actions.p2.durations.encore = @intCast(i);
1149
- try chance.encore(.P2, 0);
1150
- try expectProbability(&chance.probability, 1, d);
1151
- try expectValue(0, chance.actions.p2.durations.encore);
1152
-
1153
- chance.reset();
1154
- }
1155
-
1156
- if (i < 5) {
1157
- chance.actions.p2.durations.encore = @intCast(i);
1158
- try chance.encore(.P2, 1);
1159
- try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
1160
- try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.encore);
1161
-
1162
- chance.reset();
1163
- }
1164
- }
783
+ chance.duration(.P1, 2);
784
+ try expectValue(@as(u3, 2), chance.actions.p1.duration);
1165
785
  }
1166
786
 
1167
787
  pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
@@ -1203,12 +823,12 @@ const Null = struct {
1203
823
  _ = .{ self, player, ok, accuracy };
1204
824
  }
1205
825
 
1206
- pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
1207
- _ = .{ self, player, crit, rate };
826
+ pub fn criticalHit(self: Null, player: Player, i: u3, crit: bool, rate: u8) Error!void {
827
+ _ = .{ self, player, i, crit, rate };
1208
828
  }
1209
829
 
1210
- pub fn damage(self: Null, player: Player, roll: u8) Error!void {
1211
- _ = .{ self, player, roll };
830
+ pub fn damage(self: Null, player: Player, i: u3, roll: u8) Error!void {
831
+ _ = .{ self, player, i, roll };
1212
832
  }
1213
833
 
1214
834
  pub fn confused(self: Null, player: Player, ok: bool) Error!void {
@@ -1279,37 +899,7 @@ const Null = struct {
1279
899
  _ = .{ self, player, move, n };
1280
900
  }
1281
901
 
1282
- pub fn duration(
1283
- self: Null,
1284
- comptime field: Duration.Field,
1285
- player: Player,
1286
- target: Player,
1287
- turns: u4,
1288
- ) void {
1289
- _ = .{ self, field, player, target, turns };
1290
- }
1291
-
1292
- pub fn sleep(self: Null, player: Player, turns: u4) Error!void {
1293
- _ = .{ self, player, turns };
1294
- }
1295
-
1296
- pub fn confusion(self: Null, player: Player, turns: u4) Error!void {
1297
- _ = .{ self, player, turns };
1298
- }
1299
-
1300
- pub fn disable(self: Null, player: Player, turns: u4) Error!void {
1301
- _ = .{ self, player, turns };
1302
- }
1303
-
1304
- pub fn attacking(self: Null, player: Player, turns: u4) Error!void {
1305
- _ = .{ self, player, turns };
1306
- }
1307
-
1308
- pub fn binding(self: Null, player: Player, turns: u4) Error!void {
1309
- _ = .{ self, player, turns };
1310
- }
1311
-
1312
- pub fn encore(self: Null, player: Player, turns: u4) Error!void {
902
+ pub fn duration(self: Null, player: Player, turns: u3) void {
1313
903
  _ = .{ self, player, turns };
1314
904
  }
1315
905
  };