@pkmn/engine 0.1.0-dev.3a62fd35 → 0.1.0-dev.3de821b9
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 -3
- package/README.md +13 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +2 -3
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +32 -50
- package/package.json +24 -17
- package/src/bin/install-pkmn-engine +13 -5
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +11 -9
- package/src/lib/common/rational.zig +102 -12
- package/src/lib/common/rng.zig +6 -6
- package/src/lib/gen1/calc.zig +480 -92
- package/src/lib/gen1/chance.zig +384 -268
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +3 -3
- package/src/lib/gen1/helpers.zig +90 -534
- package/src/lib/gen1/mechanics.zig +266 -158
- package/src/lib/gen2/chance.zig +8 -440
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +119 -61
- package/src/lib/pkmn.zig +4 -12
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -42,12 +42,6 @@ pub const Actions = extern struct {
|
|
|
42
42
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
43
|
}
|
|
44
44
|
|
|
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
45
|
/// Returns true if `a` is equal to `b`.
|
|
52
46
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
53
47
|
return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
|
|
@@ -63,11 +57,6 @@ pub const Actions = extern struct {
|
|
|
63
57
|
const b_val = @field(@field(b, player.name), field.name);
|
|
64
58
|
|
|
65
59
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
66
|
-
.Struct => inline for (@typeInfo(@TypeOf(a_val)).Struct.fields) |f| {
|
|
67
|
-
if ((@field(a_val, f.name) > 0) != (@field(b_val, f.name) > 0)) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
60
|
.Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
72
61
|
return false,
|
|
73
62
|
.Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
@@ -96,9 +85,6 @@ test Actions {
|
|
|
96
85
|
const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
|
|
97
86
|
const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
|
|
98
87
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
99
|
-
const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
100
|
-
const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
101
|
-
const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
102
88
|
|
|
103
89
|
try expect(a.eql(a));
|
|
104
90
|
try expect(!a.eql(b));
|
|
@@ -111,10 +97,22 @@ test Actions {
|
|
|
111
97
|
try expect(b.matches(a));
|
|
112
98
|
try expect(!a.matches(c));
|
|
113
99
|
try expect(!c.matches(a));
|
|
114
|
-
try expect(d.matches(e));
|
|
115
|
-
try expect(!d.matches(f));
|
|
116
100
|
}
|
|
117
101
|
|
|
102
|
+
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
103
|
+
pub const Observation = enum { started, continuing, ended };
|
|
104
|
+
/// An extension of Observation to account for "hidden" confusion.
|
|
105
|
+
pub const Confusion = enum {
|
|
106
|
+
started,
|
|
107
|
+
continuing,
|
|
108
|
+
ended,
|
|
109
|
+
/// An indicator that an existing confusion duration has been overwritten by a new confusion
|
|
110
|
+
/// duration due to a Thrashing move ending. This is not immediately observable to an opponent,
|
|
111
|
+
/// to compute probabilities from an opponent's point of view based solely on public information
|
|
112
|
+
/// extra work must be done to correct for the information leak
|
|
113
|
+
overwritten,
|
|
114
|
+
};
|
|
115
|
+
|
|
118
116
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
119
117
|
/// single player.
|
|
120
118
|
pub const Action = packed struct(u64) {
|
|
@@ -139,12 +137,25 @@ pub const Action = packed struct(u64) {
|
|
|
139
137
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
140
138
|
duration: u4 = 0,
|
|
141
139
|
|
|
142
|
-
///
|
|
143
|
-
|
|
140
|
+
/// TODO
|
|
141
|
+
sleep: Optional(Observation) = .None,
|
|
142
|
+
/// TODO
|
|
143
|
+
confusion: Optional(Confusion) = .None,
|
|
144
|
+
/// TODO
|
|
145
|
+
disable: Optional(Observation) = .None,
|
|
146
|
+
/// TODO
|
|
147
|
+
bide: Optional(Observation) = .None,
|
|
148
|
+
/// TODO
|
|
149
|
+
binding: Optional(Observation) = .None,
|
|
150
|
+
/// TODO
|
|
151
|
+
thrashing: Optional(Observation) = .None,
|
|
144
152
|
|
|
145
153
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
146
154
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
147
|
-
move_slot:
|
|
155
|
+
move_slot: u3 = 0,
|
|
156
|
+
|
|
157
|
+
/// TODO
|
|
158
|
+
pp: u4 = 0,
|
|
148
159
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
149
160
|
multi_hit: u4 = 0,
|
|
150
161
|
|
|
@@ -154,15 +165,8 @@ pub const Action = packed struct(u64) {
|
|
|
154
165
|
/// If not None, the Move to return for Rolls.metronome.
|
|
155
166
|
metronome: Move = .None,
|
|
156
167
|
|
|
157
|
-
pub const DURATIONS: u64 = 0x000000FFFF000000;
|
|
158
|
-
|
|
159
168
|
pub const Field = std.meta.FieldEnum(Action);
|
|
160
169
|
|
|
161
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
162
|
-
pub fn reset(self: *Action) void {
|
|
163
|
-
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
170
|
pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
|
|
167
171
|
_ = .{ f, o };
|
|
168
172
|
try fmt(a, w, false);
|
|
@@ -174,18 +178,6 @@ pub const Action = packed struct(u64) {
|
|
|
174
178
|
inline for (@typeInfo(Action).Struct.fields) |field| {
|
|
175
179
|
const val = @field(self, field.name);
|
|
176
180
|
switch (@typeInfo(@TypeOf(val))) {
|
|
177
|
-
.Struct => inline for (@typeInfo(@TypeOf(val)).Struct.fields) |f| {
|
|
178
|
-
const v = @field(val, f.name);
|
|
179
|
-
if (v != 0) {
|
|
180
|
-
if (printed) try writer.writeAll(", ");
|
|
181
|
-
if (shape) {
|
|
182
|
-
try writer.print("{s}:?", .{f.name});
|
|
183
|
-
} else {
|
|
184
|
-
try writer.print("{s}:{d}", .{ f.name, v });
|
|
185
|
-
}
|
|
186
|
-
printed = true;
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
181
|
.Enum => if (val != .None) {
|
|
190
182
|
if (printed) try writer.writeAll(", ");
|
|
191
183
|
if (shape) {
|
|
@@ -195,6 +187,13 @@ pub const Action = packed struct(u64) {
|
|
|
195
187
|
if (val == .false) "!" else "",
|
|
196
188
|
field.name,
|
|
197
189
|
});
|
|
190
|
+
} else if (@TypeOf(val) == Optional(Observation) or
|
|
191
|
+
@TypeOf(val) == Optional(Confusion))
|
|
192
|
+
{
|
|
193
|
+
try writer.print("{s}{s}", .{
|
|
194
|
+
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
195
|
+
field.name,
|
|
196
|
+
});
|
|
198
197
|
} else {
|
|
199
198
|
try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
|
|
200
199
|
}
|
|
@@ -216,43 +215,80 @@ pub const Action = packed struct(u64) {
|
|
|
216
215
|
}
|
|
217
216
|
};
|
|
218
217
|
|
|
219
|
-
test Action {
|
|
220
|
-
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
221
|
-
a.reset();
|
|
222
|
-
|
|
223
|
-
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
218
|
/// TODO
|
|
227
|
-
pub const Durations =
|
|
228
|
-
///
|
|
219
|
+
pub const Durations = struct {
|
|
220
|
+
/// TODO
|
|
229
221
|
p1: Duration = .{},
|
|
230
|
-
///
|
|
222
|
+
/// TODO
|
|
231
223
|
p2: Duration = .{},
|
|
232
224
|
|
|
233
|
-
|
|
234
|
-
|
|
225
|
+
/// TODO
|
|
226
|
+
pub fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Duration) {
|
|
227
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Durations);
|
|
228
|
+
return if (player == .P1) &self.p1 else &self.p2;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
pub fn format(
|
|
232
|
+
self: Durations,
|
|
233
|
+
comptime fmt: []const u8,
|
|
234
|
+
opts: std.fmt.FormatOptions,
|
|
235
|
+
writer: anytype,
|
|
236
|
+
) !void {
|
|
237
|
+
_ = .{ fmt, opts };
|
|
238
|
+
try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
|
|
235
239
|
}
|
|
236
240
|
};
|
|
237
241
|
|
|
238
|
-
///
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
242
|
-
sleep: u3 = 0,
|
|
243
|
-
/// The number of turns a Pokémon has been observed to be confused.
|
|
242
|
+
/// TODO
|
|
243
|
+
pub const Duration = struct {
|
|
244
|
+
sleeps: [6]u3 = .{0} ** 6,
|
|
244
245
|
confusion: u3 = 0,
|
|
245
|
-
/// The number of turns a Pokémon has been observed to be disabled.
|
|
246
246
|
disable: u4 = 0,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
/// The number of turns a Pokémon has been observed to be binding their opponent.
|
|
247
|
+
bide: u3 = 0,
|
|
248
|
+
thrashing: u3 = 0,
|
|
250
249
|
binding: u3 = 0,
|
|
251
250
|
|
|
252
|
-
pub
|
|
251
|
+
pub fn format(
|
|
252
|
+
self: Duration,
|
|
253
|
+
comptime fmt: []const u8,
|
|
254
|
+
opts: std.fmt.FormatOptions,
|
|
255
|
+
writer: anytype,
|
|
256
|
+
) !void {
|
|
257
|
+
_ = .{ fmt, opts };
|
|
258
|
+
try writer.writeByte('(');
|
|
259
|
+
var printed = false;
|
|
260
|
+
inline for (@typeInfo(Duration).Struct.fields) |field| {
|
|
261
|
+
const val = @field(self, field.name);
|
|
262
|
+
switch (@typeInfo(@TypeOf(val))) {
|
|
263
|
+
.Int => if (val != 0) {
|
|
264
|
+
if (printed) try writer.writeAll(", ");
|
|
265
|
+
try writer.print("{s}:{d}", .{ field.name, val });
|
|
266
|
+
printed = true;
|
|
267
|
+
},
|
|
268
|
+
.Array => {
|
|
269
|
+
var total: usize = 0;
|
|
270
|
+
for (val) |v| total += v;
|
|
271
|
+
if (total > 0) {
|
|
272
|
+
if (printed) try writer.writeAll(", ");
|
|
273
|
+
try writer.print("{s}:[", .{field.name});
|
|
274
|
+
for (val, 0..) |v, i| {
|
|
275
|
+
if (i != 0) try writer.writeByte(',');
|
|
276
|
+
try writer.print("{d}", .{v});
|
|
277
|
+
}
|
|
278
|
+
try writer.writeAll("]");
|
|
279
|
+
printed = true;
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
else => unreachable,
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
try writer.writeByte(')');
|
|
286
|
+
}
|
|
253
287
|
};
|
|
254
288
|
|
|
255
|
-
pub const Commit = enum { hit, miss, binding };
|
|
289
|
+
pub const Commit = enum { hit, miss, binding, err, glitch };
|
|
290
|
+
|
|
291
|
+
const uN = if (options.miss) u8 else u9;
|
|
256
292
|
|
|
257
293
|
/// Tracks chance actions and their associated probability during a Generation I battle update when
|
|
258
294
|
/// `options.chance` is enabled.
|
|
@@ -265,14 +301,8 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
265
301
|
/// The actions taken by a hypothetical "chance player" that convey information about which
|
|
266
302
|
/// RNG events were observed during a battle `update`.
|
|
267
303
|
actions: Actions = .{},
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
271
|
-
// switching and so we must store it here. The indices of these arrays correspond to the
|
|
272
|
-
// `order` field of a Side. This information could be stored in actions but size is a
|
|
273
|
-
// concern so this is tracked separately as actions only purports to track RNG events
|
|
274
|
-
// observed during a single `update` (not across updates).
|
|
275
|
-
sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
|
|
304
|
+
/// TODO
|
|
305
|
+
durations: Durations = .{},
|
|
276
306
|
|
|
277
307
|
// In many cases on both the cartridge and on Pokémon Showdown rolls are made even though
|
|
278
308
|
// later checks render their result irrelevant (missing when the target is actually immune,
|
|
@@ -290,16 +320,13 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
290
320
|
//
|
|
291
321
|
// Finally, because each player's actions are processed sequentially we only need a single
|
|
292
322
|
// shared structure to track this information (though it needs to be cleared appropriately)
|
|
293
|
-
pending: if (showdown) struct {
|
|
294
|
-
hit: bool = false,
|
|
295
|
-
hit_probablity: u8 = 0,
|
|
296
|
-
} else struct {
|
|
323
|
+
pending: if (showdown) struct {} else struct {
|
|
297
324
|
crit: bool = false,
|
|
298
325
|
crit_probablity: u8 = 0,
|
|
299
326
|
damage_roll: u8 = 0,
|
|
300
327
|
hit: bool = false,
|
|
301
|
-
hit_probablity:
|
|
302
|
-
|
|
328
|
+
hit_probablity: uN = 0,
|
|
329
|
+
glitch: bool = false,
|
|
303
330
|
} = .{},
|
|
304
331
|
|
|
305
332
|
/// Possible error returned by operations tracking chance probability.
|
|
@@ -307,86 +334,91 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
307
334
|
|
|
308
335
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
309
336
|
pub fn reset(self: *Self) void {
|
|
337
|
+
if (!enabled) return;
|
|
338
|
+
|
|
310
339
|
self.probability.reset();
|
|
311
|
-
self.actions.
|
|
340
|
+
self.actions = .{};
|
|
312
341
|
self.pending = .{};
|
|
313
342
|
}
|
|
314
343
|
|
|
315
344
|
pub fn commit(self: *Self, player: Player, kind: Commit) Error!void {
|
|
345
|
+
if (!enabled) return;
|
|
346
|
+
|
|
347
|
+
assert(!showdown);
|
|
348
|
+
|
|
349
|
+
if (kind == .glitch and !self.pending.glitch) return;
|
|
350
|
+
|
|
316
351
|
var action = self.actions.get(player);
|
|
317
352
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
action.duration = @intCast(self.pending.binding);
|
|
324
|
-
assert(action.durations.binding == 0);
|
|
325
|
-
action.durations.binding = 1;
|
|
326
|
-
}
|
|
327
|
-
if (kind == .binding) return;
|
|
328
|
-
} else assert(kind != .binding);
|
|
353
|
+
if (kind == .err and self.pending.crit_probablity != 0) {
|
|
354
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
355
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
329
358
|
|
|
330
359
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
331
|
-
// target is immune
|
|
360
|
+
// target is immune*/behind a sub/etc)
|
|
332
361
|
if (self.pending.hit_probablity != 0) {
|
|
333
362
|
try self.probability.update(self.pending.hit_probablity, 256);
|
|
334
363
|
action.hit = if (self.pending.hit) .true else .false;
|
|
335
364
|
}
|
|
336
365
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
// to
|
|
342
|
-
|
|
343
|
-
|
|
366
|
+
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
367
|
+
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
368
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't rol for
|
|
369
|
+
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
370
|
+
// passed to the critical hit helper).
|
|
371
|
+
//
|
|
372
|
+
// There is a special edge case with the division-by-zero freeze where we need to commit
|
|
373
|
+
// the status of critical hit roll the occured before the move missed to avoid missing
|
|
374
|
+
// out on the possibility that the alternative branch encountered an error
|
|
375
|
+
if (kind != .hit and !self.pending.glitch) {
|
|
376
|
+
return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
377
|
+
}
|
|
344
378
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
379
|
+
if (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
|
+
} else assert(!self.pending.glitch);
|
|
349
383
|
|
|
384
|
+
if (self.pending.damage_roll > 0) {
|
|
350
385
|
try self.probability.update(1, 39);
|
|
351
386
|
action.damage = self.pending.damage_roll;
|
|
352
387
|
}
|
|
353
388
|
}
|
|
354
389
|
|
|
355
|
-
pub fn
|
|
390
|
+
pub fn glitch(self: *Self) void {
|
|
356
391
|
if (!enabled) return;
|
|
357
392
|
|
|
358
|
-
|
|
393
|
+
assert(!showdown);
|
|
394
|
+
self.pending.glitch = true;
|
|
359
395
|
}
|
|
360
396
|
|
|
361
|
-
pub fn
|
|
397
|
+
pub fn clearPending(self: *Self) void {
|
|
362
398
|
if (!enabled) return;
|
|
363
399
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (status) action.durations.sleep = 0;
|
|
367
|
-
action.durations.confusion = 0;
|
|
368
|
-
action.durations.disable = 0;
|
|
369
|
-
} else {
|
|
370
|
-
action.durations.attacking = 0;
|
|
371
|
-
action.durations.binding = 0;
|
|
372
|
-
}
|
|
400
|
+
assert(!showdown);
|
|
401
|
+
self.pending = .{};
|
|
373
402
|
}
|
|
374
403
|
|
|
375
|
-
pub fn switched(self: *Self, player: Player,
|
|
404
|
+
pub fn switched(self: *Self, player: Player, slot: u8) void {
|
|
376
405
|
if (!enabled) return;
|
|
377
406
|
|
|
378
|
-
assert(
|
|
379
|
-
assert(out >= 1 and out <= 6);
|
|
407
|
+
assert(slot >= 1 and slot <= 6);
|
|
380
408
|
|
|
381
|
-
var
|
|
409
|
+
var d = self.durations.get(player);
|
|
382
410
|
|
|
383
|
-
const
|
|
384
|
-
|
|
411
|
+
const out = d.sleeps[0];
|
|
412
|
+
d.sleeps[0] = d.sleeps[slot - 1];
|
|
413
|
+
d.sleeps[slot - 1] = out;
|
|
385
414
|
|
|
386
|
-
|
|
387
|
-
|
|
415
|
+
d.confusion = 0;
|
|
416
|
+
d.disable = 0;
|
|
417
|
+
d.bide = 0;
|
|
418
|
+
d.thrashing = 0;
|
|
419
|
+
d.binding = 0;
|
|
388
420
|
|
|
389
|
-
self.
|
|
421
|
+
self.durations.get(player.foe()).binding = 0;
|
|
390
422
|
}
|
|
391
423
|
|
|
392
424
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
@@ -397,12 +429,17 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
397
429
|
self.actions.p2.speed_tie = self.actions.p1.speed_tie;
|
|
398
430
|
}
|
|
399
431
|
|
|
400
|
-
pub fn hit(self: *Self, ok: bool, accuracy:
|
|
432
|
+
pub fn hit(self: *Self, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
401
433
|
if (!enabled) return;
|
|
402
434
|
|
|
403
435
|
const p = if (ok) accuracy else @as(u8, @intCast(256 - @as(u9, accuracy)));
|
|
404
|
-
|
|
405
|
-
|
|
436
|
+
if (showdown) {
|
|
437
|
+
try self.probability.update(p, 256);
|
|
438
|
+
self.actions.get(player).hit = if (ok) .true else .false;
|
|
439
|
+
} else {
|
|
440
|
+
self.pending.hit = ok;
|
|
441
|
+
self.pending.hit_probablity = p;
|
|
442
|
+
}
|
|
406
443
|
}
|
|
407
444
|
|
|
408
445
|
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -480,108 +517,182 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
480
517
|
self.actions.get(player).multi_hit = n;
|
|
481
518
|
}
|
|
482
519
|
|
|
483
|
-
pub fn duration(
|
|
520
|
+
pub fn duration(self: *Self, player: Player, turns: u4) void {
|
|
521
|
+
if (!enabled) return;
|
|
522
|
+
|
|
523
|
+
self.actions.get(player).duration = if (options.key) 2 else turns;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
pub fn observe(
|
|
484
527
|
self: *Self,
|
|
485
|
-
comptime field:
|
|
528
|
+
comptime field: Action.Field,
|
|
486
529
|
player: Player,
|
|
487
|
-
|
|
488
|
-
turns: u4,
|
|
530
|
+
obs: Optional(Confusion),
|
|
489
531
|
) void {
|
|
490
532
|
if (!enabled) return;
|
|
491
533
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
(
|
|
498
|
-
|
|
499
|
-
|
|
534
|
+
var a = self.actions.get(player);
|
|
535
|
+
const val = @field(a, @tagName(field));
|
|
536
|
+
switch (obs) {
|
|
537
|
+
.overwritten => {
|
|
538
|
+
assert(field == .confusion);
|
|
539
|
+
assert(val == .started or val == .continuing);
|
|
540
|
+
assert(a.duration > 0);
|
|
541
|
+
a.confusion = obs;
|
|
542
|
+
self.durations.get(player).confusion = 1;
|
|
543
|
+
},
|
|
544
|
+
.None => {
|
|
545
|
+
@field(a, @tagName(field)) = .None;
|
|
546
|
+
if (field == .sleep) {
|
|
547
|
+
self.durations.get(player).sleeps[0] = 0;
|
|
548
|
+
} else {
|
|
549
|
+
@field(self.durations.get(player), @tagName(field)) = 0;
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
.started => {
|
|
553
|
+
assert(val == .None or val == .ended);
|
|
554
|
+
assert(switch (field) {
|
|
555
|
+
.confusion => a.duration > 0 or self.actions.get(player.foe()).duration > 0,
|
|
556
|
+
.sleep, .disable => self.actions.get(player.foe()).duration > 0,
|
|
557
|
+
else => a.duration > 0,
|
|
558
|
+
});
|
|
559
|
+
@field(a, @tagName(field)) = .started;
|
|
560
|
+
if (field == .sleep) {
|
|
561
|
+
self.durations.get(player).sleeps[0] = 1;
|
|
562
|
+
} else {
|
|
563
|
+
@field(self.durations.get(player), @tagName(field)) = 1;
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
else => unreachable,
|
|
500
567
|
}
|
|
501
568
|
}
|
|
502
569
|
|
|
503
|
-
pub fn sleep(self: *Self, player: Player,
|
|
570
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
504
571
|
if (!enabled) return;
|
|
505
572
|
|
|
506
|
-
var
|
|
507
|
-
|
|
508
|
-
|
|
573
|
+
var a = self.actions.get(player);
|
|
574
|
+
assert(a.sleep == .None or a.sleep == .started);
|
|
575
|
+
a.sleep = obs;
|
|
576
|
+
|
|
577
|
+
var d = self.durations.get(player);
|
|
578
|
+
const n = d.sleeps[0];
|
|
579
|
+
|
|
580
|
+
if (obs == .None) {
|
|
581
|
+
d.sleeps[0] = 0;
|
|
582
|
+
} else if (obs == .ended) {
|
|
509
583
|
assert(n >= 1 and n <= 7);
|
|
510
584
|
if (n != 7) try self.probability.update(1, 8 - @as(u4, n));
|
|
511
|
-
|
|
585
|
+
d.sleeps[0] = 0;
|
|
512
586
|
} else {
|
|
587
|
+
assert(obs == .continuing);
|
|
513
588
|
assert(n >= 1 and n < 7);
|
|
514
589
|
try self.probability.update(8 - @as(u4, n) - 1, 8 - @as(u4, n));
|
|
515
|
-
|
|
590
|
+
d.sleeps[0] += 1;
|
|
516
591
|
}
|
|
517
592
|
}
|
|
518
593
|
|
|
519
|
-
pub fn confusion(self: *Self, player: Player,
|
|
594
|
+
pub fn confusion(self: *Self, player: Player, obs: Optional(Confusion)) Error!void {
|
|
520
595
|
if (!enabled) return;
|
|
521
596
|
|
|
522
|
-
var
|
|
523
|
-
|
|
524
|
-
|
|
597
|
+
var a = self.actions.get(player);
|
|
598
|
+
assert(a.confusion == .None or a.confusion == .started);
|
|
599
|
+
a.confusion = obs;
|
|
600
|
+
|
|
601
|
+
var d = self.durations.get(player);
|
|
602
|
+
const n = d.confusion;
|
|
603
|
+
|
|
604
|
+
if (obs == .ended) {
|
|
525
605
|
assert(n >= 2 and n <= 5);
|
|
526
606
|
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
527
|
-
|
|
607
|
+
d.confusion = 0;
|
|
528
608
|
} else {
|
|
609
|
+
assert(obs == .continuing);
|
|
529
610
|
assert(n >= 1 and n < 5);
|
|
530
611
|
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
531
|
-
|
|
612
|
+
d.confusion += 1;
|
|
532
613
|
}
|
|
533
614
|
}
|
|
534
615
|
|
|
535
|
-
pub fn disable(self: *Self, player: Player,
|
|
616
|
+
pub fn disable(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
536
617
|
if (!enabled) return;
|
|
537
618
|
|
|
538
|
-
var
|
|
539
|
-
|
|
540
|
-
|
|
619
|
+
var a = self.actions.get(player);
|
|
620
|
+
assert(a.disable == .None or a.disable == .started);
|
|
621
|
+
a.disable = obs;
|
|
622
|
+
|
|
623
|
+
var d = self.durations.get(player);
|
|
624
|
+
const n = d.disable;
|
|
625
|
+
|
|
626
|
+
if (obs == .ended) {
|
|
541
627
|
assert(n >= 1 and n <= 8);
|
|
542
628
|
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
543
|
-
|
|
629
|
+
d.disable = 0;
|
|
544
630
|
} else {
|
|
631
|
+
assert(obs == .continuing);
|
|
545
632
|
assert(n >= 1 and n < 8);
|
|
546
633
|
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
547
|
-
|
|
634
|
+
d.disable += 1;
|
|
548
635
|
}
|
|
549
636
|
}
|
|
550
637
|
|
|
551
|
-
pub fn
|
|
638
|
+
pub fn bide(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
639
|
+
return self.attacking(.bide, player, obs);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
pub fn thrashing(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
643
|
+
return self.attacking(.thrashing, player, obs);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
fn attacking(
|
|
647
|
+
self: *Self,
|
|
648
|
+
comptime field: Action.Field,
|
|
649
|
+
player: Player,
|
|
650
|
+
obs: Optional(Observation),
|
|
651
|
+
) Error!void {
|
|
552
652
|
if (!enabled) return;
|
|
553
653
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
654
|
+
const f = @tagName(field);
|
|
655
|
+
var a = self.actions.get(player);
|
|
656
|
+
assert(@field(a, f) == .None or @field(a, f) == .started);
|
|
657
|
+
@field(a, f) = obs;
|
|
658
|
+
|
|
659
|
+
var d = self.durations.get(player);
|
|
660
|
+
const n = @field(d, f);
|
|
661
|
+
if (obs == .ended) {
|
|
557
662
|
assert(n >= 2 and n <= 3);
|
|
558
663
|
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
559
|
-
|
|
664
|
+
@field(d, f) = 0;
|
|
560
665
|
} else {
|
|
666
|
+
assert(obs == .continuing);
|
|
561
667
|
assert(n >= 1 and n < 3);
|
|
562
668
|
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
563
|
-
|
|
669
|
+
@field(d, f) += 1;
|
|
564
670
|
}
|
|
565
671
|
}
|
|
566
672
|
|
|
567
|
-
pub fn binding(self: *Self, player: Player,
|
|
673
|
+
pub fn binding(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
568
674
|
if (!enabled) return;
|
|
569
675
|
|
|
570
|
-
var
|
|
571
|
-
|
|
676
|
+
var a = self.actions.get(player);
|
|
677
|
+
assert(a.binding == .None or a.binding == .started);
|
|
678
|
+
a.binding = obs;
|
|
679
|
+
|
|
680
|
+
var d = self.durations.get(player);
|
|
681
|
+
const n = d.binding;
|
|
572
682
|
|
|
573
683
|
assert(n > 0);
|
|
574
684
|
const p: u4 = if (n < 3) 3 else 1;
|
|
575
685
|
const q: u4 = if (n < 3) 8 - ((n - 1) * p) else 2;
|
|
576
686
|
|
|
577
|
-
if (
|
|
687
|
+
if (obs == .ended) {
|
|
578
688
|
assert(n >= 1 and n <= 4);
|
|
579
689
|
if (n != 4) try self.probability.update(p, q);
|
|
580
|
-
|
|
690
|
+
d.binding = 0;
|
|
581
691
|
} else {
|
|
692
|
+
assert(obs == .continuing);
|
|
582
693
|
assert(n >= 1 and n < 4);
|
|
583
694
|
try self.probability.update(q - p, q);
|
|
584
|
-
|
|
695
|
+
d.binding += 1;
|
|
585
696
|
}
|
|
586
697
|
}
|
|
587
698
|
|
|
@@ -598,6 +709,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
598
709
|
try self.probability.update(1, Move.METRONOME.len);
|
|
599
710
|
self.actions.get(player).metronome = move;
|
|
600
711
|
}
|
|
712
|
+
|
|
713
|
+
pub fn incrementPP(self: *Self, player: Player, n: u4) bool {
|
|
714
|
+
if (!enabled) return true;
|
|
715
|
+
|
|
716
|
+
var a = self.actions.get(player);
|
|
717
|
+
if (n > 1 and a.pp == n) return false;
|
|
718
|
+
if (a.metronome != .None) a.pp += n;
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
601
721
|
};
|
|
602
722
|
}
|
|
603
723
|
|
|
@@ -620,19 +740,25 @@ test "Chance.speedTie" {
|
|
|
620
740
|
test "Chance.hit" {
|
|
621
741
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
622
742
|
|
|
623
|
-
chance.hit(true, 229);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
743
|
+
try chance.hit(.P1, true, 229);
|
|
744
|
+
if (!showdown) {
|
|
745
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
746
|
+
try expectValue(Optional(bool).None, chance.actions.p1.hit);
|
|
747
|
+
|
|
748
|
+
try chance.commit(.P1, .hit);
|
|
749
|
+
}
|
|
627
750
|
try expectValue(Optional(bool).true, chance.actions.p1.hit);
|
|
628
751
|
try expectProbability(&chance.probability, 229, 256);
|
|
629
752
|
|
|
630
753
|
chance.reset();
|
|
631
754
|
|
|
632
|
-
chance.hit(false, 255);
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
755
|
+
try chance.hit(.P2, false, 255);
|
|
756
|
+
if (!showdown) {
|
|
757
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
758
|
+
try expectValue(Optional(bool).None, chance.actions.p2.hit);
|
|
759
|
+
|
|
760
|
+
try chance.commit(.P2, .miss);
|
|
761
|
+
}
|
|
636
762
|
try expectValue(Optional(bool).false, chance.actions.p2.hit);
|
|
637
763
|
try expectProbability(&chance.probability, 1, 256);
|
|
638
764
|
}
|
|
@@ -648,26 +774,18 @@ test "Chance.criticalHit" {
|
|
|
648
774
|
try expectProbability(&chance.probability, 1, 1);
|
|
649
775
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
650
776
|
|
|
651
|
-
try chance.
|
|
652
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
653
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
777
|
+
try chance.hit(.P1, true, 128);
|
|
654
778
|
|
|
655
|
-
chance.
|
|
656
|
-
|
|
657
|
-
try chance.commit(.P1, .hit);
|
|
658
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
779
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
659
780
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
660
781
|
|
|
661
|
-
chance.probability.reset();
|
|
662
782
|
try chance.damage(.P1, 217);
|
|
663
783
|
|
|
664
|
-
try chance.
|
|
665
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
784
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
666
785
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
667
786
|
|
|
668
|
-
chance.probability.reset();
|
|
669
|
-
|
|
670
787
|
try chance.commit(.P1, .hit);
|
|
788
|
+
|
|
671
789
|
try expectProbability(&chance.probability, 17, 19968); // (1/2) * (17/256) * (1/39)
|
|
672
790
|
try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
|
|
673
791
|
}
|
|
@@ -682,20 +800,14 @@ test "Chance.criticalHit" {
|
|
|
682
800
|
try expectProbability(&chance.probability, 1, 1);
|
|
683
801
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
684
802
|
|
|
685
|
-
try chance.
|
|
686
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
687
|
-
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
803
|
+
try chance.hit(.P1, true, 128);
|
|
688
804
|
|
|
689
|
-
chance.
|
|
690
|
-
|
|
691
|
-
try chance.commit(.P1, .hit);
|
|
692
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
805
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
693
806
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
694
807
|
|
|
695
|
-
chance.probability.reset();
|
|
696
808
|
try chance.damage(.P1, 217);
|
|
697
|
-
|
|
698
809
|
try chance.commit(.P1, .hit);
|
|
810
|
+
|
|
699
811
|
try expectProbability(&chance.probability, 251, 19968); // (1/2) * (251/256) * (1/39)
|
|
700
812
|
try expectValue(Optional(bool).false, chance.actions.p1.critical_hit);
|
|
701
813
|
}
|
|
@@ -712,11 +824,8 @@ test "Chance.damage" {
|
|
|
712
824
|
try expectProbability(&chance.probability, 1, 1);
|
|
713
825
|
try expectValue(0, chance.actions.p1.damage);
|
|
714
826
|
|
|
715
|
-
try chance.commit(.P1, .miss);
|
|
716
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
717
|
-
try expectValue(0, chance.actions.p1.damage);
|
|
718
|
-
|
|
719
827
|
try chance.commit(.P1, .hit);
|
|
828
|
+
|
|
720
829
|
try expectProbability(&chance.probability, 1, 39);
|
|
721
830
|
try expectValue(217, chance.actions.p1.damage);
|
|
722
831
|
}
|
|
@@ -796,38 +905,31 @@ test "Chance.multiHit" {
|
|
|
796
905
|
test "Chance.duration" {
|
|
797
906
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
798
907
|
|
|
799
|
-
chance.duration(.
|
|
908
|
+
chance.duration(.P1, 2);
|
|
800
909
|
try expectValue(2, chance.actions.p1.duration);
|
|
801
|
-
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
802
910
|
|
|
803
911
|
chance.reset();
|
|
804
912
|
|
|
805
|
-
chance.duration(.
|
|
806
|
-
if (!showdown) {
|
|
807
|
-
try expectValue(0, chance.actions.p2.duration);
|
|
808
|
-
try expectValue(0, chance.actions.p2.durations.binding);
|
|
809
|
-
try chance.commit(.P2, .hit);
|
|
810
|
-
}
|
|
913
|
+
chance.duration(.P2, 4);
|
|
811
914
|
try expectValue(4, chance.actions.p2.duration);
|
|
812
|
-
try expectValue(1, chance.actions.p2.durations.binding);
|
|
813
915
|
}
|
|
814
916
|
|
|
815
917
|
test "Chance.sleep" {
|
|
816
918
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
817
919
|
|
|
818
920
|
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
819
|
-
chance.
|
|
820
|
-
try chance.sleep(.P1,
|
|
921
|
+
chance.durations.p1.sleeps[0] = @intCast(i);
|
|
922
|
+
try chance.sleep(.P1, .ended);
|
|
821
923
|
try expectProbability(&chance.probability, 1, d);
|
|
822
|
-
try expectValue(0, chance.
|
|
924
|
+
try expectValue(0, chance.durations.p1.sleeps[0]);
|
|
823
925
|
|
|
824
926
|
chance.reset();
|
|
825
927
|
|
|
826
928
|
if (i < 7) {
|
|
827
|
-
chance.
|
|
828
|
-
try chance.sleep(.P1,
|
|
929
|
+
chance.durations.p1.sleeps[0] = @intCast(i);
|
|
930
|
+
try chance.sleep(.P1, .continuing);
|
|
829
931
|
try expectProbability(&chance.probability, d - 1, d);
|
|
830
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
932
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.sleeps[0]);
|
|
831
933
|
|
|
832
934
|
chance.reset();
|
|
833
935
|
}
|
|
@@ -839,19 +941,19 @@ test "Chance.confusion" {
|
|
|
839
941
|
|
|
840
942
|
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
841
943
|
if (i > 1) {
|
|
842
|
-
chance.
|
|
843
|
-
try chance.confusion(.P2,
|
|
944
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
945
|
+
try chance.confusion(.P2, .ended);
|
|
844
946
|
try expectProbability(&chance.probability, 1, d);
|
|
845
|
-
try expectValue(0, chance.
|
|
947
|
+
try expectValue(0, chance.durations.p2.confusion);
|
|
846
948
|
|
|
847
949
|
chance.reset();
|
|
848
950
|
}
|
|
849
951
|
|
|
850
952
|
if (i < 5) {
|
|
851
|
-
chance.
|
|
852
|
-
try chance.confusion(.P2,
|
|
953
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
954
|
+
try chance.confusion(.P2, .continuing);
|
|
853
955
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
854
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
956
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.confusion);
|
|
855
957
|
|
|
856
958
|
chance.reset();
|
|
857
959
|
}
|
|
@@ -862,18 +964,18 @@ test "Chance.disable" {
|
|
|
862
964
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
863
965
|
|
|
864
966
|
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
865
|
-
chance.
|
|
866
|
-
try chance.disable(.P1,
|
|
967
|
+
chance.durations.p1.disable = @intCast(i);
|
|
968
|
+
try chance.disable(.P1, .ended);
|
|
867
969
|
try expectProbability(&chance.probability, 1, d);
|
|
868
|
-
try expectValue(0, chance.
|
|
970
|
+
try expectValue(0, chance.durations.p1.disable);
|
|
869
971
|
|
|
870
972
|
chance.reset();
|
|
871
973
|
|
|
872
974
|
if (i < 8) {
|
|
873
|
-
chance.
|
|
874
|
-
try chance.disable(.P1,
|
|
975
|
+
chance.durations.p1.disable = @intCast(i);
|
|
976
|
+
try chance.disable(.P1, .continuing);
|
|
875
977
|
try expectProbability(&chance.probability, d - 1, d);
|
|
876
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.
|
|
978
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.durations.p1.disable);
|
|
877
979
|
|
|
878
980
|
chance.reset();
|
|
879
981
|
}
|
|
@@ -883,23 +985,25 @@ test "Chance.disable" {
|
|
|
883
985
|
test "Chance.attacking" {
|
|
884
986
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
885
987
|
|
|
886
|
-
for (
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
988
|
+
inline for (.{ .bide, .thrashing }) |f| {
|
|
989
|
+
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
990
|
+
if (i > 1) {
|
|
991
|
+
@field(chance.durations.p2, @tagName(f)) = @intCast(i);
|
|
992
|
+
try chance.attacking(f, .P2, .ended);
|
|
993
|
+
try expectProbability(&chance.probability, 1, d);
|
|
994
|
+
try expectValue(0, @field(chance.durations.p2, @tagName(f)));
|
|
892
995
|
|
|
893
|
-
|
|
894
|
-
|
|
996
|
+
chance.reset();
|
|
997
|
+
}
|
|
895
998
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
999
|
+
if (i < 3) {
|
|
1000
|
+
@field(chance.durations.p2, @tagName(f)) = @intCast(i);
|
|
1001
|
+
try chance.attacking(f, .P2, .continuing);
|
|
1002
|
+
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1003
|
+
try expectValue(@as(u3, @intCast(i)) + 1, @field(chance.durations.p2, @tagName(f)));
|
|
901
1004
|
|
|
902
|
-
|
|
1005
|
+
chance.reset();
|
|
1006
|
+
}
|
|
903
1007
|
}
|
|
904
1008
|
}
|
|
905
1009
|
}
|
|
@@ -911,18 +1015,18 @@ test "Chance.binding" {
|
|
|
911
1015
|
const qs = [_]u8{ 8, 5, 2, 1 };
|
|
912
1016
|
|
|
913
1017
|
for (ps, qs, 1..5) |p, q, i| {
|
|
914
|
-
chance.
|
|
915
|
-
try chance.binding(.P1,
|
|
1018
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1019
|
+
try chance.binding(.P1, .ended);
|
|
916
1020
|
try expectProbability(&chance.probability, p, q);
|
|
917
|
-
try expectValue(0, chance.
|
|
1021
|
+
try expectValue(0, chance.durations.p1.binding);
|
|
918
1022
|
|
|
919
1023
|
chance.reset();
|
|
920
1024
|
|
|
921
1025
|
if (i < 4) {
|
|
922
|
-
chance.
|
|
923
|
-
try chance.binding(.P1,
|
|
1026
|
+
chance.durations.p1.binding = @intCast(i);
|
|
1027
|
+
try chance.binding(.P1, .continuing);
|
|
924
1028
|
try expectProbability(&chance.probability, q - p, q);
|
|
925
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
1029
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.binding);
|
|
926
1030
|
|
|
927
1031
|
chance.reset();
|
|
928
1032
|
}
|
|
@@ -972,24 +1076,24 @@ const Null = struct {
|
|
|
972
1076
|
_ = .{ self, player, kind };
|
|
973
1077
|
}
|
|
974
1078
|
|
|
975
|
-
pub fn
|
|
1079
|
+
pub fn glitch(self: Null) void {
|
|
976
1080
|
_ = .{self};
|
|
977
1081
|
}
|
|
978
1082
|
|
|
979
|
-
pub fn
|
|
980
|
-
_ = .{
|
|
1083
|
+
pub fn clearPending(self: Null) void {
|
|
1084
|
+
_ = .{self};
|
|
981
1085
|
}
|
|
982
1086
|
|
|
983
|
-
pub fn switched(self: Null, player: Player,
|
|
984
|
-
_ = .{ self, player,
|
|
1087
|
+
pub fn switched(self: Null, player: Player, slot: u8) void {
|
|
1088
|
+
_ = .{ self, player, slot };
|
|
985
1089
|
}
|
|
986
1090
|
|
|
987
1091
|
pub fn speedTie(self: Null, p1: bool) Error!void {
|
|
988
1092
|
_ = .{ self, p1 };
|
|
989
1093
|
}
|
|
990
1094
|
|
|
991
|
-
pub fn hit(self: Null, ok: bool, accuracy:
|
|
992
|
-
_ = .{ self, ok, accuracy };
|
|
1095
|
+
pub fn hit(self: Null, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
1096
|
+
_ = .{ self, player, ok, accuracy };
|
|
993
1097
|
}
|
|
994
1098
|
|
|
995
1099
|
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -1020,34 +1124,41 @@ const Null = struct {
|
|
|
1020
1124
|
_ = .{ self, player, n };
|
|
1021
1125
|
}
|
|
1022
1126
|
|
|
1023
|
-
pub fn duration(
|
|
1127
|
+
pub fn duration(self: Null, player: Player, turns: u4) void {
|
|
1128
|
+
_ = .{ self, player, turns };
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
pub fn observe(
|
|
1024
1132
|
self: Null,
|
|
1025
|
-
comptime field:
|
|
1133
|
+
comptime field: Action.Field,
|
|
1026
1134
|
player: Player,
|
|
1027
|
-
|
|
1028
|
-
turns: u4,
|
|
1135
|
+
obs: Optional(Confusion),
|
|
1029
1136
|
) void {
|
|
1030
|
-
_ = .{ self, field, player,
|
|
1137
|
+
_ = .{ self, field, player, obs };
|
|
1031
1138
|
}
|
|
1032
1139
|
|
|
1033
|
-
pub fn sleep(self: Null, player: Player,
|
|
1034
|
-
_ = .{ self, player,
|
|
1140
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1141
|
+
_ = .{ self, player, obs };
|
|
1035
1142
|
}
|
|
1036
1143
|
|
|
1037
|
-
pub fn
|
|
1038
|
-
_ = .{ self, player,
|
|
1144
|
+
pub fn confusion(self: Null, player: Player, obs: Optional(Confusion)) Error!void {
|
|
1145
|
+
_ = .{ self, player, obs };
|
|
1039
1146
|
}
|
|
1040
1147
|
|
|
1041
|
-
pub fn
|
|
1042
|
-
_ = .{ self, player,
|
|
1148
|
+
pub fn disable(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1149
|
+
_ = .{ self, player, obs };
|
|
1043
1150
|
}
|
|
1044
1151
|
|
|
1045
|
-
pub fn
|
|
1046
|
-
_ = .{ self, player,
|
|
1152
|
+
pub fn bide(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1153
|
+
_ = .{ self, player, obs };
|
|
1047
1154
|
}
|
|
1048
1155
|
|
|
1049
|
-
pub fn
|
|
1050
|
-
_ = .{ self, player,
|
|
1156
|
+
pub fn thrashing(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1157
|
+
_ = .{ self, player, obs };
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
pub fn binding(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1161
|
+
_ = .{ self, player, obs };
|
|
1051
1162
|
}
|
|
1052
1163
|
|
|
1053
1164
|
pub fn psywave(self: Null, player: Player, power: u8, max: u8) Error!void {
|
|
@@ -1057,4 +1168,9 @@ const Null = struct {
|
|
|
1057
1168
|
pub fn metronome(self: Null, player: Player, move: Move) Error!void {
|
|
1058
1169
|
_ = .{ self, player, move };
|
|
1059
1170
|
}
|
|
1171
|
+
|
|
1172
|
+
pub fn incrementPP(self: Null, player: Player, n: u4) bool {
|
|
1173
|
+
_ = .{ self, player, n };
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1060
1176
|
};
|