@pkmn/engine 0.1.0-dev.dd5b9081 → 0.1.0-dev.deda8593
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/ids.json +12 -12
- package/build/pkg/data/layout.json +2 -2
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +2 -2
- 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 +61 -42
- 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 +102 -104
- package/package.json +43 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +9 -9
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +9 -2
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +739 -318
- package/src/lib/common/rational.zig +12 -9
- package/src/lib/common/rng.zig +25 -16
- package/src/lib/gen1/calc.zig +219 -49
- package/src/lib/gen1/chance.zig +138 -115
- package/src/lib/gen1/data/moves.zig +190 -176
- package/src/lib/gen1/data/species.zig +3 -3
- package/src/lib/gen1/data/types.zig +7 -7
- package/src/lib/gen1/data.zig +44 -44
- package/src/lib/gen1/helpers.zig +148 -82
- package/src/lib/gen1/mechanics.zig +392 -406
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1315 -0
- package/src/lib/gen2/data/items.zig +12 -18
- package/src/lib/gen2/data/moves.zig +377 -344
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +134 -31
- package/src/lib/gen2/data.zig +52 -44
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +3086 -211
- package/src/lib/{binding/node.zig → node.zig} +10 -13
- 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 -383
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");
|
|
@@ -35,6 +36,8 @@ const Status = data.Status;
|
|
|
35
36
|
const Action = chance.Action;
|
|
36
37
|
const Actions = chance.Actions;
|
|
37
38
|
|
|
39
|
+
const endian = builtin.cpu.arch.endian();
|
|
40
|
+
|
|
38
41
|
/// Configuration options controlling the domain of valid Pokémon that can be generated by the
|
|
39
42
|
/// random generator helpers.
|
|
40
43
|
pub const Options = struct {
|
|
@@ -43,6 +46,8 @@ pub const Options = struct {
|
|
|
43
46
|
/// Whether to generate Pokémon with moves that contain bugs on Pokémon Showdown that are
|
|
44
47
|
/// unimplementable in the engine.
|
|
45
48
|
block: bool = showdown,
|
|
49
|
+
// FIXME: remove once durations work with -Dchance
|
|
50
|
+
durations: bool = false,
|
|
46
51
|
};
|
|
47
52
|
|
|
48
53
|
/// Helpers to simplify initialization of a Generation I battle.
|
|
@@ -86,18 +91,38 @@ pub const Battle = struct {
|
|
|
86
91
|
};
|
|
87
92
|
|
|
88
93
|
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
94
|
return .{
|
|
92
95
|
.src = .{
|
|
93
96
|
.seed = if (showdown)
|
|
94
97
|
rand.newSeed()
|
|
95
|
-
else
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
else seed: {
|
|
99
|
+
// GLITCH: The initial RNG seed in Gen I link battles is heavily constrained
|
|
100
|
+
// https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
|
|
101
|
+
var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
102
|
+
|
|
103
|
+
// Bytes in seed can only range from 0-252, not 0-255
|
|
104
|
+
const max: u8 = 253;
|
|
105
|
+
// Instructions take multiples of 4 cycles to execute
|
|
106
|
+
var timer: u16 = 4 * rand.range(u16, 0, 16384);
|
|
107
|
+
var add: u8 = rand.range(u8, 0, 256);
|
|
108
|
+
var carry: u8 = 0;
|
|
109
|
+
|
|
110
|
+
var i: u8 = 0;
|
|
111
|
+
while (i < seed.len) {
|
|
112
|
+
const div: u8 = @intCast(timer >> 8);
|
|
113
|
+
add +%= div +% carry;
|
|
114
|
+
if (add < max) {
|
|
115
|
+
seed[i] = add;
|
|
116
|
+
i += 1;
|
|
117
|
+
timer +%= 472;
|
|
118
|
+
carry = 1;
|
|
119
|
+
} else {
|
|
120
|
+
timer +%= 452;
|
|
121
|
+
carry = 0;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
break :seed seed;
|
|
125
|
+
},
|
|
101
126
|
},
|
|
102
127
|
};
|
|
103
128
|
}
|
|
@@ -210,6 +235,7 @@ pub const Pokemon = struct {
|
|
|
210
235
|
var m: Move = .None;
|
|
211
236
|
sample: while (true) {
|
|
212
237
|
m = @enumFromInt(rand.range(u8, 1, Move.size - 1 + 1));
|
|
238
|
+
if (opt.durations and durations(m)) continue :sample;
|
|
213
239
|
if (opt.block and blocked(m)) continue :sample;
|
|
214
240
|
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
215
241
|
break;
|
|
@@ -247,6 +273,23 @@ fn blocked(m: Move) bool {
|
|
|
247
273
|
};
|
|
248
274
|
}
|
|
249
275
|
|
|
276
|
+
fn durations(m: Move) bool {
|
|
277
|
+
// Any moves that have effects that result in duration tracking with -Dchance
|
|
278
|
+
return switch (Move.get(m).effect) {
|
|
279
|
+
.Confusion,
|
|
280
|
+
.Bide,
|
|
281
|
+
.Sleep,
|
|
282
|
+
.Rage,
|
|
283
|
+
.Binding,
|
|
284
|
+
.Thrashing,
|
|
285
|
+
.ConfusionChance,
|
|
286
|
+
.Disable,
|
|
287
|
+
.Metronome,
|
|
288
|
+
=> true,
|
|
289
|
+
else => false,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
250
293
|
/// Convenience helper to create a move-type choice for the provided move slot.
|
|
251
294
|
pub fn move(slot: u4) Choice {
|
|
252
295
|
return .{ .type = .Move, .data = slot };
|
|
@@ -266,8 +309,8 @@ pub const Rolls = struct {
|
|
|
266
309
|
const PLAYERS = [_]Optional(Player){ .P1, .P2 };
|
|
267
310
|
|
|
268
311
|
/// Returns a slice with the correct range of values for speed ties given the `action` state.
|
|
269
|
-
pub
|
|
270
|
-
return if (
|
|
312
|
+
pub fn speedTie(action: Action) []const Optional(Player) {
|
|
313
|
+
return if (action.speed_tie == .None) &PLAYER_NONE else &PLAYERS;
|
|
271
314
|
}
|
|
272
315
|
|
|
273
316
|
const BOOL_NONE = [_]Optional(bool){.None};
|
|
@@ -275,27 +318,21 @@ pub const Rolls = struct {
|
|
|
275
318
|
|
|
276
319
|
/// Returns a slice with the correct range of values for hits given the `action` state
|
|
277
320
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
278
|
-
pub
|
|
321
|
+
pub fn hit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
279
322
|
if (parent == .true) return &BOOL_NONE;
|
|
280
|
-
return if (
|
|
323
|
+
return if (action.hit == .None) &BOOL_NONE else &BOOLS;
|
|
281
324
|
}
|
|
282
325
|
|
|
283
326
|
/// Returns a slice with the correct range of values for critical hits given the `action` state
|
|
284
327
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
285
|
-
pub
|
|
286
|
-
action: Action,
|
|
287
|
-
parent: Optional(bool),
|
|
288
|
-
) []const Optional(bool) {
|
|
328
|
+
pub fn criticalHit(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
289
329
|
if (parent == .false) return &BOOL_NONE;
|
|
290
|
-
return if (
|
|
330
|
+
return if (action.critical_hit == .None) &BOOL_NONE else &BOOLS;
|
|
291
331
|
}
|
|
292
332
|
|
|
293
333
|
/// Returns a slice with the correct range of values for secondary chances hits given the
|
|
294
334
|
/// `action` state and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
295
|
-
pub
|
|
296
|
-
action: Action,
|
|
297
|
-
parent: Optional(bool),
|
|
298
|
-
) []const Optional(bool) {
|
|
335
|
+
pub fn secondaryChance(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
299
336
|
if (parent == .false) return &BOOL_NONE;
|
|
300
337
|
return if (@field(action, "secondary_chance") == .None) &BOOL_NONE else &BOOLS;
|
|
301
338
|
}
|
|
@@ -305,8 +342,8 @@ pub const Rolls = struct {
|
|
|
305
342
|
|
|
306
343
|
/// Returns the range bounding damage rolls given the `action` state and the state of
|
|
307
344
|
/// the `parent` (whether the player's Pokémon's move hit).
|
|
308
|
-
pub
|
|
309
|
-
return if (parent == .false or
|
|
345
|
+
pub fn damage(action: Action, parent: Optional(bool)) Range {
|
|
346
|
+
return if (parent == .false or action.damage == 0)
|
|
310
347
|
.{ .min = 0, .max = 1 }
|
|
311
348
|
else
|
|
312
349
|
.{ .min = 217, .max = 256 };
|
|
@@ -314,7 +351,7 @@ pub const Rolls = struct {
|
|
|
314
351
|
|
|
315
352
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
316
353
|
/// given the base damage in `summaries`.
|
|
317
|
-
pub
|
|
354
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *calc.Summaries, cap: bool) !u8 {
|
|
318
355
|
if (roll == 0) return roll;
|
|
319
356
|
|
|
320
357
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -326,15 +363,15 @@ pub const Rolls = struct {
|
|
|
326
363
|
|
|
327
364
|
/// Returns a slice with the correct range of values for confused given the `action` state
|
|
328
365
|
/// and the state of the `parent` (the player's Pokémon's remaining confusion turns).
|
|
329
|
-
pub
|
|
330
|
-
return if (parent == 0 or
|
|
366
|
+
pub fn confused(action: Action, parent: u4) []const Optional(bool) {
|
|
367
|
+
return if (parent == 0 or action.confused == .None) &BOOL_NONE else &BOOLS;
|
|
331
368
|
}
|
|
332
369
|
|
|
333
370
|
/// Returns a slice with the correct range of values for paralysis given the `action` state
|
|
334
371
|
/// and the state of the `parent` (whether the player's Pokémon was confused).
|
|
335
|
-
pub
|
|
372
|
+
pub fn paralyzed(action: Action, parent: Optional(bool)) []const Optional(bool) {
|
|
336
373
|
if (parent == .true) return &BOOL_NONE;
|
|
337
|
-
return if (
|
|
374
|
+
return if (action.paralyzed == .None) &BOOL_NONE else &BOOLS;
|
|
338
375
|
}
|
|
339
376
|
|
|
340
377
|
const DURATION_NONE = [_]u1{0};
|
|
@@ -342,9 +379,9 @@ pub const Rolls = struct {
|
|
|
342
379
|
|
|
343
380
|
/// Returns a slice with a range of values for duration given the `action` state
|
|
344
381
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
345
|
-
pub
|
|
382
|
+
pub fn duration(action: Action, parent: Optional(bool)) []const u1 {
|
|
346
383
|
if (parent == .false) return &DURATION_NONE;
|
|
347
|
-
return if (
|
|
384
|
+
return if (action.duration == 0) &DURATION_NONE else &DURATION;
|
|
348
385
|
}
|
|
349
386
|
|
|
350
387
|
const U3_NONE = [_]u3{0};
|
|
@@ -352,8 +389,8 @@ pub const Rolls = struct {
|
|
|
352
389
|
|
|
353
390
|
/// Returns a slice with a range of values for sleep given the `action` and `origin` states.
|
|
354
391
|
pub inline fn sleep(action: Action, origin: Action) []const u3 {
|
|
355
|
-
const a =
|
|
356
|
-
const o =
|
|
392
|
+
const a = action.durations.sleep;
|
|
393
|
+
const o = origin.durations.sleep;
|
|
357
394
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
358
395
|
assert(a == 0 or a == o + 1);
|
|
359
396
|
return if (o >= 7) &U3_NONE else &[_]u3{ 0, o + 1 };
|
|
@@ -365,8 +402,8 @@ pub const Rolls = struct {
|
|
|
365
402
|
/// and the state of the `parent` (the player's Pokémon's remaining sleep turns).
|
|
366
403
|
pub inline fn disable(action: Action, origin: Action, parent: u4) []const u4 {
|
|
367
404
|
if (parent > 0) return &DISABLE_NONE;
|
|
368
|
-
const a =
|
|
369
|
-
const o =
|
|
405
|
+
const a = action.durations.disable;
|
|
406
|
+
const o = origin.durations.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 };
|
|
@@ -376,8 +413,8 @@ pub const Rolls = struct {
|
|
|
376
413
|
/// and the state of the `parent` (the player's remaining sleep turns).
|
|
377
414
|
pub inline fn confusion(action: Action, origin: Action, parent: u4) []const u3 {
|
|
378
415
|
if (parent > 0) return &U3_NONE;
|
|
379
|
-
const a =
|
|
380
|
-
const o =
|
|
416
|
+
const a = action.durations.confusion;
|
|
417
|
+
const o = origin.durations.confusion;
|
|
381
418
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
382
419
|
assert(a == 0 or a == o + 1);
|
|
383
420
|
return if (o >= 5) &U3_NONE else if (o < 2)
|
|
@@ -390,8 +427,8 @@ pub const Rolls = struct {
|
|
|
390
427
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
391
428
|
pub inline fn attacking(action: Action, origin: Action, parent: Optional(bool)) []const u3 {
|
|
392
429
|
if (parent == .true) return &U3_NONE;
|
|
393
|
-
const a =
|
|
394
|
-
const o =
|
|
430
|
+
const a = action.durations.attacking;
|
|
431
|
+
const o = origin.durations.attacking;
|
|
395
432
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
396
433
|
assert(a == 0 or a == o + 1);
|
|
397
434
|
return if (o >= 3) &U3_NONE else if (o < 2)
|
|
@@ -404,8 +441,8 @@ pub const Rolls = struct {
|
|
|
404
441
|
/// and the state of the `parent` (whether the player's Pokémon was fully paralyzed).
|
|
405
442
|
pub inline fn binding(action: Action, origin: Action, parent: Optional(bool)) []const u3 {
|
|
406
443
|
if (parent == .true) return &U3_NONE;
|
|
407
|
-
const a =
|
|
408
|
-
const o =
|
|
444
|
+
const a = action.durations.binding;
|
|
445
|
+
const o = origin.durations.binding;
|
|
409
446
|
if (o == a) return if (o == 0) &U3_NONE else &[_]u3{o};
|
|
410
447
|
assert(a == 0 or a == o + 1);
|
|
411
448
|
return if (o >= 4) &U3_NONE else &[_]u3{ 0, o + 1 };
|
|
@@ -418,9 +455,9 @@ pub const Rolls = struct {
|
|
|
418
455
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
419
456
|
///
|
|
420
457
|
/// These slots may or **may not be valid** as slots may be unset / have 0 PP.
|
|
421
|
-
pub
|
|
458
|
+
pub fn moveSlot(action: Action, parent: Optional(bool)) []const u4 {
|
|
422
459
|
if (parent == .false) return &SLOT_NONE;
|
|
423
|
-
return if (
|
|
460
|
+
return if (action.move_slot == 0) &SLOT_NONE else &SLOT;
|
|
424
461
|
}
|
|
425
462
|
|
|
426
463
|
const MULTI_NONE = [_]u4{0};
|
|
@@ -428,9 +465,9 @@ pub const Rolls = struct {
|
|
|
428
465
|
|
|
429
466
|
/// Returns a slice with the correct range of values for multi hit given the `action` state
|
|
430
467
|
/// and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
431
|
-
pub
|
|
468
|
+
pub fn multiHit(action: Action, parent: Optional(bool)) []const u4 {
|
|
432
469
|
if (parent == .false) return &MULTI_NONE;
|
|
433
|
-
return if (
|
|
470
|
+
return if (action.multi_hit == 0) &MULTI_NONE else &MULTI;
|
|
434
471
|
}
|
|
435
472
|
|
|
436
473
|
const PSYWAVE_NONE = [_]u8{0};
|
|
@@ -442,49 +479,34 @@ pub const Rolls = struct {
|
|
|
442
479
|
|
|
443
480
|
/// Returns a slice with the correct range of values for psywave given the `action` state,
|
|
444
481
|
/// the `side`, and the state of the `parent` (whether the player's Pokémon's move hit).
|
|
445
|
-
pub
|
|
482
|
+
pub fn psywave(
|
|
446
483
|
action: Action,
|
|
447
484
|
side: *data.Side,
|
|
448
485
|
parent: Optional(bool),
|
|
449
486
|
) []const u8 {
|
|
450
487
|
if (parent == .false) return &PSYWAVE_NONE;
|
|
451
|
-
return if (
|
|
488
|
+
return if (action.psywave == 0)
|
|
452
489
|
&PSYWAVE_NONE
|
|
453
490
|
else
|
|
454
491
|
PSYWAVE[0 .. @as(u16, side.stored().level) * 3 / 2];
|
|
455
492
|
}
|
|
456
493
|
|
|
457
494
|
const MOVE_NONE = [_]Move{.None};
|
|
458
|
-
const MOVES = init: {
|
|
459
|
-
var moves: [Move.size - 2]Move = undefined;
|
|
460
|
-
var i: usize = 0;
|
|
461
|
-
for (@typeInfo(Move).Enum.fields) |f| {
|
|
462
|
-
if (!(std.mem.eql(u8, f.name, "None") or
|
|
463
|
-
std.mem.eql(u8, f.name, "Metronome") or
|
|
464
|
-
std.mem.eql(u8, f.name, "Struggle") or
|
|
465
|
-
std.mem.eql(u8, f.name, "SKIP_TURN")))
|
|
466
|
-
{
|
|
467
|
-
moves[i] = @field(Move, f.name);
|
|
468
|
-
i += 1;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
break :init moves;
|
|
472
|
-
};
|
|
473
495
|
|
|
474
496
|
/// Returns a slice with the correct range of values for metronome given the `action` state.
|
|
475
|
-
pub
|
|
476
|
-
return if (
|
|
497
|
+
pub fn metronome(action: Action) []const Move {
|
|
498
|
+
return if (action.metronome == .None) &MOVE_NONE else &Move.METRONOME;
|
|
477
499
|
}
|
|
478
500
|
};
|
|
479
501
|
|
|
480
502
|
test "Rolls.speedTie" {
|
|
481
|
-
const actions =
|
|
503
|
+
const actions: Actions = .{ .p1 = .{ .speed_tie = .P2 } };
|
|
482
504
|
try expectEqualSlices(Optional(Player), &.{ .P1, .P2 }, Rolls.speedTie(actions.p1));
|
|
483
505
|
try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
|
|
484
506
|
}
|
|
485
507
|
|
|
486
508
|
test "Rolls.damage" {
|
|
487
|
-
const actions =
|
|
509
|
+
const actions: Actions = .{ .p2 = .{ .damage = 221 } };
|
|
488
510
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
|
|
489
511
|
try expectEqual(Rolls.Range{ .min = 217, .max = 256 }, Rolls.damage(actions.p2, .None));
|
|
490
512
|
try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p2, .false));
|
|
@@ -493,16 +515,16 @@ test "Rolls.damage" {
|
|
|
493
515
|
test "Rolls.coalesce" {
|
|
494
516
|
var summaries =
|
|
495
517
|
calc.Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
496
|
-
try expectEqual(
|
|
497
|
-
try expectEqual(
|
|
498
|
-
try expectEqual(
|
|
518
|
+
try expectEqual(0, try Rolls.coalesce(.P2, 0, &summaries, false));
|
|
519
|
+
try expectEqual(241, try Rolls.coalesce(.P2, 238, &summaries, false));
|
|
520
|
+
try expectEqual(255, try Rolls.coalesce(.P2, 238, &summaries, true));
|
|
499
521
|
summaries.p1.damage.final = 74;
|
|
500
|
-
try expectEqual(
|
|
501
|
-
try expectEqual(
|
|
522
|
+
try expectEqual(217, try Rolls.coalesce(.P2, 217, &summaries, false));
|
|
523
|
+
try expectEqual(255, try Rolls.coalesce(.P2, 217, &summaries, true));
|
|
502
524
|
}
|
|
503
525
|
|
|
504
526
|
test "Rolls.hit" {
|
|
505
|
-
const actions =
|
|
527
|
+
const actions: Actions = .{ .p2 = .{ .hit = .true } };
|
|
506
528
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p1, .None));
|
|
507
529
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.hit(actions.p2, .None));
|
|
508
530
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p2, .true));
|
|
@@ -510,7 +532,7 @@ test "Rolls.hit" {
|
|
|
510
532
|
}
|
|
511
533
|
|
|
512
534
|
test "Rolls.secondaryChance" {
|
|
513
|
-
const actions =
|
|
535
|
+
const actions: Actions = .{ .p1 = .{ .secondary_chance = .true } };
|
|
514
536
|
try expectEqualSlices(
|
|
515
537
|
Optional(bool),
|
|
516
538
|
&.{ .false, .true },
|
|
@@ -521,7 +543,7 @@ test "Rolls.secondaryChance" {
|
|
|
521
543
|
}
|
|
522
544
|
|
|
523
545
|
test "Rolls.criticalHit" {
|
|
524
|
-
const actions =
|
|
546
|
+
const actions: Actions = .{ .p1 = .{ .critical_hit = .true } };
|
|
525
547
|
try expectEqualSlices(
|
|
526
548
|
Optional(bool),
|
|
527
549
|
&.{ .false, .true },
|
|
@@ -532,14 +554,14 @@ test "Rolls.criticalHit" {
|
|
|
532
554
|
}
|
|
533
555
|
|
|
534
556
|
test "Rolls.confused" {
|
|
535
|
-
const actions =
|
|
557
|
+
const actions: Actions = .{ .p2 = .{ .confused = .true } };
|
|
536
558
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, 1));
|
|
537
559
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.confused(actions.p2, 1));
|
|
538
560
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, 0));
|
|
539
561
|
}
|
|
540
562
|
|
|
541
563
|
test "Rolls.paralyzed" {
|
|
542
|
-
const actions =
|
|
564
|
+
const actions: Actions = .{ .p2 = .{ .paralyzed = .true } };
|
|
543
565
|
try expectEqualSlices(Optional(bool), &.{.None}, Rolls.paralyzed(actions.p1, .None));
|
|
544
566
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .None));
|
|
545
567
|
try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .false));
|
|
@@ -547,7 +569,7 @@ test "Rolls.paralyzed" {
|
|
|
547
569
|
}
|
|
548
570
|
|
|
549
571
|
test "Rolls.duration" {
|
|
550
|
-
const actions =
|
|
572
|
+
const actions: Actions = .{ .p2 = .{ .duration = 3 } };
|
|
551
573
|
try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p1, .None));
|
|
552
574
|
try expectEqualSlices(u1, &.{1}, Rolls.duration(actions.p2, .None));
|
|
553
575
|
try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p2, .false));
|
|
@@ -757,27 +779,27 @@ test "Rolls.binding" {
|
|
|
757
779
|
}
|
|
758
780
|
|
|
759
781
|
test "Rolls.moveSlot" {
|
|
760
|
-
const actions =
|
|
782
|
+
const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
|
|
761
783
|
try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p1, .None));
|
|
762
784
|
try expectEqualSlices(u4, &.{ 1, 2, 3, 4 }, Rolls.moveSlot(actions.p2, .None));
|
|
763
785
|
try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p2, .false));
|
|
764
786
|
}
|
|
765
787
|
|
|
766
788
|
test "Rolls.multiHit" {
|
|
767
|
-
const actions =
|
|
789
|
+
const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
|
|
768
790
|
try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p1, .None));
|
|
769
791
|
try expectEqualSlices(u4, &.{ 2, 3, 4, 5 }, Rolls.multiHit(actions.p2, .None));
|
|
770
792
|
try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p2, .false));
|
|
771
793
|
}
|
|
772
794
|
|
|
773
795
|
test "Rolls.metronome" {
|
|
774
|
-
const actions =
|
|
796
|
+
const actions: Actions = .{ .p2 = .{ .metronome = .Surf } };
|
|
775
797
|
try expectEqualSlices(Move, &.{.None}, Rolls.metronome(actions.p1));
|
|
776
798
|
try expectEqual(@as(Move, @enumFromInt(24)), Rolls.metronome(actions.p2)[23]);
|
|
777
799
|
}
|
|
778
800
|
|
|
779
801
|
test "Rolls.psywave" {
|
|
780
|
-
const actions =
|
|
802
|
+
const actions: Actions = .{ .p2 = .{ .psywave = 79 } };
|
|
781
803
|
var side = Side.init(&[_]Pokemon{.{
|
|
782
804
|
.species = .Bulbasaur,
|
|
783
805
|
.level = 100,
|
|
@@ -786,9 +808,53 @@ test "Rolls.psywave" {
|
|
|
786
808
|
|
|
787
809
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
|
|
788
810
|
var rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
789
|
-
try expectEqual(
|
|
811
|
+
try expectEqual(150, rolls[rolls.len - 1]);
|
|
790
812
|
side.stored().level = 81;
|
|
791
813
|
rolls = Rolls.psywave(actions.p2, &side, .None);
|
|
792
|
-
try expectEqual(
|
|
814
|
+
try expectEqual(121, rolls[rolls.len - 1]);
|
|
793
815
|
try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
|
|
794
816
|
}
|
|
817
|
+
|
|
818
|
+
/// Empirically determined maximum number of bytes possibly required to store the diff between two
|
|
819
|
+
/// Generation I battles.
|
|
820
|
+
pub const MAX_DIFFS: usize = 117;
|
|
821
|
+
|
|
822
|
+
/// The optimal size in bytes required to hold the diff between two Generation I battles.
|
|
823
|
+
/// At least as large as MAX_DIFFS.
|
|
824
|
+
pub const DIFFS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
825
|
+
MAX_DIFFS
|
|
826
|
+
else
|
|
827
|
+
std.math.ceilPowerOfTwo(usize, MAX_DIFFS) catch unreachable;
|
|
828
|
+
|
|
829
|
+
/// TODO
|
|
830
|
+
pub fn diff(
|
|
831
|
+
a: *const data.Battle(data.PRNG),
|
|
832
|
+
b: *const data.Battle(data.PRNG),
|
|
833
|
+
w: protocol.ByteStream.Writer,
|
|
834
|
+
) !usize {
|
|
835
|
+
const pos = w.stream.pos;
|
|
836
|
+
const size = @sizeOf(data.Battle(data.PRNG)) - @sizeOf(data.PRNG);
|
|
837
|
+
assert(size % 2 == 0);
|
|
838
|
+
assert(size / 2 < 255);
|
|
839
|
+
|
|
840
|
+
var i: usize = 0;
|
|
841
|
+
while (i < size) : (i += 2) {
|
|
842
|
+
const val = std.mem.bytesAsValue(u16, std.mem.asBytes(a)[i .. i + 2]).*;
|
|
843
|
+
if (val != std.mem.bytesAsValue(u16, std.mem.asBytes(b)[i .. i + 2]).*) {
|
|
844
|
+
try w.writeByte(@intCast(i / 2));
|
|
845
|
+
try w.writeInt(u16, val, endian);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
return w.stream.pos - pos;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/// TODO
|
|
853
|
+
pub fn patch(battle: *data.Battle(data.PRNG), buf: []u8) void {
|
|
854
|
+
var i: usize = 0;
|
|
855
|
+
while (i < buf.len) : (i += 3) {
|
|
856
|
+
const off = @as(u16, @intCast(buf[i])) * 2;
|
|
857
|
+
const val = std.mem.readInt(u16, @ptrCast(buf[i + 1 .. i + 3]), endian);
|
|
858
|
+
std.mem.writeInt(u16, @ptrCast(std.mem.asBytes(battle)[off .. off + 2]), val, endian);
|
|
859
|
+
}
|
|
860
|
+
}
|