@pkmn/engine 0.1.0-dev.a0447192 → 0.1.0-dev.a15b6c17
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 +22 -12
- 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 +55 -12
- 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 +35 -15
- 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 +239 -217
- package/package.json +34 -130
- package/src/bin/install-pkmn-engine +25 -9
- package/src/lib/bindings/c.zig +200 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +50 -0
- package/src/lib/common/array.zig +5 -12
- package/src/lib/common/debug.zig +33 -59
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +11 -21
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +228 -197
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +14 -31
- package/src/lib/gen1/calc.zig +159 -163
- package/src/lib/gen1/chance.zig +146 -93
- 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 +12 -20
- package/src/lib/gen1/mechanics.zig +103 -78
- package/src/lib/gen2/calc.zig +12 -23
- package/src/lib/gen2/chance.zig +12 -18
- 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 +9 -18
- 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 +0 -5
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,15 @@ 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
|
-
inline for (@
|
|
55
|
+
inline for (@typeInfo(Actions).@"struct".fields) |player| {
|
|
56
|
+
inline for (@typeInfo(Action).@"struct".fields) |field| {
|
|
64
57
|
const a_val = @field(@field(a, player.name), field.name);
|
|
65
58
|
const b_val = @field(@field(b, player.name), field.name);
|
|
66
59
|
|
|
67
60
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
68
|
-
|
|
61
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
69
62
|
return false,
|
|
70
|
-
|
|
63
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
71
64
|
else => unreachable,
|
|
72
65
|
}
|
|
73
66
|
}
|
|
@@ -75,7 +68,26 @@ pub const Actions = extern struct {
|
|
|
75
68
|
return true;
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
|
|
71
|
+
/// TODO
|
|
72
|
+
pub fn matchesAny(a: Actions, bs: []Actions, i: usize) bool {
|
|
73
|
+
for (bs, 0..) |b, j| {
|
|
74
|
+
// TODO: is skipping this redundant check worth it?
|
|
75
|
+
if (i == j) continue;
|
|
76
|
+
if (b.matches(a)) return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// TODO
|
|
82
|
+
pub fn relax(actions: Actions) Actions {
|
|
83
|
+
if (!options.overwrite) return actions;
|
|
84
|
+
var a = actions;
|
|
85
|
+
if (a.p1.confusion == .overwritten) a.p1.confusion = .continuing;
|
|
86
|
+
if (a.p2.confusion == .overwritten) a.p2.confusion = .continuing;
|
|
87
|
+
return a;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
79
91
|
try writer.writeAll("<P1 = ");
|
|
80
92
|
try self.p1.fmt(writer, shape);
|
|
81
93
|
try writer.writeAll(", P2 = ");
|
|
@@ -83,8 +95,7 @@ pub const Actions = extern struct {
|
|
|
83
95
|
try writer.writeAll(">");
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
pub fn format(a: Actions,
|
|
87
|
-
_ = .{ f, o };
|
|
98
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
88
99
|
try fmt(a, w, false);
|
|
89
100
|
}
|
|
90
101
|
};
|
|
@@ -108,18 +119,41 @@ test Actions {
|
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
/// 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
|
-
|
|
122
|
+
pub fn Observation(comptime field: Duration.Field) type {
|
|
123
|
+
return switch (field) {
|
|
124
|
+
.attacking, .binding => enum(u2) {
|
|
125
|
+
started,
|
|
126
|
+
continuing,
|
|
127
|
+
ended,
|
|
128
|
+
},
|
|
129
|
+
.sleep, .disable => enum(u3) {
|
|
130
|
+
started,
|
|
131
|
+
continuing,
|
|
132
|
+
ended,
|
|
133
|
+
// TODO haze support
|
|
134
|
+
unused1,
|
|
135
|
+
unused2,
|
|
136
|
+
unused3,
|
|
137
|
+
unused4,
|
|
138
|
+
},
|
|
139
|
+
.confusion => enum(u4) {
|
|
140
|
+
started,
|
|
141
|
+
continuing,
|
|
142
|
+
ended,
|
|
143
|
+
/// An indicator that an existing confusion duration has been overwritten by a new
|
|
144
|
+
/// confusion duration due to a Thrashing move ending ("hidden" confusion). This is not
|
|
145
|
+
/// immediately observable to an opponent, to compute probabilities from an opponent's
|
|
146
|
+
/// point of view based solely on public information extra work must be done to correct
|
|
147
|
+
/// for the information leak
|
|
148
|
+
overwritten,
|
|
149
|
+
// TODO haze support
|
|
150
|
+
unused1,
|
|
151
|
+
unused2,
|
|
152
|
+
unused3,
|
|
153
|
+
unused4,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
123
157
|
|
|
124
158
|
/// Information about the RNG that was observed during a Generation I battle `update` for a
|
|
125
159
|
/// single player.
|
|
@@ -146,26 +180,24 @@ pub const Action = packed struct(u64) {
|
|
|
146
180
|
duration: u4 = 0,
|
|
147
181
|
|
|
148
182
|
/// TODO
|
|
149
|
-
sleep: Optional(Observation) = .None,
|
|
183
|
+
sleep: Optional(Observation(.sleep)) = .None,
|
|
150
184
|
/// TODO
|
|
151
|
-
confusion: Optional(
|
|
185
|
+
confusion: Optional(Observation(.confusion)) = .None,
|
|
152
186
|
/// TODO
|
|
153
|
-
disable: Optional(Observation) = .None,
|
|
187
|
+
disable: Optional(Observation(.disable)) = .None,
|
|
154
188
|
/// TODO
|
|
155
|
-
attacking: Optional(Observation) = .None,
|
|
189
|
+
attacking: Optional(Observation(.attacking)) = .None,
|
|
156
190
|
/// TODO
|
|
157
|
-
binding: Optional(Observation) = .None,
|
|
158
|
-
|
|
159
|
-
_: u1 = 0,
|
|
191
|
+
binding: Optional(Observation(.binding)) = .None,
|
|
160
192
|
|
|
161
193
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
162
194
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
163
|
-
move_slot:
|
|
195
|
+
move_slot: u3 = 0,
|
|
196
|
+
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
197
|
+
multi_hit: u3 = 0,
|
|
164
198
|
|
|
165
199
|
/// TODO
|
|
166
200
|
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
201
|
|
|
170
202
|
/// If not 0, psywave - 1 should be returned as the damage roll for Rolls.psywave.
|
|
171
203
|
psywave: u8 = 0,
|
|
@@ -175,18 +207,19 @@ pub const Action = packed struct(u64) {
|
|
|
175
207
|
|
|
176
208
|
pub const Field = std.meta.FieldEnum(Action);
|
|
177
209
|
|
|
178
|
-
pub fn format(a: Action,
|
|
179
|
-
_ = .{ f, o };
|
|
210
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
180
211
|
try fmt(a, w, false);
|
|
181
212
|
}
|
|
182
213
|
|
|
183
|
-
|
|
214
|
+
const SYMBOLS = [_][]const u8{ "+", "", "-", "#" };
|
|
215
|
+
|
|
216
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
184
217
|
try writer.writeByte('(');
|
|
185
218
|
var printed = false;
|
|
186
|
-
inline for (@
|
|
219
|
+
inline for (@typeInfo(Action).@"struct".fields) |field| {
|
|
187
220
|
const val = @field(self, field.name);
|
|
188
221
|
switch (@typeInfo(@TypeOf(val))) {
|
|
189
|
-
|
|
222
|
+
.@"enum" => if (val != .None) {
|
|
190
223
|
if (printed) try writer.writeAll(", ");
|
|
191
224
|
if (shape) {
|
|
192
225
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -195,19 +228,14 @@ pub const Action = packed struct(u64) {
|
|
|
195
228
|
if (val == .false) "!" else "",
|
|
196
229
|
field.name,
|
|
197
230
|
});
|
|
198
|
-
} else if (@TypeOf(val)
|
|
199
|
-
@
|
|
200
|
-
{
|
|
201
|
-
try writer.print("{s}{s}", .{
|
|
202
|
-
([_][]const u8{ "+", "", "-", "#" })[@intFromEnum(val) - 1],
|
|
203
|
-
field.name,
|
|
204
|
-
});
|
|
231
|
+
} else if (@TypeOf(val) != Move and @TypeOf(val) != Optional(Player)) {
|
|
232
|
+
try writer.print("{s}{s}", .{ SYMBOLS[@intFromEnum(val) - 1], field.name });
|
|
205
233
|
} else {
|
|
206
234
|
try writer.print("{s}:{s}", .{ field.name, @tagName(val) });
|
|
207
235
|
}
|
|
208
236
|
printed = true;
|
|
209
237
|
},
|
|
210
|
-
|
|
238
|
+
.int => if (val != 0) {
|
|
211
239
|
if (printed) try writer.writeAll(", ");
|
|
212
240
|
if (shape) {
|
|
213
241
|
try writer.print("{s}:?", .{field.name});
|
|
@@ -240,14 +268,8 @@ pub const Durations = extern struct {
|
|
|
240
268
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
241
269
|
}
|
|
242
270
|
|
|
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 });
|
|
271
|
+
pub fn format(self: Durations, writer: *std.Io.Writer) !void {
|
|
272
|
+
try writer.print("[P1 = {f}, P2 = {f}]", .{ self.p1, self.p2 });
|
|
251
273
|
}
|
|
252
274
|
};
|
|
253
275
|
|
|
@@ -263,19 +285,18 @@ pub const Duration = packed struct(u32) {
|
|
|
263
285
|
|
|
264
286
|
_: u1 = 0,
|
|
265
287
|
|
|
288
|
+
pub const Field = enum { attacking, binding, sleep, disable, confusion };
|
|
289
|
+
|
|
266
290
|
pub fn format(
|
|
267
291
|
self: Duration,
|
|
268
|
-
|
|
269
|
-
opts: std.fmt.FormatOptions,
|
|
270
|
-
writer: anytype,
|
|
292
|
+
writer: *std.Io.Writer,
|
|
271
293
|
) !void {
|
|
272
|
-
_ = .{ fmt, opts };
|
|
273
294
|
try writer.writeByte('(');
|
|
274
295
|
var printed = false;
|
|
275
|
-
inline for (@
|
|
296
|
+
inline for (@typeInfo(Duration).@"struct".fields) |field| {
|
|
276
297
|
const val = @field(self, field.name);
|
|
277
298
|
switch (@typeInfo(@TypeOf(val))) {
|
|
278
|
-
|
|
299
|
+
.int => if (val != 0) {
|
|
279
300
|
if (printed) try writer.writeAll(", ");
|
|
280
301
|
if (comptime std.mem.eql(u8, field.name, "sleeps")) {
|
|
281
302
|
try writer.print("{s}:[", .{field.name});
|
|
@@ -536,7 +557,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
536
557
|
self: *Self,
|
|
537
558
|
comptime field: Action.Field,
|
|
538
559
|
player: Player,
|
|
539
|
-
obs: Optional(
|
|
560
|
+
obs: Optional(Observation(.confusion)),
|
|
540
561
|
) void {
|
|
541
562
|
if (!enabled) return;
|
|
542
563
|
|
|
@@ -578,7 +599,7 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
578
599
|
}
|
|
579
600
|
}
|
|
580
601
|
|
|
581
|
-
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation)) Error!void {
|
|
602
|
+
pub fn sleep(self: *Self, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
582
603
|
if (!enabled) return;
|
|
583
604
|
|
|
584
605
|
var a = self.actions.get(player);
|
|
@@ -602,7 +623,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
602
623
|
}
|
|
603
624
|
}
|
|
604
625
|
|
|
605
|
-
pub fn confusion(
|
|
626
|
+
pub fn confusion(
|
|
627
|
+
self: *Self,
|
|
628
|
+
player: Player,
|
|
629
|
+
obs: Optional(Observation(.confusion)),
|
|
630
|
+
) Error!void {
|
|
606
631
|
if (!enabled) return;
|
|
607
632
|
|
|
608
633
|
var a = self.actions.get(player);
|
|
@@ -624,7 +649,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
624
649
|
}
|
|
625
650
|
}
|
|
626
651
|
|
|
627
|
-
pub fn disable(
|
|
652
|
+
pub fn disable(
|
|
653
|
+
self: *Self,
|
|
654
|
+
player: Player,
|
|
655
|
+
obs: Optional(Observation(.disable)),
|
|
656
|
+
) Error!void {
|
|
628
657
|
if (!enabled) return;
|
|
629
658
|
|
|
630
659
|
var a = self.actions.get(player);
|
|
@@ -646,7 +675,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
646
675
|
}
|
|
647
676
|
}
|
|
648
677
|
|
|
649
|
-
pub fn attacking(
|
|
678
|
+
pub fn attacking(
|
|
679
|
+
self: *Self,
|
|
680
|
+
player: Player,
|
|
681
|
+
obs: Optional(Observation(.attacking)),
|
|
682
|
+
) Error!void {
|
|
650
683
|
if (!enabled) return;
|
|
651
684
|
|
|
652
685
|
var a = self.actions.get(player);
|
|
@@ -668,7 +701,11 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
668
701
|
}
|
|
669
702
|
}
|
|
670
703
|
|
|
671
|
-
pub fn binding(
|
|
704
|
+
pub fn binding(
|
|
705
|
+
self: *Self,
|
|
706
|
+
player: Player,
|
|
707
|
+
obs: Optional(Observation(.binding)),
|
|
708
|
+
) Error!void {
|
|
672
709
|
if (!enabled) return;
|
|
673
710
|
|
|
674
711
|
var a = self.actions.get(player);
|
|
@@ -878,13 +915,13 @@ test "Chance.moveSlot" {
|
|
|
878
915
|
|
|
879
916
|
try chance.moveSlot(.P2, 2, &ms, 2);
|
|
880
917
|
try expectProbability(&chance.probability, 1, 2);
|
|
881
|
-
try expectValue(@as(
|
|
918
|
+
try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
|
|
882
919
|
|
|
883
920
|
chance.reset();
|
|
884
921
|
|
|
885
922
|
try chance.moveSlot(.P1, 1, &ms, 0);
|
|
886
923
|
try expectProbability(&chance.probability, 1, 3);
|
|
887
|
-
try expectValue(@as(
|
|
924
|
+
try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
|
|
888
925
|
}
|
|
889
926
|
|
|
890
927
|
test "Chance.multiHit" {
|
|
@@ -1051,7 +1088,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
1051
1088
|
|
|
1052
1089
|
r.reduce();
|
|
1053
1090
|
if (r.p != p or r.q != q) {
|
|
1054
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
1091
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
1055
1092
|
return error.TestExpectedEqual;
|
|
1056
1093
|
}
|
|
1057
1094
|
}
|
|
@@ -1129,28 +1166,44 @@ const Null = struct {
|
|
|
1129
1166
|
self: Null,
|
|
1130
1167
|
comptime field: Action.Field,
|
|
1131
1168
|
player: Player,
|
|
1132
|
-
obs: Optional(
|
|
1169
|
+
obs: Optional(Observation(.confusion)),
|
|
1133
1170
|
) void {
|
|
1134
1171
|
_ = .{ self, field, player, obs };
|
|
1135
1172
|
}
|
|
1136
1173
|
|
|
1137
|
-
pub fn sleep(self: Null, player: Player, obs: Optional(Observation)) Error!void {
|
|
1174
|
+
pub fn sleep(self: Null, player: Player, obs: Optional(Observation(.sleep))) Error!void {
|
|
1138
1175
|
_ = .{ self, player, obs };
|
|
1139
1176
|
}
|
|
1140
1177
|
|
|
1141
|
-
pub fn confusion(
|
|
1178
|
+
pub fn confusion(
|
|
1179
|
+
self: Null,
|
|
1180
|
+
player: Player,
|
|
1181
|
+
obs: Optional(Observation(.confusion)),
|
|
1182
|
+
) Error!void {
|
|
1142
1183
|
_ = .{ self, player, obs };
|
|
1143
1184
|
}
|
|
1144
1185
|
|
|
1145
|
-
pub fn disable(
|
|
1186
|
+
pub fn disable(
|
|
1187
|
+
self: Null,
|
|
1188
|
+
player: Player,
|
|
1189
|
+
obs: Optional(Observation(.disable)),
|
|
1190
|
+
) Error!void {
|
|
1146
1191
|
_ = .{ self, player, obs };
|
|
1147
1192
|
}
|
|
1148
1193
|
|
|
1149
|
-
pub fn attacking(
|
|
1194
|
+
pub fn attacking(
|
|
1195
|
+
self: Null,
|
|
1196
|
+
player: Player,
|
|
1197
|
+
obs: Optional(Observation(.attacking)),
|
|
1198
|
+
) Error!void {
|
|
1150
1199
|
_ = .{ self, player, obs };
|
|
1151
1200
|
}
|
|
1152
1201
|
|
|
1153
|
-
pub fn binding(
|
|
1202
|
+
pub fn binding(
|
|
1203
|
+
self: Null,
|
|
1204
|
+
player: Player,
|
|
1205
|
+
obs: Optional(Observation(.binding)),
|
|
1206
|
+
) Error!void {
|
|
1154
1207
|
_ = .{ self, player, obs };
|
|
1155
1208
|
}
|
|
1156
1209
|
|
|
@@ -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.
|
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -1,32 +1,24 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
2
|
const common = @import("../common/data.zig");
|
|
3
|
+
const data = @import("data.zig");
|
|
5
4
|
const options = @import("../common/options.zig");
|
|
6
5
|
const protocol = @import("../common/protocol.zig");
|
|
7
6
|
const rng = @import("../common/rng.zig");
|
|
7
|
+
const std = @import("std");
|
|
8
8
|
const util = @import("../common/util.zig");
|
|
9
9
|
|
|
10
|
-
const data = @import("data.zig");
|
|
11
|
-
|
|
12
10
|
const assert = std.debug.assert;
|
|
13
|
-
|
|
14
11
|
const Choice = common.Choice;
|
|
15
|
-
|
|
16
|
-
const showdown = options.showdown;
|
|
17
|
-
|
|
18
|
-
const PSRNG = rng.PSRNG;
|
|
19
|
-
|
|
20
12
|
const DVs = data.DVs;
|
|
21
13
|
const Move = data.Move;
|
|
22
14
|
const MoveSlot = data.MoveSlot;
|
|
15
|
+
const PSRNG = rng.PSRNG;
|
|
16
|
+
const showdown = options.showdown;
|
|
23
17
|
const Species = data.Species;
|
|
24
18
|
const Stats = data.Stats;
|
|
25
19
|
|
|
26
20
|
const endian = builtin.cpu.arch.endian();
|
|
27
21
|
|
|
28
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
29
|
-
|
|
30
22
|
/// Configuration options controlling the domain of valid Pokémon that can be generated by the
|
|
31
23
|
/// random generator helpers.
|
|
32
24
|
pub const Options = struct {
|
|
@@ -169,7 +161,7 @@ pub const Pokemon = struct {
|
|
|
169
161
|
var pokemon = data.Pokemon{};
|
|
170
162
|
pokemon.species = p.species;
|
|
171
163
|
const species = Species.get(p.species);
|
|
172
|
-
inline for (@
|
|
164
|
+
inline for (@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields) |field| {
|
|
173
165
|
const hp = comptime std.mem.eql(u8, field.name, "hp");
|
|
174
166
|
const spc =
|
|
175
167
|
comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
|
|
@@ -205,7 +197,7 @@ pub const Pokemon = struct {
|
|
|
205
197
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
206
198
|
var stats: Stats(u16) = .{};
|
|
207
199
|
const dvs = DVs.random(rand);
|
|
208
|
-
inline for (@
|
|
200
|
+
inline for (@typeInfo(@TypeOf(stats)).@"struct".fields) |field| {
|
|
209
201
|
@field(stats, field.name) = Stats(u16).calc(
|
|
210
202
|
field.name,
|
|
211
203
|
@field(species.stats, field.name),
|
|
@@ -218,7 +210,7 @@ pub const Pokemon = struct {
|
|
|
218
210
|
);
|
|
219
211
|
}
|
|
220
212
|
|
|
221
|
-
var ms
|
|
213
|
+
var ms: [4]MoveSlot = @splat(.{});
|
|
222
214
|
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
223
215
|
for (0..n) |i| {
|
|
224
216
|
var m: Move = .None;
|
|
@@ -298,23 +290,23 @@ else
|
|
|
298
290
|
pub fn diff(
|
|
299
291
|
a: *const data.Battle(data.PRNG),
|
|
300
292
|
b: *const data.Battle(data.PRNG),
|
|
301
|
-
w: protocol.
|
|
293
|
+
w: *protocol.Writer,
|
|
302
294
|
) !usize {
|
|
303
|
-
const pos = w.
|
|
295
|
+
const pos = w.pos;
|
|
304
296
|
const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
|
|
305
297
|
assert(size % 2 == 0);
|
|
306
298
|
assert(size / 2 < 255);
|
|
307
299
|
|
|
308
300
|
var i: usize = 0;
|
|
309
301
|
while (i < size) : (i += 2) {
|
|
310
|
-
const val =
|
|
311
|
-
if (val !=
|
|
302
|
+
const val = std.mem.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
|
|
303
|
+
if (val != std.mem.bytesAsValue(u16, std.mem.asBytes(b)[i .. i + 2]).*) {
|
|
312
304
|
try w.writeByte(@intCast(i / 2));
|
|
313
305
|
try w.writeInt(u16, val, endian);
|
|
314
306
|
}
|
|
315
307
|
}
|
|
316
308
|
|
|
317
|
-
return w.
|
|
309
|
+
return w.pos - pos;
|
|
318
310
|
}
|
|
319
311
|
|
|
320
312
|
/// TODO
|