@pkmn/engine 0.1.0-dev.7feed58c → 0.1.0-dev.8058a1d0
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 +18 -11
- package/build/pkg/addon/node.js +17 -7
- package/build/pkg/addon/node.js.map +1 -1
- 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.d.ts +1 -1
- package/build/pkg/gen1.js +17 -7
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.js +17 -7
- 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 +54 -10
- 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 +17 -7
- 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 +125 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +141 -231
- package/package.json +26 -24
- package/src/bin/install-pkmn-engine +22 -7
- package/src/lib/bindings/c.zig +27 -82
- package/src/lib/bindings/node.zig +7 -11
- 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 -9
- package/src/lib/common/optional.zig +11 -21
- package/src/lib/common/protocol.zig +224 -189
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/util.zig +14 -31
- package/src/lib/gen1/calc.zig +131 -109
- package/src/lib/gen1/chance.zig +112 -75
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +5 -5
- package/src/lib/gen1/helpers.zig +8 -10
- package/src/lib/gen1/mechanics.zig +43 -41
- package/src/lib/gen2/calc.zig +3 -6
- package/src/lib/gen2/chance.zig +12 -18
- package/src/lib/gen2/data/types.zig +6 -3
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/helpers.zig +4 -6
- package/src/lib/gen2/mechanics.zig +18 -18
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +1 -5
- package/src/lib/pkmn.zig +91 -17
- package/src/lib/wasm.zig +2 -6
- package/src/tools/display/dom.ts +43 -31
package/src/lib/common/util.zig
CHANGED
|
@@ -3,10 +3,8 @@ const std = @import("std");
|
|
|
3
3
|
const expect = std.testing.expect;
|
|
4
4
|
const expectEqual = std.testing.expectEqual;
|
|
5
5
|
|
|
6
|
-
const Pointer = if (@hasField(std.builtin.Type, "pointer")) .pointer else .Pointer;
|
|
7
|
-
|
|
8
6
|
pub fn PointerType(comptime P: type, comptime C: type) type {
|
|
9
|
-
return if (@
|
|
7
|
+
return if (@typeInfo(P).pointer.is_const) *const C else *C;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
test PointerType {
|
|
@@ -17,7 +15,7 @@ test PointerType {
|
|
|
17
15
|
pub fn isPointerTo(p: anytype, comptime P: type) bool {
|
|
18
16
|
const info = @typeInfo(@TypeOf(p));
|
|
19
17
|
return switch (info) {
|
|
20
|
-
|
|
18
|
+
.pointer => info.pointer.child == P,
|
|
21
19
|
else => false,
|
|
22
20
|
};
|
|
23
21
|
}
|
|
@@ -29,33 +27,18 @@ test isPointerTo {
|
|
|
29
27
|
try expect(isPointerTo(&s, S));
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
pub fn repeat(comptime xs: anytype, comptime n: usize) [xs.len * n]@TypeOf(xs[0]) {
|
|
31
|
+
comptime {
|
|
32
|
+
const T = @TypeOf(xs[0]);
|
|
33
|
+
var ts: [xs.len * n]T = undefined;
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
var i: usize = 0;
|
|
36
|
+
while (i < n) : (i += 1) {
|
|
37
|
+
for (xs, 0..) |v, j| {
|
|
38
|
+
ts[i * xs.len + j] = v;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
comptime size: std.builtin.Type.Pointer.Size,
|
|
44
|
-
comptime child: type,
|
|
45
|
-
) type {
|
|
46
|
-
const info = @field(@typeInfo(source), @tagName(Pointer));
|
|
47
|
-
const args: std.builtin.Type.Pointer = .{
|
|
48
|
-
.size = size,
|
|
49
|
-
.is_const = info.is_const,
|
|
50
|
-
.is_volatile = info.is_volatile,
|
|
51
|
-
.is_allowzero = info.is_allowzero,
|
|
52
|
-
.alignment = info.alignment,
|
|
53
|
-
.address_space = info.address_space,
|
|
54
|
-
.child = child,
|
|
55
|
-
.sentinel = null,
|
|
56
|
-
};
|
|
57
|
-
return @Type(if (@hasField(std.builtin.Type, "pointer"))
|
|
58
|
-
.{ .pointer = args }
|
|
59
|
-
else
|
|
60
|
-
.{ .Pointer = args });
|
|
42
|
+
return ts;
|
|
43
|
+
}
|
|
61
44
|
}
|
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
|
|
|
@@ -216,8 +213,8 @@ pub fn transitions(
|
|
|
216
213
|
const p1 = b.side(.P1);
|
|
217
214
|
const p2 = b.side(.P2);
|
|
218
215
|
|
|
219
|
-
var p: Rational(
|
|
220
|
-
try frontier.append(opts.chance.actions);
|
|
216
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
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| {
|
|
@@ -231,7 +228,9 @@ pub fn transitions(
|
|
|
231
228
|
const f = frontier.items[i];
|
|
232
229
|
const saved = stats.saved;
|
|
233
230
|
|
|
234
|
-
|
|
231
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
232
|
+
|
|
233
|
+
try debug(out, f, .{
|
|
235
234
|
.shape = true,
|
|
236
235
|
.color = i,
|
|
237
236
|
.bold = true,
|
|
@@ -251,8 +250,8 @@ pub fn transitions(
|
|
|
251
250
|
for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
|
|
252
251
|
for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
|
|
253
252
|
for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
|
|
254
|
-
for (Rolls.confusion(f.p1, d.p1, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
255
|
-
for (Rolls.confusion(f.p2, d.p2, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
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;
|
|
256
255
|
for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
|
|
257
256
|
for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
|
|
258
257
|
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
@@ -278,8 +277,8 @@ pub fn transitions(
|
|
|
278
277
|
|
|
279
278
|
var p2_dmg = Rolls.damage(f.p2, p2_hit);
|
|
280
279
|
|
|
281
|
-
const p1_min: u9 = p1_dmg.min;
|
|
282
|
-
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);
|
|
283
282
|
|
|
284
283
|
while (p2_dmg.min < p2_dmg.max) : (p2_dmg.min += 1) {
|
|
285
284
|
a.p2.damage = @intCast(p2_dmg.min);
|
|
@@ -296,7 +295,7 @@ pub fn transitions(
|
|
|
296
295
|
|
|
297
296
|
const summaries = &opts.calc.summaries;
|
|
298
297
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
299
|
-
p1_dmg.min
|
|
298
|
+
@intCast(p1_dmg.min)
|
|
300
299
|
else
|
|
301
300
|
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
302
301
|
const p2_max: u9 =
|
|
@@ -305,7 +304,7 @@ pub fn transitions(
|
|
|
305
304
|
if (opts.chance.actions.matches(f)) {
|
|
306
305
|
if (!opts.chance.actions.relax().eql(a)) {
|
|
307
306
|
if (!summary) {
|
|
308
|
-
try debug(
|
|
307
|
+
try debug(out, opts.chance.actions, .{
|
|
309
308
|
.p1_max = p1_max,
|
|
310
309
|
.p2_max = p2_max,
|
|
311
310
|
.color = i,
|
|
@@ -319,7 +318,7 @@ pub fn transitions(
|
|
|
319
318
|
}
|
|
320
319
|
|
|
321
320
|
if (!summary) {
|
|
322
|
-
try debug(
|
|
321
|
+
try debug(out, opts.chance.actions, .{
|
|
323
322
|
.p1_max = p1_max,
|
|
324
323
|
.p2_max = p2_max,
|
|
325
324
|
.color = i,
|
|
@@ -332,7 +331,7 @@ pub fn transitions(
|
|
|
332
331
|
acts.p1.damage = @intCast(p1d);
|
|
333
332
|
acts.p2.damage = @intCast(p2d);
|
|
334
333
|
if ((try seen.getOrPut(acts)).found_existing) {
|
|
335
|
-
err
|
|
334
|
+
die(err, "already seen {f}", .{acts}, options.seed);
|
|
336
335
|
return error.TestUnexpectedResult;
|
|
337
336
|
}
|
|
338
337
|
}
|
|
@@ -343,25 +342,27 @@ pub fn transitions(
|
|
|
343
342
|
q.reduce();
|
|
344
343
|
try p.add(q);
|
|
345
344
|
p.reduce();
|
|
345
|
+
try r.add(q);
|
|
346
|
+
r.reduce();
|
|
346
347
|
|
|
347
348
|
stats.saved += 1;
|
|
348
349
|
|
|
349
350
|
if (p.q < p.p) {
|
|
350
|
-
err
|
|
351
|
+
die(err, "improper fraction {f}", .{p}, options.seed);
|
|
351
352
|
return error.TestUnexpectedResult;
|
|
352
353
|
}
|
|
353
354
|
} else {
|
|
354
355
|
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
355
|
-
try frontier.append(opts.chance.actions);
|
|
356
|
+
try frontier.append(allocator, opts.chance.actions);
|
|
356
357
|
|
|
357
|
-
try debug(
|
|
358
|
+
try debug(out, opts.chance.actions, .{
|
|
358
359
|
.p1_max = p1_max,
|
|
359
360
|
.p2_max = p2_max,
|
|
360
361
|
.dim = true,
|
|
361
362
|
.newline = false,
|
|
362
363
|
});
|
|
363
|
-
try
|
|
364
|
-
try debug(
|
|
364
|
+
try out.writeAll(" → ");
|
|
365
|
+
try debug(out, opts.chance.actions, .{
|
|
365
366
|
.shape = true,
|
|
366
367
|
.color = frontier.items.len - 1,
|
|
367
368
|
.dim = true,
|
|
@@ -369,7 +370,7 @@ pub fn transitions(
|
|
|
369
370
|
.indent = false,
|
|
370
371
|
});
|
|
371
372
|
} else if (!summary) {
|
|
372
|
-
try debug(
|
|
373
|
+
try debug(out, opts.chance.actions, .{
|
|
373
374
|
.p1_max = p1_max,
|
|
374
375
|
.p2_max = p2_max,
|
|
375
376
|
.dim = true,
|
|
@@ -384,14 +385,12 @@ pub fn transitions(
|
|
|
384
385
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
385
386
|
|
|
386
387
|
assert(stats.saved > saved);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
);
|
|
394
|
-
}
|
|
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
|
+
// );
|
|
395
394
|
|
|
396
395
|
}
|
|
397
396
|
|
|
@@ -406,7 +405,7 @@ pub fn transitions(
|
|
|
406
405
|
|
|
407
406
|
p.reduce();
|
|
408
407
|
if (p.p != 1 or p.q != 1) {
|
|
409
|
-
err
|
|
408
|
+
die(err, "expected 1, found {f}", .{p}, options.seed);
|
|
410
409
|
return error.TestExpectedEqual;
|
|
411
410
|
}
|
|
412
411
|
|
|
@@ -419,7 +418,8 @@ pub fn update(
|
|
|
419
418
|
c2: Choice,
|
|
420
419
|
options: anytype,
|
|
421
420
|
allocator: std.mem.Allocator,
|
|
422
|
-
|
|
421
|
+
out: *std.Io.Writer,
|
|
422
|
+
err: *std.Io.Writer,
|
|
423
423
|
transition: bool,
|
|
424
424
|
) !Result {
|
|
425
425
|
const durations = options.chance.durations;
|
|
@@ -437,8 +437,8 @@ pub fn update(
|
|
|
437
437
|
if (@TypeOf(battle.rng) == data.PRNG) {
|
|
438
438
|
// Ensure we can encode the diffs in less than MAX_DIFFS bytes.
|
|
439
439
|
var buf: [helpers.MAX_DIFFS]u8 = undefined;
|
|
440
|
-
var
|
|
441
|
-
const n = try helpers.diff(&original, battle,
|
|
440
|
+
var pw: protocol.Writer = .{ .buffer = &buf };
|
|
441
|
+
const n = try helpers.diff(&original, battle, &pw);
|
|
442
442
|
|
|
443
443
|
// Applying the diff to the battle should take us back to the original copy
|
|
444
444
|
// of the battle (ignoring the RNG).
|
|
@@ -452,7 +452,7 @@ pub fn update(
|
|
|
452
452
|
// Ensure we can generate all transitions from the same original state
|
|
453
453
|
// (we must change the battle's RNG from a FixedRNG to a PRNG because
|
|
454
454
|
// the transitions function relies on RNG for discovery of states)
|
|
455
|
-
if (try transitions(unfix(original), c1, c2, allocator,
|
|
455
|
+
if (try transitions(unfix(original), c1, c2, allocator, out, err, .{
|
|
456
456
|
.durations = durations,
|
|
457
457
|
.cap = true,
|
|
458
458
|
})) |stats| try expect(stats.frontier <= MAX_FRONTIER);
|
|
@@ -499,10 +499,9 @@ pub fn update(
|
|
|
499
499
|
fn expectEqualActions(expected: Actions, actual: Actions) !void {
|
|
500
500
|
return expectEqual(expected, actual) catch |e| switch (e) {
|
|
501
501
|
error.TestExpectedEqual => {
|
|
502
|
-
std.debug.print("expected {}, found {}\n", .{ expected, actual });
|
|
502
|
+
std.debug.print("expected {f}, found {f}\n", .{ expected, actual });
|
|
503
503
|
return e;
|
|
504
504
|
},
|
|
505
|
-
else => return e,
|
|
506
505
|
};
|
|
507
506
|
}
|
|
508
507
|
|
|
@@ -521,10 +520,9 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
521
520
|
};
|
|
522
521
|
}
|
|
523
522
|
|
|
524
|
-
fn
|
|
525
|
-
const w = std.io.getStdErr().writer();
|
|
523
|
+
fn die(w: *std.Io.Writer, comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
526
524
|
w.print(fmt, v) catch return;
|
|
527
|
-
if (seed) |s| return w.print("{}\n", .{s}) catch return;
|
|
525
|
+
if (seed) |s| return w.print("{d}\n", .{s}) catch return;
|
|
528
526
|
return w.writeByte('\n') catch return;
|
|
529
527
|
}
|
|
530
528
|
|
|
@@ -540,7 +538,7 @@ const Style = struct {
|
|
|
540
538
|
indent: bool = true,
|
|
541
539
|
};
|
|
542
540
|
|
|
543
|
-
fn debug(writer:
|
|
541
|
+
fn debug(writer: *std.Io.Writer, actions: Actions, style: Style) !void {
|
|
544
542
|
if (style.indent) try writer.writeAll(" ");
|
|
545
543
|
if (tty) {
|
|
546
544
|
const mod: usize = if (style.dim) 2 else 1;
|
|
@@ -565,13 +563,13 @@ fn debug(writer: anytype, actions: Actions, style: Style) !void {
|
|
|
565
563
|
if (style.newline) try writer.writeByte('\n');
|
|
566
564
|
}
|
|
567
565
|
|
|
568
|
-
fn format(writer:
|
|
566
|
+
fn format(writer: *std.Io.Writer, actions: Actions, p1: u9, p2: u9) !void {
|
|
569
567
|
var input: [1024]u8 = undefined;
|
|
570
568
|
var output: [1024]u8 = undefined;
|
|
571
569
|
|
|
572
|
-
var
|
|
573
|
-
try actions.fmt(&
|
|
574
|
-
var len =
|
|
570
|
+
var w = std.Io.Writer.fixed(&input);
|
|
571
|
+
try actions.fmt(&w, false);
|
|
572
|
+
var len = w.buffered().len;
|
|
575
573
|
@memcpy(output[0..len], input[0..len]);
|
|
576
574
|
|
|
577
575
|
var find: [16]u8 = undefined;
|
|
@@ -604,78 +602,92 @@ pub const Rolls = struct {
|
|
|
604
602
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
605
603
|
}
|
|
606
604
|
|
|
607
|
-
const
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
const
|
|
612
|
-
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 };
|
|
613
610
|
|
|
614
611
|
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
615
612
|
/// `durations`.
|
|
616
|
-
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
|
|
617
|
-
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;
|
|
618
615
|
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
619
|
-
&
|
|
616
|
+
&SLP_ALL
|
|
620
617
|
else switch (action.sleep) {
|
|
621
|
-
.started => &
|
|
622
|
-
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,
|
|
623
620
|
};
|
|
624
621
|
}
|
|
625
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
|
+
|
|
626
630
|
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
627
631
|
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
628
632
|
pub fn disable(
|
|
629
633
|
action: Action,
|
|
630
634
|
duration: Duration,
|
|
631
|
-
parent: Optional(Observation),
|
|
632
|
-
) []const Optional(Observation) {
|
|
633
|
-
if (action.disable == .None) return &
|
|
635
|
+
parent: Optional(Observation(.sleep)),
|
|
636
|
+
) []const Optional(Observation(.disable)) {
|
|
637
|
+
if (action.disable == .None) return &DIS_NONE;
|
|
634
638
|
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
635
|
-
&
|
|
639
|
+
&DIS_ALL
|
|
636
640
|
else switch (action.disable) {
|
|
637
|
-
.started => &
|
|
641
|
+
.started => &DIS_STARTED,
|
|
638
642
|
else => if (duration.disable >= 8)
|
|
639
|
-
&
|
|
643
|
+
&DIS_ENDED
|
|
640
644
|
else if (parent != .None and parent != .started)
|
|
641
|
-
&
|
|
645
|
+
&DIS_CONTINUING
|
|
642
646
|
else
|
|
643
|
-
&
|
|
647
|
+
&DIS,
|
|
644
648
|
};
|
|
645
649
|
}
|
|
646
650
|
|
|
647
|
-
|
|
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
|
|
648
658
|
pub fn attacking(
|
|
649
659
|
action: Action,
|
|
650
660
|
duration: Duration,
|
|
651
|
-
parent: Optional(Observation),
|
|
652
|
-
) []const Optional(Observation) {
|
|
661
|
+
parent: Optional(Observation(.sleep)),
|
|
662
|
+
) []const Optional(Observation(.attacking)) {
|
|
653
663
|
return switch (action.attacking) {
|
|
654
|
-
.None => &
|
|
655
|
-
.started => &
|
|
664
|
+
.None => &ATK_NONE,
|
|
665
|
+
.started => &ATK_STARTED,
|
|
656
666
|
else => if (duration.attacking >= 3)
|
|
657
|
-
&
|
|
667
|
+
&ATK_ENDED
|
|
658
668
|
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
659
|
-
&
|
|
669
|
+
&ATK_CONTINUING
|
|
660
670
|
else
|
|
661
|
-
&
|
|
671
|
+
&ATK,
|
|
662
672
|
};
|
|
663
673
|
}
|
|
664
674
|
|
|
665
|
-
const CFZ_NONE = [_]Optional(
|
|
666
|
-
const CFZ_STARTED = [_]Optional(
|
|
667
|
-
const CFZ_OVERWRITTEN = [_]Optional(
|
|
668
|
-
const CFZ_CONTINUING = [_]Optional(
|
|
669
|
-
const CFZ_ENDED = [_]Optional(
|
|
670
|
-
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 };
|
|
671
682
|
|
|
672
|
-
/// FIXME
|
|
683
|
+
/// FIXME document
|
|
673
684
|
pub fn confusion(
|
|
674
685
|
action: Action,
|
|
675
686
|
duration: Duration,
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
687
|
+
tie: Optional(Player),
|
|
688
|
+
sibling: Optional(Observation(.attacking)),
|
|
689
|
+
parent: Optional(Observation(.sleep)),
|
|
690
|
+
) []const Optional(Observation(.confusion)) {
|
|
679
691
|
return switch (action.confusion) {
|
|
680
692
|
.None => &CFZ_NONE,
|
|
681
693
|
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
@@ -686,23 +698,30 @@ pub const Rolls = struct {
|
|
|
686
698
|
&CFZ_ENDED
|
|
687
699
|
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
688
700
|
&CFZ_CONTINUING
|
|
701
|
+
else if (tie != .None)
|
|
702
|
+
&CFZ_TIE
|
|
689
703
|
else
|
|
690
704
|
&CFZ,
|
|
691
705
|
};
|
|
692
706
|
}
|
|
693
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
|
+
|
|
694
713
|
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
695
714
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
696
715
|
pub fn binding(
|
|
697
716
|
action: Action,
|
|
698
717
|
duration: Duration,
|
|
699
718
|
parent: Optional(bool),
|
|
700
|
-
) []const Optional(Observation) {
|
|
701
|
-
if (parent == .true) return &
|
|
719
|
+
) []const Optional(Observation(.binding)) {
|
|
720
|
+
if (parent == .true) return &BND_NONE;
|
|
702
721
|
return switch (action.binding) {
|
|
703
|
-
.None => &
|
|
704
|
-
.started => &
|
|
705
|
-
else => if (duration.binding >= 4) &
|
|
722
|
+
.None => &BND_NONE,
|
|
723
|
+
.started => &BND_STARTED,
|
|
724
|
+
else => if (duration.binding >= 4) &BND_ENDED else &BND,
|
|
706
725
|
};
|
|
707
726
|
}
|
|
708
727
|
|
|
@@ -731,7 +750,7 @@ pub const Rolls = struct {
|
|
|
731
750
|
}
|
|
732
751
|
|
|
733
752
|
/// The min and max bounds on iteration over damage rolls.
|
|
734
|
-
pub const Range = struct { min:
|
|
753
|
+
pub const Range = struct { min: usize, max: usize };
|
|
735
754
|
|
|
736
755
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
737
756
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
@@ -756,7 +775,10 @@ pub const Rolls = struct {
|
|
|
756
775
|
|
|
757
776
|
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
758
777
|
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
759
|
-
pub fn confused(
|
|
778
|
+
pub fn confused(
|
|
779
|
+
action: Action,
|
|
780
|
+
parent: Optional(Observation(.confusion)),
|
|
781
|
+
) []const Optional(bool) {
|
|
760
782
|
const done = parent == .None or parent == .ended;
|
|
761
783
|
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
762
784
|
}
|
|
@@ -768,24 +790,24 @@ pub const Rolls = struct {
|
|
|
768
790
|
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
769
791
|
}
|
|
770
792
|
|
|
771
|
-
const SLOT_NONE = [_]
|
|
772
|
-
const SLOT = [_]
|
|
793
|
+
const SLOT_NONE = [_]u3{0};
|
|
794
|
+
const SLOT = [_]u3{ 1, 2, 3, 4 };
|
|
773
795
|
|
|
774
796
|
/// Returns a slice with a range of values for move slots given the `action` state
|
|
775
797
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
776
798
|
///
|
|
777
799
|
/// These slots may or **may not be valid** as slots may be unset / have 0 PP.
|
|
778
|
-
pub fn moveSlot(action: Action, parent: Optional(bool)) []const
|
|
800
|
+
pub fn moveSlot(action: Action, parent: Optional(bool)) []const u3 {
|
|
779
801
|
if (parent == .false) return &SLOT_NONE;
|
|
780
802
|
return if (action.move_slot == 0) &SLOT_NONE else &SLOT;
|
|
781
803
|
}
|
|
782
804
|
|
|
783
|
-
const MULTI_NONE = [_]
|
|
784
|
-
const MULTI = [_]
|
|
805
|
+
const MULTI_NONE = [_]u3{0};
|
|
806
|
+
const MULTI = [_]u3{ 2, 3, 4, 5 };
|
|
785
807
|
|
|
786
808
|
/// Returns a slice with the correct range of values for multi hit given the `action` state
|
|
787
809
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
788
|
-
pub fn multiHit(action: Action, parent: Optional(bool)) []const
|
|
810
|
+
pub fn multiHit(action: Action, parent: Optional(bool)) []const u3 {
|
|
789
811
|
if (parent == .false) return &MULTI_NONE;
|
|
790
812
|
return if (action.multi_hit == 0) &MULTI_NONE else &MULTI;
|
|
791
813
|
}
|
|
@@ -917,16 +939,16 @@ test "Rolls.paralyzed" {
|
|
|
917
939
|
|
|
918
940
|
test "Rolls.moveSlot" {
|
|
919
941
|
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
920
|
-
try expectEqualSlices(
|
|
921
|
-
try expectEqualSlices(
|
|
922
|
-
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));
|
|
923
945
|
}
|
|
924
946
|
|
|
925
947
|
test "Rolls.multiHit" {
|
|
926
948
|
const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
|
|
927
|
-
try expectEqualSlices(
|
|
928
|
-
try expectEqualSlices(
|
|
929
|
-
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));
|
|
930
952
|
}
|
|
931
953
|
|
|
932
954
|
test "Rolls.metronome" {
|