@pkmn/engine 0.1.0-dev.b01cb48c → 0.1.0-dev.b3197aa3
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 +54 -6
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -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 +110 -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 +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +40 -90
- package/build/pkg/index.js +23 -70
- 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 +52 -76
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +331 -85
- package/package.json +56 -136
- package/src/bin/install-pkmn-engine +10 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/bindings/node.zig +117 -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 -6
- package/src/lib/common/napi.zig +4 -0
- 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 +317 -304
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +65 -1
- package/src/lib/gen1/calc.zig +266 -135
- package/src/lib/gen1/chance.zig +469 -110
- 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 +2 -3
- package/src/lib/gen1/data.zig +35 -38
- package/src/lib/gen1/helpers.zig +17 -27
- package/src/lib/gen1/mechanics.zig +213 -160
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +87 -67
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +7 -5
- package/src/lib/gen2/data.zig +28 -30
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +82 -82
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +14 -7
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +93 -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);
|
|
@@ -99,8 +123,19 @@ test Actions {
|
|
|
99
123
|
try expect(!c.matches(a));
|
|
100
124
|
}
|
|
101
125
|
|
|
126
|
+
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
102
127
|
pub const Observation = enum { started, continuing, ended };
|
|
103
|
-
|
|
128
|
+
/// An extension of Observation to account for "hidden" confusion.
|
|
129
|
+
pub const Confusion = enum {
|
|
130
|
+
started,
|
|
131
|
+
continuing,
|
|
132
|
+
ended,
|
|
133
|
+
/// An indicator that an existing confusion duration has been overwritten by a new confusion
|
|
134
|
+
/// duration due to a Thrashing move ending. This is not immediately observable to an opponent,
|
|
135
|
+
/// to compute probabilities from an opponent's point of view based solely on public information
|
|
136
|
+
/// extra work must be done to correct for the information leak
|
|
137
|
+
overwritten,
|
|
138
|
+
};
|
|
104
139
|
|
|
105
140
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
106
141
|
/// single player.
|
|
@@ -126,19 +161,25 @@ pub const Action = packed struct(u64) {
|
|
|
126
161
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
127
162
|
duration: u4 = 0,
|
|
128
163
|
|
|
129
|
-
|
|
164
|
+
/// TODO
|
|
130
165
|
sleep: Optional(Observation) = .None,
|
|
166
|
+
/// TODO
|
|
131
167
|
confusion: Optional(Confusion) = .None,
|
|
168
|
+
/// TODO
|
|
132
169
|
disable: Optional(Observation) = .None,
|
|
133
|
-
|
|
170
|
+
/// TODO
|
|
171
|
+
attacking: Optional(Observation) = .None,
|
|
172
|
+
/// TODO
|
|
134
173
|
binding: Optional(Observation) = .None,
|
|
135
|
-
thrashing: Optional(Observation) = .None,
|
|
136
174
|
|
|
137
|
-
_:
|
|
175
|
+
_: u1 = 0,
|
|
138
176
|
|
|
139
177
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
140
178
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
141
179
|
move_slot: u4 = 0,
|
|
180
|
+
|
|
181
|
+
/// TODO
|
|
182
|
+
pp: u4 = 0,
|
|
142
183
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
143
184
|
multi_hit: u4 = 0,
|
|
144
185
|
|
|
@@ -158,10 +199,10 @@ pub const Action = packed struct(u64) {
|
|
|
158
199
|
pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
|
|
159
200
|
try writer.writeByte('(');
|
|
160
201
|
var printed = false;
|
|
161
|
-
inline for (@typeInfo(Action)
|
|
202
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
162
203
|
const val = @field(self, field.name);
|
|
163
204
|
switch (@typeInfo(@TypeOf(val))) {
|
|
164
|
-
|
|
205
|
+
Enum => if (val != .None) {
|
|
165
206
|
if (printed) try writer.writeAll(", ");
|
|
166
207
|
if (shape) {
|
|
167
208
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -170,25 +211,11 @@ pub const Action = packed struct(u64) {
|
|
|
170
211
|
if (val == .false) "!" else "",
|
|
171
212
|
field.name,
|
|
172
213
|
});
|
|
173
|
-
} else if (@TypeOf(val) == Optional(Observation)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
.started => "+",
|
|
177
|
-
.continuing => "",
|
|
178
|
-
.ended => "-",
|
|
179
|
-
else => unreachable,
|
|
180
|
-
},
|
|
181
|
-
field.name,
|
|
182
|
-
});
|
|
183
|
-
} else if (@TypeOf(val) == Optional(Confusion)) {
|
|
214
|
+
} else if (@TypeOf(val) == Optional(Observation) or
|
|
215
|
+
@TypeOf(val) == Optional(Confusion))
|
|
216
|
+
{
|
|
184
217
|
try writer.print("{s}{s}", .{
|
|
185
|
-
|
|
186
|
-
.started => "+",
|
|
187
|
-
.continuing => "",
|
|
188
|
-
.ended => "-",
|
|
189
|
-
.overwritten => "#",
|
|
190
|
-
else => unreachable,
|
|
191
|
-
},
|
|
218
|
+
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
192
219
|
field.name,
|
|
193
220
|
});
|
|
194
221
|
} else {
|
|
@@ -196,7 +223,7 @@ pub const Action = packed struct(u64) {
|
|
|
196
223
|
}
|
|
197
224
|
printed = true;
|
|
198
225
|
},
|
|
199
|
-
|
|
226
|
+
Int => if (val != 0) {
|
|
200
227
|
if (printed) try writer.writeAll(", ");
|
|
201
228
|
if (shape) {
|
|
202
229
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -213,15 +240,19 @@ pub const Action = packed struct(u64) {
|
|
|
213
240
|
};
|
|
214
241
|
|
|
215
242
|
/// TODO
|
|
216
|
-
pub const Durations = struct {
|
|
243
|
+
pub const Durations = extern struct {
|
|
217
244
|
/// TODO
|
|
218
245
|
p1: Duration = .{},
|
|
219
246
|
/// TODO
|
|
220
247
|
p2: Duration = .{},
|
|
221
248
|
|
|
249
|
+
comptime {
|
|
250
|
+
assert(@sizeOf(Durations) == 8);
|
|
251
|
+
}
|
|
252
|
+
|
|
222
253
|
/// TODO
|
|
223
|
-
pub fn get(self: anytype, player: Player)
|
|
224
|
-
assert(
|
|
254
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Duration) {
|
|
255
|
+
assert(isPointerTo(self, Durations));
|
|
225
256
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
226
257
|
}
|
|
227
258
|
|
|
@@ -236,15 +267,18 @@ pub const Durations = struct {
|
|
|
236
267
|
}
|
|
237
268
|
};
|
|
238
269
|
|
|
270
|
+
pub const Sleeps = Array(6, u3);
|
|
271
|
+
|
|
239
272
|
/// TODO
|
|
240
|
-
pub const Duration = struct {
|
|
241
|
-
sleeps:
|
|
273
|
+
pub const Duration = packed struct(u32) {
|
|
274
|
+
sleeps: Sleeps.T = 0,
|
|
242
275
|
confusion: u3 = 0,
|
|
243
276
|
disable: u4 = 0,
|
|
244
|
-
|
|
245
|
-
thrashing: u3 = 0,
|
|
277
|
+
attacking: u3 = 0,
|
|
246
278
|
binding: u3 = 0,
|
|
247
279
|
|
|
280
|
+
_: u1 = 0,
|
|
281
|
+
|
|
248
282
|
pub fn format(
|
|
249
283
|
self: Duration,
|
|
250
284
|
comptime fmt: []const u8,
|
|
@@ -254,27 +288,22 @@ pub const Duration = struct {
|
|
|
254
288
|
_ = .{ fmt, opts };
|
|
255
289
|
try writer.writeByte('(');
|
|
256
290
|
var printed = false;
|
|
257
|
-
inline for (@typeInfo(Duration)
|
|
291
|
+
inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
|
|
258
292
|
const val = @field(self, field.name);
|
|
259
293
|
switch (@typeInfo(@TypeOf(val))) {
|
|
260
|
-
|
|
294
|
+
Int => if (val != 0) {
|
|
261
295
|
if (printed) try writer.writeAll(", ");
|
|
262
|
-
|
|
263
|
-
printed = true;
|
|
264
|
-
},
|
|
265
|
-
.Array => {
|
|
266
|
-
var total: usize = 0;
|
|
267
|
-
for (val) |v| total += v;
|
|
268
|
-
if (total > 0) {
|
|
269
|
-
if (printed) try writer.writeAll(", ");
|
|
296
|
+
if (comptime std.mem.eql(u8, field.name, "sleeps")) {
|
|
270
297
|
try writer.print("{s}:[", .{field.name});
|
|
271
|
-
for (
|
|
298
|
+
for (0..6) |i| {
|
|
272
299
|
if (i != 0) try writer.writeByte(',');
|
|
273
|
-
try writer.print("{d}", .{
|
|
300
|
+
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
274
301
|
}
|
|
275
302
|
try writer.writeAll("]");
|
|
276
|
-
|
|
303
|
+
} else {
|
|
304
|
+
try writer.print("{s}:{d}", .{ field.name, val });
|
|
277
305
|
}
|
|
306
|
+
printed = true;
|
|
278
307
|
},
|
|
279
308
|
else => unreachable,
|
|
280
309
|
}
|
|
@@ -283,7 +312,7 @@ pub const Duration = struct {
|
|
|
283
312
|
}
|
|
284
313
|
};
|
|
285
314
|
|
|
286
|
-
pub const Commit = enum { hit, miss, binding };
|
|
315
|
+
pub const Commit = enum { hit, miss, binding, err, glitch };
|
|
287
316
|
|
|
288
317
|
const uN = if (options.miss) u8 else u9;
|
|
289
318
|
|
|
@@ -323,6 +352,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
323
352
|
damage_roll: u8 = 0,
|
|
324
353
|
hit: bool = false,
|
|
325
354
|
hit_probablity: uN = 0,
|
|
355
|
+
glitch: bool = false,
|
|
326
356
|
} = .{},
|
|
327
357
|
|
|
328
358
|
/// Possible error returned by operations tracking chance probability.
|
|
@@ -342,10 +372,18 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
342
372
|
|
|
343
373
|
assert(!showdown);
|
|
344
374
|
|
|
375
|
+
if (kind == .glitch and !self.pending.glitch) return;
|
|
376
|
+
|
|
345
377
|
var action = self.actions.get(player);
|
|
346
378
|
|
|
379
|
+
if (kind == .err and self.pending.crit_probablity != 0) {
|
|
380
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
381
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
347
385
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
348
|
-
// target is immune
|
|
386
|
+
// target is immune*/behind a sub/etc)
|
|
349
387
|
if (self.pending.hit_probablity != 0) {
|
|
350
388
|
try self.probability.update(self.pending.hit_probablity, 256);
|
|
351
389
|
action.hit = if (self.pending.hit) .true else .false;
|
|
@@ -353,28 +391,61 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
353
391
|
|
|
354
392
|
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
355
393
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
356
|
-
// "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
|
|
357
395
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
358
|
-
// passed to the critical hit helper)
|
|
359
|
-
|
|
396
|
+
// passed to the critical hit helper).
|
|
397
|
+
//
|
|
398
|
+
// There is a special edge case with the division-by-zero freeze where we need to commit
|
|
399
|
+
// the status of critical hit roll the occured before the move missed to avoid missing
|
|
400
|
+
// out on the possibility that the alternative branch encountered an error
|
|
401
|
+
if (kind != .hit and !self.pending.glitch) {
|
|
402
|
+
return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
403
|
+
}
|
|
360
404
|
|
|
361
405
|
if (self.pending.crit_probablity != 0) {
|
|
362
406
|
try self.probability.update(self.pending.crit_probablity, 256);
|
|
363
407
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
364
|
-
}
|
|
408
|
+
} else assert(!self.pending.glitch);
|
|
365
409
|
|
|
366
|
-
if (self.pending.damage_roll > 0) {
|
|
410
|
+
if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
|
|
367
411
|
try self.probability.update(1, 39);
|
|
368
412
|
action.damage = self.pending.damage_roll;
|
|
369
413
|
}
|
|
370
414
|
}
|
|
371
415
|
|
|
416
|
+
pub fn glitch(self: *Self) void {
|
|
417
|
+
if (!enabled) return;
|
|
418
|
+
|
|
419
|
+
assert(!showdown);
|
|
420
|
+
self.pending.glitch = true;
|
|
421
|
+
}
|
|
422
|
+
|
|
372
423
|
pub fn clearPending(self: *Self) void {
|
|
373
424
|
if (!enabled) return;
|
|
374
425
|
|
|
426
|
+
assert(!showdown);
|
|
375
427
|
self.pending = .{};
|
|
376
428
|
}
|
|
377
429
|
|
|
430
|
+
pub fn switched(self: *Self, player: Player, slot: u8) void {
|
|
431
|
+
if (!enabled) return;
|
|
432
|
+
|
|
433
|
+
assert(slot >= 1 and slot <= 6);
|
|
434
|
+
|
|
435
|
+
var d = self.durations.get(player);
|
|
436
|
+
|
|
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);
|
|
440
|
+
|
|
441
|
+
d.confusion = 0;
|
|
442
|
+
d.disable = 0;
|
|
443
|
+
d.attacking = 0;
|
|
444
|
+
d.binding = 0;
|
|
445
|
+
|
|
446
|
+
self.durations.get(player.foe()).binding = 0;
|
|
447
|
+
}
|
|
448
|
+
|
|
378
449
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
379
450
|
if (!enabled) return;
|
|
380
451
|
|
|
@@ -474,36 +545,169 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
474
545
|
pub fn duration(self: *Self, player: Player, turns: u4) void {
|
|
475
546
|
if (!enabled) return;
|
|
476
547
|
|
|
477
|
-
self.actions.get(player).duration = if (options.key)
|
|
548
|
+
self.actions.get(player).duration = if (options.key) 2 else turns;
|
|
478
549
|
}
|
|
479
550
|
|
|
480
551
|
pub fn observe(
|
|
481
552
|
self: *Self,
|
|
482
553
|
comptime field: Action.Field,
|
|
483
554
|
player: Player,
|
|
484
|
-
|
|
555
|
+
obs: Optional(Confusion),
|
|
485
556
|
) void {
|
|
486
557
|
if (!enabled) return;
|
|
487
558
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
559
|
+
var a = self.actions.get(player);
|
|
560
|
+
const val = @field(a, @tagName(field));
|
|
561
|
+
switch (obs) {
|
|
562
|
+
.overwritten => {
|
|
563
|
+
assert(field == .confusion);
|
|
564
|
+
assert(val == .started or val == .continuing);
|
|
565
|
+
assert(a.duration > 0);
|
|
566
|
+
a.confusion = obs;
|
|
567
|
+
self.durations.get(player).confusion = 1;
|
|
568
|
+
},
|
|
569
|
+
.None => {
|
|
570
|
+
@field(a, @tagName(field)) = .None;
|
|
571
|
+
if (field == .sleep) {
|
|
572
|
+
var d = self.durations.get(player);
|
|
573
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
574
|
+
} else {
|
|
575
|
+
@field(self.durations.get(player), @tagName(field)) = 0;
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
.started => {
|
|
579
|
+
assert(val == .None or val == .ended);
|
|
580
|
+
assert(switch (field) {
|
|
581
|
+
.confusion => a.duration > 0 or self.actions.get(player.foe()).duration > 0,
|
|
582
|
+
.sleep, .disable => self.actions.get(player.foe()).duration > 0,
|
|
583
|
+
else => a.duration > 0,
|
|
584
|
+
});
|
|
585
|
+
@field(a, @tagName(field)) = .started;
|
|
586
|
+
if (field == .sleep) {
|
|
587
|
+
var d = self.durations.get(player);
|
|
588
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 1);
|
|
589
|
+
} else {
|
|
590
|
+
@field(self.durations.get(player), @tagName(field)) = 1;
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
else => unreachable,
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
598
|
+
if (!enabled) return;
|
|
599
|
+
|
|
600
|
+
var a = self.actions.get(player);
|
|
601
|
+
assert(a.sleep == .None or a.sleep == .started);
|
|
602
|
+
a.sleep = obs;
|
|
603
|
+
|
|
604
|
+
var d = self.durations.get(player);
|
|
605
|
+
const n = Sleeps.get(d.sleeps, 0);
|
|
606
|
+
|
|
607
|
+
if (obs == .None) {
|
|
608
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
609
|
+
} else if (obs == .ended) {
|
|
610
|
+
assert(n >= 1 and n <= 7);
|
|
611
|
+
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
612
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
494
613
|
} else {
|
|
495
|
-
assert(
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
614
|
+
assert(obs == .continuing);
|
|
615
|
+
assert(n >= 1 and n < 7);
|
|
616
|
+
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
617
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, n + 1);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
|
|
622
|
+
if (!enabled) return;
|
|
623
|
+
|
|
624
|
+
var a = self.actions.get(player);
|
|
625
|
+
assert(a.confusion == .None or a.confusion == .started);
|
|
626
|
+
a.confusion = obs;
|
|
627
|
+
|
|
628
|
+
var d = self.durations.get(player);
|
|
629
|
+
const n = d.confusion;
|
|
630
|
+
|
|
631
|
+
if (obs == .ended) {
|
|
632
|
+
assert(n >= 2 and n <= 5);
|
|
633
|
+
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
634
|
+
d.confusion = 0;
|
|
635
|
+
} else {
|
|
636
|
+
assert(obs == .continuing);
|
|
637
|
+
assert(n >= 1 and n < 5);
|
|
638
|
+
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
639
|
+
d.confusion += 1;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
644
|
+
if (!enabled) return;
|
|
645
|
+
|
|
646
|
+
var a = self.actions.get(player);
|
|
647
|
+
assert(a.disable == .None or a.disable == .started);
|
|
648
|
+
a.disable = obs;
|
|
649
|
+
|
|
650
|
+
var d = self.durations.get(player);
|
|
651
|
+
const n = d.disable;
|
|
652
|
+
|
|
653
|
+
if (obs == .ended) {
|
|
654
|
+
assert(n >= 1 and n <= 8);
|
|
655
|
+
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
656
|
+
d.disable = 0;
|
|
657
|
+
} else {
|
|
658
|
+
assert(obs == .continuing);
|
|
659
|
+
assert(n >= 1 and n < 8);
|
|
660
|
+
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
661
|
+
d.disable += 1;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
pub fn attacking(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
666
|
+
if (!enabled) return;
|
|
667
|
+
|
|
668
|
+
var a = self.actions.get(player);
|
|
669
|
+
assert(a.attacking == .None or a.attacking == .started);
|
|
670
|
+
a.attacking = obs;
|
|
671
|
+
|
|
672
|
+
var d = self.durations.get(player);
|
|
673
|
+
const n = d.attacking;
|
|
674
|
+
if (obs == .ended) {
|
|
675
|
+
// "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
676
|
+
assert(n >= 2 and n <= 3);
|
|
677
|
+
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
678
|
+
d.attacking = 0;
|
|
679
|
+
} else {
|
|
680
|
+
assert(obs == .continuing);
|
|
681
|
+
assert(n >= 1 and n < 3);
|
|
682
|
+
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
683
|
+
d.attacking += 1;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
688
|
+
if (!enabled) return;
|
|
689
|
+
|
|
690
|
+
var a = self.actions.get(player);
|
|
691
|
+
assert(a.binding == .None or a.binding == .started);
|
|
692
|
+
a.binding = obs;
|
|
693
|
+
|
|
694
|
+
var d = self.durations.get(player);
|
|
695
|
+
const n = d.binding;
|
|
696
|
+
|
|
697
|
+
assert(n > 0);
|
|
698
|
+
const p: u4 = if (n < 3) 3 else 1;
|
|
699
|
+
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
700
|
+
|
|
701
|
+
if (obs == .ended) {
|
|
702
|
+
// "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
703
|
+
assert(n >= 1 and n <= 4);
|
|
704
|
+
if (n != 4) try self.probability.update(p, q);
|
|
705
|
+
d.binding = 0;
|
|
706
|
+
} else {
|
|
707
|
+
assert(obs == .continuing);
|
|
708
|
+
assert(n >= 1 and n < 4);
|
|
709
|
+
try self.probability.update(q - p, q);
|
|
710
|
+
d.binding += 1;
|
|
507
711
|
}
|
|
508
712
|
}
|
|
509
713
|
|
|
@@ -520,6 +724,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
520
724
|
try self.probability.update(1, Move.METRONOME.len);
|
|
521
725
|
self.actions.get(player).metronome = move;
|
|
522
726
|
}
|
|
727
|
+
|
|
728
|
+
pub fn pp(self: *Self, player: Player, n: u4) bool {
|
|
729
|
+
if (!enabled) return true;
|
|
730
|
+
|
|
731
|
+
var a = self.actions.get(player);
|
|
732
|
+
if (n > 1 and a.pp == n) return false;
|
|
733
|
+
if (a.metronome != .None) a.pp += n;
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
523
736
|
};
|
|
524
737
|
}
|
|
525
738
|
|
|
@@ -620,16 +833,16 @@ test "Chance.damage" {
|
|
|
620
833
|
|
|
621
834
|
try chance.damage(.P1, 217);
|
|
622
835
|
if (showdown) {
|
|
623
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
836
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
624
837
|
try expectProbability(&chance.probability, 1, 39);
|
|
625
838
|
} else {
|
|
626
839
|
try expectProbability(&chance.probability, 1, 1);
|
|
627
|
-
try expectValue(0, chance.actions.p1.damage);
|
|
840
|
+
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
628
841
|
|
|
629
842
|
try chance.commit(.P1, .hit);
|
|
630
843
|
|
|
631
844
|
try expectProbability(&chance.probability, 1, 39);
|
|
632
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
845
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
633
846
|
}
|
|
634
847
|
}
|
|
635
848
|
|
|
@@ -681,13 +894,13 @@ test "Chance.moveSlot" {
|
|
|
681
894
|
|
|
682
895
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
683
896
|
try expectProbability(&chance.probability, 1, 2);
|
|
684
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
897
|
+
try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
|
|
685
898
|
|
|
686
899
|
chance.reset();
|
|
687
900
|
|
|
688
901
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
689
902
|
try expectProbability(&chance.probability, 1, 3);
|
|
690
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
903
|
+
try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
|
|
691
904
|
}
|
|
692
905
|
|
|
693
906
|
test "Chance.multiHit" {
|
|
@@ -695,25 +908,142 @@ test "Chance.multiHit" {
|
|
|
695
908
|
|
|
696
909
|
try chance.multiHit(.P1, 3);
|
|
697
910
|
try expectProbability(&chance.probability, 3, 8);
|
|
698
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
911
|
+
try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
|
|
699
912
|
|
|
700
913
|
chance.reset();
|
|
701
914
|
|
|
702
915
|
try chance.multiHit(.P2, 5);
|
|
703
916
|
try expectProbability(&chance.probability, 1, 8);
|
|
704
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
917
|
+
try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
|
|
705
918
|
}
|
|
706
919
|
|
|
707
920
|
test "Chance.duration" {
|
|
708
921
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
709
922
|
|
|
710
923
|
chance.duration(.P1, 2);
|
|
711
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
924
|
+
try expectValue(@as(u4, 2), chance.actions.p1.duration);
|
|
712
925
|
|
|
713
926
|
chance.reset();
|
|
714
927
|
|
|
715
928
|
chance.duration(.P2, 4);
|
|
716
|
-
try expectValue(4, chance.actions.p2.duration);
|
|
929
|
+
try expectValue(@as(u4, 4), chance.actions.p2.duration);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
test "Chance.sleep" {
|
|
933
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
934
|
+
|
|
935
|
+
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
936
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
937
|
+
try chance.sleep(.P1, .ended);
|
|
938
|
+
try expectProbability(&chance.probability, 1, d);
|
|
939
|
+
try expectValue(@as(u3, 0), Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
940
|
+
|
|
941
|
+
chance.reset();
|
|
942
|
+
|
|
943
|
+
if (i < 7) {
|
|
944
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
945
|
+
try chance.sleep(.P1, .continuing);
|
|
946
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
947
|
+
try expectValue(@as(u3, @intCast(i)) + 1, Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
948
|
+
|
|
949
|
+
chance.reset();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
test "Chance.confusion" {
|
|
955
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
956
|
+
|
|
957
|
+
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
958
|
+
if (i > 1) {
|
|
959
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
960
|
+
try chance.confusion(.P2, .ended);
|
|
961
|
+
try expectProbability(&chance.probability, 1, d);
|
|
962
|
+
try expectValue(@as(u3, 0), chance.durations.p2.confusion);
|
|
963
|
+
|
|
964
|
+
chance.reset();
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
if (i < 5) {
|
|
968
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
969
|
+
try chance.confusion(.P2, .continuing);
|
|
970
|
+
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
971
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.confusion);
|
|
972
|
+
|
|
973
|
+
chance.reset();
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
test "Chance.disable" {
|
|
979
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
980
|
+
|
|
981
|
+
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
982
|
+
chance.durations.p1.disable = @intCast(i);
|
|
983
|
+
try chance.disable(.P1, .ended);
|
|
984
|
+
try expectProbability(&chance.probability, 1, d);
|
|
985
|
+
try expectValue(@as(u4, 0), chance.durations.p1.disable);
|
|
986
|
+
|
|
987
|
+
chance.reset();
|
|
988
|
+
|
|
989
|
+
if (i < 8) {
|
|
990
|
+
chance.durations.p1.disable = @intCast(i);
|
|
991
|
+
try chance.disable(.P1, .continuing);
|
|
992
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
993
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.durations.p1.disable);
|
|
994
|
+
|
|
995
|
+
chance.reset();
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
test "Chance.attacking" {
|
|
1001
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1002
|
+
|
|
1003
|
+
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
1004
|
+
if (i > 1) {
|
|
1005
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
1006
|
+
try chance.attacking(.P2, .ended);
|
|
1007
|
+
try expectProbability(&chance.probability, 1, d);
|
|
1008
|
+
try expectValue(@as(u3, 0), chance.durations.p2.attacking);
|
|
1009
|
+
|
|
1010
|
+
chance.reset();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (i < 3) {
|
|
1014
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
1015
|
+
try chance.attacking(.P2, .continuing);
|
|
1016
|
+
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1017
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.attacking);
|
|
1018
|
+
|
|
1019
|
+
chance.reset();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
test "Chance.binding" {
|
|
1025
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1026
|
+
|
|
1027
|
+
const ps = [_]u8{ 3, 3, 1, 1 };
|
|
1028
|
+
const qs = [_]u8{ 8, 5, 2, 1 };
|
|
1029
|
+
|
|
1030
|
+
for (ps, qs, 1..5) |p, q, i| {
|
|
1031
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1032
|
+
try chance.binding(.P1, .ended);
|
|
1033
|
+
try expectProbability(&chance.probability, p, q);
|
|
1034
|
+
try expectValue(@as(u3, 0), chance.durations.p1.binding);
|
|
1035
|
+
|
|
1036
|
+
chance.reset();
|
|
1037
|
+
|
|
1038
|
+
if (i < 4) {
|
|
1039
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1040
|
+
try chance.binding(.P1, .continuing);
|
|
1041
|
+
try expectProbability(&chance.probability, q - p, q);
|
|
1042
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.binding);
|
|
1043
|
+
|
|
1044
|
+
chance.reset();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
717
1047
|
}
|
|
718
1048
|
|
|
719
1049
|
test "Chance.psywave" {
|
|
@@ -721,7 +1051,7 @@ test "Chance.psywave" {
|
|
|
721
1051
|
|
|
722
1052
|
try chance.psywave(.P2, 100, 150);
|
|
723
1053
|
try expectProbability(&chance.probability, 1, 150);
|
|
724
|
-
try expectValue(101, chance.actions.p2.psywave);
|
|
1054
|
+
try expectValue(@as(u8, 101), chance.actions.p2.psywave);
|
|
725
1055
|
}
|
|
726
1056
|
|
|
727
1057
|
test "Chance.metronome" {
|
|
@@ -759,12 +1089,16 @@ const Null = struct {
|
|
|
759
1089
|
_ = .{ self, player, kind };
|
|
760
1090
|
}
|
|
761
1091
|
|
|
1092
|
+
pub fn glitch(self: Null) void {
|
|
1093
|
+
_ = .{self};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
762
1096
|
pub fn clearPending(self: Null) void {
|
|
763
1097
|
_ = .{self};
|
|
764
1098
|
}
|
|
765
1099
|
|
|
766
|
-
pub fn switched(self: Null, player: Player,
|
|
767
|
-
_ = .{ self, player,
|
|
1100
|
+
pub fn switched(self: Null, player: Player, slot: u8) void {
|
|
1101
|
+
_ = .{ self, player, slot };
|
|
768
1102
|
}
|
|
769
1103
|
|
|
770
1104
|
pub fn speedTie(self: Null, p1: bool) Error!void {
|
|
@@ -803,17 +1137,37 @@ const Null = struct {
|
|
|
803
1137
|
_ = .{ self, player, n };
|
|
804
1138
|
}
|
|
805
1139
|
|
|
1140
|
+
pub fn duration(self: Null, player: Player, turns: u4) void {
|
|
1141
|
+
_ = .{ self, player, turns };
|
|
1142
|
+
}
|
|
1143
|
+
|
|
806
1144
|
pub fn observe(
|
|
807
1145
|
self: Null,
|
|
808
1146
|
comptime field: Action.Field,
|
|
809
1147
|
player: Player,
|
|
810
|
-
|
|
1148
|
+
obs: Optional(Confusion),
|
|
811
1149
|
) void {
|
|
812
|
-
_ = .{ self, field, player,
|
|
1150
|
+
_ = .{ self, field, player, obs };
|
|
813
1151
|
}
|
|
814
1152
|
|
|
815
|
-
pub fn
|
|
816
|
-
_ = .{ self, player,
|
|
1153
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1154
|
+
_ = .{ self, player, obs };
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
|
|
1158
|
+
_ = .{ self, player, obs };
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1162
|
+
_ = .{ self, player, obs };
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
pub fn attacking(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1166
|
+
_ = .{ self, player, obs };
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1170
|
+
_ = .{ self, player, obs };
|
|
817
1171
|
}
|
|
818
1172
|
|
|
819
1173
|
pub fn psywave(self: Null, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -823,4 +1177,9 @@ const Null = struct {
|
|
|
823
1177
|
pub fn metronome(self: Null, player: Player, move: Move) Error!void {
|
|
824
1178
|
_ = .{ self, player, move };
|
|
825
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
pub fn pp(self: Null, player: Player, n: u4) bool {
|
|
1182
|
+
_ = .{ self, player, n };
|
|
1183
|
+
return true;
|
|
1184
|
+
}
|
|
826
1185
|
};
|