@pkmn/engine 0.1.0-dev.305a932f → 0.1.0-dev.32adfe77
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 +37 -14
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +2 -2
- package/build/pkg/data.d.ts +3 -2
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +2 -2
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +1 -1
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +3 -1
- package/build/tools/debug.js.map +1 -1
- package/build.zig +78 -68
- package/package.json +34 -23
- package/src/bin/install-pkmn-engine +14 -14
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/optional.zig +9 -2
- package/src/lib/common/protocol.zig +402 -383
- package/src/lib/common/rational.zig +11 -7
- package/src/lib/common/rng.zig +20 -11
- package/src/lib/gen1/README.md +28 -28
- package/src/lib/gen1/calc.zig +7 -7
- package/src/lib/gen1/chance.zig +70 -68
- package/src/lib/gen1/data/moves.zig +24 -10
- package/src/lib/gen1/data/species.zig +3 -3
- package/src/lib/gen1/data/types.zig +5 -5
- package/src/lib/gen1/data.zig +36 -36
- package/src/lib/gen1/helpers.zig +21 -36
- package/src/lib/gen1/mechanics.zig +333 -344
- package/src/lib/gen2/README.md +1 -2
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1314 -0
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +32 -15
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +75 -28
- package/src/lib/gen2/data.zig +37 -25
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/mechanics.zig +2612 -349
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +26 -3
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
|
@@ -44,7 +44,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
44
44
|
/// Both p and q must be >= 1, and if computable at comptime must have no common factors.
|
|
45
45
|
pub fn update(r: *Self, p: anytype, q: anytype) Error!void {
|
|
46
46
|
// FIXME
|
|
47
|
-
// std.debug.print("({d}/{d}) * ", .{p, q});
|
|
47
|
+
// std.debug.print("({d}/{d}) * ", .{ p, q });
|
|
48
48
|
assert(p >= 1);
|
|
49
49
|
assert(q >= 1);
|
|
50
50
|
|
|
@@ -185,16 +185,16 @@ pub fn Rational(comptime T: type) type {
|
|
|
185
185
|
try writer.print("{d}/{d}", .{ self.p, self.q });
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
fn multiplication(r: *Self, p: anytype, q: anytype) !void {
|
|
189
189
|
r.p = try std.math.mul(T, r.p, p);
|
|
190
190
|
r.q = try std.math.mul(T, r.q, q);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
fn addition(r: *Self, p: anytype, q: anytype) !void {
|
|
194
194
|
// (a/b) + (c/d) = (ad+bc)/(bd)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
const d = try std.math.mul(T, r.q, q);
|
|
196
|
+
const n1 = try std.math.mul(T, r.p, q);
|
|
197
|
+
const n2 = try std.math.mul(T, r.q, p);
|
|
198
198
|
|
|
199
199
|
r.p = try std.math.add(T, n1, n2);
|
|
200
200
|
r.q = d;
|
|
@@ -203,7 +203,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
// https://en.wikipedia.org/wiki/Euclidean_algorithm
|
|
206
|
-
|
|
206
|
+
fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
|
|
207
207
|
assert(p >= 1);
|
|
208
208
|
assert(q >= 1);
|
|
209
209
|
|
|
@@ -234,6 +234,8 @@ test Rational {
|
|
|
234
234
|
var r: Rational(t) = .{};
|
|
235
235
|
|
|
236
236
|
var c: t = 128;
|
|
237
|
+
_ = &c;
|
|
238
|
+
|
|
237
239
|
try r.update(c, 256);
|
|
238
240
|
try doTurn(&r);
|
|
239
241
|
|
|
@@ -320,6 +322,8 @@ test BigRational {
|
|
|
320
322
|
try r.reset();
|
|
321
323
|
|
|
322
324
|
var c: u8 = 128;
|
|
325
|
+
_ = &c;
|
|
326
|
+
|
|
323
327
|
try r.update(c, 256);
|
|
324
328
|
try doTurn(&r);
|
|
325
329
|
|
package/src/lib/common/rng.zig
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
|
|
3
|
-
const options = @import("
|
|
3
|
+
const options = @import("options.zig");
|
|
4
4
|
|
|
5
5
|
const assert = std.debug.assert;
|
|
6
6
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -25,6 +25,11 @@ pub fn PRNG(comptime gen: comptime_int) type {
|
|
|
25
25
|
pub fn next(self: *Self) Output(gen) {
|
|
26
26
|
return self.src.next();
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
pub fn advance(self: *Self, n: usize) void {
|
|
30
|
+
if (!options.advance) return;
|
|
31
|
+
for (0..n) |_| self.src.advance();
|
|
32
|
+
}
|
|
28
33
|
};
|
|
29
34
|
}
|
|
30
35
|
|
|
@@ -61,11 +66,15 @@ pub const PSRNG = extern struct {
|
|
|
61
66
|
(@as(u64, self.range(u16, 0, 0x10000)) << 16) |
|
|
62
67
|
(@as(u64, self.range(u16, 0, 0x10000)));
|
|
63
68
|
}
|
|
69
|
+
|
|
70
|
+
pub fn Bound(comptime T: type) type {
|
|
71
|
+
return std.math.IntFittingRange(0, std.math.maxInt(T) + 1);
|
|
72
|
+
}
|
|
64
73
|
};
|
|
65
74
|
|
|
66
75
|
test PSRNG {
|
|
67
76
|
var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
|
|
68
|
-
try expectEqual(
|
|
77
|
+
try expectEqual(121, psrng.range(u8, 0, 256));
|
|
69
78
|
try expectEqual(false, psrng.chance(u8, 128, 256)); // 226 < 128
|
|
70
79
|
}
|
|
71
80
|
|
|
@@ -89,6 +98,10 @@ pub const Gen12 = extern struct {
|
|
|
89
98
|
self.index = (self.index + 1) % 9;
|
|
90
99
|
return val;
|
|
91
100
|
}
|
|
101
|
+
|
|
102
|
+
pub fn advance(self: *Gen12) void {
|
|
103
|
+
_ = self.next();
|
|
104
|
+
}
|
|
92
105
|
};
|
|
93
106
|
|
|
94
107
|
test Gen12 {
|
|
@@ -98,9 +111,9 @@ test Gen12 {
|
|
|
98
111
|
try expectEqual(e, rng.next());
|
|
99
112
|
}
|
|
100
113
|
|
|
101
|
-
try expectEqual(
|
|
102
|
-
try expectEqual(
|
|
103
|
-
try expectEqual(
|
|
114
|
+
try expectEqual(16, Gen12.percent(6) + 1);
|
|
115
|
+
try expectEqual(16, Gen12.percent(7) - 1);
|
|
116
|
+
try expectEqual(128, Gen12.percent(50) + 1);
|
|
104
117
|
}
|
|
105
118
|
|
|
106
119
|
// https://pkmn.cc/pokeemerald/src/random.c
|
|
@@ -188,7 +201,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
188
201
|
for (0..n) |_| _ = self.next();
|
|
189
202
|
}
|
|
190
203
|
|
|
191
|
-
pub fn range(self: *Self, comptime T: type, from: T, to: Bound(T)) T {
|
|
204
|
+
pub fn range(self: *Self, comptime T: type, from: T, to: PSRNG.Bound(T)) T {
|
|
192
205
|
assert(showdown);
|
|
193
206
|
return @intCast(@as(u64, self.next()) * (to - from) / divisor + from);
|
|
194
207
|
}
|
|
@@ -197,7 +210,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
197
210
|
self: *Self,
|
|
198
211
|
comptime T: type,
|
|
199
212
|
numerator: T,
|
|
200
|
-
denominator: Bound(T),
|
|
213
|
+
denominator: PSRNG.Bound(T),
|
|
201
214
|
) bool {
|
|
202
215
|
assert(showdown);
|
|
203
216
|
assert(denominator > 0);
|
|
@@ -228,7 +241,3 @@ fn Output(comptime gen: comptime_int) type {
|
|
|
228
241
|
else => unreachable,
|
|
229
242
|
};
|
|
230
243
|
}
|
|
231
|
-
|
|
232
|
-
fn Bound(comptime T: type) type {
|
|
233
|
-
return std.math.IntFittingRange(0, std.math.maxInt(T) + 1);
|
|
234
|
-
}
|
package/src/lib/gen1/README.md
CHANGED
|
@@ -427,34 +427,34 @@ data in the browser for ease of debugging.
|
|
|
427
427
|
|
|
428
428
|
> **NOTE:** The offsets in the following table represent *bits* and **not** bytes.
|
|
429
429
|
|
|
430
|
-
| Start | End | Data
|
|
431
|
-
| ----- | --- |
|
|
432
|
-
| 0 | 1 | `Bide`
|
|
433
|
-
| 1 | 2 | `Thrashing`
|
|
434
|
-
| 2 | 3 | `MultiHit`
|
|
435
|
-
| 3 | 4 | `Flinch`
|
|
436
|
-
| 4 | 5 | `Charging`
|
|
437
|
-
| 5 | 6 | `Binding`
|
|
438
|
-
| 6 | 7 | `Invulnerable`
|
|
439
|
-
| 7 | 8 | `Confusion`
|
|
440
|
-
| 8 | 9 | `Mist`
|
|
441
|
-
| 9 | 10 | `FocusEnergy`
|
|
442
|
-
| 10 | 11 | `Substitute`
|
|
443
|
-
| 11 | 12 | `Recharging`
|
|
444
|
-
| 12 | 13 | `Rage`
|
|
445
|
-
| 13 | 14 | `LeechSeed`
|
|
446
|
-
| 14 | 15 | `Toxic`
|
|
447
|
-
| 15 | 16 | `LightScreen`
|
|
448
|
-
| 16 | 17 | `Reflect`
|
|
449
|
-
| 17 | 18 | `Transform`
|
|
450
|
-
| 18 | 21 | `confusion`
|
|
451
|
-
| 21 | 24 | `attacks`
|
|
452
|
-
| 24 | 40 | `state`
|
|
453
|
-
| 40 | 48 | `substitute`
|
|
454
|
-
| 48 | 52 | `transform`
|
|
455
|
-
| 52 | 56 | `
|
|
456
|
-
| 56 | 59 | `
|
|
457
|
-
| 59 | 64 | `toxic`
|
|
430
|
+
| Start | End | Data | Description |
|
|
431
|
+
| ----- | --- | -------------------| ----------------------------------------------------------- |
|
|
432
|
+
| 0 | 1 | `Bide` | Whether the "Bide" volatile status is present |
|
|
433
|
+
| 1 | 2 | `Thrashing` | Whether the "Thrashing" volatile status is present |
|
|
434
|
+
| 2 | 3 | `MultiHit` | Whether the "MultiHit" volatile status is present |
|
|
435
|
+
| 3 | 4 | `Flinch` | Whether the "Flinch" volatile status is present |
|
|
436
|
+
| 4 | 5 | `Charging` | Whether the "Charging" volatile status is present |
|
|
437
|
+
| 5 | 6 | `Binding` | Whether the "Binding" volatile status is present |
|
|
438
|
+
| 6 | 7 | `Invulnerable` | Whether the "Invulnerable" volatile status is present |
|
|
439
|
+
| 7 | 8 | `Confusion` | Whether the "Confusion" volatile status is present |
|
|
440
|
+
| 8 | 9 | `Mist` | Whether the "Mist" volatile status is present |
|
|
441
|
+
| 9 | 10 | `FocusEnergy` | Whether the "FocusEnergy" volatile status is present |
|
|
442
|
+
| 10 | 11 | `Substitute` | Whether the "Substitute" volatile status is present |
|
|
443
|
+
| 11 | 12 | `Recharging` | Whether the "Recharging" volatile status is present |
|
|
444
|
+
| 12 | 13 | `Rage` | Whether the "Rage" volatile status is present |
|
|
445
|
+
| 13 | 14 | `LeechSeed` | Whether the "LeechSeed" volatile status is present |
|
|
446
|
+
| 14 | 15 | `Toxic` | Whether the "Toxic" volatile status is present |
|
|
447
|
+
| 15 | 16 | `LightScreen` | Whether the "LightScreen" volatile status is present |
|
|
448
|
+
| 16 | 17 | `Reflect` | Whether the "Reflect" volatile status is present |
|
|
449
|
+
| 17 | 18 | `Transform` | Whether the "Transform" volatile status is present |
|
|
450
|
+
| 18 | 21 | `confusion` | The remaining turns of confusion |
|
|
451
|
+
| 21 | 24 | `attacks` | The number of attacks remaining |
|
|
452
|
+
| 24 | 40 | `state` | A union of either: <ul><li>the total accumulated damage from Bide</li><li>the overwritten accuracy of certain moves</li></ul> |
|
|
453
|
+
| 40 | 48 | `substitute` | The remaining HP of the Substitute |
|
|
454
|
+
| 48 | 52 | `transform` | The identity of whom the active Pokémon is transformed into |
|
|
455
|
+
| 52 | 56 | `disable_duration` | The remaining turns the move is disabled |
|
|
456
|
+
| 56 | 59 | `disable_move` | The move slot (1-4) that's disabled |
|
|
457
|
+
| 59 | 64 | `toxic` | The number of turns toxic damage has been accumulating |
|
|
458
458
|
|
|
459
459
|
### `Pokemon`
|
|
460
460
|
|
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -50,7 +50,7 @@ pub const Summaries = extern struct {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/// Returns the `Summary` for the given `player`.
|
|
53
|
-
pub
|
|
53
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
|
|
54
54
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Summaries);
|
|
55
55
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
56
56
|
}
|
|
@@ -211,8 +211,8 @@ pub fn transitions(
|
|
|
211
211
|
) !?Stats {
|
|
212
212
|
var stats: Stats = .{};
|
|
213
213
|
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
const actions = options.actions;
|
|
215
|
+
const cap = options.cap;
|
|
216
216
|
|
|
217
217
|
var seen = std.AutoHashMap(Actions, void).init(allocator);
|
|
218
218
|
defer seen.deinit();
|
|
@@ -232,8 +232,8 @@ pub fn transitions(
|
|
|
232
232
|
_ = try b.update(c1, c2, &opts);
|
|
233
233
|
stats.updates += 1;
|
|
234
234
|
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
const p1 = b.side(.P1);
|
|
236
|
+
const p2 = b.side(.P2);
|
|
237
237
|
|
|
238
238
|
var p: Rational(u128) = .{ .p = 0, .q = 1 };
|
|
239
239
|
try frontier.append(opts.chance.actions);
|
|
@@ -247,7 +247,7 @@ pub fn transitions(
|
|
|
247
247
|
var i: usize = 0;
|
|
248
248
|
assert(frontier.items.len == 1);
|
|
249
249
|
while (i < frontier.items.len) : (i += 1) {
|
|
250
|
-
|
|
250
|
+
const template = frontier.items[i];
|
|
251
251
|
opts.calc.overrides.durations = .{
|
|
252
252
|
.p1 = template.p1.durations,
|
|
253
253
|
.p2 = template.p2.durations,
|
|
@@ -387,7 +387,7 @@ pub fn transitions(
|
|
|
387
387
|
return stats;
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
|
|
390
|
+
fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
|
|
391
391
|
for (frontier, 0..) |f, j| {
|
|
392
392
|
// TODO: is skipping this redundant check worth it?
|
|
393
393
|
if (i == j) continue;
|
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -14,12 +14,14 @@ const util = @import("../common/util.zig");
|
|
|
14
14
|
const Player = @import("../common/data.zig").Player;
|
|
15
15
|
const Optional = @import("../common/optional.zig").Optional;
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const MoveSlot = @import("data.zig").MoveSlot;
|
|
17
|
+
const data = @import("data.zig");
|
|
19
18
|
|
|
20
19
|
const enabled = options.chance;
|
|
21
20
|
const showdown = options.showdown;
|
|
22
21
|
|
|
22
|
+
const Move = data.Move;
|
|
23
|
+
const MoveSlot = data.MoveSlot;
|
|
24
|
+
|
|
23
25
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
24
26
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
25
27
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -35,19 +37,19 @@ pub const Actions = extern struct {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
/// Returns the `Action` for the given `player`.
|
|
38
|
-
pub
|
|
40
|
+
pub fn get(self: anytype, player: Player) util.PointerType(@TypeOf(self), Action) {
|
|
39
41
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Actions);
|
|
40
42
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
/// Perform a reset by clearing fields which should not persist across updates.
|
|
44
|
-
pub
|
|
46
|
+
pub fn reset(self: *Actions) void {
|
|
45
47
|
self.p1.reset();
|
|
46
48
|
self.p2.reset();
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/// Returns true if `a` is equal to `b`.
|
|
50
|
-
pub
|
|
52
|
+
pub fn eql(a: Actions, b: Actions) bool {
|
|
51
53
|
return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -157,7 +159,7 @@ pub const Action = packed struct(u64) {
|
|
|
157
159
|
pub const Field = std.meta.FieldEnum(Action);
|
|
158
160
|
|
|
159
161
|
/// Perform a reset by clearing fields which should not persist across updates.
|
|
160
|
-
pub
|
|
162
|
+
pub fn reset(self: *Action) void {
|
|
161
163
|
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
162
164
|
}
|
|
163
165
|
|
|
@@ -514,22 +516,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
514
516
|
}
|
|
515
517
|
}
|
|
516
518
|
|
|
517
|
-
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
518
|
-
if (!enabled) return;
|
|
519
|
-
|
|
520
|
-
var durations = &self.actions.get(player).durations;
|
|
521
|
-
const n = durations.disable;
|
|
522
|
-
if (turns == 0) {
|
|
523
|
-
assert(n >= 1 and n <= 8);
|
|
524
|
-
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
525
|
-
durations.disable = 0;
|
|
526
|
-
} else {
|
|
527
|
-
assert(n >= 1 and n < 8);
|
|
528
|
-
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
529
|
-
durations.disable += 1;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
519
|
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
534
520
|
if (!enabled) return;
|
|
535
521
|
|
|
@@ -546,6 +532,22 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
546
532
|
}
|
|
547
533
|
}
|
|
548
534
|
|
|
535
|
+
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
536
|
+
if (!enabled) return;
|
|
537
|
+
|
|
538
|
+
var durations = &self.actions.get(player).durations;
|
|
539
|
+
const n = durations.disable;
|
|
540
|
+
if (turns == 0) {
|
|
541
|
+
assert(n >= 1 and n <= 8);
|
|
542
|
+
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
543
|
+
durations.disable = 0;
|
|
544
|
+
} else {
|
|
545
|
+
assert(n >= 1 and n < 8);
|
|
546
|
+
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
547
|
+
durations.disable += 1;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
549
551
|
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
550
552
|
if (!enabled) return;
|
|
551
553
|
|
|
@@ -593,7 +595,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
593
595
|
pub fn metronome(self: *Self, player: Player, move: Move) Error!void {
|
|
594
596
|
if (!enabled) return;
|
|
595
597
|
|
|
596
|
-
try self.probability.update(1, Move.
|
|
598
|
+
try self.probability.update(1, Move.METRONOME.len);
|
|
597
599
|
self.actions.get(player).metronome = move;
|
|
598
600
|
}
|
|
599
601
|
};
|
|
@@ -704,19 +706,19 @@ test "Chance.damage" {
|
|
|
704
706
|
|
|
705
707
|
try chance.damage(.P1, 217);
|
|
706
708
|
if (showdown) {
|
|
707
|
-
try expectValue(
|
|
709
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
708
710
|
try expectProbability(&chance.probability, 1, 39);
|
|
709
711
|
} else {
|
|
710
712
|
try expectProbability(&chance.probability, 1, 1);
|
|
711
|
-
try expectValue(
|
|
713
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
712
714
|
|
|
713
715
|
try chance.commit(.P1, .miss);
|
|
714
716
|
try expectProbability(&chance.probability, 1, 1);
|
|
715
|
-
try expectValue(
|
|
717
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
716
718
|
|
|
717
719
|
try chance.commit(.P1, .hit);
|
|
718
720
|
try expectProbability(&chance.probability, 1, 39);
|
|
719
|
-
try expectValue(
|
|
721
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
720
722
|
}
|
|
721
723
|
}
|
|
722
724
|
|
|
@@ -768,13 +770,13 @@ test "Chance.moveSlot" {
|
|
|
768
770
|
|
|
769
771
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
770
772
|
try expectProbability(&chance.probability, 1, 2);
|
|
771
|
-
try expectValue(
|
|
773
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
772
774
|
|
|
773
775
|
chance.reset();
|
|
774
776
|
|
|
775
777
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
776
778
|
try expectProbability(&chance.probability, 1, 3);
|
|
777
|
-
try expectValue(
|
|
779
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
778
780
|
}
|
|
779
781
|
|
|
780
782
|
test "Chance.multiHit" {
|
|
@@ -782,32 +784,32 @@ test "Chance.multiHit" {
|
|
|
782
784
|
|
|
783
785
|
try chance.multiHit(.P1, 3);
|
|
784
786
|
try expectProbability(&chance.probability, 3, 8);
|
|
785
|
-
try expectValue(
|
|
787
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
786
788
|
|
|
787
789
|
chance.reset();
|
|
788
790
|
|
|
789
791
|
try chance.multiHit(.P2, 5);
|
|
790
792
|
try expectProbability(&chance.probability, 1, 8);
|
|
791
|
-
try expectValue(
|
|
793
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
792
794
|
}
|
|
793
795
|
|
|
794
796
|
test "Chance.duration" {
|
|
795
797
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
796
798
|
|
|
797
799
|
chance.duration(.sleep, .P1, .P2, 2);
|
|
798
|
-
try expectValue(
|
|
799
|
-
try expectValue(
|
|
800
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
801
|
+
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
800
802
|
|
|
801
803
|
chance.reset();
|
|
802
804
|
|
|
803
805
|
chance.duration(.binding, .P2, .P2, 4);
|
|
804
806
|
if (!showdown) {
|
|
805
|
-
try expectValue(
|
|
806
|
-
try expectValue(
|
|
807
|
+
try expectValue(0, chance.actions.p2.duration);
|
|
808
|
+
try expectValue(0, chance.actions.p2.durations.binding);
|
|
807
809
|
try chance.commit(.P2, .hit);
|
|
808
810
|
}
|
|
809
|
-
try expectValue(
|
|
810
|
-
try expectValue(
|
|
811
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
812
|
+
try expectValue(1, chance.actions.p2.durations.binding);
|
|
811
813
|
}
|
|
812
814
|
|
|
813
815
|
test "Chance.sleep" {
|
|
@@ -817,7 +819,7 @@ test "Chance.sleep" {
|
|
|
817
819
|
chance.actions.p1.durations.sleep = @intCast(i);
|
|
818
820
|
try chance.sleep(.P1, 0);
|
|
819
821
|
try expectProbability(&chance.probability, 1, d);
|
|
820
|
-
try expectValue(
|
|
822
|
+
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
821
823
|
|
|
822
824
|
chance.reset();
|
|
823
825
|
|
|
@@ -832,28 +834,6 @@ test "Chance.sleep" {
|
|
|
832
834
|
}
|
|
833
835
|
}
|
|
834
836
|
|
|
835
|
-
test "Chance.disable" {
|
|
836
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
837
|
-
|
|
838
|
-
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
839
|
-
chance.actions.p1.durations.disable = @intCast(i);
|
|
840
|
-
try chance.disable(.P1, 0);
|
|
841
|
-
try expectProbability(&chance.probability, 1, d);
|
|
842
|
-
try expectValue(@as(u4, 0), chance.actions.p1.durations.disable);
|
|
843
|
-
|
|
844
|
-
chance.reset();
|
|
845
|
-
|
|
846
|
-
if (i < 8) {
|
|
847
|
-
chance.actions.p1.durations.disable = @intCast(i);
|
|
848
|
-
try chance.disable(.P1, 1);
|
|
849
|
-
try expectProbability(&chance.probability, d - 1, d);
|
|
850
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p1.durations.disable);
|
|
851
|
-
|
|
852
|
-
chance.reset();
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
|
|
857
837
|
test "Chance.confusion" {
|
|
858
838
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
859
839
|
|
|
@@ -862,7 +842,7 @@ test "Chance.confusion" {
|
|
|
862
842
|
chance.actions.p2.durations.confusion = @intCast(i);
|
|
863
843
|
try chance.confusion(.P2, 0);
|
|
864
844
|
try expectProbability(&chance.probability, 1, d);
|
|
865
|
-
try expectValue(
|
|
845
|
+
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
866
846
|
|
|
867
847
|
chance.reset();
|
|
868
848
|
}
|
|
@@ -878,6 +858,28 @@ test "Chance.confusion" {
|
|
|
878
858
|
}
|
|
879
859
|
}
|
|
880
860
|
|
|
861
|
+
test "Chance.disable" {
|
|
862
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
863
|
+
|
|
864
|
+
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
865
|
+
chance.actions.p1.durations.disable = @intCast(i);
|
|
866
|
+
try chance.disable(.P1, 0);
|
|
867
|
+
try expectProbability(&chance.probability, 1, d);
|
|
868
|
+
try expectValue(0, chance.actions.p1.durations.disable);
|
|
869
|
+
|
|
870
|
+
chance.reset();
|
|
871
|
+
|
|
872
|
+
if (i < 8) {
|
|
873
|
+
chance.actions.p1.durations.disable = @intCast(i);
|
|
874
|
+
try chance.disable(.P1, 1);
|
|
875
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
876
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p1.durations.disable);
|
|
877
|
+
|
|
878
|
+
chance.reset();
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
881
883
|
test "Chance.attacking" {
|
|
882
884
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
883
885
|
|
|
@@ -886,7 +888,7 @@ test "Chance.attacking" {
|
|
|
886
888
|
chance.actions.p2.durations.attacking = @intCast(i);
|
|
887
889
|
try chance.attacking(.P2, 0);
|
|
888
890
|
try expectProbability(&chance.probability, 1, d);
|
|
889
|
-
try expectValue(
|
|
891
|
+
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
890
892
|
|
|
891
893
|
chance.reset();
|
|
892
894
|
}
|
|
@@ -912,7 +914,7 @@ test "Chance.binding" {
|
|
|
912
914
|
chance.actions.p1.durations.binding = @intCast(i);
|
|
913
915
|
try chance.binding(.P1, 0);
|
|
914
916
|
try expectProbability(&chance.probability, p, q);
|
|
915
|
-
try expectValue(
|
|
917
|
+
try expectValue(0, chance.actions.p1.durations.binding);
|
|
916
918
|
|
|
917
919
|
chance.reset();
|
|
918
920
|
|
|
@@ -932,7 +934,7 @@ test "Chance.psywave" {
|
|
|
932
934
|
|
|
933
935
|
try chance.psywave(.P2, 100, 150);
|
|
934
936
|
try expectProbability(&chance.probability, 1, 150);
|
|
935
|
-
try expectValue(
|
|
937
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
936
938
|
}
|
|
937
939
|
|
|
938
940
|
test "Chance.metronome" {
|
|
@@ -998,10 +1000,6 @@ const Null = struct {
|
|
|
998
1000
|
_ = .{ self, player, roll };
|
|
999
1001
|
}
|
|
1000
1002
|
|
|
1001
|
-
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
1002
|
-
_ = .{ self, player, proc, rate };
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
1003
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
1006
1004
|
_ = .{ self, player, ok };
|
|
1007
1005
|
}
|
|
@@ -1010,6 +1008,10 @@ const Null = struct {
|
|
|
1010
1008
|
_ = .{ self, player, ok };
|
|
1011
1009
|
}
|
|
1012
1010
|
|
|
1011
|
+
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
1012
|
+
_ = .{ self, player, proc, rate };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1013
1015
|
pub fn moveSlot(self: Null, player: Player, slot: u4, ms: []const MoveSlot, n: u4) Error!void {
|
|
1014
1016
|
_ = .{ self, player, slot, ms, n };
|
|
1015
1017
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
3
|
const std = @import("std");
|
|
4
4
|
|
|
@@ -1599,43 +1599,43 @@ pub const Move = enum(u8) {
|
|
|
1599
1599
|
}
|
|
1600
1600
|
|
|
1601
1601
|
/// Whether this effect activates during the "begin" step of move execution.
|
|
1602
|
-
pub
|
|
1602
|
+
pub fn onBegin(effect: Effect) bool {
|
|
1603
1603
|
return @intFromEnum(effect) > 0 and @intFromEnum(effect) <= 16;
|
|
1604
1604
|
}
|
|
1605
1605
|
|
|
1606
1606
|
/// Whether this effect lowers stats.
|
|
1607
|
-
pub
|
|
1607
|
+
pub fn isStatDown(effect: Effect) bool {
|
|
1608
1608
|
return @intFromEnum(effect) > 16 and @intFromEnum(effect) <= 21;
|
|
1609
1609
|
}
|
|
1610
1610
|
|
|
1611
1611
|
/// Whether this effect activates during the "end" step of move execution.
|
|
1612
|
-
pub
|
|
1612
|
+
pub fn onEnd(effect: Effect) bool {
|
|
1613
1613
|
return @intFromEnum(effect) > 16 and @intFromEnum(effect) <= 31;
|
|
1614
1614
|
}
|
|
1615
1615
|
|
|
1616
1616
|
/// Whether this effect is considered to "always happen".
|
|
1617
|
-
pub
|
|
1617
|
+
pub fn alwaysHappens(effect: Effect) bool {
|
|
1618
1618
|
return @intFromEnum(effect) > 31 and @intFromEnum(effect) <= 38;
|
|
1619
1619
|
}
|
|
1620
1620
|
|
|
1621
1621
|
/// Whether this effect is handled specially by the engine.
|
|
1622
|
-
pub
|
|
1622
|
+
pub fn isSpecial(effect: Effect) bool {
|
|
1623
1623
|
// NB: isSpecial includes isMulti up to Twineedle
|
|
1624
1624
|
return @intFromEnum(effect) > 31 and @intFromEnum(effect) <= 46;
|
|
1625
1625
|
}
|
|
1626
1626
|
|
|
1627
1627
|
/// Whether this effect is a multi-hit effect.
|
|
1628
|
-
pub
|
|
1628
|
+
pub fn isMulti(effect: Effect) bool {
|
|
1629
1629
|
return @intFromEnum(effect) > 44 and @intFromEnum(effect) <= 47;
|
|
1630
1630
|
}
|
|
1631
1631
|
|
|
1632
1632
|
/// Whether this effect is has chance of lowering stats.
|
|
1633
|
-
pub
|
|
1633
|
+
pub fn isStatDownChance(effect: Effect) bool {
|
|
1634
1634
|
return @intFromEnum(effect) > 47 and @intFromEnum(effect) <= 51;
|
|
1635
1635
|
}
|
|
1636
1636
|
|
|
1637
1637
|
/// Whether this effect has a secondary chance.
|
|
1638
|
-
pub
|
|
1638
|
+
pub fn isSecondaryChance(effect: Effect) bool {
|
|
1639
1639
|
// NB: isSecondaryChance includes isStatDownChance as well as Twineedle
|
|
1640
1640
|
return (@intFromEnum(effect) > 46 and @intFromEnum(effect) <= 61);
|
|
1641
1641
|
}
|
|
@@ -1838,8 +1838,22 @@ pub const Move = enum(u8) {
|
|
|
1838
1838
|
/// The number of Pokémon moves in this generation.
|
|
1839
1839
|
pub const size = 165;
|
|
1840
1840
|
|
|
1841
|
+
/// Pokémon moves that can be called by Metronome in this generation.
|
|
1842
|
+
pub const METRONOME = init: {
|
|
1843
|
+
var num = 0;
|
|
1844
|
+
var moves: [163]Move = undefined;
|
|
1845
|
+
for (1..Move.size) |i| {
|
|
1846
|
+
if (i != @intFromEnum(Move.Metronome)) {
|
|
1847
|
+
moves[num] = @enumFromInt(i);
|
|
1848
|
+
num += 1;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
assert(num == moves.len);
|
|
1852
|
+
break :init moves;
|
|
1853
|
+
};
|
|
1854
|
+
|
|
1841
1855
|
/// Returns the `Data` corresponding to the move.
|
|
1842
|
-
pub
|
|
1856
|
+
pub fn get(id: Move) Data {
|
|
1843
1857
|
assert(id != .None and id != .SKIP_TURN);
|
|
1844
1858
|
return DATA[@intFromEnum(id) - 1];
|
|
1845
1859
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
3
|
const std = @import("std");
|
|
4
4
|
|
|
@@ -1091,8 +1091,8 @@ 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
|
|
1095
|
-
pub
|
|
1094
|
+
/// The Pokémon's critical hit ratio out of 256.
|
|
1095
|
+
pub fn chance(id: Species) u8 {
|
|
1096
1096
|
assert(id != .None);
|
|
1097
1097
|
return CHANCES[@intFromEnum(id) - 1];
|
|
1098
1098
|
}
|