@pkmn/engine 0.1.0-dev.62399ed5 → 0.1.0-dev.6be9a775
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/index.d.ts +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/test/display.js +6 -1
- package/build/test/display.js.map +1 -1
- package/build.zig +199 -66
- package/package.json +4 -1
- package/src/bin/install-pkmn-engine +7 -5
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/protocol.zig +41 -36
- package/src/lib/common/rational.zig +46 -46
- package/src/lib/common/rng.zig +4 -4
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +329 -119
- package/src/lib/gen1/chance.zig +332 -224
- package/src/lib/gen1/data.zig +28 -27
- package/src/lib/gen1/helpers.zig +13 -534
- package/src/lib/gen1/mechanics.zig +178 -109
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +91 -501
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +4 -2
- package/src/lib/gen2/mechanics.zig +47 -71
- package/src/lib/pkmn.zig +7 -5
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,17 +45,11 @@ 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
|
|
|
45
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
46
|
-
pub fn reset(self: *Actions) void {
|
|
47
|
-
self.p1.reset();
|
|
48
|
-
self.p2.reset();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
53
|
/// Returns true if `a` is equal to `b`.
|
|
52
54
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
53
55
|
return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
|
|
@@ -57,20 +59,15 @@ pub const Actions = extern struct {
|
|
|
57
59
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
58
60
|
/// set to the same value).
|
|
59
61
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
60
|
-
inline for (@typeInfo(Actions)
|
|
61
|
-
inline for (@typeInfo(Action)
|
|
62
|
+
inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
|
|
63
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
62
64
|
const a_val = @field(@field(a, player.name), field.name);
|
|
63
65
|
const b_val = @field(@field(b, player.name), field.name);
|
|
64
66
|
|
|
65
67
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
66
|
-
|
|
67
|
-
if ((@field(a_val, f.name) > 0) != (@field(b_val, f.name) > 0)) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
.Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
68
|
+
Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
72
69
|
return false,
|
|
73
|
-
|
|
70
|
+
Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
74
71
|
else => unreachable,
|
|
75
72
|
}
|
|
76
73
|
}
|
|
@@ -92,14 +89,10 @@ pub const Actions = extern struct {
|
|
|
92
89
|
}
|
|
93
90
|
};
|
|
94
91
|
|
|
95
|
-
// TODO: ziglang/zig#19730
|
|
96
92
|
test Actions {
|
|
97
93
|
const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
|
|
98
94
|
const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
|
|
99
95
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
100
|
-
// const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
101
|
-
// const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
102
|
-
// const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
103
96
|
|
|
104
97
|
try expect(a.eql(a));
|
|
105
98
|
try expect(!a.eql(b));
|
|
@@ -112,10 +105,22 @@ test Actions {
|
|
|
112
105
|
try expect(b.matches(a));
|
|
113
106
|
try expect(!a.matches(c));
|
|
114
107
|
try expect(!c.matches(a));
|
|
115
|
-
// try expect(d.matches(e));
|
|
116
|
-
// try expect(!d.matches(f));
|
|
117
108
|
}
|
|
118
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
|
+
|
|
119
124
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
120
125
|
/// single player.
|
|
121
126
|
pub const Action = packed struct(u64) {
|
|
@@ -140,12 +145,25 @@ pub const Action = packed struct(u64) {
|
|
|
140
145
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
141
146
|
duration: u4 = 0,
|
|
142
147
|
|
|
143
|
-
///
|
|
144
|
-
|
|
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,
|
|
145
160
|
|
|
146
161
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
147
162
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
148
163
|
move_slot: u4 = 0,
|
|
164
|
+
|
|
165
|
+
/// TODO
|
|
166
|
+
pp: u4 = 0,
|
|
149
167
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
150
168
|
multi_hit: u4 = 0,
|
|
151
169
|
|
|
@@ -155,15 +173,8 @@ pub const Action = packed struct(u64) {
|
|
|
155
173
|
/// If not None, the Move to return for Rolls.metronome.
|
|
156
174
|
metronome: Move = .None,
|
|
157
175
|
|
|
158
|
-
pub const DURATIONS: u64 = 0x000000FFFF000000;
|
|
159
|
-
|
|
160
176
|
pub const Field = std.meta.FieldEnum(Action);
|
|
161
177
|
|
|
162
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
163
|
-
pub fn reset(self: *Action) void {
|
|
164
|
-
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
178
|
pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
|
|
168
179
|
_ = .{ f, o };
|
|
169
180
|
try fmt(a, w, false);
|
|
@@ -172,22 +183,10 @@ pub const Action = packed struct(u64) {
|
|
|
172
183
|
pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
|
|
173
184
|
try writer.writeByte('(');
|
|
174
185
|
var printed = false;
|
|
175
|
-
inline for (@typeInfo(Action)
|
|
186
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
176
187
|
const val = @field(self, field.name);
|
|
177
188
|
switch (@typeInfo(@TypeOf(val))) {
|
|
178
|
-
|
|
179
|
-
const v = @field(val, f.name);
|
|
180
|
-
if (v != 0) {
|
|
181
|
-
if (printed) try writer.writeAll(", ");
|
|
182
|
-
if (shape) {
|
|
183
|
-
try writer.print("{s}:?", .{f.name});
|
|
184
|
-
} else {
|
|
185
|
-
try writer.print("{s}:{d}", .{ f.name, v });
|
|
186
|
-
}
|
|
187
|
-
printed = true;
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
.Enum => if (val != .None) {
|
|
189
|
+
Enum => if (val != .None) {
|
|
191
190
|
if (printed) try writer.writeAll(", ");
|
|
192
191
|
if (shape) {
|
|
193
192
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -196,12 +195,19 @@ pub const Action = packed struct(u64) {
|
|
|
196
195
|
if (val == .false) "!" else "",
|
|
197
196
|
field.name,
|
|
198
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
|
+
});
|
|
199
205
|
} else {
|
|
200
206
|
try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
|
|
201
207
|
}
|
|
202
208
|
printed = true;
|
|
203
209
|
},
|
|
204
|
-
|
|
210
|
+
Int => if (val != 0) {
|
|
205
211
|
if (printed) try writer.writeAll(", ");
|
|
206
212
|
if (shape) {
|
|
207
213
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -217,43 +223,80 @@ pub const Action = packed struct(u64) {
|
|
|
217
223
|
}
|
|
218
224
|
};
|
|
219
225
|
|
|
220
|
-
test Action {
|
|
221
|
-
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
222
|
-
a.reset();
|
|
223
|
-
|
|
224
|
-
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
226
|
/// TODO
|
|
228
|
-
pub const Durations =
|
|
229
|
-
///
|
|
227
|
+
pub const Durations = struct {
|
|
228
|
+
/// TODO
|
|
230
229
|
p1: Duration = .{},
|
|
231
|
-
///
|
|
230
|
+
/// TODO
|
|
232
231
|
p2: Duration = .{},
|
|
233
232
|
|
|
234
233
|
comptime {
|
|
235
|
-
assert(@sizeOf(Durations) ==
|
|
234
|
+
assert(@sizeOf(Durations) == 8);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// TODO
|
|
238
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Duration) {
|
|
239
|
+
assert(isPointerTo(self, Durations));
|
|
240
|
+
return if (player == .P1) &self.p1 else &self.p2;
|
|
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 });
|
|
236
251
|
}
|
|
237
252
|
};
|
|
238
253
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
/// The number of turns a Pokémon has been observed to be confused.
|
|
254
|
+
const Sleeps = Array(6, u3);
|
|
255
|
+
|
|
256
|
+
/// TODO
|
|
257
|
+
pub const Duration = packed struct(u32) {
|
|
258
|
+
sleeps: Sleeps.T = 0,
|
|
245
259
|
confusion: u3 = 0,
|
|
246
|
-
/// The number of turns a Pokémon has been observed to be disabled.
|
|
247
260
|
disable: u4 = 0,
|
|
248
|
-
/// The number of turns a Pokémon has been observed to be attacking.
|
|
249
261
|
attacking: u3 = 0,
|
|
250
|
-
/// The number of turns a Pokémon has been observed to be binding their opponent.
|
|
251
262
|
binding: u3 = 0,
|
|
252
263
|
|
|
253
|
-
|
|
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
|
+
}
|
|
254
297
|
};
|
|
255
298
|
|
|
256
|
-
pub const Commit = enum { hit, miss, binding };
|
|
299
|
+
pub const Commit = enum { hit, miss, binding, err, glitch };
|
|
257
300
|
|
|
258
301
|
const uN = if (options.miss) u8 else u9;
|
|
259
302
|
|
|
@@ -268,14 +311,8 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
268
311
|
/// The actions taken by a hypothetical "chance player" that convey information about which
|
|
269
312
|
/// RNG events were observed during a battle `update`.
|
|
270
313
|
actions: Actions = .{},
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
274
|
-
// switching and so we must store it here. The indices of these arrays correspond to the
|
|
275
|
-
// `order` field of a Side. This information could be stored in actions but size is a
|
|
276
|
-
// concern so this is tracked separately as actions only purports to track RNG events
|
|
277
|
-
// observed during a single `update` (not across updates).
|
|
278
|
-
sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
|
|
314
|
+
/// TODO
|
|
315
|
+
durations: Durations = .{},
|
|
279
316
|
|
|
280
317
|
// In many cases on both the cartridge and on Pokémon Showdown rolls are made even though
|
|
281
318
|
// later checks render their result irrelevant (missing when the target is actually immune,
|
|
@@ -299,7 +336,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
299
336
|
damage_roll: u8 = 0,
|
|
300
337
|
hit: bool = false,
|
|
301
338
|
hit_probablity: uN = 0,
|
|
302
|
-
|
|
339
|
+
glitch: bool = false,
|
|
303
340
|
} = .{},
|
|
304
341
|
|
|
305
342
|
/// Possible error returned by operations tracking chance probability.
|
|
@@ -310,7 +347,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
310
347
|
if (!enabled) return;
|
|
311
348
|
|
|
312
349
|
self.probability.reset();
|
|
313
|
-
self.actions.
|
|
350
|
+
self.actions = .{};
|
|
314
351
|
self.pending = .{};
|
|
315
352
|
}
|
|
316
353
|
|
|
@@ -319,20 +356,18 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
319
356
|
|
|
320
357
|
assert(!showdown);
|
|
321
358
|
|
|
359
|
+
if (kind == .glitch and !self.pending.glitch) return;
|
|
360
|
+
|
|
322
361
|
var action = self.actions.get(player);
|
|
323
362
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
action.duration = @intCast(self.pending.binding);
|
|
329
|
-
assert(action.durations.binding == 0);
|
|
330
|
-
action.durations.binding = 1;
|
|
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;
|
|
331
367
|
}
|
|
332
|
-
if (kind == .binding) return;
|
|
333
368
|
|
|
334
369
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
335
|
-
// target is immune
|
|
370
|
+
// target is immune*/behind a sub/etc)
|
|
336
371
|
if (self.pending.hit_probablity != 0) {
|
|
337
372
|
try self.probability.update(self.pending.hit_probablity, 256);
|
|
338
373
|
action.hit = if (self.pending.hit) .true else .false;
|
|
@@ -342,13 +377,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
342
377
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
343
378
|
// "critical hit" was spurious or not as moves that do 1 or less damage don't rol for
|
|
344
379
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
345
|
-
// passed to the critical hit helper)
|
|
346
|
-
|
|
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
|
+
}
|
|
347
388
|
|
|
348
389
|
if (self.pending.crit_probablity != 0) {
|
|
349
390
|
try self.probability.update(self.pending.crit_probablity, 256);
|
|
350
391
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
351
|
-
}
|
|
392
|
+
} else assert(!self.pending.glitch);
|
|
352
393
|
|
|
353
394
|
if (self.pending.damage_roll > 0) {
|
|
354
395
|
try self.probability.update(1, 39);
|
|
@@ -356,41 +397,37 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
356
397
|
}
|
|
357
398
|
}
|
|
358
399
|
|
|
359
|
-
pub fn
|
|
400
|
+
pub fn glitch(self: *Self) void {
|
|
360
401
|
if (!enabled) return;
|
|
361
402
|
|
|
362
|
-
|
|
403
|
+
assert(!showdown);
|
|
404
|
+
self.pending.glitch = true;
|
|
363
405
|
}
|
|
364
406
|
|
|
365
|
-
pub fn
|
|
407
|
+
pub fn clearPending(self: *Self) void {
|
|
366
408
|
if (!enabled) return;
|
|
367
409
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (status) action.durations.sleep = 0;
|
|
371
|
-
action.durations.confusion = 0;
|
|
372
|
-
action.durations.disable = 0;
|
|
373
|
-
} else {
|
|
374
|
-
action.durations.attacking = 0;
|
|
375
|
-
action.durations.binding = 0;
|
|
376
|
-
}
|
|
410
|
+
assert(!showdown);
|
|
411
|
+
self.pending = .{};
|
|
377
412
|
}
|
|
378
413
|
|
|
379
|
-
pub fn switched(self: *Self, player: Player,
|
|
414
|
+
pub fn switched(self: *Self, player: Player, slot: u8) void {
|
|
380
415
|
if (!enabled) return;
|
|
381
416
|
|
|
382
|
-
assert(
|
|
383
|
-
assert(out >= 1 and out <= 6);
|
|
417
|
+
assert(slot >= 1 and slot <= 6);
|
|
384
418
|
|
|
385
|
-
var
|
|
419
|
+
var d = self.durations.get(player);
|
|
386
420
|
|
|
387
|
-
const
|
|
388
|
-
|
|
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);
|
|
389
424
|
|
|
390
|
-
|
|
391
|
-
|
|
425
|
+
d.confusion = 0;
|
|
426
|
+
d.disable = 0;
|
|
427
|
+
d.attacking = 0;
|
|
428
|
+
d.binding = 0;
|
|
392
429
|
|
|
393
|
-
self.
|
|
430
|
+
self.durations.get(player.foe()).binding = 0;
|
|
394
431
|
}
|
|
395
432
|
|
|
396
433
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
@@ -489,108 +526,170 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
489
526
|
self.actions.get(player).multi_hit = n;
|
|
490
527
|
}
|
|
491
528
|
|
|
492
|
-
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(
|
|
493
536
|
self: *Self,
|
|
494
|
-
comptime field:
|
|
537
|
+
comptime field: Action.Field,
|
|
495
538
|
player: Player,
|
|
496
|
-
|
|
497
|
-
turns: u4,
|
|
539
|
+
obs: Optional(Confusion),
|
|
498
540
|
) void {
|
|
499
541
|
if (!enabled) return;
|
|
500
542
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
(
|
|
507
|
-
|
|
508
|
-
|
|
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,
|
|
509
578
|
}
|
|
510
579
|
}
|
|
511
580
|
|
|
512
|
-
pub fn sleep(self: *Self, player: Player,
|
|
581
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
513
582
|
if (!enabled) return;
|
|
514
583
|
|
|
515
|
-
var
|
|
516
|
-
|
|
517
|
-
|
|
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) {
|
|
518
594
|
assert(n >= 1 and n <= 7);
|
|
519
595
|
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
520
|
-
|
|
596
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, 0);
|
|
521
597
|
} else {
|
|
598
|
+
assert(obs == .continuing);
|
|
522
599
|
assert(n >= 1 and n < 7);
|
|
523
600
|
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
524
|
-
|
|
601
|
+
d.sleeps = Sleeps.set(d.sleeps, 0, n + 1);
|
|
525
602
|
}
|
|
526
603
|
}
|
|
527
604
|
|
|
528
|
-
pub fn confusion(self: *Self, player: Player,
|
|
605
|
+
pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
|
|
529
606
|
if (!enabled) return;
|
|
530
607
|
|
|
531
|
-
var
|
|
532
|
-
|
|
533
|
-
|
|
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) {
|
|
534
616
|
assert(n >= 2 and n <= 5);
|
|
535
617
|
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
536
|
-
|
|
618
|
+
d.confusion = 0;
|
|
537
619
|
} else {
|
|
620
|
+
assert(obs == .continuing);
|
|
538
621
|
assert(n >= 1 and n < 5);
|
|
539
622
|
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
540
|
-
|
|
623
|
+
d.confusion += 1;
|
|
541
624
|
}
|
|
542
625
|
}
|
|
543
626
|
|
|
544
|
-
pub fn disable(self: *Self, player: Player,
|
|
627
|
+
pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
545
628
|
if (!enabled) return;
|
|
546
629
|
|
|
547
|
-
var
|
|
548
|
-
|
|
549
|
-
|
|
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) {
|
|
550
638
|
assert(n >= 1 and n <= 8);
|
|
551
639
|
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
552
|
-
|
|
640
|
+
d.disable = 0;
|
|
553
641
|
} else {
|
|
642
|
+
assert(obs == .continuing);
|
|
554
643
|
assert(n >= 1 and n < 8);
|
|
555
644
|
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
556
|
-
|
|
645
|
+
d.disable += 1;
|
|
557
646
|
}
|
|
558
647
|
}
|
|
559
648
|
|
|
560
|
-
pub fn attacking(self: *Self, player: Player,
|
|
649
|
+
pub fn attacking(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
561
650
|
if (!enabled) return;
|
|
562
651
|
|
|
563
|
-
var
|
|
564
|
-
|
|
565
|
-
|
|
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) {
|
|
566
659
|
assert(n >= 2 and n <= 3);
|
|
567
660
|
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
568
|
-
|
|
661
|
+
d.attacking = 0;
|
|
569
662
|
} else {
|
|
663
|
+
assert(obs == .continuing);
|
|
570
664
|
assert(n >= 1 and n < 3);
|
|
571
665
|
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
572
|
-
|
|
666
|
+
d.attacking += 1;
|
|
573
667
|
}
|
|
574
668
|
}
|
|
575
669
|
|
|
576
|
-
pub fn binding(self: *Self, player: Player,
|
|
670
|
+
pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
577
671
|
if (!enabled) return;
|
|
578
672
|
|
|
579
|
-
var
|
|
580
|
-
|
|
673
|
+
var a = self.actions.get(player);
|
|
674
|
+
assert(a.binding == .None or a.binding == .started);
|
|
675
|
+
a.binding = obs;
|
|
676
|
+
|
|
677
|
+
var d = self.durations.get(player);
|
|
678
|
+
const n = d.binding;
|
|
581
679
|
|
|
582
680
|
assert(n > 0);
|
|
583
681
|
const p: u4 = if (n < 3) 3 else 1;
|
|
584
682
|
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
585
683
|
|
|
586
|
-
if (
|
|
684
|
+
if (obs == .ended) {
|
|
587
685
|
assert(n >= 1 and n <= 4);
|
|
588
686
|
if (n != 4) try self.probability.update(p, q);
|
|
589
|
-
|
|
687
|
+
d.binding = 0;
|
|
590
688
|
} else {
|
|
689
|
+
assert(obs == .continuing);
|
|
591
690
|
assert(n >= 1 and n < 4);
|
|
592
691
|
try self.probability.update(q - p, q);
|
|
593
|
-
|
|
692
|
+
d.binding += 1;
|
|
594
693
|
}
|
|
595
694
|
}
|
|
596
695
|
|
|
@@ -607,6 +706,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
607
706
|
try self.probability.update(1, Move.METRONOME.len);
|
|
608
707
|
self.actions.get(player).metronome = move;
|
|
609
708
|
}
|
|
709
|
+
|
|
710
|
+
pub fn pp(self: *Self, player: Player, n: u4) bool {
|
|
711
|
+
if (!enabled) return true;
|
|
712
|
+
|
|
713
|
+
var a = self.actions.get(player);
|
|
714
|
+
if (n > 1 and a.pp == n) return false;
|
|
715
|
+
if (a.metronome != .None) a.pp += n;
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
610
718
|
};
|
|
611
719
|
}
|
|
612
720
|
|
|
@@ -707,16 +815,16 @@ test "Chance.damage" {
|
|
|
707
815
|
|
|
708
816
|
try chance.damage(.P1, 217);
|
|
709
817
|
if (showdown) {
|
|
710
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
818
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
711
819
|
try expectProbability(&chance.probability, 1, 39);
|
|
712
820
|
} else {
|
|
713
821
|
try expectProbability(&chance.probability, 1, 1);
|
|
714
|
-
try expectValue(0, chance.actions.p1.damage);
|
|
822
|
+
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
715
823
|
|
|
716
824
|
try chance.commit(.P1, .hit);
|
|
717
825
|
|
|
718
826
|
try expectProbability(&chance.probability, 1, 39);
|
|
719
|
-
try expectValue(217, chance.actions.p1.damage);
|
|
827
|
+
try expectValue(@as(u8, 217), chance.actions.p1.damage);
|
|
720
828
|
}
|
|
721
829
|
}
|
|
722
830
|
|
|
@@ -768,13 +876,13 @@ test "Chance.moveSlot" {
|
|
|
768
876
|
|
|
769
877
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
770
878
|
try expectProbability(&chance.probability, 1, 2);
|
|
771
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
879
|
+
try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
|
|
772
880
|
|
|
773
881
|
chance.reset();
|
|
774
882
|
|
|
775
883
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
776
884
|
try expectProbability(&chance.probability, 1, 3);
|
|
777
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
885
|
+
try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
|
|
778
886
|
}
|
|
779
887
|
|
|
780
888
|
test "Chance.multiHit" {
|
|
@@ -782,51 +890,43 @@ test "Chance.multiHit" {
|
|
|
782
890
|
|
|
783
891
|
try chance.multiHit(.P1, 3);
|
|
784
892
|
try expectProbability(&chance.probability, 3, 8);
|
|
785
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
893
|
+
try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
|
|
786
894
|
|
|
787
895
|
chance.reset();
|
|
788
896
|
|
|
789
897
|
try chance.multiHit(.P2, 5);
|
|
790
898
|
try expectProbability(&chance.probability, 1, 8);
|
|
791
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
899
|
+
try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
|
|
792
900
|
}
|
|
793
901
|
|
|
794
902
|
test "Chance.duration" {
|
|
795
903
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
796
904
|
|
|
797
|
-
chance.duration(.
|
|
798
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
799
|
-
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
905
|
+
chance.duration(.P1, 2);
|
|
906
|
+
try expectValue(@as(u4, 2), chance.actions.p1.duration);
|
|
800
907
|
|
|
801
908
|
chance.reset();
|
|
802
909
|
|
|
803
|
-
chance.duration(.
|
|
804
|
-
|
|
805
|
-
try expectValue(0, chance.actions.p2.duration);
|
|
806
|
-
try expectValue(0, chance.actions.p2.durations.binding);
|
|
807
|
-
|
|
808
|
-
try chance.commit(.P2, .hit);
|
|
809
|
-
}
|
|
810
|
-
try expectValue(4, chance.actions.p2.duration);
|
|
811
|
-
try expectValue(1, chance.actions.p2.durations.binding);
|
|
910
|
+
chance.duration(.P2, 4);
|
|
911
|
+
try expectValue(@as(u4, 4), chance.actions.p2.duration);
|
|
812
912
|
}
|
|
813
913
|
|
|
814
914
|
test "Chance.sleep" {
|
|
815
915
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
816
916
|
|
|
817
917
|
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
818
|
-
chance.
|
|
819
|
-
try chance.sleep(.P1,
|
|
918
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
919
|
+
try chance.sleep(.P1, .ended);
|
|
820
920
|
try expectProbability(&chance.probability, 1, d);
|
|
821
|
-
try expectValue(0, chance.
|
|
921
|
+
try expectValue(@as(u3, 0), Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
822
922
|
|
|
823
923
|
chance.reset();
|
|
824
924
|
|
|
825
925
|
if (i < 7) {
|
|
826
|
-
chance.
|
|
827
|
-
try chance.sleep(.P1,
|
|
926
|
+
chance.durations.p1.sleeps = Sleeps.set(chance.durations.p1.sleeps, 0, @intCast(i));
|
|
927
|
+
try chance.sleep(.P1, .continuing);
|
|
828
928
|
try expectProbability(&chance.probability, d - 1, d);
|
|
829
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
929
|
+
try expectValue(@as(u3, @intCast(i)) + 1, Sleeps.get(chance.durations.p1.sleeps, 0));
|
|
830
930
|
|
|
831
931
|
chance.reset();
|
|
832
932
|
}
|
|
@@ -838,19 +938,19 @@ test "Chance.confusion" {
|
|
|
838
938
|
|
|
839
939
|
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
840
940
|
if (i > 1) {
|
|
841
|
-
chance.
|
|
842
|
-
try chance.confusion(.P2,
|
|
941
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
942
|
+
try chance.confusion(.P2, .ended);
|
|
843
943
|
try expectProbability(&chance.probability, 1, d);
|
|
844
|
-
try expectValue(0, chance.
|
|
944
|
+
try expectValue(@as(u3, 0), chance.durations.p2.confusion);
|
|
845
945
|
|
|
846
946
|
chance.reset();
|
|
847
947
|
}
|
|
848
948
|
|
|
849
949
|
if (i < 5) {
|
|
850
|
-
chance.
|
|
851
|
-
try chance.confusion(.P2,
|
|
950
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
951
|
+
try chance.confusion(.P2, .continuing);
|
|
852
952
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
853
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
953
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.confusion);
|
|
854
954
|
|
|
855
955
|
chance.reset();
|
|
856
956
|
}
|
|
@@ -861,18 +961,18 @@ test "Chance.disable" {
|
|
|
861
961
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
862
962
|
|
|
863
963
|
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
864
|
-
chance.
|
|
865
|
-
try chance.disable(.P1,
|
|
964
|
+
chance.durations.p1.disable = @intCast(i);
|
|
965
|
+
try chance.disable(.P1, .ended);
|
|
866
966
|
try expectProbability(&chance.probability, 1, d);
|
|
867
|
-
try expectValue(0, chance.
|
|
967
|
+
try expectValue(@as(u4, 0), chance.durations.p1.disable);
|
|
868
968
|
|
|
869
969
|
chance.reset();
|
|
870
970
|
|
|
871
971
|
if (i < 8) {
|
|
872
|
-
chance.
|
|
873
|
-
try chance.disable(.P1,
|
|
972
|
+
chance.durations.p1.disable = @intCast(i);
|
|
973
|
+
try chance.disable(.P1, .continuing);
|
|
874
974
|
try expectProbability(&chance.probability, d - 1, d);
|
|
875
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.
|
|
975
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.durations.p1.disable);
|
|
876
976
|
|
|
877
977
|
chance.reset();
|
|
878
978
|
}
|
|
@@ -884,19 +984,19 @@ test "Chance.attacking" {
|
|
|
884
984
|
|
|
885
985
|
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
886
986
|
if (i > 1) {
|
|
887
|
-
chance.
|
|
888
|
-
try chance.attacking(.P2,
|
|
987
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
988
|
+
try chance.attacking(.P2, .ended);
|
|
889
989
|
try expectProbability(&chance.probability, 1, d);
|
|
890
|
-
try expectValue(0, chance.
|
|
990
|
+
try expectValue(@as(u3, 0), chance.durations.p2.attacking);
|
|
891
991
|
|
|
892
992
|
chance.reset();
|
|
893
993
|
}
|
|
894
994
|
|
|
895
995
|
if (i < 3) {
|
|
896
|
-
chance.
|
|
897
|
-
try chance.attacking(.P2,
|
|
996
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
997
|
+
try chance.attacking(.P2, .continuing);
|
|
898
998
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
899
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
999
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.attacking);
|
|
900
1000
|
|
|
901
1001
|
chance.reset();
|
|
902
1002
|
}
|
|
@@ -910,18 +1010,18 @@ test "Chance.binding" {
|
|
|
910
1010
|
const qs = [_]u8{ 8, 5, 2, 1 };
|
|
911
1011
|
|
|
912
1012
|
for (ps, qs, 1..5) |p, q, i| {
|
|
913
|
-
chance.
|
|
914
|
-
try chance.binding(.P1,
|
|
1013
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1014
|
+
try chance.binding(.P1, .ended);
|
|
915
1015
|
try expectProbability(&chance.probability, p, q);
|
|
916
|
-
try expectValue(0, chance.
|
|
1016
|
+
try expectValue(@as(u3, 0), chance.durations.p1.binding);
|
|
917
1017
|
|
|
918
1018
|
chance.reset();
|
|
919
1019
|
|
|
920
1020
|
if (i < 4) {
|
|
921
|
-
chance.
|
|
922
|
-
try chance.binding(.P1,
|
|
1021
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1022
|
+
try chance.binding(.P1, .continuing);
|
|
923
1023
|
try expectProbability(&chance.probability, q - p, q);
|
|
924
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
1024
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.binding);
|
|
925
1025
|
|
|
926
1026
|
chance.reset();
|
|
927
1027
|
}
|
|
@@ -933,7 +1033,7 @@ test "Chance.psywave" {
|
|
|
933
1033
|
|
|
934
1034
|
try chance.psywave(.P2, 100, 150);
|
|
935
1035
|
try expectProbability(&chance.probability, 1, 150);
|
|
936
|
-
try expectValue(101, chance.actions.p2.psywave);
|
|
1036
|
+
try expectValue(@as(u8, 101), chance.actions.p2.psywave);
|
|
937
1037
|
}
|
|
938
1038
|
|
|
939
1039
|
test "Chance.metronome" {
|
|
@@ -971,16 +1071,16 @@ const Null = struct {
|
|
|
971
1071
|
_ = .{ self, player, kind };
|
|
972
1072
|
}
|
|
973
1073
|
|
|
974
|
-
pub fn
|
|
1074
|
+
pub fn glitch(self: Null) void {
|
|
975
1075
|
_ = .{self};
|
|
976
1076
|
}
|
|
977
1077
|
|
|
978
|
-
pub fn
|
|
979
|
-
_ = .{
|
|
1078
|
+
pub fn clearPending(self: Null) void {
|
|
1079
|
+
_ = .{self};
|
|
980
1080
|
}
|
|
981
1081
|
|
|
982
|
-
pub fn switched(self: Null, player: Player,
|
|
983
|
-
_ = .{ self, player,
|
|
1082
|
+
pub fn switched(self: Null, player: Player, slot: u8) void {
|
|
1083
|
+
_ = .{ self, player, slot };
|
|
984
1084
|
}
|
|
985
1085
|
|
|
986
1086
|
pub fn speedTie(self: Null, p1: bool) Error!void {
|
|
@@ -1019,34 +1119,37 @@ const Null = struct {
|
|
|
1019
1119
|
_ = .{ self, player, n };
|
|
1020
1120
|
}
|
|
1021
1121
|
|
|
1022
|
-
pub fn duration(
|
|
1122
|
+
pub fn duration(self: Null, player: Player, turns: u4) void {
|
|
1123
|
+
_ = .{ self, player, turns };
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
pub fn observe(
|
|
1023
1127
|
self: Null,
|
|
1024
|
-
comptime field:
|
|
1128
|
+
comptime field: Action.Field,
|
|
1025
1129
|
player: Player,
|
|
1026
|
-
|
|
1027
|
-
turns: u4,
|
|
1130
|
+
obs: Optional(Confusion),
|
|
1028
1131
|
) void {
|
|
1029
|
-
_ = .{ self, field, player,
|
|
1132
|
+
_ = .{ self, field, player, obs };
|
|
1030
1133
|
}
|
|
1031
1134
|
|
|
1032
|
-
pub fn sleep(self: Null, player: Player,
|
|
1033
|
-
_ = .{ self, player,
|
|
1135
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1136
|
+
_ = .{ self, player, obs };
|
|
1034
1137
|
}
|
|
1035
1138
|
|
|
1036
|
-
pub fn
|
|
1037
|
-
_ = .{ self, player,
|
|
1139
|
+
pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
|
|
1140
|
+
_ = .{ self, player, obs };
|
|
1038
1141
|
}
|
|
1039
1142
|
|
|
1040
|
-
pub fn
|
|
1041
|
-
_ = .{ self, player,
|
|
1143
|
+
pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1144
|
+
_ = .{ self, player, obs };
|
|
1042
1145
|
}
|
|
1043
1146
|
|
|
1044
|
-
pub fn attacking(self: Null, player: Player,
|
|
1045
|
-
_ = .{ self, player,
|
|
1147
|
+
pub fn attacking(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1148
|
+
_ = .{ self, player, obs };
|
|
1046
1149
|
}
|
|
1047
1150
|
|
|
1048
|
-
pub fn binding(self: Null, player: Player,
|
|
1049
|
-
_ = .{ self, player,
|
|
1151
|
+
pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1152
|
+
_ = .{ self, player, obs };
|
|
1050
1153
|
}
|
|
1051
1154
|
|
|
1052
1155
|
pub fn psywave(self: Null, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -1056,4 +1159,9 @@ const Null = struct {
|
|
|
1056
1159
|
pub fn metronome(self: Null, player: Player, move: Move) Error!void {
|
|
1057
1160
|
_ = .{ self, player, move };
|
|
1058
1161
|
}
|
|
1162
|
+
|
|
1163
|
+
pub fn pp(self: Null, player: Player, n: u4) bool {
|
|
1164
|
+
_ = .{ self, player, n };
|
|
1165
|
+
return true;
|
|
1166
|
+
}
|
|
1059
1167
|
};
|