@pkmn/engine 0.1.0-dev.6195e056 → 0.1.0-dev.6e63f5e4

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 (57) hide show
  1. package/README.md +101 -62
  2. package/build/pkg/addon.d.ts +7 -0
  3. package/build/pkg/addon.js +112 -0
  4. package/build/pkg/addon.js.map +1 -0
  5. package/build/pkg/data/layout.json +25 -3
  6. package/build/pkg/data/protocol.json +22 -4
  7. package/build/pkg/data.d.ts +1 -2
  8. package/build/pkg/data.js +3 -5
  9. package/build/pkg/data.js.map +1 -1
  10. package/build/pkg/gen1.d.ts +5 -4
  11. package/build/pkg/gen1.js +47 -31
  12. package/build/pkg/gen1.js.map +1 -1
  13. package/build/pkg/index.d.ts +16 -13
  14. package/build/pkg/index.js +1 -1
  15. package/build/pkg/index.js.map +1 -1
  16. package/build/pkg/protocol.d.ts +4 -2
  17. package/build/pkg/protocol.js +5 -3
  18. package/build/pkg/protocol.js.map +1 -1
  19. package/build/test/display.js +400 -0
  20. package/build/test/display.js.map +1 -0
  21. package/build/tools/debug.js +133 -0
  22. package/build/tools/debug.js.map +1 -0
  23. package/build.zig +104 -90
  24. package/package.json +57 -36
  25. package/src/bin/install-pkmn-engine +68 -36
  26. package/src/bin/pkmn-debug +62 -0
  27. package/src/lib/binding/node.zig +47 -142
  28. package/src/lib/binding/wasm.zig +6 -4
  29. package/src/lib/common/battle.zig +17 -0
  30. package/src/lib/common/data.zig +15 -27
  31. package/src/lib/common/debug.zig +74 -0
  32. package/src/lib/common/js.zig +118 -0
  33. package/src/lib/common/optional.zig +60 -0
  34. package/src/lib/common/options.zig +12 -3
  35. package/src/lib/common/protocol.zig +416 -198
  36. package/src/lib/common/rational.zig +353 -0
  37. package/src/lib/common/rng.zig +15 -15
  38. package/src/lib/common/util.zig +12 -0
  39. package/src/lib/gen1/README.md +282 -165
  40. package/src/lib/gen1/calc.zig +426 -0
  41. package/src/lib/gen1/chance.zig +1058 -0
  42. package/src/lib/gen1/data/moves.zig +178 -180
  43. package/src/lib/gen1/data/species.zig +2 -2
  44. package/src/lib/gen1/data/types.zig +4 -4
  45. package/src/lib/gen1/data.zig +79 -65
  46. package/src/lib/gen1/helpers.zig +592 -36
  47. package/src/lib/gen1/mechanics.zig +1097 -662
  48. package/src/lib/gen2/README.md +87 -13
  49. package/src/lib/gen2/data/items.zig +3 -3
  50. package/src/lib/gen2/data/moves.zig +635 -563
  51. package/src/lib/gen2/data/species.zig +1 -1
  52. package/src/lib/gen2/data/types.zig +4 -4
  53. package/src/lib/gen2/data.zig +109 -77
  54. package/src/lib/gen2/helpers.zig +16 -22
  55. package/src/lib/gen2/mechanics.zig +1160 -11
  56. package/src/lib/pkmn.zig +68 -9
  57. package/src/test/display/pkmn.html.tmpl +368 -0
@@ -1793,6 +1793,6 @@ pub const Species = enum(u8) {
1793
1793
  /// Returns the `Data` corresponding to the species.
1794
1794
  pub fn get(id: Species) Data {
1795
1795
  assert(id != .None);
1796
- return DATA[@enumToInt(id) - 1];
1796
+ return DATA[@intFromEnum(id) - 1];
1797
1797
  }
1798
1798
  };
