@pkmn/engine 0.1.0-dev.30895c5a → 0.1.0-dev.3158fa12
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/README.md +9 -6
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -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 +97 -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 +25 -3
- 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 +13 -17
- package/build/pkg/gen1.js +6 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +37 -89
- package/build/pkg/index.js +6 -63
- 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 +35 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +142 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +283 -77
- package/package.json +52 -134
- package/src/bin/install-pkmn-engine +7 -5
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +44 -43
- package/src/lib/common/rational.zig +45 -50
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +213 -62
- package/src/lib/gen1/chance.zig +102 -71
- 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 +1 -2
- package/src/lib/gen1/data.zig +35 -38
- package/src/lib/gen1/helpers.zig +15 -27
- package/src/lib/gen1/mechanics.zig +109 -84
- package/src/lib/gen2/calc.zig +36 -18
- package/src/lib/gen2/chance.zig +86 -66
- 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 +1 -2
- package/src/lib/gen2/data.zig +26 -29
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +81 -81
- package/src/lib/node.zig +3 -116
- package/src/lib/pkmn.zig +12 -2
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +96 -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/chance.zig
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const print = std.debug.print;
|
|
5
|
-
|
|
6
|
-
const expect = std.testing.expect;
|
|
7
|
-
const expectEqual = std.testing.expectEqual;
|
|
8
|
-
|
|
9
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
2
|
+
|
|
3
|
+
const array = @import("../common/array.zig");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
5
|
+
const data = @import("data.zig");
|
|
6
|
+
const optional = @import("../common/optional.zig");
|
|
10
7
|
const options = @import("../common/options.zig");
|
|
11
8
|
const rational = @import("../common/rational.zig");
|
|
9
|
+
const std = @import("std");
|
|
12
10
|
const util = @import("../common/util.zig");
|
|
13
11
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const data = @import("data.zig");
|
|
18
|
-
|
|
12
|
+
const Array = array.Array;
|
|
13
|
+
const assert = std.debug.assert;
|
|
19
14
|
const enabled = options.chance;
|
|
20
|
-
const
|
|
21
|
-
|
|
15
|
+
const expect = std.testing.expect;
|
|
16
|
+
const expectEqual = std.testing.expectEqual;
|
|
17
|
+
const isPointerTo = util.isPointerTo;
|
|
22
18
|
const Move = data.Move;
|
|
23
19
|
const MoveSlot = data.MoveSlot;
|
|
20
|
+
const Optional = optional.Optional;
|
|
21
|
+
const Player = common.Player;
|
|
22
|
+
const PointerType = util.PointerType;
|
|
23
|
+
const print = std.debug.print;
|
|
24
|
+
const showdown = options.showdown;
|
|
25
|
+
|
|
26
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
27
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
28
|
+
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
24
29
|
|
|
25
30
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
26
31
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
@@ -37,8 +42,8 @@ pub const Actions = extern struct {
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/// Returns the `Action` for the given `player`.
|
|
40
|
-
pub fn get(self: anytype, player: Player)
|
|
41
|
-
assert(
|
|
45
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
|
|
46
|
+
assert(isPointerTo(self, Actions));
|
|
42
47
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
48
|
}
|
|
44
49
|
|
|
@@ -51,15 +56,15 @@ pub const Actions = extern struct {
|
|
|
51
56
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
52
57
|
/// set to the same value).
|
|
53
58
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
54
|
-
inline for (@typeInfo(Actions)
|
|
55
|
-
inline for (@typeInfo(Action)
|
|
59
|
+
inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
|
|
60
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
56
61
|
const a_val = @field(@field(a, player.name), field.name);
|
|
57
62
|
const b_val = @field(@field(b, player.name), field.name);
|
|
58
63
|
|
|
59
64
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
60
|
-
|
|
65
|
+
Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
61
66
|
return false,
|
|
62
|
-
|
|
67
|
+
Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
63
68
|
else => unreachable,
|
|
64
69
|
}
|
|
65
70
|
}
|
|
@@ -67,6 +72,25 @@ pub const Actions = extern struct {
|
|
|
67
72
|
return true;
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
/// TODO
|
|
76
|
+
pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
|
|
77
|
+
for (bs, 0..) |b, j| {
|
|
78
|
+
// TODO: is skipping this redundant check worth it?
|
|
79
|
+
if (i == j) continue;
|
|
80
|
+
if (b.matches(a)) return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// TODO
|
|
86
|
+
pub fn relax(actions: Actions) Actions {
|
|
87
|
+
if (!options.overwrite) return actions;
|
|
88
|
+
var a = actions;
|
|
89
|
+
if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
|
|
90
|
+
if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
|
|
91
|
+
return a;
|
|
92
|
+
}
|
|
93
|
+
|
|
70
94
|
pub fn fmt(self: Actions, writer: anytype, shape: bool) !void {
|
|
71
95
|
try writer.writeAll("<P1 = ");
|
|
72
96
|
try self.p1.fmt(writer, shape);
|
|
@@ -175,10 +199,10 @@ pub const Action = packed struct(u64) {
|
|
|
175
199
|
pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
|
|
176
200
|
try writer.writeByte('(');
|
|
177
201
|
var printed = false;
|
|
178
|
-
inline for (@typeInfo(Action)
|
|
202
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
179
203
|
const val = @field(self, field.name);
|
|
180
204
|
switch (@typeInfo(@TypeOf(val))) {
|
|
181
|
-
|
|
205
|
+
Enum => if (val != .None) {
|
|
182
206
|
if (printed) try writer.writeAll(", ");
|
|
183
207
|
if (shape) {
|
|
184
208
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -199,7 +223,7 @@ pub const Action = packed struct(u64) {
|
|
|
199
223
|
}
|
|
200
224
|
printed = true;
|
|
201
225
|
},
|
|
202
|
-
|
|
226
|
+
Int => if (val != 0) {
|
|
203
227
|
if (printed) try writer.writeAll(", ");
|
|
204
228
|
if (shape) {
|
|
205
229
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -216,15 +240,19 @@ pub const Action = packed struct(u64) {
|
|
|
216
240
|
};
|
|
217
241
|
|
|
218
242
|
/// TODO
|
|
219
|
-
pub const Durations = struct {
|
|
243
|
+
pub const Durations = extern struct {
|
|
220
244
|
/// TODO
|
|
221
245
|
p1: Duration = .{},
|
|
222
246
|
/// TODO
|
|
223
247
|
p2: Duration = .{},
|
|
224
248
|
|
|
249
|
+
comptime {
|
|
250
|
+
assert(@sizeOf(Durations) == 8);
|
|
251
|
+
}
|
|
252
|
+
|
|
225
253
|
/// TODO
|
|
226
|
-
pub fn get(self: anytype, player: Player)
|
|
227
|
-
assert(
|
|
254
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Duration) {
|
|
255
|
+
assert(isPointerTo(self, Durations));
|
|
228
256
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
229
257
|
}
|
|
230
258
|
|
|
@@ -239,14 +267,18 @@ pub const Durations = struct {
|
|
|
239
267
|
}
|
|
240
268
|
};
|
|
241
269
|
|
|
270
|
+
pub const Sleeps = Array(6, u3);
|
|
271
|
+
|
|
242
272
|
/// TODO
|
|
243
|
-
pub const Duration = struct {
|
|
244
|
-
sleeps:
|
|
273
|
+
pub const Duration = packed struct(u32) {
|
|
274
|
+
sleeps: Sleeps.T = 0,
|
|
245
275
|
confusion: u3 = 0,
|
|
246
276
|
disable: u4 = 0,
|
|
247
277
|
attacking: u3 = 0,
|
|
248
278
|
binding: u3 = 0,
|
|
249
279
|
|
|
280
|
+
_: u1 = 0,
|
|
281
|
+
|
|
250
282
|
pub fn format(
|
|
251
283
|
self: Duration,
|
|
252
284
|
comptime fmt: []const u8,
|
|
@@ -256,27 +288,22 @@ pub const Duration = struct {
|
|
|
256
288
|
_ = .{ fmt, opts };
|
|
257
289
|
try writer.writeByte('(');
|
|
258
290
|
var printed = false;
|
|
259
|
-
inline for (@typeInfo(Duration)
|
|
291
|
+
inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
|
|
260
292
|
const val = @field(self, field.name);
|
|
261
293
|
switch (@typeInfo(@TypeOf(val))) {
|
|
262
|
-
|
|
294
|
+
Int => if (val != 0) {
|
|
263
295
|
if (printed) try writer.writeAll(", ");
|
|
264
|
-
|
|
265
|
-
printed = true;
|
|
266
|
-
},
|
|
267
|
-
.Array => {
|
|
268
|
-
var total: usize = 0;
|
|
269
|
-
for (val) |v| total += v;
|
|
270
|
-
if (total > 0) {
|
|
271
|
-
if (printed) try writer.writeAll(", ");
|
|
296
|
+
if (comptime std.mem.eql(u8, field.name, "sleeps")) {
|
|
272
297
|
try writer.print("{s}:[", .{field.name});
|
|
273
|
-
for (
|
|
298
|
+
for (0..6) |i| {
|
|
274
299
|
if (i != 0) try writer.writeByte(',');
|
|
275
|
-
try writer.print("{d}", .{
|
|
300
|
+
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
276
301
|
}
|
|
277
302
|
try writer.writeAll("]");
|
|
278
|
-
|
|
303
|
+
} else {
|
|
304
|
+
try writer.print("{s}:{d}", .{ field.name, val });
|
|
279
305
|
}
|
|
306
|
+
printed = true;
|
|
280
307
|
},
|
|
281
308
|
else => unreachable,
|
|
282
309
|
}
|
|
@@ -364,7 +391,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
364
391
|
|
|
365
392
|
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
366
393
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
367
|
-
// "critical hit" was spurious or not as moves that do 1 or less damage don't
|
|
394
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
|
|
368
395
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
369
396
|
// passed to the critical hit helper).
|
|
370
397
|
//
|
|
@@ -380,7 +407,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
380
407
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
381
408
|
} else assert(!self.pending.glitch);
|
|
382
409
|
|
|
383
|
-
if (self.pending.damage_roll > 0) {
|
|
410
|
+
if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
|
|
384
411
|
try self.probability.update(1, 39);
|
|
385
412
|
action.damage = self.pending.damage_roll;
|
|
386
413
|
}
|
|
@@ -407,9 +434,9 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
407
434
|
|
|
408
435
|
var d = self.durations.get(player);
|
|
409
436
|
|
|
410
|
-
const out = d.sleeps
|
|
411
|
-
d.sleeps
|
|
412
|
-
d.sleeps
|
|
437
|
+
const out = Sleeps.get(d.sleeps, 0);
|
|
438
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, Sleeps.get(d.sleeps, slot - 1));
|
|
439
|
+
d.sleeps = Sleeps.set(d.sleeps, slot - 1, out);
|
|
413
440
|
|
|
414
441
|
d.confusion = 0;
|
|
415
442
|
d.disable = 0;
|
|
@@ -542,7 +569,8 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
542
569
|
.None => {
|
|
543
570
|
@field(a, @tagName(field)) = .None;
|
|
544
571
|
if (field == .sleep) {
|
|
545
|
-
self.durations.get(player)
|
|
572
|
+
var d = self.durations.get(player);
|
|
573
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
546
574
|
} else {
|
|
547
575
|
@field(self.durations.get(player), @tagName(field)) = 0;
|
|
548
576
|
}
|
|
@@ -556,7 +584,8 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
556
584
|
});
|
|
557
585
|
@field(a, @tagName(field)) = .started;
|
|
558
586
|
if (field == .sleep) {
|
|
559
|
-
self.durations.get(player)
|
|
587
|
+
var d = self.durations.get(player);
|
|
588
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 1);
|
|
560
589
|
} else {
|
|
561
590
|
@field(self.durations.get(player), @tagName(field)) = 1;
|
|
562
591
|
}
|
|
@@ -573,19 +602,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
573
602
|
a.sleep = obs;
|
|
574
603
|
|
|
575
604
|
var d = self.durations.get(player);
|
|
576
|
-
const n = d.sleeps
|
|
605
|
+
const n = Sleeps.get(d.sleeps, 0);
|
|
577
606
|
|
|
578
607
|
if (obs == .None) {
|
|
579
|
-
d.sleeps
|
|
608
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
580
609
|
} else if (obs == .ended) {
|
|
581
610
|
assert(n >= 1 and n <= 7);
|
|
582
611
|
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
583
|
-
d.sleeps
|
|
612
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
584
613
|
} else {
|
|
585
614
|
assert(obs == .continuing);
|
|
586
615
|
assert(n >= 1 and n < 7);
|
|
587
616
|
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
588
|
-
d.sleeps
|
|
617
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, n + 1);
|
|
589
618
|
}
|
|
590
619
|
}
|
|
591
620
|
|
|
@@ -643,6 +672,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
643
672
|
var d = self.durations.get(player);
|
|
644
673
|
const n = d.attacking;
|
|
645
674
|
if (obs == .ended) {
|
|
675
|
+
// "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
646
676
|
assert(n >= 2 and n <= 3);
|
|
647
677
|
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
648
678
|
d.attacking = 0;
|
|
@@ -669,6 +699,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
669
699
|
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
670
700
|
|
|
671
701
|
if (obs == .ended) {
|
|
702
|
+
// "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
672
703
|
assert(n >= 1 and n <= 4);
|
|
673
704
|
if (n != 4) try self.probability.update(p, q);
|
|
674
705
|
d.binding = 0;
|
|
@@ -802,16 +833,16 @@ test "Chance.damage" {
|
|
|
802
833
|
|
|
803
834
|
try chance.damage(.P1, 217);
|
|
804
835
|
if (showdown) {
|
|
805
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
836
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
806
837
|
try expectProbability(&chance.probability, 1, 39);
|
|
807
838
|
} else {
|
|
808
839
|
try expectProbability(&chance.probability, 1, 1);
|
|
809
|
-
try expectValue(0, chance.actions.p1.damage);
|
|
840
|
+
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
810
841
|
|
|
811
842
|
try chance.commit(.P1, .hit);
|
|
812
843
|
|
|
813
844
|
try expectProbability(&chance.probability, 1, 39);
|
|
814
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
845
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
815
846
|
}
|
|
816
847
|
}
|
|
817
848
|
|
|
@@ -863,13 +894,13 @@ test "Chance.moveSlot" {
|
|
|
863
894
|
|
|
864
895
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
865
896
|
try expectProbability(&chance.probability, 1, 2);
|
|
866
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
897
|
+
try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
|
|
867
898
|
|
|
868
899
|
chance.reset();
|
|
869
900
|
|
|
870
901
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
871
902
|
try expectProbability(&chance.probability, 1, 3);
|
|
872
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
903
|
+
try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
|
|
873
904
|
}
|
|
874
905
|
|
|
875
906
|
test "Chance.multiHit" {
|
|
@@ -877,43 +908,43 @@ test "Chance.multiHit" {
|
|
|
877
908
|
|
|
878
909
|
try chance.multiHit(.P1, 3);
|
|
879
910
|
try expectProbability(&chance.probability, 3, 8);
|
|
880
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
911
|
+
try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
|
|
881
912
|
|
|
882
913
|
chance.reset();
|
|
883
914
|
|
|
884
915
|
try chance.multiHit(.P2, 5);
|
|
885
916
|
try expectProbability(&chance.probability, 1, 8);
|
|
886
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
917
|
+
try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
|
|
887
918
|
}
|
|
888
919
|
|
|
889
920
|
test "Chance.duration" {
|
|
890
921
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
891
922
|
|
|
892
923
|
chance.duration(.P1, 2);
|
|
893
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
924
|
+
try expectValue(@as(u4, 2), chance.actions.p1.duration);
|
|
894
925
|
|
|
895
926
|
chance.reset();
|
|
896
927
|
|
|
897
928
|
chance.duration(.P2, 4);
|
|
898
|
-
try expectValue(4, chance.actions.p2.duration);
|
|
929
|
+
try expectValue(@as(u4, 4), chance.actions.p2.duration);
|
|
899
930
|
}
|
|
900
931
|
|
|
901
932
|
test "Chance.sleep" {
|
|
902
933
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
903
934
|
|
|
904
935
|
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
905
|
-
chance.durations.p1.sleeps
|
|
936
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
906
937
|
try chance.sleep(.P1, .ended);
|
|
907
938
|
try expectProbability(&chance.probability, 1, d);
|
|
908
|
-
try expectValue(0, chance.durations.p1.sleeps
|
|
939
|
+
try expectValue(@as(u3, 0), Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
909
940
|
|
|
910
941
|
chance.reset();
|
|
911
942
|
|
|
912
943
|
if (i < 7) {
|
|
913
|
-
chance.durations.p1.sleeps
|
|
944
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
914
945
|
try chance.sleep(.P1, .continuing);
|
|
915
946
|
try expectProbability(&chance.probability, d - 1, d);
|
|
916
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.sleeps
|
|
947
|
+
try expectValue(@as(u3, @intCast(i)) + 1, Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
917
948
|
|
|
918
949
|
chance.reset();
|
|
919
950
|
}
|
|
@@ -928,7 +959,7 @@ test "Chance.confusion" {
|
|
|
928
959
|
chance.durations.p2.confusion = @intCast(i);
|
|
929
960
|
try chance.confusion(.P2, .ended);
|
|
930
961
|
try expectProbability(&chance.probability, 1, d);
|
|
931
|
-
try expectValue(0, chance.durations.p2.confusion);
|
|
962
|
+
try expectValue(@as(u3, 0), chance.durations.p2.confusion);
|
|
932
963
|
|
|
933
964
|
chance.reset();
|
|
934
965
|
}
|
|
@@ -951,7 +982,7 @@ test "Chance.disable" {
|
|
|
951
982
|
chance.durations.p1.disable = @intCast(i);
|
|
952
983
|
try chance.disable(.P1, .ended);
|
|
953
984
|
try expectProbability(&chance.probability, 1, d);
|
|
954
|
-
try expectValue(0, chance.durations.p1.disable);
|
|
985
|
+
try expectValue(@as(u4, 0), chance.durations.p1.disable);
|
|
955
986
|
|
|
956
987
|
chance.reset();
|
|
957
988
|
|
|
@@ -974,7 +1005,7 @@ test "Chance.attacking" {
|
|
|
974
1005
|
chance.durations.p2.attacking = @intCast(i);
|
|
975
1006
|
try chance.attacking(.P2, .ended);
|
|
976
1007
|
try expectProbability(&chance.probability, 1, d);
|
|
977
|
-
try expectValue(0, chance.durations.p2.attacking);
|
|
1008
|
+
try expectValue(@as(u3, 0), chance.durations.p2.attacking);
|
|
978
1009
|
|
|
979
1010
|
chance.reset();
|
|
980
1011
|
}
|
|
@@ -1000,7 +1031,7 @@ test "Chance.binding" {
|
|
|
1000
1031
|
chance.durations.p1.binding = @intCast(i);
|
|
1001
1032
|
try chance.binding(.P1, .ended);
|
|
1002
1033
|
try expectProbability(&chance.probability, p, q);
|
|
1003
|
-
try expectValue(0, chance.durations.p1.binding);
|
|
1034
|
+
try expectValue(@as(u3, 0), chance.durations.p1.binding);
|
|
1004
1035
|
|
|
1005
1036
|
chance.reset();
|
|
1006
1037
|
|
|
@@ -1020,7 +1051,7 @@ test "Chance.psywave" {
|
|
|
1020
1051
|
|
|
1021
1052
|
try chance.psywave(.P2, 100, 150);
|
|
1022
1053
|
try expectProbability(&chance.probability, 1, 150);
|
|
1023
|
-
try expectValue(101, chance.actions.p2.psywave);
|
|
1054
|
+
try expectValue(@as(u8, 101), chance.actions.p2.psywave);
|
|
1024
1055
|
}
|
|
1025
1056
|
|
|
1026
1057
|
test "Chance.metronome" {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
|
-
const rng = @import("../../common/rng.zig");
|
|
6
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const rng = @import("../../common/rng.zig");
|
|
5
|
+
const std = @import("std");
|
|
7
6
|
|
|
8
7
|
const assert = std.debug.assert;
|
|
9
|
-
|
|
10
8
|
const percent = rng.Gen12.percent;
|
|
11
9
|
const Type = gen1.Type;
|
|
12
10
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen1.Stats;
|
|
10
8
|
const Types = gen1.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
2
|
+
const calc = @import("calc.zig");
|
|
3
|
+
const chance = @import("chance.zig");
|
|
6
4
|
const data = @import("../common/data.zig");
|
|
5
|
+
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const moves = @import("data/moves.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
8
|
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
|
-
|
|
11
|
-
const moves = @import("data/moves.zig");
|
|
12
10
|
const species = @import("data/species.zig");
|
|
11
|
+
const std = @import("std");
|
|
13
12
|
const types = @import("data/types.zig");
|
|
14
|
-
|
|
15
|
-
const calc = @import("calc.zig");
|
|
16
|
-
const chance = @import("chance.zig");
|
|
17
|
-
const mechanics = @import("mechanics.zig");
|
|
13
|
+
const util = @import("../common/util.zig");
|
|
18
14
|
|
|
19
15
|
const assert = std.debug.assert;
|
|
20
16
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -47,6 +43,7 @@ const Player = data.Player;
|
|
|
47
43
|
const Result = data.Result;
|
|
48
44
|
|
|
49
45
|
const PointerType = util.PointerType;
|
|
46
|
+
const isPointerTo = util.isPointerTo;
|
|
50
47
|
|
|
51
48
|
const showdown = pkmn.options.showdown;
|
|
52
49
|
|
|
@@ -71,13 +68,13 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
71
68
|
|
|
72
69
|
/// Returns the `Side` for the given `player`.
|
|
73
70
|
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
74
|
-
assert(
|
|
71
|
+
assert(isPointerTo(self, Self));
|
|
75
72
|
return &self.sides[@intFromEnum(player)];
|
|
76
73
|
}
|
|
77
74
|
|
|
78
75
|
/// Returns the `Side` of the opponent for the given `player`
|
|
79
76
|
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
80
|
-
assert(
|
|
77
|
+
assert(isPointerTo(self, Self));
|
|
81
78
|
return &self.sides[@intFromEnum(player.foe())];
|
|
82
79
|
}
|
|
83
80
|
|
|
@@ -91,7 +88,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
91
88
|
self: anytype,
|
|
92
89
|
player: Player,
|
|
93
90
|
) PointerType(@TypeOf(self), MoveDetails) {
|
|
94
|
-
assert(
|
|
91
|
+
assert(isPointerTo(self, Self));
|
|
95
92
|
return &self.last_moves[@intFromEnum(player)];
|
|
96
93
|
}
|
|
97
94
|
|
|
@@ -142,7 +139,7 @@ pub const Side = extern struct {
|
|
|
142
139
|
|
|
143
140
|
/// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
|
|
144
141
|
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
-
assert(
|
|
142
|
+
assert(isPointerTo(self, Side));
|
|
146
143
|
assert(slot > 0 and slot <= 6);
|
|
147
144
|
const id = self.order[slot - 1];
|
|
148
145
|
assert(id > 0 and id <= 6);
|
|
@@ -151,7 +148,7 @@ pub const Side = extern struct {
|
|
|
151
148
|
|
|
152
149
|
/// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
|
|
153
150
|
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
154
|
-
assert(
|
|
151
|
+
assert(isPointerTo(self, Side));
|
|
155
152
|
return self.get(1);
|
|
156
153
|
}
|
|
157
154
|
};
|
|
@@ -177,7 +174,7 @@ pub const ActivePokemon = extern struct {
|
|
|
177
174
|
|
|
178
175
|
/// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
|
|
179
176
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
180
|
-
assert(
|
|
177
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
181
178
|
assert(mslot > 0 and mslot <= 4);
|
|
182
179
|
assert(self.moves[mslot - 1].id != .None);
|
|
183
180
|
return &self.moves[mslot - 1];
|
|
@@ -207,7 +204,7 @@ pub const Pokemon = extern struct {
|
|
|
207
204
|
|
|
208
205
|
/// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
|
|
209
206
|
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
210
|
-
assert(
|
|
207
|
+
assert(isPointerTo(self, Pokemon));
|
|
211
208
|
assert(mslot > 0 and mslot <= 4);
|
|
212
209
|
assert(self.moves[mslot - 1].id != .None);
|
|
213
210
|
return &self.moves[mslot - 1];
|
|
@@ -255,7 +252,7 @@ pub const Status = enum(u8) {
|
|
|
255
252
|
|
|
256
253
|
pub const TOX = 0b10001000;
|
|
257
254
|
|
|
258
|
-
const
|
|
255
|
+
const SLEEP = 0b111;
|
|
259
256
|
|
|
260
257
|
/// Whether or not the status `num` is the same as `status`.
|
|
261
258
|
pub fn is(num: u8, status: Status) bool {
|
|
@@ -283,7 +280,7 @@ pub const Status = enum(u8) {
|
|
|
283
280
|
|
|
284
281
|
/// Returns the duration of a sleep status.
|
|
285
282
|
pub fn duration(num: u8) u3 {
|
|
286
|
-
return @intCast(num &
|
|
283
|
+
return @intCast(num & SLEEP);
|
|
287
284
|
}
|
|
288
285
|
|
|
289
286
|
/// Returns whether `num` reflects any status.
|
|
@@ -316,12 +313,12 @@ test Status {
|
|
|
316
313
|
try expect(Status.is(Status.slp(5), .SLP));
|
|
317
314
|
try expect(!Status.is(Status.slp(5), .EXT));
|
|
318
315
|
try expect(!Status.is(Status.slp(7), .PSN));
|
|
319
|
-
try expectEqual(5, Status.duration(Status.slp(5)));
|
|
316
|
+
try expectEqual(@as(u3, 5), Status.duration(Status.slp(5)));
|
|
320
317
|
try expect(Status.is(Status.slf(2), .SLP));
|
|
321
318
|
try expect(Status.is(Status.slf(2), .EXT));
|
|
322
319
|
try expect(Status.is(Status.TOX, .PSN));
|
|
323
320
|
try expect(Status.is(Status.TOX, .EXT));
|
|
324
|
-
try expectEqual(1, Status.duration(Status.slp(1)));
|
|
321
|
+
try expectEqual(@as(u3, 1), Status.duration(Status.slp(1)));
|
|
325
322
|
}
|
|
326
323
|
|
|
327
324
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
@@ -405,16 +402,16 @@ test Volatiles {
|
|
|
405
402
|
try expect(!volatiles.Transform);
|
|
406
403
|
try expect(!volatiles.MultiHit);
|
|
407
404
|
|
|
408
|
-
try expectEqual(235, volatiles.state);
|
|
409
|
-
try expectEqual(42, volatiles.substitute);
|
|
410
|
-
try expectEqual(2, volatiles.disable_move);
|
|
411
|
-
try expectEqual(4, volatiles.disable_duration);
|
|
412
|
-
try expectEqual(2, volatiles.confusion);
|
|
413
|
-
try expectEqual(4, volatiles.toxic);
|
|
414
|
-
try expectEqual(3, volatiles.attacks);
|
|
405
|
+
try expectEqual(@as(u16, 235), volatiles.state);
|
|
406
|
+
try expectEqual(@as(u8, 42), volatiles.substitute);
|
|
407
|
+
try expectEqual(@as(u4, 2), volatiles.disable_move);
|
|
408
|
+
try expectEqual(@as(u4, 4), volatiles.disable_duration);
|
|
409
|
+
try expectEqual(@as(u3, 2), volatiles.confusion);
|
|
410
|
+
try expectEqual(@as(u5, 4), volatiles.toxic);
|
|
411
|
+
try expectEqual(@as(u3, 3), volatiles.attacks);
|
|
415
412
|
}
|
|
416
413
|
|
|
417
|
-
///
|
|
414
|
+
/// Representation of a Pokémon's stats in Generation I.
|
|
418
415
|
pub fn Stats(comptime T: type) type {
|
|
419
416
|
return extern struct {
|
|
420
417
|
hp: T = 0,
|
|
@@ -442,9 +439,9 @@ test Stats {
|
|
|
442
439
|
try expectEqual(0, stats.def);
|
|
443
440
|
|
|
444
441
|
var base = Species.get(.Gyarados).stats;
|
|
445
|
-
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
442
|
+
try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
446
443
|
base = Species.get(.Pidgeot).stats;
|
|
447
|
-
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
444
|
+
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
448
445
|
}
|
|
449
446
|
|
|
450
447
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
@@ -537,7 +534,7 @@ pub const Species = species.Species;
|
|
|
537
534
|
|
|
538
535
|
test Species {
|
|
539
536
|
try expectEqual(2, @intFromEnum(Species.Ivysaur));
|
|
540
|
-
try expectEqual(100, Species.get(.Mew).stats.def);
|
|
537
|
+
try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
|
|
541
538
|
}
|
|
542
539
|
|
|
543
540
|
/// Representation of a Generation I type in Pokémon.
|
|
@@ -576,8 +573,8 @@ test Types {
|
|
|
576
573
|
try expectEqual(Effectiveness.Resisted, Type.effectiveness(.Fire, .Water));
|
|
577
574
|
try expectEqual(Effectiveness.Neutral, Type.effectiveness(.Normal, .Grass));
|
|
578
575
|
|
|
579
|
-
try expectEqual(3, Type.precedence(.Water, .Water));
|
|
580
|
-
try expectEqual(26, Type.precedence(.Bug, .Ghost));
|
|
576
|
+
try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
|
|
577
|
+
try expectEqual(@as(u8, 26), Type.precedence(.Bug, .Ghost));
|
|
581
578
|
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
582
579
|
|
|
583
580
|
const t: Types = .{ .type1 = .Rock, .type2 = .Ground };
|
|
@@ -617,18 +614,18 @@ pub const DVs = struct {
|
|
|
617
614
|
|
|
618
615
|
test DVs {
|
|
619
616
|
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
620
|
-
try expectEqual(15, dvs.hp());
|
|
617
|
+
try expectEqual(@as(u4, 15), dvs.hp());
|
|
621
618
|
dvs = .{
|
|
622
619
|
.atk = 5,
|
|
623
620
|
.def = 15,
|
|
624
621
|
.spe = 13,
|
|
625
622
|
.spc = 13,
|
|
626
623
|
};
|
|
627
|
-
try expectEqual(15, dvs.hp());
|
|
624
|
+
try expectEqual(@as(u4, 15), dvs.hp());
|
|
628
625
|
dvs = .{
|
|
629
626
|
.def = 3,
|
|
630
627
|
.spe = 10,
|
|
631
628
|
.spc = 11,
|
|
632
629
|
};
|
|
633
|
-
try expectEqual(13, dvs.hp());
|
|
630
|
+
try expectEqual(@as(u4, 13), dvs.hp());
|
|
634
631
|
}
|