@pkmn/engine 0.1.0-dev.eaae4aa6 → 0.1.0-dev.f34af416
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/LICENSE +0 -2
- package/README.md +15 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +2 -3
- package/build/pkg/index.d.ts +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +189 -72
- package/package.json +17 -16
- package/src/bin/install-pkmn-engine +17 -7
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +51 -44
- package/src/lib/common/rational.zig +114 -24
- package/src/lib/common/rng.zig +10 -10
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +508 -128
- package/src/lib/gen1/chance.zig +383 -276
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +29 -28
- package/src/lib/gen1/helpers.zig +96 -536
- package/src/lib/gen1/mechanics.zig +266 -160
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +91 -500
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +41 -9
- package/src/lib/gen2/mechanics.zig +158 -97
- package/src/lib/pkmn.zig +10 -16
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -3,10 +3,12 @@ const std = @import("std");
|
|
|
3
3
|
const expect = std.testing.expect;
|
|
4
4
|
const assert = std.debug.assert;
|
|
5
5
|
|
|
6
|
+
const Bool = if (@hasField(std.builtin.Type, "bool")) .bool else .Bool;
|
|
7
|
+
|
|
6
8
|
// TODO: ziglang/zig#104
|
|
7
9
|
pub fn Optional(comptime T: type) type {
|
|
8
10
|
const fields = std.meta.fields(switch (@typeInfo(T)) {
|
|
9
|
-
|
|
11
|
+
Bool => enum { false, true },
|
|
10
12
|
else => T,
|
|
11
13
|
});
|
|
12
14
|
|
|
@@ -23,14 +25,16 @@ pub fn Optional(comptime T: type) type {
|
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const options = .{
|
|
29
|
+
.tag_type = std.math.IntFittingRange(0, fields.len),
|
|
30
|
+
.fields = &enumFields,
|
|
31
|
+
.decls = &decls,
|
|
32
|
+
.is_exhaustive = true,
|
|
33
|
+
};
|
|
34
|
+
return @Type(if (@hasField(std.builtin.Type, "enum"))
|
|
35
|
+
.{ .@"enum" = options }
|
|
36
|
+
else
|
|
37
|
+
.{ .Enum = options });
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
test Optional {
|
|
@@ -10,6 +10,7 @@ pub const Options = struct {
|
|
|
10
10
|
advance: ?bool = null,
|
|
11
11
|
ebc: ?bool = null,
|
|
12
12
|
key: ?bool = null,
|
|
13
|
+
miss: ?bool = null,
|
|
13
14
|
internal: ?bool = null,
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -25,6 +26,7 @@ pub const calc = get("calc", false);
|
|
|
25
26
|
pub const advance = get("advance", true);
|
|
26
27
|
pub const ebc = get("ebc", true);
|
|
27
28
|
pub const key = get("key", false);
|
|
29
|
+
pub const miss = get("miss", true);
|
|
28
30
|
pub const internal = get("internal", false);
|
|
29
31
|
|
|
30
32
|
fn get(comptime name: []const u8, default: bool) bool {
|
|
@@ -15,6 +15,11 @@ const Player = data.Player;
|
|
|
15
15
|
const ID = data.ID;
|
|
16
16
|
|
|
17
17
|
const endian = builtin.cpu.arch.endian();
|
|
18
|
+
const Endian = std.builtin.Endian;
|
|
19
|
+
const big = if (@hasField(Endian, "big")) Endian.big else Endian.Big;
|
|
20
|
+
const little = if (@hasField(Endian, "little")) Endian.little else Endian.Little;
|
|
21
|
+
|
|
22
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
18
23
|
|
|
19
24
|
pub const ArgType = enum(u8) {
|
|
20
25
|
None,
|
|
@@ -220,6 +225,7 @@ pub const Activate = enum(u8) {
|
|
|
220
225
|
BeatUp, // FIXME of
|
|
221
226
|
|
|
222
227
|
Magnitude,
|
|
228
|
+
Spite,
|
|
223
229
|
};
|
|
224
230
|
|
|
225
231
|
pub const Start = enum(u8) {
|
|
@@ -544,6 +550,7 @@ pub fn Log(comptime Writer: type) type {
|
|
|
544
550
|
// ident: ID, reason: Activate
|
|
545
551
|
// ident: ID, reason: .SubstituteBlock, m: <Move>
|
|
546
552
|
// ident: ID, reason: .Magnitude, num: u8
|
|
553
|
+
// ident: ID, reason: .Spite, m: <Move>, num: u8
|
|
547
554
|
pub fn activate(self: Self, args: anytype) Error!void {
|
|
548
555
|
if (!enabled) return;
|
|
549
556
|
|
|
@@ -561,6 +568,9 @@ pub fn Log(comptime Writer: type) type {
|
|
|
561
568
|
assert(reason == .Magnitude);
|
|
562
569
|
try self.writer.writeByte(args[2]);
|
|
563
570
|
}
|
|
571
|
+
} else if (args.len == 4) {
|
|
572
|
+
assert(reason == .Spite);
|
|
573
|
+
try self.writer.writeAll(&.{ @intFromEnum(args[2]), args[3] });
|
|
564
574
|
}
|
|
565
575
|
}
|
|
566
576
|
|
|
@@ -835,7 +845,7 @@ pub const ByteStream = struct {
|
|
|
835
845
|
|
|
836
846
|
pub fn writeInt(self: Writer, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
|
|
837
847
|
// TODO: rework this to write directly to the buffer?
|
|
838
|
-
var bytes: [@divExact(@typeInfo(T)
|
|
848
|
+
var bytes: [@divExact(@field(@typeInfo(T), @tagName(Int)).bits, 8)]u8 = undefined;
|
|
839
849
|
std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(v)), &bytes, v, end);
|
|
840
850
|
return self.writeAll(&bytes);
|
|
841
851
|
}
|
|
@@ -859,7 +869,7 @@ pub const ByteStream = struct {
|
|
|
859
869
|
pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
|
|
860
870
|
|
|
861
871
|
pub fn format(
|
|
862
|
-
|
|
872
|
+
gen: anytype,
|
|
863
873
|
a: []const u8,
|
|
864
874
|
b: ?[]const u8,
|
|
865
875
|
color: bool,
|
|
@@ -951,13 +961,13 @@ pub fn format(
|
|
|
951
961
|
}
|
|
952
962
|
printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
|
|
953
963
|
switch (endian) {
|
|
954
|
-
|
|
964
|
+
big => {
|
|
955
965
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
956
966
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
957
967
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
958
968
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
959
969
|
},
|
|
960
|
-
|
|
970
|
+
little => {
|
|
961
971
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
962
972
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
963
973
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -988,8 +998,8 @@ pub fn format(
|
|
|
988
998
|
},
|
|
989
999
|
.Turn => {
|
|
990
1000
|
const turn = switch (endian) {
|
|
991
|
-
|
|
992
|
-
|
|
1001
|
+
big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
|
|
1002
|
+
little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
|
|
993
1003
|
};
|
|
994
1004
|
printc(" {d}", .{turn}, a, b, &i, 2, color);
|
|
995
1005
|
},
|
|
@@ -1000,13 +1010,13 @@ pub fn format(
|
|
|
1000
1010
|
var id = ID.from(@intCast(a[i]));
|
|
1001
1011
|
printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
|
|
1002
1012
|
switch (endian) {
|
|
1003
|
-
|
|
1013
|
+
big => {
|
|
1004
1014
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1005
1015
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1006
1016
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1007
1017
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
1008
1018
|
},
|
|
1009
|
-
|
|
1019
|
+
little => {
|
|
1010
1020
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
1011
1021
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1012
1022
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -1128,13 +1138,13 @@ pub fn format(
|
|
|
1128
1138
|
const id = ID.from(@intCast(a[i]));
|
|
1129
1139
|
printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
|
|
1130
1140
|
switch (endian) {
|
|
1131
|
-
|
|
1141
|
+
big => {
|
|
1132
1142
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1133
1143
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1134
1144
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1135
1145
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
1136
1146
|
},
|
|
1137
|
-
|
|
1147
|
+
little => {
|
|
1138
1148
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
1139
1149
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1140
1150
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -1202,14 +1212,11 @@ pub fn expectLog(
|
|
|
1202
1212
|
) !void {
|
|
1203
1213
|
if (!enabled) return;
|
|
1204
1214
|
|
|
1205
|
-
const color =
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
} else {
|
|
1211
|
-
break :color std.io.getStdErr().supportsAnsiEscapeCodes();
|
|
1212
|
-
}
|
|
1215
|
+
const color = if (std.process.hasEnvVarConstant("ZIG_DEBUG_COLOR"))
|
|
1216
|
+
true
|
|
1217
|
+
else switch (std.io.tty.detectConfig(std.io.getStdErr())) {
|
|
1218
|
+
.escape_codes => true,
|
|
1219
|
+
else => false,
|
|
1213
1220
|
};
|
|
1214
1221
|
|
|
1215
1222
|
expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
|
|
@@ -1343,8 +1350,8 @@ test "|switch|" {
|
|
|
1343
1350
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1344
1351
|
const par = 0b1000000;
|
|
1345
1352
|
var expected: []const u8 = switch (endian) {
|
|
1346
|
-
|
|
1347
|
-
|
|
1353
|
+
big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 0, 200, 1, 144, par },
|
|
1354
|
+
little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 91, 200, 0, 144, 1, par },
|
|
1348
1355
|
};
|
|
1349
1356
|
try expectLog1(expected, buf[0..9]);
|
|
1350
1357
|
stream.reset();
|
|
@@ -1354,8 +1361,8 @@ test "|switch|" {
|
|
|
1354
1361
|
snorlax.status = 0;
|
|
1355
1362
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1356
1363
|
expected = switch (endian) {
|
|
1357
|
-
|
|
1358
|
-
|
|
1364
|
+
big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 1, 144, 0 },
|
|
1365
|
+
little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 0, 0, 144, 1, 0 },
|
|
1359
1366
|
};
|
|
1360
1367
|
try expectLog1(expected, buf[0..9]);
|
|
1361
1368
|
stream.reset();
|
|
@@ -1363,8 +1370,8 @@ test "|switch|" {
|
|
|
1363
1370
|
snorlax.hp = 400;
|
|
1364
1371
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1365
1372
|
expected = switch (endian) {
|
|
1366
|
-
|
|
1367
|
-
|
|
1373
|
+
big => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 1, 144, 1, 144, 0 },
|
|
1374
|
+
little => &.{ N(ArgType.Switch), 0b1011, N(S1.Snorlax), 100, 144, 1, 144, 1, 0 },
|
|
1368
1375
|
};
|
|
1369
1376
|
try expectLog1(expected, buf[0..9]);
|
|
1370
1377
|
stream.reset();
|
|
@@ -1382,8 +1389,8 @@ test "|switch|" {
|
|
|
1382
1389
|
N(gen2.Gender.Female),
|
|
1383
1390
|
91,
|
|
1384
1391
|
} ++ switch (endian) {
|
|
1385
|
-
|
|
1386
|
-
|
|
1392
|
+
big => .{ 0, 200, 1, 144, par },
|
|
1393
|
+
little => .{ 200, 0, 144, 1, par },
|
|
1387
1394
|
});
|
|
1388
1395
|
try expectLog2(expected, buf[0..10]);
|
|
1389
1396
|
stream.reset();
|
|
@@ -1412,8 +1419,8 @@ test "|faint|" {
|
|
|
1412
1419
|
test "|turn|" {
|
|
1413
1420
|
try log.turn(.{42});
|
|
1414
1421
|
const expected = switch (endian) {
|
|
1415
|
-
|
|
1416
|
-
|
|
1422
|
+
big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
|
|
1423
|
+
little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
|
|
1417
1424
|
};
|
|
1418
1425
|
try expectLog1(expected, buf[0..4]);
|
|
1419
1426
|
stream.reset();
|
|
@@ -1437,8 +1444,8 @@ test "|-damage|" {
|
|
|
1437
1444
|
chansey.status = gen1.Status.slp(1);
|
|
1438
1445
|
try log.damage(.{ p2.ident(2), &chansey, .None });
|
|
1439
1446
|
var expected: []const u8 = switch (endian) {
|
|
1440
|
-
|
|
1441
|
-
|
|
1447
|
+
big => &.{ N(ArgType.Damage), 0b1010, 2, 100, 2, 191, 1, N(Damage.None) },
|
|
1448
|
+
little => &.{ N(ArgType.Damage), 0b1010, 100, 2, 191, 2, 1, N(Damage.None) },
|
|
1442
1449
|
};
|
|
1443
1450
|
try expectLog1(expected, buf[0..8]);
|
|
1444
1451
|
stream.reset();
|
|
@@ -1448,8 +1455,8 @@ test "|-damage|" {
|
|
|
1448
1455
|
chansey.status = 0;
|
|
1449
1456
|
try log.damage(.{ p2.ident(2), &chansey, .Confusion });
|
|
1450
1457
|
expected = switch (endian) {
|
|
1451
|
-
|
|
1452
|
-
|
|
1458
|
+
big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0, N(Damage.Confusion) },
|
|
1459
|
+
little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0, N(Damage.Confusion) },
|
|
1453
1460
|
};
|
|
1454
1461
|
try expectLog1(expected, buf[0..8]);
|
|
1455
1462
|
stream.reset();
|
|
@@ -1457,8 +1464,8 @@ test "|-damage|" {
|
|
|
1457
1464
|
chansey.status = gen1.Status.init(.PSN);
|
|
1458
1465
|
try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
|
|
1459
1466
|
expected = switch (endian) {
|
|
1460
|
-
|
|
1461
|
-
|
|
1467
|
+
big => &.{ N(ArgType.Damage), 0b1010, 0, 100, 1, 0, 0b1000, N(Damage.RecoilOf), 1 },
|
|
1468
|
+
little => &.{ N(ArgType.Damage), 0b1010, 100, 0, 0, 1, 0b1000, N(Damage.RecoilOf), 1 },
|
|
1462
1469
|
};
|
|
1463
1470
|
try expectLog1(expected, buf[0..9]);
|
|
1464
1471
|
stream.reset();
|
|
@@ -1470,8 +1477,8 @@ test "|-heal|" {
|
|
|
1470
1477
|
chansey.status = gen1.Status.slp(1);
|
|
1471
1478
|
try log.heal(.{ p2.ident(2), &chansey, .None });
|
|
1472
1479
|
var expected: []const u8 = switch (endian) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1480
|
+
big => &.{ N(ArgType.Heal), 0b1010, 2, 100, 2, 191, 1, N(Heal.None) },
|
|
1481
|
+
little => &.{ N(ArgType.Heal), 0b1010, 100, 2, 191, 2, 1, N(Heal.None) },
|
|
1475
1482
|
};
|
|
1476
1483
|
try expectLog1(expected, buf[0..8]);
|
|
1477
1484
|
stream.reset();
|
|
@@ -1481,16 +1488,16 @@ test "|-heal|" {
|
|
|
1481
1488
|
chansey.status = 0;
|
|
1482
1489
|
try log.heal(.{ p2.ident(2), &chansey, .Silent });
|
|
1483
1490
|
expected = switch (endian) {
|
|
1484
|
-
|
|
1485
|
-
|
|
1491
|
+
big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Silent) },
|
|
1492
|
+
little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Silent) },
|
|
1486
1493
|
};
|
|
1487
1494
|
try expectLog1(expected, buf[0..8]);
|
|
1488
1495
|
stream.reset();
|
|
1489
1496
|
|
|
1490
1497
|
try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
|
|
1491
1498
|
expected = switch (endian) {
|
|
1492
|
-
|
|
1493
|
-
|
|
1499
|
+
big => &.{ N(ArgType.Heal), 0b1010, 0, 100, 1, 0, 0, N(Heal.Drain), 1 },
|
|
1500
|
+
little => &.{ N(ArgType.Heal), 0b1010, 100, 0, 0, 1, 0, N(Heal.Drain), 1 },
|
|
1494
1501
|
};
|
|
1495
1502
|
try expectLog1(expected, buf[0..9]);
|
|
1496
1503
|
stream.reset();
|
|
@@ -1733,8 +1740,8 @@ test "|drag|" {
|
|
|
1733
1740
|
N(gen2.Gender.Female),
|
|
1734
1741
|
91,
|
|
1735
1742
|
} ++ switch (endian) {
|
|
1736
|
-
|
|
1737
|
-
|
|
1743
|
+
big => .{ 0, 200, 1, 144, par },
|
|
1744
|
+
little => .{ 200, 0, 144, 1, par },
|
|
1738
1745
|
});
|
|
1739
1746
|
try expectLog2(expected, buf[0..10]);
|
|
1740
1747
|
stream.reset();
|
|
@@ -1767,8 +1774,8 @@ test "|-sethp|" {
|
|
|
1767
1774
|
blissey.hp = 612;
|
|
1768
1775
|
try log.sethp(.{ p2.ident(2), &blissey, .Silent });
|
|
1769
1776
|
const expected: []const u8 = switch (endian) {
|
|
1770
|
-
|
|
1771
|
-
|
|
1777
|
+
big => &.{ N(ArgType.SetHP), 0b1010, 2, 100, 2, 201, 0, N(SetHP.Silent) },
|
|
1778
|
+
little => &.{ N(ArgType.SetHP), 0b1010, 100, 2, 201, 2, 0, N(SetHP.Silent) },
|
|
1772
1779
|
};
|
|
1773
1780
|
try expectLog2(expected, buf[0..8]);
|
|
1774
1781
|
stream.reset();
|
|
@@ -6,6 +6,13 @@ 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
18
|
/// invoked explicitly before reading.
|
|
@@ -22,15 +29,15 @@ pub fn Rational(comptime T: type) type {
|
|
|
22
29
|
// start reducing when we get sufficiently close to the limit of the mantissa (in our domain
|
|
23
30
|
// we expect updates to involve numbers < 2**10, so we should be safe not reducing before we
|
|
24
31
|
// are 2**10 away from "overflowing" the mantissa)
|
|
25
|
-
const REDUCE = if (@typeInfo(T) ==
|
|
32
|
+
const REDUCE = if (@typeInfo(T) == Float)
|
|
26
33
|
std.math.pow(T, 2, std.math.floatMantissaBits(T) - 10)
|
|
27
34
|
else
|
|
28
35
|
0;
|
|
29
36
|
|
|
30
37
|
/// Possible error returned by operations on the Rational.
|
|
31
38
|
pub const Error = switch (@typeInfo(T)) {
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
Int => error{Overflow},
|
|
40
|
+
Float => error{},
|
|
34
41
|
else => unreachable,
|
|
35
42
|
};
|
|
36
43
|
|
|
@@ -43,20 +50,19 @@ pub fn Rational(comptime T: type) type {
|
|
|
43
50
|
/// Update the rational by multiplying its numerator by p and its denominator by q.
|
|
44
51
|
/// Both p and q must be >= 1, and if computable at comptime must have no common factors.
|
|
45
52
|
pub fn update(r: *Self, p: anytype, q: anytype) Error!void {
|
|
46
|
-
//
|
|
47
|
-
// std.debug.print("({d}/{d}) * ", .{ p, q });
|
|
53
|
+
// std.debug.print("({d}/{d}) * ", .{ p, q }); // DEBUG
|
|
48
54
|
assert(p >= 1);
|
|
49
55
|
assert(q >= 1);
|
|
50
56
|
|
|
51
57
|
// If our parameters are not fully reduced they may prematurely
|
|
52
58
|
// cause overflow/loss of precision after the multiplication below
|
|
53
59
|
assert(switch (@typeInfo(@TypeOf(p, q))) {
|
|
54
|
-
|
|
60
|
+
ComptimeInt, ComptimeFloat => comptime gcd(p, q),
|
|
55
61
|
else => 1,
|
|
56
62
|
} == 1);
|
|
57
63
|
|
|
58
64
|
switch (@typeInfo(T)) {
|
|
59
|
-
|
|
65
|
+
Int => {
|
|
60
66
|
// Greedily attempt to multiply and if it fails, reduce and try again
|
|
61
67
|
r.multiplication(p, q) catch |err| switch (err) {
|
|
62
68
|
error.Overflow => {
|
|
@@ -66,16 +72,16 @@ pub fn Rational(comptime T: type) type {
|
|
|
66
72
|
else => unreachable,
|
|
67
73
|
};
|
|
68
74
|
},
|
|
69
|
-
|
|
75
|
+
Float => {
|
|
70
76
|
// Reduce in situations where we're likely to start losing precision
|
|
71
77
|
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
72
78
|
|
|
73
79
|
r.p *= switch (@typeInfo(@TypeOf(p))) {
|
|
74
|
-
|
|
80
|
+
Float, ComptimeFloat => p,
|
|
75
81
|
else => @floatFromInt(p),
|
|
76
82
|
};
|
|
77
83
|
r.q *= switch (@typeInfo(@TypeOf(q))) {
|
|
78
|
-
|
|
84
|
+
Float, ComptimeFloat => q,
|
|
79
85
|
else => @floatFromInt(q),
|
|
80
86
|
};
|
|
81
87
|
|
|
@@ -90,7 +96,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
90
96
|
/// Add two rationals using the identity (a/b) + (c/d) = (ad+bc)/(bd).
|
|
91
97
|
pub fn add(r: *Self, s: *Self) Error!void {
|
|
92
98
|
switch (@typeInfo(T)) {
|
|
93
|
-
|
|
99
|
+
Int => {
|
|
94
100
|
if (r.q == s.q) {
|
|
95
101
|
r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
|
|
96
102
|
error.Overflow => val: {
|
|
@@ -111,7 +117,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
111
117
|
};
|
|
112
118
|
}
|
|
113
119
|
},
|
|
114
|
-
|
|
120
|
+
Float => {
|
|
115
121
|
if (r.q == s.q) {
|
|
116
122
|
if (r.p > REDUCE) r.reduce();
|
|
117
123
|
if (s.p > REDUCE) s.reduce();
|
|
@@ -136,7 +142,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
136
142
|
/// Multiplies two rationals.
|
|
137
143
|
pub fn mul(r: *Self, s: *Self) Error!void {
|
|
138
144
|
switch (@typeInfo(T)) {
|
|
139
|
-
|
|
145
|
+
Int => {
|
|
140
146
|
r.multiplication(s.p, s.q) catch |err| switch (err) {
|
|
141
147
|
error.Overflow => {
|
|
142
148
|
r.reduce();
|
|
@@ -146,7 +152,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
146
152
|
else => unreachable,
|
|
147
153
|
};
|
|
148
154
|
},
|
|
149
|
-
|
|
155
|
+
Float => {
|
|
150
156
|
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
151
157
|
if (s.q > REDUCE or s.p > REDUCE) s.reduce();
|
|
152
158
|
|
|
@@ -202,23 +208,74 @@ pub fn Rational(comptime T: type) type {
|
|
|
202
208
|
};
|
|
203
209
|
}
|
|
204
210
|
|
|
205
|
-
// https://en.wikipedia.org/wiki/Euclidean_algorithm
|
|
206
211
|
fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
|
|
207
212
|
assert(p >= 1);
|
|
208
213
|
assert(q >= 1);
|
|
209
214
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
215
|
+
// convert comptime_int to a sized integer within this function so that @ctz will work
|
|
216
|
+
const T = switch (@TypeOf(p, q)) {
|
|
217
|
+
comptime_int => std.math.IntFittingRange(@min(p, q), @max(p, q)),
|
|
218
|
+
else => |U| U,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
switch (@typeInfo(T)) {
|
|
222
|
+
Int => {
|
|
223
|
+
// std.math.gcd but without some checks because we have a stricter range
|
|
224
|
+
var x: T = @intCast(p);
|
|
225
|
+
var y: T = @intCast(q);
|
|
226
|
+
|
|
227
|
+
const xz = @ctz(x);
|
|
228
|
+
const yz = @ctz(y);
|
|
229
|
+
const shift = @min(xz, yz);
|
|
230
|
+
x >>= @intCast(xz);
|
|
231
|
+
y >>= @intCast(yz);
|
|
232
|
+
|
|
233
|
+
var diff = y -% x;
|
|
234
|
+
while (diff != 0) : (diff = y -% x) {
|
|
235
|
+
const zeros = @ctz(diff);
|
|
236
|
+
if (x > y) diff = -%diff;
|
|
237
|
+
y = @min(x, y);
|
|
238
|
+
x = diff >> @intCast(zeros);
|
|
239
|
+
}
|
|
213
240
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
const result = y << @intCast(shift);
|
|
242
|
+
assert(result > 0);
|
|
243
|
+
return result;
|
|
244
|
+
},
|
|
245
|
+
else => {
|
|
246
|
+
var a = p;
|
|
247
|
+
var b = q;
|
|
248
|
+
var c: T = undefined;
|
|
249
|
+
|
|
250
|
+
while (b != 0) {
|
|
251
|
+
c = b;
|
|
252
|
+
b = @mod(a, b);
|
|
253
|
+
a = c;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
assert(a > 0);
|
|
257
|
+
return a;
|
|
258
|
+
},
|
|
218
259
|
}
|
|
260
|
+
}
|
|
219
261
|
|
|
220
|
-
|
|
221
|
-
|
|
262
|
+
test gcd {
|
|
263
|
+
const seed = if (@hasDecl(std.testing, "random_seed")) std.testing.random_seed else 0x12345678;
|
|
264
|
+
var prng = (if (@hasDecl(std, "Random")) std.Random else std.rand).DefaultPrng.init(seed);
|
|
265
|
+
var random = prng.random();
|
|
266
|
+
|
|
267
|
+
for (0..1000) |_| {
|
|
268
|
+
const a = random.int(u32);
|
|
269
|
+
const b = random.int(u32);
|
|
270
|
+
if (a == 0 or b == 0) continue;
|
|
271
|
+
|
|
272
|
+
try expectEqual(
|
|
273
|
+
gcd(a, b),
|
|
274
|
+
@as(u32, @intFromFloat(gcd(@as(f64, @floatFromInt(a)), @as(f64, @floatFromInt(b))))),
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try expectEqual(gcd(300_000, @as(u32, 2_300_000)), 100_000); // NB: @ctz requires an @intCast
|
|
222
279
|
}
|
|
223
280
|
|
|
224
281
|
fn doTurn(r: anytype) !void {
|
|
@@ -355,3 +412,36 @@ test BigRational {
|
|
|
355
412
|
try s.setRatio(71, 78);
|
|
356
413
|
try expect((try s.order(r.val)) == .eq);
|
|
357
414
|
}
|
|
415
|
+
|
|
416
|
+
test "minimum" {
|
|
417
|
+
// TODO: ziglang/zig#21069
|
|
418
|
+
if (builtin.mode == .ReleaseFast or builtin.mode == .ReleaseSmall) {
|
|
419
|
+
return error.SkipZigTest;
|
|
420
|
+
}
|
|
421
|
+
var r = try BigRational.init(std.testing.allocator);
|
|
422
|
+
defer r.deinit();
|
|
423
|
+
var s = try std.math.big.Rational.init(std.testing.allocator);
|
|
424
|
+
defer s.deinit();
|
|
425
|
+
|
|
426
|
+
try r.reset();
|
|
427
|
+
|
|
428
|
+
try r.update(1, 2); // Speed Tie
|
|
429
|
+
for (0..2) |_| {
|
|
430
|
+
try r.update(1, 2); // Confusion
|
|
431
|
+
try r.update(1, 2); // Infatuation
|
|
432
|
+
try r.update(3, 4); // Paralysis
|
|
433
|
+
// Population Bomb
|
|
434
|
+
for (0..10) |_| {
|
|
435
|
+
try r.update(9, 10); // Accuracy
|
|
436
|
+
try r.update(1, 15); // Damage roll
|
|
437
|
+
try r.update(1, 24); // Critical Hit
|
|
438
|
+
try r.update(9, 10); // King's Rock non-proc
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const ZEROS = std.math.pow(u310, 10, 31);
|
|
443
|
+
const n = 36472996377170786403;
|
|
444
|
+
const d: u310 = 146267071761884981524886149560653378220687632500762654519853056 * ZEROS;
|
|
445
|
+
try s.setRatio(n, d);
|
|
446
|
+
try expect((try s.order(r.val)) == .eq);
|
|
447
|
+
}
|
package/src/lib/common/rng.zig
CHANGED
|
@@ -66,11 +66,15 @@ pub const PSRNG = extern struct {
|
|
|
66
66
|
(@as(u64, self.range(u16, 0, 0x10000)) << 16) |
|
|
67
67
|
(@as(u64, self.range(u16, 0, 0x10000)));
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
pub fn Bound(comptime T: type) type {
|
|
71
|
+
return std.math.IntFittingRange(0, std.math.maxInt(T) + 1);
|
|
72
|
+
}
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
test PSRNG {
|
|
72
76
|
var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
|
|
73
|
-
try expectEqual(121, psrng.range(u8, 0, 256));
|
|
77
|
+
try expectEqual(@as(u8, 121), psrng.range(u8, 0, 256));
|
|
74
78
|
try expectEqual(false, psrng.chance(u8, 128, 256)); // 226 < 128
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -107,9 +111,9 @@ test Gen12 {
|
|
|
107
111
|
try expectEqual(e, rng.next());
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
try expectEqual(16, Gen12.percent(6) + 1);
|
|
111
|
-
try expectEqual(16, Gen12.percent(7) - 1);
|
|
112
|
-
try expectEqual(128, Gen12.percent(50) + 1);
|
|
114
|
+
try expectEqual(@as(u8, 16), Gen12.percent(6) + 1);
|
|
115
|
+
try expectEqual(@as(u8, 16), Gen12.percent(7) - 1);
|
|
116
|
+
try expectEqual(@as(u8, 128), Gen12.percent(50) + 1);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
// https://pkmn.cc/pokeemerald/src/random.c
|
|
@@ -197,7 +201,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
197
201
|
for (0..n) |_| _ = self.next();
|
|
198
202
|
}
|
|
199
203
|
|
|
200
|
-
pub fn range(self: *Self, comptime T: type, from: T, to: Bound(T)) T {
|
|
204
|
+
pub fn range(self: *Self, comptime T: type, from: T, to: PSRNG.Bound(T)) T {
|
|
201
205
|
assert(showdown);
|
|
202
206
|
return @intCast(@as(u64, self.next()) * (to - from) / divisor + from);
|
|
203
207
|
}
|
|
@@ -206,7 +210,7 @@ pub fn FixedRNG(comptime gen: comptime_int, comptime len: usize) type {
|
|
|
206
210
|
self: *Self,
|
|
207
211
|
comptime T: type,
|
|
208
212
|
numerator: T,
|
|
209
|
-
denominator: Bound(T),
|
|
213
|
+
denominator: PSRNG.Bound(T),
|
|
210
214
|
) bool {
|
|
211
215
|
assert(showdown);
|
|
212
216
|
assert(denominator > 0);
|
|
@@ -237,7 +241,3 @@ fn Output(comptime gen: comptime_int) type {
|
|
|
237
241
|
else => unreachable,
|
|
238
242
|
};
|
|
239
243
|
}
|
|
240
|
-
|
|
241
|
-
fn Bound(comptime T: type) type {
|
|
242
|
-
return std.math.IntFittingRange(0, std.math.maxInt(T) + 1);
|
|
243
|
-
}
|
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
|
+
}
|