@pkmn/engine 0.1.0-dev.b3197aa3 → 0.1.0-dev.b396399c
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 +17 -53
- package/build/pkg/addon/wasm.js.map +1 -1
- package/build/pkg/addon.d.ts +4 -4
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.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 +37 -3
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +1 -87
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/ui.jsx +3 -2
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +125 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +141 -232
- package/package.json +22 -21
- package/src/bin/install-pkmn-engine +22 -7
- package/src/lib/bindings/c.zig +27 -82
- package/src/lib/bindings/node.zig +7 -7
- package/src/lib/bindings/wasm.zig +6 -15
- package/src/lib/common/array.zig +5 -12
- package/src/lib/common/debug.zig +33 -59
- package/src/lib/common/js.zig +3 -5
- package/src/lib/common/optional.zig +11 -21
- package/src/lib/common/protocol.zig +224 -189
- package/src/lib/common/rational.zig +18 -38
- package/src/lib/common/util.zig +14 -46
- package/src/lib/gen1/calc.zig +121 -108
- package/src/lib/gen1/chance.zig +112 -75
- package/src/lib/gen1/data/types.zig +1 -1
- package/src/lib/gen1/data.zig +5 -5
- package/src/lib/gen1/helpers.zig +8 -10
- package/src/lib/gen1/mechanics.zig +31 -31
- package/src/lib/gen2/calc.zig +3 -6
- package/src/lib/gen2/chance.zig +12 -18
- package/src/lib/gen2/data.zig +5 -5
- package/src/lib/gen2/helpers.zig +4 -6
- package/src/lib/gen2/mechanics.zig +16 -16
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +1 -1
- package/src/lib/pkmn.zig +91 -17
- package/src/lib/wasm.zig +2 -6
- package/src/tools/display/dom.ts +40 -25
- package/src/lib/common/napi.zig +0 -4
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -16,7 +16,6 @@ const Actions = chance.Actions;
|
|
|
16
16
|
const assert = std.debug.assert;
|
|
17
17
|
const Chance = chance.Chance;
|
|
18
18
|
const Choice = common.Choice;
|
|
19
|
-
const Confusion = chance.Confusion;
|
|
20
19
|
const Duration = chance.Duration;
|
|
21
20
|
const Durations = chance.Durations;
|
|
22
21
|
const enabled = pkmn.options.calc;
|
|
@@ -35,9 +34,6 @@ const Sleeps = chance.Sleeps;
|
|
|
35
34
|
const tty = true; // DEBUG
|
|
36
35
|
const summary = false; // DEBUG
|
|
37
36
|
|
|
38
|
-
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
39
|
-
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
40
|
-
|
|
41
37
|
/// Information relevant to damage calculation that occured during a Generation I battle `update`.
|
|
42
38
|
pub const Summaries = extern struct {
|
|
43
39
|
/// Relevant information for Player 1.
|
|
@@ -99,13 +95,13 @@ pub const Calc = struct {
|
|
|
99
95
|
self: Calc,
|
|
100
96
|
player: Player,
|
|
101
97
|
comptime field: Action.Field,
|
|
102
|
-
)
|
|
98
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
103
99
|
if (!enabled) return null;
|
|
104
100
|
|
|
105
101
|
const val = @field(self.overrides.get(player), @tagName(field));
|
|
106
102
|
return if (switch (@typeInfo(@TypeOf(val))) {
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
.@"enum" => val != .None,
|
|
104
|
+
.int => val != 0,
|
|
109
105
|
else => unreachable,
|
|
110
106
|
}) val else null;
|
|
111
107
|
}
|
|
@@ -146,7 +142,7 @@ const Null = struct {
|
|
|
146
142
|
self: Null,
|
|
147
143
|
player: Player,
|
|
148
144
|
comptime field: Action.Field,
|
|
149
|
-
)
|
|
145
|
+
) ?@FieldType(Action, @tagName(field)) {
|
|
150
146
|
_ = .{ self, player };
|
|
151
147
|
return null;
|
|
152
148
|
}
|
|
@@ -182,24 +178,25 @@ pub const Options = struct {
|
|
|
182
178
|
metronome: bool = false,
|
|
183
179
|
};
|
|
184
180
|
|
|
185
|
-
pub const MAX_FRONTIER =
|
|
181
|
+
pub const MAX_FRONTIER = 86;
|
|
186
182
|
|
|
187
183
|
pub fn transitions(
|
|
188
184
|
battle: anytype,
|
|
189
185
|
c1: Choice,
|
|
190
186
|
c2: Choice,
|
|
191
187
|
allocator: std.mem.Allocator,
|
|
192
|
-
|
|
188
|
+
out: *std.Io.Writer,
|
|
189
|
+
err: *std.Io.Writer,
|
|
193
190
|
options: Options,
|
|
194
191
|
) !?Stats {
|
|
195
192
|
var stats: Stats = .{};
|
|
196
193
|
|
|
197
194
|
const cap = options.cap;
|
|
198
195
|
|
|
199
|
-
var seen
|
|
196
|
+
var seen: std.AutoHashMap(Actions, void) = .init(allocator);
|
|
200
197
|
defer seen.deinit();
|
|
201
|
-
var frontier
|
|
202
|
-
defer frontier.deinit();
|
|
198
|
+
var frontier: std.array_list.Aligned(Actions, null) = .empty;
|
|
199
|
+
defer frontier.deinit(allocator);
|
|
203
200
|
|
|
204
201
|
const d = options.durations;
|
|
205
202
|
|
|
@@ -217,7 +214,7 @@ pub fn transitions(
|
|
|
217
214
|
const p2 = b.side(.P2);
|
|
218
215
|
|
|
219
216
|
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
220
|
-
try frontier.append(opts.chance.actions);
|
|
217
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
221
218
|
|
|
222
219
|
// zig fmt: off
|
|
223
220
|
for (Rolls.metronome(frontier.items[0].p1)) |p1_move| {
|
|
@@ -233,7 +230,7 @@ pub fn transitions(
|
|
|
233
230
|
|
|
234
231
|
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
235
232
|
|
|
236
|
-
try debug(
|
|
233
|
+
try debug(out, f, .{
|
|
237
234
|
.shape = true,
|
|
238
235
|
.color = i,
|
|
239
236
|
.bold = true,
|
|
@@ -280,8 +277,8 @@ pub fn transitions(
|
|
|
280
277
|
|
|
281
278
|
var p2_dmg = Rolls.damage(f.p2, p2_hit);
|
|
282
279
|
|
|
283
|
-
const p1_min: u9 = p1_dmg.min;
|
|
284
|
-
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);
|
|
285
282
|
|
|
286
283
|
while (p2_dmg.min < p2_dmg.max) : (p2_dmg.min += 1) {
|
|
287
284
|
a.p2.damage = @intCast(p2_dmg.min);
|
|
@@ -298,7 +295,7 @@ pub fn transitions(
|
|
|
298
295
|
|
|
299
296
|
const summaries = &opts.calc.summaries;
|
|
300
297
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
301
|
-
p1_dmg.min
|
|
298
|
+
@intCast(p1_dmg.min)
|
|
302
299
|
else
|
|
303
300
|
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
304
301
|
const p2_max: u9 =
|
|
@@ -307,7 +304,7 @@ pub fn transitions(
|
|
|
307
304
|
if (opts.chance.actions.matches(f)) {
|
|
308
305
|
if (!opts.chance.actions.relax().eql(a)) {
|
|
309
306
|
if (!summary) {
|
|
310
|
-
try debug(
|
|
307
|
+
try debug(out, opts.chance.actions, .{
|
|
311
308
|
.p1_max = p1_max,
|
|
312
309
|
.p2_max = p2_max,
|
|
313
310
|
.color = i,
|
|
@@ -321,7 +318,7 @@ pub fn transitions(
|
|
|
321
318
|
}
|
|
322
319
|
|
|
323
320
|
if (!summary) {
|
|
324
|
-
try debug(
|
|
321
|
+
try debug(out, opts.chance.actions, .{
|
|
325
322
|
.p1_max = p1_max,
|
|
326
323
|
.p2_max = p2_max,
|
|
327
324
|
.color = i,
|
|
@@ -334,7 +331,7 @@ pub fn transitions(
|
|
|
334
331
|
acts.p1.damage = @intCast(p1d);
|
|
335
332
|
acts.p2.damage = @intCast(p2d);
|
|
336
333
|
if ((try seen.getOrPut(acts)).found_existing) {
|
|
337
|
-
err
|
|
334
|
+
die(err, "already seen {f}", .{acts}, options.seed);
|
|
338
335
|
return error.TestUnexpectedResult;
|
|
339
336
|
}
|
|
340
337
|
}
|
|
@@ -351,21 +348,21 @@ pub fn transitions(
|
|
|
351
348
|
stats.saved += 1;
|
|
352
349
|
|
|
353
350
|
if (p.q < p.p) {
|
|
354
|
-
err
|
|
351
|
+
die(err, "improper fraction {f}", .{p}, options.seed);
|
|
355
352
|
return error.TestUnexpectedResult;
|
|
356
353
|
}
|
|
357
354
|
} else {
|
|
358
355
|
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
359
|
-
try frontier.append(opts.chance.actions);
|
|
356
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
360
357
|
|
|
361
|
-
try debug(
|
|
358
|
+
try debug(out, opts.chance.actions, .{
|
|
362
359
|
.p1_max = p1_max,
|
|
363
360
|
.p2_max = p2_max,
|
|
364
361
|
.dim = true,
|
|
365
362
|
.newline = false,
|
|
366
363
|
});
|
|
367
|
-
try
|
|
368
|
-
try debug(
|
|
364
|
+
try out.writeAll(" → ");
|
|
365
|
+
try debug(out, opts.chance.actions, .{
|
|
369
366
|
.shape = true,
|
|
370
367
|
.color = frontier.items.len - 1,
|
|
371
368
|
.dim = true,
|
|
@@ -373,7 +370,7 @@ pub fn transitions(
|
|
|
373
370
|
.indent = false,
|
|
374
371
|
});
|
|
375
372
|
} else if (!summary) {
|
|
376
|
-
try debug(
|
|
373
|
+
try debug(out, opts.chance.actions, .{
|
|
377
374
|
.p1_max = p1_max,
|
|
378
375
|
.p2_max = p2_max,
|
|
379
376
|
.dim = true,
|
|
@@ -388,15 +385,12 @@ pub fn transitions(
|
|
|
388
385
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
389
386
|
|
|
390
387
|
assert(stats.saved > saved);
|
|
391
|
-
// TODO:
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
// p, 100 * @as(f128, @floatFromInt(p.p)) / @as(f128, @floatFromInt(p.q))},
|
|
398
|
-
// );
|
|
399
|
-
// }
|
|
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
|
+
// );
|
|
400
394
|
|
|
401
395
|
}
|
|
402
396
|
|
|
@@ -411,7 +405,7 @@ pub fn transitions(
|
|
|
411
405
|
|
|
412
406
|
p.reduce();
|
|
413
407
|
if (p.p != 1 or p.q != 1) {
|
|
414
|
-
err
|
|
408
|
+
die(err, "expected 1, found {f}", .{p}, options.seed);
|
|
415
409
|
return error.TestExpectedEqual;
|
|
416
410
|
}
|
|
417
411
|
|
|
@@ -424,7 +418,8 @@ pub fn update(
|
|
|
424
418
|
c2: Choice,
|
|
425
419
|
options: anytype,
|
|
426
420
|
allocator: std.mem.Allocator,
|
|
427
|
-
|
|
421
|
+
out: *std.Io.Writer,
|
|
422
|
+
err: *std.Io.Writer,
|
|
428
423
|
transition: bool,
|
|
429
424
|
) !Result {
|
|
430
425
|
const durations = options.chance.durations;
|
|
@@ -442,8 +437,8 @@ pub fn update(
|
|
|
442
437
|
if (@TypeOf(battle.rng) == data.PRNG) {
|
|
443
438
|
// Ensure we can encode the diffs in less than MAX_DIFFS bytes.
|
|
444
439
|
var buf: [helpers.MAX_DIFFS]u8 = undefined;
|
|
445
|
-
var
|
|
446
|
-
const n = try helpers.diff(&original, battle,
|
|
440
|
+
var pw: protocol.Writer = .{ .buffer = &buf };
|
|
441
|
+
const n = try helpers.diff(&original, battle, &pw);
|
|
447
442
|
|
|
448
443
|
// Applying the diff to the battle should take us back to the original copy
|
|
449
444
|
// of the battle (ignoring the RNG).
|
|
@@ -457,7 +452,7 @@ pub fn update(
|
|
|
457
452
|
// Ensure we can generate all transitions from the same original state
|
|
458
453
|
// (we must change the battle's RNG from a FixedRNG to a PRNG because
|
|
459
454
|
// the transitions function relies on RNG for discovery of states)
|
|
460
|
-
if (try transitions(unfix(original), c1, c2, allocator,
|
|
455
|
+
if (try transitions(unfix(original), c1, c2, allocator, out, err, .{
|
|
461
456
|
.durations = durations,
|
|
462
457
|
.cap = true,
|
|
463
458
|
})) |stats| try expect(stats.frontier <= MAX_FRONTIER);
|
|
@@ -504,10 +499,9 @@ pub fn update(
|
|
|
504
499
|
fn expectEqualActions(expected: Actions, actual: Actions) !void {
|
|
505
500
|
return expectEqual(expected, actual) catch |e| switch (e) {
|
|
506
501
|
error.TestExpectedEqual => {
|
|
507
|
-
std.debug.print("expected {}, found {}\n", .{ expected, actual });
|
|
502
|
+
std.debug.print("expected {f}, found {f}\n", .{ expected, actual });
|
|
508
503
|
return e;
|
|
509
504
|
},
|
|
510
|
-
else => return e,
|
|
511
505
|
};
|
|
512
506
|
}
|
|
513
507
|
|
|
@@ -526,10 +520,9 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
526
520
|
};
|
|
527
521
|
}
|
|
528
522
|
|
|
529
|
-
fn
|
|
530
|
-
const w = std.io.getStdErr().writer();
|
|
523
|
+
fn die(w: *std.Io.Writer, comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
531
524
|
w.print(fmt, v) catch return;
|
|
532
|
-
if (seed) |s| return w.print("{}\n", .{s}) catch return;
|
|
525
|
+
if (seed) |s| return w.print("{d}\n", .{s}) catch return;
|
|
533
526
|
return w.writeByte('\n') catch return;
|
|
534
527
|
}
|
|
535
528
|
|
|
@@ -545,7 +538,7 @@ const Style = struct {
|
|
|
545
538
|
indent: bool = true,
|
|
546
539
|
};
|
|
547
540
|
|
|
548
|
-
fn debug(writer:
|
|
541
|
+
fn debug(writer: *std.Io.Writer, actions: Actions, style: Style) !void {
|
|
549
542
|
if (style.indent) try writer.writeAll(" ");
|
|
550
543
|
if (tty) {
|
|
551
544
|
const mod: usize = if (style.dim) 2 else 1;
|
|
@@ -570,13 +563,13 @@ fn debug(writer: anytype, actions: Actions, style: Style) !void {
|
|
|
570
563
|
if (style.newline) try writer.writeByte('\n');
|
|
571
564
|
}
|
|
572
565
|
|
|
573
|
-
fn format(writer:
|
|
566
|
+
fn format(writer: *std.Io.Writer, actions: Actions, p1: u9, p2: u9) !void {
|
|
574
567
|
var input: [1024]u8 = undefined;
|
|
575
568
|
var output: [1024]u8 = undefined;
|
|
576
569
|
|
|
577
|
-
var
|
|
578
|
-
try actions.fmt(&
|
|
579
|
-
var len =
|
|
570
|
+
var w = std.Io.Writer.fixed(&input);
|
|
571
|
+
try actions.fmt(&w, false);
|
|
572
|
+
var len = w.buffered().len;
|
|
580
573
|
@memcpy(output[0..len], input[0..len]);
|
|
581
574
|
|
|
582
575
|
var find: [16]u8 = undefined;
|
|
@@ -609,80 +602,92 @@ pub const Rolls = struct {
|
|
|
609
602
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
610
603
|
}
|
|
611
604
|
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
const
|
|
615
|
-
const
|
|
616
|
-
const
|
|
617
|
-
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 };
|
|
618
610
|
|
|
619
611
|
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
620
612
|
/// `durations`.
|
|
621
|
-
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
|
|
622
|
-
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;
|
|
623
615
|
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
624
|
-
&
|
|
616
|
+
&SLP_ALL
|
|
625
617
|
else switch (action.sleep) {
|
|
626
|
-
.started => &
|
|
627
|
-
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,
|
|
628
620
|
};
|
|
629
621
|
}
|
|
630
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
|
+
|
|
631
630
|
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
632
631
|
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
633
632
|
pub fn disable(
|
|
634
633
|
action: Action,
|
|
635
634
|
duration: Duration,
|
|
636
|
-
parent: Optional(Observation),
|
|
637
|
-
) []const Optional(Observation) {
|
|
638
|
-
if (action.disable == .None) return &
|
|
635
|
+
parent: Optional(Observation(.sleep)),
|
|
636
|
+
) []const Optional(Observation(.disable)) {
|
|
637
|
+
if (action.disable == .None) return &DIS_NONE;
|
|
639
638
|
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
640
|
-
&
|
|
639
|
+
&DIS_ALL
|
|
641
640
|
else switch (action.disable) {
|
|
642
|
-
.started => &
|
|
641
|
+
.started => &DIS_STARTED,
|
|
643
642
|
else => if (duration.disable >= 8)
|
|
644
|
-
&
|
|
643
|
+
&DIS_ENDED
|
|
645
644
|
else if (parent != .None and parent != .started)
|
|
646
|
-
&
|
|
645
|
+
&DIS_CONTINUING
|
|
647
646
|
else
|
|
648
|
-
&
|
|
647
|
+
&DIS,
|
|
649
648
|
};
|
|
650
649
|
}
|
|
651
650
|
|
|
652
|
-
|
|
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
|
|
653
658
|
pub fn attacking(
|
|
654
659
|
action: Action,
|
|
655
660
|
duration: Duration,
|
|
656
|
-
parent: Optional(Observation),
|
|
657
|
-
) []const Optional(Observation) {
|
|
661
|
+
parent: Optional(Observation(.sleep)),
|
|
662
|
+
) []const Optional(Observation(.attacking)) {
|
|
658
663
|
return switch (action.attacking) {
|
|
659
|
-
.None => &
|
|
660
|
-
.started => &
|
|
664
|
+
.None => &ATK_NONE,
|
|
665
|
+
.started => &ATK_STARTED,
|
|
661
666
|
else => if (duration.attacking >= 3)
|
|
662
|
-
&
|
|
667
|
+
&ATK_ENDED
|
|
663
668
|
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
664
|
-
&
|
|
669
|
+
&ATK_CONTINUING
|
|
665
670
|
else
|
|
666
|
-
&
|
|
671
|
+
&ATK,
|
|
667
672
|
};
|
|
668
673
|
}
|
|
669
674
|
|
|
670
|
-
const CFZ_NONE = [_]Optional(
|
|
671
|
-
const CFZ_STARTED = [_]Optional(
|
|
672
|
-
const CFZ_OVERWRITTEN = [_]Optional(
|
|
673
|
-
const CFZ_CONTINUING = [_]Optional(
|
|
674
|
-
const CFZ_ENDED = [_]Optional(
|
|
675
|
-
const CFZ = [_]Optional(
|
|
676
|
-
const CFZ_TIE = [_]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 };
|
|
677
682
|
|
|
678
|
-
/// FIXME
|
|
683
|
+
/// FIXME document
|
|
679
684
|
pub fn confusion(
|
|
680
685
|
action: Action,
|
|
681
686
|
duration: Duration,
|
|
682
687
|
tie: Optional(Player),
|
|
683
|
-
sibling: Optional(Observation),
|
|
684
|
-
parent: Optional(Observation),
|
|
685
|
-
) []const Optional(
|
|
688
|
+
sibling: Optional(Observation(.attacking)),
|
|
689
|
+
parent: Optional(Observation(.sleep)),
|
|
690
|
+
) []const Optional(Observation(.confusion)) {
|
|
686
691
|
return switch (action.confusion) {
|
|
687
692
|
.None => &CFZ_NONE,
|
|
688
693
|
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
@@ -700,18 +705,23 @@ pub const Rolls = struct {
|
|
|
700
705
|
};
|
|
701
706
|
}
|
|
702
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
|
+
|
|
703
713
|
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
704
714
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
705
715
|
pub fn binding(
|
|
706
716
|
action: Action,
|
|
707
717
|
duration: Duration,
|
|
708
718
|
parent: Optional(bool),
|
|
709
|
-
) []const Optional(Observation) {
|
|
710
|
-
if (parent == .true) return &
|
|
719
|
+
) []const Optional(Observation(.binding)) {
|
|
720
|
+
if (parent == .true) return &BND_NONE;
|
|
711
721
|
return switch (action.binding) {
|
|
712
|
-
.None => &
|
|
713
|
-
.started => &
|
|
714
|
-
else => if (duration.binding >= 4) &
|
|
722
|
+
.None => &BND_NONE,
|
|
723
|
+
.started => &BND_STARTED,
|
|
724
|
+
else => if (duration.binding >= 4) &BND_ENDED else &BND,
|
|
715
725
|
};
|
|
716
726
|
}
|
|
717
727
|
|
|
@@ -740,7 +750,7 @@ pub const Rolls = struct {
|
|
|
740
750
|
}
|
|
741
751
|
|
|
742
752
|
/// The min and max bounds on iteration over damage rolls.
|
|
743
|
-
pub const Range = struct { min:
|
|
753
|
+
pub const Range = struct { min: usize, max: usize };
|
|
744
754
|
|
|
745
755
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
746
756
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
@@ -765,7 +775,10 @@ pub const Rolls = struct {
|
|
|
765
775
|
|
|
766
776
|
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
767
777
|
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
768
|
-
pub fn confused(
|
|
778
|
+
pub fn confused(
|
|
779
|
+
action: Action,
|
|
780
|
+
parent: Optional(Observation(.confusion)),
|
|
781
|
+
) []const Optional(bool) {
|
|
769
782
|
const done = parent == .None or parent == .ended;
|
|
770
783
|
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
771
784
|
}
|
|
@@ -777,24 +790,24 @@ pub const Rolls = struct {
|
|
|
777
790
|
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
778
791
|
}
|
|
779
792
|
|
|
780
|
-
const SLOT_NONE = [_]
|
|
781
|
-
const SLOT = [_]
|
|
793
|
+
const SLOT_NONE = [_]u3{0};
|
|
794
|
+
const SLOT = [_]u3{ 1, 2, 3, 4 };
|
|
782
795
|
|
|
783
796
|
/// Returns a slice with a range of values for move slots given the `action` state
|
|
784
797
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
785
798
|
///
|
|
786
799
|
/// These slots may or **may not be valid** as slots may be unset / have 0 PP.
|
|
787
|
-
pub fn moveSlot(action: Action, parent: Optional(bool)) []const
|
|
800
|
+
pub fn moveSlot(action: Action, parent: Optional(bool)) []const u3 {
|
|
788
801
|
if (parent == .false) return &SLOT_NONE;
|
|
789
802
|
return if (action.move_slot == 0) &SLOT_NONE else &SLOT;
|
|
790
803
|
}
|
|
791
804
|
|
|
792
|
-
const MULTI_NONE = [_]
|
|
793
|
-
const MULTI = [_]
|
|
805
|
+
const MULTI_NONE = [_]u3{0};
|
|
806
|
+
const MULTI = [_]u3{ 2, 3, 4, 5 };
|
|
794
807
|
|
|
795
808
|
/// Returns a slice with the correct range of values for multi hit given the `action` state
|
|
796
809
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
797
|
-
pub fn multiHit(action: Action, parent: Optional(bool)) []const
|
|
810
|
+
pub fn multiHit(action: Action, parent: Optional(bool)) []const u3 {
|
|
798
811
|
if (parent == .false) return &MULTI_NONE;
|
|
799
812
|
return if (action.multi_hit == 0) &MULTI_NONE else &MULTI;
|
|
800
813
|
}
|
|
@@ -926,16 +939,16 @@ test "Rolls.paralyzed" {
|
|
|
926
939
|
|
|
927
940
|
test "Rolls.moveSlot" {
|
|
928
941
|
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
929
|
-
try expectEqualSlices(
|
|
930
|
-
try expectEqualSlices(
|
|
931
|
-
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));
|
|
932
945
|
}
|
|
933
946
|
|
|
934
947
|
test "Rolls.multiHit" {
|
|
935
948
|
const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
|
|
936
|
-
try expectEqualSlices(
|
|
937
|
-
try expectEqualSlices(
|
|
938
|
-
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));
|
|
939
952
|
}
|
|
940
953
|
|
|
941
954
|
test "Rolls.metronome" {
|