@pkmn/engine 0.1.0-dev.dd5b9081 → 0.1.0-dev.deda8593
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.
- package/LICENSE +1 -3
- package/README.md +44 -19
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +2 -2
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +2 -2
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +61 -42
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +5 -4
- package/build/tools/debug.js.map +1 -1
- package/build.zig +102 -104
- package/package.json +43 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +9 -9
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +9 -2
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +739 -318
- package/src/lib/common/rational.zig +12 -9
- package/src/lib/common/rng.zig +25 -16
- package/src/lib/gen1/calc.zig +219 -49
- package/src/lib/gen1/chance.zig +138 -115
- package/src/lib/gen1/data/moves.zig +190 -176
- package/src/lib/gen1/data/species.zig +3 -3
- package/src/lib/gen1/data/types.zig +7 -7
- package/src/lib/gen1/data.zig +44 -44
- package/src/lib/gen1/helpers.zig +148 -82
- package/src/lib/gen1/mechanics.zig +392 -406
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1315 -0
- package/src/lib/gen2/data/items.zig +12 -18
- package/src/lib/gen2/data/moves.zig +377 -344
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +134 -31
- package/src/lib/gen2/data.zig +52 -44
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +3086 -211
- package/src/lib/{binding/node.zig → node.zig} +10 -13
- package/src/lib/pkmn.zig +27 -12
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -383
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
52
|
+
pub fn eql(a: Actions, b: Actions) bool {
|
|
51
53
|
return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -90,13 +92,14 @@ pub const Actions = extern struct {
|
|
|
90
92
|
}
|
|
91
93
|
};
|
|
92
94
|
|
|
95
|
+
// TODO: ziglang/zig#19730
|
|
93
96
|
test Actions {
|
|
94
97
|
const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
|
|
95
98
|
const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
|
|
96
99
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
97
|
-
const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
98
|
-
const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
99
|
-
const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
100
|
+
// const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
101
|
+
// const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
102
|
+
// const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
100
103
|
|
|
101
104
|
try expect(a.eql(a));
|
|
102
105
|
try expect(!a.eql(b));
|
|
@@ -109,8 +112,8 @@ test Actions {
|
|
|
109
112
|
try expect(b.matches(a));
|
|
110
113
|
try expect(!a.matches(c));
|
|
111
114
|
try expect(!c.matches(a));
|
|
112
|
-
try expect(d.matches(e));
|
|
113
|
-
try expect(!d.matches(f));
|
|
115
|
+
// try expect(d.matches(e));
|
|
116
|
+
// try expect(!d.matches(f));
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
@@ -157,7 +160,7 @@ pub const Action = packed struct(u64) {
|
|
|
157
160
|
pub const Field = std.meta.FieldEnum(Action);
|
|
158
161
|
|
|
159
162
|
/// Perform a reset by clearing fields which should not persist across updates.
|
|
160
|
-
pub
|
|
163
|
+
pub fn reset(self: *Action) void {
|
|
161
164
|
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
162
165
|
}
|
|
163
166
|
|
|
@@ -215,7 +218,7 @@ pub const Action = packed struct(u64) {
|
|
|
215
218
|
};
|
|
216
219
|
|
|
217
220
|
test Action {
|
|
218
|
-
var a =
|
|
221
|
+
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
219
222
|
a.reset();
|
|
220
223
|
|
|
221
224
|
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
@@ -252,6 +255,8 @@ pub const Duration = packed struct(u16) {
|
|
|
252
255
|
|
|
253
256
|
pub const Commit = enum { hit, miss, binding };
|
|
254
257
|
|
|
258
|
+
const uN = if (options.miss) u8 else u9;
|
|
259
|
+
|
|
255
260
|
/// Tracks chance actions and their associated probability during a Generation I battle update when
|
|
256
261
|
/// `options.chance` is enabled.
|
|
257
262
|
pub fn Chance(comptime Rational: type) type {
|
|
@@ -288,15 +293,12 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
288
293
|
//
|
|
289
294
|
// Finally, because each player's actions are processed sequentially we only need a single
|
|
290
295
|
// shared structure to track this information (though it needs to be cleared appropriately)
|
|
291
|
-
pending: if (showdown) struct {
|
|
292
|
-
hit: bool = false,
|
|
293
|
-
hit_probablity: u8 = 0,
|
|
294
|
-
} else struct {
|
|
296
|
+
pending: if (showdown) struct {} else struct {
|
|
295
297
|
crit: bool = false,
|
|
296
298
|
crit_probablity: u8 = 0,
|
|
297
299
|
damage_roll: u8 = 0,
|
|
298
300
|
hit: bool = false,
|
|
299
|
-
hit_probablity:
|
|
301
|
+
hit_probablity: uN = 0,
|
|
300
302
|
binding: u8 = 0,
|
|
301
303
|
} = .{},
|
|
302
304
|
|
|
@@ -305,25 +307,29 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
305
307
|
|
|
306
308
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
307
309
|
pub fn reset(self: *Self) void {
|
|
310
|
+
if (!enabled) return;
|
|
311
|
+
|
|
308
312
|
self.probability.reset();
|
|
309
313
|
self.actions.reset();
|
|
310
314
|
self.pending = .{};
|
|
311
315
|
}
|
|
312
316
|
|
|
313
317
|
pub fn commit(self: *Self, player: Player, kind: Commit) Error!void {
|
|
318
|
+
if (!enabled) return;
|
|
319
|
+
|
|
320
|
+
assert(!showdown);
|
|
321
|
+
|
|
314
322
|
var action = self.actions.get(player);
|
|
315
323
|
|
|
316
324
|
// We need to handle binding specially because we still need to commit it when attacking
|
|
317
325
|
// into a Pokémon that is immune
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (kind == .binding) return;
|
|
326
|
-
} else assert(kind != .binding);
|
|
326
|
+
if (kind != .miss and self.pending.binding != 0) {
|
|
327
|
+
assert(action.duration == 0);
|
|
328
|
+
action.duration = @intCast(self.pending.binding);
|
|
329
|
+
assert(action.durations.binding == 0);
|
|
330
|
+
action.durations.binding = 1;
|
|
331
|
+
}
|
|
332
|
+
if (kind == .binding) return;
|
|
327
333
|
|
|
328
334
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
329
335
|
// target is immune/behind a sub/etc)
|
|
@@ -332,19 +338,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
332
338
|
action.hit = if (self.pending.hit) .true else .false;
|
|
333
339
|
}
|
|
334
340
|
|
|
335
|
-
|
|
341
|
+
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
342
|
+
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
343
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't rol for
|
|
344
|
+
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
345
|
+
// passed to the critical hit helper)
|
|
346
|
+
if (kind != .hit) return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
336
347
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
342
|
-
|
|
343
|
-
if (self.pending.crit_probablity != 0) {
|
|
344
|
-
try self.probability.update(self.pending.crit_probablity, 256);
|
|
345
|
-
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
346
|
-
}
|
|
348
|
+
if (self.pending.crit_probablity != 0) {
|
|
349
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
350
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
351
|
+
}
|
|
347
352
|
|
|
353
|
+
if (self.pending.damage_roll > 0) {
|
|
348
354
|
try self.probability.update(1, 39);
|
|
349
355
|
action.damage = self.pending.damage_roll;
|
|
350
356
|
}
|
|
@@ -395,12 +401,17 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
395
401
|
self.actions.p2.speed_tie = self.actions.p1.speed_tie;
|
|
396
402
|
}
|
|
397
403
|
|
|
398
|
-
pub fn hit(self: *Self, ok: bool, accuracy:
|
|
404
|
+
pub fn hit(self: *Self, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
399
405
|
if (!enabled) return;
|
|
400
406
|
|
|
401
407
|
const p = if (ok) accuracy else @as(u8, @intCast(256 - @as(u9, accuracy)));
|
|
402
|
-
|
|
403
|
-
|
|
408
|
+
if (showdown) {
|
|
409
|
+
try self.probability.update(p, 256);
|
|
410
|
+
self.actions.get(player).hit = if (ok) .true else .false;
|
|
411
|
+
} else {
|
|
412
|
+
self.pending.hit = ok;
|
|
413
|
+
self.pending.hit_probablity = p;
|
|
414
|
+
}
|
|
404
415
|
}
|
|
405
416
|
|
|
406
417
|
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -514,22 +525,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
514
525
|
}
|
|
515
526
|
}
|
|
516
527
|
|
|
517
|
-
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
518
|
-
if (!enabled) return;
|
|
519
|
-
|
|
520
|
-
var durations = &self.actions.get(player).durations;
|
|
521
|
-
const n = durations.disable;
|
|
522
|
-
if (turns == 0) {
|
|
523
|
-
assert(n >= 1 and n <= 8);
|
|
524
|
-
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
525
|
-
durations.disable = 0;
|
|
526
|
-
} else {
|
|
527
|
-
assert(n >= 1 and n < 8);
|
|
528
|
-
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
529
|
-
durations.disable += 1;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
528
|
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
534
529
|
if (!enabled) return;
|
|
535
530
|
|
|
@@ -546,6 +541,22 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
546
541
|
}
|
|
547
542
|
}
|
|
548
543
|
|
|
544
|
+
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
545
|
+
if (!enabled) return;
|
|
546
|
+
|
|
547
|
+
var durations = &self.actions.get(player).durations;
|
|
548
|
+
const n = durations.disable;
|
|
549
|
+
if (turns == 0) {
|
|
550
|
+
assert(n >= 1 and n <= 8);
|
|
551
|
+
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
552
|
+
durations.disable = 0;
|
|
553
|
+
} else {
|
|
554
|
+
assert(n >= 1 and n < 8);
|
|
555
|
+
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
556
|
+
durations.disable += 1;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
549
560
|
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
550
561
|
if (!enabled) return;
|
|
551
562
|
|
|
@@ -593,7 +604,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
593
604
|
pub fn metronome(self: *Self, player: Player, move: Move) Error!void {
|
|
594
605
|
if (!enabled) return;
|
|
595
606
|
|
|
596
|
-
try self.probability.update(1, Move.
|
|
607
|
+
try self.probability.update(1, Move.METRONOME.len);
|
|
597
608
|
self.actions.get(player).metronome = move;
|
|
598
609
|
}
|
|
599
610
|
};
|
|
@@ -618,19 +629,25 @@ test "Chance.speedTie" {
|
|
|
618
629
|
test "Chance.hit" {
|
|
619
630
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
620
631
|
|
|
621
|
-
chance.hit(true, 229);
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
632
|
+
try chance.hit(.P1, true, 229);
|
|
633
|
+
if (!showdown) {
|
|
634
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
635
|
+
try expectValue(Optional(bool).None, chance.actions.p1.hit);
|
|
636
|
+
|
|
637
|
+
try chance.commit(.P1, .hit);
|
|
638
|
+
}
|
|
625
639
|
try expectValue(Optional(bool).true, chance.actions.p1.hit);
|
|
626
640
|
try expectProbability(&chance.probability, 229, 256);
|
|
627
641
|
|
|
628
642
|
chance.reset();
|
|
629
643
|
|
|
630
|
-
chance.hit(false, 255);
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
644
|
+
try chance.hit(.P2, false, 255);
|
|
645
|
+
if (!showdown) {
|
|
646
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
647
|
+
try expectValue(Optional(bool).None, chance.actions.p2.hit);
|
|
648
|
+
|
|
649
|
+
try chance.commit(.P2, .miss);
|
|
650
|
+
}
|
|
634
651
|
try expectValue(Optional(bool).false, chance.actions.p2.hit);
|
|
635
652
|
try expectProbability(&chance.probability, 1, 256);
|
|
636
653
|
}
|
|
@@ -646,26 +663,18 @@ test "Chance.criticalHit" {
|
|
|
646
663
|
try expectProbability(&chance.probability, 1, 1);
|
|
647
664
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
648
665
|
|
|
649
|
-
try chance.
|
|
650
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
651
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
666
|
+
try chance.hit(.P1, true, 128);
|
|
652
667
|
|
|
653
|
-
chance.
|
|
654
|
-
|
|
655
|
-
try chance.commit(.P1, .hit);
|
|
656
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
668
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
657
669
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
658
670
|
|
|
659
|
-
chance.probability.reset();
|
|
660
671
|
try chance.damage(.P1, 217);
|
|
661
672
|
|
|
662
|
-
try chance.
|
|
663
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
673
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
664
674
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
665
675
|
|
|
666
|
-
chance.probability.reset();
|
|
667
|
-
|
|
668
676
|
try chance.commit(.P1, .hit);
|
|
677
|
+
|
|
669
678
|
try expectProbability(&chance.probability, 17, 19968); // (1/2) * (17/256) * (1/39)
|
|
670
679
|
try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
|
|
671
680
|
}
|
|
@@ -680,20 +689,14 @@ test "Chance.criticalHit" {
|
|
|
680
689
|
try expectProbability(&chance.probability, 1, 1);
|
|
681
690
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
682
691
|
|
|
683
|
-
try chance.
|
|
684
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
685
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
686
|
-
|
|
687
|
-
chance.hit(true, 128);
|
|
692
|
+
try chance.hit(.P1, true, 128);
|
|
688
693
|
|
|
689
|
-
try chance.
|
|
690
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
694
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
691
695
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
692
696
|
|
|
693
|
-
chance.probability.reset();
|
|
694
697
|
try chance.damage(.P1, 217);
|
|
695
|
-
|
|
696
698
|
try chance.commit(.P1, .hit);
|
|
699
|
+
|
|
697
700
|
try expectProbability(&chance.probability, 251, 19968); // (1/2) * (251/256) * (1/39)
|
|
698
701
|
try expectValue(Optional(bool).false, chance.actions.p1.critical_hit);
|
|
699
702
|
}
|
|
@@ -704,19 +707,16 @@ test "Chance.damage" {
|
|
|
704
707
|
|
|
705
708
|
try chance.damage(.P1, 217);
|
|
706
709
|
if (showdown) {
|
|
707
|
-
try expectValue(
|
|
710
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
708
711
|
try expectProbability(&chance.probability, 1, 39);
|
|
709
712
|
} else {
|
|
710
713
|
try expectProbability(&chance.probability, 1, 1);
|
|
711
|
-
try expectValue(
|
|
712
|
-
|
|
713
|
-
try chance.commit(.P1, .miss);
|
|
714
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
715
|
-
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
714
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
716
715
|
|
|
717
716
|
try chance.commit(.P1, .hit);
|
|
717
|
+
|
|
718
718
|
try expectProbability(&chance.probability, 1, 39);
|
|
719
|
-
try expectValue(
|
|
719
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
720
720
|
}
|
|
721
721
|
}
|
|
722
722
|
|
|
@@ -768,13 +768,13 @@ test "Chance.moveSlot" {
|
|
|
768
768
|
|
|
769
769
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
770
770
|
try expectProbability(&chance.probability, 1, 2);
|
|
771
|
-
try expectValue(
|
|
771
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
772
772
|
|
|
773
773
|
chance.reset();
|
|
774
774
|
|
|
775
775
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
776
776
|
try expectProbability(&chance.probability, 1, 3);
|
|
777
|
-
try expectValue(
|
|
777
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
778
778
|
}
|
|
779
779
|
|
|
780
780
|
test "Chance.multiHit" {
|
|
@@ -782,32 +782,33 @@ test "Chance.multiHit" {
|
|
|
782
782
|
|
|
783
783
|
try chance.multiHit(.P1, 3);
|
|
784
784
|
try expectProbability(&chance.probability, 3, 8);
|
|
785
|
-
try expectValue(
|
|
785
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
786
786
|
|
|
787
787
|
chance.reset();
|
|
788
788
|
|
|
789
789
|
try chance.multiHit(.P2, 5);
|
|
790
790
|
try expectProbability(&chance.probability, 1, 8);
|
|
791
|
-
try expectValue(
|
|
791
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
792
792
|
}
|
|
793
793
|
|
|
794
794
|
test "Chance.duration" {
|
|
795
795
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
796
796
|
|
|
797
797
|
chance.duration(.sleep, .P1, .P2, 2);
|
|
798
|
-
try expectValue(
|
|
799
|
-
try expectValue(
|
|
798
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
799
|
+
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
800
800
|
|
|
801
801
|
chance.reset();
|
|
802
802
|
|
|
803
803
|
chance.duration(.binding, .P2, .P2, 4);
|
|
804
804
|
if (!showdown) {
|
|
805
|
-
try expectValue(
|
|
806
|
-
try expectValue(
|
|
805
|
+
try expectValue(0, chance.actions.p2.duration);
|
|
806
|
+
try expectValue(0, chance.actions.p2.durations.binding);
|
|
807
|
+
|
|
807
808
|
try chance.commit(.P2, .hit);
|
|
808
809
|
}
|
|
809
|
-
try expectValue(
|
|
810
|
-
try expectValue(
|
|
810
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
811
|
+
try expectValue(1, chance.actions.p2.durations.binding);
|
|
811
812
|
}
|
|
812
813
|
|
|
813
814
|
test "Chance.sleep" {
|
|
@@ -817,7 +818,7 @@ test "Chance.sleep" {
|
|
|
817
818
|
chance.actions.p1.durations.sleep = @intCast(i);
|
|
818
819
|
try chance.sleep(.P1, 0);
|
|
819
820
|
try expectProbability(&chance.probability, 1, d);
|
|
820
|
-
try expectValue(
|
|
821
|
+
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
821
822
|
|
|
822
823
|
chance.reset();
|
|
823
824
|
|
|
@@ -840,7 +841,7 @@ test "Chance.confusion" {
|
|
|
840
841
|
chance.actions.p2.durations.confusion = @intCast(i);
|
|
841
842
|
try chance.confusion(.P2, 0);
|
|
842
843
|
try expectProbability(&chance.probability, 1, d);
|
|
843
|
-
try expectValue(
|
|
844
|
+
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
844
845
|
|
|
845
846
|
chance.reset();
|
|
846
847
|
}
|
|
@@ -856,6 +857,28 @@ test "Chance.confusion" {
|
|
|
856
857
|
}
|
|
857
858
|
}
|
|
858
859
|
|
|
860
|
+
test "Chance.disable" {
|
|
861
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
862
|
+
|
|
863
|
+
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
864
|
+
chance.actions.p1.durations.disable = @intCast(i);
|
|
865
|
+
try chance.disable(.P1, 0);
|
|
866
|
+
try expectProbability(&chance.probability, 1, d);
|
|
867
|
+
try expectValue(0, chance.actions.p1.durations.disable);
|
|
868
|
+
|
|
869
|
+
chance.reset();
|
|
870
|
+
|
|
871
|
+
if (i < 8) {
|
|
872
|
+
chance.actions.p1.durations.disable = @intCast(i);
|
|
873
|
+
try chance.disable(.P1, 1);
|
|
874
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
875
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p1.durations.disable);
|
|
876
|
+
|
|
877
|
+
chance.reset();
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
859
882
|
test "Chance.attacking" {
|
|
860
883
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
861
884
|
|
|
@@ -864,7 +887,7 @@ test "Chance.attacking" {
|
|
|
864
887
|
chance.actions.p2.durations.attacking = @intCast(i);
|
|
865
888
|
try chance.attacking(.P2, 0);
|
|
866
889
|
try expectProbability(&chance.probability, 1, d);
|
|
867
|
-
try expectValue(
|
|
890
|
+
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
868
891
|
|
|
869
892
|
chance.reset();
|
|
870
893
|
}
|
|
@@ -890,7 +913,7 @@ test "Chance.binding" {
|
|
|
890
913
|
chance.actions.p1.durations.binding = @intCast(i);
|
|
891
914
|
try chance.binding(.P1, 0);
|
|
892
915
|
try expectProbability(&chance.probability, p, q);
|
|
893
|
-
try expectValue(
|
|
916
|
+
try expectValue(0, chance.actions.p1.durations.binding);
|
|
894
917
|
|
|
895
918
|
chance.reset();
|
|
896
919
|
|
|
@@ -910,7 +933,7 @@ test "Chance.psywave" {
|
|
|
910
933
|
|
|
911
934
|
try chance.psywave(.P2, 100, 150);
|
|
912
935
|
try expectProbability(&chance.probability, 1, 150);
|
|
913
|
-
try expectValue(
|
|
936
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
914
937
|
}
|
|
915
938
|
|
|
916
939
|
test "Chance.metronome" {
|
|
@@ -939,7 +962,7 @@ pub fn expectValue(a: anytype, b: anytype) !void {
|
|
|
939
962
|
|
|
940
963
|
/// Null object pattern implementation of Generation I `Chance` which does nothing, though chance
|
|
941
964
|
/// tracking should additionally be turned off entirely via `options.chance`.
|
|
942
|
-
pub const NULL =
|
|
965
|
+
pub const NULL: Null = .{};
|
|
943
966
|
|
|
944
967
|
const Null = struct {
|
|
945
968
|
pub const Error = error{};
|
|
@@ -964,8 +987,8 @@ const Null = struct {
|
|
|
964
987
|
_ = .{ self, p1 };
|
|
965
988
|
}
|
|
966
989
|
|
|
967
|
-
pub fn hit(self: Null, ok: bool, accuracy:
|
|
968
|
-
_ = .{ self, ok, accuracy };
|
|
990
|
+
pub fn hit(self: Null, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
991
|
+
_ = .{ self, player, ok, accuracy };
|
|
969
992
|
}
|
|
970
993
|
|
|
971
994
|
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -976,10 +999,6 @@ const Null = struct {
|
|
|
976
999
|
_ = .{ self, player, roll };
|
|
977
1000
|
}
|
|
978
1001
|
|
|
979
|
-
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
980
|
-
_ = .{ self, player, proc, rate };
|
|
981
|
-
}
|
|
982
|
-
|
|
983
1002
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
984
1003
|
_ = .{ self, player, ok };
|
|
985
1004
|
}
|
|
@@ -988,6 +1007,10 @@ const Null = struct {
|
|
|
988
1007
|
_ = .{ self, player, ok };
|
|
989
1008
|
}
|
|
990
1009
|
|
|
1010
|
+
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
1011
|
+
_ = .{ self, player, proc, rate };
|
|
1012
|
+
}
|
|
1013
|
+
|
|
991
1014
|
pub fn moveSlot(self: Null, player: Player, slot: u4, ms: []const MoveSlot, n: u4) Error!void {
|
|
992
1015
|
_ = .{ self, player, slot, ms, n };
|
|
993
1016
|
}
|