@pkmn/engine 0.1.0-dev.9f75c7ae → 0.1.0-dev.a0167a9d
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.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +5 -5
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- 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 +82 -90
- package/package.json +31 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +393 -396
- package/src/lib/common/rational.zig +109 -15
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/calc.zig +269 -97
- package/src/lib/gen1/chance.zig +126 -160
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +21 -21
- package/src/lib/gen1/helpers.zig +191 -206
- package/src/lib/gen1/mechanics.zig +308 -285
- package/src/lib/gen2/chance.zig +211 -121
- package/src/lib/gen2/data.zig +13 -11
- package/src/lib/gen2/generated.zig +15 -24
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +855 -413
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +27 -12
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -330
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -14,7 +14,7 @@ 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 data = @import("
|
|
17
|
+
const data = @import("data.zig");
|
|
18
18
|
|
|
19
19
|
const enabled = options.chance;
|
|
20
20
|
const showdown = options.showdown;
|
|
@@ -42,12 +42,6 @@ pub const Actions = extern struct {
|
|
|
42
42
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
43
|
}
|
|
44
44
|
|
|
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
45
|
/// Returns true if `a` is equal to `b`.
|
|
52
46
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
53
47
|
return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
|
|
@@ -63,11 +57,6 @@ pub const Actions = extern struct {
|
|
|
63
57
|
const b_val = @field(@field(b, player.name), field.name);
|
|
64
58
|
|
|
65
59
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
66
|
-
.Struct => inline for (@typeInfo(@TypeOf(a_val)).Struct.fields) |f| {
|
|
67
|
-
if ((@field(a_val, f.name) > 0) != (@field(b_val, f.name) > 0)) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
60
|
.Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
72
61
|
return false,
|
|
73
62
|
.Int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
@@ -96,9 +85,6 @@ test Actions {
|
|
|
96
85
|
const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
|
|
97
86
|
const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
|
|
98
87
|
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
88
|
|
|
103
89
|
try expect(a.eql(a));
|
|
104
90
|
try expect(!a.eql(b));
|
|
@@ -111,8 +97,6 @@ test Actions {
|
|
|
111
97
|
try expect(b.matches(a));
|
|
112
98
|
try expect(!a.matches(c));
|
|
113
99
|
try expect(!c.matches(a));
|
|
114
|
-
try expect(d.matches(e));
|
|
115
|
-
try expect(!d.matches(f));
|
|
116
100
|
}
|
|
117
101
|
|
|
118
102
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
@@ -139,8 +123,7 @@ pub const Action = packed struct(u64) {
|
|
|
139
123
|
/// or Rolls.{sleepDuration,disableDuration,confusionDuration,attackingDuration} otherwise.
|
|
140
124
|
duration: u4 = 0,
|
|
141
125
|
|
|
142
|
-
|
|
143
|
-
durations: Duration = .{},
|
|
126
|
+
_: u16 = 0,
|
|
144
127
|
|
|
145
128
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
146
129
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
@@ -154,15 +137,8 @@ pub const Action = packed struct(u64) {
|
|
|
154
137
|
/// If not None, the Move to return for Rolls.metronome.
|
|
155
138
|
metronome: Move = .None,
|
|
156
139
|
|
|
157
|
-
pub const DURATIONS: u64 = 0x000000FFFF000000;
|
|
158
|
-
|
|
159
140
|
pub const Field = std.meta.FieldEnum(Action);
|
|
160
141
|
|
|
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
142
|
pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
|
|
167
143
|
_ = .{ f, o };
|
|
168
144
|
try fmt(a, w, false);
|
|
@@ -174,18 +150,6 @@ pub const Action = packed struct(u64) {
|
|
|
174
150
|
inline for (@typeInfo(Action).Struct.fields) |field| {
|
|
175
151
|
const val = @field(self, field.name);
|
|
176
152
|
switch (@typeInfo(@TypeOf(val))) {
|
|
177
|
-
.Struct => inline for (@typeInfo(@TypeOf(val)).Struct.fields) |f| {
|
|
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
153
|
.Enum => if (val != .None) {
|
|
190
154
|
if (printed) try writer.writeAll(", ");
|
|
191
155
|
if (shape) {
|
|
@@ -216,13 +180,6 @@ pub const Action = packed struct(u64) {
|
|
|
216
180
|
}
|
|
217
181
|
};
|
|
218
182
|
|
|
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
183
|
/// TODO
|
|
227
184
|
pub const Durations = extern struct {
|
|
228
185
|
/// Information about the durations for Player 1.
|
|
@@ -233,6 +190,12 @@ pub const Durations = extern struct {
|
|
|
233
190
|
comptime {
|
|
234
191
|
assert(@sizeOf(Durations) == 4);
|
|
235
192
|
}
|
|
193
|
+
|
|
194
|
+
/// Returns the `Duration` for the given `player`.
|
|
195
|
+
pub fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Duration) {
|
|
196
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Durations);
|
|
197
|
+
return if (player == .P1) &self.p1 else &self.p2;
|
|
198
|
+
}
|
|
236
199
|
};
|
|
237
200
|
|
|
238
201
|
/// Observed values for various durations that need to be tracked in order to properly
|
|
@@ -254,6 +217,8 @@ pub const Duration = packed struct(u16) {
|
|
|
254
217
|
|
|
255
218
|
pub const Commit = enum { hit, miss, binding };
|
|
256
219
|
|
|
220
|
+
const uN = if (options.miss) u8 else u9;
|
|
221
|
+
|
|
257
222
|
/// Tracks chance actions and their associated probability during a Generation I battle update when
|
|
258
223
|
/// `options.chance` is enabled.
|
|
259
224
|
pub fn Chance(comptime Rational: type) type {
|
|
@@ -265,6 +230,8 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
265
230
|
/// The actions taken by a hypothetical "chance player" that convey information about which
|
|
266
231
|
/// RNG events were observed during a battle `update`.
|
|
267
232
|
actions: Actions = .{},
|
|
233
|
+
/// TODO
|
|
234
|
+
durations: Durations = .{},
|
|
268
235
|
|
|
269
236
|
// Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
|
|
270
237
|
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
@@ -290,15 +257,12 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
290
257
|
//
|
|
291
258
|
// Finally, because each player's actions are processed sequentially we only need a single
|
|
292
259
|
// 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 {
|
|
260
|
+
pending: if (showdown) struct {} else struct {
|
|
297
261
|
crit: bool = false,
|
|
298
262
|
crit_probablity: u8 = 0,
|
|
299
263
|
damage_roll: u8 = 0,
|
|
300
264
|
hit: bool = false,
|
|
301
|
-
hit_probablity:
|
|
265
|
+
hit_probablity: uN = 0,
|
|
302
266
|
binding: u8 = 0,
|
|
303
267
|
} = .{},
|
|
304
268
|
|
|
@@ -307,25 +271,31 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
307
271
|
|
|
308
272
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
309
273
|
pub fn reset(self: *Self) void {
|
|
274
|
+
if (!enabled) return;
|
|
275
|
+
|
|
310
276
|
self.probability.reset();
|
|
311
|
-
self.actions.
|
|
277
|
+
self.actions = .{};
|
|
312
278
|
self.pending = .{};
|
|
313
279
|
}
|
|
314
280
|
|
|
315
281
|
pub fn commit(self: *Self, player: Player, kind: Commit) Error!void {
|
|
282
|
+
if (!enabled) return;
|
|
283
|
+
|
|
284
|
+
assert(!showdown);
|
|
285
|
+
|
|
316
286
|
var action = self.actions.get(player);
|
|
317
287
|
|
|
318
288
|
// We need to handle binding specially because we still need to commit it when attacking
|
|
319
289
|
// into a Pokémon that is immune
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
290
|
+
if (kind != .miss and self.pending.binding != 0) {
|
|
291
|
+
// assert(action.duration == 0); FIXME
|
|
292
|
+
action.duration = @intCast(self.pending.binding);
|
|
293
|
+
|
|
294
|
+
var durations = self.durations.get(player);
|
|
295
|
+
assert(durations.binding == 0);
|
|
296
|
+
durations.binding = 1;
|
|
297
|
+
}
|
|
298
|
+
if (kind == .binding) return;
|
|
329
299
|
|
|
330
300
|
// Always commit the hit result if we make it here (commit won't be called at all if the
|
|
331
301
|
// target is immune/behind a sub/etc)
|
|
@@ -334,19 +304,19 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
334
304
|
action.hit = if (self.pending.hit) .true else .false;
|
|
335
305
|
}
|
|
336
306
|
|
|
337
|
-
|
|
307
|
+
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
308
|
+
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
309
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't rol for
|
|
310
|
+
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
311
|
+
// passed to the critical hit helper)
|
|
312
|
+
if (kind != .hit) return assert(!self.pending.crit or self.pending.crit_probablity > 0);
|
|
338
313
|
|
|
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
|
-
}
|
|
314
|
+
if (self.pending.crit_probablity != 0) {
|
|
315
|
+
try self.probability.update(self.pending.crit_probablity, 256);
|
|
316
|
+
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
317
|
+
}
|
|
349
318
|
|
|
319
|
+
if (self.pending.damage_roll > 0) {
|
|
350
320
|
try self.probability.update(1, 39);
|
|
351
321
|
action.damage = self.pending.damage_roll;
|
|
352
322
|
}
|
|
@@ -361,14 +331,14 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
361
331
|
pub fn clearDurations(self: *Self, player: Player, haze: ?bool) void {
|
|
362
332
|
if (!enabled) return;
|
|
363
333
|
|
|
364
|
-
var
|
|
334
|
+
var durations = self.durations.get(player);
|
|
365
335
|
if (haze) |status| {
|
|
366
|
-
if (status)
|
|
367
|
-
|
|
368
|
-
|
|
336
|
+
if (status) durations.sleep = 0;
|
|
337
|
+
durations.confusion = 0;
|
|
338
|
+
durations.disable = 0;
|
|
369
339
|
} else {
|
|
370
|
-
|
|
371
|
-
|
|
340
|
+
durations.attacking = 0;
|
|
341
|
+
durations.binding = 0;
|
|
372
342
|
}
|
|
373
343
|
}
|
|
374
344
|
|
|
@@ -378,15 +348,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
378
348
|
assert(in >= 1 and in <= 6);
|
|
379
349
|
assert(out >= 1 and out <= 6);
|
|
380
350
|
|
|
381
|
-
var
|
|
351
|
+
var durations = self.durations.get(player);
|
|
382
352
|
|
|
383
|
-
const slp =
|
|
384
|
-
|
|
353
|
+
const slp = durations.sleep;
|
|
354
|
+
durations.* = .{};
|
|
385
355
|
|
|
386
356
|
self.sleeps[@intFromEnum(player)][out - 1] = slp;
|
|
387
|
-
|
|
357
|
+
durations.sleep = @intCast(self.sleeps[@intFromEnum(player)][in - 1]);
|
|
388
358
|
|
|
389
|
-
self.
|
|
359
|
+
self.durations.get(player.foe()).binding = 0;
|
|
390
360
|
}
|
|
391
361
|
|
|
392
362
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
@@ -397,12 +367,17 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
397
367
|
self.actions.p2.speed_tie = self.actions.p1.speed_tie;
|
|
398
368
|
}
|
|
399
369
|
|
|
400
|
-
pub fn hit(self: *Self, ok: bool, accuracy:
|
|
370
|
+
pub fn hit(self: *Self, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
401
371
|
if (!enabled) return;
|
|
402
372
|
|
|
403
373
|
const p = if (ok) accuracy else @as(u8, @intCast(256 - @as(u9, accuracy)));
|
|
404
|
-
|
|
405
|
-
|
|
374
|
+
if (showdown) {
|
|
375
|
+
try self.probability.update(p, 256);
|
|
376
|
+
self.actions.get(player).hit = if (ok) .true else .false;
|
|
377
|
+
} else {
|
|
378
|
+
self.pending.hit = ok;
|
|
379
|
+
self.pending.hit_probablity = p;
|
|
380
|
+
}
|
|
406
381
|
}
|
|
407
382
|
|
|
408
383
|
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -492,10 +467,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
492
467
|
if (!showdown and field == .binding) {
|
|
493
468
|
self.pending.binding = if (options.key) 1 else turns;
|
|
494
469
|
} else {
|
|
495
|
-
var durations =
|
|
470
|
+
var durations = self.durations.get(target);
|
|
496
471
|
assert(@field(durations, @tagName(field)) == 0 or
|
|
497
472
|
(field == .confusion and player == target));
|
|
498
473
|
@field(durations, @tagName(field)) = 1;
|
|
474
|
+
|
|
499
475
|
self.actions.get(player).duration = if (options.key) 1 else turns;
|
|
500
476
|
}
|
|
501
477
|
}
|
|
@@ -503,7 +479,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
503
479
|
pub fn sleep(self: *Self, player: Player, turns: u4) Error!void {
|
|
504
480
|
if (!enabled) return;
|
|
505
481
|
|
|
506
|
-
var durations =
|
|
482
|
+
var durations = self.durations.get(player);
|
|
507
483
|
const n = durations.sleep;
|
|
508
484
|
if (turns == 0) {
|
|
509
485
|
assert(n >= 1 and n <= 7);
|
|
@@ -519,7 +495,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
519
495
|
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
520
496
|
if (!enabled) return;
|
|
521
497
|
|
|
522
|
-
var durations =
|
|
498
|
+
var durations = self.durations.get(player);
|
|
523
499
|
const n = durations.confusion;
|
|
524
500
|
if (turns == 0) {
|
|
525
501
|
assert(n >= 2 and n <= 5);
|
|
@@ -535,7 +511,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
535
511
|
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
536
512
|
if (!enabled) return;
|
|
537
513
|
|
|
538
|
-
var durations =
|
|
514
|
+
var durations = self.durations.get(player);
|
|
539
515
|
const n = durations.disable;
|
|
540
516
|
if (turns == 0) {
|
|
541
517
|
assert(n >= 1 and n <= 8);
|
|
@@ -551,7 +527,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
551
527
|
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
552
528
|
if (!enabled) return;
|
|
553
529
|
|
|
554
|
-
var durations =
|
|
530
|
+
var durations = self.durations.get(player);
|
|
555
531
|
const n = durations.attacking;
|
|
556
532
|
if (turns == 0) {
|
|
557
533
|
assert(n >= 2 and n <= 3);
|
|
@@ -567,7 +543,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
567
543
|
pub fn binding(self: *Self, player: Player, turns: u4) Error!void {
|
|
568
544
|
if (!enabled) return;
|
|
569
545
|
|
|
570
|
-
var durations =
|
|
546
|
+
var durations = self.durations.get(player);
|
|
571
547
|
const n = durations.binding;
|
|
572
548
|
|
|
573
549
|
assert(n > 0);
|
|
@@ -620,19 +596,25 @@ test "Chance.speedTie" {
|
|
|
620
596
|
test "Chance.hit" {
|
|
621
597
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
622
598
|
|
|
623
|
-
chance.hit(true, 229);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
599
|
+
try chance.hit(.P1, true, 229);
|
|
600
|
+
if (!showdown) {
|
|
601
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
602
|
+
try expectValue(Optional(bool).None, chance.actions.p1.hit);
|
|
603
|
+
|
|
604
|
+
try chance.commit(.P1, .hit);
|
|
605
|
+
}
|
|
627
606
|
try expectValue(Optional(bool).true, chance.actions.p1.hit);
|
|
628
607
|
try expectProbability(&chance.probability, 229, 256);
|
|
629
608
|
|
|
630
609
|
chance.reset();
|
|
631
610
|
|
|
632
|
-
chance.hit(false, 255);
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
611
|
+
try chance.hit(.P2, false, 255);
|
|
612
|
+
if (!showdown) {
|
|
613
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
614
|
+
try expectValue(Optional(bool).None, chance.actions.p2.hit);
|
|
615
|
+
|
|
616
|
+
try chance.commit(.P2, .miss);
|
|
617
|
+
}
|
|
636
618
|
try expectValue(Optional(bool).false, chance.actions.p2.hit);
|
|
637
619
|
try expectProbability(&chance.probability, 1, 256);
|
|
638
620
|
}
|
|
@@ -648,26 +630,18 @@ test "Chance.criticalHit" {
|
|
|
648
630
|
try expectProbability(&chance.probability, 1, 1);
|
|
649
631
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
650
632
|
|
|
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);
|
|
633
|
+
try chance.hit(.P1, true, 128);
|
|
656
634
|
|
|
657
|
-
try chance.
|
|
658
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
635
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
659
636
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
660
637
|
|
|
661
|
-
chance.probability.reset();
|
|
662
638
|
try chance.damage(.P1, 217);
|
|
663
639
|
|
|
664
|
-
try chance.
|
|
665
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
640
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
666
641
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
667
642
|
|
|
668
|
-
chance.probability.reset();
|
|
669
|
-
|
|
670
643
|
try chance.commit(.P1, .hit);
|
|
644
|
+
|
|
671
645
|
try expectProbability(&chance.probability, 17, 19968); // (1/2) * (17/256) * (1/39)
|
|
672
646
|
try expectValue(Optional(bool).true, chance.actions.p1.critical_hit);
|
|
673
647
|
}
|
|
@@ -682,20 +656,14 @@ test "Chance.criticalHit" {
|
|
|
682
656
|
try expectProbability(&chance.probability, 1, 1);
|
|
683
657
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
684
658
|
|
|
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);
|
|
659
|
+
try chance.hit(.P1, true, 128);
|
|
690
660
|
|
|
691
|
-
try chance.
|
|
692
|
-
try expectProbability(&chance.probability, 1, 2);
|
|
661
|
+
try expectProbability(&chance.probability, 1, 1);
|
|
693
662
|
try expectValue(Optional(bool).None, chance.actions.p1.critical_hit);
|
|
694
663
|
|
|
695
|
-
chance.probability.reset();
|
|
696
664
|
try chance.damage(.P1, 217);
|
|
697
|
-
|
|
698
665
|
try chance.commit(.P1, .hit);
|
|
666
|
+
|
|
699
667
|
try expectProbability(&chance.probability, 251, 19968); // (1/2) * (251/256) * (1/39)
|
|
700
668
|
try expectValue(Optional(bool).false, chance.actions.p1.critical_hit);
|
|
701
669
|
}
|
|
@@ -706,19 +674,16 @@ test "Chance.damage" {
|
|
|
706
674
|
|
|
707
675
|
try chance.damage(.P1, 217);
|
|
708
676
|
if (showdown) {
|
|
709
|
-
try expectValue(
|
|
677
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
710
678
|
try expectProbability(&chance.probability, 1, 39);
|
|
711
679
|
} else {
|
|
712
680
|
try expectProbability(&chance.probability, 1, 1);
|
|
713
|
-
try expectValue(
|
|
714
|
-
|
|
715
|
-
try chance.commit(.P1, .miss);
|
|
716
|
-
try expectProbability(&chance.probability, 1, 1);
|
|
717
|
-
try expectValue(@as(u8, 0), chance.actions.p1.damage);
|
|
681
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
718
682
|
|
|
719
683
|
try chance.commit(.P1, .hit);
|
|
684
|
+
|
|
720
685
|
try expectProbability(&chance.probability, 1, 39);
|
|
721
|
-
try expectValue(
|
|
686
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
722
687
|
}
|
|
723
688
|
}
|
|
724
689
|
|
|
@@ -770,13 +735,13 @@ test "Chance.moveSlot" {
|
|
|
770
735
|
|
|
771
736
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
772
737
|
try expectProbability(&chance.probability, 1, 2);
|
|
773
|
-
try expectValue(
|
|
738
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
774
739
|
|
|
775
740
|
chance.reset();
|
|
776
741
|
|
|
777
742
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
778
743
|
try expectProbability(&chance.probability, 1, 3);
|
|
779
|
-
try expectValue(
|
|
744
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
780
745
|
}
|
|
781
746
|
|
|
782
747
|
test "Chance.multiHit" {
|
|
@@ -784,50 +749,51 @@ test "Chance.multiHit" {
|
|
|
784
749
|
|
|
785
750
|
try chance.multiHit(.P1, 3);
|
|
786
751
|
try expectProbability(&chance.probability, 3, 8);
|
|
787
|
-
try expectValue(
|
|
752
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
788
753
|
|
|
789
754
|
chance.reset();
|
|
790
755
|
|
|
791
756
|
try chance.multiHit(.P2, 5);
|
|
792
757
|
try expectProbability(&chance.probability, 1, 8);
|
|
793
|
-
try expectValue(
|
|
758
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
794
759
|
}
|
|
795
760
|
|
|
796
761
|
test "Chance.duration" {
|
|
797
762
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
798
763
|
|
|
799
764
|
chance.duration(.sleep, .P1, .P2, 2);
|
|
800
|
-
try expectValue(
|
|
801
|
-
try expectValue(
|
|
765
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
766
|
+
try expectValue(1, chance.durations.p2.sleep);
|
|
802
767
|
|
|
803
768
|
chance.reset();
|
|
804
769
|
|
|
805
770
|
chance.duration(.binding, .P2, .P2, 4);
|
|
806
771
|
if (!showdown) {
|
|
807
|
-
try expectValue(
|
|
808
|
-
try expectValue(
|
|
772
|
+
try expectValue(0, chance.actions.p2.duration);
|
|
773
|
+
try expectValue(0, chance.durations.p2.binding);
|
|
774
|
+
|
|
809
775
|
try chance.commit(.P2, .hit);
|
|
810
776
|
}
|
|
811
|
-
try expectValue(
|
|
812
|
-
try expectValue(
|
|
777
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
778
|
+
try expectValue(1, chance.durations.p2.binding);
|
|
813
779
|
}
|
|
814
780
|
|
|
815
781
|
test "Chance.sleep" {
|
|
816
782
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
817
783
|
|
|
818
784
|
for ([_]u8{ 7, 6, 5, 4, 3, 2, 1 }, 1..8) |d, i| {
|
|
819
|
-
chance.
|
|
785
|
+
chance.durations.p1.sleep = @intCast(i);
|
|
820
786
|
try chance.sleep(.P1, 0);
|
|
821
787
|
try expectProbability(&chance.probability, 1, d);
|
|
822
|
-
try expectValue(
|
|
788
|
+
try expectValue(0, chance.durations.p1.sleep);
|
|
823
789
|
|
|
824
790
|
chance.reset();
|
|
825
791
|
|
|
826
792
|
if (i < 7) {
|
|
827
|
-
chance.
|
|
793
|
+
chance.durations.p1.sleep = @intCast(i);
|
|
828
794
|
try chance.sleep(.P1, 1);
|
|
829
795
|
try expectProbability(&chance.probability, d - 1, d);
|
|
830
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
796
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.sleep);
|
|
831
797
|
|
|
832
798
|
chance.reset();
|
|
833
799
|
}
|
|
@@ -839,19 +805,19 @@ test "Chance.confusion" {
|
|
|
839
805
|
|
|
840
806
|
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
841
807
|
if (i > 1) {
|
|
842
|
-
chance.
|
|
808
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
843
809
|
try chance.confusion(.P2, 0);
|
|
844
810
|
try expectProbability(&chance.probability, 1, d);
|
|
845
|
-
try expectValue(
|
|
811
|
+
try expectValue(0, chance.durations.p2.confusion);
|
|
846
812
|
|
|
847
813
|
chance.reset();
|
|
848
814
|
}
|
|
849
815
|
|
|
850
816
|
if (i < 5) {
|
|
851
|
-
chance.
|
|
817
|
+
chance.durations.p2.confusion = @intCast(i);
|
|
852
818
|
try chance.confusion(.P2, 1);
|
|
853
819
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
854
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
820
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.confusion);
|
|
855
821
|
|
|
856
822
|
chance.reset();
|
|
857
823
|
}
|
|
@@ -862,18 +828,18 @@ test "Chance.disable" {
|
|
|
862
828
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
863
829
|
|
|
864
830
|
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
865
|
-
chance.
|
|
831
|
+
chance.durations.p1.disable = @intCast(i);
|
|
866
832
|
try chance.disable(.P1, 0);
|
|
867
833
|
try expectProbability(&chance.probability, 1, d);
|
|
868
|
-
try expectValue(
|
|
834
|
+
try expectValue(0, chance.durations.p1.disable);
|
|
869
835
|
|
|
870
836
|
chance.reset();
|
|
871
837
|
|
|
872
838
|
if (i < 8) {
|
|
873
|
-
chance.
|
|
839
|
+
chance.durations.p1.disable = @intCast(i);
|
|
874
840
|
try chance.disable(.P1, 1);
|
|
875
841
|
try expectProbability(&chance.probability, d - 1, d);
|
|
876
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.
|
|
842
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.durations.p1.disable);
|
|
877
843
|
|
|
878
844
|
chance.reset();
|
|
879
845
|
}
|
|
@@ -885,19 +851,19 @@ test "Chance.attacking" {
|
|
|
885
851
|
|
|
886
852
|
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
887
853
|
if (i > 1) {
|
|
888
|
-
chance.
|
|
854
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
889
855
|
try chance.attacking(.P2, 0);
|
|
890
856
|
try expectProbability(&chance.probability, 1, d);
|
|
891
|
-
try expectValue(
|
|
857
|
+
try expectValue(0, chance.durations.p2.attacking);
|
|
892
858
|
|
|
893
859
|
chance.reset();
|
|
894
860
|
}
|
|
895
861
|
|
|
896
862
|
if (i < 3) {
|
|
897
|
-
chance.
|
|
863
|
+
chance.durations.p2.attacking = @intCast(i);
|
|
898
864
|
try chance.attacking(.P2, 1);
|
|
899
865
|
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
900
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
866
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p2.attacking);
|
|
901
867
|
|
|
902
868
|
chance.reset();
|
|
903
869
|
}
|
|
@@ -911,18 +877,18 @@ test "Chance.binding" {
|
|
|
911
877
|
const qs = [_]u8{ 8, 5, 2, 1 };
|
|
912
878
|
|
|
913
879
|
for (ps, qs, 1..5) |p, q, i| {
|
|
914
|
-
chance.
|
|
880
|
+
chance.durations.p1.binding = @intCast(i);
|
|
915
881
|
try chance.binding(.P1, 0);
|
|
916
882
|
try expectProbability(&chance.probability, p, q);
|
|
917
|
-
try expectValue(
|
|
883
|
+
try expectValue(0, chance.durations.p1.binding);
|
|
918
884
|
|
|
919
885
|
chance.reset();
|
|
920
886
|
|
|
921
887
|
if (i < 4) {
|
|
922
|
-
chance.
|
|
888
|
+
chance.durations.p1.binding = @intCast(i);
|
|
923
889
|
try chance.binding(.P1, 1);
|
|
924
890
|
try expectProbability(&chance.probability, q - p, q);
|
|
925
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.
|
|
891
|
+
try expectValue(@as(u3, @intCast(i)) + 1, chance.durations.p1.binding);
|
|
926
892
|
|
|
927
893
|
chance.reset();
|
|
928
894
|
}
|
|
@@ -934,7 +900,7 @@ test "Chance.psywave" {
|
|
|
934
900
|
|
|
935
901
|
try chance.psywave(.P2, 100, 150);
|
|
936
902
|
try expectProbability(&chance.probability, 1, 150);
|
|
937
|
-
try expectValue(
|
|
903
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
938
904
|
}
|
|
939
905
|
|
|
940
906
|
test "Chance.metronome" {
|
|
@@ -988,8 +954,8 @@ const Null = struct {
|
|
|
988
954
|
_ = .{ self, p1 };
|
|
989
955
|
}
|
|
990
956
|
|
|
991
|
-
pub fn hit(self: Null, ok: bool, accuracy:
|
|
992
|
-
_ = .{ self, ok, accuracy };
|
|
957
|
+
pub fn hit(self: Null, player: Player, ok: bool, accuracy: uN) Error!void {
|
|
958
|
+
_ = .{ self, player, ok, accuracy };
|
|
993
959
|
}
|
|
994
960
|
|
|
995
961
|
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
@@ -1091,7 +1091,7 @@ pub const Species = enum(u8) {
|
|
|
1091
1091
|
/// The number of Pokémon species in this generation.
|
|
1092
1092
|
pub const size = 151;
|
|
1093
1093
|
|
|
1094
|
-
/// The Pokémon's critical hit
|
|
1094
|
+
/// The Pokémon's critical hit ratio out of 256.
|
|
1095
1095
|
pub fn chance(id: Species) u8 {
|
|
1096
1096
|
assert(id != .None);
|
|
1097
1097
|
return CHANCES[@intFromEnum(id) - 1];
|