@pkmn/engine 0.1.0-dev.63cef311 → 0.1.0-dev.6be9a775

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 (64) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +139 -73
  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 +12 -12
  7. package/build/pkg/data/layout.json +27 -5
  8. package/build/pkg/data/protocol.json +69 -12
  9. package/build/pkg/data.d.ts +5 -6
  10. package/build/pkg/data.js +3 -5
  11. package/build/pkg/data.js.map +1 -1
  12. package/build/pkg/gen1.d.ts +5 -4
  13. package/build/pkg/gen1.js +49 -33
  14. package/build/pkg/gen1.js.map +1 -1
  15. package/build/pkg/index.d.ts +18 -14
  16. package/build/pkg/index.js.map +1 -1
  17. package/build/pkg/protocol.d.ts +4 -2
  18. package/build/pkg/protocol.js +65 -44
  19. package/build/pkg/protocol.js.map +1 -1
  20. package/build/test/display.js +405 -0
  21. package/build/test/display.js.map +1 -0
  22. package/build/tools/debug.js +134 -0
  23. package/build/tools/debug.js.map +1 -0
  24. package/build.zig +299 -154
  25. package/package.json +89 -46
  26. package/src/bin/install-pkmn-engine +83 -41
  27. package/src/bin/pkmn-debug +62 -0
  28. package/src/lib/common/array.zig +77 -0
  29. package/src/lib/common/battle.zig +17 -0
  30. package/src/lib/common/data.zig +19 -31
  31. package/src/lib/common/debug.zig +23 -13
  32. package/src/lib/common/js.zig +120 -0
  33. package/src/lib/common/optional.zig +71 -0
  34. package/src/lib/common/options.zig +14 -3
  35. package/src/lib/common/protocol.zig +947 -403
  36. package/src/lib/common/rational.zig +447 -0
  37. package/src/lib/common/rng.zig +31 -22
  38. package/src/lib/common/util.zig +61 -0
  39. package/src/lib/gen1/calc.zig +806 -0
  40. package/src/lib/gen1/chance.zig +1167 -0
  41. package/src/lib/gen1/data/moves.zig +202 -190
  42. package/src/lib/gen1/data/species.zig +5 -5
  43. package/src/lib/gen1/data/types.zig +10 -10
  44. package/src/lib/gen1/data.zig +98 -83
  45. package/src/lib/gen1/helpers.zig +143 -42
  46. package/src/lib/gen1/mechanics.zig +1341 -864
  47. package/src/lib/gen2/calc.zig +161 -0
  48. package/src/lib/gen2/chance.zig +905 -0
  49. package/src/lib/gen2/data/items.zig +14 -20
  50. package/src/lib/gen2/data/moves.zig +664 -570
  51. package/src/lib/gen2/data/species.zig +2 -2
  52. package/src/lib/gen2/data/types.zig +137 -34
  53. package/src/lib/gen2/data.zig +129 -90
  54. package/src/lib/gen2/generated.zig +806 -0
  55. package/src/lib/gen2/helpers.zig +54 -28
  56. package/src/lib/gen2/mechanics.zig +3567 -12
  57. package/src/lib/node.zig +131 -0
  58. package/src/lib/pkmn.zig +88 -12
  59. package/src/lib/wasm.zig +13 -0
  60. package/src/test/display/pkmn.html.tmpl +368 -0
  61. package/src/lib/binding/node.zig +0 -229
  62. package/src/lib/binding/wasm.zig +0 -10
  63. package/src/lib/gen1/README.md +0 -778
  64. package/src/lib/gen2/README.md +0 -309
@@ -27,6 +27,8 @@ const Species = data.Species;
27
27
  const Stats = data.Stats;
28
28
  const Status = data.Status;
29
29
 
30
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
31
+
30
32
  pub const Options = struct {
31
33
  cleric: bool = showdown,
32
34
  };
