@pkmn/engine 0.1.0-dev.6158d5ad → 0.1.0-dev.62399ed5
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 +12 -24
- 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 +802 -327
- package/src/lib/common/rational.zig +113 -19
- package/src/lib/common/rng.zig +26 -17
- package/src/lib/gen1/calc.zig +219 -50
- package/src/lib/gen1/chance.zig +140 -125
- package/src/lib/gen1/data/moves.zig +192 -180
- package/src/lib/gen1/data/species.zig +3 -3
- package/src/lib/gen1/data/types.zig +7 -7
- package/src/lib/gen1/data.zig +50 -62
- package/src/lib/gen1/helpers.zig +148 -82
- package/src/lib/gen1/mechanics.zig +403 -417
- 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 +663 -569
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +134 -31
- package/src/lib/gen2/data.zig +106 -79
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +45 -19
- package/src/lib/gen2/mechanics.zig +3587 -9
- 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 -892
- package/src/lib/gen2/README.md +0 -308
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,13 +112,13 @@ 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
|
|
117
120
|
/// single player.
|
|
118
|
-
pub const Action = packed struct {
|
|
121
|
+
pub const Action = packed struct(u64) {
|
|
119
122
|
/// If not 0, the roll to be returned Rolls.damage.
|
|
120
123
|
damage: u8 = 0,
|
|
121
124
|
|
|
@@ -156,12 +159,8 @@ pub const Action = packed struct {
|
|
|
156
159
|
|
|
157
160
|
pub const Field = std.meta.FieldEnum(Action);
|
|
158
161
|
|
|
159
|
-
comptime {
|
|
160
|
-
assert(@sizeOf(Action) == 8);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
162
|
/// Perform a reset by clearing fields which should not persist across updates.
|
|
164
|
-
pub
|
|
163
|
+
pub fn reset(self: *Action) void {
|
|
165
164
|
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
166
165
|
}
|
|
167
166
|
|
|
@@ -219,7 +218,7 @@ pub const Action = packed struct {
|
|
|
219
218
|
};
|
|
220
219
|
|
|
221
220
|
test Action {
|
|
222
|
-
var a =
|
|
221
|
+
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
223
222
|
a.reset();
|
|
224
223
|
|
|
225
224
|
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
@@ -239,7 +238,7 @@ pub const Durations = extern struct {
|
|
|
239
238
|
|
|
240
239
|
/// Observed values for various durations that need to be tracked in order to properly
|
|
241
240
|
/// deduplicate transitions with a primary key. TODO
|
|
242
|
-
pub const Duration = packed struct {
|
|
241
|
+
pub const Duration = packed struct(u16) {
|
|
243
242
|
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
244
243
|
sleep: u3 = 0,
|
|
245
244
|
/// The number of turns a Pokémon has been observed to be confused.
|
|
@@ -252,14 +251,12 @@ pub const Duration = packed struct {
|
|
|
252
251
|
binding: u3 = 0,
|
|
253
252
|
|
|
254
253
|
pub const Field = std.meta.FieldEnum(Duration);
|
|
255
|
-
|
|
256
|
-
comptime {
|
|
257
|
-
assert(@sizeOf(Duration) == 2);
|
|
258
|
-
}
|
|
259
254
|
};
|
|
260
255
|
|
|
261
256
|
pub const Commit = enum { hit, miss, binding };
|
|
262
257
|
|
|
258
|
+
const uN = if (options.miss) u8 else u9;
|
|
259
|
+
|
|
263
260
|
/// Tracks chance actions and their associated probability during a Generation I battle update when
|
|
264
261
|
/// `options.chance` is enabled.
|
|
265
262
|
pub fn Chance(comptime Rational: type) type {
|
|
@@ -296,15 +293,12 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
296
293
|
//
|
|
297
294
|
// Finally, because each player's actions are processed sequentially we only need a single
|
|
298
295
|
// shared structure to track this information (though it needs to be cleared appropriately)
|
|
299
|
-
pending: if (showdown) struct {
|
|
300
|
-
hit: bool = false,
|
|
301
|
-
hit_probablity: u8 = 0,
|
|
302
|
-
} else struct {
|
|
296
|
+
pending: if (showdown) struct {} else struct {
|
|
303
297
|
crit: bool = false,
|
|
304
298
|
crit_probablity: u8 = 0,
|
|
305
299
|
damage_roll: u8 = 0,
|
|
306
300
|
hit: bool = false,
|
|
307
|
-
hit_probablity:
|
|
301
|
+
hit_probablity: uN = 0,
|
|
308
302
|
binding: u8 = 0,
|
|
309
303
|
} = .{},
|
|
310
304
|
|
|
@@ -313,25 +307,29 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
313
307
|
|
|
314
308
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
315
309
|
pub fn reset(self: *Self) void {
|
|
310
|
+
if (!enabled) return;
|
|
311
|
+
|
|
316
312
|
self.probability.reset();
|
|
317
313
|
self.actions.reset();
|
|
318
314
|
self.pending = .{};
|
|
319
315
|
}
|
|
320
316
|
|
|
321
317
|
pub fn commit(self: *Self, player: Player, kind: Commit) Error!void {
|
|
318
|
+
if (!enabled) return;
|
|
319
|
+
|
|
320
|
+
assert(!showdown);
|
|
321
|
+
|
|
322
322
|
var action = self.actions.get(player);
|
|
323
323
|
|
|
324
324
|
// We need to handle binding specially because we still need to commit it when attacking
|
|
325
325
|
// into a Pokémon that is immune
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (kind == .binding) return;
|
|
334
|
-
} 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;
|
|
335
333
|
|
|
336
334
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
337
335
|
// target is immune/behind a sub/etc)
|
|
@@ -340,19 +338,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
340
338
|
action.hit = if (self.pending.hit) .true else .false;
|
|
341
339
|
}
|
|
342
340
|
|
|
343
|
-
|
|
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);
|
|
344
347
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
350
|
-
|
|
351
|
-
if (self.pending.crit_probablity != 0) {
|
|
352
|
-
try self.probability.update(self.pending.crit_probablity, 256);
|
|
353
|
-
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
354
|
-
}
|
|
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
|
+
}
|
|
355
352
|
|
|
353
|
+
if (self.pending.damage_roll > 0) {
|
|
356
354
|
try self.probability.update(1, 39);
|
|
357
355
|
action.damage = self.pending.damage_roll;
|
|
358
356
|
}
|
|
@@ -403,12 +401,17 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
403
401
|
self.actions.p2.speed_tie = self.actions.p1.speed_tie;
|
|
404
402
|
}
|
|
405
403
|
|
|
406
|
-
pub fn hit(self: *Self, ok: bool, accuracy:
|
|
404
|
+
pub fn hit(self: *Self, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
407
405
|
if (!enabled) return;
|
|
408
406
|
|
|
409
407
|
const p = if (ok) accuracy else @as(u8, @intCast(256 - @as(u9, accuracy)));
|
|
410
|
-
|
|
411
|
-
|
|
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
|
+
}
|
|
412
415
|
}
|
|
413
416
|
|
|
414
417
|
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -522,22 +525,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
522
525
|
}
|
|
523
526
|
}
|
|
524
527
|
|
|
525
|
-
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
526
|
-
if (!enabled) return;
|
|
527
|
-
|
|
528
|
-
var durations = &self.actions.get(player).durations;
|
|
529
|
-
const n = durations.disable;
|
|
530
|
-
if (turns == 0) {
|
|
531
|
-
assert(n >= 1 and n <= 8);
|
|
532
|
-
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
533
|
-
durations.disable = 0;
|
|
534
|
-
} else {
|
|
535
|
-
assert(n >= 1 and n < 8);
|
|
536
|
-
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
537
|
-
durations.disable += 1;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
528
|
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
542
529
|
if (!enabled) return;
|
|
543
530
|
|
|
@@ -554,6 +541,22 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
554
541
|
}
|
|
555
542
|
}
|
|
556
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
|
+
|
|
557
560
|
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
558
561
|
if (!enabled) return;
|
|
559
562
|
|
|
@@ -601,7 +604,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
601
604
|
pub fn metronome(self: *Self, player: Player, move: Move) Error!void {
|
|
602
605
|
if (!enabled) return;
|
|
603
606
|
|
|
604
|
-
try self.probability.update(1, Move.
|
|
607
|
+
try self.probability.update(1, Move.METRONOME.len);
|
|
605
608
|
self.actions.get(player).metronome = move;
|
|
606
609
|
}
|
|
607
610
|
};
|
|
@@ -626,19 +629,25 @@ test "Chance.speedTie" {
|
|
|
626
629
|
test "Chance.hit" {
|
|
627
630
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
628
631
|
|
|
629
|
-
chance.hit(true, 229);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
+
}
|
|
633
639
|
try expectValue(Optional(bool).true, chance.actions.p1.hit);
|
|
634
640
|
try expectProbability(&chance.probability, 229, 256);
|
|
635
641
|
|
|
636
642
|
chance.reset();
|
|
637
643
|
|
|
638
|
-
chance.hit(false, 255);
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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
|
+
}
|
|
642
651
|
try expectValue(Optional(bool).false, chance.actions.p2.hit);
|
|
643
652
|
try expectProbability(&chance.probability, 1, 256);
|
|
644
653
|
}
|
|
@@ -654,26 +663,18 @@ test "Chance.criticalHit" {
|
|
|
654
663
|
try expectProbability(&chance.probability, 1, 1);
|
|
655
664
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
656
665
|
|
|
657
|
-
try chance.
|
|
658
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
659
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
660
|
-
|
|
661
|
-
chance.hit(true, 128);
|
|
666
|
+
try chance.hit(.P1, true, 128);
|
|
662
667
|
|
|
663
|
-
try chance.
|
|
664
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
668
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
665
669
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
666
670
|
|
|
667
|
-
chance.probability.reset();
|
|
668
671
|
try chance.damage(.P1, 217);
|
|
669
672
|
|
|
670
|
-
try chance.
|
|
671
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
673
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
672
674
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
673
675
|
|
|
674
|
-
chance.probability.reset();
|
|
675
|
-
|
|
676
676
|
try chance.commit(.P1, .hit);
|
|
677
|
+
|
|
677
678
|
try expectProbability(&chance.probability, 17, 19968); // (1/2) * (17/256) * (1/39)
|
|
678
679
|
try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
|
|
679
680
|
}
|
|
@@ -688,20 +689,14 @@ test "Chance.criticalHit" {
|
|
|
688
689
|
try expectProbability(&chance.probability, 1, 1);
|
|
689
690
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
690
691
|
|
|
691
|
-
try chance.
|
|
692
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
693
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
694
|
-
|
|
695
|
-
chance.hit(true, 128);
|
|
692
|
+
try chance.hit(.P1, true, 128);
|
|
696
693
|
|
|
697
|
-
try chance.
|
|
698
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
694
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
699
695
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
700
696
|
|
|
701
|
-
chance.probability.reset();
|
|
702
697
|
try chance.damage(.P1, 217);
|
|
703
|
-
|
|
704
698
|
try chance.commit(.P1, .hit);
|
|
699
|
+
|
|
705
700
|
try expectProbability(&chance.probability, 251, 19968); // (1/2) * (251/256) * (1/39)
|
|
706
701
|
try expectValue(Optional(bool).false, chance.actions.p1.critical_hit);
|
|
707
702
|
}
|
|
@@ -712,19 +707,16 @@ test "Chance.damage" {
|
|
|
712
707
|
|
|
713
708
|
try chance.damage(.P1, 217);
|
|
714
709
|
if (showdown) {
|
|
715
|
-
try expectValue(
|
|
710
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
716
711
|
try expectProbability(&chance.probability, 1, 39);
|
|
717
712
|
} else {
|
|
718
713
|
try expectProbability(&chance.probability, 1, 1);
|
|
719
|
-
try expectValue(
|
|
720
|
-
|
|
721
|
-
try chance.commit(.P1, .miss);
|
|
722
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
723
|
-
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
714
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
724
715
|
|
|
725
716
|
try chance.commit(.P1, .hit);
|
|
717
|
+
|
|
726
718
|
try expectProbability(&chance.probability, 1, 39);
|
|
727
|
-
try expectValue(
|
|
719
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
728
720
|
}
|
|
729
721
|
}
|
|
730
722
|
|
|
@@ -776,13 +768,13 @@ test "Chance.moveSlot" {
|
|
|
776
768
|
|
|
777
769
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
778
770
|
try expectProbability(&chance.probability, 1, 2);
|
|
779
|
-
try expectValue(
|
|
771
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
780
772
|
|
|
781
773
|
chance.reset();
|
|
782
774
|
|
|
783
775
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
784
776
|
try expectProbability(&chance.probability, 1, 3);
|
|
785
|
-
try expectValue(
|
|
777
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
786
778
|
}
|
|
787
779
|
|
|
788
780
|
test "Chance.multiHit" {
|
|
@@ -790,32 +782,33 @@ test "Chance.multiHit" {
|
|
|
790
782
|
|
|
791
783
|
try chance.multiHit(.P1, 3);
|
|
792
784
|
try expectProbability(&chance.probability, 3, 8);
|
|
793
|
-
try expectValue(
|
|
785
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
794
786
|
|
|
795
787
|
chance.reset();
|
|
796
788
|
|
|
797
789
|
try chance.multiHit(.P2, 5);
|
|
798
790
|
try expectProbability(&chance.probability, 1, 8);
|
|
799
|
-
try expectValue(
|
|
791
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
800
792
|
}
|
|
801
793
|
|
|
802
794
|
test "Chance.duration" {
|
|
803
795
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
804
796
|
|
|
805
797
|
chance.duration(.sleep, .P1, .P2, 2);
|
|
806
|
-
try expectValue(
|
|
807
|
-
try expectValue(
|
|
798
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
799
|
+
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
808
800
|
|
|
809
801
|
chance.reset();
|
|
810
802
|
|
|
811
803
|
chance.duration(.binding, .P2, .P2, 4);
|
|
812
804
|
if (!showdown) {
|
|
813
|
-
try expectValue(
|
|
814
|
-
try expectValue(
|
|
805
|
+
try expectValue(0, chance.actions.p2.duration);
|
|
806
|
+
try expectValue(0, chance.actions.p2.durations.binding);
|
|
807
|
+
|
|
815
808
|
try chance.commit(.P2, .hit);
|
|
816
809
|
}
|
|
817
|
-
try expectValue(
|
|
818
|
-
try expectValue(
|
|
810
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
811
|
+
try expectValue(1, chance.actions.p2.durations.binding);
|
|
819
812
|
}
|
|
820
813
|
|
|
821
814
|
test "Chance.sleep" {
|
|
@@ -825,7 +818,7 @@ test "Chance.sleep" {
|
|
|
825
818
|
chance.actions.p1.durations.sleep = @intCast(i);
|
|
826
819
|
try chance.sleep(.P1, 0);
|
|
827
820
|
try expectProbability(&chance.probability, 1, d);
|
|
828
|
-
try expectValue(
|
|
821
|
+
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
829
822
|
|
|
830
823
|
chance.reset();
|
|
831
824
|
|
|
@@ -848,7 +841,7 @@ test "Chance.confusion" {
|
|
|
848
841
|
chance.actions.p2.durations.confusion = @intCast(i);
|
|
849
842
|
try chance.confusion(.P2, 0);
|
|
850
843
|
try expectProbability(&chance.probability, 1, d);
|
|
851
|
-
try expectValue(
|
|
844
|
+
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
852
845
|
|
|
853
846
|
chance.reset();
|
|
854
847
|
}
|
|
@@ -864,6 +857,28 @@ test "Chance.confusion" {
|
|
|
864
857
|
}
|
|
865
858
|
}
|
|
866
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
|
+
|
|
867
882
|
test "Chance.attacking" {
|
|
868
883
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
869
884
|
|
|
@@ -872,7 +887,7 @@ test "Chance.attacking" {
|
|
|
872
887
|
chance.actions.p2.durations.attacking = @intCast(i);
|
|
873
888
|
try chance.attacking(.P2, 0);
|
|
874
889
|
try expectProbability(&chance.probability, 1, d);
|
|
875
|
-
try expectValue(
|
|
890
|
+
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
876
891
|
|
|
877
892
|
chance.reset();
|
|
878
893
|
}
|
|
@@ -898,7 +913,7 @@ test "Chance.binding" {
|
|
|
898
913
|
chance.actions.p1.durations.binding = @intCast(i);
|
|
899
914
|
try chance.binding(.P1, 0);
|
|
900
915
|
try expectProbability(&chance.probability, p, q);
|
|
901
|
-
try expectValue(
|
|
916
|
+
try expectValue(0, chance.actions.p1.durations.binding);
|
|
902
917
|
|
|
903
918
|
chance.reset();
|
|
904
919
|
|
|
@@ -918,7 +933,7 @@ test "Chance.psywave" {
|
|
|
918
933
|
|
|
919
934
|
try chance.psywave(.P2, 100, 150);
|
|
920
935
|
try expectProbability(&chance.probability, 1, 150);
|
|
921
|
-
try expectValue(
|
|
936
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
922
937
|
}
|
|
923
938
|
|
|
924
939
|
test "Chance.metronome" {
|
|
@@ -947,7 +962,7 @@ pub fn expectValue(a: anytype, b: anytype) !void {
|
|
|
947
962
|
|
|
948
963
|
/// Null object pattern implementation of Generation I `Chance` which does nothing, though chance
|
|
949
964
|
/// tracking should additionally be turned off entirely via `options.chance`.
|
|
950
|
-
pub const NULL =
|
|
965
|
+
pub const NULL: Null = .{};
|
|
951
966
|
|
|
952
967
|
const Null = struct {
|
|
953
968
|
pub const Error = error{};
|
|
@@ -972,8 +987,8 @@ const Null = struct {
|
|
|
972
987
|
_ = .{ self, p1 };
|
|
973
988
|
}
|
|
974
989
|
|
|
975
|
-
pub fn hit(self: Null, ok: bool, accuracy:
|
|
976
|
-
_ = .{ self, ok, accuracy };
|
|
990
|
+
pub fn hit(self: Null, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
991
|
+
_ = .{ self, player, ok, accuracy };
|
|
977
992
|
}
|
|
978
993
|
|
|
979
994
|
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -984,10 +999,6 @@ const Null = struct {
|
|
|
984
999
|
_ = .{ self, player, roll };
|
|
985
1000
|
}
|
|
986
1001
|
|
|
987
|
-
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
988
|
-
_ = .{ self, player, proc, rate };
|
|
989
|
-
}
|
|
990
|
-
|
|
991
1002
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
992
1003
|
_ = .{ self, player, ok };
|
|
993
1004
|
}
|
|
@@ -996,6 +1007,10 @@ const Null = struct {
|
|
|
996
1007
|
_ = .{ self, player, ok };
|
|
997
1008
|
}
|
|
998
1009
|
|
|
1010
|
+
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
1011
|
+
_ = .{ self, player, proc, rate };
|
|
1012
|
+
}
|
|
1013
|
+
|
|
999
1014
|
pub fn moveSlot(self: Null, player: Player, slot: u4, ms: []const MoveSlot, n: u4) Error!void {
|
|
1000
1015
|
_ = .{ self, player, slot, ms, n };
|
|
1001
1016
|
}
|