@pkmn/engine 0.1.0-dev.8b192003 → 0.1.0-dev.8cf7e679

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 (76) hide show
  1. package/README.md +9 -6
  2. package/build/pkg/addon/browser.d.ts +3 -0
  3. package/build/pkg/addon/browser.js +44 -0
  4. package/build/pkg/addon/browser.js.map +1 -0
  5. package/build/pkg/addon/node.d.ts +3 -0
  6. package/build/pkg/addon/node.js +97 -0
  7. package/build/pkg/addon/node.js.map +1 -0
  8. package/build/pkg/addon/wasm.d.ts +2 -0
  9. package/build/pkg/addon/wasm.js +44 -0
  10. package/build/pkg/addon/wasm.js.map +1 -0
  11. package/build/pkg/addon.d.ts +25 -3
  12. package/build/pkg/addon.js +34 -65
  13. package/build/pkg/addon.js.map +1 -1
  14. package/build/pkg/data/layout.json +15 -2
  15. package/build/pkg/data/protocol.json +23 -9
  16. package/build/pkg/data.d.ts +23 -1
  17. package/build/pkg/data.js.map +1 -1
  18. package/build/pkg/gen1.d.ts +13 -17
  19. package/build/pkg/gen1.js +6 -10
  20. package/build/pkg/gen1.js.map +1 -1
  21. package/build/pkg/index.d.ts +38 -90
  22. package/build/pkg/index.js +6 -63
  23. package/build/pkg/index.js.map +1 -1
  24. package/build/pkg/protocol.d.ts +20 -1
  25. package/build/pkg/protocol.js +105 -103
  26. package/build/pkg/protocol.js.map +1 -1
  27. package/build/tools/debug.js +35 -69
  28. package/build/tools/debug.js.map +1 -1
  29. package/build/tools/display/debug.jsx +141 -0
  30. package/build/tools/display/debug.jsx.map +1 -0
  31. package/build/tools/display/index.js +141 -0
  32. package/build/tools/display/index.js.map +1 -0
  33. package/build/tools/display/ui.jsx +349 -0
  34. package/build/tools/display/ui.jsx.map +1 -0
  35. package/build/tools/display/util.js +18 -0
  36. package/build/tools/display/util.js.map +1 -0
  37. package/build.zig +254 -77
  38. package/package.json +50 -134
  39. package/src/bin/install-pkmn-engine +9 -7
  40. package/src/bin/pkmn-debug +1 -1
  41. package/src/lib/common/array.zig +77 -0
  42. package/src/lib/common/data.zig +8 -8
  43. package/src/lib/common/debug.zig +39 -8
  44. package/src/lib/common/js.zig +4 -2
  45. package/src/lib/common/optional.zig +13 -9
  46. package/src/lib/common/options.zig +4 -0
  47. package/src/lib/common/protocol.zig +45 -44
  48. package/src/lib/common/rational.zig +53 -51
  49. package/src/lib/common/rng.zig +5 -7
  50. package/src/lib/common/util.zig +50 -1
  51. package/src/lib/gen1/calc.zig +295 -154
  52. package/src/lib/gen1/chance.zig +477 -119
  53. package/src/lib/gen1/data/moves.zig +2 -4
  54. package/src/lib/gen1/data/species.zig +1 -3
  55. package/src/lib/gen1/data/types.zig +1 -2
  56. package/src/lib/gen1/data.zig +35 -38
  57. package/src/lib/gen1/helpers.zig +17 -27
  58. package/src/lib/gen1/mechanics.zig +248 -148
  59. package/src/lib/gen2/calc.zig +37 -27
  60. package/src/lib/gen2/chance.zig +87 -67
  61. package/src/lib/gen2/data/items.zig +1 -3
  62. package/src/lib/gen2/data/moves.zig +2 -4
  63. package/src/lib/gen2/data/species.zig +1 -3
  64. package/src/lib/gen2/data/types.zig +1 -2
  65. package/src/lib/gen2/data.zig +26 -29
  66. package/src/lib/gen2/generated.zig +18 -23
  67. package/src/lib/gen2/helpers.zig +9 -18
  68. package/src/lib/gen2/mechanics.zig +81 -81
  69. package/src/lib/node.zig +2 -4
  70. package/src/lib/pkmn.zig +11 -5
  71. package/src/lib/wasm.zig +36 -1
  72. package/src/tools/display/dom.ts +96 -0
  73. package/src/tools/display/ui.css +354 -0
  74. package/build/test/display.js +0 -400
  75. package/build/test/display.js.map +0 -1
  76. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,20 +1,21 @@
