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