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