@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.
Files changed (52) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +332 -0
  3. package/build/pkg/addon.d.ts +7 -0
  4. package/build/pkg/addon.js +111 -0
  5. package/build/pkg/addon.js.map +1 -0
  6. package/build/pkg/data/ids.json +240 -0
  7. package/build/pkg/data/layout.json +86 -0
  8. package/build/pkg/data/protocol.json +173 -0
  9. package/build/pkg/data.d.ts +91 -0
  10. package/build/pkg/data.js +150 -0
  11. package/build/pkg/data.js.map +1 -0
  12. package/build/pkg/gen1.d.ts +109 -0
  13. package/build/pkg/gen1.js +668 -0
  14. package/build/pkg/gen1.js.map +1 -0
  15. package/build/pkg/index.d.ts +383 -0
  16. package/build/pkg/index.js +122 -0
  17. package/build/pkg/index.js.map +1 -0
  18. package/build/pkg/protocol.d.ts +71 -0
  19. package/build/pkg/protocol.js +452 -0
  20. package/build/pkg/protocol.js.map +1 -0
  21. package/build/test/display.js +382 -0
  22. package/build/test/display.js.map +1 -0
  23. package/build/tools/debug.js +133 -0
  24. package/build/tools/debug.js.map +1 -0
  25. package/build.zig +346 -0
  26. package/package.json +202 -0
  27. package/src/bin/install-pkmn-engine +391 -0
  28. package/src/bin/pkmn-debug +62 -0
  29. package/src/lib/binding/node.zig +229 -0
  30. package/src/lib/binding/wasm.zig +10 -0
  31. package/src/lib/common/data.zig +129 -0
  32. package/src/lib/common/debug.zig +64 -0
  33. package/src/lib/common/options.zig +40 -0
  34. package/src/lib/common/protocol.zig +1297 -0
  35. package/src/lib/common/rng.zig +234 -0
  36. package/src/lib/gen1/README.md +791 -0
  37. package/src/lib/gen1/data/moves.zig +1854 -0
  38. package/src/lib/gen1/data/species.zig +1105 -0
  39. package/src/lib/gen1/data/types.zig +131 -0
  40. package/src/lib/gen1/data.zig +620 -0
  41. package/src/lib/gen1/helpers.zig +234 -0
  42. package/src/lib/gen1/mechanics.zig +2819 -0
  43. package/src/lib/gen2/README.md +309 -0
  44. package/src/lib/gen2/data/items.zig +236 -0
  45. package/src/lib/gen2/data/moves.zig +2784 -0
  46. package/src/lib/gen2/data/species.zig +1798 -0
  47. package/src/lib/gen2/data/types.zig +123 -0
  48. package/src/lib/gen2/data.zig +425 -0
  49. package/src/lib/gen2/helpers.zig +225 -0
  50. package/src/lib/gen2/mechanics.zig +53 -0
  51. package/src/lib/pkmn.zig +54 -0
  52. package/src/test/display/pkmn.html.tmpl +368 -0