@@ -33,7 +33,7 @@ pub const Type = enum(u8) {
33
33
  Dragon,
34
34
  Dark,
35
35
 
36
- const CHART = [18][18]Effectiveness{
36
+ const CHART: [18][18]Effectiveness = .{
37
37
  [_]Effectiveness{ N, N, N, N, N, R, N, I, R, N, N, N, N, N, N, N, N, N }, // Normal
38
38
  [_]Effectiveness{ S, N, R, R, N, S, R, I, S, N, N, N, N, N, R, S, N, S }, // Fighting
39
39
  [_]Effectiveness{ N, S, N, N, N, R, S, N, R, N, N, N, S, R, N, N, N, N }, // Flying
@@ -86,17 +86,17 @@ pub const Type = enum(u8) {
86
86
 
87
87
  /// Whether or not this type is considered to be special as opposed to physical.
88
88
  pub inline fn special(self: Type) bool {
89
- return @enumToInt(self) >= @enumToInt(Type.Fire);
89
+ return @intFromEnum(self) >= @intFromEnum(Type.Fire);
90
90
  }
91
91
 
92
92
  /// The `Effectiveness` of type `t2` vs. type `t1`.
93
93
  pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
94
- return CHART[@enumToInt(t1)][@enumToInt(t2)];
94
+ return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
95
95
  }
96
96
 
97
97
  /// The precedence order of type type.
98
98
  pub inline fn precedence(self: Type) u8 {
99
- return PRECEDENCE[@enumToInt(self)];
99
+ return PRECEDENCE[@intFromEnum(self)];
100
100
  }
101
101
  };
102
102
 
@@ -1,10 +1,13 @@
1
1
  const std = @import("std");
2
2
  const builtin = @import("builtin");
3
3
 
4
+ const pkmn = @import("../pkmn.zig");
5
+
4
6
  const data = @import("../common/data.zig");
5
7
  const DEBUG = @import("../common/debug.zig").print;
6
- const options = @import("../common/options.zig");
8
+ const protocol = @import("../common/protocol.zig");
7
9
  const rng = @import("../common/rng.zig");
10
+ const util = @import("../common/util.zig");
8
11
 
9
12
  const items = @import("data/items.zig");
10
13
  const moves = @import("data/moves.zig");
@@ -13,30 +16,37 @@ const types = @import("data/types.zig");
13
16
 
14
17
  const gen1 = @import("../gen1/data.zig");
15
18
 
19
+ const calc = @import("calc.zig");
20
+ const chance = @import("chance.zig");
16
21
  const mechanics = @import("mechanics.zig");
17
22
 
18
23
  const assert = std.debug.assert;
19
24
  const expectEqual = std.testing.expectEqual;
20
25
  const expect = std.testing.expect;
21
26
 
22
- pub const MAX_OPTIONS: usize = 9; // move 1..4, switch 2..6
27
+ pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
23
28
  pub const MAX_LOGS: usize = 180; // TODO
24
29
 
25
- pub const OPTIONS_SIZE = if (builtin.mode == .ReleaseSmall)
26
- MAX_OPTIONS
30
+ pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
31
+ MAX_CHOICES
27
32
  else
28
- std.math.ceilPowerOfTwo(usize, MAX_OPTIONS) catch unreachable;
33
+ std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
29
34
  pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
30
35
  MAX_LOGS
31
36
  else
32
37
  std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
33
38
 
39
+ /// Null object pattern implementation of pkmn.battle.Options for Generation II.
40
+ pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
41
+
34
42
  const Choice = data.Choice;
35
43
  const ID = data.ID;
36
44
  const Player = data.Player;
37
45
  const Result = data.Result;
38
46
 
39
- const showdown = options.showdown;
47
+ const PointerType = util.PointerType;
48
+
49
+ const showdown = pkmn.options.showdown;
40
50
 
41
51
  pub const PRNG = rng.PRNG(2);
42
52
 
@@ -49,20 +59,25 @@ pub fn Battle(comptime RNG: anytype) type {
49
59
  turn: u16 = 0,
50
60
  field: Field = .{},
51
61
 
52
- pub inline fn side(self: *Self, player: Player) *Side {
53
- return &self.sides[@enumToInt(player)];
62
+ _1: u8 = 0,
63
+ _2: if (!showdown) u16 else u32 = 0,
64
+
65
+ pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
66
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
67
+ return &self.sides[@intFromEnum(player)];
54
68
  }
55
69
 
56
- pub inline fn foe(self: *Self, player: Player) *Side {
57
- return &self.sides[@enumToInt(player.foe())];
70
+ pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
72
+ return &self.sides[@intFromEnum(player.foe())];
58
73
  }
59
74
 
60
- pub inline fn active(self: *Self, player: Player) ID {
61
- return player.ident(@intCast(u3, self.side(player).pokemon[0].position));
75
+ pub inline fn active(self: *const Self, player: Player) ID {
76
+ return player.ident(@intCast(self.side(player).pokemon[0].position));
62
77
  }
63
78
 
64
- pub fn update(self: *Self, c1: Choice, c2: Choice, log: anytype) !Result {
65
- return mechanics.update(self, c1, c2, log);
79
+ pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
80
+ return mechanics.update(self, c1, c2, options);
66
81
  }
67
82
 
68
83
  pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
@@ -71,35 +86,34 @@ pub fn Battle(comptime RNG: anytype) type {
71
86
  };
72
87
  }
73
88
 
74
- // TODO
75
89
  test Battle {
76
- try expectEqual(496, @sizeOf(Battle(PRNG)));
90
+ try expectEqual(512, @sizeOf(Battle(PRNG)));
77
91
  }
78
92
 
79
- const Field = packed struct {
93
+ pub const Field = packed struct(u8) {
80
94
  weather: Weather = .None,
81
95
  weather_duration: u4 = 0,
82
-
83
- comptime {
84
- assert(@sizeOf(Field) == 1);
85
- }
86
96
  };
87
97
 
88
- const Weather = enum(u4) {
98
+ pub const Weather = enum(u4) {
89
99
  None,
90
100
  Rain,
91
101
  Sun,
92
102
  Sandstorm,
93
103
  };
94
104
 
95
- const Side = extern struct {
105
+ pub const Side = extern struct {
96
106
  pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
97
- active: ActivePokemon = .{},
98
- conditions: Conditions,
107
+ active: ActivePokemon align(8) = .{},
108
+ conditions: Conditions = .{},
109
+ _1: u32 = 0,
110
+ _2: u16 = 0,
111
+ // NB: wCurPlayerMove / wCurEnemyMove (?)
112
+ _3: u8 = 0,
113
+ // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
99
114
  last_used_move: Move = .None,
100
- _: u8 = 0,
101
115
 
102
- const Conditions = packed struct {
116
+ const Conditions = packed struct(u32) {
103
117
  Spikes: bool = false,
104
118
  Safeguard: bool = false,
105
119
  LightScreen: bool = false,
@@ -109,50 +123,69 @@ const Side = extern struct {
109
123
  light_screen: u4 = 0,
110
124
  reflect: u4 = 0,
111
125
 
112
- comptime {
113
- assert(@sizeOf(Conditions) == 2);
114
- }
126
+ future_sight: FutureSight = .{},
127
+
128
+ const FutureSight = packed struct(u16) {
129
+ damage: u12 = 0,
130
+ count: u4 = 0,
131
+ };
115
132
  };
116
133
 
117
134
  comptime {
118
- assert(@sizeOf(Side) == 240);
135
+ assert(@sizeOf(Side) == 248);
119
136
  }
120
137
 
121
- pub inline fn get(self: *const Side, slot: u8) *Pokemon {
138
+ pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
139
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
122
140
  assert(slot > 0 and slot <= 6);
123
141
  const id = self.pokemon[slot - 1].position;
124
142
  assert(id > 0 and id <= 6);
125
143
  return &self.pokemon[id - 1];
126
144
  }
127
145
 
128
- pub inline fn stored(self: *Side) *Pokemon {
146
+ pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
147
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
129
148
  return self.get(1);
130
149
  }
150
+
151
+ pub inline fn lastMove(self: anytype, encore: bool) Move {
152
+ assert(self.last_used_move != .None or !self.active.volatiles.dirty);
153
+ if (encore) return self.last_used_move;
154
+ return if (self.active.volatiles.dirty) .None else self.last_used_move;
155
+ }
156
+
157
+ // TODO
158
+ // pub inline fn useMove(self: anytype) void {
159
+ // assert(side.last_selected_move != .None);
160
+ // self.last_used_move = side.last_selected_move;
161
+ // self.active.volatiles.dirty = false;
162
+ // }
131
163
  };
132
164
 
133
165
  // NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
134
- const ActivePokemon = extern struct {
135
- volatiles: Volatile align(4) = .{},
166
+ pub const ActivePokemon = extern struct {
167
+ volatiles: Volatiles align(4) = .{},
136
168
  stats: Stats(u16) = .{},
137
169
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
138
170
  boosts: Boosts = .{},
171
+ types: Types = .{},
139
172
  species: Species = .None,
140
- item: Item = .None,
141
- last_move: Move = .None,
173
+
142
174
  _: u8 = 0,
143
175
 
144
176
  comptime {
145
177
  assert(@sizeOf(ActivePokemon) == 44);
146
178
  }
147
179
 
148
- pub inline fn move(self: *ActivePokemon, mslot: u8) *MoveSlot {
180
+ pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
181
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
149
182
  assert(mslot > 0 and mslot <= 4);
150
183
  assert(self.moves[mslot - 1].id != .None);
151
184
  return &self.moves[mslot - 1];
152
185
  }
153
186
  };
154
187
 
155
- const Pokemon = extern struct {
188
+ pub const Pokemon = extern struct {
156
189
  stats: Stats(u16) = .{},
157
190
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
158
191
  types: Types = .{},
@@ -169,7 +202,8 @@ const Pokemon = extern struct {
169
202
  assert(@sizeOf(Pokemon) == 32);
170
203
  }
171
204
 
172
- pub inline fn move(self: *Pokemon, mslot: u8) *MoveSlot {
205
+ pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
206
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
173
207
  assert(mslot > 0 and mslot <= 4);
174
208
  assert(self.moves[mslot - 1].id != .None);
175
209
  return &self.moves[mslot - 1];
@@ -182,7 +216,7 @@ pub const Gender = enum(u2) {
182
216
  Unknown,
183
217
  };
184
218
 
185
- const DVs = packed struct {
219
+ pub const DVs = packed struct(u16) {
186
220
  gender: Gender = .Male,
187
221
  pow: u6 = 40,
188
222
  type: Type = .Dark,
@@ -190,7 +224,7 @@ const DVs = packed struct {
190
224
  pub fn init(g: Gender, p: u6, t: Type) DVs {
191
225
  assert(p >= 1 and p <= 40);
192
226
  assert(t != .Normal and t != .@"???");
193
- return DVs{ .gender = g, .pow = p, .type = t };
227
+ return .{ .gender = g, .pow = p, .type = t };
194
228
  }
195
229
 
196
230
  pub inline fn power(dvs: DVs) u8 {
@@ -205,15 +239,11 @@ const DVs = packed struct {
205
239
  0xFF => .Unknown,
206
240
  else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
207
241
  };
208
- const p = @intCast(u6, 1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
242
+ const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
209
243
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
210
244
  var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
211
- if (t >= @enumToInt(Type.@"???")) t = t + 1;
212
- return DVs{ .gender = g, .pow = p, .type = @intToEnum(Type, t) };
213
- }
214
-
215
- comptime {
216
- assert(@sizeOf(DVs) == 2);
245
+ if (t >= @intFromEnum(Type.@"???")) t = t + 1;
246
+ return .{ .gender = g, .pow = p, .type = @enumFromInt(t) };
217
247
  }
218
248
  };
219
249
 
@@ -238,10 +268,12 @@ test DVs {
238
268
  try expectEqual(Gender.Female, dvs.gender);
239
269
  }
240
270
 
241
- const MoveSlot = packed struct {
271
+ /// Representation of a Generation II Pokémon's move slot in a battle.
272
+ pub const MoveSlot = extern struct {
273
+ /// The identifier for the move.
242
274
  id: Move = .None,
243
- pp_ups: u2 = 3,
244
- pp: u6 = 0,
275
+ /// The remaining PP of the move.
276
+ pp: u8 = 0,
245
277
 
246
278
  comptime {
247
279
  assert(@sizeOf(MoveSlot) == 2);
@@ -250,14 +282,16 @@ const MoveSlot = packed struct {
250
282
 
251
283
  pub const Status = gen1.Status;
252
284
 
253
- const Volatile = packed struct {
285
+ pub const Volatiles = packed struct(u128) {
254
286
  Bide: bool = false,
255
287
  Thrashing: bool = false,
288
+ BeatUp: bool = false,
256
289
  Flinch: bool = false,
257
290
  Charging: bool = false,
258
291
  Underground: bool = false,
259
292
  Flying: bool = false,
260
293
  Confusion: bool = false,
294
+
261
295
  Mist: bool = false,
262
296
  FocusEnergy: bool = false,
263
297
  Substitute: bool = false,
@@ -268,27 +302,27 @@ const Volatile = packed struct {
268
302
  Transform: bool = false,
269
303
 
270
304
  Nightmare: bool = false,
271
- Cure: bool = false,
305
+ Curse: bool = false,
272
306
  Protect: bool = false,
273
307
  Foresight: bool = false,
274
308
  PerishSong: bool = false,
275
309
  Endure: bool = false,
276
310
  Rollout: bool = false,
277
311
  Attract: bool = false,
312
+
278
313
  DefenseCurl: bool = false,
279
314
  Encore: bool = false,
280
315
  LockOn: bool = false,
281
316
  DestinyBond: bool = false,
282
- BeatUp: bool = false,
283
317
 
284
318
  trapped: bool = false,
285
319
  switched: bool = false,
286
320
  switching: bool = false,
321
+ dirty: bool = false,
287
322
 
288
- _: u5 = 0,
323
+ _: u20 = 0,
289
324
 
290
325
  bind: u4 = 0,
291
- future_sight: FutureSight = .{},
292
326
  bide: u16 = 0,
293
327
  disabled: Disabled = .{},
294
328
  rage: u8 = 0,
@@ -296,24 +330,16 @@ const Volatile = packed struct {
296
330
  toxic: u8 = 0,
297
331
  confusion: u4 = 0,
298
332
  encore: u4 = 0,
333
+ // TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
299
334
  fury_cutter: u4 = 0,
300
335
  perish_song: u4 = 0,
301
336
  protect: u4 = 0,
302
- rollout: u4 = 0,
337
+ attacks: u4 = 0, // bide/thrashing/rollout turns
303
338
 
304
- const Disabled = packed struct {
339
+ const Disabled = packed struct(u8) {
305
340
  move: u4 = 0,
306
341
  duration: u4 = 0,
307
342
  };
308
-
309
- const FutureSight = packed struct {
310
- damage: u12 = 0,
311
- count: u4 = 0,
312
- };
313
-
314
- comptime {
315
- assert(@sizeOf(Volatile) == 16);
316
- }
317
343
  };
318
344
 
319
345
  pub fn Stats(comptime T: type) type {
@@ -327,10 +353,11 @@ pub fn Stats(comptime T: type) type {
327
353
 
328
354
  pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
329
355
  assert(level > 0 and level <= 100);
330
- const evs = @min(255, @floatToInt(u16, @ceil(@sqrt(@intToFloat(f32, exp)))));
356
+ const float: f32 = @floatFromInt(exp);
357
+ const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
331
358
  const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
332
359
  const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
333
- return @intCast(T, core *% @as(u32, level) / 100 +% factor);
360
+ return @intCast(core *% @as(u32, level) / 100 +% factor);
334
361
  }
335
362
  };
336
363
  }
@@ -347,7 +374,7 @@ test Stats {
347
374
  try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
348
375
  }
349
376
 
350
- pub const Boosts = packed struct {
377
+ pub const Boosts = packed struct(u32) {
351
378
  atk: i4 = 0,
352
379
  def: i4 = 0,
353
380
  spe: i4 = 0,
@@ -363,7 +390,7 @@ pub const Boosts = packed struct {
363
390
  };
364
391
 
365
392
  test Boosts {
366
- const boosts = Boosts{ .spd = -6 };
393
+ const boosts: Boosts = .{ .spd = -6 };
367
394
  try expectEqual(0, boosts.atk);
368
395
  try expectEqual(-6, boosts.spd);
369
396
  }
@@ -390,17 +417,22 @@ test Item {
390
417
  pub const Move = moves.Move;
391
418
 
392
419
  test Move {
393
- try expectEqual(251, @enumToInt(Move.BeatUp));
420
+ try expectEqual(251, @intFromEnum(Move.BeatUp));
394
421
  const move = Move.get(.DynamicPunch);
395
422
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
396
- try expectEqual(@as(u8, 50), move.accuracy);
397
- try expectEqual(@as(u8, 5), move.pp);
423
+ try expectEqual(rng.Gen12.percent(50), move.accuracy);
424
+ try expectEqual(rng.Gen12.percent(100), move.chance);
425
+ try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
426
+ try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
427
+ try expect(!move.effect.isHighCritical());
428
+ try expect(Move.get(.KarateChop).effect.isHighCritical());
429
+ try expect(Move.get(.RazorWind).effect.isHighCritical());
398
430
  }
399
431
 
400
432
  pub const Species = species.Species;
401
433
 
402
434
  test Species {
403
- try expectEqual(152, @enumToInt(Species.Chikorita));
435
+ try expectEqual(152, @intFromEnum(Species.Chikorita));
404
436
  try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
405
437
  }
406
438
 
@@ -409,7 +441,7 @@ pub const Types = types.Types;
409
441
  pub const Effectiveness = gen1.Effectiveness;
410
442
 
411
443
  test Types {
412
- try expectEqual(13, @enumToInt(Type.Electric));
444
+ try expectEqual(13, @intFromEnum(Type.Electric));
413
445
 
414
446
  try expect(!Type.Steel.special());
415
447
  try expect(Type.Dark.special());
@@ -48,7 +48,7 @@ pub const Battle = struct {
48
48
  comptime rolls: anytype,
49
49
  p1: []const Pokemon,
50
50
  p2: []const Pokemon,
51
- ) data.Battle(rng.FixedRNG(1, rolls.len)) {
51
+ ) data.Battle(rng.FixedRNG(2, rolls.len)) {
52
52
  return .{
53
53
  .rng = .{ .rolls = rolls },
54
54
  .sides = .{ Side.init(p1), Side.init(p2) },
@@ -72,11 +72,9 @@ pub fn prng(rand: *PSRNG) data.PRNG {
72
72
  rand.newSeed()
73
73
  else
74
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),
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),
80
78
  },
81
79
  },
82
80
  };
@@ -89,7 +87,7 @@ pub const Side = struct {
89
87
 
90
88
  for (0..ps.len) |i| {
91
89
  side.pokemon[i] = Pokemon.init(ps[i]);
92
- // FIXME side.order[i] = i + 1;
90
+ side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
93
91
  }
94
92
  return side;
95
93
  }
@@ -100,7 +98,7 @@ pub const Side = struct {
100
98
 
101
99
  for (0..n) |i| {
102
100
  side.pokemon[i] = Pokemon.random(rand, opt);
103
- side.pokemon[i].position = i + 1;
101
+ side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
104
102
  }
105
103
 
106
104
  return side;
@@ -137,12 +135,9 @@ pub const Pokemon = struct {
137
135
  }
138
136
  assert(p.moves.len > 0 and p.moves.len <= 4);
139
137
  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
- };
138
+ pokemon.moves[j].id = m;
139
+ // NB: PP can be at most 61 legally (though can overflow to 63)
140
+ pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
146
141
  }
147
142
  pokemon.item = p.item;
148
143
  if (p.hp) |hp| {
@@ -153,16 +148,16 @@ pub const Pokemon = struct {
153
148
  pokemon.status = p.status;
154
149
  pokemon.types = species.types;
155
150
  pokemon.level = p.level;
156
- pokemon.dvs = DVs.from(p.dvs);
151
+ pokemon.dvs = DVs.from(p.species, p.dvs);
157
152
  return pokemon;
158
153
  }
159
154
 
160
155
  pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
161
- const s = @intToEnum(Species, rand.range(u8, 1, Species.size + 1));
156
+ const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
162
157
  const species = Species.get(s);
163
158
  const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
164
159
  // 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);
160
+ const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
166
161
  const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
167
162
 
168
163
  var stats: Stats(u16) = .{};
@@ -184,24 +179,23 @@ pub const Pokemon = struct {
184
179
  for (0..n) |i| {
185
180
  var m: Move = .None;
186
181
  sample: while (true) {
187
- m = @intToEnum(Move, rand.range(u8, 1, 164 + 1));
182
+ m = @enumFromInt(rand.range(u8, 1, 164 + 1));
188
183
  for (0..i) |j| if (ms[j].id == m) continue :sample;
189
184
  break;
190
185
  }
191
- const pp_ups =
186
+ const pp_ups: u8 =
192
187
  if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
193
188
  // 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));
189
+ const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
195
190
  ms[i] = .{
196
191
  .id = m,
197
- .pp_ups = pp_ups,
198
192
  .pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
199
193
  };
200
194
  }
201
195
  return .{
202
196
  .species = s,
203
197
  .types = species.types,
204
- .dvs = DVs.from(dvs),
198
+ .dvs = DVs.from(s, dvs),
205
199
  .level = lvl,
206
200
  .stats = stats,
207
201
  .hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),