@pkmn/engine 0.1.0-dev.645b4750 → 0.1.0-dev.6ba42cd5

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.
@@ -6,13 +6,6 @@ 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
-
16
9
  /// Specialization of a rational number used by the engine to compute probabilties.
17
10
  /// For performance reasons the rational is only reduced lazily and thus `reduce` must be
18
11
  /// invoked explicitly before reading. Note that this laziness means that this implementation
@@ -23,14 +16,14 @@ pub fn Rational(comptime T: type) type {
23
16
  // instead start reducing when we get sufficiently close to the limit of the mantissa
24
17
  // (in our domain we expect updates to involve numbers < 2**10, so we should be safe
25
18
  // not reducing before we are 2**10 away from "overflowing" the mantissa)
26
- const o = if (@typeInfo(T) == Float)
19
+ const o = if (@typeInfo(T) == .float)
27
20
  std.math.pow(T, 2, std.math.floatMantissaBits(T) - 10)
28
21
  else
29
22
  0;
30
23
 
31
24
  const Err = switch (@typeInfo(T)) {
32
- Int => error{Overflow},
33
- Float => error{},
25
+ .int => error{Overflow},
26
+ .float => error{},
34
27
  else => unreachable,
35
28
  };
36
29
 
@@ -68,18 +61,17 @@ pub fn Rational(comptime T: type) type {
68
61
  return mul_(T, o, r, s);
69
62
  }
70
63
 
64
+ /// Compares two rationals using the identity (a/b) > (c/d) => ad > bc
65
+ pub fn cmp(r: *Self, s: anytype) Error!std.math.Order {
66
+ return cmp_(T, o, r, s);
67
+ }
68
+
71
69
  /// Normalize the rational by reducing by the greatest common divisor.
72
70
  pub fn reduce(r: *Self) void {
73
71
  reduce_(r);
74
72
  }
75
73
 
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 };
74
+ pub fn format(self: Self, writer: *std.Io.Writer) !void {
83
75
  try writer.print("{d}/{d}", .{ self.p, self.q });
84
76
  }