@@ -0,0 +1,234 @@
1
+ const std = @import("std");
2
+
3
+ const common = @import("../common/data.zig");
4
+ const options = @import("../common/options.zig");
5
+ const protocol = @import("../common/protocol.zig");
6
+ const rng = @import("../common/rng.zig");
7
+
8
+ const data = @import("data.zig");
9
+
10
+ const assert = std.debug.assert;
11
+
12
+ const expectEqual = std.testing.expectEqual;
13
+
14
+ const Choice = common.Choice;
15
+
16
+ const showdown = options.showdown;
17
+
18
+ const PSRNG = rng.PSRNG;
19
+
20
+ const DVs = data.DVs;
21
+ const Move = data.Move;
22
+ const MoveSlot = data.MoveSlot;
23
+ const Species = data.Species;
24
+ const Stats = data.Stats;
25
+ const Status = data.Status;
26
+
27
+ /// Options with dictate
28
+ pub const Options = struct {
29
+ cleric: bool = showdown,
30
+ block: bool = showdown,
31
+ };
32
+
33
+ pub const Battle = struct {
34
+ pub fn init(
35
+ seed: u64,
36
+ p1: []const Pokemon,
37
+ p2: []const Pokemon,
38
+ ) data.Battle(data.PRNG) {
39
+ var rand = PSRNG.init(seed);
40
+ return .{
41
+ .rng = prng(&rand),
42
+ .sides = .{ Side.init(p1), Side.init(p2) },
43
+ };
44
+ }
45
+
46
+ pub fn fixed(
47
+ comptime rolls: anytype,
48
+ p1: []const Pokemon,
49
+ p2: []const Pokemon,
50
+ ) data.Battle(rng.FixedRNG(1, rolls.len)) {
51
+ return .{
52
+ .rng = .{ .rolls = rolls },
53
+ .sides = .{ Side.init(p1), Side.init(p2) },
54
+ };
55
+ }
56
+
57
+ pub fn random(rand: *PSRNG, opt: Options) data.Battle(data.PRNG) {
58
+ var p1: u4 = 0;
59
+ var p2: u4 = 0;
60
+ var battle: data.Battle(data.PRNG) = .{
61
+ .rng = prng(rand),
62
+ .turn = 0,
63
+ .last_damage = 0,
64
+ .sides = .{ Side.random(rand, opt), Side.random(rand, opt) },
65
+ };
66
+ battle.last_selected_indexes.p1 = p1;
67
+ battle.last_selected_indexes.p2 = p2;
68
+ return battle;
69
+ }
70
+ };
71
+
72
+ pub fn prng(rand: *PSRNG) data.PRNG {
73
+ // GLITCH: initial bytes in seed can only range from 0-252, not 0-255
74
+ const max: u8 = 253;
75
+ return .{
76
+ .src = .{
77
+ .seed = if (showdown)
78
+ rand.newSeed()
79
+ else
80
+ .{
81
+ rand.range(u8, 0, max), rand.range(u8, 0, max),
82
+ rand.range(u8, 0, max), rand.range(u8, 0, max),
83
+ rand.range(u8, 0, max), rand.range(u8, 0, max),
84
+ rand.range(u8, 0, max), rand.range(u8, 0, max),
85
+ rand.range(u8, 0, max), rand.range(u8, 0, max),
86
+ },
87
+ },
88
+ };
89
+ }
90
+
91
+ pub const Side = struct {
92
+ pub fn init(ps: []const Pokemon) data.Side {
93
+ assert(ps.len > 0 and ps.len <= 6);
94
+ var side = data.Side{};
95
+
96
+ for (0..ps.len) |i| {
97
+ side.pokemon[i] = Pokemon.init(ps[i]);
98
+ side.order[i] = @intCast(u4, i) + 1;
99
+ }
100
+ return side;
101
+ }
102
+
103
+ pub fn random(rand: *PSRNG, opt: Options) data.Side {
104
+ const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 5 + 1) else 6;
105
+ var side = data.Side{};
106
+
107
+ for (0..n) |i| {
108
+ side.pokemon[i] = Pokemon.random(rand, opt);
109
+ side.order[i] = @intCast(u4, i) + 1;
110
+ }
111
+
112
+ return side;
113
+ }
114
+ };
115
+
116
+ pub const EXP = 0xFFFF;
117
+
118
+ pub const Pokemon = struct {
119
+ species: Species,
120
+ moves: []const Move,
121
+ hp: ?u16 = null,
122
+ status: u8 = 0,
123
+ level: u8 = 100,
124
+ dvs: DVs = .{},
125
+ stats: Stats(u16) = .{ .hp = EXP, .atk = EXP, .def = EXP, .spe = EXP, .spc = EXP },
126
+
127
+ pub fn init(p: Pokemon) data.Pokemon {
128
+ var pokemon = data.Pokemon{};
129
+ pokemon.species = p.species;
130
+ const species = Species.get(p.species);
131
+ inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
132
+ const hp = comptime std.mem.eql(u8, field.name, "hp");
133
+ const spc =
134
+ comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
135
+ @field(pokemon.stats, field.name) = Stats(u16).calc(
136
+ field.name,
137
+ @field(species.stats, field.name),
138
+ if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field.name),
139
+ @field(p.stats, field.name),
140
+ p.level,
141
+ );
142
+ }
143
+ assert(p.moves.len > 0 and p.moves.len <= 4);
144
+ for (p.moves, 0..) |m, j| {
145
+ pokemon.moves[j].id = m;
146
+ // NB: PP can be at most 61 legally (though can overflow to 63)
147
+ pokemon.moves[j].pp = @intCast(u8, @min(Move.pp(m) / 5 * 8, 61));
148
+ }
149
+ if (p.hp) |hp| {
150
+ pokemon.hp = hp;
151
+ } else {
152
+ pokemon.hp = pokemon.stats.hp;
153
+ }
154
+ pokemon.status = p.status;
155
+ pokemon.types = species.types;
156
+ pokemon.level = p.level;
157
+ return pokemon;
158
+ }
159
+
160
+ pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
161
+ const s = @intToEnum(Species, rand.range(u8, 1, Species.size + 1));
162
+ const species = Species.get(s);
163
+ const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
164
+ var stats: Stats(u16) = .{};
165
+ const dvs = DVs.random(rand);
166
+ inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
167
+ @field(stats, field.name) = Stats(u16).calc(
168
+ field.name,
169
+ @field(species.stats, field.name),
170
+ if (comptime std.mem.eql(u8, field.name, "hp"))
171
+ dvs.hp()
172
+ else
173
+ @field(dvs, field.name),
174
+ if (rand.chance(u8, 1, 20)) rand.range(u16, 0, EXP + 1) else EXP,
175
+ lvl,
176
+ );
177
+ }
178
+
179
+ var ms = [_]MoveSlot{.{}} ** 4;
180
+ const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
181
+ for (0..n) |i| {
182
+ var m: Move = .None;
183
+ sample: while (true) {
184
+ m = @intToEnum(Move, rand.range(u8, 1, Move.size - 1 + 1));
185
+ if (opt.block and blocked(m)) continue :sample;
186
+ for (0..i) |j| if (ms[j].id == m) continue :sample;
187
+ break;
188
+ }
189
+ const pp_ups =
190
+ if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
191
+ // NB: PP can be at most 61 legally (though can overflow to 63)
192
+ const max_pp = @intCast(u8, Move.pp(m) + @as(u8, pp_ups) * @min(Move.pp(m) / 5, 7));
193
+ ms[i] = .{
194
+ .id = m,
195
+ .pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
196
+ };
197
+ }
198
+
199
+ return .{
200
+ .species = s,
201
+ .types = species.types,
202
+ .level = lvl,
203
+ .stats = stats,
204
+ .hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
205
+ .status = if (!opt.cleric and rand.chance(u8, 1, 6 + 1))
206
+ 0 | (@as(u8, 1) << rand.range(u3, 1, 6 + 1))
207
+ else
208
+ 0,
209
+ .moves = ms,
210
+ };
211
+ }
212
+ };
213
+
214
+ // cat src/test/showdown/blocklist.json | jq '."1".moves'
215
+ fn blocked(m: Move) bool {
216
+ if (Move.get(m).effect == .Binding) return true;
217
+ return switch (m) {
218
+ .Counter,
219
+ .Mimic,
220
+ .Metronome,
221
+ .MirrorMove,
222
+ .Transform,
223
+ => true,
224
+ else => false,
225
+ };
226
+ }
227
+
228
+ pub fn move(slot: u4) Choice {
229
+ return .{ .type = .Move, .data = slot };
230
+ }
231
+
232
+ pub fn swtch(slot: u4) Choice {
233
+ return .{ .type = .Switch, .data = slot };
234
+ }