@pkmn/engine 0.1.0-dev.a0447192 → 0.1.0-dev.a19bc07d
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 +29 -18
- package/build/pkg/addon/browser.d.ts +1 -1
- package/build/pkg/addon/browser.js +6 -2
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +22 -8
- package/build/pkg/addon/node.js.map +1 -1
- 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 -5
- package/build/pkg/addon.js +8 -17
- 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.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 +37 -100
- package/build/pkg/index.js +23 -75
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +11 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +55 -12
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +19 -94
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +35 -15
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +20 -9
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +140 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +351 -246
- package/package.json +34 -130
- package/src/bin/install-pkmn-engine +25 -9
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +5 -12
- package/src/lib/common/debug.zig +45 -60
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +26 -26
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +228 -197
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +34 -31
- package/src/lib/gen1/calc.zig +159 -163
- package/src/lib/gen1/chance.zig +181 -105
- 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 +34 -32
- package/src/lib/gen1/mechanics.zig +103 -78
- package/src/lib/gen2/calc.zig +12 -23
- package/src/lib/gen2/chance.zig +38 -26
- 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 +32 -31
- package/src/lib/gen2/mechanics.zig +58 -55
- 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 +43 -31
- package/src/tools/display/ui.css +0 -5
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -1,52 +1,39 @@
|
|
|
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 enabled = pkmn.options.calc;
|
|
23
|
-
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
26
|
-
const Choice = common.Choice;
|
|
27
|
-
|
|
28
|
-
const Optional = optional.Optional;
|
|
29
|
-
|
|
30
|
-
const Rational = rational.Rational;
|
|
31
|
-
|
|
32
|
-
const PointerType = util.PointerType;
|
|
33
25
|
const isPointerTo = util.isPointerTo;
|
|
34
|
-
|
|
35
|
-
const Actions = chance.Actions;
|
|
36
|
-
const Action = chance.Action;
|
|
37
|
-
const Chance = chance.Chance;
|
|
38
|
-
const Confusion = chance.Confusion;
|
|
39
|
-
const Duration = chance.Duration;
|
|
40
|
-
const Durations = chance.Durations;
|
|
41
26
|
const Observation = chance.Observation;
|
|
27
|
+
const Optional = optional.Optional;
|
|
28
|
+
const Player = common.Player;
|
|
29
|
+
const PointerType = util.PointerType;
|
|
30
|
+
const Rational = rational.Rational;
|
|
31
|
+
const Result = common.Result;
|
|
42
32
|
const Sleeps = chance.Sleeps;
|
|
43
33
|
|
|
44
34
|
const tty = true; // DEBUG
|
|
45
35
|
const summary = false; // DEBUG
|
|
46
36
|
|
|
47
|
-
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
48
|
-
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
49
|
-
|
|
50
37
|
/// Information relevant to damage calculation that occured during a Generation I battle `update`.
|
|
51
38
|
pub const Summaries = extern struct {
|
|
52
39
|
/// Relevant information for Player 1.
|
|
@@ -108,13 +95,13 @@ pub const Calc = struct {
|
|
|
108
95
|
self: Calc,
|
|
109
96
|
player: Player,
|
|
110
97
|
comptime field: Action.Field,
|
|
111
|
-
)
|
|
98
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
112
99
|
if (!enabled) return null;
|
|
113
100
|
|
|
114
101
|
const val = @field(self.overrides.get(player), @tagName(field));
|
|
115
102
|
return if (switch (@typeInfo(@TypeOf(val))) {
|
|
116
|
-
|
|
117
|
-
|
|
103
|
+
.@"enum" => val != .None,
|
|
104
|
+
.int => val != 0,
|
|
118
105
|
else => unreachable,
|
|
119
106
|
}) val else null;
|
|
120
107
|
}
|
|
@@ -155,7 +142,7 @@ const Null = struct {
|
|
|
155
142
|
self: Null,
|
|
156
143
|
player: Player,
|
|
157
144
|
comptime field: Action.Field,
|
|
158
|
-
)
|
|
145
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
159
146
|
_ = .{ self, player };
|
|
160
147
|
return null;
|
|
161
148
|
}
|
|
@@ -191,24 +178,25 @@ pub const Options = struct {
|
|
|
191
178
|
metronome: bool = false,
|
|
192
179
|
};
|
|
193
180
|
|
|
194
|
-
pub const MAX_FRONTIER =
|
|
181
|
+
pub const MAX_FRONTIER = 86;
|
|
195
182
|
|
|
196
183
|
pub fn transitions(
|
|
197
184
|
battle: anytype,
|
|
198
185
|
c1: Choice,
|
|
199
186
|
c2: Choice,
|
|
200
187
|
allocator: std.mem.Allocator,
|
|
201
|
-
|
|
188
|
+
out: *std.Io.Writer,
|
|
189
|
+
err: *std.Io.Writer,
|
|
202
190
|
options: Options,
|
|
203
191
|
) !?Stats {
|
|
204
192
|
var stats: Stats = .{};
|
|
205
193
|
|
|
206
194
|
const cap = options.cap;
|
|
207
195
|
|
|
208
|
-
var seen
|
|
196
|
+
var seen: std.AutoHashMap(Actions, void) = .init(allocator);
|
|
209
197
|
defer seen.deinit();
|
|
210
|
-
var frontier
|
|
211
|
-
defer frontier.deinit();
|
|
198
|
+
var frontier: std.array_list.Aligned(Actions, null) = .empty;
|
|
199
|
+
defer frontier.deinit(allocator);
|
|
212
200
|
|
|
213
201
|
const d = options.durations;
|
|
214
202
|
|
|
@@ -225,8 +213,8 @@ pub fn transitions(
|
|
|
225
213
|
const p1 = b.side(.P1);
|
|
226
214
|
const p2 = b.side(.P2);
|
|
227
215
|
|
|
228
|
-
var p: Rational(
|
|
229
|
-
try frontier.append(opts.chance.actions);
|
|
216
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
217
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
230
218
|
|
|
231
219
|
// zig fmt: off
|
|
232
220
|
for (Rolls.metronome(frontier.items[0].p1)) |p1_move| {
|
|
@@ -240,7 +228,9 @@ pub fn transitions(
|
|
|
240
228
|
const f = frontier.items[i];
|
|
241
229
|
const saved = stats.saved;
|
|
242
230
|
|
|
243
|
-
|
|
231
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
232
|
+
|
|
233
|
+
try debug(out, f, .{
|
|
244
234
|
.shape = true,
|
|
245
235
|
.color = i,
|
|
246
236
|
.bold = true,
|
|
@@ -260,8 +250,8 @@ pub fn transitions(
|
|
|
260
250
|
for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
|
|
261
251
|
for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
|
|
262
252
|
for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
|
|
263
|
-
for (Rolls.confusion(f.p1, d.p1, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
264
|
-
for (Rolls.confusion(f.p2, d.p2, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
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;
|
|
265
255
|
for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
|
|
266
256
|
for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
|
|
267
257
|
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
@@ -287,8 +277,8 @@ pub fn transitions(
|
|
|
287
277
|
|
|
288
278
|
var p2_dmg = Rolls.damage(f.p2, p2_hit);
|
|
289
279
|
|
|
290
|
-
const p1_min: u9 = p1_dmg.min;
|
|
291
|
-
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);
|
|
292
282
|
|
|
293
283
|
while (p2_dmg.min < p2_dmg.max) : (p2_dmg.min += 1) {
|
|
294
284
|
a.p2.damage = @intCast(p2_dmg.min);
|
|
@@ -305,16 +295,16 @@ pub fn transitions(
|
|
|
305
295
|
|
|
306
296
|
const summaries = &opts.calc.summaries;
|
|
307
297
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
308
|
-
p1_dmg.min
|
|
298
|
+
@intCast(p1_dmg.min)
|
|
309
299
|
else
|
|
310
|
-
|
|
300
|
+
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
311
301
|
const p2_max: u9 =
|
|
312
|
-
|
|
302
|
+
Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
|
|
313
303
|
|
|
314
304
|
if (opts.chance.actions.matches(f)) {
|
|
315
|
-
if (!
|
|
305
|
+
if (!opts.chance.actions.relax().eql(a)) {
|
|
316
306
|
if (!summary) {
|
|
317
|
-
try debug(
|
|
307
|
+
try debug(out, opts.chance.actions, .{
|
|
318
308
|
.p1_max = p1_max,
|
|
319
309
|
.p2_max = p2_max,
|
|
320
310
|
.color = i,
|
|
@@ -328,7 +318,7 @@ pub fn transitions(
|
|
|
328
318
|
}
|
|
329
319
|
|
|
330
320
|
if (!summary) {
|
|
331
|
-
try debug(
|
|
321
|
+
try debug(out, opts.chance.actions, .{
|
|
332
322
|
.p1_max = p1_max,
|
|
333
323
|
.p2_max = p2_max,
|
|
334
324
|
.color = i,
|
|
@@ -341,7 +331,7 @@ pub fn transitions(
|
|
|
341
331
|
acts.p1.damage = @intCast(p1d);
|
|
342
332
|
acts.p2.damage = @intCast(p2d);
|
|
343
333
|
if ((try seen.getOrPut(acts)).found_existing) {
|
|
344
|
-
err
|
|
334
|
+
die(err, "already seen {f}", .{acts}, options.seed);
|
|
345
335
|
return error.TestUnexpectedResult;
|
|
346
336
|
}
|
|
347
337
|
}
|
|
@@ -352,25 +342,27 @@ pub fn transitions(
|
|
|
352
342
|
q.reduce();
|
|
353
343
|
try p.add(q);
|
|
354
344
|
p.reduce();
|
|
345
|
+
try r.add(q);
|
|
346
|
+
r.reduce();
|
|
355
347
|
|
|
356
348
|
stats.saved += 1;
|
|
357
349
|
|
|
358
350
|
if (p.q < p.p) {
|
|
359
|
-
err
|
|
351
|
+
die(err, "improper fraction {f}", .{p}, options.seed);
|
|
360
352
|
return error.TestUnexpectedResult;
|
|
361
353
|
}
|
|
362
354
|
} else {
|
|
363
|
-
if (!
|
|
364
|
-
try frontier.append(opts.chance.actions);
|
|
355
|
+
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
356
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
365
357
|
|
|
366
|
-
try debug(
|
|
358
|
+
try debug(out, opts.chance.actions, .{
|
|
367
359
|
.p1_max = p1_max,
|
|
368
360
|
.p2_max = p2_max,
|
|
369
361
|
.dim = true,
|
|
370
362
|
.newline = false,
|
|
371
363
|
});
|
|
372
|
-
try
|
|
373
|
-
try debug(
|
|
364
|
+
try out.writeAll(" → ");
|
|
365
|
+
try debug(out, opts.chance.actions, .{
|
|
374
366
|
.shape = true,
|
|
375
367
|
.color = frontier.items.len - 1,
|
|
376
368
|
.dim = true,
|
|
@@ -378,7 +370,7 @@ pub fn transitions(
|
|
|
378
370
|
.indent = false,
|
|
379
371
|
});
|
|
380
372
|
} else if (!summary) {
|
|
381
|
-
try debug(
|
|
373
|
+
try debug(out, opts.chance.actions, .{
|
|
382
374
|
.p1_max = p1_max,
|
|
383
375
|
.p2_max = p2_max,
|
|
384
376
|
.dim = true,
|
|
@@ -393,14 +385,12 @@ pub fn transitions(
|
|
|
393
385
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
394
386
|
|
|
395
387
|
assert(stats.saved > saved);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
);
|
|
403
|
-
}
|
|
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
|
+
// );
|
|
404
394
|
|
|
405
395
|
}
|
|
406
396
|
|
|
@@ -415,7 +405,7 @@ pub fn transitions(
|
|
|
415
405
|
|
|
416
406
|
p.reduce();
|
|
417
407
|
if (p.p != 1 or p.q != 1) {
|
|
418
|
-
err
|
|
408
|
+
die(err, "expected 1, found {f}", .{p}, options.seed);
|
|
419
409
|
return error.TestExpectedEqual;
|
|
420
410
|
}
|
|
421
411
|
|
|
@@ -428,7 +418,8 @@ pub fn update(
|
|
|
428
418
|
c2: Choice,
|
|
429
419
|
options: anytype,
|
|
430
420
|
allocator: std.mem.Allocator,
|
|
431
|
-
|
|
421
|
+
out: *std.Io.Writer,
|
|
422
|
+
err: *std.Io.Writer,
|
|
432
423
|
transition: bool,
|
|
433
424
|
) !Result {
|
|
434
425
|
const durations = options.chance.durations;
|
|
@@ -446,8 +437,8 @@ pub fn update(
|
|
|
446
437
|
if (@TypeOf(battle.rng) == data.PRNG) {
|
|
447
438
|
// Ensure we can encode the diffs in less than MAX_DIFFS bytes.
|
|
448
439
|
var buf: [helpers.MAX_DIFFS]u8 = undefined;
|
|
449
|
-
var
|
|
450
|
-
const n = try helpers.diff(&original, battle,
|
|
440
|
+
var pw: protocol.Writer = .{ .buffer = &buf };
|
|
441
|
+
const n = try helpers.diff(&original, battle, &pw);
|
|
451
442
|
|
|
452
443
|
// Applying the diff to the battle should take us back to the original copy
|
|
453
444
|
// of the battle (ignoring the RNG).
|
|
@@ -461,7 +452,7 @@ pub fn update(
|
|
|
461
452
|
// Ensure we can generate all transitions from the same original state
|
|
462
453
|
// (we must change the battle's RNG from a FixedRNG to a PRNG because
|
|
463
454
|
// the transitions function relies on RNG for discovery of states)
|
|
464
|
-
if (try transitions(unfix(original), c1, c2, allocator,
|
|
455
|
+
if (try transitions(unfix(original), c1, c2, allocator, out, err, .{
|
|
465
456
|
.durations = durations,
|
|
466
457
|
.cap = true,
|
|
467
458
|
})) |stats| try expect(stats.frontier <= MAX_FRONTIER);
|
|
@@ -508,10 +499,9 @@ pub fn update(
|
|
|
508
499
|
fn expectEqualActions(expected: Actions, actual: Actions) !void {
|
|
509
500
|
return expectEqual(expected, actual) catch |e| switch (e) {
|
|
510
501
|
error.TestExpectedEqual => {
|
|
511
|
-
std.debug.print("expected {}, found {}\n", .{ expected, actual });
|
|
502
|
+
std.debug.print("expected {f}, found {f}\n", .{ expected, actual });
|
|
512
503
|
return e;
|
|
513
504
|
},
|
|
514
|
-
else => return e,
|
|
515
505
|
};
|
|
516
506
|
}
|
|
517
507
|
|
|
@@ -530,27 +520,9 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
530
520
|
};
|
|
531
521
|
}
|
|
532
522
|
|
|
533
|
-
fn
|
|
534
|
-
if (!pkmn.options.overwrite) return actions;
|
|
535
|
-
var a = actions;
|
|
536
|
-
if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
|
|
537
|
-
if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
|
|
538
|
-
return a;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
|
|
542
|
-
for (frontier, 0..) |f, j| {
|
|
543
|
-
// TODO: is skipping this redundant check worth it?
|
|
544
|
-
if (i == j) continue;
|
|
545
|
-
if (f.matches(actions)) return true;
|
|
546
|
-
}
|
|
547
|
-
return false;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
551
|
-
const w = std.io.getStdErr().writer();
|
|
523
|
+
fn die(w: *std.Io.Writer, comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
552
524
|
w.print(fmt, v) catch return;
|
|
553
|
-
if (seed) |s| return w.print("{}\n", .{s}) catch return;
|
|
525
|
+
if (seed) |s| return w.print("{d}\n", .{s}) catch return;
|
|
554
526
|
return w.writeByte('\n') catch return;
|
|
555
527
|
}
|
|
556
528
|
|
|
@@ -566,7 +538,7 @@ const Style = struct {
|
|
|
566
538
|
indent: bool = true,
|
|
567
539
|
};
|
|
568
540
|
|
|
569
|
-
fn debug(writer:
|
|
541
|
+
fn debug(writer: *std.Io.Writer, actions: Actions, style: Style) !void {
|
|
570
542
|
if (style.indent) try writer.writeAll(" ");
|
|
571
543
|
if (tty) {
|
|
572
544
|
const mod: usize = if (style.dim) 2 else 1;
|
|
@@ -591,13 +563,13 @@ fn debug(writer: anytype, actions: Actions, style: Style) !void {
|
|
|
591
563
|
if (style.newline) try writer.writeByte('\n');
|
|
592
564
|
}
|
|
593
565
|
|
|
594
|
-
fn format(writer:
|
|
566
|
+
fn format(writer: *std.Io.Writer, actions: Actions, p1: u9, p2: u9) !void {
|
|
595
567
|
var input: [1024]u8 = undefined;
|
|
596
568
|
var output: [1024]u8 = undefined;
|
|
597
569
|
|
|
598
|
-
var
|
|
599
|
-
try actions.fmt(&
|
|
600
|
-
var len =
|
|
570
|
+
var w = std.Io.Writer.fixed(&input);
|
|
571
|
+
try actions.fmt(&w, false);
|
|
572
|
+
var len = w.buffered().len;
|
|
601
573
|
@memcpy(output[0..len], input[0..len]);
|
|
602
574
|
|
|
603
575
|
var find: [16]u8 = undefined;
|
|
@@ -630,78 +602,92 @@ pub const Rolls = struct {
|
|
|
630
602
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
631
603
|
}
|
|
632
604
|
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
const
|
|
636
|
-
const
|
|
637
|
-
const
|
|
638
|
-
const OBS = [_]Optional(Observation){ .continuing, .ended };
|
|
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 };
|
|
639
610
|
|
|
640
611
|
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
641
612
|
/// `durations`.
|
|
642
|
-
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
|
|
643
|
-
if (action.sleep == .None) return &
|
|
613
|
+
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation(.sleep)) {
|
|
614
|
+
if (action.sleep == .None) return &SLP_NONE;
|
|
644
615
|
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
645
|
-
&
|
|
616
|
+
&SLP_ALL
|
|
646
617
|
else switch (action.sleep) {
|
|
647
|
-
.started => &
|
|
648
|
-
else => if (Sleeps.get(duration.sleeps, 0) >= 7) &
|
|
618
|
+
.started => &SLP_STARTED,
|
|
619
|
+
else => if (Sleeps.get(duration.sleeps, 0) >= 7) &SLP_ENDED else &SLP,
|
|
649
620
|
};
|
|
650
621
|
}
|
|
651
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
|
+
|
|
652
630
|
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
653
631
|
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
654
632
|
pub fn disable(
|
|
655
633
|
action: Action,
|
|
656
634
|
duration: Duration,
|
|
657
|
-
parent: Optional(Observation),
|
|
658
|
-
) []const Optional(Observation) {
|
|
659
|
-
if (action.disable == .None) return &
|
|
635
|
+
parent: Optional(Observation(.sleep)),
|
|
636
|
+
) []const Optional(Observation(.disable)) {
|
|
637
|
+
if (action.disable == .None) return &DIS_NONE;
|
|
660
638
|
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
661
|
-
&
|
|
639
|
+
&DIS_ALL
|
|
662
640
|
else switch (action.disable) {
|
|
663
|
-
.started => &
|
|
641
|
+
.started => &DIS_STARTED,
|
|
664
642
|
else => if (duration.disable >= 8)
|
|
665
|
-
&
|
|
643
|
+
&DIS_ENDED
|
|
666
644
|
else if (parent != .None and parent != .started)
|
|
667
|
-
&
|
|
645
|
+
&DIS_CONTINUING
|
|
668
646
|
else
|
|
669
|
-
&
|
|
647
|
+
&DIS,
|
|
670
648
|
};
|
|
671
649
|
}
|
|
672
650
|
|
|
673
|
-
|
|
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
|
|
674
658
|
pub fn attacking(
|
|
675
659
|
action: Action,
|
|
676
660
|
duration: Duration,
|
|
677
|
-
parent: Optional(Observation),
|
|
678
|
-
) []const Optional(Observation) {
|
|
661
|
+
parent: Optional(Observation(.sleep)),
|
|
662
|
+
) []const Optional(Observation(.attacking)) {
|
|
679
663
|
return switch (action.attacking) {
|
|
680
|
-
.None => &
|
|
681
|
-
.started => &
|
|
664
|
+
.None => &ATK_NONE,
|
|
665
|
+
.started => &ATK_STARTED,
|
|
682
666
|
else => if (duration.attacking >= 3)
|
|
683
|
-
&
|
|
667
|
+
&ATK_ENDED
|
|
684
668
|
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
685
|
-
&
|
|
669
|
+
&ATK_CONTINUING
|
|
686
670
|
else
|
|
687
|
-
&
|
|
671
|
+
&ATK,
|
|
688
672
|
};
|
|
689
673
|
}
|
|
690
674
|
|
|
691
|
-
const CFZ_NONE = [_]Optional(
|
|
692
|
-
const CFZ_STARTED = [_]Optional(
|
|
693
|
-
const CFZ_OVERWRITTEN = [_]Optional(
|
|
694
|
-
const CFZ_CONTINUING = [_]Optional(
|
|
695
|
-
const CFZ_ENDED = [_]Optional(
|
|
696
|
-
const CFZ = [_]Optional(
|
|
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 };
|
|
697
682
|
|
|
698
|
-
/// FIXME
|
|
683
|
+
/// FIXME document
|
|
699
684
|
pub fn confusion(
|
|
700
685
|
action: Action,
|
|
701
686
|
duration: Duration,
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
687
|
+
tie: Optional(Player),
|
|
688
|
+
sibling: Optional(Observation(.attacking)),
|
|
689
|
+
parent: Optional(Observation(.sleep)),
|
|
690
|
+
) []const Optional(Observation(.confusion)) {
|
|
705
691
|
return switch (action.confusion) {
|
|
706
692
|
.None => &CFZ_NONE,
|
|
707
693
|
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
@@ -712,23 +698,30 @@ pub const Rolls = struct {
|
|
|
712
698
|
&CFZ_ENDED
|
|
713
699
|
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
714
700
|
&CFZ_CONTINUING
|
|
701
|
+
else if (tie != .None)
|
|
702
|
+
&CFZ_TIE
|
|
715
703
|
else
|
|
716
704
|
&CFZ,
|
|
717
705
|
};
|
|
718
706
|
}
|
|
719
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
|
+
|
|
720
713
|
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
721
714
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
722
715
|
pub fn binding(
|
|
723
716
|
action: Action,
|
|
724
717
|
duration: Duration,
|
|
725
718
|
parent: Optional(bool),
|
|
726
|
-
) []const Optional(Observation) {
|
|
727
|
-
if (parent == .true) return &
|
|
719
|
+
) []const Optional(Observation(.binding)) {
|
|
720
|
+
if (parent == .true) return &BND_NONE;
|
|
728
721
|
return switch (action.binding) {
|
|
729
|
-
.None => &
|
|
730
|
-
.started => &
|
|
731
|
-
else => if (duration.binding >= 4) &
|
|
722
|
+
.None => &BND_NONE,
|
|
723
|
+
.started => &BND_STARTED,
|
|
724
|
+
else => if (duration.binding >= 4) &BND_ENDED else &BND,
|
|
732
725
|
};
|
|
733
726
|
}
|
|
734
727
|
|
|
@@ -757,7 +750,7 @@ pub const Rolls = struct {
|
|
|
757
750
|
}
|
|
758
751
|
|
|
759
752
|
/// The min and max bounds on iteration over damage rolls.
|
|
760
|
-
pub const Range = struct { min:
|
|
753
|
+
pub const Range = struct { min: usize, max: usize };
|
|
761
754
|
|
|
762
755
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
763
756
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
@@ -770,7 +763,7 @@ pub const Rolls = struct {
|
|
|
770
763
|
|
|
771
764
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
772
765
|
/// given the base damage in `summaries`.
|
|
773
|
-
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool)
|
|
766
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool) u8 {
|
|
774
767
|
if (roll == 0) return roll;
|
|
775
768
|
|
|
776
769
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -782,7 +775,10 @@ pub const Rolls = struct {
|
|
|
782
775
|
|
|
783
776
|
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
784
777
|
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
785
|
-
pub fn confused(
|
|
778
|
+
pub fn confused(
|
|
779
|
+
action: Action,
|
|
780
|
+
parent: Optional(Observation(.confusion)),
|
|
781
|
+
) []const Optional(bool) {
|
|
786
782
|
const done = parent == .None or parent == .ended;
|
|
787
783
|
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
788
784
|
}
|
|
@@ -794,24 +790,24 @@ pub const Rolls = struct {
|
|
|
794
790
|
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
795
791
|
}
|
|
796
792
|
|
|
797
|
-
const SLOT_NONE = [_]
|
|
798
|
-
const SLOT = [_]
|
|
793
|
+
const SLOT_NONE = [_]u3{0};
|
|
794
|
+
const SLOT = [_]u3{ 1, 2, 3, 4 };
|
|
799
795
|
|
|
800
796
|
/// Returns a slice with a range of values for move slots given the `action` state
|
|
801
797
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
802
798
|
///
|
|
803
799
|
/// These slots may or **may not be valid** as slots may be unset / have 0 PP.
|
|
804
|
-
pub fn moveSlot(action: Action, parent: Optional(bool)) []const
|
|
800
|
+
pub fn moveSlot(action: Action, parent: Optional(bool)) []const u3 {
|
|
805
801
|
if (parent == .false) return &SLOT_NONE;
|
|
806
802
|
return if (action.move_slot == 0) &SLOT_NONE else &SLOT;
|
|
807
803
|
}
|
|
808
804
|
|
|
809
|
-
const MULTI_NONE = [_]
|
|
810
|
-
const MULTI = [_]
|
|
805
|
+
const MULTI_NONE = [_]u3{0};
|
|
806
|
+
const MULTI = [_]u3{ 2, 3, 4, 5 };
|
|
811
807
|
|
|
812
808
|
/// Returns a slice with the correct range of values for multi hit given the `action` state
|
|
813
809
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
814
|
-
pub fn multiHit(action: Action, parent: Optional(bool)) []const
|
|
810
|
+
pub fn multiHit(action: Action, parent: Optional(bool)) []const u3 {
|
|
815
811
|
if (parent == .false) return &MULTI_NONE;
|
|
816
812
|
return if (action.multi_hit == 0) &MULTI_NONE else &MULTI;
|
|
817
813
|
}
|
|
@@ -880,12 +876,12 @@ test "Rolls.damage" {
|
|
|
880
876
|
|
|
881
877
|
test "Rolls.coalesce" {
|
|
882
878
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
883
|
-
try expectEqual(@as(u8, 0),
|
|
884
|
-
try expectEqual(@as(u8, 241),
|
|
885
|
-
try expectEqual(@as(u8, 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));
|
|
886
882
|
summaries.p1.damage.final = 74;
|
|
887
|
-
try expectEqual(@as(u8, 217),
|
|
888
|
-
try expectEqual(@as(u8, 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));
|
|
889
885
|
}
|
|
890
886
|
|
|
891
887
|
test "Rolls.hit" {
|
|
@@ -943,16 +939,16 @@ test "Rolls.paralyzed" {
|
|
|
943
939
|
|
|
944
940
|
test "Rolls.moveSlot" {
|
|
945
941
|
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
946
|
-
try expectEqualSlices(
|
|
947
|
-
try expectEqualSlices(
|
|
948
|
-
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));
|
|
949
945
|
}
|
|
950
946
|
|
|
951
947
|
test "Rolls.multiHit" {
|
|
952
948
|
const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
|
|
953
|
-
try expectEqualSlices(
|
|
954
|
-
try expectEqualSlices(
|
|
955
|
-
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));
|
|
956
952
|
}
|
|
957
953
|
|
|
958
954
|
test "Rolls.metronome" {
|