@pkmn/engine 0.1.0-dev.02b7f810
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 +20 -0
- package/README.md +332 -0
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +111 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/ids.json +240 -0
- package/build/pkg/data/layout.json +86 -0
- package/build/pkg/data/protocol.json +173 -0
- package/build/pkg/data.d.ts +91 -0
- package/build/pkg/data.js +150 -0
- package/build/pkg/data.js.map +1 -0
- package/build/pkg/gen1.d.ts +109 -0
- package/build/pkg/gen1.js +668 -0
- package/build/pkg/gen1.js.map +1 -0
- package/build/pkg/index.d.ts +383 -0
- package/build/pkg/index.js +122 -0
- package/build/pkg/index.js.map +1 -0
- package/build/pkg/protocol.d.ts +71 -0
- package/build/pkg/protocol.js +452 -0
- package/build/pkg/protocol.js.map +1 -0
- package/build/test/display.js +382 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +133 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +346 -0
- package/package.json +202 -0
- package/src/bin/install-pkmn-engine +391 -0
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +229 -0
- package/src/lib/binding/wasm.zig +10 -0
- package/src/lib/common/data.zig +129 -0
- package/src/lib/common/debug.zig +64 -0
- package/src/lib/common/options.zig +40 -0
- package/src/lib/common/protocol.zig +1297 -0
- package/src/lib/common/rng.zig +234 -0
- package/src/lib/gen1/README.md +791 -0
- package/src/lib/gen1/data/moves.zig +1854 -0
- package/src/lib/gen1/data/species.zig +1105 -0
- package/src/lib/gen1/data/types.zig +131 -0
- package/src/lib/gen1/data.zig +620 -0
- package/src/lib/gen1/helpers.zig +234 -0
- package/src/lib/gen1/mechanics.zig +2819 -0
- package/src/lib/gen2/README.md +309 -0
- package/src/lib/gen2/data/items.zig +236 -0
- package/src/lib/gen2/data/moves.zig +2784 -0
- package/src/lib/gen2/data/species.zig +1798 -0
- package/src/lib/gen2/data/types.zig +123 -0
- package/src/lib/gen2/data.zig +425 -0
- package/src/lib/gen2/helpers.zig +225 -0
- package/src/lib/gen2/mechanics.zig +53 -0
- package/src/lib/pkmn.zig +54 -0
- package/src/test/display/pkmn.html.tmpl +368 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const assert = std.debug.assert;
|
|
4
|
+
const expectEqual = std.testing.expectEqual;
|
|
5
|
+
|
|
6
|
+
/// Representation of one of the battle's participants.
|
|
7
|
+
pub const Player = enum(u1) {
|
|
8
|
+
P1,
|
|
9
|
+
P2,
|
|
10
|
+
|
|
11
|
+
/// Returns a player's opponent.
|
|
12
|
+
pub inline fn foe(self: Player) Player {
|
|
13
|
+
return @intToEnum(Player, ~@enumToInt(self));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Return's an identifier for the player's Pokémon at the one-indexed `id`.
|
|
17
|
+
pub inline fn ident(self: Player, id: u3) ID {
|
|
18
|
+
assert(id > 0 and id <= 6);
|
|
19
|
+
return .{ .id = id, .player = self };
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
test Player {
|
|
24
|
+
try expectEqual(Player.P2, Player.P1.foe());
|
|
25
|
+
try expectEqual(@as(u8, 0b0001), @bitCast(u8, Player.P1.ident(1)));
|
|
26
|
+
try expectEqual(@as(u8, 0b1101), @bitCast(u8, Player.P2.ident(5)));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// An identifier for a specific Pokémon in battle.
|
|
30
|
+
pub const ID = packed struct {
|
|
31
|
+
/// The one-indexed team slot of the Pokémon
|
|
32
|
+
id: u3 = 0,
|
|
33
|
+
/// The Pokémon's trainer.
|
|
34
|
+
player: Player = .P1,
|
|
35
|
+
_: u4 = 0,
|
|
36
|
+
|
|
37
|
+
comptime {
|
|
38
|
+
assert(@sizeOf(ID) == 1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Converts the identifier into a number.
|
|
42
|
+
pub inline fn int(self: ID) u4 {
|
|
43
|
+
return @intCast(u4, @bitCast(u8, self));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Decodes the identifier from a number.
|
|
47
|
+
pub inline fn from(id: u4) ID {
|
|
48
|
+
return @bitCast(ID, @as(u8, id));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
test ID {
|
|
53
|
+
try expectEqual(@as(u8, 0b0001), @bitCast(u8, ID{ .player = .P1, .id = 1 }));
|
|
54
|
+
try expectEqual(@as(u8, 0b1101), @bitCast(u8, ID{ .player = .P2, .id = 5 }));
|
|
55
|
+
const id = ID{ .player = .P2, .id = 4 };
|
|
56
|
+
try expectEqual(id, ID.from(id.int()));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// A choice made by a player during battle.
|
|
60
|
+
pub const Choice = packed struct {
|
|
61
|
+
/// The choice type.
|
|
62
|
+
type: Choice.Type = .Pass,
|
|
63
|
+
/// The choice data:
|
|
64
|
+
///
|
|
65
|
+
/// - 0 for 'pass'
|
|
66
|
+
/// - 0-4 for 'move'
|
|
67
|
+
/// - 2-6 for 'switch'
|
|
68
|
+
data: u6 = 0,
|
|
69
|
+
|
|
70
|
+
/// All valid choice types.
|
|
71
|
+
pub const Type = enum(u2) {
|
|
72
|
+
Pass,
|
|
73
|
+
Move,
|
|
74
|
+
Switch,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
comptime {
|
|
78
|
+
assert(@sizeOf(Choice) == 1);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
test Choice {
|
|
83
|
+
const p1: Choice = .{ .type = .Move, .data = 4 };
|
|
84
|
+
const p2: Choice = .{ .type = .Switch, .data = 5 };
|
|
85
|
+
try expectEqual(5, p2.data);
|
|
86
|
+
try expectEqual(Choice.Type.Move, p1.type);
|
|
87
|
+
try expectEqual(0b0001_0001, @bitCast(u8, p1));
|
|
88
|
+
try expectEqual(0b0001_0110, @bitCast(u8, p2));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// The result of the battle - all results other than 'None' should be considered terminal.
|
|
92
|
+
pub const Result = packed struct {
|
|
93
|
+
/// The type of result from the perspective of Player 1.
|
|
94
|
+
/// `Error` is not possible when in Pokémon Showdown compatibility mode.
|
|
95
|
+
type: Result.Type = .None,
|
|
96
|
+
/// The choice type of the result for Player 1.
|
|
97
|
+
p1: Choice.Type = .Pass,
|
|
98
|
+
/// The choice type of the result for Player 2.
|
|
99
|
+
p2: Choice.Type = .Pass,
|
|
100
|
+
|
|
101
|
+
/// All valid result types.
|
|
102
|
+
pub const Type = enum(u4) {
|
|
103
|
+
None,
|
|
104
|
+
Win,
|
|
105
|
+
Lose,
|
|
106
|
+
Tie,
|
|
107
|
+
Error, // Desync, EBC, etc.
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/// The Tie result.
|
|
111
|
+
pub const Tie: Result = .{ .type = .Tie };
|
|
112
|
+
/// The Win result.
|
|
113
|
+
pub const Win: Result = .{ .type = .Win };
|
|
114
|
+
/// The Lost result.
|
|
115
|
+
pub const Lose: Result = .{ .type = .Lose };
|
|
116
|
+
/// The Error result.
|
|
117
|
+
pub const Error: Result = .{ .type = .Error };
|
|
118
|
+
/// The default result.
|
|
119
|
+
pub const Default: Result = .{ .p1 = .Move, .p2 = .Move };
|
|
120
|
+
|
|
121
|
+
comptime {
|
|
122
|
+
assert(@sizeOf(Result) == 1);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
test Result {
|
|
127
|
+
try expectEqual(0b0101_0000, @bitCast(u8, Result.Default));
|
|
128
|
+
try expectEqual(0b1000_0000, @bitCast(u8, Result{ .p2 = .Switch }));
|
|
129
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const debug = std.debug;
|
|
4
|
+
const io = std.io;
|
|
5
|
+
|
|
6
|
+
pub fn print(value: anytype) void {
|
|
7
|
+
debug.getStderrMutex().lock();
|
|
8
|
+
defer debug.getStderrMutex().unlock();
|
|
9
|
+
const stderr = io.getStdErr().writer();
|
|
10
|
+
|
|
11
|
+
nosuspend {
|
|
12
|
+
stderr.writeAll("\x1b[41m") catch return;
|
|
13
|
+
switch (@typeInfo(@TypeOf(value))) {
|
|
14
|
+
.Struct => |info| {
|
|
15
|
+
if (info.is_tuple) {
|
|
16
|
+
inline for (info.fields, 0..) |f, i| {
|
|
17
|
+
inspect(@field(value, f.name));
|
|
18
|
+
if (i < info.fields.len - 1) stderr.writeAll(" ") catch return;
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
inspect(value);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
else => inspect(value),
|
|
25
|
+
}
|
|
26
|
+
stderr.writeAll("\x1b[K\x1b[0m\n") catch return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn inspect(value: anytype) void {
|
|
31
|
+
const stderr = io.getStdErr().writer();
|
|
32
|
+
|
|
33
|
+
nosuspend {
|
|
34
|
+
const err = "Unable to format type '" ++ @typeName(@TypeOf(value)) ++ "'";
|
|
35
|
+
switch (@typeInfo(@TypeOf(value))) {
|
|
36
|
+
.Array => |info| {
|
|
37
|
+
if (info.child == u8) return stderr.print("{s}", .{value}) catch return;
|
|
38
|
+
@compileError(err);
|
|
39
|
+
},
|
|
40
|
+
.Pointer => |ptr_info| switch (ptr_info.size) {
|
|
41
|
+
.One => switch (@typeInfo(ptr_info.child)) {
|
|
42
|
+
.Array => |info| {
|
|
43
|
+
if (info.child == u8) return stderr.print("{s}", .{value}) catch return;
|
|
44
|
+
@compileError(err);
|
|
45
|
+
},
|
|
46
|
+
.Enum, .Union, .Struct => return inspect(value.*),
|
|
47
|
+
else => @compileError(err),
|
|
48
|
+
},
|
|
49
|
+
.Many, .C => {
|
|
50
|
+
if (ptr_info.sentinel) |_| return inspect(std.mem.span(value));
|
|
51
|
+
if (ptr_info.child == u8) {
|
|
52
|
+
return stderr.print("{s}", .{std.mem.span(value)}) catch return;
|
|
53
|
+
}
|
|
54
|
+
@compileError(err);
|
|
55
|
+
},
|
|
56
|
+
.Slice => {
|
|
57
|
+
if (ptr_info.child == u8) return stderr.print("{s}", .{value}) catch return;
|
|
58
|
+
@compileError(err);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
else => stderr.print("{}", .{value}) catch return,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const build_options = @import("build_options");
|
|
2
|
+
const root = @import("root");
|
|
3
|
+
|
|
4
|
+
pub const Options = struct {
|
|
5
|
+
showdown: ?bool = null,
|
|
6
|
+
trace: ?bool = null,
|
|
7
|
+
advance: ?bool = null,
|
|
8
|
+
ebc: ?bool = null,
|
|
9
|
+
internal: ?bool = null,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/// Whether or not Pokémon Showdown compatibility mode is enabled.
|
|
13
|
+
pub const showdown = get("showdown", false);
|
|
14
|
+
/// Whether or not protocol trace logging is enabled.
|
|
15
|
+
pub const trace = get("trace", false);
|
|
16
|
+
|
|
17
|
+
pub const advance = get("advance", true);
|
|
18
|
+
pub const ebc = get("ebc", true);
|
|
19
|
+
pub const internal = get("internal", false);
|
|
20
|
+
|
|
21
|
+
fn get(comptime name: []const u8, default: bool) bool {
|
|
22
|
+
var build_enable: ?bool = null;
|
|
23
|
+
var root_enable: ?bool = null;
|
|
24
|
+
|
|
25
|
+
if (@hasDecl(root, "pkmn_options")) {
|
|
26
|
+
root_enable = @field(@as(Options, root.pkmn_options), name);
|
|
27
|
+
}
|
|
28
|
+
if (@hasDecl(build_options, name)) {
|
|
29
|
+
build_enable = @as(?bool, @field(build_options, name));
|
|
30
|
+
}
|
|
31
|
+
if (build_enable != null and root_enable != null) {
|
|
32
|
+
if (build_enable.? != root_enable.?) {
|
|
33
|
+
const r = name ++ " (" ++ (if (root_enable.?) "false" else "true") ++ ")";
|
|
34
|
+
const b = name ++ " (" ++ (if (build_enable.?) "false" else "true") ++ ")";
|
|
35
|
+
@compileError("root decl pkmn_options." ++ r ++ " != build option " ++ b ++ ".");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return root_enable orelse (build_enable orelse default);
|
|
40
|
+
}
|