@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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2021-2024 pkmn contributors
1
+ Copyright (c) 2021-2025 pkmn contributors
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of
4
4
  this software and associated documentation files (the "Software"), to deal in
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.26a79a0e",
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.27",
102
- "@pkmn/protocol": "^0.6.30"
101
+ "@pkmn/data": "^0.9.31",
102
+ "@pkmn/protocol": "^0.6.31"
103
103
  },
104
104
  "devDependencies": {
105
- "@pkmn/eslint-config": "^9.5.0",
106
- "@pkmn/img": "^0.2.34",
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.27",
109
- "@pkmn/smogon": "^0.5.17",
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.10.6",
115
- "@types/react": "^18.3.12",
114
+ "@types/node": "^22.13.9",
115
+ "@types/react": "<=18",
116
116
  "@types/semver": "^7.5.8",
117
- "@types/vscode": "^1.96.0",
118
- "@vitest/coverage-v8": "^2.1.8",
119
- "@vscode/vsce": "^3.2.1",
120
- "binaryen": "^121.0.0",
121
- "esbuild": "^0.24.2",
122
- "eslint": "^9.18.0",
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.6.3",
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.7.3",
132
- "vitest": "^2.1.8"
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
- return extern struct {
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 = switch (@typeInfo(T)) {
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
- // std.debug.print("({d}/{d}) * ", .{ p, q }); // DEBUG
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: *Self) Error!void {
100
- switch (@typeInfo(T)) {
101
- Int => {
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
- r.p = (r.p * s.q) + (r.q * s.p);
134
- r.q *= s.q;
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
- assert(std.math.modf(r.p).fpart == 0);
138
- assert(std.math.modf(r.q).fpart == 0);
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
- /// Multiplies two rationals.
145
- pub fn mul(r: *Self, s: *Self) Error!void {
146
- switch (@typeInfo(T)) {
147
- Int => {
148
- r.multiplication(s.p, s.q) catch |err| switch (err) {
149
- error.Overflow => {
150
- r.reduce();
151
- s.reduce();
152
- try r.multiplication(s.p, s.q);
153
- },
154
- else => unreachable,
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
- r.p *= s.p;
162
- r.q *= s.q;
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
- assert(std.math.modf(r.p).fpart == 0);
165
- assert(std.math.modf(r.q).fpart == 0);
166
- },
167
- else => unreachable,
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
- /// Normalize the rational by reducing by the greatest common divisor.
172
- pub fn reduce(r: *Self) void {
173
- const d = gcd(r.p, r.q);
174
- if (d == 1) return;
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
- assert(@mod(r.p, d) == 0);
177
- assert(@mod(r.q, d) == 0);
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
- r.p /= d;
180
- r.q /= d;
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
- assert(r.p >= 1);
183
- assert(r.q >= 1);
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
- fn multiplication(r: *Self, p: anytype, q: anytype) !void {
197
- r.p = try std.math.mul(T, r.p, p);
198
- r.q = try std.math.mul(T, r.q, q);
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
- fn addition(r: *Self, p: anytype, q: anytype) !void {
202
- // (a/b) + (c/d) = (ad+bc)/(bd)
203
- const d = try std.math.mul(T, r.q, q);
204
- const n1 = try std.math.mul(T, r.p, q);
205
- const n2 = try std.math.mul(T, r.q, p);
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
- r.p = try std.math.add(T, n1, n2);
208
- r.q = d;
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;
@@ -69,3 +69,8 @@ fn CopyPtrAttrs(
69
69
  else
70
70
  .{ .Pointer = args });
71
71
  }
72
+
73
+ // NOTE: std.meta.FieldType replaced by @FieldType in 0.14.0
74
+ pub fn FieldType(comptime T: type, comptime field: std.meta.FieldEnum(T)) type {
75
+ return std.meta.fieldInfo(T, field).type;
76
+ }
@@ -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
- ) ?std.meta.FieldType(Action, field) {
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
- ) ?std.meta.FieldType(Action, field) {
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 = try BigRational.init(allocator);
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 = try BigRational.init(allocator);
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.val.q.order(p.val.p) == .lt) {
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
- const old = @hasDecl(std.fmt, "formatFloatDecimal");
397
- if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
398
- try writer.print(
399
- " {} ({d:.2}%)\n = ──────────\n {} ({d:.2}%)\n\n",
400
- .{r.val, percent(r.val), p.val, percent(p.val)},
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
- if (try p.val.p.to(u128) != 1 or try p.val.q.to(u128) != 1) {
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;
@@ -148,6 +148,6 @@ fn ReturnType(comptime field: Action.Field) type {
148
148
  return ?(switch (field) {
149
149
  .damages => u8,
150
150
  .critical_hits => Optional(bool),
151
- else => std.meta.FieldType(Action, field),
151
+ else => util.FieldType(Action, field),
152
152
  });
153
153
  }
@@ -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