@@ -48,7 +50,7 @@ pub const Battle = struct {
48
50
  comptime rolls: anytype,
49
51
  p1: []const Pokemon,
50
52
  p2: []const Pokemon,
51
- ) data.Battle(rng.FixedRNG(1, rolls.len)) {
53
+ ) data.Battle(rng.FixedRNG(2, rolls.len)) {
52
54
  return .{
53
55
  .rng = .{ .rolls = rolls },
54
56
  .sides = .{ Side.init(p1), Side.init(p2) },
@@ -65,19 +67,47 @@ pub const Battle = struct {
65
67
  };
66
68
 
67
69
  pub fn prng(rand: *PSRNG) data.PRNG {
68
- // FIXME: what is max range in gen2?
69
70
  return .{
70
71
  .src = .{
71
72
  .seed = if (showdown)
72
73
  rand.newSeed()
73
- else
74
- .{
75
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
76
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
77
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
78
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
79
- rand.range(u8, 0, 256), rand.range(u8, 0, 256),
80
- },
74
+ else seed: {
75
+ // GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
76
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
77
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
78
+
79
+ // Bytes in seed can only range from 0-252, not 0-255
80
+ const max: u8 = 253;
81
+ // Instructions take multiples of 4 cycles to execute
82
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
83
+ var add: u8 = rand.range(u8, 0, 256);
84
+ var sub: u8 = rand.range(u8, 0, 256);
85
+ var carry: u8 = 0;
86
+ var div: u8 = 0;
87
+
88
+ var i: u8 = 0;
89
+ while (i < seed.len) {
90
+ div = @intCast(timer >> 8);
91
+ // This is effectively @addWithOverflow but with three operands
92
+ const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
93
+ add = @intCast(result & 255);
94
+ carry = @intFromBool(result > 255);
95
+ timer +%= 44;
96
+ div = @intCast(timer >> 8);
97
+ sub -%= div +% carry;
98
+
99
+ if (sub < max) {
100
+ seed[i] = sub;
101
+ i += 1;
102
+ timer +%= 152;
103
+ carry = 1;
104
+ } else {
105
+ timer +%= 132;
106
+ carry = 0;
107
+ }
108
+ }
109
+ break :seed seed;
110
+ },
81
111
  },
82
112
  };
83
113
  }
@@ -89,7 +119,7 @@ pub const Side = struct {
89
119
 
90
120
  for (0..ps.len) |i| {
91
121
  side.pokemon[i] = Pokemon.init(ps[i]);
92
- // FIXME side.order[i] = i + 1;
122
+ side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
93
123
  }
94
124
  return side;
95
125
  }
@@ -100,7 +130,7 @@ pub const Side = struct {
100
130
 
101
131
  for (0..n) |i| {
102
132
  side.pokemon[i] = Pokemon.random(rand, opt);
103
- side.pokemon[i].position = i + 1;
133
+ side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
104
134
  }
105
135
 
106
136
  return side;
@@ -123,7 +153,7 @@ pub const Pokemon = struct {
123
153
  var pokemon = data.Pokemon{};
124
154
  pokemon.species = p.species;
125
155
  const species = Species.get(p.species);
126
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
156
+ inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
127
157
  const hp = comptime std.mem.eql(u8, field.name, "hp");
128
158
  const spc =
129
159
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -137,12 +167,9 @@ pub const Pokemon = struct {
137
167
  }
138
168
  assert(p.moves.len > 0 and p.moves.len <= 4);
139
169
  for (p.moves, 0..) |m, j| {
140
- pokemon.moves[j] = .{
141
- .id = m,
142
- .pp_ups = 3,
143
- // NB: PP can be at most 61 legally (though can overflow to 63)
144
- .pp = @intCast(u8, @min(Move.pp(m) / 5 * 8, 61)),
145
- };
170
+ pokemon.moves[j].id = m;
171
+ // NB: PP can be at most 61 legally (though can overflow to 63)
172
+ pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
146
173
  }
147
174
  pokemon.item = p.item;
148
175
  if (p.hp) |hp| {
@@ -153,21 +180,21 @@ pub const Pokemon = struct {
153
180
  pokemon.status = p.status;
154
181
  pokemon.types = species.types;
155
182
  pokemon.level = p.level;
156
- pokemon.dvs = DVs.from(p.dvs);
183
+ pokemon.dvs = DVs.from(p.species, p.dvs);
157
184
  return pokemon;
158
185
  }
159
186
 
160
187
  pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
161
- const s = @intToEnum(Species, rand.range(u8, 1, Species.size + 1));
188
+ const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
162
189
  const species = Species.get(s);
163
190
  const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
164
191
  // Pokémon Showdown does not support most items without in-battle held item effects
165
- const max_item = @enumToInt(if (showdown) Item.FlowerMail else Item.TM50);
192
+ const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
166
193
  const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
167
194
 
168
195
  var stats: Stats(u16) = .{};
169
196
  const dvs = gen1.DVs.random(rand);
170
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
197
+ inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
171
198
  const hp = std.mem.eql(u8, field.name, "hp");
172
199
  const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
173
200
  @field(stats, field.name) = Stats(u16).calc(
@@ -184,24 +211,23 @@ pub const Pokemon = struct {
184
211
  for (0..n) |i| {
185
212
  var m: Move = .None;
186
213
  sample: while (true) {
187
- m = @intToEnum(Move, rand.range(u8, 1, 164 + 1));
214
+ m = @enumFromInt(rand.range(u8, 1, 164 + 1));
188
215
  for (0..i) |j| if (ms[j].id == m) continue :sample;
189
216
  break;
190
217
  }
191
- const pp_ups =
218
+ const pp_ups: u8 =
192
219
  if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
193
220
  // NB: PP can be at most 61 legally (though can overflow to 63)
194
- const max_pp = @intCast(u8, Move.pp(m) + @as(u8, pp_ups) * @min(Move.pp(m) / 5, 7));
221
+ const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
195
222
  ms[i] = .{
196
223
  .id = m,
197
- .pp_ups = pp_ups,
198
224
  .pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
199
225
  };
200
226
  }
201
227
  return .{
202
228
  .species = s,
203
229
  .types = species.types,
204
- .dvs = DVs.from(dvs),
230
+ .dvs = DVs.from(s, dvs),
205
231
  .level = lvl,
206
232
  .stats = stats,
207
233
  .hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),