@pkmn/engine 0.1.0-dev.b01cb48c → 0.1.0-dev.b14f2221
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 -1
- package/README.md +21 -11
- 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 +26 -4
- 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 +40 -90
- 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 +68 -74
- 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 +255 -102
- package/package.json +59 -138
- package/src/bin/install-pkmn-engine +25 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +200 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +50 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +30 -9
- package/src/lib/common/js.zig +3 -7
- package/src/lib/common/optional.zig +11 -17
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +195 -159
- package/src/lib/common/rational.zig +303 -310
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +33 -1
- package/src/lib/gen1/calc.zig +324 -180
- package/src/lib/gen1/chance.zig +536 -140
- 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 +3 -4
- package/src/lib/gen1/data.zig +40 -43
- package/src/lib/gen1/helpers.zig +17 -29
- package/src/lib/gen1/mechanics.zig +240 -187
- package/src/lib/gen2/calc.zig +35 -28
- package/src/lib/gen2/chance.zig +88 -74
- 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 +7 -5
- package/src/lib/gen2/data.zig +33 -35
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -20
- package/src/lib/gen2/mechanics.zig +98 -98
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +103 -22
- package/src/lib/wasm.zig +9 -9
- package/src/tools/display/dom.ts +93 -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,40 +1,35 @@
|
|
|
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 Duration = chance.Duration;
|
|
20
|
+
const Durations = chance.Durations;
|
|
21
|
+
const enabled = pkmn.options.calc;
|
|
18
22
|
const expect = std.testing.expect;
|
|
19
23
|
const expectEqual = std.testing.expectEqual;
|
|
20
24
|
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
|
-
|
|
25
|
+
const isPointerTo = util.isPointerTo;
|
|
26
|
+
const Observation = chance.Observation;
|
|
28
27
|
const Optional = optional.Optional;
|
|
29
|
-
|
|
30
|
-
const Rational = rational.Rational;
|
|
31
|
-
|
|
28
|
+
const Player = common.Player;
|
|
32
29
|
const PointerType = util.PointerType;
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const Chance = chance.Chance;
|
|
37
|
-
const Durations = chance.Durations;
|
|
30
|
+
const Rational = rational.Rational;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const Sleeps = chance.Sleeps;
|
|
38
33
|
|
|
39
34
|
const tty = true; // DEBUG
|
|
40
35
|
const summary = false; // DEBUG
|
|
@@ -52,7 +47,7 @@ pub const Summaries = extern struct {
|
|
|
52
47
|
|
|
53
48
|
/// Returns the `Summary` for the given `player`.
|
|
54
49
|
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
|
|
55
|
-
assert(
|
|
50
|
+
assert(isPointerTo(self, Summaries));
|
|
56
51
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
57
52
|
}
|
|
58
53
|
};
|
|
@@ -88,21 +83,11 @@ pub const Summary = extern struct {
|
|
|
88
83
|
}
|
|
89
84
|
};
|
|
90
85
|
|
|
91
|
-
/// TODO
|
|
92
|
-
pub const Overrides = extern struct {
|
|
93
|
-
/// TODO
|
|
94
|
-
actions: Actions = .{},
|
|
95
|
-
|
|
96
|
-
comptime {
|
|
97
|
-
assert(@sizeOf(Overrides) == 16);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
86
|
/// Allows for forcing the value of specific RNG events during a Generation I battle `update` via
|
|
102
87
|
/// `overrides` and tracks `summaries` of information relevant to damage calculation.
|
|
103
88
|
pub const Calc = struct {
|
|
104
89
|
/// Overrides the normal behavior of the RNG during an `update` to force specific outcomes.
|
|
105
|
-
overrides:
|
|
90
|
+
overrides: Actions = .{},
|
|
106
91
|
/// Information relevant to damage calculation.
|
|
107
92
|
summaries: Summaries = .{},
|
|
108
93
|
|
|
@@ -110,19 +95,25 @@ pub const Calc = struct {
|
|
|
110
95
|
self: Calc,
|
|
111
96
|
player: Player,
|
|
112
97
|
comptime field: Action.Field,
|
|
113
|
-
)
|
|
98
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
114
99
|
if (!enabled) return null;
|
|
115
100
|
|
|
116
|
-
const overrides
|
|
117
|
-
if (player == .P1) self.overrides.actions.p1 else self.overrides.actions.p2;
|
|
118
|
-
const val = @field(overrides, @tagName(field));
|
|
101
|
+
const val = @field(self.overrides.get(player), @tagName(field));
|
|
119
102
|
return if (switch (@typeInfo(@TypeOf(val))) {
|
|
120
|
-
|
|
121
|
-
.
|
|
103
|
+
.@"enum" => val != .None,
|
|
104
|
+
.int => val != 0,
|
|
122
105
|
else => unreachable,
|
|
123
106
|
}) val else null;
|
|
124
107
|
}
|
|
125
108
|
|
|
109
|
+
pub fn confusion(self: *Calc, player: Player) void {
|
|
110
|
+
if (!enabled) return;
|
|
111
|
+
|
|
112
|
+
if (self.overrides.get(player).confusion == .started) {
|
|
113
|
+
self.overrides.get(player).confusion = .None;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
126
117
|
pub fn base(self: *Calc, player: Player, val: u16) void {
|
|
127
118
|
if (!enabled) return;
|
|
128
119
|
|
|
@@ -151,11 +142,15 @@ const Null = struct {
|
|
|
151
142
|
self: Null,
|
|
152
143
|
player: Player,
|
|
153
144
|
comptime field: Action.Field,
|
|
154
|
-
)
|
|
145
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
155
146
|
_ = .{ self, player };
|
|
156
147
|
return null;
|
|
157
148
|
}
|
|
158
149
|
|
|
150
|
+
pub fn confusion(self: Null, player: Player) void {
|
|
151
|
+
_ = .{ self, player };
|
|
152
|
+
}
|
|
153
|
+
|
|
159
154
|
pub fn base(self: Null, player: Player, val: u16) void {
|
|
160
155
|
_ = .{ self, player, val };
|
|
161
156
|
}
|
|
@@ -183,28 +178,31 @@ pub const Options = struct {
|
|
|
183
178
|
metronome: bool = false,
|
|
184
179
|
};
|
|
185
180
|
|
|
186
|
-
pub const MAX_FRONTIER =
|
|
181
|
+
pub const MAX_FRONTIER = 83;
|
|
187
182
|
|
|
188
183
|
pub fn transitions(
|
|
189
184
|
battle: anytype,
|
|
190
185
|
c1: Choice,
|
|
191
186
|
c2: Choice,
|
|
192
187
|
allocator: std.mem.Allocator,
|
|
193
|
-
|
|
188
|
+
out: *std.Io.Writer,
|
|
189
|
+
err: *std.Io.Writer,
|
|
194
190
|
options: Options,
|
|
195
191
|
) !?Stats {
|
|
196
192
|
var stats: Stats = .{};
|
|
197
193
|
|
|
198
194
|
const cap = options.cap;
|
|
199
195
|
|
|
200
|
-
var seen
|
|
196
|
+
var seen: std.AutoHashMap(Actions, void) = .init(allocator);
|
|
201
197
|
defer seen.deinit();
|
|
202
|
-
var frontier
|
|
203
|
-
defer frontier.deinit();
|
|
198
|
+
var frontier: std.array_list.Aligned(Actions, null) = .empty;
|
|
199
|
+
defer frontier.deinit(allocator);
|
|
200
|
+
|
|
201
|
+
const d = options.durations;
|
|
204
202
|
|
|
205
203
|
var opts = pkmn.battle.options(
|
|
206
204
|
protocol.NULL,
|
|
207
|
-
Chance(Rational(u128)){ .probability = .{}, .durations =
|
|
205
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = d },
|
|
208
206
|
Calc{},
|
|
209
207
|
);
|
|
210
208
|
|
|
@@ -215,8 +213,8 @@ pub fn transitions(
|
|
|
215
213
|
const p1 = b.side(.P1);
|
|
216
214
|
const p2 = b.side(.P2);
|
|
217
215
|
|
|
218
|
-
var p: Rational(
|
|
219
|
-
try frontier.append(opts.chance.actions);
|
|
216
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
217
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
220
218
|
|
|
221
219
|
// zig fmt: off
|
|
222
220
|
for (Rolls.metronome(frontier.items[0].p1)) |p1_move| {
|
|
@@ -227,9 +225,12 @@ pub fn transitions(
|
|
|
227
225
|
var i: usize = 0;
|
|
228
226
|
assert(frontier.items.len == 1);
|
|
229
227
|
while (i < frontier.items.len) : (i += 1) {
|
|
230
|
-
const
|
|
228
|
+
const f = frontier.items[i];
|
|
229
|
+
const saved = stats.saved;
|
|
230
|
+
|
|
231
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
231
232
|
|
|
232
|
-
try debug(
|
|
233
|
+
try debug(out, f, .{
|
|
233
234
|
.shape = true,
|
|
234
235
|
.color = i,
|
|
235
236
|
.bold = true,
|
|
@@ -237,43 +238,54 @@ pub fn transitions(
|
|
|
237
238
|
.indent = false,
|
|
238
239
|
});
|
|
239
240
|
|
|
240
|
-
var a: Actions = .{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
for (Rolls.
|
|
246
|
-
for (Rolls.
|
|
247
|
-
for (Rolls.
|
|
248
|
-
for (Rolls.
|
|
249
|
-
for (Rolls.
|
|
250
|
-
for (Rolls.
|
|
251
|
-
for (Rolls.
|
|
252
|
-
for (Rolls.
|
|
253
|
-
for (Rolls.
|
|
254
|
-
for (Rolls.
|
|
255
|
-
for (Rolls.
|
|
256
|
-
for (Rolls.
|
|
257
|
-
for (Rolls.
|
|
258
|
-
for (Rolls.
|
|
259
|
-
for (Rolls.
|
|
260
|
-
for (Rolls.
|
|
261
|
-
|
|
262
|
-
|
|
241
|
+
var a: Actions = .{
|
|
242
|
+
.p1 = .{ .metronome = p1_move, .pp = f.p1.pp, .duration = f.p1.duration },
|
|
243
|
+
.p2 = .{ .metronome = p2_move, .pp = f.p2.pp, .duration = f.p2.duration },
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
for (Rolls.speedTie(f.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
|
|
247
|
+
for (Rolls.sleep(f.p1, d.p1)) |p1_slp| { a.p1.sleep = p1_slp;
|
|
248
|
+
for (Rolls.sleep(f.p2, d.p2)) |p2_slp| { a.p2.sleep = p2_slp;
|
|
249
|
+
for (Rolls.disable(f.p1, d.p1, p1_slp)) |p1_dis| { a.p1.disable = p1_dis;
|
|
250
|
+
for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
|
|
251
|
+
for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
|
|
252
|
+
for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
|
|
253
|
+
for (Rolls.confusion(f.p1, d.p1, tie, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
254
|
+
for (Rolls.confusion(f.p2, d.p2, tie, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
255
|
+
for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
|
|
256
|
+
for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
|
|
257
|
+
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
258
|
+
for (Rolls.paralyzed(f.p2, p2_cfzd)) |p2_par| { a.p2.paralyzed = p2_par;
|
|
259
|
+
for (Rolls.binding(f.p1, d.p1, p1_par)) |p1_bind| { a.p1.binding = p1_bind;
|
|
260
|
+
for (Rolls.binding(f.p2, d.p2, p2_par)) |p2_bind| { a.p2.binding = p2_bind;
|
|
261
|
+
for (Rolls.hit(f.p1, p1_par)) |p1_hit| { a.p1.hit = p1_hit;
|
|
262
|
+
for (Rolls.hit(f.p2, p2_par)) |p2_hit| { a.p2.hit = p2_hit;
|
|
263
|
+
for (Rolls.psywave(f.p1, p1, p1_hit)) |p1_psywave| { a.p1.psywave = p1_psywave;
|
|
264
|
+
for (Rolls.psywave(f.p2, p2, p2_hit)) |p2_psywave| { a.p2.psywave = p2_psywave;
|
|
265
|
+
for (Rolls.moveSlot(f.p1, p1_hit)) |p1_slot| { a.p1.move_slot = p1_slot;
|
|
266
|
+
for (Rolls.moveSlot(f.p2, p2_hit)) |p2_slot| { a.p2.move_slot = p2_slot;
|
|
267
|
+
for (Rolls.multiHit(f.p1, p1_hit)) |p1_multi| { a.p1.multi_hit = p1_multi;
|
|
268
|
+
for (Rolls.multiHit(f.p2, p2_hit)) |p2_multi| { a.p2.multi_hit = p2_multi;
|
|
269
|
+
for (Rolls.secondaryChance(f.p1, p1_hit)) |p1_sec| { a.p1.secondary_chance = p1_sec;
|
|
270
|
+
for (Rolls.secondaryChance(f.p2, p2_hit)) |p2_sec| { a.p2.secondary_chance = p2_sec;
|
|
271
|
+
for (Rolls.criticalHit(f.p1, p1_hit)) |p1_crit| { a.p1.critical_hit = p1_crit;
|
|
272
|
+
for (Rolls.criticalHit(f.p2, p2_hit)) |p2_crit| { a.p2.critical_hit = p2_crit;
|
|
273
|
+
|
|
274
|
+
var p1_dmg = Rolls.damage(f.p1, p1_hit);
|
|
263
275
|
while (p1_dmg.min < p1_dmg.max) : (p1_dmg.min += 1) {
|
|
264
276
|
a.p1.damage = @intCast(p1_dmg.min);
|
|
265
277
|
|
|
266
|
-
var p2_dmg = Rolls.damage(
|
|
278
|
+
var p2_dmg = Rolls.damage(f.p2, p2_hit);
|
|
267
279
|
|
|
268
|
-
const p1_min: u9 = p1_dmg.min;
|
|
269
|
-
const p2_min: u9 = p2_dmg.min;
|
|
280
|
+
const p1_min: u9 = @intCast(p1_dmg.min);
|
|
281
|
+
const p2_min: u9 = @intCast(p2_dmg.min);
|
|
270
282
|
|
|
271
283
|
while (p2_dmg.min < p2_dmg.max) : (p2_dmg.min += 1) {
|
|
272
284
|
a.p2.damage = @intCast(p2_dmg.min);
|
|
273
285
|
|
|
274
|
-
opts.calc.overrides
|
|
286
|
+
opts.calc.overrides = a;
|
|
275
287
|
opts.calc.summaries = .{};
|
|
276
|
-
opts.chance = .{ .probability = .{}, .durations =
|
|
288
|
+
opts.chance = .{ .probability = .{}, .durations = d };
|
|
277
289
|
const q = &opts.chance.probability;
|
|
278
290
|
|
|
279
291
|
b = battle;
|
|
@@ -283,16 +295,16 @@ pub fn transitions(
|
|
|
283
295
|
|
|
284
296
|
const summaries = &opts.calc.summaries;
|
|
285
297
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
286
|
-
p1_dmg.min
|
|
298
|
+
@intCast(p1_dmg.min)
|
|
287
299
|
else
|
|
288
|
-
|
|
300
|
+
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
289
301
|
const p2_max: u9 =
|
|
290
|
-
|
|
302
|
+
Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
|
|
291
303
|
|
|
292
|
-
if (opts.chance.actions.matches(
|
|
293
|
-
if (!opts.chance.actions.eql(a)) {
|
|
304
|
+
if (opts.chance.actions.matches(f)) {
|
|
305
|
+
if (!opts.chance.actions.relax().eql(a)) {
|
|
294
306
|
if (!summary) {
|
|
295
|
-
try debug(
|
|
307
|
+
try debug(out, opts.chance.actions, .{
|
|
296
308
|
.p1_max = p1_max,
|
|
297
309
|
.p2_max = p2_max,
|
|
298
310
|
.color = i,
|
|
@@ -306,7 +318,7 @@ pub fn transitions(
|
|
|
306
318
|
}
|
|
307
319
|
|
|
308
320
|
if (!summary) {
|
|
309
|
-
try debug(
|
|
321
|
+
try debug(out, opts.chance.actions, .{
|
|
310
322
|
.p1_max = p1_max,
|
|
311
323
|
.p2_max = p2_max,
|
|
312
324
|
.color = i,
|
|
@@ -319,33 +331,38 @@ pub fn transitions(
|
|
|
319
331
|
acts.p1.damage = @intCast(p1d);
|
|
320
332
|
acts.p2.damage = @intCast(p2d);
|
|
321
333
|
if ((try seen.getOrPut(acts)).found_existing) {
|
|
322
|
-
err
|
|
334
|
+
die(err, "already seen {f}", .{acts}, options.seed);
|
|
323
335
|
return error.TestUnexpectedResult;
|
|
324
336
|
}
|
|
325
337
|
}
|
|
326
338
|
}
|
|
327
|
-
|
|
328
339
|
if (p1_max != p1_min) try q.update(p1_max - p1_min + 1, 1);
|
|
329
340
|
if (p2_max != p2_dmg.min) try q.update(p2_max - p2_dmg.min + 1, 1);
|
|
341
|
+
|
|
342
|
+
q.reduce();
|
|
330
343
|
try p.add(q);
|
|
344
|
+
p.reduce();
|
|
345
|
+
try r.add(q);
|
|
346
|
+
r.reduce();
|
|
347
|
+
|
|
331
348
|
stats.saved += 1;
|
|
332
349
|
|
|
333
350
|
if (p.q < p.p) {
|
|
334
|
-
err
|
|
351
|
+
die(err, "improper fraction {f}", .{p}, options.seed);
|
|
335
352
|
return error.TestUnexpectedResult;
|
|
336
353
|
}
|
|
337
354
|
} else {
|
|
338
|
-
if (!
|
|
339
|
-
try frontier.append(opts.chance.actions);
|
|
355
|
+
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
356
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
340
357
|
|
|
341
|
-
try debug(
|
|
358
|
+
try debug(out, opts.chance.actions, .{
|
|
342
359
|
.p1_max = p1_max,
|
|
343
360
|
.p2_max = p2_max,
|
|
344
361
|
.dim = true,
|
|
345
362
|
.newline = false,
|
|
346
363
|
});
|
|
347
|
-
try
|
|
348
|
-
try debug(
|
|
364
|
+
try out.writeAll(" → ");
|
|
365
|
+
try debug(out, opts.chance.actions, .{
|
|
349
366
|
.shape = true,
|
|
350
367
|
.color = frontier.items.len - 1,
|
|
351
368
|
.dim = true,
|
|
@@ -353,7 +370,7 @@ pub fn transitions(
|
|
|
353
370
|
.indent = false,
|
|
354
371
|
});
|
|
355
372
|
} else if (!summary) {
|
|
356
|
-
try debug(
|
|
373
|
+
try debug(out, opts.chance.actions, .{
|
|
357
374
|
.p1_max = p1_max,
|
|
358
375
|
.p2_max = p2_max,
|
|
359
376
|
.dim = true,
|
|
@@ -365,15 +382,15 @@ pub fn transitions(
|
|
|
365
382
|
p2_dmg.min = p2_max;
|
|
366
383
|
}
|
|
367
384
|
|
|
368
|
-
}}}}}}}}}}}}}}}}}}}}
|
|
385
|
+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
369
386
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
387
|
+
assert(stats.saved > saved);
|
|
388
|
+
// TODO: LLVM - float_from_int from 'u256' without intrinsics
|
|
389
|
+
// try out.print(
|
|
390
|
+
// " {f} ({d:.2}%)\n = ──────────\n {f} ({d:.2}%)\n\n",
|
|
391
|
+
// .{r, 100 * @as(f128, @floatFromInt(r.p)) / @as(f128, @floatFromInt(r.q)),
|
|
392
|
+
// p, 100 * @as(f128, @floatFromInt(p.p)) / @as(f128, @floatFromInt(p.q))},
|
|
393
|
+
// );
|
|
377
394
|
|
|
378
395
|
}
|
|
379
396
|
|
|
@@ -388,7 +405,7 @@ pub fn transitions(
|
|
|
388
405
|
|
|
389
406
|
p.reduce();
|
|
390
407
|
if (p.p != 1 or p.q != 1) {
|
|
391
|
-
err
|
|
408
|
+
die(err, "expected 1, found {f}", .{p}, options.seed);
|
|
392
409
|
return error.TestExpectedEqual;
|
|
393
410
|
}
|
|
394
411
|
|
|
@@ -401,7 +418,8 @@ pub fn update(
|
|
|
401
418
|
c2: Choice,
|
|
402
419
|
options: anytype,
|
|
403
420
|
allocator: std.mem.Allocator,
|
|
404
|
-
|
|
421
|
+
out: *std.Io.Writer,
|
|
422
|
+
err: *std.Io.Writer,
|
|
405
423
|
transition: bool,
|
|
406
424
|
) !Result {
|
|
407
425
|
const durations = options.chance.durations;
|
|
@@ -419,8 +437,8 @@ pub fn update(
|
|
|
419
437
|
if (@TypeOf(battle.rng) == data.PRNG) {
|
|
420
438
|
// Ensure we can encode the diffs in less than MAX_DIFFS bytes.
|
|
421
439
|
var buf: [helpers.MAX_DIFFS]u8 = undefined;
|
|
422
|
-
var
|
|
423
|
-
const n = try helpers.diff(&original, battle,
|
|
440
|
+
var pw: protocol.Writer = .{ .buffer = &buf };
|
|
441
|
+
const n = try helpers.diff(&original, battle, &pw);
|
|
424
442
|
|
|
425
443
|
// Applying the diff to the battle should take us back to the original copy
|
|
426
444
|
// of the battle (ignoring the RNG).
|
|
@@ -434,7 +452,7 @@ pub fn update(
|
|
|
434
452
|
// Ensure we can generate all transitions from the same original state
|
|
435
453
|
// (we must change the battle's RNG from a FixedRNG to a PRNG because
|
|
436
454
|
// the transitions function relies on RNG for discovery of states)
|
|
437
|
-
if (try transitions(unfix(original), c1, c2, allocator,
|
|
455
|
+
if (try transitions(unfix(original), c1, c2, allocator, out, err, .{
|
|
438
456
|
.durations = durations,
|
|
439
457
|
.cap = true,
|
|
440
458
|
})) |stats| try expect(stats.frontier <= MAX_FRONTIER);
|
|
@@ -445,7 +463,7 @@ pub fn update(
|
|
|
445
463
|
var override = pkmn.battle.options(
|
|
446
464
|
protocol.NULL,
|
|
447
465
|
Chance(Rational(u128)){ .probability = .{}, .durations = durations },
|
|
448
|
-
Calc{ .overrides =
|
|
466
|
+
Calc{ .overrides = overrides },
|
|
449
467
|
);
|
|
450
468
|
var copy = original;
|
|
451
469
|
var overridden = copy.update(c1, c2, &override);
|
|
@@ -466,8 +484,8 @@ pub fn update(
|
|
|
466
484
|
|
|
467
485
|
override = pkmn.battle.options(
|
|
468
486
|
protocol.NULL,
|
|
469
|
-
Chance(Rational(u128)){ .probability = .{} },
|
|
470
|
-
Calc{ .overrides =
|
|
487
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = durations },
|
|
488
|
+
Calc{ .overrides = overrides },
|
|
471
489
|
);
|
|
472
490
|
copy = original;
|
|
473
491
|
overridden = copy.update(c1, c2, &override);
|
|
@@ -481,10 +499,9 @@ pub fn update(
|
|
|
481
499
|
fn expectEqualActions(expected: Actions, actual: Actions) !void {
|
|
482
500
|
return expectEqual(expected, actual) catch |e| switch (e) {
|
|
483
501
|
error.TestExpectedEqual => {
|
|
484
|
-
std.debug.print("expected {}, found {}\n", .{ expected, actual });
|
|
502
|
+
std.debug.print("expected {f}, found {f}\n", .{ expected, actual });
|
|
485
503
|
return e;
|
|
486
504
|
},
|
|
487
|
-
else => return e,
|
|
488
505
|
};
|
|
489
506
|
}
|
|
490
507
|
|
|
@@ -503,19 +520,9 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
503
520
|
};
|
|
504
521
|
}
|
|
505
522
|
|
|
506
|
-
fn
|
|
507
|
-
for (frontier, 0..) |f, j| {
|
|
508
|
-
// TODO: is skipping this redundant check worth it?
|
|
509
|
-
if (i == j) continue;
|
|
510
|
-
if (f.matches(actions)) return true;
|
|
511
|
-
}
|
|
512
|
-
return false;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
516
|
-
const w = std.io.getStdErr().writer();
|
|
523
|
+
fn die(w: *std.Io.Writer, comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
517
524
|
w.print(fmt, v) catch return;
|
|
518
|
-
if (seed) |s| return w.print("{}\n", .{s}) catch return;
|
|
525
|
+
if (seed) |s| return w.print("{d}\n", .{s}) catch return;
|
|
519
526
|
return w.writeByte('\n') catch return;
|
|
520
527
|
}
|
|
521
528
|
|
|
@@ -531,7 +538,7 @@ const Style = struct {
|
|
|
531
538
|
indent: bool = true,
|
|
532
539
|
};
|
|
533
540
|
|
|
534
|
-
fn debug(writer:
|
|
541
|
+
fn debug(writer: *std.Io.Writer, actions: Actions, style: Style) !void {
|
|
535
542
|
if (style.indent) try writer.writeAll(" ");
|
|
536
543
|
if (tty) {
|
|
537
544
|
const mod: usize = if (style.dim) 2 else 1;
|
|
@@ -556,13 +563,13 @@ fn debug(writer: anytype, actions: Actions, style: Style) !void {
|
|
|
556
563
|
if (style.newline) try writer.writeByte('\n');
|
|
557
564
|
}
|
|
558
565
|
|
|
559
|
-
fn format(writer:
|
|
566
|
+
fn format(writer: *std.Io.Writer, actions: Actions, p1: u9, p2: u9) !void {
|
|
560
567
|
var input: [1024]u8 = undefined;
|
|
561
568
|
var output: [1024]u8 = undefined;
|
|
562
569
|
|
|
563
|
-
var
|
|
564
|
-
try actions.fmt(&
|
|
565
|
-
var len =
|
|
570
|
+
var w = std.Io.Writer.fixed(&input);
|
|
571
|
+
try actions.fmt(&w, false);
|
|
572
|
+
var len = w.buffered().len;
|
|
566
573
|
@memcpy(output[0..len], input[0..len]);
|
|
567
574
|
|
|
568
575
|
var find: [16]u8 = undefined;
|
|
@@ -584,8 +591,6 @@ fn format(writer: anytype, actions: Actions, p1: u9, p2: u9) !void {
|
|
|
584
591
|
try writer.writeAll(output[0..len]);
|
|
585
592
|
}
|
|
586
593
|
|
|
587
|
-
const NONE = @intFromEnum(Optional(bool).None);
|
|
588
|
-
|
|
589
594
|
/// Helper functions that efficiently return valid ranges for various RNG events based on the
|
|
590
595
|
/// state of an `Action` and other events to be used to construct a "transitions" function.
|
|
591
596
|
pub const Rolls = struct {
|
|
@@ -597,6 +602,129 @@ pub const Rolls = struct {
|
|
|
597
602
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
598
603
|
}
|
|
599
604
|
|
|
605
|
+
const SLP_NONE = [_]Optional(Observation(.sleep)){.None};
|
|
606
|
+
const SLP_STARTED = [_]Optional(Observation(.sleep)){.started};
|
|
607
|
+
const SLP_ENDED = [_]Optional(Observation(.sleep)){.ended};
|
|
608
|
+
const SLP_ALL = [_]Optional(Observation(.sleep)){ .started, .continuing, .ended };
|
|
609
|
+
const SLP = [_]Optional(Observation(.sleep)){ .continuing, .ended };
|
|
610
|
+
|
|
611
|
+
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
612
|
+
/// `durations`.
|
|
613
|
+
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation(.sleep)) {
|
|
614
|
+
if (action.sleep == .None) return &SLP_NONE;
|
|
615
|
+
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
616
|
+
&SLP_ALL
|
|
617
|
+
else switch (action.sleep) {
|
|
618
|
+
.started => &SLP_STARTED,
|
|
619
|
+
else => if (Sleeps.get(duration.sleeps, 0) >= 7) &SLP_ENDED else &SLP,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const DIS_NONE = [_]Optional(Observation(.disable)){.None};
|
|
624
|
+
const DIS_STARTED = [_]Optional(Observation(.disable)){.started};
|
|
625
|
+
const DIS_CONTINUING = [_]Optional(Observation(.disable)){.continuing};
|
|
626
|
+
const DIS_ENDED = [_]Optional(Observation(.disable)){.ended};
|
|
627
|
+
const DIS_ALL = [_]Optional(Observation(.disable)){ .started, .continuing, .ended };
|
|
628
|
+
const DIS = [_]Optional(Observation(.disable)){ .continuing, .ended };
|
|
629
|
+
|
|
630
|
+
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
631
|
+
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
632
|
+
pub fn disable(
|
|
633
|
+
action: Action,
|
|
634
|
+
duration: Duration,
|
|
635
|
+
parent: Optional(Observation(.sleep)),
|
|
636
|
+
) []const Optional(Observation(.disable)) {
|
|
637
|
+
if (action.disable == .None) return &DIS_NONE;
|
|
638
|
+
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
639
|
+
&DIS_ALL
|
|
640
|
+
else switch (action.disable) {
|
|
641
|
+
.started => &DIS_STARTED,
|
|
642
|
+
else => if (duration.disable >= 8)
|
|
643
|
+
&DIS_ENDED
|
|
644
|
+
else if (parent != .None and parent != .started)
|
|
645
|
+
&DIS_CONTINUING
|
|
646
|
+
else
|
|
647
|
+
&DIS,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const ATK_NONE = [_]Optional(Observation(.attacking)){.None};
|
|
652
|
+
const ATK_STARTED = [_]Optional(Observation(.attacking)){.started};
|
|
653
|
+
const ATK_CONTINUING = [_]Optional(Observation(.attacking)){.continuing};
|
|
654
|
+
const ATK_ENDED = [_]Optional(Observation(.attacking)){.ended};
|
|
655
|
+
const ATK = [_]Optional(Observation(.attacking)){ .continuing, .ended };
|
|
656
|
+
|
|
657
|
+
/// FIXME document
|
|
658
|
+
pub fn attacking(
|
|
659
|
+
action: Action,
|
|
660
|
+
duration: Duration,
|
|
661
|
+
parent: Optional(Observation(.sleep)),
|
|
662
|
+
) []const Optional(Observation(.attacking)) {
|
|
663
|
+
return switch (action.attacking) {
|
|
664
|
+
.None => &ATK_NONE,
|
|
665
|
+
.started => &ATK_STARTED,
|
|
666
|
+
else => if (duration.attacking >= 3)
|
|
667
|
+
&ATK_ENDED
|
|
668
|
+
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
669
|
+
&ATK_CONTINUING
|
|
670
|
+
else
|
|
671
|
+
&ATK,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const CFZ_NONE = [_]Optional(Observation(.confusion)){.None};
|
|
676
|
+
const CFZ_STARTED = [_]Optional(Observation(.confusion)){.started};
|
|
677
|
+
const CFZ_OVERWRITTEN = [_]Optional(Observation(.confusion)){ .started, .overwritten };
|
|
678
|
+
const CFZ_CONTINUING = [_]Optional(Observation(.confusion)){.continuing};
|
|
679
|
+
const CFZ_ENDED = [_]Optional(Observation(.confusion)){.ended};
|
|
680
|
+
const CFZ = [_]Optional(Observation(.confusion)){ .continuing, .ended };
|
|
681
|
+
const CFZ_TIE = [_]Optional(Observation(.confusion)){ .started, .continuing, .ended };
|
|
682
|
+
|
|
683
|
+
/// FIXME document
|
|
684
|
+
pub fn confusion(
|
|
685
|
+
action: Action,
|
|
686
|
+
duration: Duration,
|
|
687
|
+
tie: Optional(Player),
|
|
688
|
+
sibling: Optional(Observation(.attacking)),
|
|
689
|
+
parent: Optional(Observation(.sleep)),
|
|
690
|
+
) []const Optional(Observation(.confusion)) {
|
|
691
|
+
return switch (action.confusion) {
|
|
692
|
+
.None => &CFZ_NONE,
|
|
693
|
+
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
694
|
+
&CFZ_OVERWRITTEN
|
|
695
|
+
else
|
|
696
|
+
&CFZ_STARTED,
|
|
697
|
+
else => if (duration.confusion >= 5)
|
|
698
|
+
&CFZ_ENDED
|
|
699
|
+
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
700
|
+
&CFZ_CONTINUING
|
|
701
|
+
else if (tie != .None)
|
|
702
|
+
&CFZ_TIE
|
|
703
|
+
else
|
|
704
|
+
&CFZ,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const BND_NONE = [_]Optional(Observation(.binding)){.None};
|
|
709
|
+
const BND_STARTED = [_]Optional(Observation(.binding)){.started};
|
|
710
|
+
const BND_ENDED = [_]Optional(Observation(.binding)){.ended};
|
|
711
|
+
const BND = [_]Optional(Observation(.binding)){ .continuing, .ended };
|
|
712
|
+
|
|
713
|
+
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
714
|
+
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
715
|
+
pub fn binding(
|
|
716
|
+
action: Action,
|
|
717
|
+
duration: Duration,
|
|
718
|
+
parent: Optional(bool),
|
|
719
|
+
) []const Optional(Observation(.binding)) {
|
|
720
|
+
if (parent == .true) return &BND_NONE;
|
|
721
|
+
return switch (action.binding) {
|
|
722
|
+
.None => &BND_NONE,
|
|
723
|
+
.started => &BND_STARTED,
|
|
724
|
+
else => if (duration.binding >= 4) &BND_ENDED else &BND,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
600
728
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
601
729
|
const BOOLS = [_]Optional(bool){ .false, .true };
|
|
602
730
|
|
|
@@ -610,7 +738,7 @@ pub const Rolls = struct {
|
|
|
610
738
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
611
739
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
612
740
|
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
613
|
-
if (parent == .false) return &BOOL_NONE;
|
|
741
|
+
if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
|
|
614
742
|
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
615
743
|
}
|
|
616
744
|
|
|
@@ -622,7 +750,7 @@ pub const Rolls = struct {
|
|
|
622
750
|
}
|
|
623
751
|
|
|
624
752
|
/// The min and max bounds on iteration over damage rolls.
|
|
625
|
-
pub const Range = struct { min:
|
|
753
|
+
pub const Range = struct { min: usize, max: usize };
|
|
626
754
|
|
|
627
755
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
628
756
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
@@ -635,7 +763,7 @@ pub const Rolls = struct {
|
|
|
635
763
|
|
|
636
764
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
637
765
|
/// given the base damage in `summaries`.
|
|
638
|
-
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool)
|
|
766
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool) u8 {
|
|
639
767
|
if (roll == 0) return roll;
|
|
640
768
|
|
|
641
769
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -645,9 +773,14 @@ pub const Rolls = struct {
|
|
|
645
773
|
return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
|
|
646
774
|
}
|
|
647
775
|
|
|
648
|
-
/// Returns a slice with the correct range of values for confused
|
|
649
|
-
|
|
650
|
-
|
|
776
|
+
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
777
|
+
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
778
|
+
pub fn confused(
|
|
779
|
+
action: Action,
|
|
780
|
+
parent: Optional(Observation(.confusion)),
|
|
781
|
+
) []const Optional(bool) {
|
|
782
|
+
const done = parent == .None or parent == .ended;
|
|
783
|
+
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
651
784
|
}
|
|
652
785
|
|
|
653
786
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
@@ -657,35 +790,24 @@ pub const Rolls = struct {
|
|
|
657
790
|
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
658
791
|
}
|
|
659
792
|
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
const DURATION = [_]u2{2};
|
|
663
|
-
|
|
664
|
-
/// Returns a slice with a range of values for duration given the `action` state
|
|
665
|
-
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
666
|
-
pub fn duration(action: Action, parent: Optional(bool)) []const u2 {
|
|
667
|
-
if (parent == .false) return &DURATION_NONE;
|
|
668
|
-
return if (action.duration == 0) &DURATION_NONE else &DURATION;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const SLOT_NONE = [_]u4{0};
|
|
672
|
-
const SLOT = [_]u4{ 1, 2, 3, 4 };
|
|
793
|
+
const SLOT_NONE = [_]u3{0};
|
|
794
|
+
const SLOT = [_]u3{ 1, 2, 3, 4 };
|
|
673
795
|
|
|
674
796
|
/// Returns a slice with a range of values for move slots given the `action` state
|
|
675
797
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
676
798
|
///
|
|
677
799
|
/// These slots may or **may not be valid** as slots may be unset / have 0 PP.
|
|
678
|
-
pub fn moveSlot(action: Action, parent: Optional(bool)) []const
|
|
800
|
+
pub fn moveSlot(action: Action, parent: Optional(bool)) []const u3 {
|
|
679
801
|
if (parent == .false) return &SLOT_NONE;
|
|
680
802
|
return if (action.move_slot == 0) &SLOT_NONE else &SLOT;
|
|
681
803
|
}
|
|
682
804
|
|
|
683
|
-
const MULTI_NONE = [_]
|
|
684
|
-
const MULTI = [_]
|
|
805
|
+
const MULTI_NONE = [_]u3{0};
|
|
806
|
+
const MULTI = [_]u3{ 2, 3, 4, 5 };
|
|
685
807
|
|
|
686
808
|
/// Returns a slice with the correct range of values for multi hit given the `action` state
|
|
687
809
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
688
|
-
pub fn multiHit(action: Action, parent: Optional(bool)) []const
|
|
810
|
+
pub fn multiHit(action: Action, parent: Optional(bool)) []const u3 {
|
|
689
811
|
if (parent == .false) return &MULTI_NONE;
|
|
690
812
|
return if (action.multi_hit == 0) &MULTI_NONE else &MULTI;
|
|
691
813
|
}
|
|
@@ -725,6 +847,26 @@ test "Rolls.speedTie" {
|
|
|
725
847
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
726
848
|
}
|
|
727
849
|
|
|
850
|
+
test "Rolls.sleep" {
|
|
851
|
+
return error.SkipZigTest; // TODO
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
test "Rolls.disable" {
|
|
855
|
+
return error.SkipZigTest; // TODO
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
test "Rolls.confusion" {
|
|
859
|
+
return error.SkipZigTest; // TODO
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
test "Rolls.atacking" {
|
|
863
|
+
return error.SkipZigTest; // TODO
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
test "Rolls.binding" {
|
|
867
|
+
return error.SkipZigTest; // TODO
|
|
868
|
+
}
|
|
869
|
+
|
|
728
870
|
test "Rolls.damage" {
|
|
729
871
|
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
730
872
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
@@ -734,12 +876,12 @@ test "Rolls.damage" {
|
|
|
734
876
|
|
|
735
877
|
test "Rolls.coalesce" {
|
|
736
878
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
737
|
-
try expectEqual(0,
|
|
738
|
-
try expectEqual(241,
|
|
739
|
-
try expectEqual(255,
|
|
879
|
+
try expectEqual(@as(u8, 0), Rolls.coalesce(.P2, 0, &summaries, false));
|
|
880
|
+
try expectEqual(@as(u8, 241), Rolls.coalesce(.P2, 238, &summaries, false));
|
|
881
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 238, &summaries, true));
|
|
740
882
|
summaries.p1.damage.final = 74;
|
|
741
|
-
try expectEqual(217,
|
|
742
|
-
try expectEqual(255,
|
|
883
|
+
try expectEqual(@as(u8, 217), Rolls.coalesce(.P2, 217, &summaries, false));
|
|
884
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 217, &summaries, true));
|
|
743
885
|
}
|
|
744
886
|
|
|
745
887
|
test "Rolls.hit" {
|
|
@@ -768,14 +910,23 @@ test "Rolls.criticalHit" {
|
|
|
768
910
|
&.{ .false, .true },
|
|
769
911
|
Rolls.criticalHit(actions.p1, .None),
|
|
770
912
|
);
|
|
771
|
-
try expectEqualSlices(
|
|
913
|
+
try expectEqualSlices(
|
|
914
|
+
Optional(bool),
|
|
915
|
+
if (pkmn.options.showdown) &.{.None} else &.{ .false, .true },
|
|
916
|
+
Rolls.criticalHit(actions.p1, .false),
|
|
917
|
+
);
|
|
772
918
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
|
|
773
919
|
}
|
|
774
920
|
|
|
775
921
|
test "Rolls.confused" {
|
|
776
922
|
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
777
|
-
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
|
|
778
|
-
try expectEqualSlices(
|
|
923
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, .None));
|
|
924
|
+
try expectEqualSlices(
|
|
925
|
+
Optional(bool),
|
|
926
|
+
&.{ .false, .true },
|
|
927
|
+
Rolls.confused(actions.p2, .continuing),
|
|
928
|
+
);
|
|
929
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, .ended));
|
|
779
930
|
}
|
|
780
931
|
|
|
781
932
|
test "Rolls.paralyzed" {
|
|
@@ -786,25 +937,18 @@ test "Rolls.paralyzed" {
|
|
|
786
937
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.paralyzed(actions.p2, .true));
|
|
787
938
|
}
|
|
788
939
|
|
|
789
|
-
test "Rolls.duration" {
|
|
790
|
-
const actions: Actions = .{ .p2 = .{ .duration = 3 } };
|
|
791
|
-
try expectEqualSlices(u2, &.{0}, Rolls.duration(actions.p1, .None));
|
|
792
|
-
try expectEqualSlices(u2, &.{2}, Rolls.duration(actions.p2, .None));
|
|
793
|
-
try expectEqualSlices(u2, &.{0}, Rolls.duration(actions.p2, .false));
|
|
794
|
-
}
|
|
795
|
-
|
|
796
940
|
test "Rolls.moveSlot" {
|
|
797
941
|
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
798
|
-
try expectEqualSlices(
|
|
799
|
-
try expectEqualSlices(
|
|
800
|
-
try expectEqualSlices(
|
|
942
|
+
try expectEqualSlices(u3, &.{0}, Rolls.moveSlot(actions.p1, .None));
|
|
943
|
+
try expectEqualSlices(u3, &.{ 1, 2, 3, 4 }, Rolls.moveSlot(actions.p2, .None));
|
|
944
|
+
try expectEqualSlices(u3, &.{0}, Rolls.moveSlot(actions.p2, .false));
|
|
801
945
|
}
|
|
802
946
|
|
|
803
947
|
test "Rolls.multiHit" {
|
|
804
948
|
const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
|
|
805
|
-
try expectEqualSlices(
|
|
806
|
-
try expectEqualSlices(
|
|
807
|
-
try expectEqualSlices(
|
|
949
|
+
try expectEqualSlices(u3, &.{0}, Rolls.multiHit(actions.p1, .None));
|
|
950
|
+
try expectEqualSlices(u3, &.{ 2, 3, 4, 5 }, Rolls.multiHit(actions.p2, .None));
|
|
951
|
+
try expectEqualSlices(u3, &.{0}, Rolls.multiHit(actions.p2, .false));
|
|
808
952
|
}
|
|
809
953
|
|
|
810
954
|
test "Rolls.metronome" {
|
|
@@ -823,9 +967,9 @@ test "Rolls.psywave" {
|
|
|
823
967
|
|
|
824
968
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
|
|
825
969
|
var rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
826
|
-
try expectEqual(150, rolls[rolls.len - 1]);
|
|
970
|
+
try expectEqual(@as(u8, 150), rolls[rolls.len - 1]);
|
|
827
971
|
side.stored().level = 81;
|
|
828
972
|
rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
829
|
-
try expectEqual(121, rolls[rolls.len - 1]);
|
|
973
|
+
try expectEqual(@as(u8, 121), rolls[rolls.len - 1]);
|
|
830
974
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
|
|
831
975
|
}
|