@pkmn/engine 0.1.0-dev.f4ba6ebd → 0.1.0-dev.f6545544
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 +5 -4
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -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 +110 -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 +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +39 -89
- package/build/pkg/index.js +23 -70
- 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 +52 -76
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +196 -84
- package/package.json +51 -135
- package/src/bin/install-pkmn-engine +3 -2
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +1 -1
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +4 -8
- package/src/lib/common/rational.zig +7 -5
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +1 -1
- package/src/lib/gen1/calc.zig +208 -61
- package/src/lib/gen1/chance.zig +40 -22
- 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 +9 -13
- package/src/lib/gen1/helpers.zig +5 -21
- package/src/lib/gen1/mechanics.zig +107 -82
- package/src/lib/gen2/calc.zig +9 -17
- package/src/lib/gen2/chance.zig +0 -3
- 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 +11 -15
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +5 -16
- package/src/lib/gen2/mechanics.zig +41 -44
- package/src/lib/node.zig +3 -116
- package/src/lib/pkmn.zig +7 -2
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +95 -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,41 +1,36 @@
|
|
|
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
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const Action = chance.Action;
|
|
37
|
-
const Chance = chance.Chance;
|
|
38
|
-
const Durations = chance.Durations;
|
|
31
|
+
const Rational = rational.Rational;
|
|
32
|
+
const Result = common.Result;
|
|
33
|
+
const Sleeps = chance.Sleeps;
|
|
39
34
|
|
|
40
35
|
const tty = true; // DEBUG
|
|
41
36
|
const summary = false; // DEBUG
|
|
@@ -115,6 +110,14 @@ pub const Calc = struct {
|
|
|
115
110
|
}) val else null;
|
|
116
111
|
}
|
|
117
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
|
+
|
|
118
121
|
pub fn base(self: *Calc, player: Player, val: u16) void {
|
|
119
122
|
if (!enabled) return;
|
|
120
123
|
|
|
@@ -148,6 +151,10 @@ const Null = struct {
|
|
|
148
151
|
return null;
|
|
149
152
|
}
|
|
150
153
|
|
|
154
|
+
pub fn confusion(self: Null, player: Player) void {
|
|
155
|
+
_ = .{ self, player };
|
|
156
|
+
}
|
|
157
|
+
|
|
151
158
|
pub fn base(self: Null, player: Player, val: u16) void {
|
|
152
159
|
_ = .{ self, player, val };
|
|
153
160
|
}
|
|
@@ -175,7 +182,7 @@ pub const Options = struct {
|
|
|
175
182
|
metronome: bool = false,
|
|
176
183
|
};
|
|
177
184
|
|
|
178
|
-
pub const MAX_FRONTIER =
|
|
185
|
+
pub const MAX_FRONTIER = 81;
|
|
179
186
|
|
|
180
187
|
pub fn transitions(
|
|
181
188
|
battle: anytype,
|
|
@@ -194,9 +201,11 @@ pub fn transitions(
|
|
|
194
201
|
var frontier = std.ArrayList(Actions).init(allocator);
|
|
195
202
|
defer frontier.deinit();
|
|
196
203
|
|
|
204
|
+
const d = options.durations;
|
|
205
|
+
|
|
197
206
|
var opts = pkmn.battle.options(
|
|
198
207
|
protocol.NULL,
|
|
199
|
-
Chance(Rational(u128)){ .probability = .{}, .durations =
|
|
208
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = d },
|
|
200
209
|
Calc{},
|
|
201
210
|
);
|
|
202
211
|
|
|
@@ -220,6 +229,7 @@ pub fn transitions(
|
|
|
220
229
|
assert(frontier.items.len == 1);
|
|
221
230
|
while (i < frontier.items.len) : (i += 1) {
|
|
222
231
|
const f = frontier.items[i];
|
|
232
|
+
const saved = stats.saved;
|
|
223
233
|
|
|
224
234
|
try debug(writer, f, .{
|
|
225
235
|
.shape = true,
|
|
@@ -235,10 +245,20 @@ pub fn transitions(
|
|
|
235
245
|
};
|
|
236
246
|
|
|
237
247
|
for (Rolls.speedTie(f.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
|
|
238
|
-
for (Rolls.
|
|
239
|
-
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;
|
|
240
258
|
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
241
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;
|
|
242
262
|
for (Rolls.hit(f.p1, p1_par)) |p1_hit| { a.p1.hit = p1_hit;
|
|
243
263
|
for (Rolls.hit(f.p2, p2_par)) |p2_hit| { a.p2.hit = p2_hit;
|
|
244
264
|
for (Rolls.psywave(f.p1, p1, p1_hit)) |p1_psywave| { a.p1.psywave = p1_psywave;
|
|
@@ -266,7 +286,7 @@ pub fn transitions(
|
|
|
266
286
|
|
|
267
287
|
opts.calc.overrides = a;
|
|
268
288
|
opts.calc.summaries = .{};
|
|
269
|
-
opts.chance = .{ .probability = .{}, .durations =
|
|
289
|
+
opts.chance = .{ .probability = .{}, .durations = d };
|
|
270
290
|
const q = &opts.chance.probability;
|
|
271
291
|
|
|
272
292
|
b = battle;
|
|
@@ -278,12 +298,12 @@ pub fn transitions(
|
|
|
278
298
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
279
299
|
p1_dmg.min
|
|
280
300
|
else
|
|
281
|
-
|
|
301
|
+
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
282
302
|
const p2_max: u9 =
|
|
283
|
-
|
|
303
|
+
Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
|
|
284
304
|
|
|
285
305
|
if (opts.chance.actions.matches(f)) {
|
|
286
|
-
if (!opts.chance.actions.eql(a)) {
|
|
306
|
+
if (!opts.chance.actions.relax().eql(a)) {
|
|
287
307
|
if (!summary) {
|
|
288
308
|
try debug(writer, opts.chance.actions, .{
|
|
289
309
|
.p1_max = p1_max,
|
|
@@ -317,10 +337,13 @@ pub fn transitions(
|
|
|
317
337
|
}
|
|
318
338
|
}
|
|
319
339
|
}
|
|
320
|
-
|
|
321
340
|
if (p1_max != p1_min) try q.update(p1_max - p1_min + 1, 1);
|
|
322
341
|
if (p2_max != p2_dmg.min) try q.update(p2_max - p2_dmg.min + 1, 1);
|
|
342
|
+
|
|
343
|
+
q.reduce();
|
|
323
344
|
try p.add(q);
|
|
345
|
+
p.reduce();
|
|
346
|
+
|
|
324
347
|
stats.saved += 1;
|
|
325
348
|
|
|
326
349
|
if (p.q < p.p) {
|
|
@@ -328,7 +351,7 @@ pub fn transitions(
|
|
|
328
351
|
return error.TestUnexpectedResult;
|
|
329
352
|
}
|
|
330
353
|
} else {
|
|
331
|
-
if (!
|
|
354
|
+
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
332
355
|
try frontier.append(opts.chance.actions);
|
|
333
356
|
|
|
334
357
|
try debug(writer, opts.chance.actions, .{
|
|
@@ -358,9 +381,11 @@ pub fn transitions(
|
|
|
358
381
|
p2_dmg.min = p2_max;
|
|
359
382
|
}
|
|
360
383
|
|
|
361
|
-
}}}}}}}}}}}}}}}}}}
|
|
384
|
+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
362
385
|
|
|
363
|
-
|
|
386
|
+
assert(stats.saved > saved);
|
|
387
|
+
const old = @hasDecl(std.fmt, "formatFloatDecimal");
|
|
388
|
+
if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
|
|
364
389
|
p.reduce();
|
|
365
390
|
try writer.print(
|
|
366
391
|
" = {s} ({d:.2}%)\n\n",
|
|
@@ -496,15 +521,6 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
496
521
|
};
|
|
497
522
|
}
|
|
498
523
|
|
|
499
|
-
fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
|
|
500
|
-
for (frontier, 0..) |f, j| {
|
|
501
|
-
// TODO: is skipping this redundant check worth it?
|
|
502
|
-
if (i == j) continue;
|
|
503
|
-
if (f.matches(actions)) return true;
|
|
504
|
-
}
|
|
505
|
-
return false;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
524
|
fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
509
525
|
const w = std.io.getStdErr().writer();
|
|
510
526
|
w.print(fmt, v) catch return;
|
|
@@ -577,8 +593,6 @@ fn format(writer: anytype, actions: Actions, p1: u9, p2: u9) !void {
|
|
|
577
593
|
try writer.writeAll(output[0..len]);
|
|
578
594
|
}
|
|
579
595
|
|
|
580
|
-
const NONE = @intFromEnum(Optional(bool).None);
|
|
581
|
-
|
|
582
596
|
/// Helper functions that efficiently return valid ranges for various RNG events based on the
|
|
583
597
|
/// state of an `Action` and other events to be used to construct a "transitions" function.
|
|
584
598
|
pub const Rolls = struct {
|
|
@@ -590,6 +604,108 @@ pub const Rolls = struct {
|
|
|
590
604
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
591
605
|
}
|
|
592
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
|
+
|
|
593
709
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
594
710
|
const BOOLS = [_]Optional(bool){ .false, .true };
|
|
595
711
|
|
|
@@ -603,7 +719,7 @@ pub const Rolls = struct {
|
|
|
603
719
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
604
720
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
605
721
|
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
606
|
-
if (parent == .false) return &BOOL_NONE;
|
|
722
|
+
if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
|
|
607
723
|
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
608
724
|
}
|
|
609
725
|
|
|
@@ -628,7 +744,7 @@ pub const Rolls = struct {
|
|
|
628
744
|
|
|
629
745
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
630
746
|
/// given the base damage in `summaries`.
|
|
631
|
-
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool)
|
|
747
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool) u8 {
|
|
632
748
|
if (roll == 0) return roll;
|
|
633
749
|
|
|
634
750
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -638,9 +754,11 @@ pub const Rolls = struct {
|
|
|
638
754
|
return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
|
|
639
755
|
}
|
|
640
756
|
|
|
641
|
-
/// Returns a slice with the correct range of values for confused
|
|
642
|
-
|
|
643
|
-
|
|
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;
|
|
644
762
|
}
|
|
645
763
|
|
|
646
764
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
@@ -707,6 +825,26 @@ test "Rolls.speedTie" {
|
|
|
707
825
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
708
826
|
}
|
|
709
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
|
+
|
|
710
848
|
test "Rolls.damage" {
|
|
711
849
|
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
712
850
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
@@ -716,12 +854,12 @@ test "Rolls.damage" {
|
|
|
716
854
|
|
|
717
855
|
test "Rolls.coalesce" {
|
|
718
856
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
719
|
-
try expectEqual(@as(u8, 0),
|
|
720
|
-
try expectEqual(@as(u8, 241),
|
|
721
|
-
try expectEqual(@as(u8, 255),
|
|
857
|
+
try expectEqual(@as(u8, 0), Rolls.coalesce(.P2, 0, &summaries, false));
|
|
858
|
+
try expectEqual(@as(u8, 241), Rolls.coalesce(.P2, 238, &summaries, false));
|
|
859
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 238, &summaries, true));
|
|
722
860
|
summaries.p1.damage.final = 74;
|
|
723
|
-
try expectEqual(@as(u8, 217),
|
|
724
|
-
try expectEqual(@as(u8, 255),
|
|
861
|
+
try expectEqual(@as(u8, 217), Rolls.coalesce(.P2, 217, &summaries, false));
|
|
862
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 217, &summaries, true));
|
|
725
863
|
}
|
|
726
864
|
|
|
727
865
|
test "Rolls.hit" {
|
|
@@ -750,14 +888,23 @@ test "Rolls.criticalHit" {
|
|
|
750
888
|
&.{ .false, .true },
|
|
751
889
|
Rolls.criticalHit(actions.p1, .None),
|
|
752
890
|
);
|
|
753
|
-
try expectEqualSlices(
|
|
891
|
+
try expectEqualSlices(
|
|
892
|
+
Optional(bool),
|
|
893
|
+
if (pkmn.options.showdown) &.{.None} else &.{ .false, .true },
|
|
894
|
+
Rolls.criticalHit(actions.p1, .false),
|
|
895
|
+
);
|
|
754
896
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
|
|
755
897
|
}
|
|
756
898
|
|
|
757
899
|
test "Rolls.confused" {
|
|
758
900
|
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
759
|
-
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
|
|
760
|
-
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));
|
|
761
908
|
}
|
|
762
909
|
|
|
763
910
|
test "Rolls.paralyzed" {
|
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const print = std.debug.print;
|
|
5
|
-
|
|
6
|
-
const expect = std.testing.expect;
|
|
7
|
-
const expectEqual = std.testing.expectEqual;
|
|
8
|
-
|
|
9
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
2
|
+
|
|
3
|
+
const array = @import("../common/array.zig");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
5
|
+
const data = @import("data.zig");
|
|
6
|
+
const optional = @import("../common/optional.zig");
|
|
10
7
|
const options = @import("../common/options.zig");
|
|
11
8
|
const rational = @import("../common/rational.zig");
|
|
9
|
+
const std = @import("std");
|
|
12
10
|
const util = @import("../common/util.zig");
|
|
13
11
|
|
|
14
|
-
const Array =
|
|
15
|
-
const
|
|
16
|
-
const Optional = @import("../common/optional.zig").Optional;
|
|
17
|
-
|
|
18
|
-
const data = @import("data.zig");
|
|
19
|
-
|
|
12
|
+
const Array = array.Array;
|
|
13
|
+
const assert = std.debug.assert;
|
|
20
14
|
const enabled = options.chance;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const PointerType = util.PointerType;
|
|
15
|
+
const expect = std.testing.expect;
|
|
16
|
+
const expectEqual = std.testing.expectEqual;
|
|
24
17
|
const isPointerTo = util.isPointerTo;
|
|
25
|
-
|
|
26
18
|
const Move = data.Move;
|
|
27
19
|
const MoveSlot = data.MoveSlot;
|
|
20
|
+
const Optional = optional.Optional;
|
|
21
|
+
const Player = common.Player;
|
|
22
|
+
const PointerType = util.PointerType;
|
|
23
|
+
const print = std.debug.print;
|
|
24
|
+
const showdown = options.showdown;
|
|
28
25
|
|
|
29
26
|
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
30
27
|
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
@@ -75,6 +72,25 @@ pub const Actions = extern struct {
|
|
|
75
72
|
return true;
|
|
76
73
|
}
|
|
77
74
|
|
|
75
|
+
/// TODO
|
|
76
|
+
pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
|
|
77
|
+
for (bs, 0..) |b, j| {
|
|
78
|
+
// TODO: is skipping this redundant check worth it?
|
|
79
|
+
if (i == j) continue;
|
|
80
|
+
if (b.matches(a)) return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// TODO
|
|
86
|
+
pub fn relax(actions: Actions) Actions {
|
|
87
|
+
if (!options.overwrite) return actions;
|
|
88
|
+
var a = actions;
|
|
89
|
+
if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
|
|
90
|
+
if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
|
|
91
|
+
return a;
|
|
92
|
+
}
|
|
93
|
+
|
|
78
94
|
pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
|
|
79
95
|
try writer.writeAll("<P1 = ");
|
|
80
96
|
try self.p1.fmt(writer, shape);
|
|
@@ -224,7 +240,7 @@ pub const Action = packed struct(u64) {
|
|
|
224
240
|
};
|
|
225
241
|
|
|
226
242
|
/// TODO
|
|
227
|
-
pub const Durations = struct {
|
|
243
|
+
pub const Durations = extern struct {
|
|
228
244
|
/// TODO
|
|
229
245
|
p1: Duration = .{},
|
|
230
246
|
/// TODO
|
|
@@ -251,7 +267,7 @@ pub const Durations = struct {
|
|
|
251
267
|
}
|
|
252
268
|
};
|
|
253
269
|
|
|
254
|
-
const Sleeps = Array(6, u3);
|
|
270
|
+
pub const Sleeps = Array(6, u3);
|
|
255
271
|
|
|
256
272
|
/// TODO
|
|
257
273
|
pub const Duration = packed struct(u32) {
|
|
@@ -375,7 +391,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
375
391
|
|
|
376
392
|
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
377
393
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
378
|
-
// "critical hit" was spurious or not as moves that do 1 or less damage don't
|
|
394
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
|
|
379
395
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
380
396
|
// passed to the critical hit helper).
|
|
381
397
|
//
|
|
@@ -391,7 +407,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
391
407
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
392
408
|
} else assert(!self.pending.glitch);
|
|
393
409
|
|
|
394
|
-
if (self.pending.damage_roll > 0) {
|
|
410
|
+
if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
|
|
395
411
|
try self.probability.update(1, 39);
|
|
396
412
|
action.damage = self.pending.damage_roll;
|
|
397
413
|
}
|
|
@@ -656,6 +672,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
656
672
|
var d = self.durations.get(player);
|
|
657
673
|
const n = d.attacking;
|
|
658
674
|
if (obs == .ended) {
|
|
675
|
+
// "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
659
676
|
assert(n >= 2 and n <= 3);
|
|
660
677
|
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
661
678
|
d.attacking = 0;
|
|
@@ -682,6 +699,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
682
699
|
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
683
700
|
|
|
684
701
|
if (obs == .ended) {
|
|
702
|
+
// "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
685
703
|
assert(n >= 1 and n <= 4);
|
|
686
704
|
if (n != 4) try self.probability.update(p, q);
|
|
687
705
|
d.binding = 0;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
|
-
const rng = @import("../../common/rng.zig");
|
|
6
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const rng = @import("../../common/rng.zig");
|
|
5
|
+
const std = @import("std");
|
|
7
6
|
|
|
8
7
|
const assert = std.debug.assert;
|
|
9
|
-
|
|
10
8
|
const percent = rng.Gen12.percent;
|
|
11
9
|
const Type = gen1.Type;
|
|
12
10
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen1.Stats;
|
|
10
8
|
const Types = gen1.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
2
|
+
const calc = @import("calc.zig");
|
|
3
|
+
const chance = @import("chance.zig");
|
|
6
4
|
const data = @import("../common/data.zig");
|
|
5
|
+
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const moves = @import("data/moves.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
8
|
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
|
-
|
|
11
|
-
const moves = @import("data/moves.zig");
|
|
12
10
|
const species = @import("data/species.zig");
|
|
11
|
+
const std = @import("std");
|
|
13
12
|
const types = @import("data/types.zig");
|
|
14
|
-
|
|
15
|
-
const calc = @import("calc.zig");
|
|
16
|
-
const chance = @import("chance.zig");
|
|
17
|
-
const mechanics = @import("mechanics.zig");
|
|
13
|
+
const util = @import("../common/util.zig");
|
|
18
14
|
|
|
19
15
|
const assert = std.debug.assert;
|
|
20
16
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -256,7 +252,7 @@ pub const Status = enum(u8) {
|
|
|
256
252
|
|
|
257
253
|
pub const TOX = 0b10001000;
|
|
258
254
|
|
|
259
|
-
const
|
|
255
|
+
const SLEEP = 0b111;
|
|
260
256
|
|
|
261
257
|
/// Whether or not the status `num` is the same as `status`.
|
|
262
258
|
pub fn is(num: u8, status: Status) bool {
|
|
@@ -284,7 +280,7 @@ pub const Status = enum(u8) {
|
|
|
284
280
|
|
|
285
281
|
/// Returns the duration of a sleep status.
|
|
286
282
|
pub fn duration(num: u8) u3 {
|
|
287
|
-
return @intCast(num &
|
|
283
|
+
return @intCast(num & SLEEP);
|
|
288
284
|
}
|
|
289
285
|
|
|
290
286
|
/// Returns whether `num` reflects any status.
|