@pkmn/engine 0.1.0-dev.b01cb48c → 0.1.0-dev.b3197aa3
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 -1
- package/README.md +54 -6
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -3
- package/build/pkg/addon.js +34 -65
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +40 -90
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +52 -76
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +331 -85
- package/package.json +56 -136
- package/src/bin/install-pkmn-engine +10 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -6
- package/src/lib/common/napi.zig +4 -0
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +44 -43
- package/src/lib/common/rational.zig +317 -304
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +65 -1
- package/src/lib/gen1/calc.zig +266 -135
- package/src/lib/gen1/chance.zig +469 -110
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +2 -3
- package/src/lib/gen1/data.zig +35 -38
- package/src/lib/gen1/helpers.zig +17 -27
- package/src/lib/gen1/mechanics.zig +213 -160
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +87 -67
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +7 -5
- package/src/lib/gen2/data.zig +28 -30
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +82 -82
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +14 -7
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +93 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -405
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -1,44 +1,43 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
1
|
+
const DEBUG = @import("../common/debug.zig").print;
|
|
4
2
|
|
|
3
|
+
const chance = @import("chance.zig");
|
|
5
4
|
const common = @import("../common/data.zig");
|
|
6
|
-
const
|
|
5
|
+
const data = @import("data.zig");
|
|
6
|
+
const helpers = @import("helpers.zig");
|
|
7
7
|
const optional = @import("../common/optional.zig");
|
|
8
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
9
|
const protocol = @import("../common/protocol.zig");
|
|
9
10
|
const rational = @import("../common/rational.zig");
|
|
11
|
+
const std = @import("std");
|
|
10
12
|
const util = @import("../common/util.zig");
|
|
11
13
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const helpers = @import("helpers.zig");
|
|
15
|
-
|
|
14
|
+
const Action = chance.Action;
|
|
15
|
+
const Actions = chance.Actions;
|
|
16
16
|
const assert = std.debug.assert;
|
|
17
|
-
|
|
17
|
+
const Chance = chance.Chance;
|
|
18
|
+
const Choice = common.Choice;
|
|
19
|
+
const Confusion = chance.Confusion;
|
|
20
|
+
const Duration = chance.Duration;
|
|
21
|
+
const Durations = chance.Durations;
|
|
22
|
+
const enabled = pkmn.options.calc;
|
|
18
23
|
const expect = std.testing.expect;
|
|
19
24
|
const expectEqual = std.testing.expectEqual;
|
|
20
25
|
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
26
|
-
const Choice = common.Choice;
|
|
27
|
-
|
|
26
|
+
const isPointerTo = util.isPointerTo;
|
|
27
|
+
const Observation = chance.Observation;
|
|
28
28
|
const Optional = optional.Optional;
|
|
29
|
-
|
|
30
|
-
const Rational = rational.Rational;
|
|
31
|
-
|
|
29
|
+
const Player = common.Player;
|
|
32
30
|
const PointerType = util.PointerType;
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const Chance = chance.Chance;
|
|
37
|
-
const Durations = chance.Durations;
|
|
31
|
+
const Rational = rational.Rational;
|
|
32
|
+
const Result = common.Result;
|
|
33
|
+
const Sleeps = chance.Sleeps;
|
|
38
34
|
|
|
39
35
|
const tty = true; // DEBUG
|
|
40
36
|
const summary = false; // DEBUG
|
|
41
37
|
|
|
38
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
39
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
40
|
+
|
|
42
41
|
/// Information relevant to damage calculation that occured during a Generation I battle `update`.
|
|
43
42
|
pub const Summaries = extern struct {
|
|
44
43
|
/// Relevant information for Player 1.
|
|
@@ -52,7 +51,7 @@ pub const Summaries = extern struct {
|
|
|
52
51
|
|
|
53
52
|
/// Returns the `Summary` for the given `player`.
|
|
54
53
|
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
|
|
55
|
-
assert(
|
|
54
|
+
assert(isPointerTo(self, Summaries));
|
|
56
55
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
57
56
|
}
|
|
58
57
|
};
|
|
@@ -88,21 +87,11 @@ pub const Summary = extern struct {
|
|
|
88
87
|
}
|
|
89
88
|
};
|
|
90
89
|
|
|
91
|
-
/// TODO
|
|
92
|
-
pub const Overrides = extern struct {
|
|
93
|
-
/// TODO
|
|
94
|
-
actions: Actions = .{},
|
|
95
|
-
|
|
96
|
-
comptime {
|
|
97
|
-
assert(@sizeOf(Overrides) == 16);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
90
|
/// Allows for forcing the value of specific RNG events during a Generation I battle `update` via
|
|
102
91
|
/// `overrides` and tracks `summaries` of information relevant to damage calculation.
|
|
103
92
|
pub const Calc = struct {
|
|
104
93
|
/// Overrides the normal behavior of the RNG during an `update` to force specific outcomes.
|
|
105
|
-
overrides:
|
|
94
|
+
overrides: Actions = .{},
|
|
106
95
|
/// Information relevant to damage calculation.
|
|
107
96
|
summaries: Summaries = .{},
|
|
108
97
|
|
|
@@ -110,19 +99,25 @@ pub const Calc = struct {
|
|
|
110
99
|
self: Calc,
|
|
111
100
|
player: Player,
|
|
112
101
|
comptime field: Action.Field,
|
|
113
|
-
) ?
|
|
102
|
+
) ?util.FieldType(Action, field) {
|
|
114
103
|
if (!enabled) return null;
|
|
115
104
|
|
|
116
|
-
const overrides
|
|
117
|
-
if (player == .P1) self.overrides.actions.p1 else self.overrides.actions.p2;
|
|
118
|
-
const val = @field(overrides, @tagName(field));
|
|
105
|
+
const val = @field(self.overrides.get(player), @tagName(field));
|
|
119
106
|
return if (switch (@typeInfo(@TypeOf(val))) {
|
|
120
|
-
|
|
121
|
-
|
|
107
|
+
Enum => val != .None,
|
|
108
|
+
Int => val != 0,
|
|
122
109
|
else => unreachable,
|
|
123
110
|
}) val else null;
|
|
124
111
|
}
|
|
125
112
|
|
|
113
|
+
pub fn confusion(self: *Calc, player: Player) void {
|
|
114
|
+
if (!enabled) return;
|
|
115
|
+
|
|
116
|
+
if (self.overrides.get(player).confusion == .started) {
|
|
117
|
+
self.overrides.get(player).confusion = .None;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
126
121
|
pub fn base(self: *Calc, player: Player, val: u16) void {
|
|
127
122
|
if (!enabled) return;
|
|
128
123
|
|
|
@@ -151,11 +146,15 @@ const Null = struct {
|
|
|
151
146
|
self: Null,
|
|
152
147
|
player: Player,
|
|
153
148
|
comptime field: Action.Field,
|
|
154
|
-
) ?
|
|
149
|
+
) ?util.FieldType(Action, field) {
|
|
155
150
|
_ = .{ self, player };
|
|
156
151
|
return null;
|
|
157
152
|
}
|
|
158
153
|
|
|
154
|
+
pub fn confusion(self: Null, player: Player) void {
|
|
155
|
+
_ = .{ self, player };
|
|
156
|
+
}
|
|
157
|
+
|
|
159
158
|
pub fn base(self: Null, player: Player, val: u16) void {
|
|
160
159
|
_ = .{ self, player, val };
|
|
161
160
|
}
|
|
@@ -183,7 +182,7 @@ pub const Options = struct {
|
|
|
183
182
|
metronome: bool = false,
|
|
184
183
|
};
|
|
185
184
|
|
|
186
|
-
pub const MAX_FRONTIER =
|
|
185
|
+
pub const MAX_FRONTIER = 83;
|
|
187
186
|
|
|
188
187
|
pub fn transitions(
|
|
189
188
|
battle: anytype,
|
|
@@ -202,9 +201,11 @@ pub fn transitions(
|
|
|
202
201
|
var frontier = std.ArrayList(Actions).init(allocator);
|
|
203
202
|
defer frontier.deinit();
|
|
204
203
|
|
|
204
|
+
const d = options.durations;
|
|
205
|
+
|
|
205
206
|
var opts = pkmn.battle.options(
|
|
206
207
|
protocol.NULL,
|
|
207
|
-
Chance(Rational(u128)){ .probability = .{}, .durations =
|
|
208
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = d },
|
|
208
209
|
Calc{},
|
|
209
210
|
);
|
|
210
211
|
|
|
@@ -215,7 +216,7 @@ pub fn transitions(
|
|
|
215
216
|
const p1 = b.side(.P1);
|
|
216
217
|
const p2 = b.side(.P2);
|
|
217
218
|
|
|
218
|
-
var p: Rational(
|
|
219
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
219
220
|
try frontier.append(opts.chance.actions);
|
|
220
221
|
|
|
221
222
|
// zig fmt: off
|
|
@@ -227,9 +228,12 @@ pub fn transitions(
|
|
|
227
228
|
var i: usize = 0;
|
|
228
229
|
assert(frontier.items.len == 1);
|
|
229
230
|
while (i < frontier.items.len) : (i += 1) {
|
|
230
|
-
const
|
|
231
|
+
const f = frontier.items[i];
|
|
232
|
+
const saved = stats.saved;
|
|
231
233
|
|
|
232
|
-
|
|
234
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
235
|
+
|
|
236
|
+
try debug(writer, f, .{
|
|
233
237
|
.shape = true,
|
|
234
238
|
.color = i,
|
|
235
239
|
.bold = true,
|
|
@@ -237,33 +241,44 @@ pub fn transitions(
|
|
|
237
241
|
.indent = false,
|
|
238
242
|
});
|
|
239
243
|
|
|
240
|
-
var a: Actions = .{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
for (Rolls.
|
|
246
|
-
for (Rolls.
|
|
247
|
-
for (Rolls.
|
|
248
|
-
for (Rolls.
|
|
249
|
-
for (Rolls.
|
|
250
|
-
for (Rolls.
|
|
251
|
-
for (Rolls.
|
|
252
|
-
for (Rolls.
|
|
253
|
-
for (Rolls.
|
|
254
|
-
for (Rolls.
|
|
255
|
-
for (Rolls.
|
|
256
|
-
for (Rolls.
|
|
257
|
-
for (Rolls.
|
|
258
|
-
for (Rolls.
|
|
259
|
-
for (Rolls.
|
|
260
|
-
for (Rolls.
|
|
261
|
-
|
|
262
|
-
|
|
244
|
+
var a: Actions = .{
|
|
245
|
+
.p1 = .{ .metronome = p1_move, .pp = f.p1.pp, .duration = f.p1.duration },
|
|
246
|
+
.p2 = .{ .metronome = p2_move, .pp = f.p2.pp, .duration = f.p2.duration },
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
for (Rolls.speedTie(f.p1)) |tie| { a.p1.speed_tie = tie; a.p2.speed_tie = tie;
|
|
250
|
+
for (Rolls.sleep(f.p1, d.p1)) |p1_slp| { a.p1.sleep = p1_slp;
|
|
251
|
+
for (Rolls.sleep(f.p2, d.p2)) |p2_slp| { a.p2.sleep = p2_slp;
|
|
252
|
+
for (Rolls.disable(f.p1, d.p1, p1_slp)) |p1_dis| { a.p1.disable = p1_dis;
|
|
253
|
+
for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
|
|
254
|
+
for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
|
|
255
|
+
for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
|
|
256
|
+
for (Rolls.confusion(f.p1, d.p1, tie, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
257
|
+
for (Rolls.confusion(f.p2, d.p2, tie, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
258
|
+
for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
|
|
259
|
+
for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
|
|
260
|
+
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
261
|
+
for (Rolls.paralyzed(f.p2, p2_cfzd)) |p2_par| { a.p2.paralyzed = p2_par;
|
|
262
|
+
for (Rolls.binding(f.p1, d.p1, p1_par)) |p1_bind| { a.p1.binding = p1_bind;
|
|
263
|
+
for (Rolls.binding(f.p2, d.p2, p2_par)) |p2_bind| { a.p2.binding = p2_bind;
|
|
264
|
+
for (Rolls.hit(f.p1, p1_par)) |p1_hit| { a.p1.hit = p1_hit;
|
|
265
|
+
for (Rolls.hit(f.p2, p2_par)) |p2_hit| { a.p2.hit = p2_hit;
|
|
266
|
+
for (Rolls.psywave(f.p1, p1, p1_hit)) |p1_psywave| { a.p1.psywave = p1_psywave;
|
|
267
|
+
for (Rolls.psywave(f.p2, p2, p2_hit)) |p2_psywave| { a.p2.psywave = p2_psywave;
|
|
268
|
+
for (Rolls.moveSlot(f.p1, p1_hit)) |p1_slot| { a.p1.move_slot = p1_slot;
|
|
269
|
+
for (Rolls.moveSlot(f.p2, p2_hit)) |p2_slot| { a.p2.move_slot = p2_slot;
|
|
270
|
+
for (Rolls.multiHit(f.p1, p1_hit)) |p1_multi| { a.p1.multi_hit = p1_multi;
|
|
271
|
+
for (Rolls.multiHit(f.p2, p2_hit)) |p2_multi| { a.p2.multi_hit = p2_multi;
|
|
272
|
+
for (Rolls.secondaryChance(f.p1, p1_hit)) |p1_sec| { a.p1.secondary_chance = p1_sec;
|
|
273
|
+
for (Rolls.secondaryChance(f.p2, p2_hit)) |p2_sec| { a.p2.secondary_chance = p2_sec;
|
|
274
|
+
for (Rolls.criticalHit(f.p1, p1_hit)) |p1_crit| { a.p1.critical_hit = p1_crit;
|
|
275
|
+
for (Rolls.criticalHit(f.p2, p2_hit)) |p2_crit| { a.p2.critical_hit = p2_crit;
|
|
276
|
+
|
|
277
|
+
var p1_dmg = Rolls.damage(f.p1, p1_hit);
|
|
263
278
|
while (p1_dmg.min < p1_dmg.max) : (p1_dmg.min += 1) {
|
|
264
279
|
a.p1.damage = @intCast(p1_dmg.min);
|
|
265
280
|
|
|
266
|
-
var p2_dmg = Rolls.damage(
|
|
281
|
+
var p2_dmg = Rolls.damage(f.p2, p2_hit);
|
|
267
282
|
|
|
268
283
|
const p1_min: u9 = p1_dmg.min;
|
|
269
284
|
const p2_min: u9 = p2_dmg.min;
|
|
@@ -271,9 +286,9 @@ pub fn transitions(
|
|
|
271
286
|
while (p2_dmg.min < p2_dmg.max) : (p2_dmg.min += 1) {
|
|
272
287
|
a.p2.damage = @intCast(p2_dmg.min);
|
|
273
288
|
|
|
274
|
-
opts.calc.overrides
|
|
289
|
+
opts.calc.overrides = a;
|
|
275
290
|
opts.calc.summaries = .{};
|
|
276
|
-
opts.chance = .{ .probability = .{}, .durations =
|
|
291
|
+
opts.chance = .{ .probability = .{}, .durations = d };
|
|
277
292
|
const q = &opts.chance.probability;
|
|
278
293
|
|
|
279
294
|
b = battle;
|
|
@@ -285,12 +300,12 @@ pub fn transitions(
|
|
|
285
300
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
286
301
|
p1_dmg.min
|
|
287
302
|
else
|
|
288
|
-
|
|
303
|
+
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
289
304
|
const p2_max: u9 =
|
|
290
|
-
|
|
305
|
+
Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
|
|
291
306
|
|
|
292
|
-
if (opts.chance.actions.matches(
|
|
293
|
-
if (!opts.chance.actions.eql(a)) {
|
|
307
|
+
if (opts.chance.actions.matches(f)) {
|
|
308
|
+
if (!opts.chance.actions.relax().eql(a)) {
|
|
294
309
|
if (!summary) {
|
|
295
310
|
try debug(writer, opts.chance.actions, .{
|
|
296
311
|
.p1_max = p1_max,
|
|
@@ -324,10 +339,15 @@ pub fn transitions(
|
|
|
324
339
|
}
|
|
325
340
|
}
|
|
326
341
|
}
|
|
327
|
-
|
|
328
342
|
if (p1_max != p1_min) try q.update(p1_max - p1_min + 1, 1);
|
|
329
343
|
if (p2_max != p2_dmg.min) try q.update(p2_max - p2_dmg.min + 1, 1);
|
|
344
|
+
|
|
345
|
+
q.reduce();
|
|
330
346
|
try p.add(q);
|
|
347
|
+
p.reduce();
|
|
348
|
+
try r.add(q);
|
|
349
|
+
r.reduce();
|
|
350
|
+
|
|
331
351
|
stats.saved += 1;
|
|
332
352
|
|
|
333
353
|
if (p.q < p.p) {
|
|
@@ -335,7 +355,7 @@ pub fn transitions(
|
|
|
335
355
|
return error.TestUnexpectedResult;
|
|
336
356
|
}
|
|
337
357
|
} else {
|
|
338
|
-
if (!
|
|
358
|
+
if (!opts.chance.actions.matchesAny(frontier.items, i)) {
|
|
339
359
|
try frontier.append(opts.chance.actions);
|
|
340
360
|
|
|
341
361
|
try debug(writer, opts.chance.actions, .{
|
|
@@ -365,15 +385,18 @@ pub fn transitions(
|
|
|
365
385
|
p2_dmg.min = p2_max;
|
|
366
386
|
}
|
|
367
387
|
|
|
368
|
-
}}}}}}}}}}}}}}}}}}}}
|
|
388
|
+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
369
389
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
390
|
+
assert(stats.saved > saved);
|
|
391
|
+
// TODO: ziglang/zig#22667
|
|
392
|
+
// const old = @hasDecl(std.fmt, "formatFloatDecimal");
|
|
393
|
+
// if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
|
|
394
|
+
// try writer.print(
|
|
395
|
+
// " {} ({d:.2}%)\n = ──────────\n {} ({d:.2}%)\n\n",
|
|
396
|
+
// .{r, 100 * @as(f128, @floatFromInt(r.p)) / @as(f128, @floatFromInt(r.q)),
|
|
397
|
+
// p, 100 * @as(f128, @floatFromInt(p.p)) / @as(f128, @floatFromInt(p.q))},
|
|
398
|
+
// );
|
|
399
|
+
// }
|
|
377
400
|
|
|
378
401
|
}
|
|
379
402
|
|
|
@@ -445,7 +468,7 @@ pub fn update(
|
|
|
445
468
|
var override = pkmn.battle.options(
|
|
446
469
|
protocol.NULL,
|
|
447
470
|
Chance(Rational(u128)){ .probability = .{}, .durations = durations },
|
|
448
|
-
Calc{ .overrides =
|
|
471
|
+
Calc{ .overrides = overrides },
|
|
449
472
|
);
|
|
450
473
|
var copy = original;
|
|
451
474
|
var overridden = copy.update(c1, c2, &override);
|
|
@@ -466,8 +489,8 @@ pub fn update(
|
|
|
466
489
|
|
|
467
490
|
override = pkmn.battle.options(
|
|
468
491
|
protocol.NULL,
|
|
469
|
-
Chance(Rational(u128)){ .probability = .{} },
|
|
470
|
-
Calc{ .overrides =
|
|
492
|
+
Chance(Rational(u128)){ .probability = .{}, .durations = durations },
|
|
493
|
+
Calc{ .overrides = overrides },
|
|
471
494
|
);
|
|
472
495
|
copy = original;
|
|
473
496
|
overridden = copy.update(c1, c2, &override);
|
|
@@ -503,15 +526,6 @@ fn unfix(actual: anytype) data.Battle(data.PRNG) {
|
|
|
503
526
|
};
|
|
504
527
|
}
|
|
505
528
|
|
|
506
|
-
fn matches(actions: Actions, i: usize, frontier: []Actions) bool {
|
|
507
|
-
for (frontier, 0..) |f, j| {
|
|
508
|
-
// TODO: is skipping this redundant check worth it?
|
|
509
|
-
if (i == j) continue;
|
|
510
|
-
if (f.matches(actions)) return true;
|
|
511
|
-
}
|
|
512
|
-
return false;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
529
|
fn err(comptime fmt: []const u8, v: anytype, seed: ?u64) void {
|
|
516
530
|
const w = std.io.getStdErr().writer();
|
|
517
531
|
w.print(fmt, v) catch return;
|
|
@@ -584,8 +598,6 @@ fn format(writer: anytype, actions: Actions, p1: u9, p2: u9) !void {
|
|
|
584
598
|
try writer.writeAll(output[0..len]);
|
|
585
599
|
}
|
|
586
600
|
|
|
587
|
-
const NONE = @intFromEnum(Optional(bool).None);
|
|
588
|
-
|
|
589
601
|
/// Helper functions that efficiently return valid ranges for various RNG events based on the
|
|
590
602
|
/// state of an `Action` and other events to be used to construct a "transitions" function.
|
|
591
603
|
pub const Rolls = struct {
|
|
@@ -597,6 +609,112 @@ pub const Rolls = struct {
|
|
|
597
609
|
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
598
610
|
}
|
|
599
611
|
|
|
612
|
+
const OBS_NONE = [_]Optional(Observation){.None};
|
|
613
|
+
const OBS_STARTED = [_]Optional(Observation){.started};
|
|
614
|
+
const OBS_CONTINUING = [_]Optional(Observation){.continuing};
|
|
615
|
+
const OBS_ENDED = [_]Optional(Observation){.ended};
|
|
616
|
+
const OBS_ALL = [_]Optional(Observation){ .started, .continuing, .ended };
|
|
617
|
+
const OBS = [_]Optional(Observation){ .continuing, .ended };
|
|
618
|
+
|
|
619
|
+
/// Returns a slice with a range of values for sleep given the `action` state and observed
|
|
620
|
+
/// `durations`.
|
|
621
|
+
pub fn sleep(action: Action, duration: Duration) []const Optional(Observation) {
|
|
622
|
+
if (action.sleep == .None) return &OBS_NONE;
|
|
623
|
+
return if (Sleeps.get(duration.sleeps, 0) == 0 and action.speed_tie != .None)
|
|
624
|
+
&OBS_ALL
|
|
625
|
+
else switch (action.sleep) {
|
|
626
|
+
.started => &OBS_STARTED,
|
|
627
|
+
else => if (Sleeps.get(duration.sleeps, 0) >= 7) &OBS_ENDED else &OBS,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/// Returns a slice with a range of values for disable given the `action`, observed `durations`,
|
|
632
|
+
/// and the state of the `parent` (observation of the player's Pokémon sleep status).
|
|
633
|
+
pub fn disable(
|
|
634
|
+
action: Action,
|
|
635
|
+
duration: Duration,
|
|
636
|
+
parent: Optional(Observation),
|
|
637
|
+
) []const Optional(Observation) {
|
|
638
|
+
if (action.disable == .None) return &OBS_NONE;
|
|
639
|
+
return if (duration.disable == 0 and action.speed_tie != .None)
|
|
640
|
+
&OBS_ALL
|
|
641
|
+
else switch (action.disable) {
|
|
642
|
+
.started => &OBS_STARTED,
|
|
643
|
+
else => if (duration.disable >= 8)
|
|
644
|
+
&OBS_ENDED
|
|
645
|
+
else if (parent != .None and parent != .started)
|
|
646
|
+
&OBS_CONTINUING
|
|
647
|
+
else
|
|
648
|
+
&OBS,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/// FIXME
|
|
653
|
+
pub fn attacking(
|
|
654
|
+
action: Action,
|
|
655
|
+
duration: Duration,
|
|
656
|
+
parent: Optional(Observation),
|
|
657
|
+
) []const Optional(Observation) {
|
|
658
|
+
return switch (action.attacking) {
|
|
659
|
+
.None => &OBS_NONE,
|
|
660
|
+
.started => &OBS_STARTED,
|
|
661
|
+
else => if (duration.attacking >= 3)
|
|
662
|
+
&OBS_ENDED
|
|
663
|
+
else if ((parent == .continuing and parent == .ended) or duration.attacking < 2)
|
|
664
|
+
&OBS_CONTINUING
|
|
665
|
+
else
|
|
666
|
+
&OBS,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const CFZ_NONE = [_]Optional(Confusion){.None};
|
|
671
|
+
const CFZ_STARTED = [_]Optional(Confusion){.started};
|
|
672
|
+
const CFZ_OVERWRITTEN = [_]Optional(Confusion){ .started, .overwritten };
|
|
673
|
+
const CFZ_CONTINUING = [_]Optional(Confusion){.continuing};
|
|
674
|
+
const CFZ_ENDED = [_]Optional(Confusion){.ended};
|
|
675
|
+
const CFZ = [_]Optional(Confusion){ .continuing, .ended };
|
|
676
|
+
const CFZ_TIE = [_]Optional(Confusion){ .started, .continuing, .ended };
|
|
677
|
+
|
|
678
|
+
/// FIXME
|
|
679
|
+
pub fn confusion(
|
|
680
|
+
action: Action,
|
|
681
|
+
duration: Duration,
|
|
682
|
+
tie: Optional(Player),
|
|
683
|
+
sibling: Optional(Observation),
|
|
684
|
+
parent: Optional(Observation),
|
|
685
|
+
) []const Optional(Confusion) {
|
|
686
|
+
return switch (action.confusion) {
|
|
687
|
+
.None => &CFZ_NONE,
|
|
688
|
+
.started => if (pkmn.options.overwrite and sibling == .ended and duration.confusion < 5)
|
|
689
|
+
&CFZ_OVERWRITTEN
|
|
690
|
+
else
|
|
691
|
+
&CFZ_STARTED,
|
|
692
|
+
else => if (duration.confusion >= 5)
|
|
693
|
+
&CFZ_ENDED
|
|
694
|
+
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
695
|
+
&CFZ_CONTINUING
|
|
696
|
+
else if (tie != .None)
|
|
697
|
+
&CFZ_TIE
|
|
698
|
+
else
|
|
699
|
+
&CFZ,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/// Returns a slice with a range of values for binding given the `action`, observed `durations`,
|
|
704
|
+
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
705
|
+
pub fn binding(
|
|
706
|
+
action: Action,
|
|
707
|
+
duration: Duration,
|
|
708
|
+
parent: Optional(bool),
|
|
709
|
+
) []const Optional(Observation) {
|
|
710
|
+
if (parent == .true) return &OBS_NONE;
|
|
711
|
+
return switch (action.binding) {
|
|
712
|
+
.None => &OBS_NONE,
|
|
713
|
+
.started => &OBS_STARTED,
|
|
714
|
+
else => if (duration.binding >= 4) &OBS_ENDED else &OBS,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
600
718
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
601
719
|
const BOOLS = [_]Optional(bool){ .false, .true };
|
|
602
720
|
|
|
@@ -610,7 +728,7 @@ pub const Rolls = struct {
|
|
|
610
728
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
611
729
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
612
730
|
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
613
|
-
if (parent == .false) return &BOOL_NONE;
|
|
731
|
+
if (pkmn.options.showdown and parent == .false) return &BOOL_NONE;
|
|
614
732
|
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
615
733
|
}
|
|
616
734
|
|
|
@@ -635,7 +753,7 @@ pub const Rolls = struct {
|
|
|
635
753
|
|
|
636
754
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
637
755
|
/// given the base damage in `summaries`.
|
|
638
|
-
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool)
|
|
756
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool) u8 {
|
|
639
757
|
if (roll == 0) return roll;
|
|
640
758
|
|
|
641
759
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -645,9 +763,11 @@ pub const Rolls = struct {
|
|
|
645
763
|
return @min(255, roll + ((254 - ((@as(u32, dmg.base) * roll) % 255)) / dmg.base));
|
|
646
764
|
}
|
|
647
765
|
|
|
648
|
-
/// Returns a slice with the correct range of values for confused
|
|
649
|
-
|
|
650
|
-
|
|
766
|
+
/// Returns a slice with the correct range of values for confused given the `action` state and
|
|
767
|
+
/// the state of the `parent` (observation of the player's Pokémon confusion status).
|
|
768
|
+
pub fn confused(action: Action, parent: Optional(Confusion)) []const Optional(bool) {
|
|
769
|
+
const done = parent == .None or parent == .ended;
|
|
770
|
+
return if (done or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
651
771
|
}
|
|
652
772
|
|
|
653
773
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
@@ -657,17 +777,6 @@ pub const Rolls = struct {
|
|
|
657
777
|
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
658
778
|
}
|
|
659
779
|
|
|
660
|
-
const DURATION_NONE = [_]u2{0};
|
|
661
|
-
// NB: DURATION must be at least 2 because that is the maximum of the minimums for all rolls
|
|
662
|
-
const DURATION = [_]u2{2};
|
|
663
|
-
|
|
664
|
-
/// Returns a slice with a range of values for duration given the `action` state
|
|
665
|
-
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
666
|
-
pub fn duration(action: Action, parent: Optional(bool)) []const u2 {
|
|
667
|
-
if (parent == .false) return &DURATION_NONE;
|
|
668
|
-
return if (action.duration == 0) &DURATION_NONE else &DURATION;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
780
|
const SLOT_NONE = [_]u4{0};
|
|
672
781
|
const SLOT = [_]u4{ 1, 2, 3, 4 };
|
|
673
782
|
|
|
@@ -725,6 +834,26 @@ test "Rolls.speedTie" {
|
|
|
725
834
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
726
835
|
}
|
|
727
836
|
|
|
837
|
+
test "Rolls.sleep" {
|
|
838
|
+
return error.SkipZigTest; // TODO
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
test "Rolls.disable" {
|
|
842
|
+
return error.SkipZigTest; // TODO
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
test "Rolls.confusion" {
|
|
846
|
+
return error.SkipZigTest; // TODO
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
test "Rolls.atacking" {
|
|
850
|
+
return error.SkipZigTest; // TODO
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
test "Rolls.binding" {
|
|
854
|
+
return error.SkipZigTest; // TODO
|
|
855
|
+
}
|
|
856
|
+
|
|
728
857
|
test "Rolls.damage" {
|
|
729
858
|
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
730
859
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
@@ -734,12 +863,12 @@ test "Rolls.damage" {
|
|
|
734
863
|
|
|
735
864
|
test "Rolls.coalesce" {
|
|
736
865
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
737
|
-
try expectEqual(0,
|
|
738
|
-
try expectEqual(241,
|
|
739
|
-
try expectEqual(255,
|
|
866
|
+
try expectEqual(@as(u8, 0), Rolls.coalesce(.P2, 0, &summaries, false));
|
|
867
|
+
try expectEqual(@as(u8, 241), Rolls.coalesce(.P2, 238, &summaries, false));
|
|
868
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 238, &summaries, true));
|
|
740
869
|
summaries.p1.damage.final = 74;
|
|
741
|
-
try expectEqual(217,
|
|
742
|
-
try expectEqual(255,
|
|
870
|
+
try expectEqual(@as(u8, 217), Rolls.coalesce(.P2, 217, &summaries, false));
|
|
871
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 217, &summaries, true));
|
|
743
872
|
}
|
|
744
873
|
|
|
745
874
|
test "Rolls.hit" {
|
|
@@ -768,14 +897,23 @@ test "Rolls.criticalHit" {
|
|
|
768
897
|
&.{ .false, .true },
|
|
769
898
|
Rolls.criticalHit(actions.p1, .None),
|
|
770
899
|
);
|
|
771
|
-
try expectEqualSlices(
|
|
900
|
+
try expectEqualSlices(
|
|
901
|
+
Optional(bool),
|
|
902
|
+
if (pkmn.options.showdown) &.{.None} else &.{ .false, .true },
|
|
903
|
+
Rolls.criticalHit(actions.p1, .false),
|
|
904
|
+
);
|
|
772
905
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.criticalHit(actions.p2, .None));
|
|
773
906
|
}
|
|
774
907
|
|
|
775
908
|
test "Rolls.confused" {
|
|
776
909
|
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
777
|
-
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1));
|
|
778
|
-
try expectEqualSlices(
|
|
910
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, .None));
|
|
911
|
+
try expectEqualSlices(
|
|
912
|
+
Optional(bool),
|
|
913
|
+
&.{ .false, .true },
|
|
914
|
+
Rolls.confused(actions.p2, .continuing),
|
|
915
|
+
);
|
|
916
|
+
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, .ended));
|
|
779
917
|
}
|
|
780
918
|
|
|
781
919
|
test "Rolls.paralyzed" {
|
|
@@ -786,13 +924,6 @@ test "Rolls.paralyzed" {
|
|
|
786
924
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.paralyzed(actions.p2, .true));
|
|
787
925
|
}
|
|
788
926
|
|
|
789
|
-
test "Rolls.duration" {
|
|
790
|
-
const actions: Actions = .{ .p2 = .{ .duration = 3 } };
|
|
791
|
-
try expectEqualSlices(u2, &.{0}, Rolls.duration(actions.p1, .None));
|
|
792
|
-
try expectEqualSlices(u2, &.{2}, Rolls.duration(actions.p2, .None));
|
|
793
|
-
try expectEqualSlices(u2, &.{0}, Rolls.duration(actions.p2, .false));
|
|
794
|
-
}
|
|
795
|
-
|
|
796
927
|
test "Rolls.moveSlot" {
|
|
797
928
|
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
798
929
|
try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p1, .None));
|
|
@@ -823,9 +954,9 @@ test "Rolls.psywave" {
|
|
|
823
954
|
|
|
824
955
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
|
|
825
956
|
var rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
826
|
-
try expectEqual(150, rolls[rolls.len - 1]);
|
|
957
|
+
try expectEqual(@as(u8, 150), rolls[rolls.len - 1]);
|
|
827
958
|
side.stored().level = 81;
|
|
828
959
|
rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
829
|
-
try expectEqual(121, rolls[rolls.len - 1]);
|
|
960
|
+
try expectEqual(@as(u8, 121), rolls[rolls.len - 1]);
|
|
830
961
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
|
|
831
962
|
}
|