@pkmn/engine 0.1.0-dev.eaae4aa6 → 0.1.0-dev.f3aab252

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 (41) hide show
  1. package/LICENSE +0 -2
  2. package/README.md +15 -11
  3. package/build/pkg/addon.js +6 -7
  4. package/build/pkg/addon.js.map +1 -1
  5. package/build/pkg/data.d.ts +2 -3
  6. package/build/pkg/index.d.ts +1 -1
  7. package/build/pkg/index.js.map +1 -1
  8. package/build/pkg/protocol.js +4 -4
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build/test/display.js +8 -3
  11. package/build/test/display.js.map +1 -1
  12. package/build/tools/debug.js +2 -3
  13. package/build/tools/debug.js.map +1 -1
  14. package/build.zig +187 -72
  15. package/package.json +18 -16
  16. package/src/bin/install-pkmn-engine +17 -7
  17. package/src/lib/common/array.zig +77 -0
  18. package/src/lib/common/data.zig +8 -8
  19. package/src/lib/common/debug.zig +2 -2
  20. package/src/lib/common/js.zig +4 -2
  21. package/src/lib/common/optional.zig +13 -9
  22. package/src/lib/common/options.zig +2 -0
  23. package/src/lib/common/protocol.zig +51 -44
  24. package/src/lib/common/rational.zig +114 -24
  25. package/src/lib/common/rng.zig +10 -10
  26. package/src/lib/common/util.zig +50 -1
  27. package/src/lib/gen1/calc.zig +506 -128
  28. package/src/lib/gen1/chance.zig +383 -276
  29. package/src/lib/gen1/data/species.zig +1 -1
  30. package/src/lib/gen1/data.zig +31 -30
  31. package/src/lib/gen1/helpers.zig +95 -538
  32. package/src/lib/gen1/mechanics.zig +265 -162
  33. package/src/lib/gen2/calc.zig +34 -16
  34. package/src/lib/gen2/chance.zig +90 -502
  35. package/src/lib/gen2/data.zig +15 -14
  36. package/src/lib/gen2/helpers.zig +41 -13
  37. package/src/lib/gen2/mechanics.zig +158 -103
  38. package/src/lib/pkmn.zig +10 -16
  39. package/src/test/display/pkmn.html.tmpl +5 -5
  40. package/src/lib/gen1/README.md +0 -894
  41. package/src/lib/gen2/README.md +0 -329
@@ -45,6 +45,7 @@ const Player = data.Player;
45
45
  const Result = data.Result;
46
46
 
47
47
  const PointerType = util.PointerType;
48
+ const isPointerTo = util.isPointerTo;
48
49
 
49
50
  const showdown = pkmn.options.showdown;
50
51
 
@@ -63,12 +64,12 @@ pub fn Battle(comptime RNG: anytype) type {
63
64
  _: [padding]u8 = .{0} ** padding,
64
65
 
65
66
  pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
66
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
67
+ assert(isPointerTo(self, Self));
67
68
  return &self.sides[@intFromEnum(player)];
68
69
  }
69
70
 
70
71
  pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
72
+ assert(isPointerTo(self, Self));
72
73
  return &self.sides[@intFromEnum(player.foe())];
73
74
  }
74
75
 
@@ -134,7 +135,7 @@ pub const Side = extern struct {
134
135
  }
135
136
 
136
137
  pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
137
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
138
+ assert(isPointerTo(self, Side));
138
139
  assert(slot > 0 and slot <= 6);
139
140
  const id = self.pokemon[slot - 1].position;
140
141
  assert(id > 0 and id <= 6);
@@ -142,7 +143,7 @@ pub const Side = extern struct {
142
143
  }
143
144
 
144
145
  pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
145
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
146
+ assert(isPointerTo(self, Side));
146
147
  return self.get(1);
147
148
  }
148
149
 
@@ -176,7 +177,7 @@ pub const ActivePokemon = extern struct {
176
177
  }
177
178
 
178
179
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
179
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
180
+ assert(isPointerTo(self, Pokemon));
180
181
  assert(mslot > 0 and mslot <= 4);
181
182
  assert(self.moves[mslot - 1].id != .None);
182
183
  return &self.moves[mslot - 1];
@@ -201,7 +202,7 @@ pub const Pokemon = extern struct {
201
202
  }
202
203
 
203
204
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
204
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
205
+ assert(isPointerTo(self, Pokemon));
205
206
  assert(mslot > 0 and mslot <= 4);
206
207
  assert(self.moves[mslot - 1].id != .None);
207
208
  return &self.moves[mslot - 1];