85
77
  } else struct {
@@ -115,18 +107,17 @@ pub fn Rational(comptime T: type) type {
115
107
  return mul_(T, o, r, s);
116
108
  }
117
109
 
110
+ /// Compares two rationals using the identity (a/b) > (c/d) => ad > bc
111
+ pub fn cmp(r: *Self, s: anytype) Error!std.math.Order {
112
+ return cmp_(T, o, r, s);
113
+ }
114
+
118
115
  /// Normalize the rational by reducing by the greatest common divisor.
119
116
  pub fn reduce(r: *Self) void {
120
117
  reduce_(r);
121
118
  }
122
119
 
123
- pub fn format(
124
- self: Self,
125
- comptime fmt: []const u8,
126
- opts: std.fmt.FormatOptions,
127
- writer: anytype,
128
- ) !void {
129
- _ = .{ fmt, opts };
120
+ pub fn format(self: Self, writer: *std.Io.Writer) !void {
130
121
  try writer.print("{d}/{d}", .{ self.p, self.q });
131
122
  }
132
123
  };
@@ -140,12 +131,12 @@ fn update_(comptime T: type, comptime o: comptime_int, r: anytype, p: anytype, q
140
131
  // If our parameters are not fully reduced they may prematurely
141
132
  // cause overflow/loss of precision after the multiplication below
142
133
  assert(switch (@typeInfo(@TypeOf(p, q))) {
143
- ComptimeInt, ComptimeFloat => comptime gcd(p, q),
134
+ .comptime_int, .comptime_float => comptime gcd(p, q),
144
135
  else => 1,
145
136
  } == 1);
146
137
 
147
138
  switch (@typeInfo(T)) {
148
- Int => {
139
+ .int => {
149
140
  // Greedily attempt to multiply and if it fails, reduce and try again
150
141
  multiplication(T, r, p, q) catch |err| switch (err) {
151
142
  error.Overflow => {
@@ -155,16 +146,16 @@ fn update_(comptime T: type, comptime o: comptime_int, r: anytype, p: anytype, q
155
146
  else => unreachable,
156
147
  };
157
148
  },
158
- Float => {
149
+ .float => {
159
150
  // Reduce in situations where we're likely to start losing precision
160
151
  if (r.q > o or r.p > o) r.reduce();
161
152
 
162
153
  r.p *= switch (@typeInfo(@TypeOf(p))) {
163
- Float, ComptimeFloat => p,
154
+ .float, .comptime_float => p,
164
155
  else => @floatFromInt(p),
165
156
  };
166
157
  r.q *= switch (@typeInfo(@TypeOf(q))) {
167
- Float, ComptimeFloat => q,
158
+ .float, .comptime_float => q,
168
159
  else => @floatFromInt(q),
169
160
  };
170
161
 
@@ -178,7 +169,7 @@ fn update_(comptime T: type, comptime o: comptime_int, r: anytype, p: anytype, q
178
169
 
179
170
  fn add_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !void {
180
171
  switch (@typeInfo(T)) {
181
- Int => {
172
+ .int => {
182
173
  if (r.q == s.q) {
183
174
  r.p = std.math.add(T, r.p, s.p) catch |err| switch (err) {
184
175
  error.Overflow => val: {
@@ -199,7 +190,7 @@ fn add_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !voi
199
190
  };
200
191
  }
201
192
  },
202
- Float => {
193
+ .float => {
203
194
  if (r.q == s.q) {
204
195
  if (r.p > o) r.reduce();
205
196
  if (s.p > o) s.reduce();
@@ -223,7 +214,7 @@ fn add_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !voi
223
214
 
224
215
  fn mul_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !void {
225
216
  switch (@typeInfo(T)) {
226
- Int => {
217
+ .int => {
227
218
  multiplication(T, r, s.p, s.q) catch |err| switch (err) {
228
219
  error.Overflow => {
229
220
  r.reduce();
@@ -233,7 +224,7 @@ fn mul_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !voi
233
224
  else => unreachable,
234
225
  };
235
226
  },
236
- Float => {
227
+ .float => {
237
228
  if (r.q > o or r.p > o) r.reduce();
238
229
  if (s.q > o or s.p > o) s.reduce();
239
230
 
@@ -247,6 +238,41 @@ fn mul_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !voi
247
238
  }
248
239
  }
249
240
 
241
+ fn cmp_(comptime T: type, comptime o: comptime_int, r: anytype, s: anytype) !std.math.Order {
242
+ switch (@typeInfo(T)) {
243
+ .int => {
244
+ const ad = std.math.mul(T, r.p, s.q) catch |err| switch (err) {
245
+ error.Overflow => ad: {
246
+ r.reduce();
247
+ s.reduce();
248
+ break :ad try std.math.mul(T, r.p, s.q);
249
+ },
250
+ else => unreachable,
251
+ };
252
+ const bc = std.math.mul(T, r.q, s.p) catch |err| switch (err) {
253
+ error.Overflow => bc: {
254
+ r.reduce();
255
+ s.reduce();
256
+ break :bc try std.math.mul(T, r.q, s.p);
257
+ },
258
+ else => unreachable,
259
+ };
260
+
261
+ return std.math.order(ad, bc);
262
+ },
263
+ .float => {
264
+ if (r.q > o or r.p > o) r.reduce();
265
+ if (s.q > o or s.p > o) s.reduce();
266
+
267
+ const ad: T = r.p * s.q;
268
+ const bc: T = r.q * s.p;
269
+
270
+ return std.math.order(ad, bc);
271
+ },
272
+ else => unreachable,
273
+ }
274
+ }
275
+
250
276
  fn reduce_(r: anytype) void {
251
277
  const d = gcd(r.p, r.q);
252
278
  if (d == 1) return;
@@ -268,12 +294,12 @@ fn multiplication(comptime T: type, r: anytype, p: anytype, q: anytype) !void {
268
294
 
269
295
  fn addition(comptime T: type, r: anytype, p: anytype, q: anytype) !void {
270
296
  // (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);
297
+ const bd = try std.math.mul(T, r.q, q);
298
+ const ad = try std.math.mul(T, r.p, q);
299
+ const bc = try std.math.mul(T, r.q, p);
274
300
 
275
- r.p = try std.math.add(T, n1, n2);
276
- r.q = d;
301
+ r.p = try std.math.add(T, ad, bc);
302
+ r.q = bd;
277
303
  }
278
304
 
279
305
  fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
@@ -287,7 +313,7 @@ fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
287
313
  };
288
314
 
289
315
  switch (@typeInfo(T)) {
290
- Int => {
316
+ .int => {
291
317
  // std.math.gcd but without some checks because we have a stricter range
292
318
  var x: T = @intCast(p);
293
319
  var y: T = @intCast(q);
@@ -328,8 +354,7 @@ fn gcd(p: anytype, q: anytype) @TypeOf(p, q) {
328
354
  }
329
355
 
330
356
  test gcd {
331
- const seed = if (@hasDecl(std.testing, "random_seed")) std.testing.random_seed else 0x12345678;
332
- var prng = (if (@hasDecl(std, "Random")) std.Random else std.rand).DefaultPrng.init(seed);
357
+ var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
333
358
  var random = prng.random();
334
359
 
335
360
  for (0..1000) |_| {
@@ -356,7 +381,8 @@ fn doTurn(r: anytype) !void {
356
381
 
357
382
  test Rational {
358
383
  inline for (.{ u64, u128, u256, f64 }) |t| {
359
- var r: Rational(t) = .{};
384
+ const R = Rational(t);
385
+ var r: R = .{};
360
386
 
361
387
  var c: t = 128;
362
388
  _ = &c;
@@ -365,7 +391,7 @@ test Rational {
365
391
  try doTurn(&r);
366
392
 
367
393
  r.reduce();
368
- try expectEqual(Rational(t){ .p = 75383, .q = 35550920704 }, r);
394
+ try expectEqual(R{ .p = 75383, .q = 35550920704 }, r);
369
395
 
370
396
  try r.update(1, 4);
371
397
  if (t == u64) {
@@ -373,150 +399,42 @@ test Rational {
373
399
  } else {
374
400
  try doTurn(&r);
375
401
  r.reduce();
376
- try expectEqual(Rational(t){ .p = 5682596689, .q = 2527735925804191711232 }, r);
402
+ try expectEqual(R{ .p = 5682596689, .q = 2527735925804191711232 }, r);
377
403
  }
378
404
 
379
405
  r.reset();
380
406
 
381
- var s = Rational(t){ .p = 10, .q = 13 };
407
+ var s = R{ .p = 10, .q = 13 };
382
408
  try r.mul(&s);
383
- s = Rational(t){ .p = 3, .q = 4 };
409
+ s = R{ .p = 3, .q = 4 };
384
410
  try r.mul(&s);
385
- try expectEqual(Rational(t){ .p = 30, .q = 52 }, r);
411
+ try expectEqual(R{ .p = 30, .q = 52 }, r);
386
412
 
387
- s = Rational(t){ .p = 1, .q = 3 };
413
+ s = R{ .p = 1, .q = 3 };
388
414
  try r.add(&s);
389
415
  r.reduce();
390
- try expectEqual(Rational(t){ .p = 71, .q = 78 }, r);
391
- }
392
- }
416
+ try expectEqual(R{ .p = 71, .q = 78 }, r);
393
417
 
394
- /// Adapter of std.math.big.Rational to the engines Rational interface.
395
- pub const BigRational = struct {
396
- /// The underlying rational number.
397
- val: std.math.big.Rational,
418
+ var u = R{ .p = 3, .q = 5 };
419
+ var v = R{ .p = 2, .q = 5 };
420
+ try expectEqual(u.cmp(&v), .gt);
421
+ v = R{ .p = 4, .q = 5 };
422
+ try expectEqual(u.cmp(&v), .lt);
398
423
 
399
- /// Possible error returned by operations on the rational.
400
- pub const Error = error{OutOfMemory};
424
+ v = R{ .p = 3, .q = 6 };
425
+ try expectEqual(u.cmp(&v), .gt);
426
+ v = R{ .p = 3, .q = 4 };
427
+ try expectEqual(u.cmp(&v), .lt);
401
428
 
402
- /// Create a new BigRational wrapper. A small amount of memory will be allocated on
403
- /// initialization. Not default initialized to 1 - you must explicitly `reset` first.
404
- pub fn init(alloc: std.mem.Allocator) !BigRational {
405
- return .{ .val = try std.math.big.Rational.init(alloc) };
406
- }
429
+ v = R{ .p = 3, .q = 5 };
430
+ try expectEqual(u.cmp(&v), .eq);
407
431
 
408
- /// Frees all memory associated with a rational.
409
- pub fn deinit(r: *BigRational) void {
410
- r.val.deinit();
411
- }
412
-
413
- /// Resets the rational back to 1.
414
- pub fn reset(r: *BigRational) !void {
415
- try r.val.setInt(1);
416
- }
417
-
418
- /// Update the rational by multiplying its numerator by p and its denominator by q.
419
- /// Both p and q must be >= 1.
420
- pub fn update(r: *BigRational, p: anytype, q: anytype) !void {
421
- assert(p >= 1);
422
- assert(q >= 1);
423
-
424
- var s = try std.math.big.Rational.init(r.val.p.allocator);
425
- defer s.deinit();
426
- try s.setRatio(p, q);
427
- try r.val.mul(r.val, s);
428
- }
429
-
430
- /// Adds two rationals.
431
- pub fn add(r: *BigRational, s: anytype) !void {
432
- if (@hasField(@TypeOf(s.*), "val")) {
433
- try r.val.add(r.val, s.val);
432
+ try doTurn(&u);
433
+ v = R{ .p = 4567216874, .q = 89124781931235 };
434
+ if (t == u64) {
435
+ try expectEqual(u.cmp(&v), error.Overflow);
434
436
  } else {
435
- var t = try std.math.big.Rational.init(r.val.p.allocator);
436
- defer t.deinit();
437
- try t.setRatio(s.p, s.q);
438
- try r.val.add(r.val, t);
437
+ try expectEqual(u.cmp(&v), .lt);
439
438
  }
440
439
  }
441
-
442
- /// Multiplies two rationals.
443
- pub fn mul(r: *BigRational, s: *const BigRational) !void {
444
- try r.val.mul(r.val, s.val);
445
- }
446
- };
447
-
448
- test BigRational {
449
- var r = try BigRational.init(std.testing.allocator);
450
- defer r.deinit();
451
- var s = try std.math.big.Rational.init(std.testing.allocator);
452
- defer s.deinit();
453
-
454
- try r.reset();
455
-
456
- var c: u8 = 128;
457
- _ = &c;
458
-
459
- try r.update(c, 256);
460
- try doTurn(&r);
461
-
462
- try s.setRatio(75383, 35550920704);
463
- try expect((try s.order(r.val)) == .eq);
464
-
465
- try r.update(1, 4);
466
- try doTurn(&r);
467
-
468
- try s.setRatio(5682596689, 2527735925804191711232);
469
- try expect((try s.order(r.val)) == .eq);
470
-
471
- try r.reset();
472
-
473
- var t = try BigRational.init(std.testing.allocator);
474
- defer t.deinit();
475
-
476
- try t.val.setRatio(10, 13);
477
- try r.mul(&t);
478
- try t.val.setRatio(3, 4);
479
- try r.mul(&t);
480
-
481
- try s.setRatio(15, 26);
482
- try expect((try s.order(r.val)) == .eq);
483
-
484
- try t.val.setRatio(1, 3);
485
- try r.add(&t);
486
-
487
- try s.setRatio(71, 78);
488
- try expect((try s.order(r.val)) == .eq);
489
- }
490
-
491
- test "minimum" {
492
- // TODO: ziglang/zig#21069
493
- if (builtin.mode == .ReleaseFast or builtin.mode == .ReleaseSmall) {
494
- return error.SkipZigTest;
495
- }
496
- var r = try BigRational.init(std.testing.allocator);
497
- defer r.deinit();
498
- var s = try std.math.big.Rational.init(std.testing.allocator);
499
- defer s.deinit();
500
-
501
- try r.reset();
502
-
503
- try r.update(1, 2); // Speed Tie
504
- for (0..2) |_| {
505
- try r.update(1, 2); // Confusion
506
- try r.update(1, 2); // Infatuation
507
- try r.update(3, 4); // Paralysis
508
- // Population Bomb
509
- for (0..10) |_| {
510
- try r.update(9, 10); // Accuracy
511
- try r.update(1, 15); // Damage roll
512
- try r.update(1, 24); // Critical Hit
513
- try r.update(9, 10); // King's Rock non-proc
514
- }
515
- }
516
-
517
- const ZEROS = std.math.pow(u310, 10, 31);
518
- const n = 36472996377170786403;
519
- const d: u310 = 146267071761884981524886149560653378220687632500762654519853056 * ZEROS;
520
- try s.setRatio(n, d);
521
- try expect((try s.order(r.val)) == .eq);
522
440
  }
@@ -3,11 +3,8 @@ const std = @import("std");
3
3
  const expect = std.testing.expect;
4
4
  const expectEqual = std.testing.expectEqual;
5
5
 
6
- const Pointer = if (@hasField(std.builtin.Type, "pointer")) .pointer else .Pointer;
7
- const One = if (@hasField(std.builtin.Type.Pointer.Size, "one")) .one else .One;
8
-
9
6
  pub fn PointerType(comptime P: type, comptime C: type) type {
10
- return if (@field(@typeInfo(P), @tagName(Pointer)).is_const) *const C else *C;
7
+ return if (@typeInfo(P).pointer.is_const) *const C else *C;
11
8
  }
12
9
 
13
10
  test PointerType {
@@ -18,7 +15,7 @@ test PointerType {
18
15
  pub fn isPointerTo(p: anytype, comptime P: type) bool {
19
16
  const info = @typeInfo(@TypeOf(p));
20
17
  return switch (info) {
21
- Pointer => @field(info, @tagName(Pointer)).child == P,
18
+ .pointer => info.pointer.child == P,
22
19
  else => false,
23
20
  };
24
21
  }
@@ -30,47 +27,18 @@ test isPointerTo {
30
27
  try expect(isPointerTo(&s, S));
31
28
  }
32
29
 
33
- // NOTE: std.mem.bytesAsValue backported from ziglang/zig#18061
34
- pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
35
- return @as(BytesAsValueReturnType(T, @TypeOf(bytes)), @ptrCast(bytes));
36
- }
37
-
38
- fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
39
- return CopyPtrAttrs(B, One, T);
40
- }
30
+ pub fn repeat(comptime xs: anytype, comptime n: usize) [xs.len * n]@TypeOf(xs[0]) {
31
+ comptime {
32
+ const T = @TypeOf(xs[0]);
33
+ var ts: [xs.len * n]T = undefined;
41
34
 
42
- fn CopyPtrAttrs(
43
- comptime source: type,
44
- comptime size: std.builtin.Type.Pointer.Size,
45
- comptime child: type,
46
- ) type {
47
- const info = @field(@typeInfo(source), @tagName(Pointer));
48
- const args: std.builtin.Type.Pointer = if (@hasField(std.builtin.Type.Pointer, "sentinel")) .{
49
- .size = size,
50
- .is_const = info.is_const,
51
- .is_volatile = info.is_volatile,
52
- .is_allowzero = info.is_allowzero,
53
- .alignment = info.alignment,
54
- .address_space = info.address_space,
55
- .child = child,
56
- .sentinel = null,
57
- } else .{
58
- .size = size,
59
- .is_const = info.is_const,
60
- .is_volatile = info.is_volatile,
61
- .is_allowzero = info.is_allowzero,
62
- .alignment = info.alignment,
63
- .address_space = info.address_space,
64
- .child = child,
65
- .sentinel_ptr = null,
66
- };
67
- return @Type(if (@hasField(std.builtin.Type, "pointer"))
68
- .{ .pointer = args }
69
- else
70
- .{ .Pointer = args });
71
- }
35
+ var i: usize = 0;
36
+ while (i < n) : (i += 1) {
37
+ for (xs, 0..) |v, j| {
38
+ ts[i * xs.len + j] = v;
39
+ }
40
+ }
72
41
 
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;
42
+ return ts;
43
+ }
76
44
  }