1
- const std = @import("std");
2
1
  const builtin = @import("builtin");
3
-
4
- const pkmn = @import("../pkmn.zig");
5
-
6
2
  const data = @import("data.zig");
7
3
  const options = @import("options.zig");
4
+ const pkmn = @import("../pkmn.zig");
5
+ const std = @import("std");
8
6
 
9
7
  const assert = std.debug.assert;
10
- const print = std.debug.print;
11
-
12
8
  const enabled = options.log;
13
-
14
- const Player = data.Player;
15
9
  const ID = data.ID;
10
+ const Player = data.Player;
11
+ const print = std.debug.print;
16
12
 
17
13
  const endian = builtin.cpu.arch.endian();
14
+ const Endian = std.builtin.Endian;
15
+ const big = if (@hasField(Endian, "big")) Endian.big else Endian.Big;
16
+ const little = if (@hasField(Endian, "little")) Endian.little else Endian.Little;
17
+
18
+ const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
18
19
 
19
20
  pub const ArgType = enum(u8) {
20
21
  None,
@@ -840,7 +841,7 @@ pub const ByteStream = struct {
840
841
 
841
842
  pub fn writeInt(self: Writer, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
842
843
  // TODO: rework this to write directly to the buffer?
843
- var bytes: [@divExact(@typeInfo(T).Int.bits, 8)]u8 = undefined;
844
+ var bytes: [@divExact(@field(@typeInfo(T), @tagName(Int)).bits, 8)]u8 = undefined;
844
845
  std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(v)), &bytes, v, end);
845
846
  return self.writeAll(&bytes);
846
847
  }
