@pkmn/engine 0.1.0-dev.9f75c7ae → 0.1.0-dev.a0167a9d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +44 -19
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +5 -5
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +5 -4
- package/build/tools/debug.js.map +1 -1
- package/build.zig +82 -90
- package/package.json +31 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +393 -396
- package/src/lib/common/rational.zig +109 -15
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/calc.zig +269 -97
- package/src/lib/gen1/chance.zig +126 -160
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +21 -21
- package/src/lib/gen1/helpers.zig +191 -206
- package/src/lib/gen1/mechanics.zig +308 -285
- package/src/lib/gen2/chance.zig +211 -121
- package/src/lib/gen2/data.zig +13 -11
- package/src/lib/gen2/generated.zig +15 -24
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +855 -413
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +27 -12
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -330
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
2
3
|
|
|
3
4
|
const common = @import("../common/data.zig");
|
|
4
5
|
const optional = @import("../common/optional.zig");
|
|
@@ -34,6 +35,9 @@ const Status = data.Status;
|
|
|
34
35
|
|
|
35
36
|
const Action = chance.Action;
|
|
36
37
|
const Actions = chance.Actions;
|
|
38
|
+
const Duration = chance.Duration;
|
|
39
|
+
|
|
40
|
+
const endian = builtin.cpu.arch.endian();
|
|
37
41
|
|
|
38
42
|
/// Configuration options controlling the domain of valid Pokémon that can be generated by the
|
|
39
43
|
/// random generator helpers.
|
|
@@ -43,6 +47,8 @@ pub const Options = struct {
|
|
|
43
47
|
/// Whether to generate Pokémon with moves that contain bugs on Pokémon Showdown that are
|
|
44
48
|
/// unimplementable in the engine.
|
|
45
49
|
block: bool = showdown,
|
|
50
|
+
// FIXME: remove once durations work with -Dchance
|
|
51
|
+
durations: bool = false,
|
|
46
52
|
};
|
|
47
53
|
|
|
48
54
|
/// Helpers to simplify initialization of a Generation I battle.
|
|
@@ -86,18 +92,38 @@ pub const Battle = struct {
|
|
|
86
92
|
};
|
|
87
93
|
|
|
88
94
|
fn prng(rand: *PSRNG) data.PRNG {
|
|
89
|
-
// GLITCH: initial bytes in seed can only range from 0-252, not 0-255
|
|
90
|
-
const max: u8 = 253;
|
|
91
95
|
return .{
|
|
92
96
|
.src = .{
|
|
93
97
|
.seed = if (showdown)
|
|
94
98
|
rand.newSeed()
|
|
95
|
-
else
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
else seed: {
|
|
100
|
+
// GLITCH: The initial RNG seed in Gen I link battles is heavily constrained
|
|
101
|
+
// https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
|
|
102
|
+
var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
103
|
+
|
|
104
|
+
// Bytes in seed can only range from 0-252, not 0-255
|
|
105
|
+
const max: u8 = 253;
|
|
106
|
+
// Instructions take multiples of 4 cycles to execute
|
|
107
|
+
var timer: u16 = 4 * rand.range(u16, 0, 16384);
|
|
108
|
+
var add: u8 = rand.range(u8, 0, 256);
|
|
109
|
+
var carry: u8 = 0;
|
|
110
|
+
|
|
111
|
+
var i: u8 = 0;
|
|
112
|
+
while (i < seed.len) {
|
|
113
|
+
const div: u8 = @intCast(timer >> 8);
|
|
114
|
+
add +%= div +% carry;
|
|
115
|
+
if (add < max) {
|
|
116
|
+
seed[i] = add;
|
|
117
|
+
i += 1;
|
|
118
|
+
timer +%= 472;
|
|
119
|
+
carry = 1;
|
|
120
|
+
} else {
|
|
121
|
+
timer +%= 452;
|
|
122
|
+
carry = 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
break :seed seed;
|
|
126
|
+
},
|
|
101
127
|
},
|
|
102
128
|
};
|
|
103
129
|
}
|
|
@@ -210,6 +236,7 @@ pub const Pokemon = struct {
|
|
|
210
236
|
var m: Move = .None;
|
|
211
237
|
sample: while (true) {
|
|
212
238
|
m = @enumFromInt(rand.range(u8, 1, Move.size - 1 + 1));
|
|
239
|
+
if (opt.durations and durationsX(m)) continue :sample;
|
|
213
240
|
if (opt.block and blocked(m)) continue :sample;
|
|
214
241
|
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
215
242
|
break;
|
|
@@ -247,6 +274,23 @@ fn blocked(m: Move) bool {
|
|
|
247
274
|
};
|
|
248
275
|
}
|
|
249
276
|
|
|
277
|
+
fn durationsX(m: Move) bool {
|
|
278
|
+
// Any moves that have effects that result in duration tracking with -Dchance
|
|
279
|
+
return switch (Move.get(m).effect) {
|
|
280
|
+
.Confusion,
|
|
281
|
+
.Bide,
|
|
282
|
+
.Sleep,
|
|
283
|
+
.Rage,
|
|
284
|
+
.Binding,
|
|
285
|
+
.Thrashing,
|
|
286
|
+
.ConfusionChance,
|
|
287
|
+
.Disable,
|
|
288
|
+
.Metronome,
|
|
289
|
+
=> true,
|
|
290
|
+
else => false,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
250
294
|
/// Convenience helper to create a move-type choice for the provided move slot.
|
|
251
295
|
pub fn move(slot: u4) Choice {
|
|
252
296
|
return .{ .type = .Move, .data = slot };
|
|
@@ -267,7 +311,7 @@ pub const Rolls = struct {
|
|
|
267
311
|
|
|
268
312
|
/// Returns a slice with the correct range of values for speed ties given the `action` state.
|
|
269
313
|
pub fn speedTie(action: Action) []const Optional(Player) {
|
|
270
|
-
return if (
|
|
314
|
+
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
271
315
|
}
|
|
272
316
|
|
|
273
317
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
@@ -277,25 +321,19 @@ pub const Rolls = struct {
|
|
|
277
321
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
278
322
|
pub fn hit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
279
323
|
if (parent == .true) return &BOOL_NONE;
|
|
280
|
-
return if (
|
|
324
|
+
return if (action.hit == .None) &BOOL_NONE else &BOOLS;
|
|
281
325
|
}
|
|
282
326
|
|
|
283
327
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
284
328
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
285
|
-
pub fn criticalHit(
|
|
286
|
-
action: Action,
|
|
287
|
-
parent: Optional(bool),
|
|
288
|
-
) []const Optional(bool) {
|
|
329
|
+
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
289
330
|
if (parent == .false) return &BOOL_NONE;
|
|
290
|
-
return if (
|
|
331
|
+
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
291
332
|
}
|
|
292
333
|
|
|
293
334
|
/// Returns a slice with the correct range of values for secondary chances hits given the
|
|
294
335
|
/// `action` state and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
295
|
-
pub fn secondaryChance(
|
|
296
|
-
action: Action,
|
|
297
|
-
parent: Optional(bool),
|
|
298
|
-
) []const Optional(bool) {
|
|
336
|
+
pub fn secondaryChance(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
299
337
|
if (parent == .false) return &BOOL_NONE;
|
|
300
338
|
return if (@field(action, "secondary_chance") == .None) &BOOL_NONE else &BOOLS;
|
|
301
339
|
}
|
|
@@ -306,7 +344,7 @@ pub const Rolls = struct {
|
|
|
306
344
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
307
345
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
308
346
|
pub fn damage(action: Action, parent: Optional(bool)) Range {
|
|
309
|
-
return if (parent == .false or
|
|
347
|
+
return if (parent == .false or action.damage == 0)
|
|
310
348
|
.{ .min = 0, .max = 1 }
|
|
311
349
|
else
|
|
312
350
|
.{ .min = 217, .max = 256 };
|
|
@@ -327,14 +365,14 @@ pub const Rolls = struct {
|
|
|
327
365
|
/// Returns a slice with the correct range of values for confused given the `action` state
|
|
328
366
|
/// and the state of the `parent` (the player's Pokémon's remaining confusion turns).
|
|
329
367
|
pub fn confused(action: Action, parent: u4) []const Optional(bool) {
|
|
330
|
-
return if (parent == 0 or
|
|
368
|
+
return if (parent == 0 or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
331
369
|
}
|
|
332
370
|
|
|
333
371
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
334
372
|
/// and the state of the `parent` (whether the player's Pokémon was confused).
|
|
335
373
|
pub fn paralyzed(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
336
374
|
if (parent == .true) return &BOOL_NONE;
|
|
337
|
-
return if (
|
|
375
|
+
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
338
376
|
}
|
|
339
377
|
|
|
340
378
|
const DURATION_NONE = [_]u1{0};
|
|
@@ -344,40 +382,38 @@ pub const Rolls = struct {
|
|
|
344
382
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
345
383
|
pub fn duration(action: Action, parent: Optional(bool)) []const u1 {
|
|
346
384
|
if (parent == .false) return &DURATION_NONE;
|
|
347
|
-
return if (
|
|
385
|
+
return if (action.duration == 0) &DURATION_NONE else &DURATION;
|
|
348
386
|
}
|
|
349
387
|
|
|
350
388
|
const U3_NONE = [_]u3{0};
|
|
351
389
|
const U3_FORCED = [_]u3{1};
|
|
352
390
|
|
|
353
|
-
///
|
|
354
|
-
pub inline fn sleep(
|
|
355
|
-
const
|
|
356
|
-
const o =
|
|
357
|
-
if (o ==
|
|
358
|
-
assert(
|
|
391
|
+
/// TODO
|
|
392
|
+
pub inline fn sleep(dur: Duration, origin: Duration) []const u3 {
|
|
393
|
+
const d = dur.sleep;
|
|
394
|
+
const o = origin.sleep;
|
|
395
|
+
if (o == d) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
396
|
+
assert(d == 0 or d == o + 1);
|
|
359
397
|
return if (o >= 7) &U3_NONE else &[_]u3{ 0, o + 1 };
|
|
360
398
|
}
|
|
361
399
|
|
|
362
400
|
const DISABLE_NONE = [_]u4{0};
|
|
363
401
|
|
|
364
|
-
///
|
|
365
|
-
|
|
366
|
-
pub inline fn disable(action: Action, origin: Action, parent: u4) []const u4 {
|
|
402
|
+
/// TODO
|
|
403
|
+
pub inline fn disable(dur: Duration, origin: Duration, parent: u4) []const u4 {
|
|
367
404
|
if (parent > 0) return &DISABLE_NONE;
|
|
368
|
-
const a =
|
|
369
|
-
const o =
|
|
405
|
+
const a = dur.disable;
|
|
406
|
+
const o = origin.disable;
|
|
370
407
|
if (o == a) return if (o == 0) &DISABLE_NONE else &[_]u4{o};
|
|
371
408
|
assert(a == 0 or a == o + 1);
|
|
372
409
|
return if (o >= 8) &DISABLE_NONE else &[_]u4{ 0, o + 1 };
|
|
373
410
|
}
|
|
374
411
|
|
|
375
|
-
///
|
|
376
|
-
|
|
377
|
-
pub inline fn confusion(action: Action, origin: Action, parent: u4) []const u3 {
|
|
412
|
+
/// TODO
|
|
413
|
+
pub inline fn confusion(dur: Duration, origin: Duration, parent: u4) []const u3 {
|
|
378
414
|
if (parent > 0) return &U3_NONE;
|
|
379
|
-
const a =
|
|
380
|
-
const o =
|
|
415
|
+
const a = dur.confusion;
|
|
416
|
+
const o = origin.confusion;
|
|
381
417
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
382
418
|
assert(a == 0 or a == o + 1);
|
|
383
419
|
return if (o >= 5) &U3_NONE else if (o < 2)
|
|
@@ -386,12 +422,11 @@ pub const Rolls = struct {
|
|
|
386
422
|
&[_]u3{ 0, o + 1 };
|
|
387
423
|
}
|
|
388
424
|
|
|
389
|
-
///
|
|
390
|
-
|
|
391
|
-
pub inline fn attacking(action: Action, origin: Action, parent: Optional(bool)) []const u3 {
|
|
425
|
+
/// TODO
|
|
426
|
+
pub inline fn attacking(dur: Duration, origin: Duration, parent: Optional(bool)) []const u3 {
|
|
392
427
|
if (parent == .true) return &U3_NONE;
|
|
393
|
-
const a =
|
|
394
|
-
const o =
|
|
428
|
+
const a = dur.attacking;
|
|
429
|
+
const o = origin.attacking;
|
|
395
430
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
396
431
|
assert(a == 0 or a == o + 1);
|
|
397
432
|
return if (o >= 3) &U3_NONE else if (o < 2)
|
|
@@ -400,12 +435,11 @@ pub const Rolls = struct {
|
|
|
400
435
|
&[_]u3{ 0, o + 1 };
|
|
401
436
|
}
|
|
402
437
|
|
|
403
|
-
///
|
|
404
|
-
|
|
405
|
-
pub inline fn binding(action: Action, origin: Action, parent: Optional(bool)) []const u3 {
|
|
438
|
+
/// TODO
|
|
439
|
+
pub inline fn binding(dur: Duration, origin: Duration, parent: Optional(bool)) []const u3 {
|
|
406
440
|
if (parent == .true) return &U3_NONE;
|
|
407
|
-
const a =
|
|
408
|
-
const o =
|
|
441
|
+
const a = dur.binding;
|
|
442
|
+
const o = origin.binding;
|
|
409
443
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
410
444
|
assert(a == 0 or a == o + 1);
|
|
411
445
|
return if (o >= 4) &U3_NONE else &[_]u3{ 0, o + 1 };
|
|
@@ -420,7 +454,7 @@ pub const Rolls = struct {
|
|
|
420
454
|
/// These slots may or **may not be valid** as slots may be unset / have 0 PP.
|
|
421
455
|
pub fn moveSlot(action: Action, parent: Optional(bool)) []const u4 {
|
|
422
456
|
if (parent == .false) return &SLOT_NONE;
|
|
423
|
-
return if (
|
|
457
|
+
return if (action.move_slot == 0) &SLOT_NONE else &SLOT;
|
|
424
458
|
}
|
|
425
459
|
|
|
426
460
|
const MULTI_NONE = [_]u4{0};
|
|
@@ -430,7 +464,7 @@ pub const Rolls = struct {
|
|
|
430
464
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
431
465
|
pub fn multiHit(action: Action, parent: Optional(bool)) []const u4 {
|
|
432
466
|
if (parent == .false) return &MULTI_NONE;
|
|
433
|
-
return if (
|
|
467
|
+
return if (action.multi_hit == 0) &MULTI_NONE else &MULTI;
|
|
434
468
|
}
|
|
435
469
|
|
|
436
470
|
const PSYWAVE_NONE = [_]u8{0};
|
|
@@ -448,7 +482,7 @@ pub const Rolls = struct {
|
|
|
448
482
|
parent: Optional(bool),
|
|
449
483
|
) []const u8 {
|
|
450
484
|
if (parent == .false) return &PSYWAVE_NONE;
|
|
451
|
-
return if (
|
|
485
|
+
return if (action.psywave == 0)
|
|
452
486
|
&PSYWAVE_NONE
|
|
453
487
|
else
|
|
454
488
|
PSYWAVE[0 .. @as(u16, side.stored().level) * 3 / 2];
|
|
@@ -458,7 +492,7 @@ pub const Rolls = struct {
|
|
|
458
492
|
|
|
459
493
|
/// Returns a slice with the correct range of values for metronome given the `action` state.
|
|
460
494
|
pub fn metronome(action: Action) []const Move {
|
|
461
|
-
return if (
|
|
495
|
+
return if (action.metronome == .None) &MOVE_NONE else &Move.METRONOME;
|
|
462
496
|
}
|
|
463
497
|
};
|
|
464
498
|
|
|
@@ -478,12 +512,12 @@ test "Rolls.damage" {
|
|
|
478
512
|
test "Rolls.coalesce" {
|
|
479
513
|
var summaries =
|
|
480
514
|
calc.Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
481
|
-
try expectEqual(
|
|
482
|
-
try expectEqual(
|
|
483
|
-
try expectEqual(
|
|
515
|
+
try expectEqual(0, try Rolls.coalesce(.P2, 0, &summaries, false));
|
|
516
|
+
try expectEqual(241, try Rolls.coalesce(.P2, 238, &summaries, false));
|
|
517
|
+
try expectEqual(255, try Rolls.coalesce(.P2, 238, &summaries, true));
|
|
484
518
|
summaries.p1.damage.final = 74;
|
|
485
|
-
try expectEqual(
|
|
486
|
-
try expectEqual(
|
|
519
|
+
try expectEqual(217, try Rolls.coalesce(.P2, 217, &summaries, false));
|
|
520
|
+
try expectEqual(255, try Rolls.coalesce(.P2, 217, &summaries, true));
|
|
487
521
|
}
|
|
488
522
|
|
|
489
523
|
test "Rolls.hit" {
|
|
@@ -539,206 +573,113 @@ test "Rolls.duration" {
|
|
|
539
573
|
}
|
|
540
574
|
|
|
541
575
|
test "Rolls.sleep" {
|
|
542
|
-
try expectEqualSlices(u3, &.{0}, Rolls.sleep(
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
));
|
|
546
|
-
try expectEqualSlices(u3, &.{ 0,
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
));
|
|
550
|
-
try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.sleep(
|
|
551
|
-
.{ .durations = .{ .sleep = 0 } },
|
|
552
|
-
.{ .durations = .{ .sleep = 1 } },
|
|
553
|
-
));
|
|
554
|
-
try expectEqualSlices(u3, &.{1}, Rolls.sleep(
|
|
555
|
-
.{ .durations = .{ .sleep = 1 } },
|
|
556
|
-
.{ .durations = .{ .sleep = 1 } },
|
|
557
|
-
));
|
|
558
|
-
try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.sleep(
|
|
559
|
-
.{ .durations = .{ .sleep = 0 } },
|
|
560
|
-
.{ .durations = .{ .sleep = 2 } },
|
|
561
|
-
));
|
|
562
|
-
try expectEqualSlices(u3, &.{ 0, 7 }, Rolls.sleep(
|
|
563
|
-
.{ .durations = .{ .sleep = 7 } },
|
|
564
|
-
.{ .durations = .{ .sleep = 6 } },
|
|
565
|
-
));
|
|
566
|
-
try expectEqualSlices(u3, &.{0}, Rolls.sleep(
|
|
567
|
-
.{ .durations = .{ .sleep = 0 } },
|
|
568
|
-
.{ .durations = .{ .sleep = 7 } },
|
|
569
|
-
));
|
|
576
|
+
try expectEqualSlices(u3, &.{0}, Rolls.sleep(.{ .sleep = 0 }, .{ .sleep = 0 }));
|
|
577
|
+
try expectEqualSlices(u3, &.{ 0, 1 }, Rolls.sleep(.{ .sleep = 1 }, .{ .sleep = 0 }));
|
|
578
|
+
try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.sleep(.{ .sleep = 0 }, .{ .sleep = 1 }));
|
|
579
|
+
try expectEqualSlices(u3, &.{1}, Rolls.sleep(.{ .sleep = 1 }, .{ .sleep = 1 }));
|
|
580
|
+
try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.sleep(.{ .sleep = 0 }, .{ .sleep = 2 }));
|
|
581
|
+
try expectEqualSlices(u3, &.{ 0, 7 }, Rolls.sleep(.{ .sleep = 7 }, .{ .sleep = 6 }));
|
|
582
|
+
try expectEqualSlices(u3, &.{0}, Rolls.sleep(.{ .sleep = 0 }, .{ .sleep = 7 }));
|
|
570
583
|
}
|
|
571
584
|
|
|
572
585
|
test "Rolls.disable" {
|
|
573
|
-
try expectEqualSlices(u4, &.{0}, Rolls.disable(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
));
|
|
578
|
-
try expectEqualSlices(u4, &.{
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
0,
|
|
582
|
-
));
|
|
583
|
-
try expectEqualSlices(u4, &.{ 0, 2 }, Rolls.disable(
|
|
584
|
-
.{ .durations = .{ .disable = 0 } },
|
|
585
|
-
.{ .durations = .{ .disable = 1 } },
|
|
586
|
-
0,
|
|
587
|
-
));
|
|
588
|
-
try expectEqualSlices(u4, &.{1}, Rolls.disable(
|
|
589
|
-
.{ .durations = .{ .disable = 1 } },
|
|
590
|
-
.{ .durations = .{ .disable = 1 } },
|
|
591
|
-
0,
|
|
592
|
-
));
|
|
593
|
-
try expectEqualSlices(u4, &.{ 0, 3 }, Rolls.disable(
|
|
594
|
-
.{ .durations = .{ .disable = 0 } },
|
|
595
|
-
.{ .durations = .{ .disable = 2 } },
|
|
596
|
-
0,
|
|
597
|
-
));
|
|
598
|
-
try expectEqualSlices(u4, &.{0}, Rolls.disable(
|
|
599
|
-
.{ .durations = .{ .disable = 0 } },
|
|
600
|
-
.{ .durations = .{ .disable = 2 } },
|
|
601
|
-
1,
|
|
602
|
-
));
|
|
603
|
-
try expectEqualSlices(u4, &.{ 0, 8 }, Rolls.disable(
|
|
604
|
-
.{ .durations = .{ .disable = 8 } },
|
|
605
|
-
.{ .durations = .{ .disable = 7 } },
|
|
606
|
-
0,
|
|
607
|
-
));
|
|
608
|
-
try expectEqualSlices(u4, &.{0}, Rolls.disable(
|
|
609
|
-
.{ .durations = .{ .disable = 0 } },
|
|
610
|
-
.{ .durations = .{ .disable = 8 } },
|
|
611
|
-
0,
|
|
612
|
-
));
|
|
586
|
+
try expectEqualSlices(u4, &.{0}, Rolls.disable(.{ .disable = 0 }, .{ .disable = 0 }, 0));
|
|
587
|
+
try expectEqualSlices(u4, &.{ 0, 1 }, Rolls.disable(.{ .disable = 1 }, .{ .disable = 0 }, 0));
|
|
588
|
+
try expectEqualSlices(u4, &.{ 0, 2 }, Rolls.disable(.{ .disable = 0 }, .{ .disable = 1 }, 0));
|
|
589
|
+
try expectEqualSlices(u4, &.{1}, Rolls.disable(.{ .disable = 1 }, .{ .disable = 1 }, 0));
|
|
590
|
+
try expectEqualSlices(u4, &.{ 0, 3 }, Rolls.disable(.{ .disable = 0 }, .{ .disable = 2 }, 0));
|
|
591
|
+
try expectEqualSlices(u4, &.{0}, Rolls.disable(.{ .disable = 0 }, .{ .disable = 2 }, 1));
|
|
592
|
+
try expectEqualSlices(u4, &.{ 0, 8 }, Rolls.disable(.{ .disable = 8 }, .{ .disable = 7 }, 0));
|
|
593
|
+
try expectEqualSlices(u4, &.{0}, Rolls.disable(.{ .disable = 0 }, .{ .disable = 8 }, 0));
|
|
613
594
|
}
|
|
614
595
|
|
|
615
596
|
test "Rolls.confusion" {
|
|
616
|
-
try expectEqualSlices(u3, &.{0}, Rolls.confusion(
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
));
|
|
621
|
-
try expectEqualSlices(u3, &.{1}, Rolls.confusion(
|
|
622
|
-
.{ .durations = .{ .confusion = 1 } },
|
|
623
|
-
.{ .durations = .{ .confusion = 0 } },
|
|
624
|
-
0,
|
|
625
|
-
));
|
|
626
|
-
try expectEqualSlices(u3, &.{0}, Rolls.confusion(
|
|
627
|
-
.{ .durations = .{ .confusion = 0 } },
|
|
628
|
-
.{ .durations = .{ .confusion = 1 } },
|
|
629
|
-
0,
|
|
630
|
-
));
|
|
631
|
-
try expectEqualSlices(u3, &.{1}, Rolls.confusion(
|
|
632
|
-
.{ .durations = .{ .confusion = 1 } },
|
|
633
|
-
.{ .durations = .{ .confusion = 1 } },
|
|
634
|
-
0,
|
|
635
|
-
));
|
|
597
|
+
try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{ .confusion = 0 }, .{ .confusion = 0 }, 0));
|
|
598
|
+
try expectEqualSlices(u3, &.{1}, Rolls.confusion(.{ .confusion = 1 }, .{ .confusion = 0 }, 0));
|
|
599
|
+
try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{ .confusion = 0 }, .{ .confusion = 1 }, 0));
|
|
600
|
+
try expectEqualSlices(u3, &.{1}, Rolls.confusion(.{ .confusion = 1 }, .{ .confusion = 1 }, 0));
|
|
636
601
|
try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.confusion(
|
|
637
|
-
.{ .
|
|
638
|
-
.{ .
|
|
602
|
+
.{ .confusion = 0 },
|
|
603
|
+
.{ .confusion = 2 },
|
|
639
604
|
0,
|
|
640
605
|
));
|
|
641
|
-
try expectEqualSlices(u3, &.{0}, Rolls.confusion(
|
|
642
|
-
.{ .durations = .{ .confusion = 0 } },
|
|
643
|
-
.{ .durations = .{ .confusion = 2 } },
|
|
644
|
-
1,
|
|
645
|
-
));
|
|
606
|
+
try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{ .confusion = 0 }, .{ .confusion = 2 }, 1));
|
|
646
607
|
try expectEqualSlices(u3, &.{ 0, 5 }, Rolls.confusion(
|
|
647
|
-
.{ .
|
|
648
|
-
.{ .
|
|
649
|
-
0,
|
|
650
|
-
));
|
|
651
|
-
try expectEqualSlices(u3, &.{0}, Rolls.confusion(
|
|
652
|
-
.{ .durations = .{ .confusion = 0 } },
|
|
653
|
-
.{ .durations = .{ .confusion = 5 } },
|
|
608
|
+
.{ .confusion = 5 },
|
|
609
|
+
.{ .confusion = 4 },
|
|
654
610
|
0,
|
|
655
611
|
));
|
|
612
|
+
try expectEqualSlices(u3, &.{0}, Rolls.confusion(.{ .confusion = 0 }, .{ .confusion = 5 }, 0));
|
|
656
613
|
}
|
|
657
614
|
|
|
658
615
|
test "Rolls.attacking" {
|
|
659
616
|
try expectEqualSlices(u3, &.{0}, Rolls.attacking(
|
|
660
|
-
.{ .
|
|
661
|
-
.{ .
|
|
617
|
+
.{ .attacking = 0 },
|
|
618
|
+
.{ .attacking = 0 },
|
|
662
619
|
.false,
|
|
663
620
|
));
|
|
664
621
|
try expectEqualSlices(u3, &.{1}, Rolls.attacking(
|
|
665
|
-
.{ .
|
|
666
|
-
.{ .
|
|
622
|
+
.{ .attacking = 1 },
|
|
623
|
+
.{ .attacking = 0 },
|
|
667
624
|
.false,
|
|
668
625
|
));
|
|
669
626
|
try expectEqualSlices(u3, &.{0}, Rolls.attacking(
|
|
670
|
-
.{ .
|
|
671
|
-
.{ .
|
|
627
|
+
.{ .attacking = 0 },
|
|
628
|
+
.{ .attacking = 1 },
|
|
672
629
|
.false,
|
|
673
630
|
));
|
|
674
631
|
try expectEqualSlices(u3, &.{1}, Rolls.attacking(
|
|
675
|
-
.{ .
|
|
676
|
-
.{ .
|
|
632
|
+
.{ .attacking = 1 },
|
|
633
|
+
.{ .attacking = 1 },
|
|
677
634
|
.false,
|
|
678
635
|
));
|
|
679
636
|
try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.attacking(
|
|
680
|
-
.{ .
|
|
681
|
-
.{ .
|
|
637
|
+
.{ .attacking = 0 },
|
|
638
|
+
.{ .attacking = 2 },
|
|
682
639
|
.false,
|
|
683
640
|
));
|
|
684
641
|
try expectEqualSlices(u3, &.{0}, Rolls.attacking(
|
|
685
|
-
.{ .
|
|
686
|
-
.{ .
|
|
642
|
+
.{ .attacking = 0 },
|
|
643
|
+
.{ .attacking = 2 },
|
|
687
644
|
.true,
|
|
688
645
|
));
|
|
689
646
|
try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.attacking(
|
|
690
|
-
.{ .
|
|
691
|
-
.{ .
|
|
647
|
+
.{ .attacking = 3 },
|
|
648
|
+
.{ .attacking = 2 },
|
|
692
649
|
.false,
|
|
693
650
|
));
|
|
694
651
|
try expectEqualSlices(u3, &.{0}, Rolls.attacking(
|
|
695
|
-
.{ .
|
|
696
|
-
.{ .
|
|
652
|
+
.{ .attacking = 0 },
|
|
653
|
+
.{ .attacking = 3 },
|
|
697
654
|
.false,
|
|
698
655
|
));
|
|
699
656
|
}
|
|
700
657
|
|
|
701
658
|
test "Rolls.binding" {
|
|
702
|
-
try expectEqualSlices(u3, &.{0}, Rolls.binding(
|
|
703
|
-
.{ .durations = .{ .binding = 0 } },
|
|
704
|
-
.{ .durations = .{ .binding = 0 } },
|
|
705
|
-
.false,
|
|
706
|
-
));
|
|
659
|
+
try expectEqualSlices(u3, &.{0}, Rolls.binding(.{ .binding = 0 }, .{ .binding = 0 }, .false));
|
|
707
660
|
try expectEqualSlices(u3, &.{ 0, 1 }, Rolls.binding(
|
|
708
|
-
.{ .
|
|
709
|
-
.{ .
|
|
661
|
+
.{ .binding = 1 },
|
|
662
|
+
.{ .binding = 0 },
|
|
710
663
|
.false,
|
|
711
664
|
));
|
|
712
665
|
try expectEqualSlices(u3, &.{ 0, 2 }, Rolls.binding(
|
|
713
|
-
.{ .
|
|
714
|
-
.{ .
|
|
715
|
-
.false,
|
|
716
|
-
));
|
|
717
|
-
try expectEqualSlices(u3, &.{1}, Rolls.binding(
|
|
718
|
-
.{ .durations = .{ .binding = 1 } },
|
|
719
|
-
.{ .durations = .{ .binding = 1 } },
|
|
666
|
+
.{ .binding = 0 },
|
|
667
|
+
.{ .binding = 1 },
|
|
720
668
|
.false,
|
|
721
669
|
));
|
|
670
|
+
try expectEqualSlices(u3, &.{1}, Rolls.binding(.{ .binding = 1 }, .{ .binding = 1 }, .false));
|
|
722
671
|
try expectEqualSlices(u3, &.{ 0, 3 }, Rolls.binding(
|
|
723
|
-
.{ .
|
|
724
|
-
.{ .
|
|
672
|
+
.{ .binding = 0 },
|
|
673
|
+
.{ .binding = 2 },
|
|
725
674
|
.false,
|
|
726
675
|
));
|
|
727
|
-
try expectEqualSlices(u3, &.{0}, Rolls.binding(
|
|
728
|
-
.{ .durations = .{ .binding = 0 } },
|
|
729
|
-
.{ .durations = .{ .binding = 2 } },
|
|
730
|
-
.true,
|
|
731
|
-
));
|
|
676
|
+
try expectEqualSlices(u3, &.{0}, Rolls.binding(.{ .binding = 0 }, .{ .binding = 2 }, .true));
|
|
732
677
|
try expectEqualSlices(u3, &.{ 0, 4 }, Rolls.binding(
|
|
733
|
-
.{ .
|
|
734
|
-
.{ .
|
|
735
|
-
.false,
|
|
736
|
-
));
|
|
737
|
-
try expectEqualSlices(u3, &.{0}, Rolls.binding(
|
|
738
|
-
.{ .durations = .{ .binding = 0 } },
|
|
739
|
-
.{ .durations = .{ .binding = 4 } },
|
|
678
|
+
.{ .binding = 4 },
|
|
679
|
+
.{ .binding = 3 },
|
|
740
680
|
.false,
|
|
741
681
|
));
|
|
682
|
+
try expectEqualSlices(u3, &.{0}, Rolls.binding(.{ .binding = 0 }, .{ .binding = 4 }, .false));
|
|
742
683
|
}
|
|
743
684
|
|
|
744
685
|
test "Rolls.moveSlot" {
|
|
@@ -771,9 +712,53 @@ test "Rolls.psywave" {
|
|
|
771
712
|
|
|
772
713
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
|
|
773
714
|
var rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
774
|
-
try expectEqual(
|
|
715
|
+
try expectEqual(150, rolls[rolls.len - 1]);
|
|
775
716
|
side.stored().level = 81;
|
|
776
717
|
rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
777
|
-
try expectEqual(
|
|
718
|
+
try expectEqual(121, rolls[rolls.len - 1]);
|
|
778
719
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
|
|
779
720
|
}
|
|
721
|
+
|
|
722
|
+
/// Empirically determined maximum number of bytes possibly required to store the diff between two
|
|
723
|
+
/// Generation I battles.
|
|
724
|
+
pub const MAX_DIFFS: usize = 117;
|
|
725
|
+
|
|
726
|
+
/// The optimal size in bytes required to hold the diff between two Generation I battles.
|
|
727
|
+
/// At least as large as MAX_DIFFS.
|
|
728
|
+
pub const DIFFS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
729
|
+
MAX_DIFFS
|
|
730
|
+
else
|
|
731
|
+
std.math.ceilPowerOfTwo(usize, MAX_DIFFS) catch unreachable;
|
|
732
|
+
|
|
733
|
+
/// TODO
|
|
734
|
+
pub fn diff(
|
|
735
|
+
a: *const data.Battle(data.PRNG),
|
|
736
|
+
b: *const data.Battle(data.PRNG),
|
|
737
|
+
w: protocol.ByteStream.Writer,
|
|
738
|
+
) !usize {
|
|
739
|
+
const pos = w.stream.pos;
|
|
740
|
+
const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
|
|
741
|
+
assert(size % 2 == 0);
|
|
742
|
+
assert(size / 2 < 255);
|
|
743
|
+
|
|
744
|
+
var i: usize = 0;
|
|
745
|
+
while (i < size) : (i += 2) {
|
|
746
|
+
const val = std.mem.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
|
|
747
|
+
if (val != std.mem.bytesAsValue(u16, std.mem.asBytes(b)[i .. i + 2]).*) {
|
|
748
|
+
try w.writeByte(@intCast(i / 2));
|
|
749
|
+
try w.writeInt(u16, val, endian);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return w.stream.pos - pos;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/// TODO
|
|
757
|
+
pub fn patch(battle: *data.Battle(data.PRNG), buf: []u8) void {
|
|
758
|
+
var i: usize = 0;
|
|
759
|
+
while (i < buf.len) : (i += 3) {
|
|
760
|
+
const off = @as(u16, @intCast(buf[i])) * 2;
|
|
761
|
+
const val = std.mem.readInt(u16, @ptrCast(buf[i + 1 .. i + 3]), endian);
|
|
762
|
+
std.mem.writeInt(u16, @ptrCast(std.mem.asBytes(battle)[off .. off + 2]), val, endian);
|
|
763
|
+
}
|
|
764
|
+
}
|