@pkmn/engine 0.1.0-dev.f3aab252 → 0.1.0-dev.f3b63b86
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 +35 -8
- 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 +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 +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 +235 -213
- package/package.json +59 -141
- package/src/bin/install-pkmn-engine +25 -9
- 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 +5 -12
- package/src/lib/common/debug.zig +30 -9
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +11 -21
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +180 -171
- package/src/lib/common/rational.zig +278 -285
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +14 -31
- package/src/lib/gen1/calc.zig +258 -106
- package/src/lib/gen1/chance.zig +57 -54
- 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 +12 -16
- package/src/lib/gen1/helpers.zig +13 -28
- package/src/lib/gen1/mechanics.zig +131 -101
- package/src/lib/gen2/calc.zig +12 -23
- package/src/lib/gen2/chance.zig +12 -18
- 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 +19 -22
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +50 -47
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +98 -19
- 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,48 +1,40 @@
|
|
|
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
|
|
42
37
|
|
|
43
|
-
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
44
|
-
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
45
|
-
|
|
46
38
|
/// Information relevant to damage calculation that occured during a Generation I battle `update`.
|
|
47
39
|
pub const Summaries = extern struct {
|
|
48
40
|
/// Relevant information for Player 1.
|
|
@@ -104,17 +96,25 @@ pub const Calc = struct {
|
|
|
104
96
|
self: Calc,
|
|
105
97
|
player: Player,
|
|
106
98
|
comptime field: Action.Field,
|
|
107
|
-
)
|
|
99
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
108
100
|
if (!enabled) return null;
|
|
109
101
|
|
|
110
102
|
const val = @field(self.overrides.get(player), @tagName(field));
|
|
111
103
|
return if (switch (@typeInfo(@TypeOf(val))) {
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
.@"enum" => val != .None,
|
|
105
|
+
.int => val != 0,
|
|
114
106
|
else => unreachable,
|
|
115
107
|
}) val else null;
|
|
116
108
|
}
|
|
117
109
|
|
|
110
|
+
pub fn confusion(self: *Calc, player: Player) void {
|
|
111
|
+
if (!enabled) return;
|
|
112
|
+
|
|
113
|
+
if (self.overrides.get(player).confusion == .started) {
|
|
114
|
+
self.overrides.get(player).confusion = .None;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
118
|
pub fn base(self: *Calc, player: Player, val: u16) void {
|
|
119
119
|
if (!enabled) return;
|
|
120
120
|
|
|
@@ -143,11 +143,15 @@ const Null = struct {
|
|
|
143
143
|
self: Null,
|
|
144
144
|
player: Player,
|
|
145
145
|
comptime field: Action.Field,
|
|
146
|
-
)
|
|
146
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
147
147
|
_ = .{ self, player };
|
|
148
148
|
return null;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
pub fn confusion(self: Null, player: Player) void {
|
|
152
|
+
_ = .{ self, player };
|
|
153
|
+
}
|
|
154
|
+
|
|
151
155
|
pub fn base(self: Null, player: Player, val: u16) void {
|
|
152
156
|
_ = .{ self, player, val };
|
|
153
157
|
}
|
|
@@ -175,14 +179,15 @@ pub const Options = struct {
|
|
|
175
179
|
metronome: bool = false,
|
|
176
180
|
};
|
|
177
181
|
|
|
178
|
-
pub const MAX_FRONTIER =
|
|
182
|
+
pub const MAX_FRONTIER = 83;
|
|
179
183
|
|
|
180
184
|
pub fn transitions(
|
|
181
185
|
battle: anytype,
|
|
182
186
|
c1: Choice,
|
|
183
187
|
c2: Choice,
|
|
184
188
|
allocator: std.mem.Allocator,
|
|
185
|
-
|
|
189
|
+
out: *std.Io.Writer,
|
|
190
|
+
err: *std.Io.Writer,
|
|
186
191
|
options: Options,
|
|
187
192
|
) !?Stats {
|
|
188
193
|
var stats: Stats = .{};
|
|
@@ -191,12 +196,14 @@ pub fn transitions(
|
|
|
191
196
|
|
|
192
197
|
var seen = std.AutoHashMap(Actions, void).init(allocator);
|
|
193
198
|
defer seen.deinit();
|
|
194
|
-
var frontier
|
|
195
|
-
defer frontier.deinit();
|
|
199
|
+
var frontier: std.array_list.Aligned(Actions, null) = .empty;
|
|
200
|
+
defer frontier.deinit(allocator);
|
|
201
|
+
|
|
202
|
+
const d = options.durations;
|
|
196
203
|
|
|
197
204
|
var opts = pkmn.battle.options(
|
|
198
205
|
protocol.NULL,
|
|
199
|
-
Chance(Rational(u128)){ .probability = .{}, .durations =
|
|
206
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = d },
|
|
200
207
|
Calc{},
|
|
201
208
|
);
|
|
202
209
|
|
|
@@ -207,8 +214,8 @@ pub fn transitions(
|
|
|
207
214
|
const p1 = b.side(.P1);
|
|
208
215
|
const p2 = b.side(.P2);
|
|
209
216
|
|
|
210
|
-
var p: Rational(
|
|
211
|
-
try frontier.append(opts.chance.actions);
|
|
217
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
218
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
212
219
|
|
|
213
220
|
// zig fmt: off
|
|
214
221
|
for (Rolls.metronome(frontier.items[0].p1)) |p1_move| {
|
|
@@ -220,8 +227,11 @@ pub fn transitions(
|
|
|
220
227
|
assert(frontier.items.len == 1);
|
|
221
228
|
while (i < frontier.items.len) : (i += 1) {
|
|
222
229
|
const f = frontier.items[i];
|
|
230
|
+
const saved = stats.saved;
|
|
231
|
+
|
|
232
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
223
233
|
|
|
224
|
-
try debug(
|
|
234
|
+
try debug(out, f, .{
|
|
225
235
|
.shape = true,
|
|
226
236
|
.color = i,
|
|
227
237
|
.bold = 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, tie, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
255
|
+
for (Rolls.confusion(f.p2, d.p2, tie, 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;
|
|
@@ -258,15 +278,15 @@ pub fn transitions(
|
|
|
258
278
|
|
|
259
279
|
var p2_dmg = Rolls.damage(f.p2, p2_hit);
|
|
260
280
|
|
|
261
|
-
const p1_min: u9 = p1_dmg.min;
|
|
262
|
-
const p2_min: u9 = p2_dmg.min;
|
|
281
|
+
const p1_min: u9 = @intCast(p1_dmg.min);
|
|
282
|
+
const p2_min: u9 = @intCast(p2_dmg.min);
|
|
263
283
|
|
|
264
284
|
while (p2_dmg.min < p2_dmg.max) : (p2_dmg.min += 1) {
|
|
265
285
|
a.p2.damage = @intCast(p2_dmg.min);
|
|
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;
|
|
@@ -276,16 +296,16 @@ pub fn transitions(
|
|
|
276
296
|
|
|
277
297
|
const summaries = &opts.calc.summaries;
|
|
278
298
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
279
|
-
p1_dmg.min
|
|
299
|
+
@intCast(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
|
-
try debug(
|
|
308
|
+
try debug(out, opts.chance.actions, .{
|
|
289
309
|
.p1_max = p1_max,
|
|
290
310
|
.p2_max = p2_max,
|
|
291
311
|
.color = i,
|
|
@@ -299,7 +319,7 @@ pub fn transitions(
|
|
|
299
319
|
}
|
|
300
320
|
|
|
301
321
|
if (!summary) {
|
|
302
|
-
try debug(
|
|
322
|
+
try debug(out, opts.chance.actions, .{
|
|
303
323
|
.p1_max = p1_max,
|
|
304
324
|
.p2_max = p2_max,
|
|
305
325
|
.color = i,
|
|
@@ -312,33 +332,38 @@ pub fn transitions(
|
|
|
312
332
|
acts.p1.damage = @intCast(p1d);
|
|
313
333
|
acts.p2.damage = @intCast(p2d);
|
|
314
334
|
if ((try seen.getOrPut(acts)).found_existing) {
|
|
315
|
-
err
|
|
335
|
+
die(err, "already seen {f}", .{acts}, options.seed);
|
|
316
336
|
return error.TestUnexpectedResult;
|
|
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
|
+
try r.add(q);
|
|
347
|
+
r.reduce();
|
|
348
|
+
|
|
324
349
|
stats.saved += 1;
|
|
325
350
|
|
|
326
351
|
if (p.q < p.p) {
|
|
327
|
-
err
|
|
352
|
+
die(err, "improper fraction {f}", .{p}, options.seed);
|
|
328
353
|
return error.TestUnexpectedResult;
|
|
329
354
|
}
|
|
330
355
|
} else {
|
|
331
|
-
if (!
|
|
332
|
-
try frontier.append(opts.chance.actions);
|
|
356
|
+
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
357
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
333
358
|
|
|
334
|
-
try debug(
|
|
359
|
+
try debug(out, opts.chance.actions, .{
|
|
335
360
|
.p1_max = p1_max,
|
|
336
361
|
.p2_max = p2_max,
|
|
337
362
|
.dim = true,
|
|
338
363
|
.newline = false,
|
|
339
364
|
});
|
|
340
|
-
try
|
|
341
|
-
try debug(
|
|
365
|
+
try out.writeAll(" → ");
|
|
366
|
+
try debug(out, opts.chance.actions, .{
|
|
342
367
|
.shape = true,
|
|
343
368
|
.color = frontier.items.len - 1,
|
|
344
369
|
.dim = true,
|
|
@@ -346,7 +371,7 @@ pub fn transitions(
|
|
|
346
371
|
.indent = false,
|
|
347
372
|
});
|
|
348
373
|
} else if (!summary) {
|
|
349
|
-
try debug(
|
|
374
|
+
try debug(out, opts.chance.actions, .{
|
|
350
375
|
.p1_max = p1_max,
|
|
351
376
|
.p2_max = p2_max,
|
|
352
377
|
.dim = true,
|
|
@@ -358,15 +383,15 @@ pub fn transitions(
|
|
|
358
383
|
p2_dmg.min = p2_max;
|
|
359
384
|
}
|
|
360
385
|
|
|
361
|
-
}}}}}}}}}}}}}}}}}}
|
|
386
|
+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
362
387
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
388
|
+
assert(stats.saved > saved);
|
|
389
|
+
// TODO: LLVM - float_from_int from 'u256' without intrinsics
|
|
390
|
+
// try out.print(
|
|
391
|
+
// " {f} ({d:.2}%)\n = ──────────\n {f} ({d:.2}%)\n\n",
|
|
392
|
+
// .{r, 100 * @as(f128, @floatFromInt(r.p)) / @as(f128, @floatFromInt(r.q)),
|
|
393
|
+
// p, 100 * @as(f128, @floatFromInt(p.p)) / @as(f128, @floatFromInt(p.q))},
|
|
394
|
+
// );
|
|
370
395
|
|
|
371
396
|
}
|
|
372
397
|
|
|
@@ -381,7 +406,7 @@ pub fn transitions(
|
|
|
381
406
|
|
|
382
407
|
p.reduce();
|
|
383
408
|
if (p.p != 1 or p.q != 1) {
|
|
384
|
-
err
|
|
409
|
+
die(err, "expected 1, found {f}", .{p}, options.seed);
|
|
385
410
|
return error.TestExpectedEqual;
|
|
386
411
|
}
|
|
387
412
|
|
|
@@ -394,7 +419,8 @@ pub fn update(
|
|
|
394
419
|
c2: Choice,
|
|
395
420
|
options: anytype,
|
|
396
421
|
allocator: std.mem.Allocator,
|
|
397
|
-
|
|
422
|
+
out: *std.Io.Writer,
|
|
423
|
+
err: *std.Io.Writer,
|
|
398
424
|
transition: bool,
|
|
399
425
|
) !Result {
|
|
400
426
|
const durations = options.chance.durations;
|
|
@@ -412,8 +438,8 @@ pub fn update(
|
|
|
412
438
|
if (@TypeOf(battle.rng) == data.PRNG) {
|
|
413
439
|
// Ensure we can encode the diffs in less than MAX_DIFFS bytes.
|
|
414
440
|
var buf: [helpers.MAX_DIFFS]u8 = undefined;
|
|
415
|
-
var
|
|
416
|
-
const n = try helpers.diff(&original, battle,
|
|
441
|
+
var pw: protocol.Writer = .{ .buffer = &buf };
|
|
442
|
+
const n = try helpers.diff(&original, battle, &pw);
|
|
417
443
|
|
|
418
444
|
// Applying the diff to the battle should take us back to the original copy
|
|
419
445
|
// of the battle (ignoring the RNG).
|
|
@@ -427,7 +453,7 @@ pub fn update(
|
|
|
427
453
|
// Ensure we can generate all transitions from the same original state
|
|
428
454
|
// (we must change the battle's RNG from a FixedRNG to a PRNG because
|
|
429
455
|
// the transitions function relies on RNG for discovery of states)
|
|
430
|
-
if (try transitions(unfix(original), c1, c2, allocator,
|
|
456
|
+
if (try transitions(unfix(original), c1, c2, allocator, out, err, .{
|
|
431
457
|
.durations = durations,
|
|
432
458
|
.cap = true,
|
|
433
459
|
})) |stats| try expect(stats.frontier <= MAX_FRONTIER);
|
|
@@ -474,10 +500,9 @@ pub fn update(
|
|
|
474
500
|
fn expectEqualActions(expected: Actions, actual: Actions) !void {
|
|
475
501
|
return expectEqual(expected, actual) catch |e| switch (e) {
|
|
476
502
|
error.TestExpectedEqual => {
|
|
477
|
-
std.debug.print("expected {}, found {}\n", .{ expected, actual });
|
|
503
|
+
std.debug.print("expected {f}, found {f}\n", .{ expected, actual });
|
|
478
504
|
return e;
|
|
479
505
|
},
|
|
480
|
-
else => return e,
|
|
481
506
|
};
|
|
482
507
|
}
|
|
483
508
|
|
|
@@ -496,19 +521,9 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
496
521
|
};
|
|
497
522
|
}
|
|
498
523
|
|
|
499
|
-
fn
|
|
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
|
-
fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
509
|
-
const w = std.io.getStdErr().writer();
|
|
524
|
+
fn die(w: *std.Io.Writer, comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
510
525
|
w.print(fmt, v) catch return;
|
|
511
|
-
if (seed) |s| return w.print("{}\n", .{s}) catch return;
|
|
526
|
+
if (seed) |s| return w.print("{d}\n", .{s}) catch return;
|
|
512
527
|
return w.writeByte('\n') catch return;
|
|
513
528
|
}
|
|
514
529
|
|
|
@@ -524,7 +539,7 @@ const Style = struct {
|
|
|
524
539
|
indent: bool = true,
|
|
525
540
|
};
|
|
526
541
|
|
|
527
|
-
fn debug(writer:
|
|
542
|
+
fn debug(writer: *std.Io.Writer, actions: Actions, style: Style) !void {
|
|
528
543
|
if (style.indent) try writer.writeAll(" ");
|
|
529
544
|
if (tty) {
|
|
530
545
|
const mod: usize = if (style.dim) 2 else 1;
|
|
@@ -549,13 +564,13 @@ fn debug(writer: anytype, actions: Actions, style: Style) !void {
|
|
|
549
564
|
if (style.newline) try writer.writeByte('\n');
|
|
550
565
|
}
|
|
551
566
|
|
|
552
|
-
fn format(writer:
|
|
567
|
+
fn format(writer: *std.Io.Writer, actions: Actions, p1: u9, p2: u9) !void {
|
|
553
568
|
var input: [1024]u8 = undefined;
|
|
554
569
|
var output: [1024]u8 = undefined;
|
|
555
570
|
|
|
556
|
-
var
|
|
557
|
-
try actions.fmt(&
|
|
558
|
-
var len =
|
|
571
|
+
var w = std.Io.Writer.fixed(&input);
|
|
572
|
+
try actions.fmt(&w, false);
|
|
573
|
+
var len = w.buffered().len;
|
|
559
574
|
@memcpy(output[0..len], input[0..len]);
|
|
560
575
|
|
|
561
576
|
var find: [16]u8 = undefined;
|
|
@@ -588,6 +603,112 @@ pub const Rolls = struct {
|
|
|
588
603
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
589
604
|
}
|
|
590
605
|
|
|
606
|
+
const OBS_NONE = [_]Optional(Observation){.None};
|
|
607
|
+
const OBS_STARTED = [_]Optional(Observation){.started};
|
|
608
|
+
const OBS_CONTINUING = [_]Optional(Observation){.continuing};
|
|
609
|
+
const OBS_ENDED = [_]Optional(Observation){.ended};
|
|
610
|
+
const OBS_ALL = [_]Optional(Observation){ .started, .continuing, .ended };
|
|
611
|
+
const OBS = [_]Optional(Observation){ .continuing, .ended };
|
|
612
|
+
|
|
613
|
+
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
614
|
+
/// `durations`.
|
|
615
|
+
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
|
|
616
|
+
if (action.sleep == .None) return &OBS_NONE;
|
|
617
|
+
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
618
|
+
&OBS_ALL
|
|
619
|
+
else switch (action.sleep) {
|
|
620
|
+
.started => &OBS_STARTED,
|
|
621
|
+
else => if (Sleeps.get(duration.sleeps, 0) >= 7) &OBS_ENDED else &OBS,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
626
|
+
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
627
|
+
pub fn disable(
|
|
628
|
+
action: Action,
|
|
629
|
+
duration: Duration,
|
|
630
|
+
parent: Optional(Observation),
|
|
631
|
+
) []const Optional(Observation) {
|
|
632
|
+
if (action.disable == .None) return &OBS_NONE;
|
|
633
|
+
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
634
|
+
&OBS_ALL
|
|
635
|
+
else switch (action.disable) {
|
|
636
|
+
.started => &OBS_STARTED,
|
|
637
|
+
else => if (duration.disable >= 8)
|
|
638
|
+
&OBS_ENDED
|
|
639
|
+
else if (parent != .None and parent != .started)
|
|
640
|
+
&OBS_CONTINUING
|
|
641
|
+
else
|
|
642
|
+
&OBS,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/// FIXME document
|
|
647
|
+
pub fn attacking(
|
|
648
|
+
action: Action,
|
|
649
|
+
duration: Duration,
|
|
650
|
+
parent: Optional(Observation),
|
|
651
|
+
) []const Optional(Observation) {
|
|
652
|
+
return switch (action.attacking) {
|
|
653
|
+
.None => &OBS_NONE,
|
|
654
|
+
.started => &OBS_STARTED,
|
|
655
|
+
else => if (duration.attacking >= 3)
|
|
656
|
+
&OBS_ENDED
|
|
657
|
+
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
658
|
+
&OBS_CONTINUING
|
|
659
|
+
else
|
|
660
|
+
&OBS,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const CFZ_NONE = [_]Optional(Confusion){.None};
|
|
665
|
+
const CFZ_STARTED = [_]Optional(Confusion){.started};
|
|
666
|
+
const CFZ_OVERWRITTEN = [_]Optional(Confusion){ .started, .overwritten };
|
|
667
|
+
const CFZ_CONTINUING = [_]Optional(Confusion){.continuing};
|
|
668
|
+
const CFZ_ENDED = [_]Optional(Confusion){.ended};
|
|
669
|
+
const CFZ = [_]Optional(Confusion){ .continuing, .ended };
|
|
670
|
+
const CFZ_TIE = [_]Optional(Confusion){ .started, .continuing, .ended };
|
|
671
|
+
|
|
672
|
+
/// FIXME document
|
|
673
|
+
pub fn confusion(
|
|
674
|
+
action: Action,
|
|
675
|
+
duration: Duration,
|
|
676
|
+
tie: Optional(Player),
|
|
677
|
+
sibling: Optional(Observation),
|
|
678
|
+
parent: Optional(Observation),
|
|
679
|
+
) []const Optional(Confusion) {
|
|
680
|
+
return switch (action.confusion) {
|
|
681
|
+
.None => &CFZ_NONE,
|
|
682
|
+
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
683
|
+
&CFZ_OVERWRITTEN
|
|
684
|
+
else
|
|
685
|
+
&CFZ_STARTED,
|
|
686
|
+
else => if (duration.confusion >= 5)
|
|
687
|
+
&CFZ_ENDED
|
|
688
|
+
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
689
|
+
&CFZ_CONTINUING
|
|
690
|
+
else if (tie != .None)
|
|
691
|
+
&CFZ_TIE
|
|
692
|
+
else
|
|
693
|
+
&CFZ,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
698
|
+
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
699
|
+
pub fn binding(
|
|
700
|
+
action: Action,
|
|
701
|
+
duration: Duration,
|
|
702
|
+
parent: Optional(bool),
|
|
703
|
+
) []const Optional(Observation) {
|
|
704
|
+
if (parent == .true) return &OBS_NONE;
|
|
705
|
+
return switch (action.binding) {
|
|
706
|
+
.None => &OBS_NONE,
|
|
707
|
+
.started => &OBS_STARTED,
|
|
708
|
+
else => if (duration.binding >= 4) &OBS_ENDED else &OBS,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
591
712
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
592
713
|
const BOOLS = [_]Optional(bool){ .false, .true };
|
|
593
714
|
|
|
@@ -601,7 +722,7 @@ pub const Rolls = struct {
|
|
|
601
722
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
602
723
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
603
724
|
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
604
|
-
if (parent == .false) return &BOOL_NONE;
|
|
725
|
+
if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
|
|
605
726
|
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
606
727
|
}
|
|
607
728
|
|
|
@@ -613,7 +734,7 @@ pub const Rolls = struct {
|
|
|
613
734
|
}
|
|
614
735
|
|
|
615
736
|
/// The min and max bounds on iteration over damage rolls.
|
|
616
|
-
pub const Range = struct { min:
|
|
737
|
+
pub const Range = struct { min: usize, max: usize };
|
|
617
738
|
|
|
618
739
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
619
740
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
@@ -626,7 +747,7 @@ pub const Rolls = struct {
|
|
|
626
747
|
|
|
627
748
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
628
749
|
/// given the base damage in `summaries`.
|
|
629
|
-
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool)
|
|
750
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool) u8 {
|
|
630
751
|
if (roll == 0) return roll;
|
|
631
752
|
|
|
632
753
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -636,9 +757,11 @@ pub const Rolls = struct {
|
|
|
636
757
|
return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
|
|
637
758
|
}
|
|
638
759
|
|
|
639
|
-
/// Returns a slice with the correct range of values for confused
|
|
640
|
-
|
|
641
|
-
|
|
760
|
+
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
761
|
+
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
762
|
+
pub fn confused(action: Action, parent: Optional(Confusion)) []const Optional(bool) {
|
|
763
|
+
const done = parent == .None or parent == .ended;
|
|
764
|
+
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
642
765
|
}
|
|
643
766
|
|
|
644
767
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
@@ -705,6 +828,26 @@ test "Rolls.speedTie" {
|
|
|
705
828
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
706
829
|
}
|
|
707
830
|
|
|
831
|
+
test "Rolls.sleep" {
|
|
832
|
+
return error.SkipZigTest; // TODO
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
test "Rolls.disable" {
|
|
836
|
+
return error.SkipZigTest; // TODO
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
test "Rolls.confusion" {
|
|
840
|
+
return error.SkipZigTest; // TODO
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
test "Rolls.atacking" {
|
|
844
|
+
return error.SkipZigTest; // TODO
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
test "Rolls.binding" {
|
|
848
|
+
return error.SkipZigTest; // TODO
|
|
849
|
+
}
|
|
850
|
+
|
|
708
851
|
test "Rolls.damage" {
|
|
709
852
|
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
710
853
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
@@ -714,12 +857,12 @@ test "Rolls.damage" {
|
|
|
714
857
|
|
|
715
858
|
test "Rolls.coalesce" {
|
|
716
859
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
717
|
-
try expectEqual(@as(u8, 0),
|
|
718
|
-
try expectEqual(@as(u8, 241),
|
|
719
|
-
try expectEqual(@as(u8, 255),
|
|
860
|
+
try expectEqual(@as(u8, 0), Rolls.coalesce(.P2, 0, &summaries, false));
|
|
861
|
+
try expectEqual(@as(u8, 241), Rolls.coalesce(.P2, 238, &summaries, false));
|
|
862
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 238, &summaries, true));
|
|
720
863
|
summaries.p1.damage.final = 74;
|
|
721
|
-
try expectEqual(@as(u8, 217),
|
|
722
|
-
try expectEqual(@as(u8, 255),
|
|
864
|
+
try expectEqual(@as(u8, 217), Rolls.coalesce(.P2, 217, &summaries, false));
|
|
865
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 217, &summaries, true));
|
|
723
866
|
}
|
|
724
867
|
|
|
725
868
|
test "Rolls.hit" {
|
|
@@ -748,14 +891,23 @@ test "Rolls.criticalHit" {
|
|
|
748
891
|
&.{ .false, .true },
|
|
749
892
|
Rolls.criticalHit(actions.p1, .None),
|
|
750
893
|
);
|
|
751
|
-
try expectEqualSlices(
|
|
894
|
+
try expectEqualSlices(
|
|
895
|
+
Optional(bool),
|
|
896
|
+
if (pkmn.options.showdown) &.{.None} else &.{ .false, .true },
|
|
897
|
+
Rolls.criticalHit(actions.p1, .false),
|
|
898
|
+
);
|
|
752
899
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
|
|
753
900
|
}
|
|
754
901
|
|
|
755
902
|
test "Rolls.confused" {
|
|
756
903
|
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
757
|
-
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
|
|
758
|
-
try expectEqualSlices(
|
|
904
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, .None));
|
|
905
|
+
try expectEqualSlices(
|
|
906
|
+
Optional(bool),
|
|
907
|
+
&.{ .false, .true },
|
|
908
|
+
Rolls.confused(actions.p2, .continuing),
|
|
909
|
+
);
|
|
910
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, .ended));
|
|
759
911
|
}
|
|
760
912
|
|
|
761
913
|
test "Rolls.paralyzed" {
|