@@ -864,7 +865,7 @@ pub const ByteStream = struct {
864
865
  pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
865
866
 
866
867
  pub fn format(
867
- comptime gen: anytype,
868
+ gen: anytype,
868
869
  a: []const u8,
869
870
  b: ?[]const u8,
870
871
  color: bool,
@@ -956,13 +957,13 @@ pub fn format(
956
957
  }
957
958
  printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
958
959
  switch (endian) {
959
- .big => {
960
+ big => {
960
961
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
961
962
  printc(" {d}", .{hp}, a, b, &i, 2, color);
962
963
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
963
964
  printc("/{d}", .{hp}, a, b, &i, 2, color);
964
965
  },
965
- .little => {
966
+ little => {
966
967
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
967
968
  printc(" {d}", .{hp}, a, b, &i, 2, color);
968
969
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
@@ -993,8 +994,8 @@ pub fn format(
993
994
  },
994
995
  .Turn => {
995
996
  const turn = switch (endian) {
996
- .big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
997
- .little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
997
+ big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
998
+ little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
998
999
  };
999
1000
  printc(" {d}", .{turn}, a, b, &i, 2, color);
1000
1001
  },
@@ -1005,13 +1006,13 @@ pub fn format(
1005
1006
  var id = ID.from(@intCast(a[i]));
1006
1007
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1007
1008
  switch (endian) {
1008
- .big => {
1009
+ big => {
1009
1010
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1010
1011
  printc(" {d}", .{hp}, a, b, &i, 2, color);
1011
1012
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1012
1013
  printc("/{d}", .{hp}, a, b, &i, 2, color);
1013
1014
  },
1014
- .little => {
1015
+ little => {
1015
1016
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1016
1017
  printc(" {d}", .{hp}, a, b, &i, 2, color);
1017
1018
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
@@ -1133,13 +1134,13 @@ pub fn format(
1133
1134
  const id = ID.from(@intCast(a[i]));
1134
1135
  printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
1135
1136
  switch (endian) {
1136
- .big => {
1137
+ big => {
1137
1138
  var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1138
1139
  printc(" {d}", .{hp}, a, b, &i, 2, color);
1139
1140
  hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
1140
1141
  printc("/{d}", .{hp}, a, b, &i, 2, color);
1141
1142
  },
1142
- .little => {
1143
+ little => {
1143
1144
  var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
1144
1145
  printc(" {d}", .{hp}, a, b, &i, 2, color);
1145
1146
  hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
@@ -1345,8 +1346,8 @@ test "|switch|" {
1345
1346
  try log.switched(.{ p2.ident(3), &snorlax });
1346
1347
  const par = 0b1000000;
1347
1348
  var expected: []const u8 = switch (endian) {
1348
- .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1349
- .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
1349
+ big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
1350
+ little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
1350
1351
  };
1351
1352
  try expectLog1(expected, buf[0..9]);
1352
1353
  stream.reset();
@@ -1356,8 +1357,8 @@ test "|switch|" {
1356
1357
  snorlax.status = 0;
1357
1358
  try log.switched(.{ p2.ident(3), &snorlax });
1358
1359
  expected = switch (endian) {
1359
- .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1360
- .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
1360
+ big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
1361
+ little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
1361
1362
  };
1362
1363
  try expectLog1(expected, buf[0..9]);
1363
1364
  stream.reset();
@@ -1365,8 +1366,8 @@ test "|switch|" {
1365
1366
  snorlax.hp = 400;
1366
1367
  try log.switched(.{ p2.ident(3), &snorlax });
1367
1368
  expected = switch (endian) {
1368
- .big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1369
- .little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
1369
+ big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
1370
+ little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
1370
1371
  };
1371
1372
  try expectLog1(expected, buf[0..9]);
1372
1373
  stream.reset();
@@ -1384,8 +1385,8 @@ test "|switch|" {
1384
1385
  N(gen2.Gender.Female),
1385
1386
  91,
1386
1387
  } ++ switch (endian) {
1387
- .big => .{ 0, 200, 1, 144, par },
1388
- .little => .{ 200, 0, 144, 1, par },
1388
+ big => .{ 0, 200, 1, 144, par },
1389
+ little => .{ 200, 0, 144, 1, par },
1389
1390
  });
1390
1391
  try expectLog2(expected, buf[0..10]);
1391
1392
  stream.reset();
@@ -1414,8 +1415,8 @@ test "|faint|" {
1414
1415
  test "|turn|" {
1415
1416
  try log.turn(.{42});
1416
1417
  const expected = switch (endian) {
1417
- .big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1418
- .little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1418
+ big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
1419
+ little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
1419
1420
  };
1420
1421
  try expectLog1(expected, buf[0..4]);
1421
1422
  stream.reset();
@@ -1439,8 +1440,8 @@ test "|-damage|" {
1439
1440
  chansey.status = gen1.Status.slp(1);
1440
1441
  try log.damage(.{ p2.ident(2), &chansey, .None });
1441
1442
  var expected: []const u8 = switch (endian) {
1442
- .big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1443
- .little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1443
+ big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
1444
+ little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
1444
1445
  };
1445
1446
  try expectLog1(expected, buf[0..8]);
1446
1447
  stream.reset();
@@ -1450,8 +1451,8 @@ test "|-damage|" {
1450
1451
  chansey.status = 0;
1451
1452
  try log.damage(.{ p2.ident(2), &chansey, .Confusion });
1452
1453
  expected = switch (endian) {
1453
- .big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1454
- .little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1454
+ big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
1455
+ little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
1455
1456
  };
1456
1457
  try expectLog1(expected, buf[0..8]);
1457
1458
  stream.reset();
@@ -1459,8 +1460,8 @@ test "|-damage|" {
1459
1460
  chansey.status = gen1.Status.init(.PSN);
1460
1461
  try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
1461
1462
  expected = switch (endian) {
1462
- .big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1463
- .little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1463
+ big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
1464
+ little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
1464
1465
  };
1465
1466
  try expectLog1(expected, buf[0..9]);
1466
1467
  stream.reset();
@@ -1472,8 +1473,8 @@ test "|-heal|" {
1472
1473
  chansey.status = gen1.Status.slp(1);
1473
1474
  try log.heal(.{ p2.ident(2), &chansey, .None });
1474
1475
  var expected: []const u8 = switch (endian) {
1475
- .big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1476
- .little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1476
+ big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
1477
+ little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
1477
1478
  };
1478
1479
  try expectLog1(expected, buf[0..8]);
1479
1480
  stream.reset();
@@ -1483,16 +1484,16 @@ test "|-heal|" {
1483
1484
  chansey.status = 0;
1484
1485
  try log.heal(.{ p2.ident(2), &chansey, .Silent });
1485
1486
  expected = switch (endian) {
1486
- .big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1487
- .little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1487
+ big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
1488
+ little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
1488
1489
  };
1489
1490
  try expectLog1(expected, buf[0..8]);
1490
1491
  stream.reset();
1491
1492
 
1492
1493
  try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
1493
1494
  expected = switch (endian) {
1494
- .big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1495
- .little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1495
+ big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
1496
+ little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
1496
1497
  };
1497
1498
  try expectLog1(expected, buf[0..9]);
1498
1499
  stream.reset();
@@ -1735,8 +1736,8 @@ test "|drag|" {
1735
1736
  N(gen2.Gender.Female),
1736
1737
  91,
1737
1738
  } ++ switch (endian) {
1738
- .big => .{ 0, 200, 1, 144, par },
1739
- .little => .{ 200, 0, 144, 1, par },
1739
+ big => .{ 0, 200, 1, 144, par },
1740
+ little => .{ 200, 0, 144, 1, par },
1740
1741
  });
1741
1742
  try expectLog2(expected, buf[0..10]);
1742
1743
  stream.reset();
@@ -1769,8 +1770,8 @@ test "|-sethp|" {
1769
1770
  blissey.hp = 612;
1770
1771
  try log.sethp(.{ p2.ident(2), &blissey, .Silent });
1771
1772
  const expected: []const u8 = switch (endian) {
1772
- .big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1773
- .little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1773
+ big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
1774
+ little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
1774
1775
  };
1775
1776
  try expectLog2(expected, buf[0..8]);
1776
1777
  stream.reset();
@@ -6,9 +6,18 @@ const expect = std.testing.expect;
6
6
  const expectEqual = std.testing.expectEqual;
7
7
  const expectError = std.testing.expectError;
8
8
 
9
+ const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
10
+ const Float = if (@hasField(std.builtin.Type, "float")) .float else .Float;
11
+ const ComptimeInt =
12
+ if (@hasField(std.builtin.Type, "comptime_int")) .comptime_int else .ComptimeInt;
13
+ const ComptimeFloat =
14
+ if (@hasField(std.builtin.Type, "comptime_float")) .comptime_float else .ComptimeFloat;
15
+
9
16
  /// Specialization of a rational number used by the engine to compute probabilties.
10
17
  /// For performance reasons the rational is only reduced lazily and thus `reduce` must be
11
- /// invoked explicitly before reading.
18
+ /// invoked explicitly before reading. Note that this laziness means that this implementation
19
+ /// sometimes overflows in places where an eager reduced implementation would not - to achieve
20
+ /// the same behavior as an eager implementation simply call `reduce` after each operation.
12
21
  pub fn Rational(comptime T: type) type {
13
22
  return extern struct {
14
23
  const Self = @This();
@@ -18,19 +27,19 @@ pub fn Rational(comptime T: type) type {
18
27
  /// Denominator. Must always be >= 1. Not guaranteed to be reduced in all cases.
19
28
  q: T = 1,
20
29
 
21
- // With floats we can't rely on an overflow bit to let us know when to reduce, so we instead
22
- // start reducing when we get sufficiently close to the limit of the mantissa (in our domain
23
- // we expect updates to involve numbers < 2**10, so we should be safe not reducing before we
24
- // are 2**10 away from "overflowing" the mantissa)
25
- const REDUCE = if (@typeInfo(T) == .Float)
30
+ // With floats we can't rely on an overflow bit to let us know when to reduce, so we
31
+ // instead start reducing when we get sufficiently close to the limit of the mantissa
32
+ // (in our domain we expect updates to involve numbers < 2**10, so we should be safe
33
+ // not reducing before we are 2**10 away from "overflowing" the mantissa)
34
+ const REDUCE = if (@typeInfo(T) == Float)
26
35
  std.math.pow(T, 2, std.math.floatMantissaBits(T) - 10)
27
36
  else
28
37
  0;
29
38
 
30
39
  /// Possible error returned by operations on the Rational.
31
40
  pub const Error = switch (@typeInfo(T)) {
32
- .Int => error{Overflow},
33
- .Float => error{},
41
+ Int => error{Overflow},
42
+ Float => error{},
34
43
  else => unreachable,
35
44
  };
36
45
 
@@ -50,12 +59,12 @@ pub fn Rational(comptime T: type) type {
50
59
  // If our parameters are not fully reduced they may prematurely
51
60
  // cause overflow/loss of precision after the multiplication below
52
61
  assert(switch (@typeInfo(@TypeOf(p, q))) {
53
- .ComptimeInt, .ComptimeFloat => comptime gcd(p, q),
62
+ ComptimeInt, ComptimeFloat => comptime gcd(p, q),
54
63
  else => 1,
55
64
  } == 1);
56
65
 
57
66
  switch (@typeInfo(T)) {
58
- .Int => {
67
+ Int => {
59
68
  // Greedily attempt to multiply and if it fails, reduce and try again
60
69
  r.multiplication(p, q) catch |err| switch (err) {
61
70
  error.Overflow => {
@@ -65,16 +74,16 @@ pub fn Rational(comptime T: type) type {
65
74
  else => unreachable,
66
75
  };
67
76
  },
68
- .Float => {
77
+ Float => {
69
78
  // Reduce in situations where we're likely to start losing precision
70
79
  if (r.q > REDUCE or r.p > REDUCE) r.reduce();
71
80
 
72
81
  r.p *= switch (@typeInfo(@TypeOf(p))) {
73
- .Float, .ComptimeFloat => p,
82
+ Float, ComptimeFloat => p,
74
83
  else => @floatFromInt(p),
75
84
  };
76
85
  r.q *= switch (@typeInfo(@TypeOf(q))) {
77
- .Float, .ComptimeFloat => q,
86
+ Float, ComptimeFloat => q,
78
87
  else => @floatFromInt(q),
79
88
  };
80
89
 
@@ -89,7 +98,7 @@ pub fn Rational(comptime T: type) type {
89
98
  /// Add two rationals using the identity (a/b) + (c/d) = (ad+bc)/(bd).
90
99
  pub fn add(r: *Self, s: *Self) Error!void {
91
100
  switch (@typeInfo(T)) {
92
- .Int => {
101
+ Int => {
93
102
  if (r.q == s.q) {
94
103
  r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
95
104
  error.Overflow => val: {
@@ -110,7 +119,7 @@ pub fn Rational(comptime T: type) type {
110
119
  };
111
120
  }
112
121
  },
113
- .Float => {
122
+ Float => {
114
123
  if (r.q == s.q) {
115
124
  if (r.p > REDUCE) r.reduce();
116
125
  if (s.p > REDUCE) s.reduce();
@@ -135,7 +144,7 @@ pub fn Rational(comptime T: type) type {
135
144
  /// Multiplies two rationals.
136
145
  pub fn mul(r: *Self, s: *Self) Error!void {
137
146
  switch (@typeInfo(T)) {
138
- .Int => {
147
+ Int => {
139
148
  r.multiplication(s.p, s.q) catch |err| switch (err) {
140
149
  error.Overflow => {
141
150
  r.reduce();
@@ -145,7 +154,7 @@ pub fn Rational(comptime T: type) type {
145
154
  else => unreachable,
146
155
  };
147
156
  },
148
- .Float => {
157
+ Float => {
149
158
  if (r.q > REDUCE or r.p > REDUCE) r.reduce();
150
159
  if (s.q > REDUCE or s.p > REDUCE) s.reduce();
151
160
 
@@ -201,46 +210,37 @@ pub fn Rational(comptime T: type) type {
201
210
  };
202
211
  }
203
212
 
204
- // https://lemire.me/blog/greatest-common-divisor-the-extended-euclidean-algorithm-and-speed/
205
213
  fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
206
214
  assert(p >= 1);
207
215
  assert(q >= 1);
208
216
 
209
- const T = @TypeOf(p, q);
217
+ // convert comptime_int to a sized integer within this function so that @ctz will work
218
+ const T = switch (@TypeOf(p, q)) {
219
+ comptime_int => std.math.IntFittingRange(@min(p, q), @max(p, q)),
220
+ else => |U| U,
221
+ };
222
+
210
223
  switch (@typeInfo(T)) {
211
- .Int => {
212
- var u: T = undefined;
213
- var v: T = undefined;
214
- if (p < q) {
215
- u = q;
216
- v = p;
217
- } else {
218
- u = p;
219
- v = q;
220
- }
221
- assert(v <= u);
222
- u %= v;
223
- if (u == 0) return v;
224
-
225
- const zu = @ctz(u);
226
- const zv = @ctz(v);
227
- const shift = @min(zu, zv);
228
- u >>= @intCast(zu);
229
- v >>= @intCast(zv);
230
-
231
- while (true) {
232
- const diff = u -% v;
233
- if (u > v) {
234
- u = v;
235
- v = diff;
236
- } else {
237
- v -= u;
238
- }
239
- if (diff != 0) v >>= @intCast(@ctz(diff));
240
- if (v == 0) break;
224
+ Int => {
225
+ // std.math.gcd but without some checks because we have a stricter range
226
+ var x: T = @intCast(p);
227
+ var y: T = @intCast(q);
228
+
229
+ const xz = @ctz(x);
230
+ const yz = @ctz(y);
231
+ const shift = @min(xz, yz);
232
+ x >>= @intCast(xz);
233
+ y >>= @intCast(yz);
234
+
235
+ var diff = y -% x;
236
+ while (diff != 0) : (diff = y -% x) {
237
+ const zeros = @ctz(diff);
238
+ if (x > y) diff = -%diff;
239
+ y = @min(x, y);
240
+ x = diff >> @intCast(zeros);
241
241
  }
242
242
 
243
- const result = u << @intCast(shift);
243
+ const result = y << @intCast(shift);
244
244
  assert(result > 0);
245
245
  return result;
246
246
  },
@@ -263,7 +263,7 @@ fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
263
263
 
264
264
  test gcd {
265
265
  const seed = if (@hasDecl(std.testing, "random_seed")) std.testing.random_seed else 0x12345678;
266
- var prng = std.Random.DefaultPrng.init(seed);
266
+ var prng = (if (@hasDecl(std, "Random")) std.Random else std.rand).DefaultPrng.init(seed);
267
267
  var random = prng.random();
268
268
 
269
269
  for (0..1000) |_| {
@@ -276,6 +276,8 @@ test gcd {
276
276
  @as(u32, @intFromFloat(gcd(@as(f64, @floatFromInt(a)), @as(f64, @floatFromInt(b))))),
277
277
  );
278
278
  }
279
+
280
+ try expectEqual(gcd(300_000, @as(u32, 2_300_000)), 100_000); // NB: @ctz requires an @intCast
279
281
  }
280
282
 
281
283
  fn doTurn(r: anytype) !void {
@@ -1,10 +1,8 @@
1
- const std = @import("std");
2
-
3
1
  const options = @import("options.zig");
2
+ const std = @import("std");
4
3
 
5
4
  const assert = std.debug.assert;
6
5
  const expectEqual = std.testing.expectEqual;
7
-
8
6
  const showdown = options.showdown;
9
7
 
10
8
  pub fn PRNG(comptime gen: comptime_int) type {
@@ -74,7 +72,7 @@ pub const PSRNG = extern struct {
74
72
 
75
73
  test PSRNG {
76
74
  var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
77
- try expectEqual(121, psrng.range(u8, 0, 256));
75
+ try expectEqual(@as(u8, 121), psrng.range(u8, 0, 256));
78
76
  try expectEqual(false, psrng.chance(u8, 128, 256)); // 226 < 128
79
77
  }
80
78
 
@@ -111,9 +109,9 @@ test Gen12 {
111
109
  try expectEqual(e, rng.next());
112
110
  }
113
111
 
114
- try expectEqual(16, Gen12.percent(6) + 1);
115
- try expectEqual(16, Gen12.percent(7) - 1);
116
- try expectEqual(128, Gen12.percent(50) + 1);
112
+ try expectEqual(@as(u8, 16), Gen12.percent(6) + 1);
113
+ try expectEqual(@as(u8, 16), Gen12.percent(7) - 1);
114
+ try expectEqual(@as(u8, 128), Gen12.percent(50) + 1);
117
115
  }
118
116
 
119
117
  // https://pkmn.cc/pokeemerald/src/random.c
@@ -1,12 +1,61 @@
1
1
  const std = @import("std");
2
2
 
3
+ const expect = std.testing.expect;
3
4
  const expectEqual = std.testing.expectEqual;
4
5
 
6
+ const Pointer = if (@hasField(std.builtin.Type, "pointer")) .pointer else .Pointer;
7
+
5
8
  pub fn PointerType(comptime P: type, comptime C: type) type {
6
- return if (@typeInfo(P).Pointer.is_const) *const C else *C;
9
+ return if (@field(@typeInfo(P), @tagName(Pointer)).is_const) *const C else *C;
7
10
  }
8
11
 
9
12
  test PointerType {
10
13
  try expectEqual(*bool, PointerType(*u8, bool));
11
14
  try expectEqual(*const f64, PointerType(*const i32, f64));
12
15
  }
16
+
17
+ pub fn isPointerTo(p: anytype, comptime P: type) bool {
18
+ const info = @typeInfo(@TypeOf(p));
19
+ return switch (info) {
20
+ Pointer => @field(info, @tagName(Pointer)).child == P,
21
+ else => false,
22
+ };
23
+ }
24
+
25
+ test isPointerTo {
26
+ const S = struct {};
27
+ const s: S = .{};
28
+ try expect(!isPointerTo(s, S));
29
+ try expect(isPointerTo(&s, S));
30
+ }
31
+
32
+ // NOTE: std.mem.bytesAsValue backported from ziglang/zig#18061
33
+ pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
34
+ return @as(BytesAsValueReturnType(T, @TypeOf(bytes)), @ptrCast(bytes));
35
+ }
36
+
37
+ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
38
+ return CopyPtrAttrs(B, .One, T);
39
+ }
40
+
41
+ fn CopyPtrAttrs(
42
+ comptime source: type,
43
+ comptime size: std.builtin.Type.Pointer.Size,
44
+ comptime child: type,
45
+ ) type {
46
+ const info = @field(@typeInfo(source), @tagName(Pointer));
47
+ const args = .{
48
+ .size = size,
49
+ .is_const = info.is_const,
50
+ .is_volatile = info.is_volatile,
51
+ .is_allowzero = info.is_allowzero,
52
+ .alignment = info.alignment,
53
+ .address_space = info.address_space,
54
+ .child = child,
55
+ .sentinel = null,
56
+ };
57
+ return @Type(if (@hasField(std.builtin.Type, "pointer"))
58
+ .{ .pointer = args }
59
+ else
60
+ .{ .Pointer = args });
61
+ }