@@ -248,12 +249,12 @@ test DVs {
248
249
  var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
249
250
  try expectEqual(Gender.Unknown, dvs.gender);
250
251
  try expectEqual(Type.Ice, dvs.type);
251
- try expectEqual(70, dvs.power());
252
+ try expectEqual(@as(u8, 70), dvs.power());
252
253
 
253
254
  dvs = DVs.from(.Pikachu, .{ .spc = 14 });
254
255
  try expectEqual(Gender.Male, dvs.gender);
255
256
  try expectEqual(Type.Dark, dvs.type);
256
- try expectEqual(69, dvs.power());
257
+ try expectEqual(@as(u8, 69), dvs.power());
257
258
 
258
259
  dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
259
260
  try expectEqual(Gender.Female, dvs.gender);
@@ -380,9 +381,9 @@ test Stats {
380
381
  try expectEqual(0, stats.def);
381
382
 
382
383
  var base = Species.get(.Gyarados).stats;
383
- try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
384
+ try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
384
385
  base = Species.get(.Pidgeot).stats;
385
- try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
386
+ try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
386
387
  }
387
388
 
388
389
  pub const Boosts = packed struct(u32) {
@@ -428,8 +429,8 @@ test Move {
428
429
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
429
430
  try expectEqual(rng.Gen12.percent(50), move.accuracy);
430
431
  try expectEqual(rng.Gen12.percent(100), move.chance);
431
- try expectEqual(5, Move.pp(.DynamicPunch));
432
- try expectEqual(-1, Move.get(.Counter).priority);
432
+ try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
433
+ try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
433
434
  try expect(!move.effect.isHighCritical());
434
435
  try expect(Move.get(.KarateChop).effect.isHighCritical());
435
436
  try expect(Move.get(.RazorWind).effect.isHighCritical());
@@ -439,7 +440,7 @@ pub const Species = species.Species;
439
440
 
440
441
  test Species {
441
442
  try expectEqual(152, @intFromEnum(Species.Chikorita));
442
- try expectEqual(100, Species.get(.Celebi).stats.spd);
443
+ try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
443
444
  }
444
445
 
445
446
  pub const Type = types.Type;
@@ -452,7 +453,7 @@ test Types {
452
453
  try expect(!Type.Steel.special());
453
454
  try expect(Type.Dark.special());
454
455
 
455
- try expectEqual(3, Type.Water.precedence());
456
+ try expectEqual(@as(u8, 3), Type.Water.precedence());
456
457
  try expect(Type.Bug.precedence() > Type.Poison.precedence());
457
458
 
458
459
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
@@ -2,7 +2,6 @@ const std = @import("std");
2
2
 
3
3
  const common = @import("../common/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
6
 
8
7
  const gen1 = @import("../gen1/data.zig");
@@ -11,8 +10,6 @@ const data = @import("data.zig");
11
10
 
12
11
  const assert = std.debug.assert;
13
12
 
14
- const expectEqual = std.testing.expectEqual;
15
-
16
13
  const Choice = common.Choice;
17
14
 
18
15
  const showdown = options.showdown;
@@ -25,7 +22,8 @@ const Move = data.Move;
25
22
  const MoveSlot = data.MoveSlot;
26
23
  const Species = data.Species;
27
24
  const Stats = data.Stats;
28
- const Status = data.Status;
25
+
26
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
29
27
 
30
28
  pub const Options = struct {
31
29
  cleric: bool = showdown,
@@ -65,17 +63,47 @@ pub const Battle = struct {
65
63
  };
66
64
 
67
65
  pub fn prng(rand: *PSRNG) data.PRNG {
68
- // FIXME: what is max range in gen2?
69
66
  return .{
70
67
  .src = .{
71
68
  .seed = if (showdown)
72
69
  rand.newSeed()
73
- else
74
- .{
75
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
76
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
77
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
78
- },
70
+ else seed: {
71
+ // GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
72
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
73
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
74
+
75
+ // Bytes in seed can only range from 0-252, not 0-255
76
+ const max: u8 = 253;
77
+ // Instructions take multiples of 4 cycles to execute
78
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
79
+ var add: u8 = rand.range(u8, 0, 256);
80
+ var sub: u8 = rand.range(u8, 0, 256);
81
+ var carry: u8 = 0;
82
+ var div: u8 = 0;
83
+
84
+ var i: u8 = 0;
85
+ while (i < seed.len) {
86
+ div = @intCast(timer >> 8);
87
+ // This is effectively @addWithOverflow but with three operands
88
+ const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
89
+ add = @intCast(result & 255);
90
+ carry = @intFromBool(result > 255);
91
+ timer +%= 44;
92
+ div = @intCast(timer >> 8);
93
+ sub -%= div +% carry;
94
+
95
+ if (sub < max) {
96
+ seed[i] = sub;
97
+ i += 1;
98
+ timer +%= 152;
99
+ carry = 1;
100
+ } else {
101
+ timer +%= 132;
102
+ carry = 0;
103
+ }
104
+ }
105
+ break :seed seed;
106
+ },
79
107
  },
80
108
  };
81
109
  }
@@ -121,7 +149,7 @@ pub const Pokemon = struct {
121
149
  var pokemon = data.Pokemon{};
122
150
  pokemon.species = p.species;
123
151
  const species = Species.get(p.species);
124
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
152
+ inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
125
153
  const hp = comptime std.mem.eql(u8, field.name, "hp");
126
154
  const spc =
127
155
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -162,7 +190,7 @@ pub const Pokemon = struct {
162
190
 
163
191
  var stats: Stats(u16) = .{};
164
192
  const dvs = gen1.DVs.random(rand);
165
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
193
+ inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
166
194
  const hp = std.mem.eql(u8, field.name, "hp");
167
195
  const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
168
196
  @field(stats, field.name) = Stats(u16).calc(