@pkmn/engine 0.1.0-dev.30895c5a → 0.1.0-dev.3158fa12
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/README.md +9 -6
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +97 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -3
- package/build/pkg/addon.js +34 -65
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +13 -17
- package/build/pkg/gen1.js +6 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +37 -89
- package/build/pkg/index.js +6 -63
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +35 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +142 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +283 -77
- package/package.json +52 -134
- package/src/bin/install-pkmn-engine +7 -5
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +44 -43
- package/src/lib/common/rational.zig +45 -50
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +213 -62
- package/src/lib/gen1/chance.zig +102 -71
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +1 -2
- package/src/lib/gen1/data.zig +35 -38
- package/src/lib/gen1/helpers.zig +15 -27
- package/src/lib/gen1/mechanics.zig +109 -84
- package/src/lib/gen2/calc.zig +36 -18
- package/src/lib/gen2/chance.zig +86 -66
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +1 -2
- package/src/lib/gen2/data.zig +26 -29
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +81 -81
- package/src/lib/node.zig +3 -116
- package/src/lib/pkmn.zig +12 -2
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -405
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -1,44 +1,43 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
1
|
+
const DEBUG = @import("../common/debug.zig").print;
|
|
4
2
|
|
|
3
|
+
const chance = @import("chance.zig");
|
|
5
4
|
const common = @import("../common/data.zig");
|
|
6
|
-
const
|
|
5
|
+
const data = @import("data.zig");
|
|
6
|
+
const helpers = @import("helpers.zig");
|
|
7
7
|
const optional = @import("../common/optional.zig");
|
|
8
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
9
|
const protocol = @import("../common/protocol.zig");
|
|
9
10
|
const rational = @import("../common/rational.zig");
|
|
11
|
+
const std = @import("std");
|
|
10
12
|
const util = @import("../common/util.zig");
|
|
11
13
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const helpers = @import("helpers.zig");
|
|
15
|
-
|
|
14
|
+
const Action = chance.Action;
|
|
15
|
+
const Actions = chance.Actions;
|
|
16
16
|
const assert = std.debug.assert;
|
|
17
|
-
|
|
17
|
+
const Chance = chance.Chance;
|
|
18
|
+
const Choice = common.Choice;
|
|
19
|
+
const Confusion = chance.Confusion;
|
|
20
|
+
const Duration = chance.Duration;
|
|
21
|
+
const Durations = chance.Durations;
|
|
22
|
+
const enabled = pkmn.options.calc;
|
|
18
23
|
const expect = std.testing.expect;
|
|
19
24
|
const expectEqual = std.testing.expectEqual;
|
|
20
25
|
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
26
|
-
const Choice = common.Choice;
|
|
27
|
-
|
|
26
|
+
const isPointerTo = util.isPointerTo;
|
|
27
|
+
const Observation = chance.Observation;
|
|
28
28
|
const Optional = optional.Optional;
|
|
29
|
-
|
|
30
|
-
const Rational = rational.Rational;
|
|
31
|
-
|
|
29
|
+
const Player = common.Player;
|
|
32
30
|
const PointerType = util.PointerType;
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const Chance = chance.Chance;
|
|
37
|
-
const Durations = chance.Durations;
|
|
31
|
+
const Rational = rational.Rational;
|
|
32
|
+
const Result = common.Result;
|
|
33
|
+
const Sleeps = chance.Sleeps;
|
|
38
34
|
|
|
39
35
|
const tty = true; // DEBUG
|
|
40
36
|
const summary = false; // DEBUG
|
|
41
37
|
|
|
38
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
39
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
40
|
+
|
|
42
41
|
/// Information relevant to damage calculation that occured during a Generation I battle `update`.
|
|
43
42
|
pub const Summaries = extern struct {
|
|
44
43
|
/// Relevant information for Player 1.
|
|
@@ -52,7 +51,7 @@ pub const Summaries = extern struct {
|
|
|
52
51
|
|
|
53
52
|
/// Returns the `Summary` for the given `player`.
|
|
54
53
|
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
|
|
55
|
-
assert(
|
|
54
|
+
assert(isPointerTo(self, Summaries));
|
|
56
55
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
57
56
|
}
|
|
58
57
|
};
|
|
@@ -105,12 +104,20 @@ pub const Calc = struct {
|
|
|
105
104
|
|
|
106
105
|
const val = @field(self.overrides.get(player), @tagName(field));
|
|
107
106
|
return if (switch (@typeInfo(@TypeOf(val))) {
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
Enum => val != .None,
|
|
108
|
+
Int => val != 0,
|
|
110
109
|
else => unreachable,
|
|
111
110
|
}) val else null;
|
|
112
111
|
}
|
|
113
112
|
|
|
113
|
+
pub fn confusion(self: *Calc, player: Player) void {
|
|
114
|
+
if (!enabled) return;
|
|
115
|
+
|
|
116
|
+
if (self.overrides.get(player).confusion == .started) {
|
|
117
|
+
self.overrides.get(player).confusion = .None;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
pub fn base(self: *Calc, player: Player, val: u16) void {
|
|
115
122
|
if (!enabled) return;
|
|
116
123
|
|
|
@@ -144,6 +151,10 @@ const Null = struct {
|
|
|
144
151
|
return null;
|
|
145
152
|
}
|
|
146
153
|
|
|
154
|
+
pub fn confusion(self: Null, player: Player) void {
|
|
155
|
+
_ = .{ self, player };
|
|
156
|
+
}
|
|
157
|
+
|
|
147
158
|
pub fn base(self: Null, player: Player, val: u16) void {
|
|
148
159
|
_ = .{ self, player, val };
|
|
149
160
|
}
|
|
@@ -171,7 +182,7 @@ pub const Options = struct {
|
|
|
171
182
|
metronome: bool = false,
|
|
172
183
|
};
|
|
173
184
|
|
|
174
|
-
pub const MAX_FRONTIER =
|
|
185
|
+
pub const MAX_FRONTIER = 81;
|
|
175
186
|
|
|
176
187
|
pub fn transitions(
|
|
177
188
|
battle: anytype,
|
|
@@ -190,9 +201,11 @@ pub fn transitions(
|
|
|
190
201
|
var frontier = std.ArrayList(Actions).init(allocator);
|
|
191
202
|
defer frontier.deinit();
|
|
192
203
|
|
|
204
|
+
const d = options.durations;
|
|
205
|
+
|
|
193
206
|
var opts = pkmn.battle.options(
|
|
194
207
|
protocol.NULL,
|
|
195
|
-
Chance(Rational(u128)){ .probability = .{}, .durations =
|
|
208
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = d },
|
|
196
209
|
Calc{},
|
|
197
210
|
);
|
|
198
211
|
|
|
@@ -216,6 +229,7 @@ pub fn transitions(
|
|
|
216
229
|
assert(frontier.items.len == 1);
|
|
217
230
|
while (i < frontier.items.len) : (i += 1) {
|
|
218
231
|
const f = frontier.items[i];
|
|
232
|
+
const saved = stats.saved;
|
|
219
233
|
|
|
220
234
|
try debug(writer, f, .{
|
|
221
235
|
.shape = true,
|
|
@@ -231,10 +245,20 @@ pub fn transitions(
|
|
|
231
245
|
};
|
|
232
246
|
|
|
233
247
|
for (Rolls.speedTie(f.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
|
|
234
|
-
for (Rolls.
|
|
235
|
-
for (Rolls.
|
|
248
|
+
for (Rolls.sleep(f.p1, d.p1)) |p1_slp| { a.p1.sleep = p1_slp;
|
|
249
|
+
for (Rolls.sleep(f.p2, d.p2)) |p2_slp| { a.p2.sleep = p2_slp;
|
|
250
|
+
for (Rolls.disable(f.p1, d.p1, p1_slp)) |p1_dis| { a.p1.disable = p1_dis;
|
|
251
|
+
for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
|
|
252
|
+
for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
|
|
253
|
+
for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
|
|
254
|
+
for (Rolls.confusion(f.p1, d.p1, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
255
|
+
for (Rolls.confusion(f.p2, d.p2, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
256
|
+
for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
|
|
257
|
+
for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
|
|
236
258
|
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
237
259
|
for (Rolls.paralyzed(f.p2, p2_cfzd)) |p2_par| { a.p2.paralyzed = p2_par;
|
|
260
|
+
for (Rolls.binding(f.p1, d.p1, p1_par)) |p1_bind| { a.p1.binding = p1_bind;
|
|
261
|
+
for (Rolls.binding(f.p2, d.p2, p2_par)) |p2_bind| { a.p2.binding = p2_bind;
|
|
238
262
|
for (Rolls.hit(f.p1, p1_par)) |p1_hit| { a.p1.hit = p1_hit;
|
|
239
263
|
for (Rolls.hit(f.p2, p2_par)) |p2_hit| { a.p2.hit = p2_hit;
|
|
240
264
|
for (Rolls.psywave(f.p1, p1, p1_hit)) |p1_psywave| { a.p1.psywave = p1_psywave;
|
|
@@ -262,7 +286,7 @@ pub fn transitions(
|
|
|
262
286
|
|
|
263
287
|
opts.calc.overrides = a;
|
|
264
288
|
opts.calc.summaries = .{};
|
|
265
|
-
opts.chance = .{ .probability = .{}, .durations =
|
|
289
|
+
opts.chance = .{ .probability = .{}, .durations = d };
|
|
266
290
|
const q = &opts.chance.probability;
|
|
267
291
|
|
|
268
292
|
b = battle;
|
|
@@ -279,7 +303,7 @@ pub fn transitions(
|
|
|
279
303
|
try Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
|
|
280
304
|
|
|
281
305
|
if (opts.chance.actions.matches(f)) {
|
|
282
|
-
if (!opts.chance.actions.eql(a)) {
|
|
306
|
+
if (!opts.chance.actions.relax().eql(a)) {
|
|
283
307
|
if (!summary) {
|
|
284
308
|
try debug(writer, opts.chance.actions, .{
|
|
285
309
|
.p1_max = p1_max,
|
|
@@ -313,10 +337,13 @@ pub fn transitions(
|
|
|
313
337
|
}
|
|
314
338
|
}
|
|
315
339
|
}
|
|
316
|
-
|
|
317
340
|
if (p1_max != p1_min) try q.update(p1_max - p1_min + 1, 1);
|
|
318
341
|
if (p2_max != p2_dmg.min) try q.update(p2_max - p2_dmg.min + 1, 1);
|
|
342
|
+
|
|
343
|
+
q.reduce();
|
|
319
344
|
try p.add(q);
|
|
345
|
+
p.reduce();
|
|
346
|
+
|
|
320
347
|
stats.saved += 1;
|
|
321
348
|
|
|
322
349
|
if (p.q < p.p) {
|
|
@@ -324,7 +351,7 @@ pub fn transitions(
|
|
|
324
351
|
return error.TestUnexpectedResult;
|
|
325
352
|
}
|
|
326
353
|
} else {
|
|
327
|
-
if (!
|
|
354
|
+
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
328
355
|
try frontier.append(opts.chance.actions);
|
|
329
356
|
|
|
330
357
|
try debug(writer, opts.chance.actions, .{
|
|
@@ -354,9 +381,11 @@ pub fn transitions(
|
|
|
354
381
|
p2_dmg.min = p2_max;
|
|
355
382
|
}
|
|
356
383
|
|
|
357
|
-
}}}}}}}}}}}}}}}}}}
|
|
384
|
+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
358
385
|
|
|
359
|
-
|
|
386
|
+
assert(stats.saved > saved);
|
|
387
|
+
const old = @hasDecl(std.fmt, "formatFloatDecimal");
|
|
388
|
+
if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
|
|
360
389
|
p.reduce();
|
|
361
390
|
try writer.print(
|
|
362
391
|
" = {s} ({d:.2}%)\n\n",
|
|
@@ -492,15 +521,6 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
492
521
|
};
|
|
493
522
|
}
|
|
494
523
|
|
|
495
|
-
fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
|
|
496
|
-
for (frontier, 0..) |f, j| {
|
|
497
|
-
// TODO: is skipping this redundant check worth it?
|
|
498
|
-
if (i == j) continue;
|
|
499
|
-
if (f.matches(actions)) return true;
|
|
500
|
-
}
|
|
501
|
-
return false;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
524
|
fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
505
525
|
const w = std.io.getStdErr().writer();
|
|
506
526
|
w.print(fmt, v) catch return;
|
|
@@ -573,8 +593,6 @@ fn format(writer: anytype, actions: Actions, p1: u9, p2: u9) !void {
|
|
|
573
593
|
try writer.writeAll(output[0..len]);
|
|
574
594
|
}
|
|
575
595
|
|
|
576
|
-
const NONE = @intFromEnum(Optional(bool).None);
|
|
577
|
-
|
|
578
596
|
/// Helper functions that efficiently return valid ranges for various RNG events based on the
|
|
579
597
|
/// state of an `Action` and other events to be used to construct a "transitions" function.
|
|
580
598
|
pub const Rolls = struct {
|
|
@@ -586,6 +604,108 @@ pub const Rolls = struct {
|
|
|
586
604
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
587
605
|
}
|
|
588
606
|
|
|
607
|
+
const OBS_NONE = [_]Optional(Observation){.None};
|
|
608
|
+
const OBS_STARTED = [_]Optional(Observation){.started};
|
|
609
|
+
const OBS_CONTINUING = [_]Optional(Observation){.continuing};
|
|
610
|
+
const OBS_ENDED = [_]Optional(Observation){.ended};
|
|
611
|
+
const OBS_ALL = [_]Optional(Observation){ .started, .continuing, .ended };
|
|
612
|
+
const OBS = [_]Optional(Observation){ .continuing, .ended };
|
|
613
|
+
|
|
614
|
+
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
615
|
+
/// `durations`.
|
|
616
|
+
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
|
|
617
|
+
if (action.sleep == .None) return &OBS_NONE;
|
|
618
|
+
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
619
|
+
&OBS_ALL
|
|
620
|
+
else switch (action.sleep) {
|
|
621
|
+
.started => &OBS_STARTED,
|
|
622
|
+
else => if (Sleeps.get(duration.sleeps, 0) >= 7) &OBS_ENDED else &OBS,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
627
|
+
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
628
|
+
pub fn disable(
|
|
629
|
+
action: Action,
|
|
630
|
+
duration: Duration,
|
|
631
|
+
parent: Optional(Observation),
|
|
632
|
+
) []const Optional(Observation) {
|
|
633
|
+
if (action.disable == .None) return &OBS_NONE;
|
|
634
|
+
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
635
|
+
&OBS_ALL
|
|
636
|
+
else switch (action.disable) {
|
|
637
|
+
.started => &OBS_STARTED,
|
|
638
|
+
else => if (duration.disable >= 8)
|
|
639
|
+
&OBS_ENDED
|
|
640
|
+
else if (parent != .None and parent != .started)
|
|
641
|
+
&OBS_CONTINUING
|
|
642
|
+
else
|
|
643
|
+
&OBS,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/// FIXME
|
|
648
|
+
pub fn attacking(
|
|
649
|
+
action: Action,
|
|
650
|
+
duration: Duration,
|
|
651
|
+
parent: Optional(Observation),
|
|
652
|
+
) []const Optional(Observation) {
|
|
653
|
+
return switch (action.attacking) {
|
|
654
|
+
.None => &OBS_NONE,
|
|
655
|
+
.started => &OBS_STARTED,
|
|
656
|
+
else => if (duration.attacking >= 3)
|
|
657
|
+
&OBS_ENDED
|
|
658
|
+
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
659
|
+
&OBS_CONTINUING
|
|
660
|
+
else
|
|
661
|
+
&OBS,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const CFZ_NONE = [_]Optional(Confusion){.None};
|
|
666
|
+
const CFZ_STARTED = [_]Optional(Confusion){.started};
|
|
667
|
+
const CFZ_OVERWRITTEN = [_]Optional(Confusion){ .started, .overwritten };
|
|
668
|
+
const CFZ_CONTINUING = [_]Optional(Confusion){.continuing};
|
|
669
|
+
const CFZ_ENDED = [_]Optional(Confusion){.ended};
|
|
670
|
+
const CFZ = [_]Optional(Confusion){ .continuing, .ended };
|
|
671
|
+
|
|
672
|
+
/// FIXME
|
|
673
|
+
pub fn confusion(
|
|
674
|
+
action: Action,
|
|
675
|
+
duration: Duration,
|
|
676
|
+
sibling: Optional(Observation),
|
|
677
|
+
parent: Optional(Observation),
|
|
678
|
+
) []const Optional(Confusion) {
|
|
679
|
+
return switch (action.confusion) {
|
|
680
|
+
.None => &CFZ_NONE,
|
|
681
|
+
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
682
|
+
&CFZ_OVERWRITTEN
|
|
683
|
+
else
|
|
684
|
+
&CFZ_STARTED,
|
|
685
|
+
else => if (duration.confusion >= 5)
|
|
686
|
+
&CFZ_ENDED
|
|
687
|
+
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
688
|
+
&CFZ_CONTINUING
|
|
689
|
+
else
|
|
690
|
+
&CFZ,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
695
|
+
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
696
|
+
pub fn binding(
|
|
697
|
+
action: Action,
|
|
698
|
+
duration: Duration,
|
|
699
|
+
parent: Optional(bool),
|
|
700
|
+
) []const Optional(Observation) {
|
|
701
|
+
if (parent == .true) return &OBS_NONE;
|
|
702
|
+
return switch (action.binding) {
|
|
703
|
+
.None => &OBS_NONE,
|
|
704
|
+
.started => &OBS_STARTED,
|
|
705
|
+
else => if (duration.binding >= 4) &OBS_ENDED else &OBS,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
589
709
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
590
710
|
const BOOLS = [_]Optional(bool){ .false, .true };
|
|
591
711
|
|
|
@@ -599,7 +719,7 @@ pub const Rolls = struct {
|
|
|
599
719
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
600
720
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
601
721
|
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
602
|
-
if (parent == .false) return &BOOL_NONE;
|
|
722
|
+
if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
|
|
603
723
|
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
604
724
|
}
|
|
605
725
|
|
|
@@ -634,9 +754,11 @@ pub const Rolls = struct {
|
|
|
634
754
|
return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
|
|
635
755
|
}
|
|
636
756
|
|
|
637
|
-
/// Returns a slice with the correct range of values for confused
|
|
638
|
-
|
|
639
|
-
|
|
757
|
+
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
758
|
+
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
759
|
+
pub fn confused(action: Action, parent: Optional(Confusion)) []const Optional(bool) {
|
|
760
|
+
const done = parent == .None or parent == .ended;
|
|
761
|
+
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
640
762
|
}
|
|
641
763
|
|
|
642
764
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
@@ -703,6 +825,26 @@ test "Rolls.speedTie" {
|
|
|
703
825
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
704
826
|
}
|
|
705
827
|
|
|
828
|
+
test "Rolls.sleep" {
|
|
829
|
+
return error.SkipZigTest; // TODO
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
test "Rolls.disable" {
|
|
833
|
+
return error.SkipZigTest; // TODO
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
test "Rolls.confusion" {
|
|
837
|
+
return error.SkipZigTest; // TODO
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
test "Rolls.atacking" {
|
|
841
|
+
return error.SkipZigTest; // TODO
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
test "Rolls.binding" {
|
|
845
|
+
return error.SkipZigTest; // TODO
|
|
846
|
+
}
|
|
847
|
+
|
|
706
848
|
test "Rolls.damage" {
|
|
707
849
|
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
708
850
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
@@ -712,12 +854,12 @@ test "Rolls.damage" {
|
|
|
712
854
|
|
|
713
855
|
test "Rolls.coalesce" {
|
|
714
856
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
715
|
-
try expectEqual(0, try Rolls.coalesce(.P2, 0, &summaries, false));
|
|
716
|
-
try expectEqual(241, try Rolls.coalesce(.P2, 238, &summaries, false));
|
|
717
|
-
try expectEqual(255, try Rolls.coalesce(.P2, 238, &summaries, true));
|
|
857
|
+
try expectEqual(@as(u8, 0), try Rolls.coalesce(.P2, 0, &summaries, false));
|
|
858
|
+
try expectEqual(@as(u8, 241), try Rolls.coalesce(.P2, 238, &summaries, false));
|
|
859
|
+
try expectEqual(@as(u8, 255), try Rolls.coalesce(.P2, 238, &summaries, true));
|
|
718
860
|
summaries.p1.damage.final = 74;
|
|
719
|
-
try expectEqual(217, try Rolls.coalesce(.P2, 217, &summaries, false));
|
|
720
|
-
try expectEqual(255, try Rolls.coalesce(.P2, 217, &summaries, true));
|
|
861
|
+
try expectEqual(@as(u8, 217), try Rolls.coalesce(.P2, 217, &summaries, false));
|
|
862
|
+
try expectEqual(@as(u8, 255), try Rolls.coalesce(.P2, 217, &summaries, true));
|
|
721
863
|
}
|
|
722
864
|
|
|
723
865
|
test "Rolls.hit" {
|
|
@@ -746,14 +888,23 @@ test "Rolls.criticalHit" {
|
|
|
746
888
|
&.{ .false, .true },
|
|
747
889
|
Rolls.criticalHit(actions.p1, .None),
|
|
748
890
|
);
|
|
749
|
-
try expectEqualSlices(
|
|
891
|
+
try expectEqualSlices(
|
|
892
|
+
Optional(bool),
|
|
893
|
+
if (pkmn.options.showdown) &.{.None} else &.{ .false, .true },
|
|
894
|
+
Rolls.criticalHit(actions.p1, .false),
|
|
895
|
+
);
|
|
750
896
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
|
|
751
897
|
}
|
|
752
898
|
|
|
753
899
|
test "Rolls.confused" {
|
|
754
900
|
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
755
|
-
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
|
|
756
|
-
try expectEqualSlices(
|
|
901
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, .None));
|
|
902
|
+
try expectEqualSlices(
|
|
903
|
+
Optional(bool),
|
|
904
|
+
&.{ .false, .true },
|
|
905
|
+
Rolls.confused(actions.p2, .continuing),
|
|
906
|
+
);
|
|
907
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, .ended));
|
|
757
908
|
}
|
|
758
909
|
|
|
759
910
|
test "Rolls.paralyzed" {
|
|
@@ -794,9 +945,9 @@ test "Rolls.psywave" {
|
|
|
794
945
|
|
|
795
946
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
|
|
796
947
|
var rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
797
|
-
try expectEqual(150, rolls[rolls.len - 1]);
|
|
948
|
+
try expectEqual(@as(u8, 150), rolls[rolls.len - 1]);
|
|
798
949
|
side.stored().level = 81;
|
|
799
950
|
rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
800
|
-
try expectEqual(121, rolls[rolls.len - 1]);
|
|
951
|
+
try expectEqual(@as(u8, 121), rolls[rolls.len - 1]);
|
|
801
952
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
|
|
802
953
|
}
|