@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.
- package/README.md +9 -6
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +97 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +44 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -3
- package/build/pkg/addon.js +34 -65
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +13 -17
- package/build/pkg/gen1.js +6 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +38 -90
- package/build/pkg/index.js +6 -63
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +35 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +141 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +254 -77
- package/package.json +50 -134
- package/src/bin/install-pkmn-engine +9 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +45 -44
- package/src/lib/common/rational.zig +53 -51
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +295 -154
- package/src/lib/gen1/chance.zig +477 -119
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +1 -2
- package/src/lib/gen1/data.zig +35 -38
- package/src/lib/gen1/helpers.zig +17 -27
- package/src/lib/gen1/mechanics.zig +248 -148
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +87 -67
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +1 -2
- package/src/lib/gen2/data.zig +26 -29
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +9 -18
- package/src/lib/gen2/mechanics.zig +81 -81
- package/src/lib/node.zig +2 -4
- package/src/lib/pkmn.zig +11 -5
- package/src/lib/wasm.zig +36 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- 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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
997
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1349
|
-
|
|
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
|
-
|
|
1360
|
-
|
|
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
|
-
|
|
1369
|
-
|
|
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
|
-
|
|
1388
|
-
|
|
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
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1443
|
-
|
|
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
|
-
|
|
1454
|
-
|
|
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
|
-
|
|
1463
|
-
|
|
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
|
-
|
|
1476
|
-
|
|
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
|
-
|
|
1487
|
-
|
|
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
|
-
|
|
1495
|
-
|
|
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
|
-
|
|
1739
|
-
|
|
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
|
-
|
|
1773
|
-
|
|
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
|
|
22
|
-
// start reducing when we get sufficiently close to the limit of the mantissa
|
|
23
|
-
// we expect updates to involve numbers < 2**10, so we should be safe
|
|
24
|
-
// are 2**10 away from "overflowing" the mantissa)
|
|
25
|
-
const REDUCE = if (@typeInfo(T) ==
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
62
|
+
ComptimeInt, ComptimeFloat => comptime gcd(p, q),
|
|
54
63
|
else => 1,
|
|
55
64
|
} == 1);
|
|
56
65
|
|
|
57
66
|
switch (@typeInfo(T)) {
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
Float, ComptimeFloat => p,
|
|
74
83
|
else => @floatFromInt(p),
|
|
75
84
|
};
|
|
76
85
|
r.q *= switch (@typeInfo(@TypeOf(q))) {
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
var
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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 =
|
|
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 {
|
package/src/lib/common/rng.zig
CHANGED
|
@@ -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
|
package/src/lib/common/util.zig
CHANGED
|
@@ -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)
|
|
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
|
+
}
|