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