@pkmn/engine 0.1.0-dev.15f64810 → 0.1.0-dev.18478e7d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +30 -17
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +26 -4
- package/build/pkg/addon.js +34 -65
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +40 -90
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +29 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +83 -73
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +66 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +360 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +157 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +346 -128
- package/package.json +59 -138
- package/src/bin/install-pkmn-engine +25 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +45 -29
- package/src/lib/common/js.zig +3 -7
- package/src/lib/common/optional.zig +26 -22
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +196 -160
- package/src/lib/common/rational.zig +338 -254
- package/src/lib/common/rng.zig +5 -7
- package/src/lib/common/util.zig +53 -1
- package/src/lib/gen1/calc.zig +651 -205
- package/src/lib/gen1/chance.zig +443 -257
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +3 -4
- package/src/lib/gen1/data.zig +40 -43
- package/src/lib/gen1/helpers.zig +39 -574
- package/src/lib/gen1/mechanics.zig +328 -229
- package/src/lib/gen2/calc.zig +35 -28
- package/src/lib/gen2/chance.zig +117 -518
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +7 -5
- package/src/lib/gen2/data.zig +33 -35
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +32 -33
- package/src/lib/gen2/mechanics.zig +105 -132
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +4 -121
- package/src/lib/pkmn.zig +103 -22
- package/src/lib/wasm.zig +9 -9
- package/src/tools/display/dom.ts +108 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen2/chance.zig
CHANGED
|
@@ -8,9 +8,9 @@ const expectEqual = std.testing.expectEqual;
|
|
|
8
8
|
|
|
9
9
|
const options = @import("../common/options.zig");
|
|
10
10
|
const rational = @import("../common/rational.zig");
|
|
11
|
-
const rng = @import("../common/rng.zig");
|
|
12
11
|
const util = @import("../common/util.zig");
|
|
13
12
|
|
|
13
|
+
const Array = @import("../common/array.zig").Array;
|
|
14
14
|
const Player = @import("../common/data.zig").Player;
|
|
15
15
|
const Optional = @import("../common/optional.zig").Optional;
|
|
16
16
|
|
|
@@ -19,7 +19,8 @@ const data = @import("data.zig");
|
|
|
19
19
|
const enabled = options.chance;
|
|
20
20
|
const showdown = options.showdown;
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const PointerType = util.PointerType;
|
|
23
|
+
const isPointerTo = util.isPointerTo;
|
|
23
24
|
|
|
24
25
|
const Move = data.Move;
|
|
25
26
|
const Type = data.Type;
|
|
@@ -37,17 +38,11 @@ pub const Actions = extern struct {
|
|
|
37
38
|
p2: Action = .{},
|
|
38
39
|
|
|
39
40
|
/// Returns the `Action` for the given `player`.
|
|
40
|
-
pub fn get(self: anytype, player: Player)
|
|
41
|
-
assert(
|
|
41
|
+
pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Action) {
|
|
42
|
+
assert(isPointerTo(self, Actions));
|
|
42
43
|
return if (player == .P1) &self.p1 else &self.p2;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
/// Perform a reset by clearing fields which should not persist across updates.
|
|
46
|
-
pub fn reset(self: *Actions) void {
|
|
47
|
-
self.p1.reset();
|
|
48
|
-
self.p2.reset();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
46
|
/// Returns true if `a` is equal to `b`.
|
|
52
47
|
pub fn eql(a: Actions, b: Actions) bool {
|
|
53
48
|
return @as(u128, @bitCast(a.p1)) == @as(u128, @bitCast(b.p1)) and
|
|
@@ -58,24 +53,28 @@ pub const Actions = extern struct {
|
|
|
58
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
59
54
|
/// set to the same value).
|
|
60
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
61
|
-
inline for (@typeInfo(Actions)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
57
|
+
@typeInfo(Actions).@"struct".fields
|
|
58
|
+
else
|
|
59
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
60
|
+
{
|
|
61
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
62
|
+
player.name
|
|
63
|
+
else
|
|
64
|
+
player;
|
|
65
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
66
|
+
@typeInfo(Action).@"struct".fields
|
|
67
|
+
else
|
|
68
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
69
|
+
{
|
|
70
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
71
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
72
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
65
73
|
|
|
66
74
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
67
|
-
|
|
68
|
-
const a_val_f = @field(a_val, f.name);
|
|
69
|
-
const b_val_f = @field(b_val, f.name);
|
|
70
|
-
switch (@typeInfo(@TypeOf(a_val_f))) {
|
|
71
|
-
.Int => if ((a_val_f > 0) != (b_val_f > 0)) return false,
|
|
72
|
-
.Bool => if (a_val_f != b_val_f) return false,
|
|
73
|
-
else => unreachable,
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
.Enum => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
75
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
77
76
|
return false,
|
|
78
|
-
.
|
|
77
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
79
78
|
else => unreachable,
|
|
80
79
|
}
|
|
81
80
|
}
|
|
@@ -83,7 +82,7 @@ pub const Actions = extern struct {
|
|
|
83
82
|
return true;
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
pub fn fmt(self: Actions, writer:
|
|
85
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
87
86
|
try writer.writeAll("<P1 = ");
|
|
88
87
|
try self.p1.fmt(writer, shape);
|
|
89
88
|
try writer.writeAll(", P2 = ");
|
|
@@ -91,23 +90,15 @@ pub const Actions = extern struct {
|
|
|
91
90
|
try writer.writeAll(">");
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
pub fn format(a: Actions,
|
|
95
|
-
_ = .{ f, o };
|
|
93
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
96
94
|
try fmt(a, w, false);
|
|
97
95
|
}
|
|
98
96
|
};
|
|
99
97
|
|
|
100
|
-
// TODO: ziglang/zig#19730
|
|
101
98
|
test Actions {
|
|
102
|
-
const a: Actions = .{ .p1 = .{ .hit = .true, .
|
|
103
|
-
const b: Actions = .{ .p1 = .{ .hit = .false, .
|
|
99
|
+
const a: Actions = .{ .p1 = .{ .hit = .true, .confused = .false, .damages = 245 } };
|
|
100
|
+
const b: Actions = .{ .p1 = .{ .hit = .false, .confused = .true, .damages = 246 } };
|
|
104
101
|
const c: Actions = .{ .p1 = .{ .hit = .true } };
|
|
105
|
-
// const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
|
|
106
|
-
// const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
107
|
-
// const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
|
|
108
|
-
// const g: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1 } } };
|
|
109
|
-
// const h: Actions =
|
|
110
|
-
// .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1, .thrash = true } } };
|
|
111
102
|
|
|
112
103
|
try expect(a.eql(a));
|
|
113
104
|
try expect(!a.eql(b));
|
|
@@ -120,17 +111,21 @@ test Actions {
|
|
|
120
111
|
try expect(b.matches(a));
|
|
121
112
|
try expect(!a.matches(c));
|
|
122
113
|
try expect(!c.matches(a));
|
|
123
|
-
// try expect(d.matches(e));
|
|
124
|
-
// try expect(!d.matches(f));
|
|
125
|
-
// try expect(!g.matches(h));
|
|
126
|
-
// try expect(h.matches(h));
|
|
127
114
|
}
|
|
128
115
|
|
|
116
|
+
/// Observation made about a duration - whether the duration has started, been continued, or ended.
|
|
117
|
+
pub const Observation = enum { started, continuing, ended };
|
|
118
|
+
|
|
119
|
+
pub const Damages = Array(5, u8);
|
|
120
|
+
pub const Criticals = Array(5, Optional(bool));
|
|
121
|
+
|
|
129
122
|
/// Information about the RNG that was observed during a Generation II battle `update` for a
|
|
130
123
|
/// single player.
|
|
131
124
|
pub const Action = packed struct(u128) {
|
|
132
|
-
///
|
|
133
|
-
|
|
125
|
+
/// TODO
|
|
126
|
+
damages: Damages.T = 0,
|
|
127
|
+
// TODO
|
|
128
|
+
critical_hits: Criticals.T = 0,
|
|
134
129
|
|
|
135
130
|
/// If not None, the Player to be returned by Rolls.speedTie.
|
|
136
131
|
speed_tie: Optional(Player) = .None,
|
|
@@ -138,50 +133,58 @@ pub const Action = packed struct(u128) {
|
|
|
138
133
|
quick_claw: Optional(bool) = .None,
|
|
139
134
|
/// If not None, the value to return for Rolls.hit.
|
|
140
135
|
hit: Optional(bool) = .None,
|
|
141
|
-
/// If not None, the value to be returned by Rolls.criticalHit.
|
|
142
|
-
critical_hit: Optional(bool) = .None,
|
|
143
|
-
|
|
144
136
|
/// If not None, the value to return for Rolls.confused.
|
|
145
137
|
confused: Optional(bool) = .None,
|
|
138
|
+
|
|
146
139
|
/// If not None, the value to return for Rolls.attract.
|
|
147
140
|
attract: Optional(bool) = .None,
|
|
148
141
|
/// If not None, the value to return for Rolls.paralyzed.
|
|
149
142
|
paralyzed: Optional(bool) = .None,
|
|
150
143
|
/// If not None, the value to return for Rolls.defrost.
|
|
151
144
|
defrost: Optional(bool) = .None,
|
|
152
|
-
|
|
153
145
|
/// If not None, the value to be returned for Rolls.secondaryChance.
|
|
154
146
|
secondary_chance: Optional(bool) = .None,
|
|
147
|
+
|
|
155
148
|
/// If not None, the value to be returned for Rolls.item (Focus Band / King's Rock).
|
|
156
149
|
item: Optional(bool) = .None,
|
|
157
150
|
/// If not None, the value to be returned for Rolls.protect.
|
|
158
151
|
protect: Optional(bool) = .None,
|
|
159
152
|
/// If not None, the value to return for Rolls.triAttack.
|
|
160
153
|
tri_attack: Optional(TriAttack) = .None,
|
|
154
|
+
/// If not 0, the value to return for Rolls.tripleKick.
|
|
155
|
+
triple_kick: u2 = 0,
|
|
161
156
|
|
|
162
157
|
/// If not 0, (present - 1) * 40 should be returned as the base power for Rolls.present.
|
|
163
158
|
present: u3 = 0,
|
|
164
159
|
/// If not 0, magnitude + 3 should be returned as the number for Rolls.magnitude.
|
|
165
160
|
magnitude: u3 = 0,
|
|
166
|
-
/// If not 0, the
|
|
167
|
-
triple_kick: u2 = 0,
|
|
168
|
-
|
|
169
|
-
/// If not 0, the amount of PP to deduct for Rolls.spite.
|
|
161
|
+
/// If not 0, the amount of PP to deduct for Rolls.spite.
|
|
170
162
|
spite: u3 = 0,
|
|
171
|
-
/// If not None, the value to return for Rolls.conversion2.
|
|
172
|
-
conversion_2: Optional(Type) = .None,
|
|
173
|
-
|
|
174
163
|
/// If not 0, the move slot (1-4) to return in Rolls.moveSlot. If present as an override,
|
|
175
164
|
/// invalid values (eg. due to empty move slots or 0 PP) will be ignored.
|
|
176
|
-
move_slot:
|
|
165
|
+
move_slot: u3 = 0,
|
|
177
166
|
/// If not 0, the party slot (1-6) to return in Rolls.forceSwitch. If present as an override,
|
|
178
167
|
/// invalid values (eg. due to empty party slots or fainted members) will be ignored.
|
|
179
|
-
force_switch:
|
|
180
|
-
|
|
168
|
+
force_switch: u3 = 0,
|
|
181
169
|
/// If not 0, the value (2-5) to return for Rolls.distribution for multi hit.
|
|
182
|
-
multi_hit:
|
|
170
|
+
multi_hit: u3 = 0,
|
|
171
|
+
|
|
172
|
+
sleep: Optional(Observation) = .None,
|
|
173
|
+
/// TODO
|
|
174
|
+
confusion: Optional(Observation) = .None,
|
|
175
|
+
/// TODO
|
|
176
|
+
disable: Optional(Observation) = .None,
|
|
177
|
+
/// TODO
|
|
178
|
+
attacking: Optional(Observation) = .None,
|
|
179
|
+
/// TODO
|
|
180
|
+
binding: Optional(Observation) = .None,
|
|
181
|
+
/// TODO
|
|
182
|
+
encore: Optional(Observation) = .None,
|
|
183
|
+
|
|
183
184
|
/// If not 0, the value to by one of the Rolls.*Duration rolls.
|
|
184
|
-
duration:
|
|
185
|
+
duration: u3 = 0,
|
|
186
|
+
/// If not None, the value to return for Rolls.conversion2.
|
|
187
|
+
conversion_2: Optional(Type) = .None,
|
|
185
188
|
|
|
186
189
|
/// If not 0, psywave should be returned as the damage roll for Rolls.psywave.
|
|
187
190
|
psywave: u8 = 0,
|
|
@@ -189,63 +192,43 @@ pub const Action = packed struct(u128) {
|
|
|
189
192
|
/// If not None, the Move to return for Rolls.metronome.
|
|
190
193
|
metronome: Move = .None,
|
|
191
194
|
|
|
192
|
-
_: u16 = 0,
|
|
193
|
-
|
|
194
|
-
/// Observed values of various durations. Does not influence future RNG calls. TODO
|
|
195
|
-
durations: Duration = .{},
|
|
196
|
-
|
|
197
|
-
pub const DURATIONS: u128 = 0xFFFFFFFF00000000_0000000000000000;
|
|
198
|
-
|
|
199
195
|
pub const Field = std.meta.FieldEnum(Action);
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
pub fn reset(self: *Action) void {
|
|
203
|
-
self.* = @bitCast(@as(u128, @bitCast(self.*)) & DURATIONS);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
pub fn format(a: Action, comptime f: []const u8, o: std.fmt.FormatOptions, w: anytype) !void {
|
|
207
|
-
_ = .{ f, o };
|
|
197
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
208
198
|
try fmt(a, w, false);
|
|
209
199
|
}
|
|
210
200
|
|
|
211
|
-
pub fn fmt(self: Action, writer:
|
|
201
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
212
202
|
try writer.writeByte('(');
|
|
213
203
|
var printed = false;
|
|
214
|
-
inline for (@typeInfo(Action)
|
|
215
|
-
|
|
204
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
205
|
+
@typeInfo(Action).@"struct".fields
|
|
206
|
+
else
|
|
207
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
208
|
+
{
|
|
209
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
210
|
+
const val = @field(self, field_name);
|
|
216
211
|
switch (@typeInfo(@TypeOf(val))) {
|
|
217
|
-
|
|
218
|
-
const v = @field(val, f.name);
|
|
219
|
-
if (v != 0) {
|
|
220
|
-
if (printed) try writer.writeAll(", ");
|
|
221
|
-
if (shape) {
|
|
222
|
-
try writer.print("{s}:?", .{f.name});
|
|
223
|
-
} else {
|
|
224
|
-
try writer.print("{s}:{d}", .{ f.name, v });
|
|
225
|
-
}
|
|
226
|
-
printed = true;
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
.Enum => if (val != .None) {
|
|
212
|
+
.@"enum" => if (val != .None) {
|
|
230
213
|
if (printed) try writer.writeAll(", ");
|
|
231
214
|
if (shape) {
|
|
232
|
-
try writer.print("{s}:?", .{
|
|
215
|
+
try writer.print("{s}:?", .{field_name});
|
|
233
216
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
234
217
|
try writer.print("{s}{s}", .{
|
|
235
218
|
if (val == .false) "!" else "",
|
|
236
|
-
|
|
219
|
+
field_name,
|
|
237
220
|
});
|
|
238
221
|
} else {
|
|
239
|
-
try writer.print("{s}:{s}", .{
|
|
222
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
240
223
|
}
|
|
241
224
|
printed = true;
|
|
242
225
|
},
|
|
243
|
-
.
|
|
226
|
+
.int => if (val != 0) {
|
|
244
227
|
if (printed) try writer.writeAll(", ");
|
|
245
228
|
if (shape) {
|
|
246
|
-
try writer.print("{s}:?", .{
|
|
229
|
+
try writer.print("{s}:?", .{field_name});
|
|
247
230
|
} else {
|
|
248
|
-
try writer.print("{s}:{d}", .{
|
|
231
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
249
232
|
}
|
|
250
233
|
printed = true;
|
|
251
234
|
},
|
|
@@ -256,36 +239,6 @@ pub const Action = packed struct(u128) {
|
|
|
256
239
|
}
|
|
257
240
|
};
|
|
258
241
|
|
|
259
|
-
test Action {
|
|
260
|
-
var a: Action = .{ .hit = .true, .durations = .{ .sleep = 3 }, .damage = 225 };
|
|
261
|
-
a.reset();
|
|
262
|
-
|
|
263
|
-
try expectEqual(Action{ .hit = .None, .durations = .{ .sleep = 3 }, .damage = 0 }, a);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/// Observed values for various durations that need to be tracked in order to properly
|
|
267
|
-
/// deduplicate transitions with a primary key. TODO
|
|
268
|
-
pub const Duration = packed struct(u32) {
|
|
269
|
-
/// The number of turns a Pokémon has been observed to be sleeping.
|
|
270
|
-
sleep: u3 = 0,
|
|
271
|
-
/// The number of turns a Pokémon has been observed to be confused.
|
|
272
|
-
confusion: u3 = 0,
|
|
273
|
-
/// TODO
|
|
274
|
-
thrash: bool = false,
|
|
275
|
-
/// The number of turns a Pokémon has been observed to be disabled.
|
|
276
|
-
disable: u4 = 0,
|
|
277
|
-
/// The number of turns a Pokémon has been observed to be attacking.
|
|
278
|
-
attacking: u3 = 0,
|
|
279
|
-
/// The number of turns a Pokémon has been observed to be binding their opponent.
|
|
280
|
-
binding: u3 = 0,
|
|
281
|
-
/// The number of turns a Pokémon has been observed to be encored.
|
|
282
|
-
encore: u3 = 0,
|
|
283
|
-
|
|
284
|
-
_: u12 = 0,
|
|
285
|
-
|
|
286
|
-
pub const Field = std.meta.FieldEnum(Duration);
|
|
287
|
-
};
|
|
288
|
-
|
|
289
242
|
/// Tracks chance actions and their associated probability during a Generation II battle update when
|
|
290
243
|
/// `options.chance` is enabled.
|
|
291
244
|
pub fn Chance(comptime Rational: type) type {
|
|
@@ -298,37 +251,15 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
298
251
|
/// RNG events were observed during a battle `update`.
|
|
299
252
|
actions: Actions = .{},
|
|
300
253
|
|
|
301
|
-
// Tracks the observed sleep durations for the Pokémon in both player's parties. Unlike
|
|
302
|
-
// other durations which are all tied to volatiles, sleep's counter persists through
|
|
303
|
-
// switching and so we must store it here. The indices of these arrays correspond to the
|
|
304
|
-
// `order` field of a Side. This information could be stored in actions but size is a
|
|
305
|
-
// concern so this is tracked separately as actions only purports to track RNG events
|
|
306
|
-
// observed during a single `update` (not across updates).
|
|
307
|
-
sleeps: [2][6]u3 = .{ .{0} ** 6, .{0} ** 6 },
|
|
308
|
-
|
|
309
254
|
/// Possible error returned by operations tracking chance probability.
|
|
310
255
|
pub const Error = Rational.Error;
|
|
311
256
|
|
|
312
257
|
/// Convenience helper to clear fields which typically should be cleared between updates.
|
|
313
258
|
pub fn reset(self: *Self) void {
|
|
314
|
-
self.probability.reset();
|
|
315
|
-
self.actions.reset();
|
|
316
|
-
// TODO: self.pending = .{};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
pub fn switched(self: *Self, player: Player, in: u8, out: u8) void {
|
|
320
259
|
if (!enabled) return;
|
|
321
260
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
var action = self.actions.get(player);
|
|
326
|
-
|
|
327
|
-
const slp = action.durations.sleep;
|
|
328
|
-
action.durations = .{};
|
|
329
|
-
|
|
330
|
-
self.sleeps[@intFromEnum(player)][out - 1] = slp;
|
|
331
|
-
action.durations.sleep = @intCast(self.sleeps[@intFromEnum(player)][in - 1]);
|
|
261
|
+
self.probability.reset();
|
|
262
|
+
self.actions = .{};
|
|
332
263
|
}
|
|
333
264
|
|
|
334
265
|
pub fn speedTie(self: *Self, p1: bool) Error!void {
|
|
@@ -354,19 +285,21 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
354
285
|
self.actions.get(player).hit = if (ok) .true else .false;
|
|
355
286
|
}
|
|
356
287
|
|
|
357
|
-
pub fn criticalHit(self: *Self, player: Player, crit: bool, rate: u8) Error!void {
|
|
288
|
+
pub fn criticalHit(self: *Self, player: Player, i: u3, crit: bool, rate: u8) Error!void {
|
|
358
289
|
if (!enabled) return;
|
|
359
290
|
|
|
360
291
|
const n = if (crit) rate else @as(u8, @intCast(256 - @as(u9, rate)));
|
|
361
292
|
try self.probability.update(n, 256);
|
|
362
|
-
self.actions.get(player)
|
|
293
|
+
var a = self.actions.get(player);
|
|
294
|
+
a.critical_hits = Criticals.set(a.critical_hits, i, if (crit) .true else .false);
|
|
363
295
|
}
|
|
364
296
|
|
|
365
|
-
pub fn damage(self: *Self, player: Player, roll: u8) Error!void {
|
|
297
|
+
pub fn damage(self: *Self, player: Player, i: u3, roll: u8) Error!void {
|
|
366
298
|
if (!enabled) return;
|
|
367
299
|
|
|
368
300
|
try self.probability.update(1, 39);
|
|
369
|
-
self.actions.get(player)
|
|
301
|
+
var a = self.actions.get(player);
|
|
302
|
+
a.damages = Damages.set(a.damages, i, roll);
|
|
370
303
|
}
|
|
371
304
|
|
|
372
305
|
pub fn confused(self: *Self, player: Player, cfz: bool) Error!void {
|
|
@@ -518,122 +451,10 @@ pub fn Chance(comptime Rational: type) type {
|
|
|
518
451
|
self.actions.get(player).multi_hit = n;
|
|
519
452
|
}
|
|
520
453
|
|
|
521
|
-
pub fn duration(
|
|
522
|
-
self: *Self,
|
|
523
|
-
comptime field: Duration.Field,
|
|
524
|
-
player: Player,
|
|
525
|
-
target: Player,
|
|
526
|
-
turns: u4,
|
|
527
|
-
) void {
|
|
528
|
-
if (!enabled) return;
|
|
529
|
-
|
|
530
|
-
var durations = &self.actions.get(target).durations;
|
|
531
|
-
assert(@field(durations, @tagName(field)) == 0 or
|
|
532
|
-
(field == .confusion and player == target));
|
|
533
|
-
@field(durations, @tagName(field)) = 1;
|
|
534
|
-
if (field == .confusion) durations.thrash = player == target;
|
|
535
|
-
self.actions.get(player).duration = if (options.key) 1 else turns;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
pub fn sleep(self: *Self, player: Player, turns: u4) Error!void {
|
|
454
|
+
pub fn duration(self: *Self, player: Player, turns: u3) void {
|
|
539
455
|
if (!enabled) return;
|
|
540
456
|
|
|
541
|
-
|
|
542
|
-
const n = durations.sleep;
|
|
543
|
-
if (turns == 0) {
|
|
544
|
-
assert(n >= 1 and n <= 6);
|
|
545
|
-
if (n != 6) try self.probability.update(1, 7 - @as(u4, n));
|
|
546
|
-
durations.sleep = 0;
|
|
547
|
-
} else {
|
|
548
|
-
assert(n >= 1 and n < 6);
|
|
549
|
-
try self.probability.update(7 - @as(u4, n) - 1, 7 - @as(u4, n));
|
|
550
|
-
durations.sleep += 1;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
pub fn confusion(self: *Self, player: Player, turns: u4) Error!void {
|
|
555
|
-
if (!enabled) return;
|
|
556
|
-
|
|
557
|
-
var durations = &self.actions.get(player).durations;
|
|
558
|
-
const n = durations.confusion;
|
|
559
|
-
const hi: u8 = if (durations.thrash) 3 else 5;
|
|
560
|
-
const p = hi + 1;
|
|
561
|
-
if (turns == 0) {
|
|
562
|
-
assert(n >= 2 and n <= hi);
|
|
563
|
-
if (n != hi) try self.probability.update(1, p - @as(u4, n));
|
|
564
|
-
durations.confusion = 0;
|
|
565
|
-
} else {
|
|
566
|
-
assert(n >= 1 and n < hi);
|
|
567
|
-
if (n > 1) try self.probability.update(p - @as(u4, n) - 1, p - @as(u4, n));
|
|
568
|
-
durations.confusion += 1;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
pub fn disable(self: *Self, player: Player, turns: u4) Error!void {
|
|
573
|
-
if (!enabled) return;
|
|
574
|
-
|
|
575
|
-
var durations = &self.actions.get(player).durations;
|
|
576
|
-
const n = durations.disable;
|
|
577
|
-
const hi = if (showdown) 5 else 8;
|
|
578
|
-
const p = hi + 1;
|
|
579
|
-
if (turns == 0) {
|
|
580
|
-
assert(n >= 2 and n <= hi);
|
|
581
|
-
if (n != hi) try self.probability.update(1, p - n);
|
|
582
|
-
durations.disable = 0;
|
|
583
|
-
} else {
|
|
584
|
-
assert(n >= 1 and n < hi);
|
|
585
|
-
if (n > 1) try self.probability.update(p - n - 1, p - n);
|
|
586
|
-
durations.disable += 1;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
pub fn attacking(self: *Self, player: Player, turns: u4) Error!void {
|
|
591
|
-
if (!enabled) return;
|
|
592
|
-
|
|
593
|
-
var durations = &self.actions.get(player).durations;
|
|
594
|
-
const n = durations.attacking;
|
|
595
|
-
if (turns == 0) {
|
|
596
|
-
assert(n >= 2 and n <= 3);
|
|
597
|
-
if (n != 3) try self.probability.update(1, 4 - @as(u4, n));
|
|
598
|
-
durations.attacking = 0;
|
|
599
|
-
} else {
|
|
600
|
-
assert(n >= 1 and n < 3);
|
|
601
|
-
if (n > 1) try self.probability.update(4 - @as(u4, n) - 1, 4 - @as(u4, n));
|
|
602
|
-
durations.attacking += 1;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
pub fn binding(self: *Self, player: Player, turns: u4) Error!void {
|
|
607
|
-
if (!enabled) return;
|
|
608
|
-
|
|
609
|
-
var durations = &self.actions.get(player).durations;
|
|
610
|
-
const n = durations.binding;
|
|
611
|
-
if (turns == 0) {
|
|
612
|
-
assert(n >= 2 and n <= 5);
|
|
613
|
-
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
614
|
-
durations.binding = 0;
|
|
615
|
-
} else {
|
|
616
|
-
assert(n >= 1 and n < 5);
|
|
617
|
-
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
618
|
-
durations.binding += 1;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// TODO: consider sharing implementation with binding
|
|
623
|
-
pub fn encore(self: *Self, player: Player, turns: u4) Error!void {
|
|
624
|
-
if (!enabled) return;
|
|
625
|
-
|
|
626
|
-
var durations = &self.actions.get(player).durations;
|
|
627
|
-
const n = durations.encore;
|
|
628
|
-
if (turns == 0) {
|
|
629
|
-
assert(n >= 2 and n <= 5);
|
|
630
|
-
if (n != 5) try self.probability.update(1, 6 - @as(u4, n));
|
|
631
|
-
durations.encore = 0;
|
|
632
|
-
} else {
|
|
633
|
-
assert(n >= 1 and n < 5);
|
|
634
|
-
if (n > 1) try self.probability.update(6 - @as(u4, n) - 1, 6 - @as(u4, n));
|
|
635
|
-
durations.encore += 1;
|
|
636
|
-
}
|
|
457
|
+
self.actions.get(player).duration = if (options.key) 2 else turns;
|
|
637
458
|
}
|
|
638
459
|
};
|
|
639
460
|
}
|
|
@@ -685,22 +506,22 @@ test "Chance.hit" {
|
|
|
685
506
|
test "Chance.criticalHit" {
|
|
686
507
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
687
508
|
|
|
688
|
-
try chance.criticalHit(.P1, true, 17);
|
|
689
|
-
try expectValue(Optional(bool).true, chance.actions.p1.
|
|
509
|
+
try chance.criticalHit(.P1, 0, true, 17);
|
|
510
|
+
try expectValue(Optional(bool).true, Criticals.get(chance.actions.p1.critical_hits, 0));
|
|
690
511
|
try expectProbability(&chance.probability, 17, 256);
|
|
691
512
|
|
|
692
513
|
chance.reset();
|
|
693
514
|
|
|
694
|
-
try chance.criticalHit(.P2, false, 5);
|
|
515
|
+
try chance.criticalHit(.P2, 1, false, 5);
|
|
695
516
|
try expectProbability(&chance.probability, 251, 256);
|
|
696
|
-
try expectValue(Optional(bool).false, chance.actions.p2.
|
|
517
|
+
try expectValue(Optional(bool).false, Criticals.get(chance.actions.p2.critical_hits, 1));
|
|
697
518
|
}
|
|
698
519
|
|
|
699
520
|
test "Chance.damage" {
|
|
700
521
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
701
522
|
|
|
702
|
-
try chance.damage(.P1,
|
|
703
|
-
try expectValue(
|
|
523
|
+
try chance.damage(.P1, 0, 219);
|
|
524
|
+
try expectValue(@as(u8, 219), Damages.get(chance.actions.p1.damages, 0));
|
|
704
525
|
try expectProbability(&chance.probability, 1, 39);
|
|
705
526
|
}
|
|
706
527
|
|
|
@@ -825,7 +646,7 @@ test "Chance.present" {
|
|
|
825
646
|
} else {
|
|
826
647
|
try expectProbability(&chance.probability, 25, 256);
|
|
827
648
|
}
|
|
828
|
-
try expectValue(4, chance.actions.p2.present);
|
|
649
|
+
try expectValue(@as(u3, 4), chance.actions.p2.present);
|
|
829
650
|
|
|
830
651
|
chance.reset();
|
|
831
652
|
|
|
@@ -835,7 +656,7 @@ test "Chance.present" {
|
|
|
835
656
|
} else {
|
|
836
657
|
try expectProbability(&chance.probability, 51, 256);
|
|
837
658
|
}
|
|
838
|
-
try expectValue(1, chance.actions.p1.present);
|
|
659
|
+
try expectValue(@as(u3, 1), chance.actions.p1.present);
|
|
839
660
|
}
|
|
840
661
|
|
|
841
662
|
test "Chance.magnitude" {
|
|
@@ -847,7 +668,7 @@ test "Chance.magnitude" {
|
|
|
847
668
|
} else {
|
|
848
669
|
try expectProbability(&chance.probability, 13, 256);
|
|
849
670
|
}
|
|
850
|
-
try expectValue(7, chance.actions.p2.magnitude);
|
|
671
|
+
try expectValue(@as(u3, 7), chance.actions.p2.magnitude);
|
|
851
672
|
|
|
852
673
|
chance.reset();
|
|
853
674
|
|
|
@@ -857,7 +678,7 @@ test "Chance.magnitude" {
|
|
|
857
678
|
} else {
|
|
858
679
|
try expectProbability(&chance.probability, 51, 256);
|
|
859
680
|
}
|
|
860
|
-
try expectValue(5, chance.actions.p1.magnitude);
|
|
681
|
+
try expectValue(@as(u3, 5), chance.actions.p1.magnitude);
|
|
861
682
|
}
|
|
862
683
|
|
|
863
684
|
test "Chance.tripleKick" {
|
|
@@ -865,7 +686,7 @@ test "Chance.tripleKick" {
|
|
|
865
686
|
|
|
866
687
|
try chance.tripleKick(.P1, 2);
|
|
867
688
|
try expectProbability(&chance.probability, 1, 3);
|
|
868
|
-
try expectValue(2, chance.actions.p1.triple_kick);
|
|
689
|
+
try expectValue(@as(u2, 2), chance.actions.p1.triple_kick);
|
|
869
690
|
}
|
|
870
691
|
|
|
871
692
|
test "Chance.spite" {
|
|
@@ -873,7 +694,7 @@ test "Chance.spite" {
|
|
|
873
694
|
|
|
874
695
|
try chance.spite(.P1, 3);
|
|
875
696
|
try expectProbability(&chance.probability, 1, 4);
|
|
876
|
-
try expectValue(3, chance.actions.p1.spite);
|
|
697
|
+
try expectValue(@as(u3, 3), chance.actions.p1.spite);
|
|
877
698
|
}
|
|
878
699
|
|
|
879
700
|
test "Chance.conversion2" {
|
|
@@ -900,7 +721,7 @@ test "Chance.psywave" {
|
|
|
900
721
|
|
|
901
722
|
try chance.psywave(.P2, 100, 150);
|
|
902
723
|
try expectProbability(&chance.probability, 1, 150);
|
|
903
|
-
try expectValue(100, chance.actions.p2.psywave);
|
|
724
|
+
try expectValue(@as(u8, 100), chance.actions.p2.psywave);
|
|
904
725
|
}
|
|
905
726
|
|
|
906
727
|
test "Chance.metronome" {
|
|
@@ -916,19 +737,19 @@ test "Chance.moveSlot" {
|
|
|
916
737
|
|
|
917
738
|
try chance.moveSlot(.P2, 2, 2);
|
|
918
739
|
try expectProbability(&chance.probability, 1, 2);
|
|
919
|
-
try expectValue(2, chance.actions.p2.move_slot);
|
|
740
|
+
try expectValue(@as(u3, 2), chance.actions.p2.move_slot);
|
|
920
741
|
|
|
921
742
|
chance.reset();
|
|
922
743
|
|
|
923
744
|
try chance.moveSlot(.P1, 1, 3);
|
|
924
745
|
try expectProbability(&chance.probability, 1, 3);
|
|
925
|
-
try expectValue(1, chance.actions.p1.move_slot);
|
|
746
|
+
try expectValue(@as(u3, 1), chance.actions.p1.move_slot);
|
|
926
747
|
|
|
927
748
|
chance.reset();
|
|
928
749
|
|
|
929
750
|
try chance.moveSlot(.P1, 4, 1);
|
|
930
751
|
try expectProbability(&chance.probability, 1, 1);
|
|
931
|
-
try expectValue(4, chance.actions.p1.move_slot);
|
|
752
|
+
try expectValue(@as(u3, 4), chance.actions.p1.move_slot);
|
|
932
753
|
}
|
|
933
754
|
|
|
934
755
|
test "Chance.forceSwitch" {
|
|
@@ -936,19 +757,19 @@ test "Chance.forceSwitch" {
|
|
|
936
757
|
|
|
937
758
|
try chance.forceSwitch(.P2, 2, 2);
|
|
938
759
|
try expectProbability(&chance.probability, 1, 2);
|
|
939
|
-
try expectValue(2, chance.actions.p2.force_switch);
|
|
760
|
+
try expectValue(@as(u3, 2), chance.actions.p2.force_switch);
|
|
940
761
|
|
|
941
762
|
chance.reset();
|
|
942
763
|
|
|
943
764
|
try chance.forceSwitch(.P1, 1, 3);
|
|
944
765
|
try expectProbability(&chance.probability, 1, 3);
|
|
945
|
-
try expectValue(1, chance.actions.p1.force_switch);
|
|
766
|
+
try expectValue(@as(u3, 1), chance.actions.p1.force_switch);
|
|
946
767
|
|
|
947
768
|
chance.reset();
|
|
948
769
|
|
|
949
770
|
try chance.forceSwitch(.P1, 4, 1);
|
|
950
771
|
try expectProbability(&chance.probability, 1, 1);
|
|
951
|
-
try expectValue(4, chance.actions.p1.force_switch);
|
|
772
|
+
try expectValue(@as(u3, 4), chance.actions.p1.force_switch);
|
|
952
773
|
}
|
|
953
774
|
|
|
954
775
|
test "Chance.multiHit" {
|
|
@@ -956,212 +777,20 @@ test "Chance.multiHit" {
|
|
|
956
777
|
|
|
957
778
|
try chance.multiHit(.P1, 3);
|
|
958
779
|
try expectProbability(&chance.probability, 3, 8);
|
|
959
|
-
try expectValue(3, chance.actions.p1.multi_hit);
|
|
780
|
+
try expectValue(@as(u3, 3), chance.actions.p1.multi_hit);
|
|
960
781
|
|
|
961
782
|
chance.reset();
|
|
962
783
|
|
|
963
784
|
try chance.multiHit(.P2, 5);
|
|
964
785
|
try expectProbability(&chance.probability, 1, 8);
|
|
965
|
-
try expectValue(5, chance.actions.p2.multi_hit);
|
|
786
|
+
try expectValue(@as(u3, 5), chance.actions.p2.multi_hit);
|
|
966
787
|
}
|
|
967
788
|
|
|
968
789
|
test "Chance.duration" {
|
|
969
790
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
970
791
|
|
|
971
|
-
chance.duration(.
|
|
972
|
-
try expectValue(2, chance.actions.p1.duration);
|
|
973
|
-
try expectValue(1, chance.actions.p2.durations.sleep);
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
test "Chance.sleep" {
|
|
977
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
978
|
-
|
|
979
|
-
for ([_]u8{ 6, 5, 4, 3, 2, 1 }, 1..7) |d, i| {
|
|
980
|
-
chance.actions.p1.durations.sleep = @intCast(i);
|
|
981
|
-
try chance.sleep(.P1, 0);
|
|
982
|
-
try expectProbability(&chance.probability, 1, d);
|
|
983
|
-
try expectValue(0, chance.actions.p1.durations.sleep);
|
|
984
|
-
|
|
985
|
-
chance.reset();
|
|
986
|
-
|
|
987
|
-
if (i < 6) {
|
|
988
|
-
chance.actions.p1.durations.sleep = @intCast(i);
|
|
989
|
-
try chance.sleep(.P1, 1);
|
|
990
|
-
try expectProbability(&chance.probability, d - 1, d);
|
|
991
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p1.durations.sleep);
|
|
992
|
-
|
|
993
|
-
chance.reset();
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
test "Chance.confusion" {
|
|
999
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1000
|
-
|
|
1001
|
-
chance.actions.p2.durations.thrash = false;
|
|
1002
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1003
|
-
if (i > 1) {
|
|
1004
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1005
|
-
try chance.confusion(.P2, 0);
|
|
1006
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1007
|
-
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
1008
|
-
try expectValue(false, chance.actions.p2.durations.thrash);
|
|
1009
|
-
|
|
1010
|
-
chance.reset();
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
if (i < 5) {
|
|
1014
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1015
|
-
try chance.confusion(.P2, 1);
|
|
1016
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1017
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
|
|
1018
|
-
try expectValue(false, chance.actions.p2.durations.thrash);
|
|
1019
|
-
|
|
1020
|
-
chance.reset();
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
chance.reset();
|
|
1025
|
-
|
|
1026
|
-
chance.actions.p2.durations.thrash = true;
|
|
1027
|
-
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
1028
|
-
if (i > 1) {
|
|
1029
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1030
|
-
try chance.confusion(.P2, 0);
|
|
1031
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1032
|
-
try expectValue(0, chance.actions.p2.durations.confusion);
|
|
1033
|
-
try expectValue(true, chance.actions.p2.durations.thrash);
|
|
1034
|
-
|
|
1035
|
-
chance.reset();
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
if (i < 3) {
|
|
1039
|
-
chance.actions.p2.durations.confusion = @intCast(i);
|
|
1040
|
-
try chance.confusion(.P2, 1);
|
|
1041
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1042
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.confusion);
|
|
1043
|
-
try expectValue(true, chance.actions.p2.durations.thrash);
|
|
1044
|
-
|
|
1045
|
-
chance.reset();
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
test "Chance.disable" {
|
|
1051
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1052
|
-
if (showdown) {
|
|
1053
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1054
|
-
if (i > 1) {
|
|
1055
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1056
|
-
try chance.disable(.P2, 0);
|
|
1057
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1058
|
-
try expectValue(0, chance.actions.p2.durations.disable);
|
|
1059
|
-
|
|
1060
|
-
chance.reset();
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
if (i < 5) {
|
|
1064
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1065
|
-
try chance.disable(.P2, 1);
|
|
1066
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1067
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations.disable);
|
|
1068
|
-
|
|
1069
|
-
chance.reset();
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
} else {
|
|
1073
|
-
for ([_]u8{ 1, 7, 6, 5, 4, 3, 2, 1 }, 1..9) |d, i| {
|
|
1074
|
-
if (i > 1) {
|
|
1075
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1076
|
-
try chance.disable(.P2, 0);
|
|
1077
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1078
|
-
try expectValue(0, chance.actions.p2.durations.disable);
|
|
1079
|
-
|
|
1080
|
-
chance.reset();
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
if (i < 8) {
|
|
1084
|
-
chance.actions.p2.durations.disable = @intCast(i);
|
|
1085
|
-
try chance.disable(.P2, 1);
|
|
1086
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1087
|
-
try expectValue(@as(u4, @intCast(i)) + 1, chance.actions.p2.durations.disable);
|
|
1088
|
-
|
|
1089
|
-
chance.reset();
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
test "Chance.attacking" {
|
|
1096
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1097
|
-
|
|
1098
|
-
for ([_]u8{ 1, 2, 1 }, 1..4) |d, i| {
|
|
1099
|
-
if (i > 1) {
|
|
1100
|
-
chance.actions.p2.durations.attacking = @intCast(i);
|
|
1101
|
-
try chance.attacking(.P2, 0);
|
|
1102
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1103
|
-
try expectValue(0, chance.actions.p2.durations.attacking);
|
|
1104
|
-
|
|
1105
|
-
chance.reset();
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
if (i < 3) {
|
|
1109
|
-
chance.actions.p2.durations.attacking = @intCast(i);
|
|
1110
|
-
try chance.attacking(.P2, 1);
|
|
1111
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1112
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.attacking);
|
|
1113
|
-
|
|
1114
|
-
chance.reset();
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
test "Chance.binding" {
|
|
1120
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1121
|
-
|
|
1122
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1123
|
-
if (i > 1) {
|
|
1124
|
-
chance.actions.p2.durations.binding = @intCast(i);
|
|
1125
|
-
try chance.binding(.P2, 0);
|
|
1126
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1127
|
-
try expectValue(0, chance.actions.p2.durations.binding);
|
|
1128
|
-
|
|
1129
|
-
chance.reset();
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
if (i < 5) {
|
|
1133
|
-
chance.actions.p2.durations.binding = @intCast(i);
|
|
1134
|
-
try chance.binding(.P2, 1);
|
|
1135
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1136
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.binding);
|
|
1137
|
-
|
|
1138
|
-
chance.reset();
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
test "Chance.encore" {
|
|
1144
|
-
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
1145
|
-
|
|
1146
|
-
for ([_]u8{ 1, 4, 3, 2, 1 }, 1..6) |d, i| {
|
|
1147
|
-
if (i > 1) {
|
|
1148
|
-
chance.actions.p2.durations.encore = @intCast(i);
|
|
1149
|
-
try chance.encore(.P2, 0);
|
|
1150
|
-
try expectProbability(&chance.probability, 1, d);
|
|
1151
|
-
try expectValue(0, chance.actions.p2.durations.encore);
|
|
1152
|
-
|
|
1153
|
-
chance.reset();
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
if (i < 5) {
|
|
1157
|
-
chance.actions.p2.durations.encore = @intCast(i);
|
|
1158
|
-
try chance.encore(.P2, 1);
|
|
1159
|
-
try expectProbability(&chance.probability, if (d > 1) d - 1 else d, d);
|
|
1160
|
-
try expectValue(@as(u3, @intCast(i)) + 1, chance.actions.p2.durations.encore);
|
|
1161
|
-
|
|
1162
|
-
chance.reset();
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
792
|
+
chance.duration(.P1, 2);
|
|
793
|
+
try expectValue(@as(u3, 2), chance.actions.p1.duration);
|
|
1165
794
|
}
|
|
1166
795
|
|
|
1167
796
|
pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
@@ -1169,7 +798,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
1169
798
|
|
|
1170
799
|
r.reduce();
|
|
1171
800
|
if (r.p != p or r.q != q) {
|
|
1172
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
801
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
1173
802
|
return error.TestExpectedEqual;
|
|
1174
803
|
}
|
|
1175
804
|
}
|
|
@@ -1203,12 +832,12 @@ const Null = struct {
|
|
|
1203
832
|
_ = .{ self, player, ok, accuracy };
|
|
1204
833
|
}
|
|
1205
834
|
|
|
1206
|
-
pub fn criticalHit(self: Null, player: Player, crit: bool, rate: u8) Error!void {
|
|
1207
|
-
_ = .{ self, player, crit, rate };
|
|
835
|
+
pub fn criticalHit(self: Null, player: Player, i: u3, crit: bool, rate: u8) Error!void {
|
|
836
|
+
_ = .{ self, player, i, crit, rate };
|
|
1208
837
|
}
|
|
1209
838
|
|
|
1210
|
-
pub fn damage(self: Null, player: Player, roll: u8) Error!void {
|
|
1211
|
-
_ = .{ self, player, roll };
|
|
839
|
+
pub fn damage(self: Null, player: Player, i: u3, roll: u8) Error!void {
|
|
840
|
+
_ = .{ self, player, i, roll };
|
|
1212
841
|
}
|
|
1213
842
|
|
|
1214
843
|
pub fn confused(self: Null, player: Player, ok: bool) Error!void {
|
|
@@ -1279,37 +908,7 @@ const Null = struct {
|
|
|
1279
908
|
_ = .{ self, player, move, n };
|
|
1280
909
|
}
|
|
1281
910
|
|
|
1282
|
-
pub fn duration(
|
|
1283
|
-
self: Null,
|
|
1284
|
-
comptime field: Duration.Field,
|
|
1285
|
-
player: Player,
|
|
1286
|
-
target: Player,
|
|
1287
|
-
turns: u4,
|
|
1288
|
-
) void {
|
|
1289
|
-
_ = .{ self, field, player, target, turns };
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
pub fn sleep(self: Null, player: Player, turns: u4) Error!void {
|
|
1293
|
-
_ = .{ self, player, turns };
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
pub fn confusion(self: Null, player: Player, turns: u4) Error!void {
|
|
1297
|
-
_ = .{ self, player, turns };
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
pub fn disable(self: Null, player: Player, turns: u4) Error!void {
|
|
1301
|
-
_ = .{ self, player, turns };
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
pub fn attacking(self: Null, player: Player, turns: u4) Error!void {
|
|
1305
|
-
_ = .{ self, player, turns };
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
pub fn binding(self: Null, player: Player, turns: u4) Error!void {
|
|
1309
|
-
_ = .{ self, player, turns };
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
pub fn encore(self: Null, player: Player, turns: u4) Error!void {
|
|
911
|
+
pub fn duration(self: Null, player: Player, turns: u3) void {
|
|
1313
912
|
_ = .{ self, player, turns };
|
|
1314
913
|
}
|
|
1315
914
|
};
|