@pkmn/engine 0.1.0-dev.90d6c14c → 0.1.0-dev.9257876b
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 +52 -21
- 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 +125 -102
- package/package.json +43 -23
- package/src/bin/install-pkmn-engine +27 -19
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/optional.zig +9 -2
- package/src/lib/common/protocol.zig +805 -327
- package/src/lib/common/rational.zig +12 -8
- package/src/lib/common/rng.zig +26 -17
- package/src/lib/gen1/calc.zig +9 -10
- package/src/lib/gen1/chance.zig +83 -66
- package/src/lib/gen1/data/moves.zig +192 -180
- package/src/lib/gen1/data/species.zig +3 -3
- package/src/lib/gen1/data/types.zig +7 -7
- package/src/lib/gen1/data.zig +60 -67
- package/src/lib/gen1/helpers.zig +261 -123
- package/src/lib/gen1/mechanics.zig +370 -403
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1315 -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 +3587 -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
- package/src/lib/gen1/README.md +0 -812
- package/src/lib/gen2/README.md +0 -308
|
@@ -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
|
|
|
@@ -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
|
-
var psrng =
|
|
68
|
-
try expectEqual(
|
|
76
|
+
var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
|
|
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
|
|
|
@@ -79,7 +88,7 @@ pub const Gen12 = extern struct {
|
|
|
79
88
|
assert(@sizeOf(Gen12) == 10);
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
pub fn percent(p:
|
|
91
|
+
pub fn percent(comptime p: comptime_int) u8 {
|
|
83
92
|
return @intCast((@as(u16, p) * 0xFF) / 100);
|
|
84
93
|
}
|
|
85
94
|
|
|
@@ -89,18 +98,22 @@ 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 {
|
|
95
108
|
const expected = [_]u8{ 6, 11, 16, 21, 26, 31, 36, 41, 46, 31, 56, 81 };
|
|
96
|
-
var rng =
|
|
109
|
+
var rng: Gen12 = .{ .seed = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } };
|
|
97
110
|
for (expected) |e| {
|
|
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
|
|
@@ -129,7 +142,7 @@ test Gen34 {
|
|
|
129
142
|
.{ 0x80000000, 5, 0x0E425287 }, .{ 0x80000000, 10, 0x6F2CF4B2 },
|
|
130
143
|
};
|
|
131
144
|
for (data) |d| {
|
|
132
|
-
var rng =
|
|
145
|
+
var rng: Gen34 = .{ .seed = d[0] };
|
|
133
146
|
for (0..d[1]) |_| _ = rng.next();
|
|
134
147
|
try expectEqual(d[2], rng.seed);
|
|
135
148
|
}
|
|
@@ -161,7 +174,7 @@ test Gen56 {
|
|
|
161
174
|
.{ 0x8000000000000000, 10, 0xE7795501267F125A },
|
|
162
175
|
};
|
|
163
176
|
for (data) |d| {
|
|
164
|
-
var rng =
|
|
177
|
+
var rng: Gen56 = .{ .seed = d[0] };
|
|
165
178
|
for (0..d[1]) |_| _ = rng.next();
|
|
166
179
|
try expectEqual(d[2], rng.seed);
|
|
167
180
|
}
|
|
@@ -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);
|
|
@@ -213,7 +226,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
213
226
|
test FixedRNG {
|
|
214
227
|
const Type = if (showdown) u32 else u8;
|
|
215
228
|
const expected = [_]Type{ 42, 255, 0 };
|
|
216
|
-
var rng
|
|
229
|
+
var rng: FixedRNG(1, expected.len) = .{ .rolls = expected };
|
|
217
230
|
for (expected) |e| {
|
|
218
231
|
try expectEqual(e, rng.next());
|
|
219
232
|
}
|
|
@@ -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/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
|
|
|
@@ -90,13 +92,14 @@ pub const Actions = extern struct {
|
|
|
90
92
|
}
|
|
91
93
|
};
|
|
92
94
|
|
|
95
|
+
// TODO: ziglang/zig#19730
|
|
93
96
|
test Actions {
|
|
94
97
|
const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
|
|
95
98
|
const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
|
|
96
99
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
97
|
-
const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
98
|
-
const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
99
|
-
const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
100
|
+
// const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
101
|
+
// const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
102
|
+
// const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
100
103
|
|
|
101
104
|
try expect(a.eql(a));
|
|
102
105
|
try expect(!a.eql(b));
|
|
@@ -109,13 +112,13 @@ test Actions {
|
|
|
109
112
|
try expect(b.matches(a));
|
|
110
113
|
try expect(!a.matches(c));
|
|
111
114
|
try expect(!c.matches(a));
|
|
112
|
-
try expect(d.matches(e));
|
|
113
|
-
try expect(!d.matches(f));
|
|
115
|
+
// try expect(d.matches(e));
|
|
116
|
+
// try expect(!d.matches(f));
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
117
120
|
/// single player.
|
|
118
|
-
pub const Action = packed struct {
|
|
121
|
+
pub const Action = packed struct(u64) {
|
|
119
122
|
/// If not 0, the roll to be returned Rolls.damage.
|
|
120
123
|
damage: u8 = 0,
|
|
121
124
|
|
|
@@ -126,7 +129,7 @@ pub const Action = packed struct {
|
|
|
126
129
|
/// If not None, the value to be returned for
|
|
127
130
|
/// Rolls.{confusionChance,secondaryChance,poisonChance}.
|
|
128
131
|
secondary_chance: Optional(bool) = .None,
|
|
129
|
-
/// If not None, the
|
|
132
|
+
/// If not None, the Player to be returned by Rolls.speedTie.
|
|
130
133
|
speed_tie: Optional(Player) = .None,
|
|
131
134
|
|
|
132
135
|
/// If not None, the value to return for Rolls.confused.
|
|
@@ -156,12 +159,8 @@ pub const Action = packed struct {
|
|
|
156
159
|
|
|
157
160
|
pub const Field = std.meta.FieldEnum(Action);
|
|
158
161
|
|
|
159
|
-
comptime {
|
|
160
|
-
assert(@sizeOf(Action) == 8);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
162
|
/// Perform a reset by clearing fields which should not persist across updates.
|
|
164
|
-
pub
|
|
163
|
+
pub fn reset(self: *Action) void {
|
|
165
164
|
self.* = @bitCast(@as(u64, @bitCast(self.*)) & DURATIONS);
|
|
166
165
|
}
|
|
167
166
|
|
|
@@ -181,9 +180,9 @@ pub const Action = packed struct {
|
|
|
181
180
|
if (v != 0) {
|
|
182
181
|
if (printed) try writer.writeAll(", ");
|
|
183
182
|
if (shape) {
|
|
184
|
-
try writer.print("{s}:?", .{
|
|
183
|
+
try writer.print("{s}:?", .{f.name});
|
|
185
184
|
} else {
|
|
186
|
-
try writer.print("{s}:{d}", .{
|
|
185
|
+
try writer.print("{s}:{d}", .{ f.name, v });
|
|
187
186
|
}
|
|
188
187
|
printed = true;
|
|
189
188
|
}
|
|
@@ -219,7 +218,7 @@ pub const Action = packed struct {
|
|
|
219
218
|
};
|
|
220
219
|
|
|
221
220
|
test Action {
|
|
222
|
-
var a =
|
|
221
|
+
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
223
222
|
a.reset();
|
|
224
223
|
|
|
225
224
|
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
@@ -239,7 +238,7 @@ pub const Durations = extern struct {
|
|
|
239
238
|
|
|
240
239
|
/// Observed values for various durations that need to be tracked in order to properly
|
|
241
240
|
/// deduplicate transitions with a primary key. TODO
|
|
242
|
-
pub const Duration = packed struct {
|
|
241
|
+
pub const Duration = packed struct(u16) {
|
|
243
242
|
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
244
243
|
sleep: u3 = 0,
|
|
245
244
|
/// The number of turns a Pokémon has been observed to be confused.
|
|
@@ -252,10 +251,6 @@ pub const Duration = packed struct {
|
|
|
252
251
|
binding: u3 = 0,
|
|
253
252
|
|
|
254
253
|
pub const Field = std.meta.FieldEnum(Duration);
|
|
255
|
-
|
|
256
|
-
comptime {
|
|
257
|
-
assert(@sizeOf(Duration) == 2);
|
|
258
|
-
}
|
|
259
254
|
};
|
|
260
255
|
|
|
261
256
|
pub const Commit = enum { hit, miss, binding };
|
|
@@ -522,22 +517,6 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
522
517
|
}
|
|
523
518
|
}
|
|
524
519
|
|
|
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
520
|
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
542
521
|
if (!enabled) return;
|
|
543
522
|
|
|
@@ -554,6 +533,22 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
554
533
|
}
|
|
555
534
|
}
|
|
556
535
|
|
|
536
|
+
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
537
|
+
if (!enabled) return;
|
|
538
|
+
|
|
539
|
+
var durations = &self.actions.get(player).durations;
|
|
540
|
+
const n = durations.disable;
|
|
541
|
+
if (turns == 0) {
|
|
542
|
+
assert(n >= 1 and n <= 8);
|
|
543
|
+
if (n != 8) try self.probability.update(1, 9 - @as(u4, n));
|
|
544
|
+
durations.disable = 0;
|
|
545
|
+
} else {
|
|
546
|
+
assert(n >= 1 and n < 8);
|
|
547
|
+
try self.probability.update(9 - @as(u4, n) - 1, 9 - @as(u4, n));
|
|
548
|
+
durations.disable += 1;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
557
552
|
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
558
553
|
if (!enabled) return;
|
|
559
554
|
|
|
@@ -601,7 +596,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
601
596
|
pub fn metronome(self: *Self, player: Player, move: Move) Error!void {
|
|
602
597
|
if (!enabled) return;
|
|
603
598
|
|
|
604
|
-
try self.probability.update(1, Move.
|
|
599
|
+
try self.probability.update(1, Move.METRONOME.len);
|
|
605
600
|
self.actions.get(player).metronome = move;
|
|
606
601
|
}
|
|
607
602
|
};
|
|
@@ -712,19 +707,19 @@ test "Chance.damage" {
|
|
|
712
707
|
|
|
713
708
|
try chance.damage(.P1, 217);
|
|
714
709
|
if (showdown) {
|
|
715
|
-
try expectValue(
|
|
710
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
716
711
|
try expectProbability(&chance.probability, 1, 39);
|
|
717
712
|
} else {
|
|
718
713
|
try expectProbability(&chance.probability, 1, 1);
|
|
719
|
-
try expectValue(
|
|
714
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
720
715
|
|
|
721
716
|
try chance.commit(.P1, .miss);
|
|
722
717
|
try expectProbability(&chance.probability, 1, 1);
|
|
723
|
-
try expectValue(
|
|
718
|
+
try expectValue(0, chance.actions.p1.damage);
|
|
724
719
|
|
|
725
720
|
try chance.commit(.P1, .hit);
|
|
726
721
|
try expectProbability(&chance.probability, 1, 39);
|
|
727
|
-
try expectValue(
|
|
722
|
+
try expectValue(217, chance.actions.p1.damage);
|
|
728
723
|
}
|
|
729
724
|
}
|
|
730
725
|
|
|
@@ -776,13 +771,13 @@ test "Chance.moveSlot" {
|
|
|
776
771
|
|
|
777
772
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
778
773
|
try expectProbability(&chance.probability, 1, 2);
|
|
779
|
-
try expectValue(
|
|
774
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
780
775
|
|
|
781
776
|
chance.reset();
|
|
782
777
|
|
|
783
778
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
784
779
|
try expectProbability(&chance.probability, 1, 3);
|
|
785
|
-
try expectValue(
|
|
780
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
786
781
|
}
|
|
787
782
|
|
|
788
783
|
test "Chance.multiHit" {
|
|
@@ -790,32 +785,32 @@ test "Chance.multiHit" {
|
|
|
790
785
|
|
|
791
786
|
try chance.multiHit(.P1, 3);
|
|
792
787
|
try expectProbability(&chance.probability, 3, 8);
|
|
793
|
-
try expectValue(
|
|
788
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
794
789
|
|
|
795
790
|
chance.reset();
|
|
796
791
|
|
|
797
792
|
try chance.multiHit(.P2, 5);
|
|
798
793
|
try expectProbability(&chance.probability, 1, 8);
|
|
799
|
-
try expectValue(
|
|
794
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
800
795
|
}
|
|
801
796
|
|
|
802
797
|
test "Chance.duration" {
|
|
803
798
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
804
799
|
|
|
805
800
|
chance.duration(.sleep, .P1, .P2, 2);
|
|
806
|
-
try expectValue(
|
|
807
|
-
try expectValue(
|
|
801
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
802
|
+
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
808
803
|
|
|
809
804
|
chance.reset();
|
|
810
805
|
|
|
811
806
|
chance.duration(.binding, .P2, .P2, 4);
|
|
812
807
|
if (!showdown) {
|
|
813
|
-
try expectValue(
|
|
814
|
-
try expectValue(
|
|
808
|
+
try expectValue(0, chance.actions.p2.duration);
|
|
809
|
+
try expectValue(0, chance.actions.p2.durations.binding);
|
|
815
810
|
try chance.commit(.P2, .hit);
|
|
816
811
|
}
|
|
817
|
-
try expectValue(
|
|
818
|
-
try expectValue(
|
|
812
|
+
try expectValue(4, chance.actions.p2.duration);
|
|
813
|
+
try expectValue(1, chance.actions.p2.durations.binding);
|
|
819
814
|
}
|
|
820
815
|
|
|
821
816
|
test "Chance.sleep" {
|
|
@@ -825,7 +820,7 @@ test "Chance.sleep" {
|
|
|
825
820
|
chance.actions.p1.durations.sleep = @intCast(i);
|
|
826
821
|
try chance.sleep(.P1, 0);
|
|
827
822
|
try expectProbability(&chance.probability, 1, d);
|
|
828
|
-
try expectValue(
|
|
823
|
+
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
829
824
|
|
|
830
825
|
chance.reset();
|
|
831
826
|
|
|
@@ -848,7 +843,7 @@ test "Chance.confusion" {
|
|
|
848
843
|
chance.actions.p2.durations.confusion = @intCast(i);
|
|
849
844
|
try chance.confusion(.P2, 0);
|
|
850
845
|
try expectProbability(&chance.probability, 1, d);
|
|
851
|
-
try expectValue(
|
|
846
|
+
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
852
847
|
|
|
853
848
|
chance.reset();
|
|
854
849
|
}
|
|
@@ -864,6 +859,28 @@ test "Chance.confusion" {
|
|
|
864
859
|
}
|
|
865
860
|
}
|
|
866
861
|
|
|
862
|
+
test "Chance.disable" {
|
|
863
|
+
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
864
|
+
|
|
865
|
+
for ([_]u8{ 8, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
866
|
+
chance.actions.p1.durations.disable = @intCast(i);
|
|
867
|
+
try chance.disable(.P1, 0);
|
|
868
|
+
try expectProbability(&chance.probability, 1, d);
|
|
869
|
+
try expectValue(0, chance.actions.p1.durations.disable);
|
|
870
|
+
|
|
871
|
+
chance.reset();
|
|
872
|
+
|
|
873
|
+
if (i < 8) {
|
|
874
|
+
chance.actions.p1.durations.disable = @intCast(i);
|
|
875
|
+
try chance.disable(.P1, 1);
|
|
876
|
+
try expectProbability(&chance.probability, d - 1, d);
|
|
877
|
+
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p1.durations.disable);
|
|
878
|
+
|
|
879
|
+
chance.reset();
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
867
884
|
test "Chance.attacking" {
|
|
868
885
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
869
886
|
|
|
@@ -872,7 +889,7 @@ test "Chance.attacking" {
|
|
|
872
889
|
chance.actions.p2.durations.attacking = @intCast(i);
|
|
873
890
|
try chance.attacking(.P2, 0);
|
|
874
891
|
try expectProbability(&chance.probability, 1, d);
|
|
875
|
-
try expectValue(
|
|
892
|
+
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
876
893
|
|
|
877
894
|
chance.reset();
|
|
878
895
|
}
|
|
@@ -898,7 +915,7 @@ test "Chance.binding" {
|
|
|
898
915
|
chance.actions.p1.durations.binding = @intCast(i);
|
|
899
916
|
try chance.binding(.P1, 0);
|
|
900
917
|
try expectProbability(&chance.probability, p, q);
|
|
901
|
-
try expectValue(
|
|
918
|
+
try expectValue(0, chance.actions.p1.durations.binding);
|
|
902
919
|
|
|
903
920
|
chance.reset();
|
|
904
921
|
|
|
@@ -918,7 +935,7 @@ test "Chance.psywave" {
|
|
|
918
935
|
|
|
919
936
|
try chance.psywave(.P2, 100, 150);
|
|
920
937
|
try expectProbability(&chance.probability, 1, 150);
|
|
921
|
-
try expectValue(
|
|
938
|
+
try expectValue(101, chance.actions.p2.psywave);
|
|
922
939
|
}
|
|
923
940
|
|
|
924
941
|
test "Chance.metronome" {
|
|
@@ -947,7 +964,7 @@ pub fn expectValue(a: anytype, b: anytype) !void {
|
|
|
947
964
|
|
|
948
965
|
/// Null object pattern implementation of Generation I `Chance` which does nothing, though chance
|
|
949
966
|
/// tracking should additionally be turned off entirely via `options.chance`.
|
|
950
|
-
pub const NULL =
|
|
967
|
+
pub const NULL: Null = .{};
|
|
951
968
|
|
|
952
969
|
const Null = struct {
|
|
953
970
|
pub const Error = error{};
|
|
@@ -984,10 +1001,6 @@ const Null = struct {
|
|
|
984
1001
|
_ = .{ self, player, roll };
|
|
985
1002
|
}
|
|
986
1003
|
|
|
987
|
-
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
988
|
-
_ = .{ self, player, proc, rate };
|
|
989
|
-
}
|
|
990
|
-
|
|
991
1004
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
992
1005
|
_ = .{ self, player, ok };
|
|
993
1006
|
}
|
|
@@ -996,6 +1009,10 @@ const Null = struct {
|
|
|
996
1009
|
_ = .{ self, player, ok };
|
|
997
1010
|
}
|
|
998
1011
|
|
|
1012
|
+
pub fn secondaryChance(self: Null, player: Player, proc: bool, rate: u8) Error!void {
|
|
1013
|
+
_ = .{ self, player, proc, rate };
|
|
1014
|
+
}
|
|
1015
|
+
|
|
999
1016
|
pub fn moveSlot(self: Null, player: Player, slot: u4, ms: []const MoveSlot, n: u4) Error!void {
|
|
1000
1017
|
_ = .{ self, player, slot, ms, n };
|
|
1001
1018
|
}
|