@pkmn/engine 0.1.0-dev.683fba11 → 0.1.0-dev.6b383c80
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 +22 -16
- package/build/pkg/addon/browser.js +6 -3
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +23 -10
- package/build/pkg/addon/node.js.map +1 -1
- package/build/pkg/addon/wasm.js +23 -14
- package/build/pkg/addon/wasm.js.map +1 -1
- package/build/pkg/addon.d.ts +5 -5
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +2 -1
- package/build/pkg/gen1.js +22 -9
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -0
- 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 -11
- 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 +18 -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 +140 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +255 -313
- package/package.json +29 -131
- package/src/bin/install-pkmn-engine +24 -22
- 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 +9 -16
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +46 -61
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +20 -25
- package/src/lib/common/protocol.zig +224 -189
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/rng.zig +4 -4
- package/src/lib/common/util.zig +34 -31
- package/src/lib/gen1/calc.zig +141 -119
- package/src/lib/gen1/chance.zig +162 -101
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +27 -24
- package/src/lib/gen1/helpers.zig +36 -26
- package/src/lib/gen1/mechanics.zig +45 -49
- package/src/lib/gen2/calc.zig +3 -6
- package/src/lib/gen2/chance.zig +55 -43
- package/src/lib/gen2/data/types.zig +6 -3
- package/src/lib/gen2/data.zig +20 -16
- package/src/lib/gen2/helpers.zig +27 -19
- package/src/lib/gen2/mechanics.zig +17 -17
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +3 -118
- package/src/lib/pkmn.zig +94 -19
- package/src/lib/wasm.zig +9 -44
- package/src/tools/display/dom.ts +43 -31
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -23,10 +23,6 @@ const PointerType = util.PointerType;
|
|
|
23
23
|
const print = std.debug.print;
|
|
24
24
|
const showdown = options.showdown;
|
|
25
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;
|
|
29
|
-
|
|
30
26
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
31
27
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
32
28
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -49,22 +45,36 @@ pub const Actions = extern struct {
|
|
|
49
45
|
|
|
50
46
|
/// Returns true if `a` is equal to `b`.
|
|
51
47
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
52
|
-
return @as(
|
|
48
|
+
return @as(u64, @bitCast(a.p1)) == @as(u64, @bitCast(b.p1)) and
|
|
49
|
+
@as(u64, @bitCast(a.p2)) == @as(u64, @bitCast(b.p2));
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
/// Returns true if `a` has the same "shape" as `b`, where `Actions` are defined to have the
|
|
56
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
57
54
|
/// set to the same value).
|
|
58
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
59
|
-
inline for (@
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
57
|
+
@typeInfo(Actions).@"struct".fields
|
|
58
|
+
else
|
|
59
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
60
|
+
{
|
|
61
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
62
|
+
player.name
|
|
63
|
+
else
|
|
64
|
+
player;
|
|
65
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
66
|
+
@typeInfo(Action).@"struct".fields
|
|
67
|
+
else
|
|
68
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
69
|
+
{
|
|
70
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
71
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
72
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
63
73
|
|
|
64
74
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
65
|
-
|
|
75
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
66
76
|
return false,
|
|
67
|
-
|
|
77
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
68
78
|
else => unreachable,
|
|
69
79
|
}
|
|
70
80
|
}
|
|
@@ -91,7 +101,7 @@ pub const Actions = extern struct {
|
|
|
91
101
|
return a;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
pub fn fmt(self: Actions, writer:
|
|
104
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
95
105
|
try writer.writeAll("<P1 = ");
|
|
96
106
|
try self.p1.fmt(writer, shape);
|
|
97
107
|
try writer.writeAll(", P2 = ");
|
|
@@ -99,8 +109,7 @@ pub const Actions = extern struct {
|
|
|
99
109
|
try writer.writeAll(">");
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
pub fn format(a: Actions,
|
|
103
|
-
_ = .{ f, o };
|
|
112
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
104
113
|
try fmt(a, w, false);
|
|
105
114
|
}
|
|
106
115
|
};
|
|
@@ -124,18 +133,41 @@ test Actions {
|
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
127
|
-
pub
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
pub fn Observation(comptime field: Duration.Field) type {
|
|
137
|
+
return switch (field) {
|
|
138
|
+
.attacking, .binding => enum(u2) {
|
|
139
|
+
started,
|
|
140
|
+
continuing,
|
|
141
|
+
ended,
|
|
142
|
+
},
|
|
143
|
+
.sleep, .disable => enum(u3) {
|
|
144
|
+
started,
|
|
145
|
+
continuing,
|
|
146
|
+
ended,
|
|
147
|
+
// TODO haze support
|
|
148
|
+
unused1,
|
|
149
|
+
unused2,
|
|
150
|
+
unused3,
|
|
151
|
+
unused4,
|
|
152
|
+
},
|
|
153
|
+
.confusion => enum(u4) {
|
|
154
|
+
started,
|
|
155
|
+
continuing,
|
|
156
|
+
ended,
|
|
157
|
+
/// An indicator that an existing confusion duration has been overwritten by a new
|
|
158
|
+
/// confusion duration due to a Thrashing move ending ("hidden" confusion). This is not
|
|
159
|
+
/// immediately observable to an opponent, to compute probabilities from an opponent's
|
|
160
|
+
/// point of view based solely on public information extra work must be done to correct
|
|
161
|
+
/// for the information leak
|
|
162
|
+
overwritten,
|
|
163
|
+
// TODO haze support
|
|
164
|
+
unused1,
|
|
165
|
+
unused2,
|
|
166
|
+
unused3,
|
|
167
|
+
unused4,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
139
171
|
|
|
140
172
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
141
173
|
/// single player.
|
|
@@ -162,26 +194,24 @@ pub const Action = packed struct(u64) {
|
|
|
162
194
|
duration: u4 = 0,
|
|
163
195
|
|
|
164
196
|
/// TODO
|
|
165
|
-
sleep: Optional(Observation) = .None,
|
|
197
|
+
sleep: Optional(Observation(.sleep)) = .None,
|
|
166
198
|
/// TODO
|
|
167
|
-
confusion: Optional(
|
|
199
|
+
confusion: Optional(Observation(.confusion)) = .None,
|
|
168
200
|
/// TODO
|
|
169
|
-
disable: Optional(Observation) = .None,
|
|
201
|
+
disable: Optional(Observation(.disable)) = .None,
|
|
170
202
|
/// TODO
|
|
171
|
-
attacking: Optional(Observation) = .None,
|
|
203
|
+
attacking: Optional(Observation(.attacking)) = .None,
|
|
172
204
|
/// TODO
|
|
173
|
-
binding: Optional(Observation) = .None,
|
|
174
|
-
|
|
175
|
-
_: u1 = 0,
|
|
205
|
+
binding: Optional(Observation(.binding)) = .None,
|
|
176
206
|
|
|
177
207
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
178
208
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
179
|
-
move_slot:
|
|
209
|
+
move_slot: u3 = 0,
|
|
210
|
+
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
211
|
+
multi_hit: u3 = 0,
|
|
180
212
|
|
|
181
213
|
/// TODO
|
|
182
214
|
pp: u4 = 0,
|
|
183
|
-
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
184
|
-
multi_hit: u4 = 0,
|
|
185
215
|
|
|
186
216
|
/// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
|
|
187
217
|
psywave: u8 = 0,
|
|
@@ -191,44 +221,45 @@ pub const Action = packed struct(u64) {
|
|
|
191
221
|
|
|
192
222
|
pub const Field = std.meta.FieldEnum(Action);
|
|
193
223
|
|
|
194
|
-
pub fn format(a: Action,
|
|
195
|
-
_ = .{ f, o };
|
|
224
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
196
225
|
try fmt(a, w, false);
|
|
197
226
|
}
|
|
198
227
|
|
|
199
|
-
|
|
228
|
+
const SYMBOLS = [_][]const u8{ "+", "", "-", "#" };
|
|
229
|
+
|
|
230
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
200
231
|
try writer.writeByte('(');
|
|
201
232
|
var printed = false;
|
|
202
|
-
inline for (@
|
|
203
|
-
|
|
233
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
234
|
+
@typeInfo(Action).@"struct".fields
|
|
235
|
+
else
|
|
236
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
237
|
+
{
|
|
238
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
239
|
+
const val = @field(self, field_name);
|
|
204
240
|
switch (@typeInfo(@TypeOf(val))) {
|
|
205
|
-
|
|
241
|
+
.@"enum" => if (val != .None) {
|
|
206
242
|
if (printed) try writer.writeAll(", ");
|
|
207
243
|
if (shape) {
|
|
208
|
-
try writer.print("{s}:?", .{
|
|
244
|
+
try writer.print("{s}:?", .{field_name});
|
|
209
245
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
210
246
|
try writer.print("{s}{s}", .{
|
|
211
247
|
if (val == .false) "!" else "",
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
} else if (@TypeOf(val) == Optional(Observation) or
|
|
215
|
-
@TypeOf(val) == Optional(Confusion))
|
|
216
|
-
{
|
|
217
|
-
try writer.print("{s}{s}", .{
|
|
218
|
-
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
219
|
-
field.name,
|
|
248
|
+
field_name,
|
|
220
249
|
});
|
|
250
|
+
} else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
|
|
251
|
+
try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field_name });
|
|
221
252
|
} else {
|
|
222
|
-
try writer.print("{s}:{s}", .{
|
|
253
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
223
254
|
}
|
|
224
255
|
printed = true;
|
|
225
256
|
},
|
|
226
|
-
|
|
257
|
+
.int => if (val != 0) {
|
|
227
258
|
if (printed) try writer.writeAll(", ");
|
|
228
259
|
if (shape) {
|
|
229
|
-
try writer.print("{s}:?", .{
|
|
260
|
+
try writer.print("{s}:?", .{field_name});
|
|
230
261
|
} else {
|
|
231
|
-
try writer.print("{s}:{d}", .{
|
|
262
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
232
263
|
}
|
|
233
264
|
printed = true;
|
|
234
265
|
},
|
|
@@ -256,14 +287,8 @@ pub const Durations = extern struct {
|
|
|
256
287
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
257
288
|
}
|
|
258
289
|
|
|
259
|
-
pub fn format(
|
|
260
|
-
self
|
|
261
|
-
comptime fmt: []const u8,
|
|
262
|
-
opts: std.fmt.FormatOptions,
|
|
263
|
-
writer: anytype,
|
|
264
|
-
) !void {
|
|
265
|
-
_ = .{ fmt, opts };
|
|
266
|
-
try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
|
|
290
|
+
pub fn format(self: Durations, writer: *std.Io.Writer) !void {
|
|
291
|
+
try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
|
|
267
292
|
}
|
|
268
293
|
};
|
|
269
294
|
|
|
@@ -279,29 +304,33 @@ pub const Duration = packed struct(u32) {
|
|
|
279
304
|
|
|
280
305
|
_: u1 = 0,
|
|
281
306
|
|
|
307
|
+
pub const Field = enum { attacking, binding, sleep, disable, confusion };
|
|
308
|
+
|
|
282
309
|
pub fn format(
|
|
283
310
|
self: Duration,
|
|
284
|
-
|
|
285
|
-
opts: std.fmt.FormatOptions,
|
|
286
|
-
writer: anytype,
|
|
311
|
+
writer: *std.Io.Writer,
|
|
287
312
|
) !void {
|
|
288
|
-
_ = .{ fmt, opts };
|
|
289
313
|
try writer.writeByte('(');
|
|
290
314
|
var printed = false;
|
|
291
|
-
inline for (@
|
|
292
|
-
|
|
315
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Duration).@"struct"), "fields"))
|
|
316
|
+
@typeInfo(Duration).@"struct".fields
|
|
317
|
+
else
|
|
318
|
+
@typeInfo(Duration).@"struct".field_names) |field|
|
|
319
|
+
{
|
|
320
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
321
|
+
const val = @field(self, field_name);
|
|
293
322
|
switch (@typeInfo(@TypeOf(val))) {
|
|
294
|
-
|
|
323
|
+
.int => if (val != 0) {
|
|
295
324
|
if (printed) try writer.writeAll(", ");
|
|
296
|
-
if (comptime std.mem.eql(u8,
|
|
297
|
-
try writer.print("{s}:[", .{
|
|
325
|
+
if (comptime std.mem.eql(u8, field_name, "sleeps")) {
|
|
326
|
+
try writer.print("{s}:[", .{field_name});
|
|
298
327
|
for (0..6) |i| {
|
|
299
328
|
if (i != 0) try writer.writeByte(',');
|
|
300
329
|
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
301
330
|
}
|
|
302
331
|
try writer.writeAll("]");
|
|
303
332
|
} else {
|
|
304
|
-
try writer.print("{s}:{d}", .{
|
|
333
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
305
334
|
}
|
|
306
335
|
printed = true;
|
|
307
336
|
},
|
|
@@ -552,7 +581,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
552
581
|
self: *Self,
|
|
553
582
|
comptime field: Action.Field,
|
|
554
583
|
player: Player,
|
|
555
|
-
obs: Optional(
|
|
584
|
+
obs: Optional(Observation(.confusion)),
|
|
556
585
|
) void {
|
|
557
586
|
if (!enabled) return;
|
|
558
587
|
|
|
@@ -594,7 +623,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
594
623
|
}
|
|
595
624
|
}
|
|
596
625
|
|
|
597
|
-
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
626
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
598
627
|
if (!enabled) return;
|
|
599
628
|
|
|
600
629
|
var a = self.actions.get(player);
|
|
@@ -618,7 +647,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
618
647
|
}
|
|
619
648
|
}
|
|
620
649
|
|
|
621
|
-
pub fn confusion(
|
|
650
|
+
pub fn confusion(
|
|
651
|
+
self: *Self,
|
|
652
|
+
player: Player,
|
|
653
|
+
obs: Optional(Observation(.confusion)),
|
|
654
|
+
) Error!void {
|
|
622
655
|
if (!enabled) return;
|
|
623
656
|
|
|
624
657
|
var a = self.actions.get(player);
|
|
@@ -640,7 +673,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
640
673
|
}
|
|
641
674
|
}
|
|
642
675
|
|
|
643
|
-
pub fn disable(
|
|
676
|
+
pub fn disable(
|
|
677
|
+
self: *Self,
|
|
678
|
+
player: Player,
|
|
679
|
+
obs: Optional(Observation(.disable)),
|
|
680
|
+
) Error!void {
|
|
644
681
|
if (!enabled) return;
|
|
645
682
|
|
|
646
683
|
var a = self.actions.get(player);
|
|
@@ -662,7 +699,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
662
699
|
}
|
|
663
700
|
}
|
|
664
701
|
|
|
665
|
-
pub fn attacking(
|
|
702
|
+
pub fn attacking(
|
|
703
|
+
self: *Self,
|
|
704
|
+
player: Player,
|
|
705
|
+
obs: Optional(Observation(.attacking)),
|
|
706
|
+
) Error!void {
|
|
666
707
|
if (!enabled) return;
|
|
667
708
|
|
|
668
709
|
var a = self.actions.get(player);
|
|
@@ -684,7 +725,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
684
725
|
}
|
|
685
726
|
}
|
|
686
727
|
|
|
687
|
-
pub fn binding(
|
|
728
|
+
pub fn binding(
|
|
729
|
+
self: *Self,
|
|
730
|
+
player: Player,
|
|
731
|
+
obs: Optional(Observation(.binding)),
|
|
732
|
+
) Error!void {
|
|
688
733
|
if (!enabled) return;
|
|
689
734
|
|
|
690
735
|
var a = self.actions.get(player);
|
|
@@ -833,16 +878,16 @@ test "Chance.damage" {
|
|
|
833
878
|
|
|
834
879
|
try chance.damage(.P1, 217);
|
|
835
880
|
if (showdown) {
|
|
836
|
-
try expectValue(
|
|
881
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
837
882
|
try expectProbability(&chance.probability, 1, 39);
|
|
838
883
|
} else {
|
|
839
884
|
try expectProbability(&chance.probability, 1, 1);
|
|
840
|
-
try expectValue(
|
|
885
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
841
886
|
|
|
842
887
|
try chance.commit(.P1, .hit);
|
|
843
888
|
|
|
844
889
|
try expectProbability(&chance.probability, 1, 39);
|
|
845
|
-
try expectValue(
|
|
890
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
846
891
|
}
|
|
847
892
|
}
|
|
848
893
|
|
|
@@ -894,13 +939,13 @@ test "Chance.moveSlot" {
|
|
|
894
939
|
|
|
895
940
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
896
941
|
try expectProbability(&chance.probability, 1, 2);
|
|
897
|
-
try expectValue(
|
|
942
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
898
943
|
|
|
899
944
|
chance.reset();
|
|
900
945
|
|
|
901
946
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
902
947
|
try expectProbability(&chance.probability, 1, 3);
|
|
903
|
-
try expectValue(
|
|
948
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
904
949
|
}
|
|
905
950
|
|
|
906
951
|
test "Chance.multiHit" {
|
|
@@ -908,25 +953,25 @@ test "Chance.multiHit" {
|
|
|
908
953
|
|
|
909
954
|
try chance.multiHit(.P1, 3);
|
|
910
955
|
try expectProbability(&chance.probability, 3, 8);
|
|
911
|
-
try expectValue(
|
|
956
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
912
957
|
|
|
913
958
|
chance.reset();
|
|
914
959
|
|
|
915
960
|
try chance.multiHit(.P2, 5);
|
|
916
961
|
try expectProbability(&chance.probability, 1, 8);
|
|
917
|
-
try expectValue(
|
|
962
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
918
963
|
}
|
|
919
964
|
|
|
920
965
|
test "Chance.duration" {
|
|
921
966
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
922
967
|
|
|
923
968
|
chance.duration(.P1, 2);
|
|
924
|
-
try expectValue(
|
|
969
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
925
970
|
|
|
926
971
|
chance.reset();
|
|
927
972
|
|
|
928
973
|
chance.duration(.P2, 4);
|
|
929
|
-
try expectValue(
|
|
974
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
930
975
|
}
|
|
931
976
|
|
|
932
977
|
test "Chance.sleep" {
|
|
@@ -936,7 +981,7 @@ test "Chance.sleep" {
|
|
|
936
981
|
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
937
982
|
try chance.sleep(.P1, .ended);
|
|
938
983
|
try expectProbability(&chance.probability, 1, d);
|
|
939
|
-
try expectValue(
|
|
984
|
+
try expectValue(0, Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
940
985
|
|
|
941
986
|
chance.reset();
|
|
942
987
|
|
|
@@ -959,7 +1004,7 @@ test "Chance.confusion" {
|
|
|
959
1004
|
chance.durations.p2.confusion = @intCast(i);
|
|
960
1005
|
try chance.confusion(.P2, .ended);
|
|
961
1006
|
try expectProbability(&chance.probability, 1, d);
|
|
962
|
-
try expectValue(
|
|
1007
|
+
try expectValue(0, chance.durations.p2.confusion);
|
|
963
1008
|
|
|
964
1009
|
chance.reset();
|
|
965
1010
|
}
|
|
@@ -982,7 +1027,7 @@ test "Chance.disable" {
|
|
|
982
1027
|
chance.durations.p1.disable = @intCast(i);
|
|
983
1028
|
try chance.disable(.P1, .ended);
|
|
984
1029
|
try expectProbability(&chance.probability, 1, d);
|
|
985
|
-
try expectValue(
|
|
1030
|
+
try expectValue(0, chance.durations.p1.disable);
|
|
986
1031
|
|
|
987
1032
|
chance.reset();
|
|
988
1033
|
|
|
@@ -1005,7 +1050,7 @@ test "Chance.attacking" {
|
|
|
1005
1050
|
chance.durations.p2.attacking = @intCast(i);
|
|
1006
1051
|
try chance.attacking(.P2, .ended);
|
|
1007
1052
|
try expectProbability(&chance.probability, 1, d);
|
|
1008
|
-
try expectValue(
|
|
1053
|
+
try expectValue(0, chance.durations.p2.attacking);
|
|
1009
1054
|
|
|
1010
1055
|
chance.reset();
|
|
1011
1056
|
}
|
|
@@ -1031,7 +1076,7 @@ test "Chance.binding" {
|
|
|
1031
1076
|
chance.durations.p1.binding = @intCast(i);
|
|
1032
1077
|
try chance.binding(.P1, .ended);
|
|
1033
1078
|
try expectProbability(&chance.probability, p, q);
|
|
1034
|
-
try expectValue(
|
|
1079
|
+
try expectValue(0, chance.durations.p1.binding);
|
|
1035
1080
|
|
|
1036
1081
|
chance.reset();
|
|
1037
1082
|
|
|
@@ -1051,7 +1096,7 @@ test "Chance.psywave" {
|
|
|
1051
1096
|
|
|
1052
1097
|
try chance.psywave(.P2, 100, 150);
|
|
1053
1098
|
try expectProbability(&chance.probability, 1, 150);
|
|
1054
|
-
try expectValue(
|
|
1099
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
1055
1100
|
}
|
|
1056
1101
|
|
|
1057
1102
|
test "Chance.metronome" {
|
|
@@ -1067,7 +1112,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
1067
1112
|
|
|
1068
1113
|
r.reduce();
|
|
1069
1114
|
if (r.p != p or r.q != q) {
|
|
1070
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
1115
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
1071
1116
|
return error.TestExpectedEqual;
|
|
1072
1117
|
}
|
|
1073
1118
|
}
|
|
@@ -1145,28 +1190,44 @@ const Null = struct {
|
|
|
1145
1190
|
self: Null,
|
|
1146
1191
|
comptime field: Action.Field,
|
|
1147
1192
|
player: Player,
|
|
1148
|
-
obs: Optional(
|
|
1193
|
+
obs: Optional(Observation(.confusion)),
|
|
1149
1194
|
) void {
|
|
1150
1195
|
_ = .{ self, field, player, obs };
|
|
1151
1196
|
}
|
|
1152
1197
|
|
|
1153
|
-
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1198
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
1154
1199
|
_ = .{ self, player, obs };
|
|
1155
1200
|
}
|
|
1156
1201
|
|
|
1157
|
-
pub fn confusion(
|
|
1202
|
+
pub fn confusion(
|
|
1203
|
+
self: Null,
|
|
1204
|
+
player: Player,
|
|
1205
|
+
obs: Optional(Observation(.confusion)),
|
|
1206
|
+
) Error!void {
|
|
1158
1207
|
_ = .{ self, player, obs };
|
|
1159
1208
|
}
|
|
1160
1209
|
|
|
1161
|
-
pub fn disable(
|
|
1210
|
+
pub fn disable(
|
|
1211
|
+
self: Null,
|
|
1212
|
+
player: Player,
|
|
1213
|
+
obs: Optional(Observation(.disable)),
|
|
1214
|
+
) Error!void {
|
|
1162
1215
|
_ = .{ self, player, obs };
|
|
1163
1216
|
}
|
|
1164
1217
|
|
|
1165
|
-
pub fn attacking(
|
|
1218
|
+
pub fn attacking(
|
|
1219
|
+
self: Null,
|
|
1220
|
+
player: Player,
|
|
1221
|
+
obs: Optional(Observation(.attacking)),
|
|
1222
|
+
) Error!void {
|
|
1166
1223
|
_ = .{ self, player, obs };
|
|
1167
1224
|
}
|
|
1168
1225
|
|
|
1169
|
-
pub fn binding(
|
|
1226
|
+
pub fn binding(
|
|
1227
|
+
self: Null,
|
|
1228
|
+
player: Player,
|
|
1229
|
+
obs: Optional(Observation(.binding)),
|
|
1230
|
+
) Error!void {
|
|
1170
1231
|
_ = .{ self, player, obs };
|
|
1171
1232
|
}
|
|
1172
1233
|
|
|
@@ -98,7 +98,7 @@ pub const Type = enum(u4) {
|
|
|
98
98
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/// The precedence order of Type `
|
|
101
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
102
102
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
103
103
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
104
104
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
@@ -108,7 +108,7 @@ pub const Type = enum(u4) {
|
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
/// Representation of a Pokémon's typing.
|
|
111
|
-
pub const Types = packed struct {
|
|
111
|
+
pub const Types = packed struct(u8) {
|
|
112
112
|
/// A Pokémon's primary type.
|
|
113
113
|
type1: Type = .Normal,
|
|
114
114
|
/// A Pokémon's secondary type (may be identical to its primary type).
|