@pkmn/engine 0.1.0-dev.a0167a9d → 0.1.0-dev.a0447192
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 +4 -2
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -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 +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +29 -51
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +22 -9
- package/build/pkg/index.js +15 -10
- 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 -68
- 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 +132 -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 +199 -66
- package/package.json +46 -29
- package/src/bin/install-pkmn-engine +7 -5
- package/src/bin/pkmn-debug +1 -1
- 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 +2 -0
- package/src/lib/common/protocol.zig +41 -36
- package/src/lib/common/rational.zig +53 -51
- package/src/lib/common/rng.zig +4 -4
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +508 -127
- package/src/lib/gen1/chance.zig +316 -173
- package/src/lib/gen1/data.zig +28 -27
- package/src/lib/gen1/helpers.zig +15 -450
- package/src/lib/gen1/mechanics.zig +216 -145
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +90 -592
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +4 -6
- package/src/lib/gen2/mechanics.zig +47 -77
- package/src/lib/pkmn.zig +7 -5
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +359 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -11,6 +11,7 @@ const options = @import("../common/options.zig");
|
|
|
11
11
|
const rational = @import("../common/rational.zig");
|
|
12
12
|
const util = @import("../common/util.zig");
|
|
13
13
|
|
|
14
|
+
const Array = @import("../common/array.zig").Array;
|
|
14
15
|
const Player = @import("../common/data.zig").Player;
|
|
15
16
|
const Optional = @import("../common/optional.zig").Optional;
|
|
16
17
|
|
|
@@ -19,9 +20,16 @@ const data = @import("data.zig");
|
|
|
19
20
|
const enabled = options.chance;
|
|
20
21
|
const showdown = options.showdown;
|
|
21
22
|
|
|
23
|
+
const PointerType = util.PointerType;
|
|
24
|
+
const isPointerTo = util.isPointerTo;
|
|
25
|
+
|
|
22
26
|
const Move = data.Move;
|
|
23
27
|
const MoveSlot = data.MoveSlot;
|
|
24
28
|
|
|
29
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
30
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
31
|
+
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
32
|
+
|
|
25
33
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
26
34
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
27
35
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -37,8 +45,8 @@ pub const Actions = extern struct {
|
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
/// Returns the `Action` for the given `player`.
|
|
40
|
-
pub fn get(self: anytype, player: Player)
|
|
41
|
-
assert(
|
|
48
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
|
|
49
|
+
assert(isPointerTo(self, Actions));
|
|
42
50
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -51,15 +59,15 @@ pub const Actions = extern struct {
|
|
|
51
59
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
52
60
|
/// set to the same value).
|
|
53
61
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
54
|
-
inline for (@typeInfo(Actions)
|
|
55
|
-
inline for (@typeInfo(Action)
|
|
62
|
+
inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
|
|
63
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
56
64
|
const a_val = @field(@field(a, player.name), field.name);
|
|
57
65
|
const b_val = @field(@field(b, player.name), field.name);
|
|
58
66
|
|
|
59
67
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
60
|
-
|
|
68
|
+
Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
61
69
|
return false,
|
|
62
|
-
|
|
70
|
+
Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
63
71
|
else => unreachable,
|
|
64
72
|
}
|
|
65
73
|
}
|
|
@@ -99,6 +107,20 @@ test Actions {
|
|
|
99
107
|
try expect(!c.matches(a));
|
|
100
108
|
}
|
|
101
109
|
|
|
110
|
+
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
111
|
+
pub const Observation = enum { started, continuing, ended };
|
|
112
|
+
/// An extension of Observation to account for "hidden" confusion.
|
|
113
|
+
pub const Confusion = enum {
|
|
114
|
+
started,
|
|
115
|
+
continuing,
|
|
116
|
+
ended,
|
|
117
|
+
/// An indicator that an existing confusion duration has been overwritten by a new confusion
|
|
118
|
+
/// duration due to a Thrashing move ending. This is not immediately observable to an opponent,
|
|
119
|
+
/// to compute probabilities from an opponent's point of view based solely on public information
|
|
120
|
+
/// extra work must be done to correct for the information leak
|
|
121
|
+
overwritten,
|
|
122
|
+
};
|
|
123
|
+
|
|
102
124
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
103
125
|
/// single player.
|
|
104
126
|
pub const Action = packed struct(u64) {
|
|
@@ -123,11 +145,25 @@ pub const Action = packed struct(u64) {
|
|
|
123
145
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
124
146
|
duration: u4 = 0,
|
|
125
147
|
|
|
126
|
-
|
|
148
|
+
/// TODO
|
|
149
|
+
sleep: Optional(Observation) = .None,
|
|
150
|
+
/// TODO
|
|
151
|
+
confusion: Optional(Confusion) = .None,
|
|
152
|
+
/// TODO
|
|
153
|
+
disable: Optional(Observation) = .None,
|
|
154
|
+
/// TODO
|
|
155
|
+
attacking: Optional(Observation) = .None,
|
|
156
|
+
/// TODO
|
|
157
|
+
binding: Optional(Observation) = .None,
|
|
158
|
+
|
|
159
|
+
_: u1 = 0,
|
|
127
160
|
|
|
128
161
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
129
162
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
130
163
|
move_slot: u4 = 0,
|
|
164
|
+
|
|
165
|
+
/// TODO
|
|
166
|
+
pp: u4 = 0,
|
|
131
167
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
132
168
|
multi_hit: u4 = 0,
|
|
133
169
|
|
|
@@ -147,10 +183,10 @@ pub const Action = packed struct(u64) {
|
|
|
147
183
|
pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
|
|
148
184
|
try writer.writeByte('(');
|
|
149
185
|
var printed = false;
|
|
150
|
-
inline for (@typeInfo(Action)
|
|
186
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
151
187
|
const val = @field(self, field.name);
|
|
152
188
|
switch (@typeInfo(@TypeOf(val))) {
|
|
153
|
-
|
|
189
|
+
Enum => if (val != .None) {
|
|
154
190
|
if (printed) try writer.writeAll(", ");
|
|
155
191
|
if (shape) {
|
|
156
192
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -159,12 +195,19 @@ pub const Action = packed struct(u64) {
|
|
|
159
195
|
if (val == .false) "!" else "",
|
|
160
196
|
field.name,
|
|
161
197
|
});
|
|
198
|
+
} else if (@TypeOf(val) == Optional(Observation) or
|
|
199
|
+
@TypeOf(val) == Optional(Confusion))
|
|
200
|
+
{
|
|
201
|
+
try writer.print("{s}{s}", .{
|
|
202
|
+
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
203
|
+
field.name,
|
|
204
|
+
});
|
|
162
205
|
} else {
|
|
163
206
|
try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
|
|
164
207
|
}
|
|
165
208
|
printed = true;
|
|
166
209
|
},
|
|
167
|
-
|
|
210
|
+
Int => if (val != 0) {
|
|
168
211
|
if (printed) try writer.writeAll(", ");
|
|
169
212
|
if (shape) {
|
|
170
213
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -182,40 +225,78 @@ pub const Action = packed struct(u64) {
|
|
|
182
225
|
|
|
183
226
|
/// TODO
|
|
184
227
|
pub const Durations = extern struct {
|
|
185
|
-
///
|
|
228
|
+
/// TODO
|
|
186
229
|
p1: Duration = .{},
|
|
187
|
-
///
|
|
230
|
+
/// TODO
|
|
188
231
|
p2: Duration = .{},
|
|
189
232
|
|
|
190
233
|
comptime {
|
|
191
|
-
assert(@sizeOf(Durations) ==
|
|
234
|
+
assert(@sizeOf(Durations) == 8);
|
|
192
235
|
}
|
|
193
236
|
|
|
194
|
-
///
|
|
195
|
-
pub fn get(self: anytype, player: Player)
|
|
196
|
-
assert(
|
|
237
|
+
/// TODO
|
|
238
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Duration) {
|
|
239
|
+
assert(isPointerTo(self, Durations));
|
|
197
240
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
198
241
|
}
|
|
242
|
+
|
|
243
|
+
pub fn format(
|
|
244
|
+
self: Durations,
|
|
245
|
+
comptime fmt: []const u8,
|
|
246
|
+
opts: std.fmt.FormatOptions,
|
|
247
|
+
writer: anytype,
|
|
248
|
+
) !void {
|
|
249
|
+
_ = .{ fmt, opts };
|
|
250
|
+
try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
|
|
251
|
+
}
|
|
199
252
|
};
|
|
200
253
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
/// The number of turns a Pokémon has been observed to be confused.
|
|
254
|
+
pub const Sleeps = Array(6, u3);
|
|
255
|
+
|
|
256
|
+
/// TODO
|
|
257
|
+
pub const Duration = packed struct(u32) {
|
|
258
|
+
sleeps: Sleeps.T = 0,
|
|
207
259
|
confusion: u3 = 0,
|
|
208
|
-
/// The number of turns a Pokémon has been observed to be disabled.
|
|
209
260
|
disable: u4 = 0,
|
|
210
|
-
/// The number of turns a Pokémon has been observed to be attacking.
|
|
211
261
|
attacking: u3 = 0,
|
|
212
|
-
/// The number of turns a Pokémon has been observed to be binding their opponent.
|
|
213
262
|
binding: u3 = 0,
|
|
214
263
|
|
|
215
|
-
|
|
264
|
+
_: u1 = 0,
|
|
265
|
+
|
|
266
|
+
pub fn format(
|
|
267
|
+
self: Duration,
|
|
268
|
+
comptime fmt: []const u8,
|
|
269
|
+
opts: std.fmt.FormatOptions,
|
|
270
|
+
writer: anytype,
|
|
271
|
+
) !void {
|
|
272
|
+
_ = .{ fmt, opts };
|
|
273
|
+
try writer.writeByte('(');
|
|
274
|
+
var printed = false;
|
|
275
|
+
inline for (@field(@typeInfo(Duration), @tagName(Struct)).fields) |field| {
|
|
276
|
+
const val = @field(self, field.name);
|
|
277
|
+
switch (@typeInfo(@TypeOf(val))) {
|
|
278
|
+
Int => if (val != 0) {
|
|
279
|
+
if (printed) try writer.writeAll(", ");
|
|
280
|
+
if (comptime std.mem.eql(u8, field.name, "sleeps")) {
|
|
281
|
+
try writer.print("{s}:[", .{field.name});
|
|
282
|
+
for (0..6) |i| {
|
|
283
|
+
if (i != 0) try writer.writeByte(',');
|
|
284
|
+
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
285
|
+
}
|
|
286
|
+
try writer.writeAll("]");
|
|
287
|
+
} else {
|
|
288
|
+
try writer.print("{s}:{d}", .{ field.name, val });
|
|
289
|
+
}
|
|
290
|
+
printed = true;
|
|
291
|
+
},
|
|
292
|
+
else => unreachable,
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
try writer.writeByte(')');
|
|
296
|
+
}
|
|
216
297
|
};
|
|
217
298
|
|
|
218
|
-
pub const Commit = enum { hit, miss, binding };
|
|
299
|
+
pub const Commit = enum { hit, miss, binding, err, glitch };
|
|
219
300
|
|
|
220
301
|
const uN = if (options.miss) u8 else u9;
|
|
221
302
|
|
|
@@ -233,14 +314,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
233
314
|
/// TODO
|
|
234
315
|
durations: Durations = .{},
|
|
235
316
|
|
|
236
|
-
// Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
|
|
237
|
-
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
238
|
-
// switching and so we must store it here. The indices of these arrays correspond to the
|
|
239
|
-
// `order` field of a Side. This information could be stored in actions but size is a
|
|
240
|
-
// concern so this is tracked separately as actions only purports to track RNG events
|
|
241
|
-
// observed during a single `update` (not across updates).
|
|
242
|
-
sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
|
|
243
|
-
|
|
244
317
|
// In many cases on both the cartridge and on Pokémon Showdown rolls are made even though
|
|
245
318
|
// later checks render their result irrelevant (missing when the target is actually immune,
|
|
246
319
|
// rolling for damage when the move is later revealed to have missed, etc). We can't reorder
|
|
@@ -263,7 +336,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
263
336
|
damage_roll: u8 = 0,
|
|
264
337
|
hit: bool = false,
|
|
265
338
|
hit_probablity: uN = 0,
|
|
266
|
-
|
|
339
|
+
glitch: bool = false,
|
|
267
340
|
} = .{},
|
|
268
341
|
|
|
269
342
|
/// Possible error returned by operations tracking chance probability.
|
|
@@ -283,22 +356,18 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
283
356
|
|
|
284
357
|
assert(!showdown);
|
|
285
358
|
|
|
286
|
-
|
|
359
|
+
if (kind == .glitch and !self.pending.glitch) return;
|
|
287
360
|
|
|
288
|
-
|
|
289
|
-
// into a Pokémon that is immune
|
|
290
|
-
if (kind != .miss and self.pending.binding != 0) {
|
|
291
|
-
// assert(action.duration == 0); FIXME
|
|
292
|
-
action.duration = @intCast(self.pending.binding);
|
|
361
|
+
var action = self.actions.get(player);
|
|
293
362
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
363
|
+
if (kind == .err and self.pending.crit_probablity != 0) {
|
|
364
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
365
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
366
|
+
return;
|
|
297
367
|
}
|
|
298
|
-
if (kind == .binding) return;
|
|
299
368
|
|
|
300
369
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
301
|
-
// target is immune
|
|
370
|
+
// target is immune*/behind a sub/etc)
|
|
302
371
|
if (self.pending.hit_probablity != 0) {
|
|
303
372
|
try self.probability.update(self.pending.hit_probablity, 256);
|
|
304
373
|
action.hit = if (self.pending.hit) .true else .false;
|
|
@@ -306,55 +375,57 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
306
375
|
|
|
307
376
|
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
308
377
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
309
|
-
// "critical hit" was spurious or not as moves that do 1 or less damage don't
|
|
378
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
|
|
310
379
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
311
|
-
// passed to the critical hit helper)
|
|
312
|
-
|
|
380
|
+
// passed to the critical hit helper).
|
|
381
|
+
//
|
|
382
|
+
// There is a special edge case with the division-by-zero freeze where we need to commit
|
|
383
|
+
// the status of critical hit roll the occured before the move missed to avoid missing
|
|
384
|
+
// out on the possibility that the alternative branch encountered an error
|
|
385
|
+
if (kind != .hit and !self.pending.glitch) {
|
|
386
|
+
return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
387
|
+
}
|
|
313
388
|
|
|
314
389
|
if (self.pending.crit_probablity != 0) {
|
|
315
390
|
try self.probability.update(self.pending.crit_probablity, 256);
|
|
316
391
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
317
|
-
}
|
|
392
|
+
} else assert(!self.pending.glitch);
|
|
318
393
|
|
|
319
|
-
if (self.pending.damage_roll > 0) {
|
|
394
|
+
if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
|
|
320
395
|
try self.probability.update(1, 39);
|
|
321
396
|
action.damage = self.pending.damage_roll;
|
|
322
397
|
}
|
|
323
398
|
}
|
|
324
399
|
|
|
325
|
-
pub fn
|
|
400
|
+
pub fn glitch(self: *Self) void {
|
|
326
401
|
if (!enabled) return;
|
|
327
402
|
|
|
328
|
-
|
|
403
|
+
assert(!showdown);
|
|
404
|
+
self.pending.glitch = true;
|
|
329
405
|
}
|
|
330
406
|
|
|
331
|
-
pub fn
|
|
407
|
+
pub fn clearPending(self: *Self) void {
|
|
332
408
|
if (!enabled) return;
|
|
333
409
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (status) durations.sleep = 0;
|
|
337
|
-
durations.confusion = 0;
|
|
338
|
-
durations.disable = 0;
|
|
339
|
-
} else {
|
|
340
|
-
durations.attacking = 0;
|
|
341
|
-
durations.binding = 0;
|
|
342
|
-
}
|
|
410
|
+
assert(!showdown);
|
|
411
|
+
self.pending = .{};
|
|
343
412
|
}
|
|
344
413
|
|
|
345
|
-
pub fn switched(self: *Self, player: Player,
|
|
414
|
+
pub fn switched(self: *Self, player: Player, slot: u8) void {
|
|
346
415
|
if (!enabled) return;
|
|
347
416
|
|
|
348
|
-
assert(
|
|
349
|
-
assert(out >= 1 and out <= 6);
|
|
417
|
+
assert(slot >= 1 and slot <= 6);
|
|
350
418
|
|
|
351
|
-
var
|
|
419
|
+
var d = self.durations.get(player);
|
|
352
420
|
|
|
353
|
-
const
|
|
354
|
-
|
|
421
|
+
const out = Sleeps.get(d.sleeps, 0);
|
|
422
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, Sleeps.get(d.sleeps, slot - 1));
|
|
423
|
+
d.sleeps = Sleeps.set(d.sleeps, slot - 1, out);
|
|
355
424
|
|
|
356
|
-
|
|
357
|
-
|
|
425
|
+
d.confusion = 0;
|
|
426
|
+
d.disable = 0;
|
|
427
|
+
d.attacking = 0;
|
|
428
|
+
d.binding = 0;
|
|
358
429
|
|
|
359
430
|
self.durations.get(player.foe()).binding = 0;
|
|
360
431
|
}
|
|
@@ -455,109 +526,172 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
455
526
|
self.actions.get(player).multi_hit = n;
|
|
456
527
|
}
|
|
457
528
|
|
|
458
|
-
pub fn duration(
|
|
529
|
+
pub fn duration(self: *Self, player: Player, turns: u4) void {
|
|
530
|
+
if (!enabled) return;
|
|
531
|
+
|
|
532
|
+
self.actions.get(player).duration = if (options.key) 2 else turns;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
pub fn observe(
|
|
459
536
|
self: *Self,
|
|
460
|
-
comptime field:
|
|
537
|
+
comptime field: Action.Field,
|
|
461
538
|
player: Player,
|
|
462
|
-
|
|
463
|
-
turns: u4,
|
|
539
|
+
obs: Optional(Confusion),
|
|
464
540
|
) void {
|
|
465
541
|
if (!enabled) return;
|
|
466
542
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
(
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
543
|
+
var a = self.actions.get(player);
|
|
544
|
+
const val = @field(a, @tagName(field));
|
|
545
|
+
switch (obs) {
|
|
546
|
+
.overwritten => {
|
|
547
|
+
assert(field == .confusion);
|
|
548
|
+
assert(val == .started or val == .continuing);
|
|
549
|
+
assert(a.duration > 0);
|
|
550
|
+
a.confusion = obs;
|
|
551
|
+
self.durations.get(player).confusion = 1;
|
|
552
|
+
},
|
|
553
|
+
.None => {
|
|
554
|
+
@field(a, @tagName(field)) = .None;
|
|
555
|
+
if (field == .sleep) {
|
|
556
|
+
var d = self.durations.get(player);
|
|
557
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
558
|
+
} else {
|
|
559
|
+
@field(self.durations.get(player), @tagName(field)) = 0;
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
.started => {
|
|
563
|
+
assert(val == .None or val == .ended);
|
|
564
|
+
assert(switch (field) {
|
|
565
|
+
.confusion => a.duration > 0 or self.actions.get(player.foe()).duration > 0,
|
|
566
|
+
.sleep, .disable => self.actions.get(player.foe()).duration > 0,
|
|
567
|
+
else => a.duration > 0,
|
|
568
|
+
});
|
|
569
|
+
@field(a, @tagName(field)) = .started;
|
|
570
|
+
if (field == .sleep) {
|
|
571
|
+
var d = self.durations.get(player);
|
|
572
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 1);
|
|
573
|
+
} else {
|
|
574
|
+
@field(self.durations.get(player), @tagName(field)) = 1;
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
else => unreachable,
|
|
476
578
|
}
|
|
477
579
|
}
|
|
478
580
|
|
|
479
|
-
pub fn sleep(self: *Self, player: Player,
|
|
581
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
480
582
|
if (!enabled) return;
|
|
481
583
|
|
|
482
|
-
var
|
|
483
|
-
|
|
484
|
-
|
|
584
|
+
var a = self.actions.get(player);
|
|
585
|
+
assert(a.sleep == .None or a.sleep == .started);
|
|
586
|
+
a.sleep = obs;
|
|
587
|
+
|
|
588
|
+
var d = self.durations.get(player);
|
|
589
|
+
const n = Sleeps.get(d.sleeps, 0);
|
|
590
|
+
|
|
591
|
+
if (obs == .None) {
|
|
592
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
593
|
+
} else if (obs == .ended) {
|
|
485
594
|
assert(n >= 1 and n <= 7);
|
|
486
595
|
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
487
|
-
|
|
596
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
488
597
|
} else {
|
|
598
|
+
assert(obs == .continuing);
|
|
489
599
|
assert(n >= 1 and n < 7);
|
|
490
600
|
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
491
|
-
|
|
601
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, n + 1);
|
|
492
602
|
}
|
|
493
603
|
}
|
|
494
604
|
|
|
495
|
-
pub fn confusion(self: *Self, player: Player,
|
|
605
|
+
pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
|
|
496
606
|
if (!enabled) return;
|
|
497
607
|
|
|
498
|
-
var
|
|
499
|
-
|
|
500
|
-
|
|
608
|
+
var a = self.actions.get(player);
|
|
609
|
+
assert(a.confusion == .None or a.confusion == .started);
|
|
610
|
+
a.confusion = obs;
|
|
611
|
+
|
|
612
|
+
var d = self.durations.get(player);
|
|
613
|
+
const n = d.confusion;
|
|
614
|
+
|
|
615
|
+
if (obs == .ended) {
|
|
501
616
|
assert(n >= 2 and n <= 5);
|
|
502
617
|
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
503
|
-
|
|
618
|
+
d.confusion = 0;
|
|
504
619
|
} else {
|
|
620
|
+
assert(obs == .continuing);
|
|
505
621
|
assert(n >= 1 and n < 5);
|
|
506
622
|
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
507
|
-
|
|
623
|
+
d.confusion += 1;
|
|
508
624
|
}
|
|
509
625
|
}
|
|
510
626
|
|
|
511
|
-
pub fn disable(self: *Self, player: Player,
|
|
627
|
+
pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
512
628
|
if (!enabled) return;
|
|
513
629
|
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
|
|
630
|
+
var a = self.actions.get(player);
|
|
631
|
+
assert(a.disable == .None or a.disable == .started);
|
|
632
|
+
a.disable = obs;
|
|
633
|
+
|
|
634
|
+
var d = self.durations.get(player);
|
|
635
|
+
const n = d.disable;
|
|
636
|
+
|
|
637
|
+
if (obs == .ended) {
|
|
517
638
|
assert(n >= 1 and n <= 8);
|
|
518
639
|
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
519
|
-
|
|
640
|
+
d.disable = 0;
|
|
520
641
|
} else {
|
|
642
|
+
assert(obs == .continuing);
|
|
521
643
|
assert(n >= 1 and n < 8);
|
|
522
644
|
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
523
|
-
|
|
645
|
+
d.disable += 1;
|
|
524
646
|
}
|
|
525
647
|
}
|
|
526
648
|
|
|
527
|
-
pub fn attacking(self: *Self, player: Player,
|
|
649
|
+
pub fn attacking(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
528
650
|
if (!enabled) return;
|
|
529
651
|
|
|
530
|
-
var
|
|
531
|
-
|
|
532
|
-
|
|
652
|
+
var a = self.actions.get(player);
|
|
653
|
+
assert(a.attacking == .None or a.attacking == .started);
|
|
654
|
+
a.attacking = obs;
|
|
655
|
+
|
|
656
|
+
var d = self.durations.get(player);
|
|
657
|
+
const n = d.attacking;
|
|
658
|
+
if (obs == .ended) {
|
|
659
|
+
// "3-4 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
533
660
|
assert(n >= 2 and n <= 3);
|
|
534
661
|
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
535
|
-
|
|
662
|
+
d.attacking = 0;
|
|
536
663
|
} else {
|
|
664
|
+
assert(obs == .continuing);
|
|
537
665
|
assert(n >= 1 and n < 3);
|
|
538
666
|
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
539
|
-
|
|
667
|
+
d.attacking += 1;
|
|
540
668
|
}
|
|
541
669
|
}
|
|
542
670
|
|
|
543
|
-
pub fn binding(self: *Self, player: Player,
|
|
671
|
+
pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
544
672
|
if (!enabled) return;
|
|
545
673
|
|
|
546
|
-
var
|
|
547
|
-
|
|
674
|
+
var a = self.actions.get(player);
|
|
675
|
+
assert(a.binding == .None or a.binding == .started);
|
|
676
|
+
a.binding = obs;
|
|
677
|
+
|
|
678
|
+
var d = self.durations.get(player);
|
|
679
|
+
const n = d.binding;
|
|
548
680
|
|
|
549
681
|
assert(n > 0);
|
|
550
682
|
const p: u4 = if (n < 3) 3 else 1;
|
|
551
683
|
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
552
684
|
|
|
553
|
-
if (
|
|
685
|
+
if (obs == .ended) {
|
|
686
|
+
// "2-5 turns" includes the turn for the initial attack *plus* the forced attacks
|
|
554
687
|
assert(n >= 1 and n <= 4);
|
|
555
688
|
if (n != 4) try self.probability.update(p, q);
|
|
556
|
-
|
|
689
|
+
d.binding = 0;
|
|
557
690
|
} else {
|
|
691
|
+
assert(obs == .continuing);
|
|
558
692
|
assert(n >= 1 and n < 4);
|
|
559
693
|
try self.probability.update(q - p, q);
|
|
560
|
-
|
|
694
|
+
d.binding += 1;
|
|
561
695
|
}
|
|
562
696
|
}
|
|
563
697
|
|
|
@@ -574,6 +708,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
574
708
|
try self.probability.update(1, Move.METRONOME.len);
|
|
575
709
|
self.actions.get(player).metronome = move;
|
|
576
710
|
}
|
|
711
|
+
|
|
712
|
+
pub fn pp(self: *Self, player: Player, n: u4) bool {
|
|
713
|
+
if (!enabled) return true;
|
|
714
|
+
|
|
715
|
+
var a = self.actions.get(player);
|
|
716
|
+
if (n > 1 and a.pp == n) return false;
|
|
717
|
+
if (a.metronome != .None) a.pp += n;
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
577
720
|
};
|
|
578
721
|
}
|
|
579
722
|
|
|
@@ -674,16 +817,16 @@ test "Chance.damage" {
|
|
|
674
817
|
|
|
675
818
|
try chance.damage(.P1, 217);
|
|
676
819
|
if (showdown) {
|
|
677
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
820
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
678
821
|
try expectProbability(&chance.probability, 1, 39);
|
|
679
822
|
} else {
|
|
680
823
|
try expectProbability(&chance.probability, 1, 1);
|
|
681
|
-
try expectValue(0, chance.actions.p1.damage);
|
|
824
|
+
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
682
825
|
|
|
683
826
|
try chance.commit(.P1, .hit);
|
|
684
827
|
|
|
685
828
|
try expectProbability(&chance.probability, 1, 39);
|
|
686
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
829
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
687
830
|
}
|
|
688
831
|
}
|
|
689
832
|
|
|
@@ -735,13 +878,13 @@ test "Chance.moveSlot" {
|
|
|
735
878
|
|
|
736
879
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
737
880
|
try expectProbability(&chance.probability, 1, 2);
|
|
738
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
881
|
+
try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
|
|
739
882
|
|
|
740
883
|
chance.reset();
|
|
741
884
|
|
|
742
885
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
743
886
|
try expectProbability(&chance.probability, 1, 3);
|
|
744
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
887
|
+
try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
|
|
745
888
|
}
|
|
746
889
|
|
|
747
890
|
test "Chance.multiHit" {
|
|
@@ -749,51 +892,43 @@ test "Chance.multiHit" {
|
|
|
749
892
|
|
|
750
893
|
try chance.multiHit(.P1, 3);
|
|
751
894
|
try expectProbability(&chance.probability, 3, 8);
|
|
752
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
895
|
+
try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
|
|
753
896
|
|
|
754
897
|
chance.reset();
|
|
755
898
|
|
|
756
899
|
try chance.multiHit(.P2, 5);
|
|
757
900
|
try expectProbability(&chance.probability, 1, 8);
|
|
758
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
901
|
+
try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
|
|
759
902
|
}
|
|
760
903
|
|
|
761
904
|
test "Chance.duration" {
|
|
762
905
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
763
906
|
|
|
764
|
-
chance.duration(.
|
|
765
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
766
|
-
try expectValue(1, chance.durations.p2.sleep);
|
|
907
|
+
chance.duration(.P1, 2);
|
|
908
|
+
try expectValue(@as(u4, 2), chance.actions.p1.duration);
|
|
767
909
|
|
|
768
910
|
chance.reset();
|
|
769
911
|
|
|
770
|
-
chance.duration(.
|
|
771
|
-
|
|
772
|
-
try expectValue(0, chance.actions.p2.duration);
|
|
773
|
-
try expectValue(0, chance.durations.p2.binding);
|
|
774
|
-
|
|
775
|
-
try chance.commit(.P2, .hit);
|
|
776
|
-
}
|
|
777
|
-
try expectValue(4, chance.actions.p2.duration);
|
|
778
|
-
try expectValue(1, chance.durations.p2.binding);
|
|
912
|
+
chance.duration(.P2, 4);
|
|
913
|
+
try expectValue(@as(u4, 4), chance.actions.p2.duration);
|
|
779
914
|
}
|
|
780
915
|
|
|
781
916
|
test "Chance.sleep" {
|
|
782
917
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
783
918
|
|
|
784
919
|
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
785
|
-
chance.durations.p1.
|
|
786
|
-
try chance.sleep(.P1,
|
|
920
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
921
|
+
try chance.sleep(.P1, .ended);
|
|
787
922
|
try expectProbability(&chance.probability, 1, d);
|
|
788
|
-
try expectValue(0, chance.durations.p1.
|
|
923
|
+
try expectValue(@as(u3, 0), Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
789
924
|
|
|
790
925
|
chance.reset();
|
|
791
926
|
|
|
792
927
|
if (i < 7) {
|
|
793
|
-
chance.durations.p1.
|
|
794
|
-
try chance.sleep(.P1,
|
|
928
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
929
|
+
try chance.sleep(.P1, .continuing);
|
|
795
930
|
try expectProbability(&chance.probability, d - 1, d);
|
|
796
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.
|
|
931
|
+
try expectValue(@as(u3, @intCast(i)) + 1, Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
797
932
|
|
|
798
933
|
chance.reset();
|
|
799
934
|
}
|
|
@@ -806,16 +941,16 @@ test "Chance.confusion" {
|
|
|
806
941
|
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
807
942
|
if (i > 1) {
|
|
808
943
|
chance.durations.p2.confusion = @intCast(i);
|
|
809
|
-
try chance.confusion(.P2,
|
|
944
|
+
try chance.confusion(.P2, .ended);
|
|
810
945
|
try expectProbability(&chance.probability, 1, d);
|
|
811
|
-
try expectValue(0, chance.durations.p2.confusion);
|
|
946
|
+
try expectValue(@as(u3, 0), chance.durations.p2.confusion);
|
|
812
947
|
|
|
813
948
|
chance.reset();
|
|
814
949
|
}
|
|
815
950
|
|
|
816
951
|
if (i < 5) {
|
|
817
952
|
chance.durations.p2.confusion = @intCast(i);
|
|
818
|
-
try chance.confusion(.P2,
|
|
953
|
+
try chance.confusion(.P2, .continuing);
|
|
819
954
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
820
955
|
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.confusion);
|
|
821
956
|
|
|
@@ -829,15 +964,15 @@ test "Chance.disable" {
|
|
|
829
964
|
|
|
830
965
|
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
831
966
|
chance.durations.p1.disable = @intCast(i);
|
|
832
|
-
try chance.disable(.P1,
|
|
967
|
+
try chance.disable(.P1, .ended);
|
|
833
968
|
try expectProbability(&chance.probability, 1, d);
|
|
834
|
-
try expectValue(0, chance.durations.p1.disable);
|
|
969
|
+
try expectValue(@as(u4, 0), chance.durations.p1.disable);
|
|
835
970
|
|
|
836
971
|
chance.reset();
|
|
837
972
|
|
|
838
973
|
if (i < 8) {
|
|
839
974
|
chance.durations.p1.disable = @intCast(i);
|
|
840
|
-
try chance.disable(.P1,
|
|
975
|
+
try chance.disable(.P1, .continuing);
|
|
841
976
|
try expectProbability(&chance.probability, d - 1, d);
|
|
842
977
|
try expectValue(@as(u4, @intCast(i)) + 1, chance.durations.p1.disable);
|
|
843
978
|
|
|
@@ -852,16 +987,16 @@ test "Chance.attacking" {
|
|
|
852
987
|
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
853
988
|
if (i > 1) {
|
|
854
989
|
chance.durations.p2.attacking = @intCast(i);
|
|
855
|
-
try chance.attacking(.P2,
|
|
990
|
+
try chance.attacking(.P2, .ended);
|
|
856
991
|
try expectProbability(&chance.probability, 1, d);
|
|
857
|
-
try expectValue(0, chance.durations.p2.attacking);
|
|
992
|
+
try expectValue(@as(u3, 0), chance.durations.p2.attacking);
|
|
858
993
|
|
|
859
994
|
chance.reset();
|
|
860
995
|
}
|
|
861
996
|
|
|
862
997
|
if (i < 3) {
|
|
863
998
|
chance.durations.p2.attacking = @intCast(i);
|
|
864
|
-
try chance.attacking(.P2,
|
|
999
|
+
try chance.attacking(.P2, .continuing);
|
|
865
1000
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
866
1001
|
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.attacking);
|
|
867
1002
|
|
|
@@ -878,15 +1013,15 @@ test "Chance.binding" {
|
|
|
878
1013
|
|
|
879
1014
|
for (ps, qs, 1..5) |p, q, i| {
|
|
880
1015
|
chance.durations.p1.binding = @intCast(i);
|
|
881
|
-
try chance.binding(.P1,
|
|
1016
|
+
try chance.binding(.P1, .ended);
|
|
882
1017
|
try expectProbability(&chance.probability, p, q);
|
|
883
|
-
try expectValue(0, chance.durations.p1.binding);
|
|
1018
|
+
try expectValue(@as(u3, 0), chance.durations.p1.binding);
|
|
884
1019
|
|
|
885
1020
|
chance.reset();
|
|
886
1021
|
|
|
887
1022
|
if (i < 4) {
|
|
888
1023
|
chance.durations.p1.binding = @intCast(i);
|
|
889
|
-
try chance.binding(.P1,
|
|
1024
|
+
try chance.binding(.P1, .continuing);
|
|
890
1025
|
try expectProbability(&chance.probability, q - p, q);
|
|
891
1026
|
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.binding);
|
|
892
1027
|
|
|
@@ -900,7 +1035,7 @@ test "Chance.psywave" {
|
|
|
900
1035
|
|
|
901
1036
|
try chance.psywave(.P2, 100, 150);
|
|
902
1037
|
try expectProbability(&chance.probability, 1, 150);
|
|
903
|
-
try expectValue(101, chance.actions.p2.psywave);
|
|
1038
|
+
try expectValue(@as(u8, 101), chance.actions.p2.psywave);
|
|
904
1039
|
}
|
|
905
1040
|
|
|
906
1041
|
test "Chance.metronome" {
|
|
@@ -938,16 +1073,16 @@ const Null = struct {
|
|
|
938
1073
|
_ = .{ self, player, kind };
|
|
939
1074
|
}
|
|
940
1075
|
|
|
941
|
-
pub fn
|
|
1076
|
+
pub fn glitch(self: Null) void {
|
|
942
1077
|
_ = .{self};
|
|
943
1078
|
}
|
|
944
1079
|
|
|
945
|
-
pub fn
|
|
946
|
-
_ = .{
|
|
1080
|
+
pub fn clearPending(self: Null) void {
|
|
1081
|
+
_ = .{self};
|
|
947
1082
|
}
|
|
948
1083
|
|
|
949
|
-
pub fn switched(self: Null, player: Player,
|
|
950
|
-
_ = .{ self, player,
|
|
1084
|
+
pub fn switched(self: Null, player: Player, slot: u8) void {
|
|
1085
|
+
_ = .{ self, player, slot };
|
|
951
1086
|
}
|
|
952
1087
|
|
|
953
1088
|
pub fn speedTie(self: Null, p1: bool) Error!void {
|
|
@@ -986,34 +1121,37 @@ const Null = struct {
|
|
|
986
1121
|
_ = .{ self, player, n };
|
|
987
1122
|
}
|
|
988
1123
|
|
|
989
|
-
pub fn duration(
|
|
1124
|
+
pub fn duration(self: Null, player: Player, turns: u4) void {
|
|
1125
|
+
_ = .{ self, player, turns };
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
pub fn observe(
|
|
990
1129
|
self: Null,
|
|
991
|
-
comptime field:
|
|
1130
|
+
comptime field: Action.Field,
|
|
992
1131
|
player: Player,
|
|
993
|
-
|
|
994
|
-
turns: u4,
|
|
1132
|
+
obs: Optional(Confusion),
|
|
995
1133
|
) void {
|
|
996
|
-
_ = .{ self, field, player,
|
|
1134
|
+
_ = .{ self, field, player, obs };
|
|
997
1135
|
}
|
|
998
1136
|
|
|
999
|
-
pub fn sleep(self: Null, player: Player,
|
|
1000
|
-
_ = .{ self, player,
|
|
1137
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1138
|
+
_ = .{ self, player, obs };
|
|
1001
1139
|
}
|
|
1002
1140
|
|
|
1003
|
-
pub fn
|
|
1004
|
-
_ = .{ self, player,
|
|
1141
|
+
pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
|
|
1142
|
+
_ = .{ self, player, obs };
|
|
1005
1143
|
}
|
|
1006
1144
|
|
|
1007
|
-
pub fn
|
|
1008
|
-
_ = .{ self, player,
|
|
1145
|
+
pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1146
|
+
_ = .{ self, player, obs };
|
|
1009
1147
|
}
|
|
1010
1148
|
|
|
1011
|
-
pub fn attacking(self: Null, player: Player,
|
|
1012
|
-
_ = .{ self, player,
|
|
1149
|
+
pub fn attacking(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1150
|
+
_ = .{ self, player, obs };
|
|
1013
1151
|
}
|
|
1014
1152
|
|
|
1015
|
-
pub fn binding(self: Null, player: Player,
|
|
1016
|
-
_ = .{ self, player,
|
|
1153
|
+
pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1154
|
+
_ = .{ self, player, obs };
|
|
1017
1155
|
}
|
|
1018
1156
|
|
|
1019
1157
|
pub fn psywave(self: Null, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -1023,4 +1161,9 @@ const Null = struct {
|
|
|
1023
1161
|
pub fn metronome(self: Null, player: Player, move: Move) Error!void {
|
|
1024
1162
|
_ = .{ self, player, move };
|
|
1025
1163
|
}
|
|
1164
|
+
|
|
1165
|
+
pub fn pp(self: Null, player: Player, n: u4) bool {
|
|
1166
|
+
_ = .{ self, player, n };
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1026
1169
|
};
|