@pkmn/engine 0.1.0-dev.26a79a0e → 0.1.0-dev.2916f414
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 -1
- package/README.md +46 -1
- package/package.json +18 -18
- package/src/lib/common/rational.zig +210 -144
- package/src/lib/common/util.zig +5 -0
- package/src/lib/gen1/calc.zig +18 -25
- package/src/lib/gen1/mechanics.zig +1 -1
- package/src/lib/gen2/calc.zig +1 -1
- package/src/lib/gen2/mechanics.zig +1 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -61,7 +61,10 @@ walk through how to install Zig on each platform - the engine code should work o
|
|
|
61
61
|
though tracks Zig's master branch so this may change in the future if breaking language changes are
|
|
62
62
|
introduced. Note that **due to a [bug in the Zig
|
|
63
63
|
compiler](https://github.com/ziglang/zig/issues/17768), compiling with a version of Zig before
|
|
64
|
-
`0.12.0-dev.876+aaf46187a` is recommended for performance**.
|
|
64
|
+
`0.12.0-dev.876+aaf46187a` is recommended for performance**. Alternatively, building a version of
|
|
65
|
+
[Zig from source](https://github.com/ziglang/zig/wiki/Building-Zig-From-Source) after having applied
|
|
66
|
+
a [patch](zig.patch) to revert [ziglang/zig#17391](ziglang/zig/pull/17391) will restore performance
|
|
67
|
+
while allowing modern Zig features to be used.
|
|
65
68
|
|
|
66
69
|
`libpkmn` can be built with `-Dshowdown` to instead produce the Pokémon Showdown compatible
|
|
67
70
|
`libpkmn-showdown` library. Furthermore, protocol message logging can be enabled through `-Dlog`.
|
|
@@ -115,6 +118,48 @@ error: dependency is missing hash field
|
|
|
115
118
|
note: expected .hash = "122056b93b403033cb7d9bed96a02c2eb8cc1275515976167726ada6eb4207e8ef8a",
|
|
116
119
|
```
|
|
117
120
|
|
|
121
|
+
You must add `build.zig.zon` to the engine package to be able to import it in your `build.zig`:
|
|
122
|
+
|
|
123
|
+
<details><summary>Before v0.14.0</summary>
|
|
124
|
+
|
|
125
|
+
```zig
|
|
126
|
+
.{
|
|
127
|
+
.name = "pkmn",
|
|
128
|
+
.version = "0.1.0-dev",
|
|
129
|
+
.minimum_zig_version = "0.11.0",
|
|
130
|
+
.paths = .{
|
|
131
|
+
"build.zig",
|
|
132
|
+
"docs",
|
|
133
|
+
"LICENSE",
|
|
134
|
+
"README.md",
|
|
135
|
+
"src/include",
|
|
136
|
+
"src/lib",
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
</details>
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
<details><summary>After v0.14.0</summary>
|
|
144
|
+
|
|
145
|
+
```zig
|
|
146
|
+
.{
|
|
147
|
+
.name = .pkmn,
|
|
148
|
+
.version = "0.1.0-dev",
|
|
149
|
+
.minimum_zig_version = "0.14.0",
|
|
150
|
+
.paths = .{
|
|
151
|
+
"build.zig",
|
|
152
|
+
"docs",
|
|
153
|
+
"LICENSE",
|
|
154
|
+
"README.md",
|
|
155
|
+
"src/include",
|
|
156
|
+
"src/lib",
|
|
157
|
+
},
|
|
158
|
+
.fingerprint = 0x74a002f44f200a47,
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
</details>
|
|
162
|
+
|
|
118
163
|
Note that this hash is **not** going to be the same as the commit hash. After adding the hash to
|
|
119
164
|
your `build.zig.zon` you will be able to import the `pkmn` package's build helpers into your
|
|
120
165
|
`build.zig`:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pkmn/engine",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.2916f414",
|
|
4
4
|
"description": "A minimal, complete, Pokémon battle simulation engine optimized for performance",
|
|
5
5
|
"repository": "github:pkmn/engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -98,38 +98,38 @@
|
|
|
98
98
|
"access": "public"
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
|
-
"@pkmn/data": "^0.9.
|
|
102
|
-
"@pkmn/protocol": "^0.6.
|
|
101
|
+
"@pkmn/data": "^0.9.31",
|
|
102
|
+
"@pkmn/protocol": "^0.6.31"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
|
-
"@pkmn/eslint-config": "^9.
|
|
106
|
-
"@pkmn/img": "^0.2.
|
|
105
|
+
"@pkmn/eslint-config": "^9.7.0",
|
|
106
|
+
"@pkmn/img": "^0.2.36",
|
|
107
107
|
"@pkmn/sets": "^5.1.2",
|
|
108
|
-
"@pkmn/sim": "0.9.
|
|
109
|
-
"@pkmn/smogon": "^0.5.
|
|
108
|
+
"@pkmn/sim": "0.9.31",
|
|
109
|
+
"@pkmn/smogon": "^0.5.19",
|
|
110
110
|
"@types/html-minifier": "^4.0.5",
|
|
111
111
|
"@types/jest": "^29.5.14",
|
|
112
112
|
"@types/minimist": "^1.2.5",
|
|
113
113
|
"@types/mustache": "^4.2.5",
|
|
114
|
-
"@types/node": "^22.
|
|
115
|
-
"@types/react": "
|
|
114
|
+
"@types/node": "^22.13.9",
|
|
115
|
+
"@types/react": "<=18",
|
|
116
116
|
"@types/semver": "^7.5.8",
|
|
117
|
-
"@types/vscode": "^1.
|
|
118
|
-
"@vitest/coverage-v8": "^
|
|
119
|
-
"@vscode/vsce": "^3.2.
|
|
120
|
-
"binaryen": "^
|
|
121
|
-
"esbuild": "^0.
|
|
122
|
-
"eslint": "^9.
|
|
117
|
+
"@types/vscode": "^1.97.0",
|
|
118
|
+
"@vitest/coverage-v8": "^3.0.7",
|
|
119
|
+
"@vscode/vsce": "^3.2.2",
|
|
120
|
+
"binaryen": "^122.0.0",
|
|
121
|
+
"esbuild": "^0.25.0",
|
|
122
|
+
"eslint": "^9.21.0",
|
|
123
123
|
"html-minifier": "^4.0.0",
|
|
124
124
|
"json-stringify-pretty-compact": "3.0.0",
|
|
125
125
|
"minimist": "^1.2.8",
|
|
126
126
|
"mustache": "^4.2.0",
|
|
127
|
-
"semver": "^7.
|
|
127
|
+
"semver": "^7.7.1",
|
|
128
128
|
"source-map-support": "^0.5.21",
|
|
129
129
|
"table": "^6.9.0",
|
|
130
130
|
"trakr": "^0.2.0",
|
|
131
|
-
"typescript": "^5.
|
|
132
|
-
"vitest": "^
|
|
131
|
+
"typescript": "^5.8.2",
|
|
132
|
+
"vitest": "^3.0.7"
|
|
133
133
|
},
|
|
134
134
|
"scripts": {
|
|
135
135
|
"lint": "eslint --cache src",
|
|
@@ -19,7 +19,23 @@ const ComptimeFloat =
|
|
|
19
19
|
/// sometimes overflows in places where an eager reduced implementation would not - to achieve
|
|
20
20
|
/// the same behavior as an eager implementation simply call `reduce` after each operation.
|
|
21
21
|
pub fn Rational(comptime T: type) type {
|
|
22
|
-
|
|
22
|
+
// With floats we can't rely on an overflow bit to let us know when to reduce, so we
|
|
23
|
+
// instead start reducing when we get sufficiently close to the limit of the mantissa
|
|
24
|
+
// (in our domain we expect updates to involve numbers < 2**10, so we should be safe
|
|
25
|
+
// not reducing before we are 2**10 away from "overflowing" the mantissa)
|
|
26
|
+
const o = if (@typeInfo(T) == Float)
|
|
27
|
+
std.math.pow(T, 2, std.math.floatMantissaBits(T) - 10)
|
|
28
|
+
else
|
|
29
|
+
0;
|
|
30
|
+
|
|
31
|
+
const Err = switch (@typeInfo(T)) {
|
|
32
|
+
Int => error{Overflow},
|
|
33
|
+
Float => error{},
|
|
34
|
+
else => unreachable,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Zig doesn't allow integers > u128 in an extern struct...
|
|
38
|
+
return if (@sizeOf(T) <= 16) extern struct {
|
|
23
39
|
const Self = @This();
|
|
24
40
|
|
|
25
41
|
/// Numerator. Must always be >= 1. Not guaranteed to be reduced in all cases.
|
|
@@ -27,21 +43,8 @@ pub fn Rational(comptime T: type) type {
|
|
|
27
43
|
/// Denominator. Must always be >= 1. Not guaranteed to be reduced in all cases.
|
|
28
44
|
q: T = 1,
|
|
29
45
|
|
|
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)
|
|
35
|
-
std.math.pow(T, 2, std.math.floatMantissaBits(T) - 10)
|
|
36
|
-
else
|
|
37
|
-
0;
|
|
38
|
-
|
|
39
46
|
/// Possible error returned by operations on the Rational.
|
|
40
|
-
pub const Error =
|
|
41
|
-
Int => error{Overflow},
|
|
42
|
-
Float => error{},
|
|
43
|
-
else => unreachable,
|
|
44
|
-
};
|
|
47
|
+
pub const Error = Err;
|
|
45
48
|
|
|
46
49
|
/// Resets the rational back to 1.
|
|
47
50
|
pub fn reset(r: *Self) void {
|
|
@@ -52,135 +55,69 @@ pub fn Rational(comptime T: type) type {
|
|
|
52
55
|
/// Update the rational by multiplying its numerator by p and its denominator by q.
|
|
53
56
|
/// Both p and q must be >= 1, and if computable at comptime must have no common factors.
|
|
54
57
|
pub fn update(r: *Self, p: anytype, q: anytype) Error!void {
|
|
55
|
-
|
|
56
|
-
assert(p >= 1);
|
|
57
|
-
assert(q >= 1);
|
|
58
|
-
|
|
59
|
-
// If our parameters are not fully reduced they may prematurely
|
|
60
|
-
// cause overflow/loss of precision after the multiplication below
|
|
61
|
-
assert(switch (@typeInfo(@TypeOf(p, q))) {
|
|
62
|
-
ComptimeInt, ComptimeFloat => comptime gcd(p, q),
|
|
63
|
-
else => 1,
|
|
64
|
-
} == 1);
|
|
65
|
-
|
|
66
|
-
switch (@typeInfo(T)) {
|
|
67
|
-
Int => {
|
|
68
|
-
// Greedily attempt to multiply and if it fails, reduce and try again
|
|
69
|
-
r.multiplication(p, q) catch |err| switch (err) {
|
|
70
|
-
error.Overflow => {
|
|
71
|
-
r.reduce();
|
|
72
|
-
try r.multiplication(p, q);
|
|
73
|
-
},
|
|
74
|
-
else => unreachable,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
Float => {
|
|
78
|
-
// Reduce in situations where we're likely to start losing precision
|
|
79
|
-
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
80
|
-
|
|
81
|
-
r.p *= switch (@typeInfo(@TypeOf(p))) {
|
|
82
|
-
Float, ComptimeFloat => p,
|
|
83
|
-
else => @floatFromInt(p),
|
|
84
|
-
};
|
|
85
|
-
r.q *= switch (@typeInfo(@TypeOf(q))) {
|
|
86
|
-
Float, ComptimeFloat => q,
|
|
87
|
-
else => @floatFromInt(q),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// We should always be dealing with whole numbers
|
|
91
|
-
assert(std.math.modf(r.p).fpart == 0);
|
|
92
|
-
assert(std.math.modf(r.q).fpart == 0);
|
|
93
|
-
},
|
|
94
|
-
else => unreachable,
|
|
95
|
-
}
|
|
58
|
+
return update_(T, o, r, p, q);
|
|
96
59
|
}
|
|
97
60
|
|
|
98
61
|
/// Add two rationals using the identity (a/b) + (c/d) = (ad+bc)/(bd).
|
|
99
|
-
pub fn add(r: *Self, s:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (r.q == s.q) {
|
|
103
|
-
r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
|
|
104
|
-
error.Overflow => val: {
|
|
105
|
-
r.reduce();
|
|
106
|
-
s.reduce();
|
|
107
|
-
break :val try std.math.add(T, r.p, s.p);
|
|
108
|
-
},
|
|
109
|
-
else => unreachable,
|
|
110
|
-
};
|
|
111
|
-
} else {
|
|
112
|
-
r.addition(s.p, s.q) catch |err| switch (err) {
|
|
113
|
-
error.Overflow => {
|
|
114
|
-
r.reduce();
|
|
115
|
-
s.reduce();
|
|
116
|
-
try r.addition(s.p, s.q);
|
|
117
|
-
},
|
|
118
|
-
else => unreachable,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
Float => {
|
|
123
|
-
if (r.q == s.q) {
|
|
124
|
-
if (r.p > REDUCE) r.reduce();
|
|
125
|
-
if (s.p > REDUCE) s.reduce();
|
|
126
|
-
|
|
127
|
-
r.p += s.p;
|
|
128
|
-
} else {
|
|
129
|
-
// Always reduce to minimize loss of precision from the multiplications
|
|
130
|
-
r.reduce();
|
|
131
|
-
s.reduce();
|
|
62
|
+
pub fn add(r: *Self, s: anytype) Error!void {
|
|
63
|
+
return add_(T, o, r, s);
|
|
64
|
+
}
|
|
132
65
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
66
|
+
/// Multiplies two rationals.
|
|
67
|
+
pub fn mul(r: *Self, s: anytype) Error!void {
|
|
68
|
+
return mul_(T, o, r, s);
|
|
69
|
+
}
|
|
136
70
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
else => unreachable,
|
|
141
|
-
}
|
|
71
|
+
/// Normalize the rational by reducing by the greatest common divisor.
|
|
72
|
+
pub fn reduce(r: *Self) void {
|
|
73
|
+
reduce_(r);
|
|
142
74
|
}
|
|
143
75
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
Float => {
|
|
158
|
-
if (r.q > REDUCE or r.p > REDUCE) r.reduce();
|
|
159
|
-
if (s.q > REDUCE or s.p > REDUCE) s.reduce();
|
|
76
|
+
pub fn format(
|
|
77
|
+
self: Self,
|
|
78
|
+
comptime fmt: []const u8,
|
|
79
|
+
opts: std.fmt.FormatOptions,
|
|
80
|
+
writer: anytype,
|
|
81
|
+
) !void {
|
|
82
|
+
_ = .{ fmt, opts };
|
|
83
|
+
try writer.print("{d}/{d}", .{ self.p, self.q });
|
|
84
|
+
}
|
|
85
|
+
} else struct {
|
|
86
|
+
const Self = @This();
|
|
160
87
|
|
|
161
|
-
|
|
162
|
-
|
|
88
|
+
/// Numerator. Must always be >= 1. Not guaranteed to be reduced in all cases.
|
|
89
|
+
p: T = 1,
|
|
90
|
+
/// Denominator. Must always be >= 1. Not guaranteed to be reduced in all cases.
|
|
91
|
+
q: T = 1,
|
|
163
92
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
93
|
+
/// Possible error returned by operations on the Rational.
|
|
94
|
+
pub const Error = Err;
|
|
95
|
+
|
|
96
|
+
/// Resets the rational back to 1.
|
|
97
|
+
pub fn reset(r: *Self) void {
|
|
98
|
+
r.p = 1;
|
|
99
|
+
r.q = 1;
|
|
169
100
|
}
|
|
170
101
|
|
|
171
|
-
///
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
102
|
+
/// Update the rational by multiplying its numerator by p and its denominator by q.
|
|
103
|
+
/// Both p and q must be >= 1, and if computable at comptime must have no common factors.
|
|
104
|
+
pub fn update(r: *Self, p: anytype, q: anytype) Error!void {
|
|
105
|
+
return update_(T, o, r, p, q);
|
|
106
|
+
}
|
|
175
107
|
|
|
176
|
-
|
|
177
|
-
|
|
108
|
+
/// Add two rationals using the identity (a/b) + (c/d) = (ad+bc)/(bd).
|
|
109
|
+
pub fn add(r: *Self, s: anytype) Error!void {
|
|
110
|
+
return add_(T, o, r, s);
|
|
111
|
+
}
|
|
178
112
|
|
|
179
|
-
|
|
180
|
-
|
|
113
|
+
/// Multiplies two rationals.
|
|
114
|
+
pub fn mul(r: *Self, s: anytype) Error!void {
|
|
115
|
+
return mul_(T, o, r, s);
|
|
116
|
+
}
|
|
181
117
|
|
|
182
|
-
|
|
183
|
-
|
|
118
|
+
/// Normalize the rational by reducing by the greatest common divisor.
|
|
119
|
+
pub fn reduce(r: *Self) void {
|
|
120
|
+
reduce_(r);
|
|
184
121
|
}
|
|
185
122
|
|
|
186
123
|
pub fn format(
|
|
@@ -192,22 +129,151 @@ pub fn Rational(comptime T: type) type {
|
|
|
192
129
|
_ = .{ fmt, opts };
|
|
193
130
|
try writer.print("{d}/{d}", .{ self.p, self.q });
|
|
194
131
|
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
195
134
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
135
|
+
fn update_(comptime T: type, comptime o: comptime_int, r: anytype, p: anytype, q: anytype) !void {
|
|
136
|
+
// std.debug.print("({d}/{d}) * ", .{ p, q }); // DEBUG
|
|
137
|
+
assert(p >= 1);
|
|
138
|
+
assert(q >= 1);
|
|
200
139
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
140
|
+
// If our parameters are not fully reduced they may prematurely
|
|
141
|
+
// cause overflow/loss of precision after the multiplication below
|
|
142
|
+
assert(switch (@typeInfo(@TypeOf(p, q))) {
|
|
143
|
+
ComptimeInt, ComptimeFloat => comptime gcd(p, q),
|
|
144
|
+
else => 1,
|
|
145
|
+
} == 1);
|
|
206
146
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
147
|
+
switch (@typeInfo(T)) {
|
|
148
|
+
Int => {
|
|
149
|
+
// Greedily attempt to multiply and if it fails, reduce and try again
|
|
150
|
+
multiplication(T, r, p, q) catch |err| switch (err) {
|
|
151
|
+
error.Overflow => {
|
|
152
|
+
r.reduce();
|
|
153
|
+
try multiplication(T, r, p, q);
|
|
154
|
+
},
|
|
155
|
+
else => unreachable,
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
Float => {
|
|
159
|
+
// Reduce in situations where we're likely to start losing precision
|
|
160
|
+
if (r.q > o or r.p > o) r.reduce();
|
|
161
|
+
|
|
162
|
+
r.p *= switch (@typeInfo(@TypeOf(p))) {
|
|
163
|
+
Float, ComptimeFloat => p,
|
|
164
|
+
else => @floatFromInt(p),
|
|
165
|
+
};
|
|
166
|
+
r.q *= switch (@typeInfo(@TypeOf(q))) {
|
|
167
|
+
Float, ComptimeFloat => q,
|
|
168
|
+
else => @floatFromInt(q),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// We should always be dealing with whole numbers
|
|
172
|
+
assert(std.math.modf(r.p).fpart == 0);
|
|
173
|
+
assert(std.math.modf(r.q).fpart == 0);
|
|
174
|
+
},
|
|
175
|
+
else => unreachable,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fn add_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !void {
|
|
180
|
+
switch (@typeInfo(T)) {
|
|
181
|
+
Int => {
|
|
182
|
+
if (r.q == s.q) {
|
|
183
|
+
r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
|
|
184
|
+
error.Overflow => val: {
|
|
185
|
+
r.reduce();
|
|
186
|
+
s.reduce();
|
|
187
|
+
break :val try std.math.add(T, r.p, s.p);
|
|
188
|
+
},
|
|
189
|
+
else => unreachable,
|
|
190
|
+
};
|
|
191
|
+
} else {
|
|
192
|
+
addition(T, r, s.p, s.q) catch |err| switch (err) {
|
|
193
|
+
error.Overflow => {
|
|
194
|
+
r.reduce();
|
|
195
|
+
s.reduce();
|
|
196
|
+
try addition(T, r, s.p, s.q);
|
|
197
|
+
},
|
|
198
|
+
else => unreachable,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
Float => {
|
|
203
|
+
if (r.q == s.q) {
|
|
204
|
+
if (r.p > o) r.reduce();
|
|
205
|
+
if (s.p > o) s.reduce();
|
|
206
|
+
|
|
207
|
+
r.p += s.p;
|
|
208
|
+
} else {
|
|
209
|
+
// Always reduce to minimize loss of precision from the multiplications
|
|
210
|
+
r.reduce();
|
|
211
|
+
s.reduce();
|
|
212
|
+
|
|
213
|
+
r.p = (r.p * s.q) + (r.q * s.p);
|
|
214
|
+
r.q *= s.q;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
assert(std.math.modf(r.p).fpart == 0);
|
|
218
|
+
assert(std.math.modf(r.q).fpart == 0);
|
|
219
|
+
},
|
|
220
|
+
else => unreachable,
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn mul_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !void {
|
|
225
|
+
switch (@typeInfo(T)) {
|
|
226
|
+
Int => {
|
|
227
|
+
multiplication(T, r, s.p, s.q) catch |err| switch (err) {
|
|
228
|
+
error.Overflow => {
|
|
229
|
+
r.reduce();
|
|
230
|
+
s.reduce();
|
|
231
|
+
try multiplication(T, r, s.p, s.q);
|
|
232
|
+
},
|
|
233
|
+
else => unreachable,
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
Float => {
|
|
237
|
+
if (r.q > o or r.p > o) r.reduce();
|
|
238
|
+
if (s.q > o or s.p > o) s.reduce();
|
|
239
|
+
|
|
240
|
+
r.p *= s.p;
|
|
241
|
+
r.q *= s.q;
|
|
242
|
+
|
|
243
|
+
assert(std.math.modf(r.p).fpart == 0);
|
|
244
|
+
assert(std.math.modf(r.q).fpart == 0);
|
|
245
|
+
},
|
|
246
|
+
else => unreachable,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
fn reduce_(r: anytype) void {
|
|
251
|
+
const d = gcd(r.p, r.q);
|
|
252
|
+
if (d == 1) return;
|
|
253
|
+
|
|
254
|
+
assert(@mod(r.p, d) == 0);
|
|
255
|
+
assert(@mod(r.q, d) == 0);
|
|
256
|
+
|
|
257
|
+
r.p /= d;
|
|
258
|
+
r.q /= d;
|
|
259
|
+
|
|
260
|
+
assert(r.p >= 1);
|
|
261
|
+
assert(r.q >= 1);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fn multiplication(comptime T: type, r: anytype, p: anytype, q: anytype) !void {
|
|
265
|
+
r.p = try std.math.mul(T, r.p, p);
|
|
266
|
+
r.q = try std.math.mul(T, r.q, q);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
fn addition(comptime T: type, r: anytype, p: anytype, q: anytype) !void {
|
|
270
|
+
// (a/b) + (c/d) = (ad+bc)/(bd)
|
|
271
|
+
const d = try std.math.mul(T, r.q, q);
|
|
272
|
+
const n1 = try std.math.mul(T, r.p, q);
|
|
273
|
+
const n2 = try std.math.mul(T, r.q, p);
|
|
274
|
+
|
|
275
|
+
r.p = try std.math.add(T, n1, n2);
|
|
276
|
+
r.q = d;
|
|
211
277
|
}
|
|
212
278
|
|
|
213
279
|
fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
|
|
@@ -289,7 +355,7 @@ fn doTurn(r: anytype) !void {
|
|
|
289
355
|
}
|
|
290
356
|
|
|
291
357
|
test Rational {
|
|
292
|
-
inline for (.{ u64, u128, f64 }) |t| {
|
|
358
|
+
inline for (.{ u64, u128, u256, f64 }) |t| {
|
|
293
359
|
var r: Rational(t) = .{};
|
|
294
360
|
|
|
295
361
|
var c: t = 128;
|
package/src/lib/common/util.zig
CHANGED
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -14,7 +14,6 @@ const util = @import("../common/util.zig");
|
|
|
14
14
|
const Action = chance.Action;
|
|
15
15
|
const Actions = chance.Actions;
|
|
16
16
|
const assert = std.debug.assert;
|
|
17
|
-
const BigRational = rational.BigRational;
|
|
18
17
|
const Chance = chance.Chance;
|
|
19
18
|
const Choice = common.Choice;
|
|
20
19
|
const Confusion = chance.Confusion;
|
|
@@ -100,7 +99,7 @@ pub const Calc = struct {
|
|
|
100
99
|
self: Calc,
|
|
101
100
|
player: Player,
|
|
102
101
|
comptime field: Action.Field,
|
|
103
|
-
) ?
|
|
102
|
+
) ?util.FieldType(Action, field) {
|
|
104
103
|
if (!enabled) return null;
|
|
105
104
|
|
|
106
105
|
const val = @field(self.overrides.get(player), @tagName(field));
|
|
@@ -147,7 +146,7 @@ const Null = struct {
|
|
|
147
146
|
self: Null,
|
|
148
147
|
player: Player,
|
|
149
148
|
comptime field: Action.Field,
|
|
150
|
-
) ?
|
|
149
|
+
) ?util.FieldType(Action, field) {
|
|
151
150
|
_ = .{ self, player };
|
|
152
151
|
return null;
|
|
153
152
|
}
|
|
@@ -217,10 +216,7 @@ pub fn transitions(
|
|
|
217
216
|
const p1 = b.side(.P1);
|
|
218
217
|
const p2 = b.side(.P2);
|
|
219
218
|
|
|
220
|
-
var p =
|
|
221
|
-
defer p.deinit();
|
|
222
|
-
try p.val.p.set(0);
|
|
223
|
-
try p.val.q.set(1);
|
|
219
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
224
220
|
try frontier.append(opts.chance.actions);
|
|
225
221
|
|
|
226
222
|
// zig fmt: off
|
|
@@ -235,10 +231,7 @@ pub fn transitions(
|
|
|
235
231
|
const f = frontier.items[i];
|
|
236
232
|
const saved = stats.saved;
|
|
237
233
|
|
|
238
|
-
var r =
|
|
239
|
-
defer r.deinit();
|
|
240
|
-
try r.val.p.set(0);
|
|
241
|
-
try r.val.q.set(1);
|
|
234
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
242
235
|
|
|
243
236
|
try debug(writer, f, .{
|
|
244
237
|
.shape = true,
|
|
@@ -351,11 +344,13 @@ pub fn transitions(
|
|
|
351
344
|
|
|
352
345
|
q.reduce();
|
|
353
346
|
try p.add(q);
|
|
347
|
+
p.reduce();
|
|
354
348
|
try r.add(q);
|
|
349
|
+
r.reduce();
|
|
355
350
|
|
|
356
351
|
stats.saved += 1;
|
|
357
352
|
|
|
358
|
-
if (p.
|
|
353
|
+
if (p.q < p.p) {
|
|
359
354
|
err("improper fraction {}", .{p}, options.seed);
|
|
360
355
|
return error.TestUnexpectedResult;
|
|
361
356
|
}
|
|
@@ -393,13 +388,15 @@ pub fn transitions(
|
|
|
393
388
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
394
389
|
|
|
395
390
|
assert(stats.saved > saved);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
391
|
+
// TODO: ziglang/zig#22667
|
|
392
|
+
// const old = @hasDecl(std.fmt, "formatFloatDecimal");
|
|
393
|
+
// if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
|
|
394
|
+
// try writer.print(
|
|
395
|
+
// " {} ({d:.2}%)\n = ──────────\n {} ({d:.2}%)\n\n",
|
|
396
|
+
// .{r, 100 * @as(f128, @floatFromInt(r.p)) / @as(f128, @floatFromInt(r.q)),
|
|
397
|
+
// p, 100 * @as(f128, @floatFromInt(p.p)) / @as(f128, @floatFromInt(p.q))},
|
|
398
|
+
// );
|
|
399
|
+
// }
|
|
403
400
|
|
|
404
401
|
}
|
|
405
402
|
|
|
@@ -412,7 +409,8 @@ pub fn transitions(
|
|
|
412
409
|
|
|
413
410
|
stats.seen = seen.count();
|
|
414
411
|
|
|
415
|
-
|
|
412
|
+
p.reduce();
|
|
413
|
+
if (p.p != 1 or p.q != 1) {
|
|
416
414
|
err("expected 1, found {}", .{p}, options.seed);
|
|
417
415
|
return error.TestExpectedEqual;
|
|
418
416
|
}
|
|
@@ -420,11 +418,6 @@ pub fn transitions(
|
|
|
420
418
|
return stats;
|
|
421
419
|
}
|
|
422
420
|
|
|
423
|
-
fn percent(r: anytype) f128 {
|
|
424
|
-
return 100 * @as(f128, @floatFromInt(r.p.to(u128) catch unreachable)) /
|
|
425
|
-
@as(f128, @floatFromInt(r.q.to(u128) catch unreachable));
|
|
426
|
-
}
|
|
427
|
-
|
|
428
421
|
pub fn update(
|
|
429
422
|
battle: anytype,
|
|
430
423
|
c1: Choice,
|
|
@@ -1297,7 +1297,7 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
1299
|
// NOTE: llvm/llvm-project#58557
|
|
1300
|
-
battle.last_damage = if (comptime builtin.target.isWasm())
|
|
1300
|
+
battle.last_damage = if (comptime builtin.target.cpu.arch.isWasm())
|
|
1301
1301
|
@max(battle.last_damage *% 2, std.math.maxInt(u16))
|
|
1302
1302
|
else
|
|
1303
1303
|
battle.last_damage *| 2;
|
package/src/lib/gen2/calc.zig
CHANGED
|
@@ -2841,7 +2841,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2841
2841
|
|
|
2842
2842
|
// NOTE: llvm/llvm-project#58557
|
|
2843
2843
|
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
-
if (!comptime builtin.target.isWasm()) return a *| b;
|
|
2844
|
+
if (!comptime builtin.target.cpu.arch.isWasm()) return a *| b;
|
|
2845
2845
|
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
2846
|
}
|
|
2847
2847
|
|