@pkmn/engine 0.1.0-dev.46307385 → 0.1.0-dev.49f6bf26
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/ids.json +12 -12
- package/build/pkg/data/layout.json +2 -2
- package/build/pkg/data/protocol.json +69 -12
- 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 +57 -38
- 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 +84 -64
- package/package.json +43 -22
- package/src/bin/install-pkmn-engine +14 -14
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/optional.zig +9 -2
- package/src/lib/common/protocol.zig +792 -319
- package/src/lib/common/rational.zig +12 -8
- package/src/lib/common/rng.zig +20 -11
- package/src/lib/gen1/README.md +31 -29
- package/src/lib/gen1/calc.zig +9 -10
- package/src/lib/gen1/chance.zig +74 -58
- package/src/lib/gen1/data/moves.zig +192 -180
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +7 -7
- package/src/lib/gen1/data.zig +47 -59
- package/src/lib/gen1/helpers.zig +33 -48
- package/src/lib/gen1/mechanics.zig +317 -368
- package/src/lib/gen2/README.md +93 -72
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1314 -0
- package/src/lib/gen2/data/items.zig +12 -18
- package/src/lib/gen2/data/moves.zig +663 -569
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +134 -31
- package/src/lib/gen2/data.zig +106 -79
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +8 -12
- package/src/lib/gen2/mechanics.zig +3502 -9
- package/src/lib/{binding/node.zig → node.zig} +10 -13
- 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
|
|
|
@@ -275,7 +277,7 @@ pub const BigRational = struct {
|
|
|
275
277
|
/// Create a new BigRational wrapper. A small amount of memory will be allocated on
|
|
276
278
|
/// initialization. Not default initialized to 1 - you must explicitly `reset` first.
|
|
277
279
|
pub fn init(alloc: std.mem.Allocator) !BigRational {
|
|
278
|
-
return
|
|
280
|
+
return .{ .val = try std.math.big.Rational.init(alloc) };
|
|
279
281
|
}
|
|
280
282
|
|
|
281
283
|
/// Frees all memory associated with a rational.
|
|
@@ -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
|
|
|
@@ -64,8 +69,8 @@ pub const PSRNG = extern struct {
|
|
|
64
69
|
};
|
|
65
70
|
|
|
66
71
|
test PSRNG {
|
|
67
|
-
var psrng =
|
|
68
|
-
try expectEqual(
|
|
72
|
+
var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
|
|
73
|
+
try expectEqual(121, psrng.range(u8, 0, 256));
|
|
69
74
|
try expectEqual(false, psrng.chance(u8, 128, 256)); // 226 < 128
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -79,7 +84,7 @@ pub const Gen12 = extern struct {
|
|
|
79
84
|
assert(@sizeOf(Gen12) == 10);
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
pub fn percent(p:
|
|
87
|
+
pub fn percent(comptime p: comptime_int) u8 {
|
|
83
88
|
return @intCast((@as(u16, p) * 0xFF) / 100);
|
|
84
89
|
}
|
|
85
90
|
|
|
@@ -89,18 +94,22 @@ pub const Gen12 = extern struct {
|
|
|
89
94
|
self.index = (self.index + 1) % 9;
|
|
90
95
|
return val;
|
|
91
96
|
}
|
|
97
|
+
|
|
98
|
+
pub fn advance(self: *Gen12) void {
|
|
99
|
+
_ = self.next();
|
|
100
|
+
}
|
|
92
101
|
};
|
|
93
102
|
|
|
94
103
|
test Gen12 {
|
|
95
104
|
const expected = [_]u8{ 6, 11, 16, 21, 26, 31, 36, 41, 46, 31, 56, 81 };
|
|
96
|
-
var rng =
|
|
105
|
+
var rng: Gen12 = .{ .seed = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } };
|
|
97
106
|
for (expected) |e| {
|
|
98
107
|
try expectEqual(e, rng.next());
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
try expectEqual(
|
|
102
|
-
try expectEqual(
|
|
103
|
-
try expectEqual(
|
|
110
|
+
try expectEqual(16, Gen12.percent(6) + 1);
|
|
111
|
+
try expectEqual(16, Gen12.percent(7) - 1);
|
|
112
|
+
try expectEqual(128, Gen12.percent(50) + 1);
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
// https://pkmn.cc/pokeemerald/src/random.c
|
|
@@ -129,7 +138,7 @@ test Gen34 {
|
|
|
129
138
|
.{ 0x80000000, 5, 0x0E425287 }, .{ 0x80000000, 10, 0x6F2CF4B2 },
|
|
130
139
|
};
|
|
131
140
|
for (data) |d| {
|
|
132
|
-
var rng =
|
|
141
|
+
var rng: Gen34 = .{ .seed = d[0] };
|
|
133
142
|
for (0..d[1]) |_| _ = rng.next();
|
|
134
143
|
try expectEqual(d[2], rng.seed);
|
|
135
144
|
}
|
|
@@ -161,7 +170,7 @@ test Gen56 {
|
|
|
161
170
|
.{ 0x8000000000000000, 10, 0xE7795501267F125A },
|
|
162
171
|
};
|
|
163
172
|
for (data) |d| {
|
|
164
|
-
var rng =
|
|
173
|
+
var rng: Gen56 = .{ .seed = d[0] };
|
|
165
174
|
for (0..d[1]) |_| _ = rng.next();
|
|
166
175
|
try expectEqual(d[2], rng.seed);
|
|
167
176
|
}
|
|
@@ -213,7 +222,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
213
222
|
test FixedRNG {
|
|
214
223
|
const Type = if (showdown) u32 else u8;
|
|
215
224
|
const expected = [_]Type{ 42, 255, 0 };
|
|
216
|
-
var rng
|
|
225
|
+
var rng: FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
217
226
|
for (expected) |e| {
|
|
218
227
|
try expectEqual(e, rng.next());
|
|
219
228
|
}
|
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
|
|
|
@@ -612,7 +612,8 @@ engine, but the following moves have their broken behavior preserved in `-Dshowd
|
|
|
612
612
|
- **Roar** / **Whirlwind**: these moves can miss on Pokémon Showdown (and advance the RNG when
|
|
613
613
|
checking) which is incorrect (these moves should always fail, but don't check accuracy or advance
|
|
614
614
|
the RNG). More importantly, these moves should *not* cause the tracked last battle damage to be
|
|
615
|
-
zeroed, but on Pokémon Showdown they do.
|
|
615
|
+
zeroed, but on Pokémon Showdown they do. These should also `|-fail|...|[still]` instead of doing
|
|
616
|
+
nothing.
|
|
616
617
|
- **Substitute**: in addition to the [Substitute + Confusion
|
|
617
618
|
glitch](https://pkmn.cc/bulba-glitch-1#Substitute_.2B_Confusion_glitch) not being implemented
|
|
618
619
|
correctly (covered earlier), the [Substitute 1/4
|
|
@@ -634,6 +635,7 @@ only pedantic UI correctness):
|
|
|
634
635
|
|
|
635
636
|
- **Rage**: Rage should report the `|-boost|` before the Disable `|-miss|`, not after.
|
|
636
637
|
- **Haze**: Haze clears the status/volatiles in an incorrect order.
|
|
638
|
+
- **Teleport**: Teleport should `|-fail|...|[still]` instead of doing nothing.
|
|
637
639
|
- **Twineedle**: the `|-hitcount|` ("Hit 2 times") should come before the `|-status|` message, not
|
|
638
640
|
after.
|
|
639
641
|
- **Thrash** / **Petal Dance**: confusion should silently get applied before the `|move|` message,
|
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -29,7 +29,6 @@ const Optional = optional.Optional;
|
|
|
29
29
|
const Rational = rational.Rational;
|
|
30
30
|
|
|
31
31
|
const PointerType = util.PointerType;
|
|
32
|
-
const FieldType = util.FieldType;
|
|
33
32
|
|
|
34
33
|
const Actions = chance.Actions;
|
|
35
34
|
const Action = chance.Action;
|
|
@@ -51,7 +50,7 @@ pub const Summaries = extern struct {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
/// Returns the `Summary` for the given `player`.
|
|
54
|
-
pub
|
|
53
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
|
|
55
54
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Summaries);
|
|
56
55
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
57
56
|
}
|
|
@@ -158,7 +157,7 @@ pub const Calc = struct {
|
|
|
158
157
|
|
|
159
158
|
/// Null object pattern implementation of Generation I `Calc` which does nothing, though damage
|
|
160
159
|
/// calculator support should additionally be turned off entirely via `options.calc`.
|
|
161
|
-
pub const NULL =
|
|
160
|
+
pub const NULL: Null = .{};
|
|
162
161
|
|
|
163
162
|
const Null = struct {
|
|
164
163
|
pub fn overridden(
|
|
@@ -212,8 +211,8 @@ pub fn transitions(
|
|
|
212
211
|
) !?Stats {
|
|
213
212
|
var stats: Stats = .{};
|
|
214
213
|
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
const actions = options.actions;
|
|
215
|
+
const cap = options.cap;
|
|
217
216
|
|
|
218
217
|
var seen = std.AutoHashMap(Actions, void).init(allocator);
|
|
219
218
|
defer seen.deinit();
|
|
@@ -233,8 +232,8 @@ pub fn transitions(
|
|
|
233
232
|
_ = try b.update(c1, c2, &opts);
|
|
234
233
|
stats.updates += 1;
|
|
235
234
|
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
const p1 = b.side(.P1);
|
|
236
|
+
const p2 = b.side(.P2);
|
|
238
237
|
|
|
239
238
|
var p: Rational(u128) = .{ .p = 0, .q = 1 };
|
|
240
239
|
try frontier.append(opts.chance.actions);
|
|
@@ -248,7 +247,7 @@ pub fn transitions(
|
|
|
248
247
|
var i: usize = 0;
|
|
249
248
|
assert(frontier.items.len == 1);
|
|
250
249
|
while (i < frontier.items.len) : (i += 1) {
|
|
251
|
-
|
|
250
|
+
const template = frontier.items[i];
|
|
252
251
|
opts.calc.overrides.durations = .{
|
|
253
252
|
.p1 = template.p1.durations,
|
|
254
253
|
.p2 = template.p2.durations,
|
|
@@ -256,7 +255,7 @@ pub fn transitions(
|
|
|
256
255
|
|
|
257
256
|
try debug(writer, template, true, .{ .color = i, .bold = true, .background = true });
|
|
258
257
|
|
|
259
|
-
var a =
|
|
258
|
+
var a: Actions = .{ .p1 = .{ .metronome = p1_move }, .p2 = .{ .metronome = p2_move } };
|
|
260
259
|
|
|
261
260
|
for (Rolls.speedTie(template.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
|
|
262
261
|
for (Rolls.sleep(template.p1, actions.p1)) |p1_slp| { a.p1.durations.sleep = p1_slp;
|
|
@@ -388,7 +387,7 @@ pub fn transitions(
|
|
|
388
387
|
return stats;
|
|
389
388
|
}
|
|
390
389
|
|
|
391
|
-
|
|
390
|
+
fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
|
|
392
391
|
for (frontier, 0..) |f, j| {
|
|
393
392
|
// TODO: is skipping this redundant check worth it?
|
|
394
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
|
|
|
@@ -115,7 +117,7 @@ test Actions {
|
|
|
115
117
|
|
|
116
118
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
117
119
|
/// single player.
|
|
118
|
-
pub const Action = packed struct {
|
|
120
|
+
pub const Action = packed struct(u64) {
|
|
119
121
|
/// If not 0, the roll to be returned Rolls.damage.
|
|
120
122
|
damage: u8 = 0,
|
|
121
123
|
|
|
@@ -156,12 +158,8 @@ pub const Action = packed struct {
|
|
|
156
158
|
|
|
157
159
|
pub const Field = std.meta.FieldEnum(Action);
|
|
158
160
|
|
|
159
|
-
comptime {
|
|
160
|
-
assert(@sizeOf(Action) == 8);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
161
|
/// Perform a reset by clearing fields which should not persist across updates.
|
|
164
|
-
pub
|
|
162
|
+
pub fn reset(self: *Action) void {
|
|
165
163
|
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
166
164
|
}
|
|
167
165
|
|
|
@@ -219,7 +217,7 @@ pub const Action = packed struct {
|
|
|
219
217
|
};
|
|
220
218
|
|
|
221
219
|
test Action {
|
|
222
|
-
var a =
|
|
220
|
+
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
223
221
|
a.reset();
|
|
224
222
|
|
|
225
223
|
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
@@ -239,7 +237,7 @@ pub const Durations = extern struct {
|
|
|
239
237
|
|
|
240
238
|
/// Observed values for various durations that need to be tracked in order to properly
|
|
241
239
|
/// deduplicate transitions with a primary key. TODO
|
|
242
|
-
pub const Duration = packed struct {
|
|
240
|
+
pub const Duration = packed struct(u16) {
|
|
243
241
|
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
244
242
|
sleep: u3 = 0,
|
|
245
243
|
/// The number of turns a Pokémon has been observed to be confused.
|
|
@@ -252,10 +250,6 @@ pub const Duration = packed struct {
|
|
|
252
250
|
binding: u3 = 0,
|
|
253
251
|
|
|
254
252
|
pub const Field = std.meta.FieldEnum(Duration);
|
|
255
|
-
|
|
256
|
-
comptime {
|
|
257
|
-
assert(@sizeOf(Duration) == 2);
|
|
258
|
-
}
|
|
259
253
|
};
|
|
260
254
|
|
|
261
255
|
pub const Commit = enum { hit, miss, binding };
|
|
@@ -522,22 +516,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
522
516
|
}
|
|
523
517
|
}
|
|
524
518
|
|
|
525
|
-
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
526
|
-
if (!enabled) return;
|
|
527
|
-
|
|
528
|
-
var durations = &self.actions.get(player).durations;
|
|
529
|
-
const n = durations.disable;
|
|
530
|
-
if (turns == 0) {
|
|
531
|
-
assert(n >= 1 and n <= 8);
|
|
532
|
-
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
533
|
-
durations.disable = 0;
|
|
534
|
-
} else {
|
|
535
|
-
assert(n >= 1 and n < 8);
|
|
536
|
-
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
537
|
-
durations.disable += 1;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
519
|
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
542
520
|
if (!enabled) return;
|
|
543
521
|
|
|
@@ -554,6 +532,22 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
554
532
|
}
|
|
555
533
|
}
|
|
556
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
|
+
|
|
557
551
|
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
558
552
|
if (!enabled) return;
|
|
559
553
|
|
|
@@ -601,7 +595,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
601
595
|
pub fn metronome(self: *Self, player: Player, move: Move) Error!void {
|
|
602
596
|
if (!enabled) return;
|
|
603
597
|
|
|
604
|
-
try self.probability.update(1, Move.
|
|
598
|
+
try self.probability.update(1, Move.METRONOME.len);
|
|
605
599
|
self.actions.get(player).metronome = move;
|
|
606
600
|
}
|
|
607
601
|
};
|
|
@@ -712,19 +706,19 @@ test "Chance.damage" {
|
|
|
712
706
|
|
|
713
707
|
try chance.damage(.P1, 217);
|
|
714
708
|
if (showdown) {
|
|
715
|
-
try expectValue(
|
|
709
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
716
710
|
try expectProbability(&chance.probability, 1, 39);
|
|
717
711
|
} else {
|
|
718
712
|
try expectProbability(&chance.probability, 1, 1);
|
|
719
|
-
try expectValue(
|
|
713
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
720
714
|
|
|
721
715
|
try chance.commit(.P1, .miss);
|
|
722
716
|
try expectProbability(&chance.probability, 1, 1);
|
|
723
|
-
try expectValue(
|
|
717
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
724
718
|
|
|
725
719
|
try chance.commit(.P1, .hit);
|
|
726
720
|
try expectProbability(&chance.probability, 1, 39);
|
|
727
|
-
try expectValue(
|
|
721
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
728
722
|
}
|
|
729
723
|
}
|
|
730
724
|
|
|
@@ -776,13 +770,13 @@ test "Chance.moveSlot" {
|
|
|
776
770
|
|
|
777
771
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
778
772
|
try expectProbability(&chance.probability, 1, 2);
|
|
779
|
-
try expectValue(
|
|
773
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
780
774
|
|
|
781
775
|
chance.reset();
|
|
782
776
|
|
|
783
777
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
784
778
|
try expectProbability(&chance.probability, 1, 3);
|
|
785
|
-
try expectValue(
|
|
779
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
786
780
|
}
|
|
787
781
|
|
|
788
782
|
test "Chance.multiHit" {
|
|
@@ -790,32 +784,32 @@ test "Chance.multiHit" {
|
|
|
790
784
|
|
|
791
785
|
try chance.multiHit(.P1, 3);
|
|
792
786
|
try expectProbability(&chance.probability, 3, 8);
|
|
793
|
-
try expectValue(
|
|
787
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
794
788
|
|
|
795
789
|
chance.reset();
|
|
796
790
|
|
|
797
791
|
try chance.multiHit(.P2, 5);
|
|
798
792
|
try expectProbability(&chance.probability, 1, 8);
|
|
799
|
-
try expectValue(
|
|
793
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
800
794
|
}
|
|
801
795
|
|
|
802
796
|
test "Chance.duration" {
|
|
803
797
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
804
798
|
|
|
805
799
|
chance.duration(.sleep, .P1, .P2, 2);
|
|
806
|
-
try expectValue(
|
|
807
|
-
try expectValue(
|
|
800
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
801
|
+
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
808
802
|
|
|
809
803
|
chance.reset();
|
|
810
804
|
|
|
811
805
|
chance.duration(.binding, .P2, .P2, 4);
|
|
812
806
|
if (!showdown) {
|
|
813
|
-
try expectValue(
|
|
814
|
-
try expectValue(
|
|
807
|
+
try expectValue(0, chance.actions.p2.duration);
|
|
808
|
+
try expectValue(0, chance.actions.p2.durations.binding);
|
|
815
809
|
try chance.commit(.P2, .hit);
|
|
816
810
|
}
|
|
817
|
-
try expectValue(
|
|
818
|
-
try expectValue(
|
|
811
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
812
|
+
try expectValue(1, chance.actions.p2.durations.binding);
|
|
819
813
|
}
|
|
820
814
|
|
|
821
815
|
test "Chance.sleep" {
|
|
@@ -825,7 +819,7 @@ test "Chance.sleep" {
|
|
|
825
819
|
chance.actions.p1.durations.sleep = @intCast(i);
|
|
826
820
|
try chance.sleep(.P1, 0);
|
|
827
821
|
try expectProbability(&chance.probability, 1, d);
|
|
828
|
-
try expectValue(
|
|
822
|
+
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
829
823
|
|
|
830
824
|
chance.reset();
|
|
831
825
|
|
|
@@ -848,7 +842,7 @@ test "Chance.confusion" {
|
|
|
848
842
|
chance.actions.p2.durations.confusion = @intCast(i);
|
|
849
843
|
try chance.confusion(.P2, 0);
|
|
850
844
|
try expectProbability(&chance.probability, 1, d);
|
|
851
|
-
try expectValue(
|
|
845
|
+
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
852
846
|
|
|
853
847
|
chance.reset();
|
|
854
848
|
}
|
|
@@ -864,6 +858,28 @@ test "Chance.confusion" {
|
|
|
864
858
|
}
|
|
865
859
|
}
|
|
866
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
|
+
|
|
867
883
|
test "Chance.attacking" {
|
|
868
884
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
869
885
|
|
|
@@ -872,7 +888,7 @@ test "Chance.attacking" {
|
|
|
872
888
|
chance.actions.p2.durations.attacking = @intCast(i);
|
|
873
889
|
try chance.attacking(.P2, 0);
|
|
874
890
|
try expectProbability(&chance.probability, 1, d);
|
|
875
|
-
try expectValue(
|
|
891
|
+
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
876
892
|
|
|
877
893
|
chance.reset();
|
|
878
894
|
}
|
|
@@ -898,7 +914,7 @@ test "Chance.binding" {
|
|
|
898
914
|
chance.actions.p1.durations.binding = @intCast(i);
|
|
899
915
|
try chance.binding(.P1, 0);
|
|
900
916
|
try expectProbability(&chance.probability, p, q);
|
|
901
|
-
try expectValue(
|
|
917
|
+
try expectValue(0, chance.actions.p1.durations.binding);
|
|
902
918
|
|
|
903
919
|
chance.reset();
|
|
904
920
|
|
|
@@ -918,7 +934,7 @@ test "Chance.psywave" {
|
|
|
918
934
|
|
|
919
935
|
try chance.psywave(.P2, 100, 150);
|
|
920
936
|
try expectProbability(&chance.probability, 1, 150);
|
|
921
|
-
try expectValue(
|
|
937
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
922
938
|
}
|
|
923
939
|
|
|
924
940
|
test "Chance.metronome" {
|
|
@@ -947,7 +963,7 @@ pub fn expectValue(a: anytype, b: anytype) !void {
|
|
|
947
963
|
|
|
948
964
|
/// Null object pattern implementation of Generation I `Chance` which does nothing, though chance
|
|
949
965
|
/// tracking should additionally be turned off entirely via `options.chance`.
|
|
950
|
-
pub const NULL =
|
|
966
|
+
pub const NULL: Null = .{};
|
|
951
967
|
|
|
952
968
|
const Null = struct {
|
|
953
969
|
pub const Error = error{};
|
|
@@ -984,10 +1000,6 @@ const Null = struct {
|
|
|
984
1000
|
_ = .{ self, player, roll };
|
|
985
1001
|
}
|
|
986
1002
|
|
|
987
|
-
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
988
|
-
_ = .{ self, player, proc, rate };
|
|
989
|
-
}
|
|
990
|
-
|
|
991
1003
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
992
1004
|
_ = .{ self, player, ok };
|
|
993
1005
|
}
|
|
@@ -996,6 +1008,10 @@ const Null = struct {
|
|
|
996
1008
|
_ = .{ self, player, ok };
|
|
997
1009
|
}
|
|
998
1010
|
|
|
1011
|
+
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
1012
|
+
_ = .{ self, player, proc, rate };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
999
1015
|
pub fn moveSlot(self: Null, player: Player, slot: u4, ms: []const MoveSlot, n: u4) Error!void {
|
|
1000
1016
|
_ = .{ self, player, slot, ms, n };
|
|
1001
1017
|
}
|