@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/gen2/chance.zig
CHANGED
|
@@ -8,9 +8,9 @@ const expectEqual = std.testing.expectEqual;
|
|
|
8
8
|
|
|
9
9
|
const options = @import("../common/options.zig");
|
|
10
10
|
const rational = @import("../common/rational.zig");
|
|
11
|
-
const rng = @import("../common/rng.zig");
|
|
12
11
|
const util = @import("../common/util.zig");
|
|
13
12
|
|
|
13
|
+
const Array = @import("../common/array.zig").Array;
|
|
14
14
|
const Player = @import("../common/data.zig").Player;
|
|
15
15
|
const Optional = @import("../common/optional.zig").Optional;
|
|
16
16
|
|
|
@@ -19,13 +19,18 @@ const data = @import("data.zig");
|
|
|
19
19
|
const enabled = options.chance;
|
|
20
20
|
const showdown = options.showdown;
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const PointerType = util.PointerType;
|
|
23
|
+
const isPointerTo = util.isPointerTo;
|
|
23
24
|
|
|
24
25
|
const Move = data.Move;
|
|
25
26
|
const Type = data.Type;
|
|
26
27
|
const Effectiveness = data.Effectiveness;
|
|
27
28
|
const TriAttack = data.TriAttack;
|
|
28
29
|
|
|
30
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
31
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
32
|
+
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
33
|
+
|
|
29
34
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
30
35
|
/// were observed during a Generation II battle `update`. This can additionally be provided as input
|
|
31
36
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -37,17 +42,11 @@ pub const Actions = extern struct {
|
|
|
37
42
|
p2: Action = .{},
|
|
38
43
|
|
|
39
44
|
/// Returns the `Action` for the given `player`.
|
|
40
|
-
pub fn get(self: anytype, player: Player)
|
|
41
|
-
assert(
|
|
45
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
|
|
46
|
+
assert(isPointerTo(self, Actions));
|
|
42
47
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
48
|
}
|
|
44
49
|
|
|
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
50
|
/// Returns true if `a` is equal to `b`.
|
|
52
51
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
53
52
|
return @as(u128, @bitCast(a.p1)) == @as(u128, @bitCast(b.p1)) and
|
|
@@ -58,24 +57,15 @@ pub const Actions = extern struct {
|
|
|
58
57
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
59
58
|
/// set to the same value).
|
|
60
59
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
61
|
-
inline for (@typeInfo(Actions)
|
|
62
|
-
inline for (@typeInfo(Action)
|
|
60
|
+
inline for (@field(@typeInfo(Actions), @tagName(Struct)).fields) |player| {
|
|
61
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
63
62
|
const a_val = @field(@field(a, player.name), field.name);
|
|
64
63
|
const b_val = @field(@field(b, player.name), field.name);
|
|
65
64
|
|
|
66
65
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
67
|
-
|
|
68
|
-
const a_val_f = @field(a_val, f.name);
|
|
69
|
-
const b_val_f = @field(b_val, f.name);
|
|
70
|
-
switch (@typeInfo(@TypeOf(a_val_f))) {
|
|
71
|
-
.Int => if ((a_val_f > 0) != (b_val_f > 0)) return false,
|
|
72
|
-
.Bool => if (a_val_f != b_val_f) return false,
|
|
73
|
-
else => unreachable,
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
.Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
66
|
+
Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
77
67
|
return false,
|
|
78
|
-
|
|
68
|
+
Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
79
69
|
else => unreachable,
|
|
80
70
|
}
|
|
81
71
|
}
|
|
@@ -98,15 +88,9 @@ pub const Actions = extern struct {
|
|
|
98
88
|
};
|
|
99
89
|
|
|
100
90
|
test Actions {
|
|
101
|
-
const a: Actions = .{ .p1 = .{ .hit = .true, .
|
|
102
|
-
const b: Actions = .{ .p1 = .{ .hit = .false, .
|
|
91
|
+
const a: Actions = .{ .p1 = .{ .hit = .true, .confused = .false, .damages = 245 } };
|
|
92
|
+
const b: Actions = .{ .p1 = .{ .hit = .false, .confused = .true, .damages = 246 } };
|
|
103
93
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
104
|
-
const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
105
|
-
const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
106
|
-
const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
107
|
-
const g: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1 } } };
|
|
108
|
-
const h: Actions =
|
|
109
|
-
.{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1, .thrash = true } } };
|
|
110
94
|
|
|
111
95
|
try expect(a.eql(a));
|
|
112
96
|
try expect(!a.eql(b));
|
|
@@ -119,17 +103,21 @@ test Actions {
|
|
|
119
103
|
try expect(b.matches(a));
|
|
120
104
|
try expect(!a.matches(c));
|
|
121
105
|
try expect(!c.matches(a));
|
|
122
|
-
try expect(d.matches(e));
|
|
123
|
-
try expect(!d.matches(f));
|
|
124
|
-
try expect(!g.matches(h));
|
|
125
|
-
try expect(h.matches(h));
|
|
126
106
|
}
|
|
127
107
|
|
|
108
|
+
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
109
|
+
pub const Observation = enum { started, continuing, ended };
|
|
110
|
+
|
|
111
|
+
pub const Damages = Array(5, u8);
|
|
112
|
+
pub const Criticals = Array(5, Optional(bool));
|
|
113
|
+
|
|
128
114
|
/// Information about the RNG that was observed during a Generation II battle `update` for a
|
|
129
115
|
/// single player.
|
|
130
116
|
pub const Action = packed struct(u128) {
|
|
131
|
-
///
|
|
132
|
-
|
|
117
|
+
/// TODO
|
|
118
|
+
damages: Damages.T = 0,
|
|
119
|
+
// TODO
|
|
120
|
+
critical_hits: Criticals.T = 0,
|
|
133
121
|
|
|
134
122
|
/// If not None, the Player to be returned by Rolls.speedTie.
|
|
135
123
|
speed_tie: Optional(Player) = .None,
|
|
@@ -137,50 +125,58 @@ pub const Action = packed struct(u128) {
|
|
|
137
125
|
quick_claw: Optional(bool) = .None,
|
|
138
126
|
/// If not None, the value to return for Rolls.hit.
|
|
139
127
|
hit: Optional(bool) = .None,
|
|
140
|
-
/// If not None, the value to be returned by Rolls.criticalHit.
|
|
141
|
-
critical_hit: Optional(bool) = .None,
|
|
142
|
-
|
|
143
128
|
/// If not None, the value to return for Rolls.confused.
|
|
144
129
|
confused: Optional(bool) = .None,
|
|
130
|
+
|
|
145
131
|
/// If not None, the value to return for Rolls.attract.
|
|
146
132
|
attract: Optional(bool) = .None,
|
|
147
133
|
/// If not None, the value to return for Rolls.paralyzed.
|
|
148
134
|
paralyzed: Optional(bool) = .None,
|
|
149
135
|
/// If not None, the value to return for Rolls.defrost.
|
|
150
136
|
defrost: Optional(bool) = .None,
|
|
151
|
-
|
|
152
137
|
/// If not None, the value to be returned for Rolls.secondaryChance.
|
|
153
138
|
secondary_chance: Optional(bool) = .None,
|
|
139
|
+
|
|
154
140
|
/// If not None, the value to be returned for Rolls.item (Focus Band / King's Rock).
|
|
155
141
|
item: Optional(bool) = .None,
|
|
156
142
|
/// If not None, the value to be returned for Rolls.protect.
|
|
157
143
|
protect: Optional(bool) = .None,
|
|
158
144
|
/// If not None, the value to return for Rolls.triAttack.
|
|
159
145
|
tri_attack: Optional(TriAttack) = .None,
|
|
146
|
+
/// If not 0, the value to return for Rolls.tripleKick.
|
|
147
|
+
triple_kick: u2 = 0,
|
|
160
148
|
|
|
161
149
|
/// If not 0, (present - 1) * 40 should be returned as the base power for Rolls.present.
|
|
162
150
|
present: u3 = 0,
|
|
163
151
|
/// If not 0, magnitude + 3 should be returned as the number for Rolls.magnitude.
|
|
164
152
|
magnitude: u3 = 0,
|
|
165
|
-
/// If not 0, the
|
|
166
|
-
triple_kick: u2 = 0,
|
|
167
|
-
|
|
168
|
-
/// If not 0, the amount of PP to deduct for Rolls.spite.
|
|
153
|
+
/// If not 0, the amount of PP to deduct for Rolls.spite.
|
|
169
154
|
spite: u3 = 0,
|
|
170
|
-
/// If not None, the value to return for Rolls.conversion2.
|
|
171
|
-
conversion_2: Optional(Type) = .None,
|
|
172
|
-
|
|
173
155
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
174
156
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
175
|
-
move_slot:
|
|
157
|
+
move_slot: u3 = 0,
|
|
176
158
|
/// If not 0, the party slot (1-6) to return in Rolls.forceSwitch. If present as an override,
|
|
177
159
|
/// invalid values (eg. due to empty party slots or fainted members) will be ignored.
|
|
178
|
-
force_switch:
|
|
179
|
-
|
|
160
|
+
force_switch: u3 = 0,
|
|
180
161
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
181
|
-
multi_hit:
|
|
162
|
+
multi_hit: u3 = 0,
|
|
163
|
+
|
|
164
|
+
sleep: Optional(Observation) = .None,
|
|
165
|
+
/// TODO
|
|
166
|
+
confusion: Optional(Observation) = .None,
|
|
167
|
+
/// TODO
|
|
168
|
+
disable: Optional(Observation) = .None,
|
|
169
|
+
/// TODO
|
|
170
|
+
attacking: Optional(Observation) = .None,
|
|
171
|
+
/// TODO
|
|
172
|
+
binding: Optional(Observation) = .None,
|
|
173
|
+
/// TODO
|
|
174
|
+
encore: Optional(Observation) = .None,
|
|
175
|
+
|
|
182
176
|
/// If not 0, the value to by one of the Rolls.*Duration rolls.
|
|
183
|
-
duration:
|
|
177
|
+
duration: u3 = 0,
|
|
178
|
+
/// If not None, the value to return for Rolls.conversion2.
|
|
179
|
+
conversion_2: Optional(Type) = .None,
|
|
184
180
|
|
|
185
181
|
/// If not 0, psywave should be returned as the damage roll for Rolls.psywave.
|
|
186
182
|
psywave: u8 = 0,
|
|
@@ -188,20 +184,8 @@ pub const Action = packed struct(u128) {
|
|
|
188
184
|
/// If not None, the Move to return for Rolls.metronome.
|
|
189
185
|
metronome: Move = .None,
|
|
190
186
|
|
|
191
|
-
_: u16 = 0,
|
|
192
|
-
|
|
193
|
-
/// Observed values of various durations. Does not influence future RNG calls. TODO
|
|
194
|
-
durations: Duration = .{},
|
|
195
|
-
|
|
196
|
-
pub const DURATIONS: u128 = 0xFFFFFFFF00000000_0000000000000000;
|
|
197
|
-
|
|
198
187
|
pub const Field = std.meta.FieldEnum(Action);
|
|
199
188
|
|
|
200
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
201
|
-
pub fn reset(self: *Action) void {
|
|
202
|
-
self.* = @bitCast(@as(u128, @bitCast(self.*)) & DURATIONS);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
189
|
pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
|
|
206
190
|
_ = .{ f, o };
|
|
207
191
|
try fmt(a, w, false);
|
|
@@ -210,22 +194,10 @@ pub const Action = packed struct(u128) {
|
|
|
210
194
|
pub fn fmt(self: Action, writer: anytype, shape: bool) !void {
|
|
211
195
|
try writer.writeByte('(');
|
|
212
196
|
var printed = false;
|
|
213
|
-
inline for (@typeInfo(Action)
|
|
197
|
+
inline for (@field(@typeInfo(Action), @tagName(Struct)).fields) |field| {
|
|
214
198
|
const val = @field(self, field.name);
|
|
215
199
|
switch (@typeInfo(@TypeOf(val))) {
|
|
216
|
-
|
|
217
|
-
const v = @field(val, f.name);
|
|
218
|
-
if (v != 0) {
|
|
219
|
-
if (printed) try writer.writeAll(", ");
|
|
220
|
-
if (shape) {
|
|
221
|
-
try writer.print("{s}:?", .{f.name});
|
|
222
|
-
} else {
|
|
223
|
-
try writer.print("{s}:{d}", .{ f.name, v });
|
|
224
|
-
}
|
|
225
|
-
printed = true;
|
|
226
|
-
}
|
|
227
|
-
},
|
|
228
|
-
.Enum => if (val != .None) {
|
|
200
|
+
Enum => if (val != .None) {
|
|
229
201
|
if (printed) try writer.writeAll(", ");
|
|
230
202
|
if (shape) {
|
|
231
203
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -239,7 +211,7 @@ pub const Action = packed struct(u128) {
|
|
|
239
211
|
}
|
|
240
212
|
printed = true;
|
|
241
213
|
},
|
|
242
|
-
|
|
214
|
+
Int => if (val != 0) {
|
|
243
215
|
if (printed) try writer.writeAll(", ");
|
|
244
216
|
if (shape) {
|
|
245
217
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -255,36 +227,6 @@ pub const Action = packed struct(u128) {
|
|
|
255
227
|
}
|
|
256
228
|
};
|
|
257
229
|
|
|
258
|
-
test Action {
|
|
259
|
-
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
260
|
-
a.reset();
|
|
261
|
-
|
|
262
|
-
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/// Observed values for various durations that need to be tracked in order to properly
|
|
266
|
-
/// deduplicate transitions with a primary key. TODO
|
|
267
|
-
pub const Duration = packed struct(u32) {
|
|
268
|
-
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
269
|
-
sleep: u3 = 0,
|
|
270
|
-
/// The number of turns a Pokémon has been observed to be confused.
|
|
271
|
-
confusion: u3 = 0,
|
|
272
|
-
/// TODO
|
|
273
|
-
thrash: bool = false,
|
|
274
|
-
/// The number of turns a Pokémon has been observed to be disabled.
|
|
275
|
-
disable: u4 = 0,
|
|
276
|
-
/// The number of turns a Pokémon has been observed to be attacking.
|
|
277
|
-
attacking: u3 = 0,
|
|
278
|
-
/// The number of turns a Pokémon has been observed to be binding their opponent.
|
|
279
|
-
binding: u3 = 0,
|
|
280
|
-
/// The number of turns a Pokémon has been observed to be encored.
|
|
281
|
-
encore: u3 = 0,
|
|
282
|
-
|
|
283
|
-
_: u12 = 0,
|
|
284
|
-
|
|
285
|
-
pub const Field = std.meta.FieldEnum(Duration);
|
|
286
|
-
};
|
|
287
|
-
|
|
288
230
|
/// Tracks chance actions and their associated probability during a Generation II battle update when
|
|
289
231
|
/// `options.chance` is enabled.
|
|
290
232
|
pub fn Chance(comptime Rational: type) type {
|
|
@@ -297,37 +239,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
297
239
|
/// RNG events were observed during a battle `update`.
|
|
298
240
|
actions: Actions = .{},
|
|
299
241
|
|
|
300
|
-
// Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
|
|
301
|
-
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
302
|
-
// switching and so we must store it here. The indices of these arrays correspond to the
|
|
303
|
-
// `order` field of a Side. This information could be stored in actions but size is a
|
|
304
|
-
// concern so this is tracked separately as actions only purports to track RNG events
|
|
305
|
-
// observed during a single `update` (not across updates).
|
|
306
|
-
sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
|
|
307
|
-
|
|
308
242
|
/// Possible error returned by operations tracking chance probability.
|
|
309
243
|
pub const Error = Rational.Error;
|
|
310
244
|
|
|
311
245
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
312
246
|
pub fn reset(self: *Self) void {
|
|
313
|
-
self.probability.reset();
|
|
314
|
-
self.actions.reset();
|
|
315
|
-
// TODO: self.pending = .{};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
pub fn switched(self: *Self, player: Player, in: u8, out: u8) void {
|
|
319
247
|
if (!enabled) return;
|
|
320
248
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
var action = self.actions.get(player);
|
|
325
|
-
|
|
326
|
-
const slp = action.durations.sleep;
|
|
327
|
-
action.durations = .{};
|
|
328
|
-
|
|
329
|
-
self.sleeps[@intFromEnum(player)][out - 1] = slp;
|
|
330
|
-
action.durations.sleep = @intCast(self.sleeps[@intFromEnum(player)][in - 1]);
|
|
249
|
+
self.probability.reset();
|
|
250
|
+
self.actions = .{};
|
|
331
251
|
}
|
|
332
252
|
|
|
333
253
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
@@ -353,19 +273,21 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
353
273
|
self.actions.get(player).hit = if (ok) .true else .false;
|
|
354
274
|
}
|
|
355
275
|
|
|
356
|
-
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
276
|
+
pub fn criticalHit(self: *Self, player: Player, i: u3, crit: bool, rate: u8) Error!void {
|
|
357
277
|
if (!enabled) return;
|
|
358
278
|
|
|
359
279
|
const n = if (crit) rate else @as(u8, @intCast(256 - @as(u9, rate)));
|
|
360
280
|
try self.probability.update(n, 256);
|
|
361
|
-
self.actions.get(player)
|
|
281
|
+
var a = self.actions.get(player);
|
|
282
|
+
a.critical_hits = Criticals.set(a.critical_hits, i, if (crit) .true else .false);
|
|
362
283
|
}
|
|
363
284
|
|
|
364
|
-
pub fn damage(self: *Self, player: Player, roll: u8) Error!void {
|
|
285
|
+
pub fn damage(self: *Self, player: Player, i: u3, roll: u8) Error!void {
|
|
365
286
|
if (!enabled) return;
|
|
366
287
|
|
|
367
288
|
try self.probability.update(1, 39);
|
|
368
|
-
self.actions.get(player)
|
|
289
|
+
var a = self.actions.get(player);
|
|
290
|
+
a.damages = Damages.set(a.damages, i, roll);
|
|
369
291
|
}
|
|
370
292
|
|
|
371
293
|
pub fn confused(self: *Self, player: Player, cfz: bool) Error!void {
|
|
@@ -517,122 +439,10 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
517
439
|
self.actions.get(player).multi_hit = n;
|
|
518
440
|
}
|
|
519
441
|
|
|
520
|
-
pub fn duration(
|
|
521
|
-
self: *Self,
|
|
522
|
-
comptime field: Duration.Field,
|
|
523
|
-
player: Player,
|
|
524
|
-
target: Player,
|
|
525
|
-
turns: u4,
|
|
526
|
-
) void {
|
|
527
|
-
if (!enabled) return;
|
|
528
|
-
|
|
529
|
-
var durations = &self.actions.get(target).durations;
|
|
530
|
-
assert(@field(durations, @tagName(field)) == 0 or
|
|
531
|
-
(field == .confusion and player == target));
|
|
532
|
-
@field(durations, @tagName(field)) = 1;
|
|
533
|
-
if (field == .confusion) durations.thrash = player == target;
|
|
534
|
-
self.actions.get(player).duration = if (options.key) 1 else turns;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
pub fn sleep(self: *Self, player: Player, turns: u4) Error!void {
|
|
538
|
-
if (!enabled) return;
|
|
539
|
-
|
|
540
|
-
var durations = &self.actions.get(player).durations;
|
|
541
|
-
const n = durations.sleep;
|
|
542
|
-
if (turns == 0) {
|
|
543
|
-
assert(n >= 1 and n <= 6);
|
|
544
|
-
if (n != 6) try self.probability.update(1, 7 - @as(u4, n));
|
|
545
|
-
durations.sleep = 0;
|
|
546
|
-
} else {
|
|
547
|
-
assert(n >= 1 and n < 6);
|
|
548
|
-
try self.probability.update(7 - @as(u4, n) - 1, 7 - @as(u4, n));
|
|
549
|
-
durations.sleep += 1;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
554
|
-
if (!enabled) return;
|
|
555
|
-
|
|
556
|
-
var durations = &self.actions.get(player).durations;
|
|
557
|
-
const n = durations.confusion;
|
|
558
|
-
const hi: u8 = if (durations.thrash) 3 else 5;
|
|
559
|
-
const p = hi + 1;
|
|
560
|
-
if (turns == 0) {
|
|
561
|
-
assert(n >= 2 and n <= hi);
|
|
562
|
-
if (n != hi) try self.probability.update(1, p - @as(u4, n));
|
|
563
|
-
durations.confusion = 0;
|
|
564
|
-
} else {
|
|
565
|
-
assert(n >= 1 and n < hi);
|
|
566
|
-
if (n > 1) try self.probability.update(p - @as(u4, n) - 1, p - @as(u4, n));
|
|
567
|
-
durations.confusion += 1;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
572
|
-
if (!enabled) return;
|
|
573
|
-
|
|
574
|
-
var durations = &self.actions.get(player).durations;
|
|
575
|
-
const n = durations.disable;
|
|
576
|
-
const hi = if (showdown) 5 else 8;
|
|
577
|
-
const p = hi + 1;
|
|
578
|
-
if (turns == 0) {
|
|
579
|
-
assert(n >= 2 and n <= hi);
|
|
580
|
-
if (n != hi) try self.probability.update(1, p - n);
|
|
581
|
-
durations.disable = 0;
|
|
582
|
-
} else {
|
|
583
|
-
assert(n >= 1 and n < hi);
|
|
584
|
-
if (n > 1) try self.probability.update(p - n - 1, p - n);
|
|
585
|
-
durations.disable += 1;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
442
|
+
pub fn duration(self: *Self, player: Player, turns: u3) void {
|
|
590
443
|
if (!enabled) return;
|
|
591
444
|
|
|
592
|
-
|
|
593
|
-
const n = durations.attacking;
|
|
594
|
-
if (turns == 0) {
|
|
595
|
-
assert(n >= 2 and n <= 3);
|
|
596
|
-
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
597
|
-
durations.attacking = 0;
|
|
598
|
-
} else {
|
|
599
|
-
assert(n >= 1 and n < 3);
|
|
600
|
-
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
601
|
-
durations.attacking += 1;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
pub fn binding(self: *Self, player: Player, turns: u4) Error!void {
|
|
606
|
-
if (!enabled) return;
|
|
607
|
-
|
|
608
|
-
var durations = &self.actions.get(player).durations;
|
|
609
|
-
const n = durations.binding;
|
|
610
|
-
if (turns == 0) {
|
|
611
|
-
assert(n >= 2 and n <= 5);
|
|
612
|
-
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
613
|
-
durations.binding = 0;
|
|
614
|
-
} else {
|
|
615
|
-
assert(n >= 1 and n < 5);
|
|
616
|
-
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
617
|
-
durations.binding += 1;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// TODO: consider sharing implementation with binding
|
|
622
|
-
pub fn encore(self: *Self, player: Player, turns: u4) Error!void {
|
|
623
|
-
if (!enabled) return;
|
|
624
|
-
|
|
625
|
-
var durations = &self.actions.get(player).durations;
|
|
626
|
-
const n = durations.encore;
|
|
627
|
-
if (turns == 0) {
|
|
628
|
-
assert(n >= 2 and n <= 5);
|
|
629
|
-
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
630
|
-
durations.encore = 0;
|
|
631
|
-
} else {
|
|
632
|
-
assert(n >= 1 and n < 5);
|
|
633
|
-
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
634
|
-
durations.encore += 1;
|
|
635
|
-
}
|
|
445
|
+
self.actions.get(player).duration = if (options.key) 2 else turns;
|
|
636
446
|
}
|
|
637
447
|
};
|
|
638
448
|
}
|
|
@@ -684,22 +494,22 @@ test "Chance.hit" {
|
|
|
684
494
|
test "Chance.criticalHit" {
|
|
685
495
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
686
496
|
|
|
687
|
-
try chance.criticalHit(.P1, true, 17);
|
|
688
|
-
try expectValue(Optional(bool).true, chance.actions.p1.
|
|
497
|
+
try chance.criticalHit(.P1, 0, true, 17);
|
|
498
|
+
try expectValue(Optional(bool).true, Criticals.get(chance.actions.p1.critical_hits, 0));
|
|
689
499
|
try expectProbability(&chance.probability, 17, 256);
|
|
690
500
|
|
|
691
501
|
chance.reset();
|
|
692
502
|
|
|
693
|
-
try chance.criticalHit(.P2, false, 5);
|
|
503
|
+
try chance.criticalHit(.P2, 1, false, 5);
|
|
694
504
|
try expectProbability(&chance.probability, 251, 256);
|
|
695
|
-
try expectValue(Optional(bool).false, chance.actions.p2.
|
|
505
|
+
try expectValue(Optional(bool).false, Criticals.get(chance.actions.p2.critical_hits, 1));
|
|
696
506
|
}
|
|
697
507
|
|
|
698
508
|
test "Chance.damage" {
|
|
699
509
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
700
510
|
|
|
701
|
-
try chance.damage(.P1,
|
|
702
|
-
try expectValue(
|
|
511
|
+
try chance.damage(.P1, 0, 219);
|
|
512
|
+
try expectValue(@as(u8, 219), Damages.get(chance.actions.p1.damages, 0));
|
|
703
513
|
try expectProbability(&chance.probability, 1, 39);
|
|
704
514
|
}
|
|
705
515
|
|
|
@@ -824,7 +634,7 @@ test "Chance.present" {
|
|
|
824
634
|
} else {
|
|
825
635
|
try expectProbability(&chance.probability, 25, 256);
|
|
826
636
|
}
|
|
827
|
-
try expectValue(4, chance.actions.p2.present);
|
|
637
|
+
try expectValue(@as(u3, 4), chance.actions.p2.present);
|
|
828
638
|
|
|
829
639
|
chance.reset();
|
|
830
640
|
|
|
@@ -834,7 +644,7 @@ test "Chance.present" {
|
|
|
834
644
|
} else {
|
|
835
645
|
try expectProbability(&chance.probability, 51, 256);
|
|
836
646
|
}
|
|
837
|
-
try expectValue(1, chance.actions.p1.present);
|
|
647
|
+
try expectValue(@as(u3, 1), chance.actions.p1.present);
|
|
838
648
|
}
|
|
839
649
|
|
|
840
650
|
test "Chance.magnitude" {
|
|
@@ -846,7 +656,7 @@ test "Chance.magnitude" {
|
|
|
846
656
|
} else {
|
|
847
657
|
try expectProbability(&chance.probability, 13, 256);
|
|
848
658
|
}
|
|
849
|
-
try expectValue(7, chance.actions.p2.magnitude);
|
|
659
|
+
try expectValue(@as(u3, 7), chance.actions.p2.magnitude);
|
|
850
660
|
|
|
851
661
|
chance.reset();
|
|
852
662
|
|
|
@@ -856,7 +666,7 @@ test "Chance.magnitude" {
|
|
|
856
666
|
} else {
|
|
857
667
|
try expectProbability(&chance.probability, 51, 256);
|
|
858
668
|
}
|
|
859
|
-
try expectValue(5, chance.actions.p1.magnitude);
|
|
669
|
+
try expectValue(@as(u3, 5), chance.actions.p1.magnitude);
|
|
860
670
|
}
|
|
861
671
|
|
|
862
672
|
test "Chance.tripleKick" {
|
|
@@ -864,7 +674,7 @@ test "Chance.tripleKick" {
|
|
|
864
674
|
|
|
865
675
|
try chance.tripleKick(.P1, 2);
|
|
866
676
|
try expectProbability(&chance.probability, 1, 3);
|
|
867
|
-
try expectValue(2, chance.actions.p1.triple_kick);
|
|
677
|
+
try expectValue(@as(u2, 2), chance.actions.p1.triple_kick);
|
|
868
678
|
}
|
|
869
679
|
|
|
870
680
|
test "Chance.spite" {
|
|
@@ -872,7 +682,7 @@ test "Chance.spite" {
|
|
|
872
682
|
|
|
873
683
|
try chance.spite(.P1, 3);
|
|
874
684
|
try expectProbability(&chance.probability, 1, 4);
|
|
875
|
-
try expectValue(3, chance.actions.p1.spite);
|
|
685
|
+
try expectValue(@as(u3, 3), chance.actions.p1.spite);
|
|
876
686
|
}
|
|
877
687
|
|
|
878
688
|
test "Chance.conversion2" {
|
|
@@ -899,7 +709,7 @@ test "Chance.psywave" {
|
|
|
899
709
|
|
|
900
710
|
try chance.psywave(.P2, 100, 150);
|
|
901
711
|
try expectProbability(&chance.probability, 1, 150);
|
|
902
|
-
try expectValue(100, chance.actions.p2.psywave);
|
|
712
|
+
try expectValue(@as(u8, 100), chance.actions.p2.psywave);
|
|
903
713
|
}
|
|
904
714
|
|
|
905
715
|
test "Chance.metronome" {
|
|
@@ -915,19 +725,19 @@ test "Chance.moveSlot" {
|
|
|
915
725
|
|
|
916
726
|
try chance.moveSlot(.P2, 2, 2);
|
|
917
727
|
try expectProbability(&chance.probability, 1, 2);
|
|
918
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
728
|
+
try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
|
|
919
729
|
|
|
920
730
|
chance.reset();
|
|
921
731
|
|
|
922
732
|
try chance.moveSlot(.P1, 1, 3);
|
|
923
733
|
try expectProbability(&chance.probability, 1, 3);
|
|
924
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
734
|
+
try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
|
|
925
735
|
|
|
926
736
|
chance.reset();
|
|
927
737
|
|
|
928
738
|
try chance.moveSlot(.P1, 4, 1);
|
|
929
739
|
try expectProbability(&chance.probability, 1, 1);
|
|
930
|
-
try expectValue(4, chance.actions.p1.move_slot);
|
|
740
|
+
try expectValue(@as(u3, 4), chance.actions.p1.move_slot);
|
|
931
741
|
}
|
|
932
742
|
|
|
933
743
|
test "Chance.forceSwitch" {
|
|
@@ -935,19 +745,19 @@ test "Chance.forceSwitch" {
|
|
|
935
745
|
|
|
936
746
|
try chance.forceSwitch(.P2, 2, 2);
|
|
937
747
|
try expectProbability(&chance.probability, 1, 2);
|
|
938
|
-
try expectValue(2, chance.actions.p2.force_switch);
|
|
748
|
+
try expectValue(@as(u3, 2), chance.actions.p2.force_switch);
|
|
939
749
|
|
|
940
750
|
chance.reset();
|
|
941
751
|
|
|
942
752
|
try chance.forceSwitch(.P1, 1, 3);
|
|
943
753
|
try expectProbability(&chance.probability, 1, 3);
|
|
944
|
-
try expectValue(1, chance.actions.p1.force_switch);
|
|
754
|
+
try expectValue(@as(u3, 1), chance.actions.p1.force_switch);
|
|
945
755
|
|
|
946
756
|
chance.reset();
|
|
947
757
|
|
|
948
758
|
try chance.forceSwitch(.P1, 4, 1);
|
|
949
759
|
try expectProbability(&chance.probability, 1, 1);
|
|
950
|
-
try expectValue(4, chance.actions.p1.force_switch);
|
|
760
|
+
try expectValue(@as(u3, 4), chance.actions.p1.force_switch);
|
|
951
761
|
}
|
|
952
762
|
|
|
953
763
|
test "Chance.multiHit" {
|
|
@@ -955,212 +765,20 @@ test "Chance.multiHit" {
|
|
|
955
765
|
|
|
956
766
|
try chance.multiHit(.P1, 3);
|
|
957
767
|
try expectProbability(&chance.probability, 3, 8);
|
|
958
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
768
|
+
try expectValue(@as(u3, 3), chance.actions.p1.multi_hit);
|
|
959
769
|
|
|
960
770
|
chance.reset();
|
|
961
771
|
|
|
962
772
|
try chance.multiHit(.P2, 5);
|
|
963
773
|
try expectProbability(&chance.probability, 1, 8);
|
|
964
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
774
|
+
try expectValue(@as(u3, 5), chance.actions.p2.multi_hit);
|
|
965
775
|
}
|
|
966
776
|
|
|
967
777
|
test "Chance.duration" {
|
|
968
778
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
969
779
|
|
|
970
|
-
chance.duration(.
|
|
971
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
972
|
-
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
test "Chance.sleep" {
|
|
976
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
977
|
-
|
|
978
|
-
for ([_]u8{ 6, 5, 4, 3, 2, 1 }, 1..7) |d, i| {
|
|
979
|
-
chance.actions.p1.durations.sleep = @intCast(i);
|
|
980
|
-
try chance.sleep(.P1, 0);
|
|
981
|
-
try expectProbability(&chance.probability, 1, d);
|
|
982
|
-
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
983
|
-
|
|
984
|
-
chance.reset();
|
|
985
|
-
|
|
986
|
-
if (i < 6) {
|
|
987
|
-
chance.actions.p1.durations.sleep = @intCast(i);
|
|
988
|
-
try chance.sleep(.P1, 1);
|
|
989
|
-
try expectProbability(&chance.probability, d - 1, d);
|
|
990
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p1.durations.sleep);
|
|
991
|
-
|
|
992
|
-
chance.reset();
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
test "Chance.confusion" {
|
|
998
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
999
|
-
|
|
1000
|
-
chance.actions.p2.durations.thrash = false;
|
|
1001
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1002
|
-
if (i > 1) {
|
|
1003
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1004
|
-
try chance.confusion(.P2, 0);
|
|
1005
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1006
|
-
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
1007
|
-
try expectValue(false, chance.actions.p2.durations.thrash);
|
|
1008
|
-
|
|
1009
|
-
chance.reset();
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (i < 5) {
|
|
1013
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1014
|
-
try chance.confusion(.P2, 1);
|
|
1015
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1016
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
|
|
1017
|
-
try expectValue(false, chance.actions.p2.durations.thrash);
|
|
1018
|
-
|
|
1019
|
-
chance.reset();
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
chance.reset();
|
|
1024
|
-
|
|
1025
|
-
chance.actions.p2.durations.thrash = true;
|
|
1026
|
-
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
1027
|
-
if (i > 1) {
|
|
1028
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1029
|
-
try chance.confusion(.P2, 0);
|
|
1030
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1031
|
-
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
1032
|
-
try expectValue(true, chance.actions.p2.durations.thrash);
|
|
1033
|
-
|
|
1034
|
-
chance.reset();
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
if (i < 3) {
|
|
1038
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1039
|
-
try chance.confusion(.P2, 1);
|
|
1040
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1041
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
|
|
1042
|
-
try expectValue(true, chance.actions.p2.durations.thrash);
|
|
1043
|
-
|
|
1044
|
-
chance.reset();
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
test "Chance.disable" {
|
|
1050
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1051
|
-
if (showdown) {
|
|
1052
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1053
|
-
if (i > 1) {
|
|
1054
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1055
|
-
try chance.disable(.P2, 0);
|
|
1056
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1057
|
-
try expectValue(0, chance.actions.p2.durations.disable);
|
|
1058
|
-
|
|
1059
|
-
chance.reset();
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (i < 5) {
|
|
1063
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1064
|
-
try chance.disable(.P2, 1);
|
|
1065
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1066
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations.disable);
|
|
1067
|
-
|
|
1068
|
-
chance.reset();
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
} else {
|
|
1072
|
-
for ([_]u8{ 1, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
1073
|
-
if (i > 1) {
|
|
1074
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1075
|
-
try chance.disable(.P2, 0);
|
|
1076
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1077
|
-
try expectValue(0, chance.actions.p2.durations.disable);
|
|
1078
|
-
|
|
1079
|
-
chance.reset();
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
if (i < 8) {
|
|
1083
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1084
|
-
try chance.disable(.P2, 1);
|
|
1085
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1086
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations.disable);
|
|
1087
|
-
|
|
1088
|
-
chance.reset();
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
test "Chance.attacking" {
|
|
1095
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1096
|
-
|
|
1097
|
-
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
1098
|
-
if (i > 1) {
|
|
1099
|
-
chance.actions.p2.durations.attacking = @intCast(i);
|
|
1100
|
-
try chance.attacking(.P2, 0);
|
|
1101
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1102
|
-
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
1103
|
-
|
|
1104
|
-
chance.reset();
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
if (i < 3) {
|
|
1108
|
-
chance.actions.p2.durations.attacking = @intCast(i);
|
|
1109
|
-
try chance.attacking(.P2, 1);
|
|
1110
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1111
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.attacking);
|
|
1112
|
-
|
|
1113
|
-
chance.reset();
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
test "Chance.binding" {
|
|
1119
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1120
|
-
|
|
1121
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1122
|
-
if (i > 1) {
|
|
1123
|
-
chance.actions.p2.durations.binding = @intCast(i);
|
|
1124
|
-
try chance.binding(.P2, 0);
|
|
1125
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1126
|
-
try expectValue(0, chance.actions.p2.durations.binding);
|
|
1127
|
-
|
|
1128
|
-
chance.reset();
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
if (i < 5) {
|
|
1132
|
-
chance.actions.p2.durations.binding = @intCast(i);
|
|
1133
|
-
try chance.binding(.P2, 1);
|
|
1134
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1135
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.binding);
|
|
1136
|
-
|
|
1137
|
-
chance.reset();
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
test "Chance.encore" {
|
|
1143
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1144
|
-
|
|
1145
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1146
|
-
if (i > 1) {
|
|
1147
|
-
chance.actions.p2.durations.encore = @intCast(i);
|
|
1148
|
-
try chance.encore(.P2, 0);
|
|
1149
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1150
|
-
try expectValue(0, chance.actions.p2.durations.encore);
|
|
1151
|
-
|
|
1152
|
-
chance.reset();
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
if (i < 5) {
|
|
1156
|
-
chance.actions.p2.durations.encore = @intCast(i);
|
|
1157
|
-
try chance.encore(.P2, 1);
|
|
1158
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1159
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.encore);
|
|
1160
|
-
|
|
1161
|
-
chance.reset();
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
780
|
+
chance.duration(.P1, 2);
|
|
781
|
+
try expectValue(@as(u3, 2), chance.actions.p1.duration);
|
|
1164
782
|
}
|
|
1165
783
|
|
|
1166
784
|
pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
@@ -1202,12 +820,12 @@ const Null = struct {
|
|
|
1202
820
|
_ = .{ self, player, ok, accuracy };
|
|
1203
821
|
}
|
|
1204
822
|
|
|
1205
|
-
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
1206
|
-
_ = .{ self, player, crit, rate };
|
|
823
|
+
pub fn criticalHit(self: Null, player: Player, i: u3, crit: bool, rate: u8) Error!void {
|
|
824
|
+
_ = .{ self, player, i, crit, rate };
|
|
1207
825
|
}
|
|
1208
826
|
|
|
1209
|
-
pub fn damage(self: Null, player: Player, roll: u8) Error!void {
|
|
1210
|
-
_ = .{ self, player, roll };
|
|
827
|
+
pub fn damage(self: Null, player: Player, i: u3, roll: u8) Error!void {
|
|
828
|
+
_ = .{ self, player, i, roll };
|
|
1211
829
|
}
|
|
1212
830
|
|
|
1213
831
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
@@ -1278,37 +896,7 @@ const Null = struct {
|
|
|
1278
896
|
_ = .{ self, player, move, n };
|
|
1279
897
|
}
|
|
1280
898
|
|
|
1281
|
-
pub fn duration(
|
|
1282
|
-
self: Null,
|
|
1283
|
-
comptime field: Duration.Field,
|
|
1284
|
-
player: Player,
|
|
1285
|
-
target: Player,
|
|
1286
|
-
turns: u4,
|
|
1287
|
-
) void {
|
|
1288
|
-
_ = .{ self, field, player, target, turns };
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
pub fn sleep(self: Null, player: Player, turns: u4) Error!void {
|
|
1292
|
-
_ = .{ self, player, turns };
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
pub fn confusion(self: Null, player: Player, turns: u4) Error!void {
|
|
1296
|
-
_ = .{ self, player, turns };
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
pub fn disable(self: Null, player: Player, turns: u4) Error!void {
|
|
1300
|
-
_ = .{ self, player, turns };
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
pub fn attacking(self: Null, player: Player, turns: u4) Error!void {
|
|
1304
|
-
_ = .{ self, player, turns };
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
pub fn binding(self: Null, player: Player, turns: u4) Error!void {
|
|
1308
|
-
_ = .{ self, player, turns };
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
pub fn encore(self: Null, player: Player, turns: u4) Error!void {
|
|
899
|
+
pub fn duration(self: Null, player: Player, turns: u3) void {
|
|
1312
900
|
_ = .{ self, player, turns };
|
|
1313
901
|
}
|
|
1314
902
|
};
|