@pkmn/engine 0.1.0-dev.b3f3e3a6 → 0.1.0-dev.b9015a94
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 +1 -3
- package/README.md +41 -14
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -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 +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +24 -10
- 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 +109 -107
- 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 +128 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +132 -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 +206 -81
- package/package.json +49 -28
- package/src/bin/install-pkmn-engine +29 -19
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +75 -74
- package/src/lib/common/rational.zig +121 -27
- package/src/lib/common/rng.zig +7 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +509 -131
- package/src/lib/gen1/chance.zig +374 -267
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +17 -16
- package/src/lib/gen1/helpers.zig +95 -538
- package/src/lib/gen1/mechanics.zig +284 -191
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +84 -496
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/generated.zig +1 -3
- package/src/lib/gen2/helpers.zig +41 -13
- package/src/lib/gen2/mechanics.zig +178 -127
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +32 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +351 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -3,8 +3,8 @@ const builtin = @import("builtin");
|
|
|
3
3
|
|
|
4
4
|
const pkmn = @import("../pkmn.zig");
|
|
5
5
|
|
|
6
|
-
const data = @import("
|
|
7
|
-
const options = @import("
|
|
6
|
+
const data = @import("data.zig");
|
|
7
|
+
const options = @import("options.zig");
|
|
8
8
|
|
|
9
9
|
const assert = std.debug.assert;
|
|
10
10
|
const print = std.debug.print;
|
|
@@ -14,6 +14,13 @@ const enabled = options.log;
|
|
|
14
14
|
const Player = data.Player;
|
|
15
15
|
const ID = data.ID;
|
|
16
16
|
|
|
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;
|
|
23
|
+
|
|
17
24
|
pub const ArgType = enum(u8) {
|
|
18
25
|
None,
|
|
19
26
|
|
|
@@ -71,7 +78,7 @@ pub const ArgType = enum(u8) {
|
|
|
71
78
|
EndAbility,
|
|
72
79
|
ClearNegativeBoost,
|
|
73
80
|
FormeChange,
|
|
74
|
-
|
|
81
|
+
LastNoTarget, // FIXME
|
|
75
82
|
|
|
76
83
|
// Gen 4
|
|
77
84
|
SwapBoost,
|
|
@@ -218,6 +225,7 @@ pub const Activate = enum(u8) {
|
|
|
218
225
|
BeatUp, // FIXME of
|
|
219
226
|
|
|
220
227
|
Magnitude,
|
|
228
|
+
Spite,
|
|
221
229
|
};
|
|
222
230
|
|
|
223
231
|
pub const Start = enum(u8) {
|
|
@@ -359,8 +367,8 @@ pub fn Log(comptime Writer: type) type {
|
|
|
359
367
|
} else {
|
|
360
368
|
try self.writer.writeAll(&.{ @intFromEnum(args[1].species), args[1].level });
|
|
361
369
|
}
|
|
362
|
-
try self.writer.
|
|
363
|
-
try self.writer.
|
|
370
|
+
try self.writer.writeInt(u16, args[1].hp, endian);
|
|
371
|
+
try self.writer.writeInt(u16, args[1].stats.hp, endian);
|
|
364
372
|
try self.writer.writeAll(&.{args[1].status});
|
|
365
373
|
}
|
|
366
374
|
|
|
@@ -391,7 +399,7 @@ pub fn Log(comptime Writer: type) type {
|
|
|
391
399
|
if (!enabled) return;
|
|
392
400
|
|
|
393
401
|
try self.writer.writeByte(@intFromEnum(ArgType.Turn));
|
|
394
|
-
try self.writer.
|
|
402
|
+
try self.writer.writeInt(u16, args[0], endian);
|
|
395
403
|
try self.writer.writeByte(@intFromEnum(ArgType.None));
|
|
396
404
|
}
|
|
397
405
|
|
|
@@ -423,8 +431,8 @@ pub fn Log(comptime Writer: type) type {
|
|
|
423
431
|
@intFromEnum(ArgType.Damage),
|
|
424
432
|
@as(u8, @bitCast(args[0])),
|
|
425
433
|
});
|
|
426
|
-
try self.writer.
|
|
427
|
-
try self.writer.
|
|
434
|
+
try self.writer.writeInt(u16, args[1].hp, endian);
|
|
435
|
+
try self.writer.writeInt(u16, args[1].stats.hp, endian);
|
|
428
436
|
try self.writer.writeAll(&.{ args[1].status, @intFromEnum(@as(Damage, args[2])) });
|
|
429
437
|
if (args.len == 4) try self.writer.writeByte(@as(u8, @bitCast(args[3])));
|
|
430
438
|
}
|
|
@@ -436,8 +444,8 @@ pub fn Log(comptime Writer: type) type {
|
|
|
436
444
|
|
|
437
445
|
assert(args[2] != .Drain or args.len == 4);
|
|
438
446
|
try self.writer.writeAll(&.{ @intFromEnum(ArgType.Heal), @as(u8, @bitCast(args[0])) });
|
|
439
|
-
try self.writer.
|
|
440
|
-
try self.writer.
|
|
447
|
+
try self.writer.writeInt(u16, args[1].hp, endian);
|
|
448
|
+
try self.writer.writeInt(u16, args[1].stats.hp, endian);
|
|
441
449
|
try self.writer.writeAll(&.{ args[1].status, @intFromEnum(@as(Heal, args[2])) });
|
|
442
450
|
if (args.len == 4) try self.writer.writeByte(@as(u8, @bitCast(args[3])));
|
|
443
451
|
}
|
|
@@ -542,6 +550,7 @@ pub fn Log(comptime Writer: type) type {
|
|
|
542
550
|
// ident: ID, reason: Activate
|
|
543
551
|
// ident: ID, reason: .SubstituteBlock, m: <Move>
|
|
544
552
|
// ident: ID, reason: .Magnitude, num: u8
|
|
553
|
+
// ident: ID, reason: .Spite, m: <Move>, num: u8
|
|
545
554
|
pub fn activate(self: Self, args: anytype) Error!void {
|
|
546
555
|
if (!enabled) return;
|
|
547
556
|
|
|
@@ -559,6 +568,9 @@ pub fn Log(comptime Writer: type) type {
|
|
|
559
568
|
assert(reason == .Magnitude);
|
|
560
569
|
try self.writer.writeByte(args[2]);
|
|
561
570
|
}
|
|
571
|
+
} else if (args.len == 4) {
|
|
572
|
+
assert(reason == .Spite);
|
|
573
|
+
try self.writer.writeAll(&.{ @intFromEnum(args[2]), args[3] });
|
|
562
574
|
}
|
|
563
575
|
}
|
|
564
576
|
|
|
@@ -706,8 +718,8 @@ pub fn Log(comptime Writer: type) type {
|
|
|
706
718
|
if (!enabled) return;
|
|
707
719
|
|
|
708
720
|
try self.writer.writeAll(&.{ @intFromEnum(ArgType.SetHP), @as(u8, @bitCast(args[0])) });
|
|
709
|
-
try self.writer.
|
|
710
|
-
try self.writer.
|
|
721
|
+
try self.writer.writeInt(u16, args[1].hp, endian);
|
|
722
|
+
try self.writer.writeInt(u16, args[1].stats.hp, endian);
|
|
711
723
|
try self.writer.writeAll(&.{args[1].status});
|
|
712
724
|
try self.writer.writeByte(@intFromEnum(@as(SetHP, args[2])));
|
|
713
725
|
}
|
|
@@ -831,10 +843,10 @@ pub const ByteStream = struct {
|
|
|
831
843
|
try self.stream.writeByte(byte);
|
|
832
844
|
}
|
|
833
845
|
|
|
834
|
-
pub fn
|
|
846
|
+
pub fn writeInt(self: Writer, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
|
|
835
847
|
// TODO: rework this to write directly to the buffer?
|
|
836
|
-
var bytes: [(@typeInfo(T)
|
|
837
|
-
std.mem.
|
|
848
|
+
var bytes: [@divExact(@field(@typeInfo(T), @tagName(Int)).bits, 8)]u8 = undefined;
|
|
849
|
+
std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(v)), &bytes, v, end);
|
|
838
850
|
return self.writeAll(&bytes);
|
|
839
851
|
}
|
|
840
852
|
};
|
|
@@ -857,7 +869,7 @@ pub const ByteStream = struct {
|
|
|
857
869
|
pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
|
|
858
870
|
|
|
859
871
|
pub fn format(
|
|
860
|
-
|
|
872
|
+
gen: anytype,
|
|
861
873
|
a: []const u8,
|
|
862
874
|
b: ?[]const u8,
|
|
863
875
|
color: bool,
|
|
@@ -943,19 +955,19 @@ pub fn format(
|
|
|
943
955
|
if (a[i] == N(gen.Gender.Unknown)) {
|
|
944
956
|
i += 1;
|
|
945
957
|
} else {
|
|
946
|
-
|
|
958
|
+
const gender = if (a[i] == N(gen.Gender.Male)) "M" else "F";
|
|
947
959
|
printc(", {s}", .{gender}, a, b, &i, 1, color);
|
|
948
960
|
}
|
|
949
961
|
}
|
|
950
962
|
printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
|
|
951
963
|
switch (endian) {
|
|
952
|
-
|
|
964
|
+
big => {
|
|
953
965
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
954
966
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
955
967
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
956
968
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
957
969
|
},
|
|
958
|
-
|
|
970
|
+
little => {
|
|
959
971
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
960
972
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
961
973
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -986,8 +998,8 @@ pub fn format(
|
|
|
986
998
|
},
|
|
987
999
|
.Turn => {
|
|
988
1000
|
const turn = switch (endian) {
|
|
989
|
-
|
|
990
|
-
|
|
1001
|
+
big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
|
|
1002
|
+
little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
|
|
991
1003
|
};
|
|
992
1004
|
printc(" {d}", .{turn}, a, b, &i, 2, color);
|
|
993
1005
|
},
|
|
@@ -998,13 +1010,13 @@ pub fn format(
|
|
|
998
1010
|
var id = ID.from(@intCast(a[i]));
|
|
999
1011
|
printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
|
|
1000
1012
|
switch (endian) {
|
|
1001
|
-
|
|
1013
|
+
big => {
|
|
1002
1014
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1003
1015
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1004
1016
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1005
1017
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
1006
1018
|
},
|
|
1007
|
-
|
|
1019
|
+
little => {
|
|
1008
1020
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
1009
1021
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1010
1022
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -1083,7 +1095,8 @@ pub fn format(
|
|
|
1083
1095
|
const reason = a[i];
|
|
1084
1096
|
printc(" {s}", .{@tagName(@as(Start, @enumFromInt(reason)))}, a, b, &i, 1, color);
|
|
1085
1097
|
if (@as(Start, @enumFromInt(reason)) == .TypeChange) {
|
|
1086
|
-
|
|
1098
|
+
// FIXME
|
|
1099
|
+
const types = @as(gen1.Types, @bitCast(a[i]));
|
|
1087
1100
|
const args = .{
|
|
1088
1101
|
formatter(gen, .Type, @intFromEnum(types.type1)),
|
|
1089
1102
|
formatter(gen, .Type, @intFromEnum(types.type2)),
|
|
@@ -1125,13 +1138,13 @@ pub fn format(
|
|
|
1125
1138
|
const id = ID.from(@intCast(a[i]));
|
|
1126
1139
|
printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
|
|
1127
1140
|
switch (endian) {
|
|
1128
|
-
|
|
1141
|
+
big => {
|
|
1129
1142
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1130
1143
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1131
1144
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1132
1145
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
1133
1146
|
},
|
|
1134
|
-
|
|
1147
|
+
little => {
|
|
1135
1148
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
1136
1149
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1137
1150
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -1199,14 +1212,11 @@ pub fn expectLog(
|
|
|
1199
1212
|
) !void {
|
|
1200
1213
|
if (!enabled) return;
|
|
1201
1214
|
|
|
1202
|
-
const color =
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
} else {
|
|
1208
|
-
break :color std.io.getStdErr().supportsAnsiEscapeCodes();
|
|
1209
|
-
}
|
|
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,
|
|
1210
1220
|
};
|
|
1211
1221
|
|
|
1212
1222
|
expectEqualBytes(expected, actual, offset) catch |err| switch (err) {
|
|
@@ -1238,8 +1248,6 @@ fn expectEqualBytes(expected: []const u8, actual: []const u8, offset: usize) !vo
|
|
|
1238
1248
|
}
|
|
1239
1249
|
}
|
|
1240
1250
|
|
|
1241
|
-
const endian = builtin.target.cpu.arch.endian();
|
|
1242
|
-
|
|
1243
1251
|
fn N(e: anytype) u8 {
|
|
1244
1252
|
return @intFromEnum(e);
|
|
1245
1253
|
}
|
|
@@ -1247,20 +1255,13 @@ fn N(e: anytype) u8 {
|
|
|
1247
1255
|
const p1 = Player.P1;
|
|
1248
1256
|
const p2 = Player.P2;
|
|
1249
1257
|
|
|
1250
|
-
const gen1 = struct {
|
|
1251
|
-
pub usingnamespace @import("../gen1/data.zig");
|
|
1252
|
-
pub const helpers = @import("../gen1/helpers.zig");
|
|
1253
|
-
};
|
|
1254
|
-
|
|
1255
|
-
const gen2 = struct {
|
|
1256
|
-
pub usingnamespace @import("../gen2/data.zig");
|
|
1257
|
-
pub const helpers = @import("../gen2/helpers.zig");
|
|
1258
|
-
};
|
|
1259
|
-
|
|
1260
1258
|
var buf: [pkmn.LOGS_SIZE]u8 = undefined;
|
|
1261
1259
|
var stream: ByteStream = .{ .buffer = &buf };
|
|
1262
1260
|
var log: FixedLog = .{ .writer = stream.writer() };
|
|
1263
1261
|
|
|
1262
|
+
const gen1 = pkmn.gen1;
|
|
1263
|
+
const gen2 = pkmn.gen2;
|
|
1264
|
+
|
|
1264
1265
|
const M1 = gen1.Move;
|
|
1265
1266
|
const S1 = gen1.Species;
|
|
1266
1267
|
|
|
@@ -1349,8 +1350,8 @@ test "|switch|" {
|
|
|
1349
1350
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1350
1351
|
const par = 0b1000000;
|
|
1351
1352
|
var expected: []const u8 = switch (endian) {
|
|
1352
|
-
|
|
1353
|
-
|
|
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 },
|
|
1354
1355
|
};
|
|
1355
1356
|
try expectLog1(expected, buf[0..9]);
|
|
1356
1357
|
stream.reset();
|
|
@@ -1360,8 +1361,8 @@ test "|switch|" {
|
|
|
1360
1361
|
snorlax.status = 0;
|
|
1361
1362
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1362
1363
|
expected = switch (endian) {
|
|
1363
|
-
|
|
1364
|
-
|
|
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 },
|
|
1365
1366
|
};
|
|
1366
1367
|
try expectLog1(expected, buf[0..9]);
|
|
1367
1368
|
stream.reset();
|
|
@@ -1369,8 +1370,8 @@ test "|switch|" {
|
|
|
1369
1370
|
snorlax.hp = 400;
|
|
1370
1371
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1371
1372
|
expected = switch (endian) {
|
|
1372
|
-
|
|
1373
|
-
|
|
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 },
|
|
1374
1375
|
};
|
|
1375
1376
|
try expectLog1(expected, buf[0..9]);
|
|
1376
1377
|
stream.reset();
|
|
@@ -1388,8 +1389,8 @@ test "|switch|" {
|
|
|
1388
1389
|
N(gen2.Gender.Female),
|
|
1389
1390
|
91,
|
|
1390
1391
|
} ++ switch (endian) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1392
|
+
big => .{ 0, 200, 1, 144, par },
|
|
1393
|
+
little => .{ 200, 0, 144, 1, par },
|
|
1393
1394
|
});
|
|
1394
1395
|
try expectLog2(expected, buf[0..10]);
|
|
1395
1396
|
stream.reset();
|
|
@@ -1417,9 +1418,9 @@ test "|faint|" {
|
|
|
1417
1418
|
|
|
1418
1419
|
test "|turn|" {
|
|
1419
1420
|
try log.turn(.{42});
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1421
|
+
const expected = switch (endian) {
|
|
1422
|
+
big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
|
|
1423
|
+
little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
|
|
1423
1424
|
};
|
|
1424
1425
|
try expectLog1(expected, buf[0..4]);
|
|
1425
1426
|
stream.reset();
|
|
@@ -1443,8 +1444,8 @@ test "|-damage|" {
|
|
|
1443
1444
|
chansey.status = gen1.Status.slp(1);
|
|
1444
1445
|
try log.damage(.{ p2.ident(2), &chansey, .None });
|
|
1445
1446
|
var expected: []const u8 = switch (endian) {
|
|
1446
|
-
|
|
1447
|
-
|
|
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) },
|
|
1448
1449
|
};
|
|
1449
1450
|
try expectLog1(expected, buf[0..8]);
|
|
1450
1451
|
stream.reset();
|
|
@@ -1454,8 +1455,8 @@ test "|-damage|" {
|
|
|
1454
1455
|
chansey.status = 0;
|
|
1455
1456
|
try log.damage(.{ p2.ident(2), &chansey, .Confusion });
|
|
1456
1457
|
expected = switch (endian) {
|
|
1457
|
-
|
|
1458
|
-
|
|
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) },
|
|
1459
1460
|
};
|
|
1460
1461
|
try expectLog1(expected, buf[0..8]);
|
|
1461
1462
|
stream.reset();
|
|
@@ -1463,8 +1464,8 @@ test "|-damage|" {
|
|
|
1463
1464
|
chansey.status = gen1.Status.init(.PSN);
|
|
1464
1465
|
try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
|
|
1465
1466
|
expected = switch (endian) {
|
|
1466
|
-
|
|
1467
|
-
|
|
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 },
|
|
1468
1469
|
};
|
|
1469
1470
|
try expectLog1(expected, buf[0..9]);
|
|
1470
1471
|
stream.reset();
|
|
@@ -1476,8 +1477,8 @@ test "|-heal|" {
|
|
|
1476
1477
|
chansey.status = gen1.Status.slp(1);
|
|
1477
1478
|
try log.heal(.{ p2.ident(2), &chansey, .None });
|
|
1478
1479
|
var expected: []const u8 = switch (endian) {
|
|
1479
|
-
|
|
1480
|
-
|
|
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) },
|
|
1481
1482
|
};
|
|
1482
1483
|
try expectLog1(expected, buf[0..8]);
|
|
1483
1484
|
stream.reset();
|
|
@@ -1487,16 +1488,16 @@ test "|-heal|" {
|
|
|
1487
1488
|
chansey.status = 0;
|
|
1488
1489
|
try log.heal(.{ p2.ident(2), &chansey, .Silent });
|
|
1489
1490
|
expected = switch (endian) {
|
|
1490
|
-
|
|
1491
|
-
|
|
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) },
|
|
1492
1493
|
};
|
|
1493
1494
|
try expectLog1(expected, buf[0..8]);
|
|
1494
1495
|
stream.reset();
|
|
1495
1496
|
|
|
1496
1497
|
try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
|
|
1497
1498
|
expected = switch (endian) {
|
|
1498
|
-
|
|
1499
|
-
|
|
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 },
|
|
1500
1501
|
};
|
|
1501
1502
|
try expectLog1(expected, buf[0..9]);
|
|
1502
1503
|
stream.reset();
|
|
@@ -1739,8 +1740,8 @@ test "|drag|" {
|
|
|
1739
1740
|
N(gen2.Gender.Female),
|
|
1740
1741
|
91,
|
|
1741
1742
|
} ++ switch (endian) {
|
|
1742
|
-
|
|
1743
|
-
|
|
1743
|
+
big => .{ 0, 200, 1, 144, par },
|
|
1744
|
+
little => .{ 200, 0, 144, 1, par },
|
|
1744
1745
|
});
|
|
1745
1746
|
try expectLog2(expected, buf[0..10]);
|
|
1746
1747
|
stream.reset();
|
|
@@ -1772,9 +1773,9 @@ test "|-sethp|" {
|
|
|
1772
1773
|
var blissey = gen2.helpers.Pokemon.init(.{ .species = .Blissey, .moves = &.{.PainSplit} });
|
|
1773
1774
|
blissey.hp = 612;
|
|
1774
1775
|
try log.sethp(.{ p2.ident(2), &blissey, .Silent });
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1776
|
+
const expected: []const u8 = switch (endian) {
|
|
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) },
|
|
1778
1779
|
};
|
|
1779
1780
|
try expectLog2(expected, buf[0..8]);
|
|
1780
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
|
|
|
@@ -192,9 +198,9 @@ pub fn Rational(comptime T: type) type {
|
|
|
192
198
|
|
|
193
199
|
fn addition(r: *Self, p: anytype, q: anytype) !void {
|
|
194
200
|
// (a/b) + (c/d) = (ad+bc)/(bd)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
const d = try std.math.mul(T, r.q, q);
|
|
202
|
+
const n1 = try std.math.mul(T, r.p, q);
|
|
203
|
+
const n2 = try std.math.mul(T, r.q, p);
|
|
198
204
|
|
|
199
205
|
r.p = try std.math.add(T, n1, n2);
|
|
200
206
|
r.q = d;
|
|
@@ -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
|
+
}
|
|
240
|
+
|
|
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
|
+
},
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
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;
|
|
213
271
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
272
|
+
try expectEqual(
|
|
273
|
+
gcd(a, b),
|
|
274
|
+
@as(u32, @intFromFloat(gcd(@as(f64, @floatFromInt(a)), @as(f64, @floatFromInt(b))))),
|
|
275
|
+
);
|
|
218
276
|
}
|
|
219
277
|
|
|
220
|
-
|
|
221
|
-
return a;
|
|
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 {
|
|
@@ -234,6 +291,8 @@ test Rational {
|
|
|
234
291
|
var r: Rational(t) = .{};
|
|
235
292
|
|
|
236
293
|
var c: t = 128;
|
|
294
|
+
_ = &c;
|
|
295
|
+
|
|
237
296
|
try r.update(c, 256);
|
|
238
297
|
try doTurn(&r);
|
|
239
298
|
|
|
@@ -320,6 +379,8 @@ test BigRational {
|
|
|
320
379
|
try r.reset();
|
|
321
380
|
|
|
322
381
|
var c: u8 = 128;
|
|
382
|
+
_ = &c;
|
|
383
|
+
|
|
323
384
|
try r.update(c, 256);
|
|
324
385
|
try doTurn(&r);
|
|
325
386
|
|
|
@@ -351,3 +412,36 @@ test BigRational {
|
|
|
351
412
|
try s.setRatio(71, 78);
|
|
352
413
|
try expect((try s.order(r.val)) == .eq);
|
|
353
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
|
+
}
|