@pkmn/engine 0.1.0-dev.36b5d56b → 0.1.0-dev.388b6109
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 +26 -19
- package/build/pkg/addon/browser.js +4 -1
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +20 -7
- package/build/pkg/addon/node.js.map +1 -1
- package/build/pkg/addon/wasm.js +41 -9
- package/build/pkg/addon/wasm.js.map +1 -1
- package/build/pkg/addon.d.ts +7 -7
- 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 +54 -11
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +19 -94
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +18 -7
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +20 -9
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +140 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +309 -233
- package/package.json +31 -130
- 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 -60
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +26 -26
- package/src/lib/common/protocol.zig +224 -189
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/util.zig +34 -31
- package/src/lib/gen1/calc.zig +139 -117
- package/src/lib/gen1/chance.zig +149 -88
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +5 -5
- package/src/lib/gen1/helpers.zig +30 -22
- package/src/lib/gen1/mechanics.zig +50 -43
- package/src/lib/gen2/calc.zig +3 -6
- package/src/lib/gen2/chance.zig +38 -26
- package/src/lib/gen2/data/types.zig +6 -3
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/helpers.zig +27 -19
- package/src/lib/gen2/mechanics.zig +36 -25
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +3 -118
- package/src/lib/pkmn.zig +94 -19
- package/src/lib/wasm.zig +10 -9
- package/src/tools/display/dom.ts +43 -31
package/src/lib/gen1/chance.zig
CHANGED
|
@@ -23,10 +23,6 @@ const PointerType = util.PointerType;
|
|
|
23
23
|
const print = std.debug.print;
|
|
24
24
|
const showdown = options.showdown;
|
|
25
25
|
|
|
26
|
-
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
27
|
-
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
28
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
29
|
-
|
|
30
26
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
31
27
|
/// were observed during a Generation I battle `update`. This can additionally be provided as input
|
|
32
28
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -49,22 +45,36 @@ pub const Actions = extern struct {
|
|
|
49
45
|
|
|
50
46
|
/// Returns true if `a` is equal to `b`.
|
|
51
47
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
52
|
-
return @as(
|
|
48
|
+
return @as(u64, @bitCast(a.p1)) == @as(u64, @bitCast(b.p1)) and
|
|
49
|
+
@as(u64, @bitCast(a.p2)) == @as(u64, @bitCast(b.p2));
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
/// Returns true if `a` has the same "shape" as `b`, where `Actions` are defined to have the
|
|
56
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
57
54
|
/// set to the same value).
|
|
58
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
59
|
-
inline for (@
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
57
|
+
@typeInfo(Actions).@"struct".fields
|
|
58
|
+
else
|
|
59
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
60
|
+
{
|
|
61
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
62
|
+
player.name
|
|
63
|
+
else
|
|
64
|
+
player;
|
|
65
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
66
|
+
@typeInfo(Action).@"struct".fields
|
|
67
|
+
else
|
|
68
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
69
|
+
{
|
|
70
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
71
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
72
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
63
73
|
|
|
64
74
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
65
|
-
|
|
75
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
66
76
|
return false,
|
|
67
|
-
|
|
77
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
68
78
|
else => unreachable,
|
|
69
79
|
}
|
|
70
80
|
}
|
|
@@ -91,7 +101,7 @@ pub const Actions = extern struct {
|
|
|
91
101
|
return a;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
pub fn fmt(self: Actions, writer:
|
|
104
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
95
105
|
try writer.writeAll("<P1 = ");
|
|
96
106
|
try self.p1.fmt(writer, shape);
|
|
97
107
|
try writer.writeAll(", P2 = ");
|
|
@@ -99,8 +109,7 @@ pub const Actions = extern struct {
|
|
|
99
109
|
try writer.writeAll(">");
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
pub fn format(a: Actions,
|
|
103
|
-
_ = .{ f, o };
|
|
112
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
104
113
|
try fmt(a, w, false);
|
|
105
114
|
}
|
|
106
115
|
};
|
|
@@ -124,18 +133,41 @@ test Actions {
|
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
127
|
-
pub
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
pub fn Observation(comptime field: Duration.Field) type {
|
|
137
|
+
return switch (field) {
|
|
138
|
+
.attacking, .binding => enum(u2) {
|
|
139
|
+
started,
|
|
140
|
+
continuing,
|
|
141
|
+
ended,
|
|
142
|
+
},
|
|
143
|
+
.sleep, .disable => enum(u3) {
|
|
144
|
+
started,
|
|
145
|
+
continuing,
|
|
146
|
+
ended,
|
|
147
|
+
// TODO haze support
|
|
148
|
+
unused1,
|
|
149
|
+
unused2,
|
|
150
|
+
unused3,
|
|
151
|
+
unused4,
|
|
152
|
+
},
|
|
153
|
+
.confusion => enum(u4) {
|
|
154
|
+
started,
|
|
155
|
+
continuing,
|
|
156
|
+
ended,
|
|
157
|
+
/// An indicator that an existing confusion duration has been overwritten by a new
|
|
158
|
+
/// confusion duration due to a Thrashing move ending ("hidden" confusion). This is not
|
|
159
|
+
/// immediately observable to an opponent, to compute probabilities from an opponent's
|
|
160
|
+
/// point of view based solely on public information extra work must be done to correct
|
|
161
|
+
/// for the information leak
|
|
162
|
+
overwritten,
|
|
163
|
+
// TODO haze support
|
|
164
|
+
unused1,
|
|
165
|
+
unused2,
|
|
166
|
+
unused3,
|
|
167
|
+
unused4,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
139
171
|
|
|
140
172
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
141
173
|
/// single player.
|
|
@@ -162,26 +194,24 @@ pub const Action = packed struct(u64) {
|
|
|
162
194
|
duration: u4 = 0,
|
|
163
195
|
|
|
164
196
|
/// TODO
|
|
165
|
-
sleep: Optional(Observation) = .None,
|
|
197
|
+
sleep: Optional(Observation(.sleep)) = .None,
|
|
166
198
|
/// TODO
|
|
167
|
-
confusion: Optional(
|
|
199
|
+
confusion: Optional(Observation(.confusion)) = .None,
|
|
168
200
|
/// TODO
|
|
169
|
-
disable: Optional(Observation) = .None,
|
|
201
|
+
disable: Optional(Observation(.disable)) = .None,
|
|
170
202
|
/// TODO
|
|
171
|
-
attacking: Optional(Observation) = .None,
|
|
203
|
+
attacking: Optional(Observation(.attacking)) = .None,
|
|
172
204
|
/// TODO
|
|
173
|
-
binding: Optional(Observation) = .None,
|
|
174
|
-
|
|
175
|
-
_: u1 = 0,
|
|
205
|
+
binding: Optional(Observation(.binding)) = .None,
|
|
176
206
|
|
|
177
207
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
178
208
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
179
|
-
move_slot:
|
|
209
|
+
move_slot: u3 = 0,
|
|
210
|
+
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
211
|
+
multi_hit: u3 = 0,
|
|
180
212
|
|
|
181
213
|
/// TODO
|
|
182
214
|
pp: u4 = 0,
|
|
183
|
-
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
184
|
-
multi_hit: u4 = 0,
|
|
185
215
|
|
|
186
216
|
/// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
|
|
187
217
|
psywave: u8 = 0,
|
|
@@ -191,44 +221,45 @@ pub const Action = packed struct(u64) {
|
|
|
191
221
|
|
|
192
222
|
pub const Field = std.meta.FieldEnum(Action);
|
|
193
223
|
|
|
194
|
-
pub fn format(a: Action,
|
|
195
|
-
_ = .{ f, o };
|
|
224
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
196
225
|
try fmt(a, w, false);
|
|
197
226
|
}
|
|
198
227
|
|
|
199
|
-
|
|
228
|
+
const SYMBOLS = [_][]const u8{ "+", "", "-", "#" };
|
|
229
|
+
|
|
230
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
200
231
|
try writer.writeByte('(');
|
|
201
232
|
var printed = false;
|
|
202
|
-
inline for (@
|
|
203
|
-
|
|
233
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
234
|
+
@typeInfo(Action).@"struct".fields
|
|
235
|
+
else
|
|
236
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
237
|
+
{
|
|
238
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
239
|
+
const val = @field(self, field_name);
|
|
204
240
|
switch (@typeInfo(@TypeOf(val))) {
|
|
205
|
-
|
|
241
|
+
.@"enum" => if (val != .None) {
|
|
206
242
|
if (printed) try writer.writeAll(", ");
|
|
207
243
|
if (shape) {
|
|
208
|
-
try writer.print("{s}:?", .{
|
|
244
|
+
try writer.print("{s}:?", .{field_name});
|
|
209
245
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
210
246
|
try writer.print("{s}{s}", .{
|
|
211
247
|
if (val == .false) "!" else "",
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
} else if (@TypeOf(val) == Optional(Observation) or
|
|
215
|
-
@TypeOf(val) == Optional(Confusion))
|
|
216
|
-
{
|
|
217
|
-
try writer.print("{s}{s}", .{
|
|
218
|
-
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
219
|
-
field.name,
|
|
248
|
+
field_name,
|
|
220
249
|
});
|
|
250
|
+
} else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
|
|
251
|
+
try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field_name });
|
|
221
252
|
} else {
|
|
222
|
-
try writer.print("{s}:{s}", .{
|
|
253
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
223
254
|
}
|
|
224
255
|
printed = true;
|
|
225
256
|
},
|
|
226
|
-
|
|
257
|
+
.int => if (val != 0) {
|
|
227
258
|
if (printed) try writer.writeAll(", ");
|
|
228
259
|
if (shape) {
|
|
229
|
-
try writer.print("{s}:?", .{
|
|
260
|
+
try writer.print("{s}:?", .{field_name});
|
|
230
261
|
} else {
|
|
231
|
-
try writer.print("{s}:{d}", .{
|
|
262
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
232
263
|
}
|
|
233
264
|
printed = true;
|
|
234
265
|
},
|
|
@@ -256,14 +287,8 @@ pub const Durations = extern struct {
|
|
|
256
287
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
257
288
|
}
|
|
258
289
|
|
|
259
|
-
pub fn format(
|
|
260
|
-
self
|
|
261
|
-
comptime fmt: []const u8,
|
|
262
|
-
opts: std.fmt.FormatOptions,
|
|
263
|
-
writer: anytype,
|
|
264
|
-
) !void {
|
|
265
|
-
_ = .{ fmt, opts };
|
|
266
|
-
try writer.print("[P1 = {}, P2 = {}]", .{ self.p1, self.p2 });
|
|
290
|
+
pub fn format(self: Durations, writer: *std.Io.Writer) !void {
|
|
291
|
+
try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
|
|
267
292
|
}
|
|
268
293
|
};
|
|
269
294
|
|
|
@@ -279,29 +304,33 @@ pub const Duration = packed struct(u32) {
|
|
|
279
304
|
|
|
280
305
|
_: u1 = 0,
|
|
281
306
|
|
|
307
|
+
pub const Field = enum { attacking, binding, sleep, disable, confusion };
|
|
308
|
+
|
|
282
309
|
pub fn format(
|
|
283
310
|
self: Duration,
|
|
284
|
-
|
|
285
|
-
opts: std.fmt.FormatOptions,
|
|
286
|
-
writer: anytype,
|
|
311
|
+
writer: *std.Io.Writer,
|
|
287
312
|
) !void {
|
|
288
|
-
_ = .{ fmt, opts };
|
|
289
313
|
try writer.writeByte('(');
|
|
290
314
|
var printed = false;
|
|
291
|
-
inline for (@
|
|
292
|
-
|
|
315
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Duration).@"struct"), "fields"))
|
|
316
|
+
@typeInfo(Duration).@"struct".fields
|
|
317
|
+
else
|
|
318
|
+
@typeInfo(Duration).@"struct".field_names) |field|
|
|
319
|
+
{
|
|
320
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
321
|
+
const val = @field(self, field_name);
|
|
293
322
|
switch (@typeInfo(@TypeOf(val))) {
|
|
294
|
-
|
|
323
|
+
.int => if (val != 0) {
|
|
295
324
|
if (printed) try writer.writeAll(", ");
|
|
296
|
-
if (comptime std.mem.eql(u8,
|
|
297
|
-
try writer.print("{s}:[", .{
|
|
325
|
+
if (comptime std.mem.eql(u8, field_name, "sleeps")) {
|
|
326
|
+
try writer.print("{s}:[", .{field_name});
|
|
298
327
|
for (0..6) |i| {
|
|
299
328
|
if (i != 0) try writer.writeByte(',');
|
|
300
329
|
try writer.print("{d}", .{Sleeps.get(val, i)});
|
|
301
330
|
}
|
|
302
331
|
try writer.writeAll("]");
|
|
303
332
|
} else {
|
|
304
|
-
try writer.print("{s}:{d}", .{
|
|
333
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
305
334
|
}
|
|
306
335
|
printed = true;
|
|
307
336
|
},
|
|
@@ -552,7 +581,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
552
581
|
self: *Self,
|
|
553
582
|
comptime field: Action.Field,
|
|
554
583
|
player: Player,
|
|
555
|
-
obs: Optional(
|
|
584
|
+
obs: Optional(Observation(.confusion)),
|
|
556
585
|
) void {
|
|
557
586
|
if (!enabled) return;
|
|
558
587
|
|
|
@@ -594,7 +623,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
594
623
|
}
|
|
595
624
|
}
|
|
596
625
|
|
|
597
|
-
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
626
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
598
627
|
if (!enabled) return;
|
|
599
628
|
|
|
600
629
|
var a = self.actions.get(player);
|
|
@@ -618,7 +647,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
618
647
|
}
|
|
619
648
|
}
|
|
620
649
|
|
|
621
|
-
pub fn confusion(
|
|
650
|
+
pub fn confusion(
|
|
651
|
+
self: *Self,
|
|
652
|
+
player: Player,
|
|
653
|
+
obs: Optional(Observation(.confusion)),
|
|
654
|
+
) Error!void {
|
|
622
655
|
if (!enabled) return;
|
|
623
656
|
|
|
624
657
|
var a = self.actions.get(player);
|
|
@@ -640,7 +673,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
640
673
|
}
|
|
641
674
|
}
|
|
642
675
|
|
|
643
|
-
pub fn disable(
|
|
676
|
+
pub fn disable(
|
|
677
|
+
self: *Self,
|
|
678
|
+
player: Player,
|
|
679
|
+
obs: Optional(Observation(.disable)),
|
|
680
|
+
) Error!void {
|
|
644
681
|
if (!enabled) return;
|
|
645
682
|
|
|
646
683
|
var a = self.actions.get(player);
|
|
@@ -662,7 +699,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
662
699
|
}
|
|
663
700
|
}
|
|
664
701
|
|
|
665
|
-
pub fn attacking(
|
|
702
|
+
pub fn attacking(
|
|
703
|
+
self: *Self,
|
|
704
|
+
player: Player,
|
|
705
|
+
obs: Optional(Observation(.attacking)),
|
|
706
|
+
) Error!void {
|
|
666
707
|
if (!enabled) return;
|
|
667
708
|
|
|
668
709
|
var a = self.actions.get(player);
|
|
@@ -684,7 +725,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
684
725
|
}
|
|
685
726
|
}
|
|
686
727
|
|
|
687
|
-
pub fn binding(
|
|
728
|
+
pub fn binding(
|
|
729
|
+
self: *Self,
|
|
730
|
+
player: Player,
|
|
731
|
+
obs: Optional(Observation(.binding)),
|
|
732
|
+
) Error!void {
|
|
688
733
|
if (!enabled) return;
|
|
689
734
|
|
|
690
735
|
var a = self.actions.get(player);
|
|
@@ -894,13 +939,13 @@ test "Chance.moveSlot" {
|
|
|
894
939
|
|
|
895
940
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
896
941
|
try expectProbability(&chance.probability, 1, 2);
|
|
897
|
-
try expectValue(@as(
|
|
942
|
+
try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
|
|
898
943
|
|
|
899
944
|
chance.reset();
|
|
900
945
|
|
|
901
946
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
902
947
|
try expectProbability(&chance.probability, 1, 3);
|
|
903
|
-
try expectValue(@as(
|
|
948
|
+
try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
|
|
904
949
|
}
|
|
905
950
|
|
|
906
951
|
test "Chance.multiHit" {
|
|
@@ -1067,7 +1112,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
1067
1112
|
|
|
1068
1113
|
r.reduce();
|
|
1069
1114
|
if (r.p != p or r.q != q) {
|
|
1070
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
1115
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
1071
1116
|
return error.TestExpectedEqual;
|
|
1072
1117
|
}
|
|
1073
1118
|
}
|
|
@@ -1145,28 +1190,44 @@ const Null = struct {
|
|
|
1145
1190
|
self: Null,
|
|
1146
1191
|
comptime field: Action.Field,
|
|
1147
1192
|
player: Player,
|
|
1148
|
-
obs: Optional(
|
|
1193
|
+
obs: Optional(Observation(.confusion)),
|
|
1149
1194
|
) void {
|
|
1150
1195
|
_ = .{ self, field, player, obs };
|
|
1151
1196
|
}
|
|
1152
1197
|
|
|
1153
|
-
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1198
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
1154
1199
|
_ = .{ self, player, obs };
|
|
1155
1200
|
}
|
|
1156
1201
|
|
|
1157
|
-
pub fn confusion(
|
|
1202
|
+
pub fn confusion(
|
|
1203
|
+
self: Null,
|
|
1204
|
+
player: Player,
|
|
1205
|
+
obs: Optional(Observation(.confusion)),
|
|
1206
|
+
) Error!void {
|
|
1158
1207
|
_ = .{ self, player, obs };
|
|
1159
1208
|
}
|
|
1160
1209
|
|
|
1161
|
-
pub fn disable(
|
|
1210
|
+
pub fn disable(
|
|
1211
|
+
self: Null,
|
|
1212
|
+
player: Player,
|
|
1213
|
+
obs: Optional(Observation(.disable)),
|
|
1214
|
+
) Error!void {
|
|
1162
1215
|
_ = .{ self, player, obs };
|
|
1163
1216
|
}
|
|
1164
1217
|
|
|
1165
|
-
pub fn attacking(
|
|
1218
|
+
pub fn attacking(
|
|
1219
|
+
self: Null,
|
|
1220
|
+
player: Player,
|
|
1221
|
+
obs: Optional(Observation(.attacking)),
|
|
1222
|
+
) Error!void {
|
|
1166
1223
|
_ = .{ self, player, obs };
|
|
1167
1224
|
}
|
|
1168
1225
|
|
|
1169
|
-
pub fn binding(
|
|
1226
|
+
pub fn binding(
|
|
1227
|
+
self: Null,
|
|
1228
|
+
player: Player,
|
|
1229
|
+
obs: Optional(Observation(.binding)),
|
|
1230
|
+
) Error!void {
|
|
1170
1231
|
_ = .{ self, player, obs };
|
|
1171
1232
|
}
|
|
1172
1233
|
|
|
@@ -98,7 +98,7 @@ pub const Type = enum(u4) {
|
|
|
98
98
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/// The precedence order of Type `
|
|
101
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
102
102
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
103
103
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
104
104
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
@@ -108,7 +108,7 @@ pub const Type = enum(u4) {
|
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
/// Representation of a Pokémon's typing.
|
|
111
|
-
pub const Types = packed struct {
|
|
111
|
+
pub const Types = packed struct(u8) {
|
|
112
112
|
/// A Pokémon's primary type.
|
|
113
113
|
type1: Type = .Normal,
|
|
114
114
|
/// A Pokémon's secondary type (may be identical to its primary type).
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -62,7 +62,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
62
62
|
/// The last damage dealt by either side.
|
|
63
63
|
last_damage: u16 = 0,
|
|
64
64
|
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
65
|
-
last_moves: [2]MoveDetails =
|
|
65
|
+
last_moves: [2]MoveDetails = @splat(.{}),
|
|
66
66
|
/// The pseudo random number generator.
|
|
67
67
|
rng: RNG,
|
|
68
68
|
|
|
@@ -122,12 +122,12 @@ test Battle {
|
|
|
122
122
|
/// Representation of one side of a Generation I Pokémon battle.
|
|
123
123
|
pub const Side = extern struct {
|
|
124
124
|
/// The player's party in its original order
|
|
125
|
-
pokemon: [6]Pokemon =
|
|
125
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
126
126
|
/// The active Pokémon for the side, zero initialized if the battle has yet to start.
|
|
127
127
|
/// Note that fainted Pokémon are still consider "active" until their replacement switches in.
|
|
128
128
|
active: ActivePokemon = .{},
|
|
129
129
|
/// One-based slot indexes reflecting the current order of the player's party.
|
|
130
|
-
order: [6]u8 =
|
|
130
|
+
order: [6]u8 = @splat(0),
|
|
131
131
|
/// The last move the player selected.
|
|
132
132
|
last_selected_move: Move = .None,
|
|
133
133
|
/// The last move the player used.
|
|
@@ -166,7 +166,7 @@ pub const ActivePokemon = extern struct {
|
|
|
166
166
|
/// The active Pokémon's volatile statuses and associated data.
|
|
167
167
|
volatiles: Volatiles = .{},
|
|
168
168
|
/// The active Pokémon's current move slots.
|
|
169
|
-
moves: [4]MoveSlot =
|
|
169
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
170
170
|
|
|
171
171
|
comptime {
|
|
172
172
|
assert(@sizeOf(ActivePokemon) == 32);
|
|
@@ -186,7 +186,7 @@ pub const Pokemon = extern struct {
|
|
|
186
186
|
/// The Pokémon's unmodified stats.
|
|
187
187
|
stats: Stats(u16) = .{},
|
|
188
188
|
/// The Pokémon's original stored move slots.
|
|
189
|
-
moves: [4]MoveSlot =
|
|
189
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
190
190
|
/// The Pokémon's current HP.
|
|
191
191
|
hp: u16 = 0,
|
|
192
192
|
/// The Pokémon's current status.
|
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -19,8 +19,6 @@ const Stats = data.Stats;
|
|
|
19
19
|
|
|
20
20
|
const endian = builtin.cpu.arch.endian();
|
|
21
21
|
|
|
22
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
23
|
-
|
|
24
22
|
/// Configuration options controlling the domain of valid Pokémon that can be generated by the
|
|
25
23
|
/// random generator helpers.
|
|
26
24
|
pub const Options = struct {
|
|
@@ -163,15 +161,20 @@ pub const Pokemon = struct {
|
|
|
163
161
|
var pokemon = data.Pokemon{};
|
|
164
162
|
pokemon.species = p.species;
|
|
165
163
|
const species = Species.get(p.species);
|
|
166
|
-
inline for (@
|
|
167
|
-
|
|
164
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(pokemon.stats)).@"struct"), "fields"))
|
|
165
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields
|
|
166
|
+
else
|
|
167
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".field_names) |field|
|
|
168
|
+
{
|
|
169
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
170
|
+
const hp = comptime std.mem.eql(u8, field_name, "hp");
|
|
168
171
|
const spc =
|
|
169
|
-
comptime std.mem.eql(u8,
|
|
170
|
-
@field(pokemon.stats,
|
|
171
|
-
|
|
172
|
-
@field(species.stats,
|
|
173
|
-
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs,
|
|
174
|
-
@field(p.stats,
|
|
172
|
+
comptime std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
|
|
173
|
+
@field(pokemon.stats, field_name) = Stats(u16).calc(
|
|
174
|
+
field_name,
|
|
175
|
+
@field(species.stats, field_name),
|
|
176
|
+
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field_name),
|
|
177
|
+
@field(p.stats, field_name),
|
|
175
178
|
p.level,
|
|
176
179
|
);
|
|
177
180
|
}
|
|
@@ -199,20 +202,25 @@ pub const Pokemon = struct {
|
|
|
199
202
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
200
203
|
var stats: Stats(u16) = .{};
|
|
201
204
|
const dvs = DVs.random(rand);
|
|
202
|
-
inline for (@
|
|
203
|
-
@
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(stats)).@"struct"), "fields"))
|
|
206
|
+
@typeInfo(@TypeOf(stats)).@"struct".fields
|
|
207
|
+
else
|
|
208
|
+
@typeInfo(@TypeOf(stats)).@"struct".field_names) |field|
|
|
209
|
+
{
|
|
210
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
211
|
+
@field(stats, field_name) = Stats(u16).calc(
|
|
212
|
+
field_name,
|
|
213
|
+
@field(species.stats, field_name),
|
|
214
|
+
if (comptime std.mem.eql(u8, field_name, "hp"))
|
|
207
215
|
dvs.hp()
|
|
208
216
|
else
|
|
209
|
-
@field(dvs,
|
|
217
|
+
@field(dvs, field_name),
|
|
210
218
|
if (rand.chance(u8, 1, 20)) rand.range(u16, 0, EXP + 1) else EXP,
|
|
211
219
|
lvl,
|
|
212
220
|
);
|
|
213
221
|
}
|
|
214
222
|
|
|
215
|
-
var ms
|
|
223
|
+
var ms: [4]MoveSlot = @splat(.{});
|
|
216
224
|
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
217
225
|
for (0..n) |i| {
|
|
218
226
|
var m: Move = .None;
|
|
@@ -292,23 +300,23 @@ else
|
|
|
292
300
|
pub fn diff(
|
|
293
301
|
a: *const data.Battle(data.PRNG),
|
|
294
302
|
b: *const data.Battle(data.PRNG),
|
|
295
|
-
w: protocol.
|
|
303
|
+
w: *protocol.Writer,
|
|
296
304
|
) !usize {
|
|
297
|
-
const pos = w.
|
|
305
|
+
const pos = w.pos;
|
|
298
306
|
const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
|
|
299
307
|
assert(size % 2 == 0);
|
|
300
308
|
assert(size / 2 < 255);
|
|
301
309
|
|
|
302
310
|
var i: usize = 0;
|
|
303
311
|
while (i < size) : (i += 2) {
|
|
304
|
-
const val =
|
|
305
|
-
if (val !=
|
|
312
|
+
const val = std.mem.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
|
|
313
|
+
if (val != std.mem.bytesAsValue(u16, std.mem.asBytes(b)[i .. i + 2]).*) {
|
|
306
314
|
try w.writeByte(@intCast(i / 2));
|
|
307
315
|
try w.writeInt(u16, val, endian);
|
|
308
316
|
}
|
|
309
317
|
}
|
|
310
318
|
|
|
311
|
-
return w.
|
|
319
|
+
return w.pos - pos;
|
|
312
320
|
}
|
|
313
321
|
|
|
314
322
|
/// TODO
|