@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.d73cd76c

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