@pkmn/engine 0.1.0-dev.5570b5c5 → 0.1.0-dev.565de616
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 +12 -8
- 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 +21 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +22 -0
- package/build/pkg/addon.js +29 -51
- 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 +10 -13
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +53 -12
- package/build/pkg/index.js +15 -10
- 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 -68
- 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 +230 -74
- package/package.json +51 -31
- package/src/bin/install-pkmn-engine +8 -6
- 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 +6 -0
- package/src/lib/common/protocol.zig +45 -44
- package/src/lib/common/rational.zig +121 -29
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +683 -156
- package/src/lib/gen1/chance.zig +415 -291
- 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 +38 -41
- package/src/lib/gen1/helpers.zig +92 -548
- package/src/lib/gen1/mechanics.zig +325 -235
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +90 -503
- 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 +46 -25
- package/src/lib/gen2/mechanics.zig +69 -107
- package/src/lib/node.zig +2 -4
- package/src/lib/pkmn.zig +14 -16
- package/src/lib/wasm.zig +1 -2
- 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
|
|
|
@@ -43,20 +52,19 @@ pub fn Rational(comptime T: type) type {
|
|
|
43
52
|
/// Update the rational by multiplying its numerator by p and its denominator by q.
|
|
44
53
|
/// Both p and q must be >= 1, and if computable at comptime must have no common factors.
|
|
45
54
|
pub fn update(r: *Self, p: anytype, q: anytype) Error!void {
|
|
46
|
-
//
|
|
47
|
-
// std.debug.print("({d}/{d}) * ", .{ p, q });
|
|
55
|
+
// std.debug.print("({d}/{d}) * ", .{ p, q }); // DEBUG
|
|
48
56
|
assert(p >= 1);
|
|
49
57
|
assert(q >= 1);
|
|
50
58
|
|
|
51
59
|
// If our parameters are not fully reduced they may prematurely
|
|
52
60
|
// cause overflow/loss of precision after the multiplication below
|
|
53
61
|
assert(switch (@typeInfo(@TypeOf(p, q))) {
|
|
54
|
-
|
|
62
|
+
ComptimeInt, ComptimeFloat => comptime gcd(p, q),
|
|
55
63
|
else => 1,
|
|
56
64
|
} == 1);
|
|
57
65
|
|
|
58
66
|
switch (@typeInfo(T)) {
|
|
59
|
-
|
|
67
|
+
Int => {
|
|
60
68
|
// Greedily attempt to multiply and if it fails, reduce and try again
|
|
61
69
|
r.multiplication(p, q) catch |err| switch (err) {
|
|
62
70
|
error.Overflow => {
|
|
@@ -66,16 +74,16 @@ pub fn Rational(comptime T: type) type {
|
|
|
66
74
|
else => unreachable,
|
|
67
75
|
};
|
|
68
76
|
},
|
|
69
|
-
|
|
77
|
+
Float => {
|
|
70
78
|
// Reduce in situations where we're likely to start losing precision
|
|
71
79
|
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
72
80
|
|
|
73
81
|
r.p *= switch (@typeInfo(@TypeOf(p))) {
|
|
74
|
-
|
|
82
|
+
Float, ComptimeFloat => p,
|
|
75
83
|
else => @floatFromInt(p),
|
|
76
84
|
};
|
|
77
85
|
r.q *= switch (@typeInfo(@TypeOf(q))) {
|
|
78
|
-
|
|
86
|
+
Float, ComptimeFloat => q,
|
|
79
87
|
else => @floatFromInt(q),
|
|
80
88
|
};
|
|
81
89
|
|
|
@@ -90,7 +98,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
90
98
|
/// Add two rationals using the identity (a/b) + (c/d) = (ad+bc)/(bd).
|
|
91
99
|
pub fn add(r: *Self, s: *Self) Error!void {
|
|
92
100
|
switch (@typeInfo(T)) {
|
|
93
|
-
|
|
101
|
+
Int => {
|
|
94
102
|
if (r.q == s.q) {
|
|
95
103
|
r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
|
|
96
104
|
error.Overflow => val: {
|
|
@@ -111,7 +119,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
111
119
|
};
|
|
112
120
|
}
|
|
113
121
|
},
|
|
114
|
-
|
|
122
|
+
Float => {
|
|
115
123
|
if (r.q == s.q) {
|
|
116
124
|
if (r.p > REDUCE) r.reduce();
|
|
117
125
|
if (s.p > REDUCE) s.reduce();
|
|
@@ -136,7 +144,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
136
144
|
/// Multiplies two rationals.
|
|
137
145
|
pub fn mul(r: *Self, s: *Self) Error!void {
|
|
138
146
|
switch (@typeInfo(T)) {
|
|
139
|
-
|
|
147
|
+
Int => {
|
|
140
148
|
r.multiplication(s.p, s.q) catch |err| switch (err) {
|
|
141
149
|
error.Overflow => {
|
|
142
150
|
r.reduce();
|
|
@@ -146,7 +154,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
146
154
|
else => unreachable,
|
|
147
155
|
};
|
|
148
156
|
},
|
|
149
|
-
|
|
157
|
+
Float => {
|
|
150
158
|
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
151
159
|
if (s.q > REDUCE or s.p > REDUCE) s.reduce();
|
|
152
160
|
|
|
@@ -202,23 +210,74 @@ pub fn Rational(comptime T: type) type {
|
|
|
202
210
|
};
|
|
203
211
|
}
|
|
204
212
|
|
|
205
|
-
// https://en.wikipedia.org/wiki/Euclidean_algorithm
|
|
206
213
|
fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
|
|
207
214
|
assert(p >= 1);
|
|
208
215
|
assert(q >= 1);
|
|
209
216
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
+
|
|
223
|
+
switch (@typeInfo(T)) {
|
|
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
|
+
}
|
|
213
242
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
243
|
+
const result = y << @intCast(shift);
|
|
244
|
+
assert(result > 0);
|
|
245
|
+
return result;
|
|
246
|
+
},
|
|
247
|
+
else => {
|
|
248
|
+
var a = p;
|
|
249
|
+
var b = q;
|
|
250
|
+
var c: T = undefined;
|
|
251
|
+
|
|
252
|
+
while (b != 0) {
|
|
253
|
+
c = b;
|
|
254
|
+
b = @mod(a, b);
|
|
255
|
+
a = c;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
assert(a > 0);
|
|
259
|
+
return a;
|
|
260
|
+
},
|
|
218
261
|
}
|
|
262
|
+
}
|
|
219
263
|
|
|
220
|
-
|
|
221
|
-
|
|
264
|
+
test gcd {
|
|
265
|
+
const seed = if (@hasDecl(std.testing, "random_seed")) std.testing.random_seed else 0x12345678;
|
|
266
|
+
var prng = (if (@hasDecl(std, "Random")) std.Random else std.rand).DefaultPrng.init(seed);
|
|
267
|
+
var random = prng.random();
|
|
268
|
+
|
|
269
|
+
for (0..1000) |_| {
|
|
270
|
+
const a = random.int(u32);
|
|
271
|
+
const b = random.int(u32);
|
|
272
|
+
if (a == 0 or b == 0) continue;
|
|
273
|
+
|
|
274
|
+
try expectEqual(
|
|
275
|
+
gcd(a, b),
|
|
276
|
+
@as(u32, @intFromFloat(gcd(@as(f64, @floatFromInt(a)), @as(f64, @floatFromInt(b))))),
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try expectEqual(gcd(300_000, @as(u32, 2_300_000)), 100_000); // NB: @ctz requires an @intCast
|
|
222
281
|
}
|
|
223
282
|
|
|
224
283
|
fn doTurn(r: anytype) !void {
|
|
@@ -355,3 +414,36 @@ test BigRational {
|
|
|
355
414
|
try s.setRatio(71, 78);
|
|
356
415
|
try expect((try s.order(r.val)) == .eq);
|
|
357
416
|
}
|
|
417
|
+
|
|
418
|
+
test "minimum" {
|
|
419
|
+
// TODO: ziglang/zig#21069
|
|
420
|
+
if (builtin.mode == .ReleaseFast or builtin.mode == .ReleaseSmall) {
|
|
421
|
+
return error.SkipZigTest;
|
|
422
|
+
}
|
|
423
|
+
var r = try BigRational.init(std.testing.allocator);
|
|
424
|
+
defer r.deinit();
|
|
425
|
+
var s = try std.math.big.Rational.init(std.testing.allocator);
|
|
426
|
+
defer s.deinit();
|
|
427
|
+
|
|
428
|
+
try r.reset();
|
|
429
|
+
|
|
430
|
+
try r.update(1, 2); // Speed Tie
|
|
431
|
+
for (0..2) |_| {
|
|
432
|
+
try r.update(1, 2); // Confusion
|
|
433
|
+
try r.update(1, 2); // Infatuation
|
|
434
|
+
try r.update(3, 4); // Paralysis
|
|
435
|
+
// Population Bomb
|
|
436
|
+
for (0..10) |_| {
|
|
437
|
+
try r.update(9, 10); // Accuracy
|
|
438
|
+
try r.update(1, 15); // Damage roll
|
|
439
|
+
try r.update(1, 24); // Critical Hit
|
|
440
|
+
try r.update(9, 10); // King's Rock non-proc
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const ZEROS = std.math.pow(u310, 10, 31);
|
|
445
|
+
const n = 36472996377170786403;
|
|
446
|
+
const d: u310 = 146267071761884981524886149560653378220687632500762654519853056 * ZEROS;
|
|
447
|
+
try s.setRatio(n, d);
|
|
448
|
+
try expect((try s.order(r.val)) == .eq);
|
|
449
|
+
}
|
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
|
+
}
|