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