@pkmn/engine 0.1.0-dev.ce4c0b2a → 0.1.0-dev.d127cb34
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 +23 -15
- package/build/pkg/addon/browser.d.ts +1 -1
- package/build/pkg/addon/browser.js +6 -2
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +22 -8
- package/build/pkg/addon/node.js.map +1 -1
- 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 -5
- package/build/pkg/addon.js +8 -17
- 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.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 +37 -100
- package/build/pkg/index.js +23 -75
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +11 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +56 -13
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +19 -81
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +35 -15
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +27 -16
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +141 -2
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +330 -245
- package/package.json +36 -128
- package/src/bin/install-pkmn-engine +25 -9
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +5 -12
- package/src/lib/common/debug.zig +45 -34
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +26 -26
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +228 -197
- package/src/lib/common/rational.zig +278 -285
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +34 -31
- package/src/lib/gen1/calc.zig +220 -195
- package/src/lib/gen1/chance.zig +184 -108
- 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 +3 -4
- package/src/lib/gen1/data.zig +12 -16
- package/src/lib/gen1/helpers.zig +35 -40
- package/src/lib/gen1/mechanics.zig +147 -127
- package/src/lib/gen2/calc.zig +12 -23
- package/src/lib/gen2/chance.zig +38 -26
- 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 +19 -22
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +32 -31
- package/src/lib/gen2/mechanics.zig +58 -55
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +98 -19
- package/src/lib/wasm.zig +9 -9
- package/src/tools/display/dom.ts +43 -31
- package/src/tools/display/ui.css +12 -9
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -1,34 +1,27 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const assert = std.debug.assert;
|
|
4
|
-
const print = std.debug.print;
|
|
5
|
-
|
|
6
|
-
const expect = std.testing.expect;
|
|
7
|
-
const expectEqual = std.testing.expectEqual;
|
|
8
|
-
|
|
9
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
2
|
+
|
|
3
|
+
const array = @import("../common/array.zig");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
5
|
+
const data = @import("data.zig");
|
|
6
|
+
const optional = @import("../common/optional.zig");
|
|
10
7
|
const options = @import("../common/options.zig");
|
|
11
8
|
const rational = @import("../common/rational.zig");
|
|
9
|
+
const std = @import("std");
|
|
12
10
|
const util = @import("../common/util.zig");
|
|
13
11
|
|
|
14
|
-
const Array =
|
|
15
|
-
const
|
|
16
|
-
const Optional = @import("../common/optional.zig").Optional;
|
|
17
|
-
|
|
18
|
-
const data = @import("data.zig");
|
|
19
|
-
|
|
12
|
+
const Array = array.Array;
|
|
13
|
+
const assert = std.debug.assert;
|
|
20
14
|
const enabled = options.chance;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const PointerType = util.PointerType;
|
|
15
|
+
const expect = std.testing.expect;
|
|
16
|
+
const expectEqual = std.testing.expectEqual;
|
|
24
17
|
const isPointerTo = util.isPointerTo;
|
|
25
|
-
|
|
26
18
|
const Move = data.Move;
|
|
27
19
|
const MoveSlot = data.MoveSlot;
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
20
|
+
const Optional = optional.Optional;
|
|
21
|
+
const Player = common.Player;
|
|
22
|
+
const PointerType = util.PointerType;
|
|
23
|
+
const print = std.debug.print;
|
|
24
|
+
const showdown = options.showdown;
|
|
32
25
|
|
|
33
26
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
34
27
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
@@ -59,15 +52,28 @@ pub const Actions = extern struct {
|
|
|
59
52
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
60
53
|
/// set to the same value).
|
|
61
54
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
62
|
-
inline for (@
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
56
|
+
@typeInfo(Actions).@"struct".fields
|
|
57
|
+
else
|
|
58
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
59
|
+
{
|
|
60
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
61
|
+
player.name
|
|
62
|
+
else
|
|
63
|
+
player;
|
|
64
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
65
|
+
@typeInfo(Action).@"struct".fields
|
|
66
|
+
else
|
|
67
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
68
|
+
{
|
|
69
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
70
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
71
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
66
72
|
|
|
67
73
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
68
|
-
|
|
74
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
69
75
|
return false,
|
|
70
|
-
|
|
76
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
71
77
|
else => unreachable,
|
|
72
78
|
}
|
|
73
79
|
}
|
|
@@ -75,7 +81,26 @@ pub const Actions = extern struct {
|
|
|
75
81
|
return true;
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
|
|
84
|
+
/// TODO
|
|
85
|
+
pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
|
|
86
|
+
for (bs, 0..) |b, j| {
|
|
87
|
+
// TODO: is skipping this redundant check worth it?
|
|
88
|
+
if (i == j) continue;
|
|
89
|
+
if (b.matches(a)) return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// TODO
|
|
95
|
+
pub fn relax(actions: Actions) Actions {
|
|
96
|
+
if (!options.overwrite) return actions;
|
|
97
|
+
var a = actions;
|
|
98
|
+
if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
|
|
99
|
+
if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
|
|
100
|
+
return a;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
79
104
|
try writer.writeAll("<P1 = ");
|
|
80
105
|
try self.p1.fmt(writer, shape);
|
|
81
106
|
try writer.writeAll(", P2 = ");
|
|
@@ -83,8 +108,7 @@ pub const Actions = extern struct {
|
|
|
83
108
|
try writer.writeAll(">");
|
|
84
109
|
}
|
|
85
110
|
|
|
86
|
-
pub fn format(a: Actions,
|
|
87
|
-
_ = .{ f, o };
|
|
111
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
88
112
|
try fmt(a, w, false);
|
|
89
113
|
}
|
|
90
114
|
};
|
|
@@ -108,18 +132,41 @@ test Actions {
|
|
|
108
132
|
}
|
|
109
133
|
|
|
110
134
|
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
111
|
-
pub
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
pub fn Observation(comptime field: Duration.Field) type {
|
|
136
|
+
return switch (field) {
|
|
137
|
+
.attacking, .binding => enum(u2) {
|
|
138
|
+
started,
|
|
139
|
+
continuing,
|
|
140
|
+
ended,
|
|
141
|
+
},
|
|
142
|
+
.sleep, .disable => enum(u3) {
|
|
143
|
+
started,
|
|
144
|
+
continuing,
|
|
145
|
+
ended,
|
|
146
|
+
// TODO haze support
|
|
147
|
+
unused1,
|
|
148
|
+
unused2,
|
|
149
|
+
unused3,
|
|
150
|
+
unused4,
|
|
151
|
+
},
|
|
152
|
+
.confusion => enum(u4) {
|
|
153
|
+
started,
|
|
154
|
+
continuing,
|
|
155
|
+
ended,
|
|
156
|
+
/// An indicator that an existing confusion duration has been overwritten by a new
|
|
157
|
+
/// confusion duration due to a Thrashing move ending ("hidden" confusion). This is not
|
|
158
|
+
/// immediately observable to an opponent, to compute probabilities from an opponent's
|
|
159
|
+
/// point of view based solely on public information extra work must be done to correct
|
|
160
|
+
/// for the information leak
|
|
161
|
+
overwritten,
|
|
162
|
+
// TODO haze support
|
|
163
|
+
unused1,
|
|
164
|
+
unused2,
|
|
165
|
+
unused3,
|
|
166
|
+
unused4,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
123
170
|
|
|
124
171
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
125
172
|
/// single player.
|
|
@@ -146,26 +193,24 @@ pub const Action = packed struct(u64) {
|
|
|
146
193
|
duration: u4 = 0,
|
|
147
194
|
|
|
148
195
|
/// TODO
|
|
149
|
-
sleep: Optional(Observation) = .None,
|
|
196
|
+
sleep: Optional(Observation(.sleep)) = .None,
|
|
150
197
|
/// TODO
|
|
151
|
-
confusion: Optional(
|
|
198
|
+
confusion: Optional(Observation(.confusion)) = .None,
|
|
152
199
|
/// TODO
|
|
153
|
-
disable: Optional(Observation) = .None,
|
|
200
|
+
disable: Optional(Observation(.disable)) = .None,
|
|
154
201
|
/// TODO
|
|
155
|
-
attacking: Optional(Observation) = .None,
|
|
202
|
+
attacking: Optional(Observation(.attacking)) = .None,
|
|
156
203
|
/// TODO
|
|
157
|
-
binding: Optional(Observation) = .None,
|
|
158
|
-
|
|
159
|
-
_: u1 = 0,
|
|
204
|
+
binding: Optional(Observation(.binding)) = .None,
|
|
160
205
|
|
|
161
206
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
162
207
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
163
|
-
move_slot:
|
|
208
|
+
move_slot: u3 = 0,
|
|
209
|
+
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
210
|
+
multi_hit: u3 = 0,
|
|
164
211
|
|
|
165
212
|
/// TODO
|
|
166
213
|
pp: u4 = 0,
|
|
167
|
-
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
168
|
-
multi_hit: u4 = 0,
|
|
169
214
|
|
|
170
215
|
/// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
|
|
171
216
|
psywave: u8 = 0,
|
|
@@ -175,44 +220,45 @@ pub const Action = packed struct(u64) {
|
|
|
175
220
|
|
|
176
221
|
pub const Field = std.meta.FieldEnum(Action);
|
|
177
222
|
|
|
178
|
-
pub fn format(a: Action,
|
|
179
|
-
_ = .{ f, o };
|
|
223
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
180
224
|
try fmt(a, w, false);
|
|
181
225
|
}
|
|
182
226
|
|
|
183
|
-
|
|
227
|
+
const SYMBOLS = [_][]const u8{ "+", "", "-", "#" };
|
|
228
|
+
|
|
229
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
184
230
|
try writer.writeByte('(');
|
|
185
231
|
var printed = false;
|
|
186
|
-
inline for (@
|
|
187
|
-
|
|
232
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
233
|
+
@typeInfo(Action).@"struct".fields
|
|
234
|
+
else
|
|
235
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
236
|
+
{
|
|
237
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
238
|
+
const val = @field(self, field_name);
|
|
188
239
|
switch (@typeInfo(@TypeOf(val))) {
|
|
189
|
-
|
|
240
|
+
.@"enum" => if (val != .None) {
|
|
190
241
|
if (printed) try writer.writeAll(", ");
|
|
191
242
|
if (shape) {
|
|
192
|
-
try writer.print("{s}:?", .{
|
|
243
|
+
try writer.print("{s}:?", .{field_name});
|
|
193
244
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
194
245
|
try writer.print("{s}{s}", .{
|
|
195
246
|
if (val == .false) "!" else "",
|
|
196
|
-
|
|
197
|
-
});
|
|
198
|
-
} else if (@TypeOf(val) == Optional(Observation) or
|
|
199
|
-
@TypeOf(val) == Optional(Confusion))
|
|
200
|
-
{
|
|
201
|
-
try writer.print("{s}{s}", .{
|
|
202
|
-
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
203
|
-
field.name,
|
|
247
|
+
field_name,
|
|
204
248
|
});
|
|
249
|
+
} else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
|
|
250
|
+
try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field_name });
|
|
205
251
|
} else {
|
|
206
|
-
try writer.print("{s}:{s}", .{
|
|
252
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
207
253
|
}
|
|
208
254
|
printed = true;
|
|
209
255
|
},
|
|
210
|
-
|
|
256
|
+
.int => if (val != 0) {
|
|
211
257
|
if (printed) try writer.writeAll(", ");
|
|
212
258
|
if (shape) {
|
|
213
|
-
try writer.print("{s}:?", .{
|
|
259
|
+
try writer.print("{s}:?", .{field_name});
|
|
214
260
|
} else {
|
|
215
|
-
try writer.print("{s}:{d}", .{
|
|
261
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
216
262
|
}
|
|
217
263
|
printed = true;
|
|
218
264
|
},
|
|
@@ -224,7 +270,7 @@ pub const Action = packed struct(u64) {
|
|
|
224
270
|
};
|
|
225
271
|
|
|
226
272
|
/// TODO
|
|
227
|
-
pub const Durations = struct {
|
|
273
|
+
pub const Durations = extern struct {
|
|
228
274
|
/// TODO
|
|
229
275
|
p1: Duration = .{},
|
|
230
276
|
/// TODO
|
|
@@ -240,14 +286,8 @@ pub const Durations = struct {
|
|
|
240
286
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
241
287
|
}
|
|
242
288
|
|
|
243
|
-
pub fn format(
|
|
244
|
-
self
|
|
245
|
-
comptime fmt: []const u8,
|
|
246
|
-
opts: std.fmt.FormatOptions,
|
|
247
|
-
writer: anytype,
|
|
248
|
-
) !void {
|
|
249
|
-
_ = .{ fmt, opts };
|
|
250
|
-
try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
|
|
289
|
+
pub fn format(self: Durations, writer: *std.Io.Writer) !void {
|
|
290
|
+
try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
|
|
251
291
|
}
|
|
252
292
|
};
|
|
253
293
|
|
|
@@ -263,29 +303,33 @@ pub const Duration = packed struct(u32) {
|
|
|
263
303
|
|
|
264
304
|
_: u1 = 0,
|
|
265
305
|
|
|
306
|
+
pub const Field = enum { attacking, binding, sleep, disable, confusion };
|
|
307
|
+
|
|
266
308
|
pub fn format(
|
|
267
309
|
self: Duration,
|
|
268
|
-
|
|
269
|
-
opts: std.fmt.FormatOptions,
|
|
270
|
-
writer: anytype,
|
|
310
|
+
writer: *std.Io.Writer,
|
|
271
311
|
) !void {
|
|
272
|
-
_ = .{ fmt, opts };
|
|
273
312
|
try writer.writeByte('(');
|
|
274
313
|
var printed = false;
|
|
275
|
-
inline for (@
|
|
276
|
-
|
|
314
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Duration).@"struct"), "fields"))
|
|
315
|
+
@typeInfo(Duration).@"struct".fields
|
|
316
|
+
else
|
|
317
|
+
@typeInfo(Duration).@"struct".field_names) |field|
|
|
318
|
+
{
|
|
319
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
320
|
+
const val = @field(self, field_name);
|
|
277
321
|
switch (@typeInfo(@TypeOf(val))) {
|
|
278
|
-
|
|
322
|
+
.int => if (val != 0) {
|
|
279
323
|
if (printed) try writer.writeAll(", ");
|
|
280
|
-
if (comptime std.mem.eql(u8,
|
|
281
|
-
try writer.print("{s}:[", .{
|
|
324
|
+
if (comptime std.mem.eql(u8, field_name, "sleeps")) {
|
|
325
|
+
try writer.print("{s}:[", .{field_name});
|
|
282
326
|
for (0..6) |i| {
|
|
283
327
|
if (i != 0) try writer.writeByte(',');
|
|
284
328
|
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
285
329
|
}
|
|
286
330
|
try writer.writeAll("]");
|
|
287
331
|
} else {
|
|
288
|
-
try writer.print("{s}:{d}", .{
|
|
332
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
289
333
|
}
|
|
290
334
|
printed = true;
|
|
291
335
|
},
|
|
@@ -375,7 +419,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
375
419
|
|
|
376
420
|
// If the move actually lands we can commit any past critical hit / damage rolls. Note
|
|
377
421
|
// we can't rely on the presence or absence of a damage roll to determine whether a
|
|
378
|
-
// "critical hit" was spurious or not as moves that do 1 or less damage don't
|
|
422
|
+
// "critical hit" was spurious or not as moves that do 1 or less damage don't roll for
|
|
379
423
|
// damage but can still crit (instead, whether or not a roll is deemed to be a no-op is
|
|
380
424
|
// passed to the critical hit helper).
|
|
381
425
|
//
|
|
@@ -391,7 +435,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
391
435
|
action.critical_hit = if (self.pending.crit) .true else .false;
|
|
392
436
|
} else assert(!self.pending.glitch);
|
|
393
437
|
|
|
394
|
-
if (self.pending.damage_roll > 0) {
|
|
438
|
+
if (self.pending.damage_roll > 0 and (!self.pending.glitch or self.pending.hit)) {
|
|
395
439
|
try self.probability.update(1, 39);
|
|
396
440
|
action.damage = self.pending.damage_roll;
|
|
397
441
|
}
|
|
@@ -536,7 +580,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
536
580
|
self: *Self,
|
|
537
581
|
comptime field: Action.Field,
|
|
538
582
|
player: Player,
|
|
539
|
-
obs: Optional(
|
|
583
|
+
obs: Optional(Observation(.confusion)),
|
|
540
584
|
) void {
|
|
541
585
|
if (!enabled) return;
|
|
542
586
|
|
|
@@ -578,7 +622,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
578
622
|
}
|
|
579
623
|
}
|
|
580
624
|
|
|
581
|
-
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
625
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
582
626
|
if (!enabled) return;
|
|
583
627
|
|
|
584
628
|
var a = self.actions.get(player);
|
|
@@ -602,7 +646,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
602
646
|
}
|
|
603
647
|
}
|
|
604
648
|
|
|
605
|
-
pub fn confusion(
|
|
649
|
+
pub fn confusion(
|
|
650
|
+
self: *Self,
|
|
651
|
+
player: Player,
|
|
652
|
+
obs: Optional(Observation(.confusion)),
|
|
653
|
+
) Error!void {
|
|
606
654
|
if (!enabled) return;
|
|
607
655
|
|
|
608
656
|
var a = self.actions.get(player);
|
|
@@ -624,7 +672,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
624
672
|
}
|
|
625
673
|
}
|
|
626
674
|
|
|
627
|
-
pub fn disable(
|
|
675
|
+
pub fn disable(
|
|
676
|
+
self: *Self,
|
|
677
|
+
player: Player,
|
|
678
|
+
obs: Optional(Observation(.disable)),
|
|
679
|
+
) Error!void {
|
|
628
680
|
if (!enabled) return;
|
|
629
681
|
|
|
630
682
|
var a = self.actions.get(player);
|
|
@@ -646,7 +698,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
646
698
|
}
|
|
647
699
|
}
|
|
648
700
|
|
|
649
|
-
pub fn attacking(
|
|
701
|
+
pub fn attacking(
|
|
702
|
+
self: *Self,
|
|
703
|
+
player: Player,
|
|
704
|
+
obs: Optional(Observation(.attacking)),
|
|
705
|
+
) Error!void {
|
|
650
706
|
if (!enabled) return;
|
|
651
707
|
|
|
652
708
|
var a = self.actions.get(player);
|
|
@@ -668,7 +724,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
668
724
|
}
|
|
669
725
|
}
|
|
670
726
|
|
|
671
|
-
pub fn binding(
|
|
727
|
+
pub fn binding(
|
|
728
|
+
self: *Self,
|
|
729
|
+
player: Player,
|
|
730
|
+
obs: Optional(Observation(.binding)),
|
|
731
|
+
) Error!void {
|
|
672
732
|
if (!enabled) return;
|
|
673
733
|
|
|
674
734
|
var a = self.actions.get(player);
|
|
@@ -878,13 +938,13 @@ test "Chance.moveSlot" {
|
|
|
878
938
|
|
|
879
939
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
880
940
|
try expectProbability(&chance.probability, 1, 2);
|
|
881
|
-
try expectValue(@as(
|
|
941
|
+
try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
|
|
882
942
|
|
|
883
943
|
chance.reset();
|
|
884
944
|
|
|
885
945
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
886
946
|
try expectProbability(&chance.probability, 1, 3);
|
|
887
|
-
try expectValue(@as(
|
|
947
|
+
try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
|
|
888
948
|
}
|
|
889
949
|
|
|
890
950
|
test "Chance.multiHit" {
|
|
@@ -1051,7 +1111,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
1051
1111
|
|
|
1052
1112
|
r.reduce();
|
|
1053
1113
|
if (r.p != p or r.q != q) {
|
|
1054
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
1114
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
1055
1115
|
return error.TestExpectedEqual;
|
|
1056
1116
|
}
|
|
1057
1117
|
}
|
|
@@ -1129,28 +1189,44 @@ const Null = struct {
|
|
|
1129
1189
|
self: Null,
|
|
1130
1190
|
comptime field: Action.Field,
|
|
1131
1191
|
player: Player,
|
|
1132
|
-
obs: Optional(
|
|
1192
|
+
obs: Optional(Observation(.confusion)),
|
|
1133
1193
|
) void {
|
|
1134
1194
|
_ = .{ self, field, player, obs };
|
|
1135
1195
|
}
|
|
1136
1196
|
|
|
1137
|
-
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1197
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
1138
1198
|
_ = .{ self, player, obs };
|
|
1139
1199
|
}
|
|
1140
1200
|
|
|
1141
|
-
pub fn confusion(
|
|
1201
|
+
pub fn confusion(
|
|
1202
|
+
self: Null,
|
|
1203
|
+
player: Player,
|
|
1204
|
+
obs: Optional(Observation(.confusion)),
|
|
1205
|
+
) Error!void {
|
|
1142
1206
|
_ = .{ self, player, obs };
|
|
1143
1207
|
}
|
|
1144
1208
|
|
|
1145
|
-
pub fn disable(
|
|
1209
|
+
pub fn disable(
|
|
1210
|
+
self: Null,
|
|
1211
|
+
player: Player,
|
|
1212
|
+
obs: Optional(Observation(.disable)),
|
|
1213
|
+
) Error!void {
|
|
1146
1214
|
_ = .{ self, player, obs };
|
|
1147
1215
|
}
|
|
1148
1216
|
|
|
1149
|
-
pub fn attacking(
|
|
1217
|
+
pub fn attacking(
|
|
1218
|
+
self: Null,
|
|
1219
|
+
player: Player,
|
|
1220
|
+
obs: Optional(Observation(.attacking)),
|
|
1221
|
+
) Error!void {
|
|
1150
1222
|
_ = .{ self, player, obs };
|
|
1151
1223
|
}
|
|
1152
1224
|
|
|
1153
|
-
pub fn binding(
|
|
1225
|
+
pub fn binding(
|
|
1226
|
+
self: Null,
|
|
1227
|
+
player: Player,
|
|
1228
|
+
obs: Optional(Observation(.binding)),
|
|
1229
|
+
) Error!void {
|
|
1154
1230
|
_ = .{ self, player, obs };
|
|
1155
1231
|
}
|
|
1156
1232
|
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
|
-
const rng = @import("../../common/rng.zig");
|
|
6
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const rng = @import("../../common/rng.zig");
|
|
5
|
+
const std = @import("std");
|
|
7
6
|
|
|
8
7
|
const assert = std.debug.assert;
|
|
9
|
-
|
|
10
8
|
const percent = rng.Gen12.percent;
|
|
11
9
|
const Type = gen1.Type;
|
|
12
10
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen1.Stats;
|
|
10
8
|
const Types = gen1.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
|
@@ -99,7 +98,7 @@ pub const Type = enum(u4) {
|
|
|
99
98
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
/// The precedence order of Type `
|
|
101
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
103
102
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
104
103
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
105
104
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
@@ -109,7 +108,7 @@ pub const Type = enum(u4) {
|
|
|
109
108
|
};
|
|
110
109
|
|
|
111
110
|
/// Representation of a Pokémon's typing.
|
|
112
|
-
pub const Types = packed struct {
|
|
111
|
+
pub const Types = packed struct(u8) {
|
|
113
112
|
/// A Pokémon's primary type.
|
|
114
113
|
type1: Type = .Normal,
|
|
115
114
|
/// A Pokémon's secondary type (may be identical to its primary type).
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
2
|
+
const calc = @import("calc.zig");
|
|
3
|
+
const chance = @import("chance.zig");
|
|
6
4
|
const data = @import("../common/data.zig");
|
|
5
|
+
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const moves = @import("data/moves.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
8
|
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
|
-
|
|
11
|
-
const moves = @import("data/moves.zig");
|
|
12
10
|
const species = @import("data/species.zig");
|
|
11
|
+
const std = @import("std");
|
|
13
12
|
const types = @import("data/types.zig");
|
|
14
|
-
|
|
15
|
-
const calc = @import("calc.zig");
|
|
16
|
-
const chance = @import("chance.zig");
|
|
17
|
-
const mechanics = @import("mechanics.zig");
|
|
13
|
+
const util = @import("../common/util.zig");
|
|
18
14
|
|
|
19
15
|
const assert = std.debug.assert;
|
|
20
16
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -66,7 +62,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
66
62
|
/// The last damage dealt by either side.
|
|
67
63
|
last_damage: u16 = 0,
|
|
68
64
|
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
69
|
-
last_moves: [2]MoveDetails =
|
|
65
|
+
last_moves: [2]MoveDetails = @splat(.{}),
|
|
70
66
|
/// The pseudo random number generator.
|
|
71
67
|
rng: RNG,
|
|
72
68
|
|
|
@@ -126,12 +122,12 @@ test Battle {
|
|
|
126
122
|
/// Representation of one side of a Generation I Pokémon battle.
|
|
127
123
|
pub const Side = extern struct {
|
|
128
124
|
/// The player's party in its original order
|
|
129
|
-
pokemon: [6]Pokemon =
|
|
125
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
130
126
|
/// The active Pokémon for the side, zero initialized if the battle has yet to start.
|
|
131
127
|
/// Note that fainted Pokémon are still consider "active" until their replacement switches in.
|
|
132
128
|
active: ActivePokemon = .{},
|
|
133
129
|
/// One-based slot indexes reflecting the current order of the player's party.
|
|
134
|
-
order: [6]u8 =
|
|
130
|
+
order: [6]u8 = @splat(0),
|
|
135
131
|
/// The last move the player selected.
|
|
136
132
|
last_selected_move: Move = .None,
|
|
137
133
|
/// The last move the player used.
|
|
@@ -170,7 +166,7 @@ pub const ActivePokemon = extern struct {
|
|
|
170
166
|
/// The active Pokémon's volatile statuses and associated data.
|
|
171
167
|
volatiles: Volatiles = .{},
|
|
172
168
|
/// The active Pokémon's current move slots.
|
|
173
|
-
moves: [4]MoveSlot =
|
|
169
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
174
170
|
|
|
175
171
|
comptime {
|
|
176
172
|
assert(@sizeOf(ActivePokemon) == 32);
|
|
@@ -190,7 +186,7 @@ pub const Pokemon = extern struct {
|
|
|
190
186
|
/// The Pokémon's unmodified stats.
|
|
191
187
|
stats: Stats(u16) = .{},
|
|
192
188
|
/// The Pokémon's original stored move slots.
|
|
193
|
-
moves: [4]MoveSlot =
|
|
189
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
194
190
|
/// The Pokémon's current HP.
|
|
195
191
|
hp: u16 = 0,
|
|
196
192
|
/// The Pokémon's current status.
|