@pkmn/engine 0.1.0-dev.62399ed5 → 0.1.0-dev.6be9a775
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 +4 -2
- package/build/pkg/index.d.ts +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/test/display.js +6 -1
- package/build/test/display.js.map +1 -1
- package/build.zig +199 -66
- package/package.json +4 -1
- package/src/bin/install-pkmn-engine +7 -5
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/protocol.zig +41 -36
- package/src/lib/common/rational.zig +46 -46
- package/src/lib/common/rng.zig +4 -4
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +329 -119
- package/src/lib/gen1/chance.zig +332 -224
- package/src/lib/gen1/data.zig +28 -27
- package/src/lib/gen1/helpers.zig +13 -534
- package/src/lib/gen1/mechanics.zig +178 -109
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +91 -501
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +4 -2
- package/src/lib/gen2/mechanics.zig +47 -71
- package/src/lib/pkmn.zig +7 -5
|
@@ -10,7 +10,7 @@ const path = require('path');
|
|
|
10
10
|
const {pipeline} = require('stream/promises');
|
|
11
11
|
const tty = require('tty');
|
|
12
12
|
|
|
13
|
-
const ZIG_VERSION = {major: 0, minor:
|
|
13
|
+
const ZIG_VERSION = {major: 0, minor: 11, patch: 0};
|
|
14
14
|
const DEV = ZIG_VERSION.dev ? `-dev.${ZIG_VERSION.dev}` : '';
|
|
15
15
|
const VERSION = `${ZIG_VERSION.major}.${ZIG_VERSION.minor}.${ZIG_VERSION.patch}${DEV}`;
|
|
16
16
|
const INDEX = 'https://ziglang.org/download/index.json';
|
|
@@ -163,8 +163,10 @@ const install = async () => {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
const index = JSON.parse(await fetch(INDEX));
|
|
166
|
-
|
|
167
|
-
const
|
|
166
|
+
// TODO: ziglang/zig#17768
|
|
167
|
+
// const version = Object.keys(index)[+!ZIG_VERSION.dev];
|
|
168
|
+
const version = VERSION;
|
|
169
|
+
const release = index[VERSION][`${arch}-${platform}`];
|
|
168
170
|
const url = release.tarball;
|
|
169
171
|
|
|
170
172
|
let base = path.basename(url);
|
|
@@ -389,11 +391,11 @@ const downloadImportLib = async () => {
|
|
|
389
391
|
const ext = process.platform === 'darwin'
|
|
390
392
|
? 'dylib' : process.platform === 'win32' ? 'dll' : 'so';
|
|
391
393
|
try {
|
|
392
|
-
const actual = path.join('build', 'lib', `${prefix}pkmn${suffix}.node.${ext}`);
|
|
394
|
+
const actual = path.join(ROOT, 'build', 'lib', `${prefix}pkmn${suffix}.node.${ext}`);
|
|
393
395
|
fs.renameSync(actual, artifact);
|
|
394
396
|
} catch (err) {
|
|
395
397
|
// ziglang/zig#19743 changed this path
|
|
396
|
-
const actual = path.join('build', 'bin', `${prefix}pkmn${suffix}.node.${ext}`);
|
|
398
|
+
const actual = path.join(ROOT, 'build', 'bin', `${prefix}pkmn${suffix}.node.${ext}`);
|
|
397
399
|
fs.renameSync(actual, artifact);
|
|
398
400
|
}
|
|
399
401
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const expectEqual = std.testing.expectEqual;
|
|
4
|
+
const assert = std.debug.assert;
|
|
5
|
+
|
|
6
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
7
|
+
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
8
|
+
|
|
9
|
+
/// Helpers for working with bit-packed arrays of non-powers-of-2 types.
|
|
10
|
+
/// NOTE: ziglang/zig#12547
|
|
11
|
+
pub fn Array(comptime n: comptime_int, comptime U: type) type {
|
|
12
|
+
return struct {
|
|
13
|
+
const size = @bitSizeOf(U);
|
|
14
|
+
const options = .{ .signedness = .unsigned, .bits = size * n };
|
|
15
|
+
pub const T = @Type(if (@hasField(std.builtin.Type, "int"))
|
|
16
|
+
.{ .int = options }
|
|
17
|
+
else
|
|
18
|
+
.{ .Int = options });
|
|
19
|
+
|
|
20
|
+
/// Returns the value stored at index `i` in the array `a`.
|
|
21
|
+
pub fn get(a: T, i: usize) U {
|
|
22
|
+
assert(i < n);
|
|
23
|
+
const shift = i * size;
|
|
24
|
+
const mask: T = (1 << size) - 1;
|
|
25
|
+
const result = (a >> @intCast(shift)) & mask;
|
|
26
|
+
return switch (@typeInfo(U)) {
|
|
27
|
+
Enum => @enumFromInt(result),
|
|
28
|
+
Int => @intCast(result),
|
|
29
|
+
else => unreachable,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Sets the value at index `i` to `val` in the array `a`.
|
|
34
|
+
pub fn set(a: T, i: usize, val: U) T {
|
|
35
|
+
assert(i < n);
|
|
36
|
+
const v: T = switch (@typeInfo(U)) {
|
|
37
|
+
Enum => @intFromEnum(val),
|
|
38
|
+
Int => @intCast(val),
|
|
39
|
+
else => unreachable,
|
|
40
|
+
};
|
|
41
|
+
const shift = i * size;
|
|
42
|
+
const mask: T = @as(T, @intCast((1 << size) - 1)) << @intCast(shift);
|
|
43
|
+
const result: T = a & ~mask;
|
|
44
|
+
return result | (v << @intCast(shift));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test Array {
|
|
50
|
+
const A = Array(5, u8);
|
|
51
|
+
var a: A.T = 0;
|
|
52
|
+
try expectEqual(u40, @TypeOf(a));
|
|
53
|
+
|
|
54
|
+
a = A.set(a, 4, 241);
|
|
55
|
+
try expectEqual(@as(u8, 241), A.get(a, 4));
|
|
56
|
+
a = A.set(a, 4, 1);
|
|
57
|
+
try expectEqual(@as(u8, 1), A.get(a, 4));
|
|
58
|
+
|
|
59
|
+
for (0..5) |i| a = A.set(a, i, @intCast(i));
|
|
60
|
+
for (0..5) |i| try expectEqual(@as(u8, @intCast(i)), A.get(a, i));
|
|
61
|
+
|
|
62
|
+
const Optional = @import("optional.zig").Optional;
|
|
63
|
+
|
|
64
|
+
const B = Array(4, Optional(bool));
|
|
65
|
+
var b: B.T = 0;
|
|
66
|
+
try expectEqual(u8, @TypeOf(b));
|
|
67
|
+
|
|
68
|
+
b = B.set(b, 2, .true);
|
|
69
|
+
try expectEqual(Optional(bool).None, B.get(b, 1));
|
|
70
|
+
try expectEqual(Optional(bool).true, B.get(b, 2));
|
|
71
|
+
try expectEqual(@as(u8, 0b00100000), b);
|
|
72
|
+
b = B.set(b, 2, .None);
|
|
73
|
+
b = B.set(b, 0, .false);
|
|
74
|
+
try expectEqual(Optional(bool).None, B.get(b, 2));
|
|
75
|
+
try expectEqual(Optional(bool).false, B.get(b, 0));
|
|
76
|
+
try expectEqual(@as(u8, 0b0000001), b);
|
|
77
|
+
}
|
package/src/lib/common/data.zig
CHANGED
|
@@ -22,8 +22,8 @@ pub const Player = enum(u1) {
|
|
|
22
22
|
|
|
23
23
|
test Player {
|
|
24
24
|
try expectEqual(Player.P2, Player.P1.foe());
|
|
25
|
-
try expectEqual(0b0001, @as(u8, @bitCast(Player.P1.ident(1))));
|
|
26
|
-
try expectEqual(0b1101, @as(u8, @bitCast(Player.P2.ident(5))));
|
|
25
|
+
try expectEqual(@as(u8, 0b0001), @as(u8, @bitCast(Player.P1.ident(1))));
|
|
26
|
+
try expectEqual(@as(u8, 0b1101), @as(u8, @bitCast(Player.P2.ident(5))));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/// An identifier for a specific Pokémon in battle.
|
|
@@ -46,8 +46,8 @@ pub const ID = packed struct(u8) {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
test ID {
|
|
49
|
-
try expectEqual(0b0001, @as(u8, @bitCast(ID{ .player = .P1, .id = 1 })));
|
|
50
|
-
try expectEqual(0b1101, @as(u8, @bitCast(ID{ .player = .P2, .id = 5 })));
|
|
49
|
+
try expectEqual(@as(u8, 0b0001), @as(u8, @bitCast(ID{ .player = .P1, .id = 1 })));
|
|
50
|
+
try expectEqual(@as(u8, 0b1101), @as(u8, @bitCast(ID{ .player = .P2, .id = 5 })));
|
|
51
51
|
const id: ID = .{ .player = .P2, .id = 4 };
|
|
52
52
|
try expectEqual(id, ID.from(id.int()));
|
|
53
53
|
}
|
|
@@ -76,8 +76,8 @@ test Choice {
|
|
|
76
76
|
const p2: Choice = .{ .type = .Switch, .data = 5 };
|
|
77
77
|
try expectEqual(5, p2.data);
|
|
78
78
|
try expectEqual(Choice.Type.Move, p1.type);
|
|
79
|
-
try expectEqual(0b0001_0001, @as(u8, @bitCast(p1)));
|
|
80
|
-
try expectEqual(0b0001_0110, @as(u8, @bitCast(p2)));
|
|
79
|
+
try expectEqual(@as(u8, 0b0001_0001), @as(u8, @bitCast(p1)));
|
|
80
|
+
try expectEqual(@as(u8, 0b0001_0110), @as(u8, @bitCast(p2)));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/// The result of the battle - all results other than 'None' should be considered terminal.
|
|
@@ -112,6 +112,6 @@ pub const Result = packed struct(u8) {
|
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
test Result {
|
|
115
|
-
try expectEqual(0b0101_0000, @as(u8, @bitCast(Result.Default)));
|
|
116
|
-
try expectEqual(0b1000_0000, @as(u8, @bitCast(Result{ .p2 = .Switch })));
|
|
115
|
+
try expectEqual(@as(u8, 0b0101_0000), @as(u8, @bitCast(Result.Default)));
|
|
116
|
+
try expectEqual(@as(u8, 0b1000_0000), @as(u8, @bitCast(Result{ .p2 = .Switch })));
|
|
117
117
|
}
|
package/src/lib/common/js.zig
CHANGED
|
@@ -2,6 +2,8 @@ const std = @import("std");
|
|
|
2
2
|
|
|
3
3
|
const assert = std.debug.assert;
|
|
4
4
|
|
|
5
|
+
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
6
|
+
|
|
5
7
|
const c = @cImport({
|
|
6
8
|
@cDefine("NAPI_VERSION", "8");
|
|
7
9
|
@cInclude("node_api.h");
|
|
@@ -35,7 +37,7 @@ pub const Number = struct {
|
|
|
35
37
|
const T = @TypeOf(value);
|
|
36
38
|
var result: c.napi_value = undefined;
|
|
37
39
|
assert(c.napi_ok == switch (@typeInfo(T)) {
|
|
38
|
-
|
|
40
|
+
Int => |info| switch (info.bits) {
|
|
39
41
|
0...32 => switch (info.signedness) {
|
|
40
42
|
.signed => c.napi_create_int32(env, @as(i32, value), &result),
|
|
41
43
|
.unsigned => c.napi_create_uint32(env, @as(u32, value), &result),
|
|
@@ -50,7 +52,7 @@ pub const Number = struct {
|
|
|
50
52
|
|
|
51
53
|
pub fn get(env: c.napi_env, value: c.napi_value, comptime T: type) T {
|
|
52
54
|
switch (@typeInfo(T)) {
|
|
53
|
-
|
|
55
|
+
Int => |info| switch (info.bits) {
|
|
54
56
|
0...32 => switch (info.signedness) {
|
|
55
57
|
.signed => {
|
|
56
58
|
var result: i32 = undefined;
|
|
@@ -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 {
|
|
@@ -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,
|
|
@@ -840,7 +845,7 @@ pub const ByteStream = struct {
|
|
|
840
845
|
|
|
841
846
|
pub fn writeInt(self: Writer, comptime T: type, v: T, end: std.builtin.Endian) Error!void {
|
|
842
847
|
// TODO: rework this to write directly to the buffer?
|
|
843
|
-
var bytes: [@divExact(@typeInfo(T)
|
|
848
|
+
var bytes: [@divExact(@field(@typeInfo(T), @tagName(Int)).bits, 8)]u8 = undefined;
|
|
844
849
|
std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(v)), &bytes, v, end);
|
|
845
850
|
return self.writeAll(&bytes);
|
|
846
851
|
}
|
|
@@ -864,7 +869,7 @@ pub const ByteStream = struct {
|
|
|
864
869
|
pub const Kind = enum { Move, Species, Type, Status, Item, Weather };
|
|
865
870
|
|
|
866
871
|
pub fn format(
|
|
867
|
-
|
|
872
|
+
gen: anytype,
|
|
868
873
|
a: []const u8,
|
|
869
874
|
b: ?[]const u8,
|
|
870
875
|
color: bool,
|
|
@@ -956,13 +961,13 @@ pub fn format(
|
|
|
956
961
|
}
|
|
957
962
|
printc(" L{d}", .{a[i]}, a, b, &i, 1, color);
|
|
958
963
|
switch (endian) {
|
|
959
|
-
|
|
964
|
+
big => {
|
|
960
965
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
961
966
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
962
967
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
963
968
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
964
969
|
},
|
|
965
|
-
|
|
970
|
+
little => {
|
|
966
971
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
967
972
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
968
973
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -993,8 +998,8 @@ pub fn format(
|
|
|
993
998
|
},
|
|
994
999
|
.Turn => {
|
|
995
1000
|
const turn = switch (endian) {
|
|
996
|
-
|
|
997
|
-
|
|
1001
|
+
big => @as(u16, a[i]) << 8 | @as(u16, a[i + 1]),
|
|
1002
|
+
little => @as(u16, a[i + 1]) << 8 | @as(u16, a[i]),
|
|
998
1003
|
};
|
|
999
1004
|
printc(" {d}", .{turn}, a, b, &i, 2, color);
|
|
1000
1005
|
},
|
|
@@ -1005,13 +1010,13 @@ pub fn format(
|
|
|
1005
1010
|
var id = ID.from(@intCast(a[i]));
|
|
1006
1011
|
printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
|
|
1007
1012
|
switch (endian) {
|
|
1008
|
-
|
|
1013
|
+
big => {
|
|
1009
1014
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1010
1015
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1011
1016
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1012
1017
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
1013
1018
|
},
|
|
1014
|
-
|
|
1019
|
+
little => {
|
|
1015
1020
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
1016
1021
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1017
1022
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -1133,13 +1138,13 @@ pub fn format(
|
|
|
1133
1138
|
const id = ID.from(@intCast(a[i]));
|
|
1134
1139
|
printc(" {s}({d})", .{ @tagName(id.player), id.id }, a, b, &i, 1, color);
|
|
1135
1140
|
switch (endian) {
|
|
1136
|
-
|
|
1141
|
+
big => {
|
|
1137
1142
|
var hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1138
1143
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1139
1144
|
hp = @as(u16, a[i]) << 8 | @as(u16, a[i + 1]);
|
|
1140
1145
|
printc("/{d}", .{hp}, a, b, &i, 2, color);
|
|
1141
1146
|
},
|
|
1142
|
-
|
|
1147
|
+
little => {
|
|
1143
1148
|
var hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
1144
1149
|
printc(" {d}", .{hp}, a, b, &i, 2, color);
|
|
1145
1150
|
hp = @as(u16, a[i + 1]) << 8 | @as(u16, a[i]);
|
|
@@ -1345,8 +1350,8 @@ test "|switch|" {
|
|
|
1345
1350
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1346
1351
|
const par = 0b1000000;
|
|
1347
1352
|
var expected: []const u8 = switch (endian) {
|
|
1348
|
-
|
|
1349
|
-
|
|
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 },
|
|
1350
1355
|
};
|
|
1351
1356
|
try expectLog1(expected, buf[0..9]);
|
|
1352
1357
|
stream.reset();
|
|
@@ -1356,8 +1361,8 @@ test "|switch|" {
|
|
|
1356
1361
|
snorlax.status = 0;
|
|
1357
1362
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1358
1363
|
expected = switch (endian) {
|
|
1359
|
-
|
|
1360
|
-
|
|
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 },
|
|
1361
1366
|
};
|
|
1362
1367
|
try expectLog1(expected, buf[0..9]);
|
|
1363
1368
|
stream.reset();
|
|
@@ -1365,8 +1370,8 @@ test "|switch|" {
|
|
|
1365
1370
|
snorlax.hp = 400;
|
|
1366
1371
|
try log.switched(.{ p2.ident(3), &snorlax });
|
|
1367
1372
|
expected = switch (endian) {
|
|
1368
|
-
|
|
1369
|
-
|
|
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 },
|
|
1370
1375
|
};
|
|
1371
1376
|
try expectLog1(expected, buf[0..9]);
|
|
1372
1377
|
stream.reset();
|
|
@@ -1384,8 +1389,8 @@ test "|switch|" {
|
|
|
1384
1389
|
N(gen2.Gender.Female),
|
|
1385
1390
|
91,
|
|
1386
1391
|
} ++ switch (endian) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1392
|
+
big => .{ 0, 200, 1, 144, par },
|
|
1393
|
+
little => .{ 200, 0, 144, 1, par },
|
|
1389
1394
|
});
|
|
1390
1395
|
try expectLog2(expected, buf[0..10]);
|
|
1391
1396
|
stream.reset();
|
|
@@ -1414,8 +1419,8 @@ test "|faint|" {
|
|
|
1414
1419
|
test "|turn|" {
|
|
1415
1420
|
try log.turn(.{42});
|
|
1416
1421
|
const expected = switch (endian) {
|
|
1417
|
-
|
|
1418
|
-
|
|
1422
|
+
big => &.{ N(ArgType.Turn), 0, 42, N(ArgType.None) },
|
|
1423
|
+
little => &.{ N(ArgType.Turn), 42, 0, N(ArgType.None) },
|
|
1419
1424
|
};
|
|
1420
1425
|
try expectLog1(expected, buf[0..4]);
|
|
1421
1426
|
stream.reset();
|
|
@@ -1439,8 +1444,8 @@ test "|-damage|" {
|
|
|
1439
1444
|
chansey.status = gen1.Status.slp(1);
|
|
1440
1445
|
try log.damage(.{ p2.ident(2), &chansey, .None });
|
|
1441
1446
|
var expected: []const u8 = switch (endian) {
|
|
1442
|
-
|
|
1443
|
-
|
|
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) },
|
|
1444
1449
|
};
|
|
1445
1450
|
try expectLog1(expected, buf[0..8]);
|
|
1446
1451
|
stream.reset();
|
|
@@ -1450,8 +1455,8 @@ test "|-damage|" {
|
|
|
1450
1455
|
chansey.status = 0;
|
|
1451
1456
|
try log.damage(.{ p2.ident(2), &chansey, .Confusion });
|
|
1452
1457
|
expected = switch (endian) {
|
|
1453
|
-
|
|
1454
|
-
|
|
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) },
|
|
1455
1460
|
};
|
|
1456
1461
|
try expectLog1(expected, buf[0..8]);
|
|
1457
1462
|
stream.reset();
|
|
@@ -1459,8 +1464,8 @@ test "|-damage|" {
|
|
|
1459
1464
|
chansey.status = gen1.Status.init(.PSN);
|
|
1460
1465
|
try log.damage(.{ p2.ident(2), &chansey, .RecoilOf, p1.ident(1) });
|
|
1461
1466
|
expected = switch (endian) {
|
|
1462
|
-
|
|
1463
|
-
|
|
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 },
|
|
1464
1469
|
};
|
|
1465
1470
|
try expectLog1(expected, buf[0..9]);
|
|
1466
1471
|
stream.reset();
|
|
@@ -1472,8 +1477,8 @@ test "|-heal|" {
|
|
|
1472
1477
|
chansey.status = gen1.Status.slp(1);
|
|
1473
1478
|
try log.heal(.{ p2.ident(2), &chansey, .None });
|
|
1474
1479
|
var expected: []const u8 = switch (endian) {
|
|
1475
|
-
|
|
1476
|
-
|
|
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) },
|
|
1477
1482
|
};
|
|
1478
1483
|
try expectLog1(expected, buf[0..8]);
|
|
1479
1484
|
stream.reset();
|
|
@@ -1483,16 +1488,16 @@ test "|-heal|" {
|
|
|
1483
1488
|
chansey.status = 0;
|
|
1484
1489
|
try log.heal(.{ p2.ident(2), &chansey, .Silent });
|
|
1485
1490
|
expected = switch (endian) {
|
|
1486
|
-
|
|
1487
|
-
|
|
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) },
|
|
1488
1493
|
};
|
|
1489
1494
|
try expectLog1(expected, buf[0..8]);
|
|
1490
1495
|
stream.reset();
|
|
1491
1496
|
|
|
1492
1497
|
try log.heal(.{ p2.ident(2), &chansey, .Drain, p1.ident(1) });
|
|
1493
1498
|
expected = switch (endian) {
|
|
1494
|
-
|
|
1495
|
-
|
|
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 },
|
|
1496
1501
|
};
|
|
1497
1502
|
try expectLog1(expected, buf[0..9]);
|
|
1498
1503
|
stream.reset();
|
|
@@ -1735,8 +1740,8 @@ test "|drag|" {
|
|
|
1735
1740
|
N(gen2.Gender.Female),
|
|
1736
1741
|
91,
|
|
1737
1742
|
} ++ switch (endian) {
|
|
1738
|
-
|
|
1739
|
-
|
|
1743
|
+
big => .{ 0, 200, 1, 144, par },
|
|
1744
|
+
little => .{ 200, 0, 144, 1, par },
|
|
1740
1745
|
});
|
|
1741
1746
|
try expectLog2(expected, buf[0..10]);
|
|
1742
1747
|
stream.reset();
|
|
@@ -1769,8 +1774,8 @@ test "|-sethp|" {
|
|
|
1769
1774
|
blissey.hp = 612;
|
|
1770
1775
|
try log.sethp(.{ p2.ident(2), &blissey, .Silent });
|
|
1771
1776
|
const expected: []const u8 = switch (endian) {
|
|
1772
|
-
|
|
1773
|
-
|
|
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) },
|
|
1774
1779
|
};
|
|
1775
1780
|
try expectLog2(expected, buf[0..8]);
|
|
1776
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
|
|
|
@@ -50,12 +57,12 @@ pub fn Rational(comptime T: type) type {
|
|
|
50
57
|
// If our parameters are not fully reduced they may prematurely
|
|
51
58
|
// cause overflow/loss of precision after the multiplication below
|
|
52
59
|
assert(switch (@typeInfo(@TypeOf(p, q))) {
|
|
53
|
-
|
|
60
|
+
ComptimeInt, ComptimeFloat => comptime gcd(p, q),
|
|
54
61
|
else => 1,
|
|
55
62
|
} == 1);
|
|
56
63
|
|
|
57
64
|
switch (@typeInfo(T)) {
|
|
58
|
-
|
|
65
|
+
Int => {
|
|
59
66
|
// Greedily attempt to multiply and if it fails, reduce and try again
|
|
60
67
|
r.multiplication(p, q) catch |err| switch (err) {
|
|
61
68
|
error.Overflow => {
|
|
@@ -65,16 +72,16 @@ pub fn Rational(comptime T: type) type {
|
|
|
65
72
|
else => unreachable,
|
|
66
73
|
};
|
|
67
74
|
},
|
|
68
|
-
|
|
75
|
+
Float => {
|
|
69
76
|
// Reduce in situations where we're likely to start losing precision
|
|
70
77
|
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
71
78
|
|
|
72
79
|
r.p *= switch (@typeInfo(@TypeOf(p))) {
|
|
73
|
-
|
|
80
|
+
Float, ComptimeFloat => p,
|
|
74
81
|
else => @floatFromInt(p),
|
|
75
82
|
};
|
|
76
83
|
r.q *= switch (@typeInfo(@TypeOf(q))) {
|
|
77
|
-
|
|
84
|
+
Float, ComptimeFloat => q,
|
|
78
85
|
else => @floatFromInt(q),
|
|
79
86
|
};
|
|
80
87
|
|
|
@@ -89,7 +96,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
89
96
|
/// Add two rationals using the identity (a/b) + (c/d) = (ad+bc)/(bd).
|
|
90
97
|
pub fn add(r: *Self, s: *Self) Error!void {
|
|
91
98
|
switch (@typeInfo(T)) {
|
|
92
|
-
|
|
99
|
+
Int => {
|
|
93
100
|
if (r.q == s.q) {
|
|
94
101
|
r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
|
|
95
102
|
error.Overflow => val: {
|
|
@@ -110,7 +117,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
110
117
|
};
|
|
111
118
|
}
|
|
112
119
|
},
|
|
113
|
-
|
|
120
|
+
Float => {
|
|
114
121
|
if (r.q == s.q) {
|
|
115
122
|
if (r.p > REDUCE) r.reduce();
|
|
116
123
|
if (s.p > REDUCE) s.reduce();
|
|
@@ -135,7 +142,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
135
142
|
/// Multiplies two rationals.
|
|
136
143
|
pub fn mul(r: *Self, s: *Self) Error!void {
|
|
137
144
|
switch (@typeInfo(T)) {
|
|
138
|
-
|
|
145
|
+
Int => {
|
|
139
146
|
r.multiplication(s.p, s.q) catch |err| switch (err) {
|
|
140
147
|
error.Overflow => {
|
|
141
148
|
r.reduce();
|
|
@@ -145,7 +152,7 @@ pub fn Rational(comptime T: type) type {
|
|
|
145
152
|
else => unreachable,
|
|
146
153
|
};
|
|
147
154
|
},
|
|
148
|
-
|
|
155
|
+
Float => {
|
|
149
156
|
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
150
157
|
if (s.q > REDUCE or s.p > REDUCE) s.reduce();
|
|
151
158
|
|
|
@@ -201,46 +208,37 @@ pub fn Rational(comptime T: type) type {
|
|
|
201
208
|
};
|
|
202
209
|
}
|
|
203
210
|
|
|
204
|
-
// https://lemire.me/blog/greatest-common-divisor-the-extended-euclidean-algorithm-and-speed/
|
|
205
211
|
fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
|
|
206
212
|
assert(p >= 1);
|
|
207
213
|
assert(q >= 1);
|
|
208
214
|
|
|
209
|
-
|
|
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
|
+
|
|
210
221
|
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;
|
|
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);
|
|
241
239
|
}
|
|
242
240
|
|
|
243
|
-
const result =
|
|
241
|
+
const result = y << @intCast(shift);
|
|
244
242
|
assert(result > 0);
|
|
245
243
|
return result;
|
|
246
244
|
},
|
|
@@ -263,7 +261,7 @@ fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
|
|
|
263
261
|
|
|
264
262
|
test gcd {
|
|
265
263
|
const seed = if (@hasDecl(std.testing, "random_seed")) std.testing.random_seed else 0x12345678;
|
|
266
|
-
var prng = std.Random.DefaultPrng.init(seed);
|
|
264
|
+
var prng = (if (@hasDecl(std, "Random")) std.Random else std.rand).DefaultPrng.init(seed);
|
|
267
265
|
var random = prng.random();
|
|
268
266
|
|
|
269
267
|
for (0..1000) |_| {
|
|
@@ -276,6 +274,8 @@ test gcd {
|
|
|
276
274
|
@as(u32, @intFromFloat(gcd(@as(f64, @floatFromInt(a)), @as(f64, @floatFromInt(b))))),
|
|
277
275
|
);
|
|
278
276
|
}
|
|
277
|
+
|
|
278
|
+
try expectEqual(gcd(300_000, @as(u32, 2_300_000)), 100_000); // NB: @ctz requires an @intCast
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
fn doTurn(r: anytype) !void {
|
package/src/lib/common/rng.zig
CHANGED
|
@@ -74,7 +74,7 @@ pub const PSRNG = extern struct {
|
|
|
74
74
|
|
|
75
75
|
test PSRNG {
|
|
76
76
|
var psrng: PSRNG = .{ .src = .{ .seed = 0x0001000200030004 } };
|
|
77
|
-
try expectEqual(121, psrng.range(u8, 0, 256));
|
|
77
|
+
try expectEqual(@as(u8, 121), psrng.range(u8, 0, 256));
|
|
78
78
|
try expectEqual(false, psrng.chance(u8, 128, 256)); // 226 < 128
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -111,9 +111,9 @@ test Gen12 {
|
|
|
111
111
|
try expectEqual(e, rng.next());
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
try expectEqual(16, Gen12.percent(6) + 1);
|
|
115
|
-
try expectEqual(16, Gen12.percent(7) - 1);
|
|
116
|
-
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);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// https://pkmn.cc/pokeemerald/src/random.c
|