@pkmn/engine 0.1.0-dev.a23f64f3 → 0.1.0-dev.a27b3ff3
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 +46 -1
- package/build/pkg/addon/browser.js +4 -1
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +20 -7
- package/build/pkg/addon/node.js.map +1 -1
- package/build/pkg/addon/wasm.js +23 -14
- package/build/pkg/addon/wasm.js.map +1 -1
- package/build/pkg/addon.d.ts +1 -1
- package/build/pkg/gen1.d.ts +2 -1
- package/build/pkg/gen1.js +22 -9
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -0
- package/build/pkg/index.js +17 -7
- package/build/pkg/index.js.map +1 -1
- package/build/tools/debug.js +17 -7
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +18 -7
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +18 -7
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +17 -7
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +16 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +95 -26
- package/package.json +24 -20
- package/src/bin/install-pkmn-engine +1 -0
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +1 -1
- package/src/lib/common/js.zig +1 -5
- package/src/lib/common/napi.zig +4 -0
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/rational.zig +281 -270
- package/src/lib/common/util.zig +17 -2
- package/src/lib/gen1/calc.zig +31 -22
- package/src/lib/gen1/data/types.zig +1 -1
- package/src/lib/gen1/mechanics.zig +13 -11
- package/src/lib/gen2/calc.zig +1 -1
- package/src/lib/gen2/data/types.zig +6 -3
- package/src/lib/gen2/data.zig +3 -2
- package/src/lib/gen2/mechanics.zig +2 -2
- package/src/lib/node.zig +3 -118
- package/src/lib/pkmn.zig +3 -2
- package/src/lib/wasm.zig +12 -43
- package/src/tools/display/dom.ts +5 -8
package/src/lib/gen1/calc.zig
CHANGED
|
@@ -99,7 +99,7 @@ pub const Calc = struct {
|
|
|
99
99
|
self: Calc,
|
|
100
100
|
player: Player,
|
|
101
101
|
comptime field: Action.Field,
|
|
102
|
-
) ?
|
|
102
|
+
) ?util.FieldType(Action, field) {
|
|
103
103
|
if (!enabled) return null;
|
|
104
104
|
|
|
105
105
|
const val = @field(self.overrides.get(player), @tagName(field));
|
|
@@ -146,7 +146,7 @@ const Null = struct {
|
|
|
146
146
|
self: Null,
|
|
147
147
|
player: Player,
|
|
148
148
|
comptime field: Action.Field,
|
|
149
|
-
) ?
|
|
149
|
+
) ?util.FieldType(Action, field) {
|
|
150
150
|
_ = .{ self, player };
|
|
151
151
|
return null;
|
|
152
152
|
}
|
|
@@ -182,7 +182,7 @@ pub const Options = struct {
|
|
|
182
182
|
metronome: bool = false,
|
|
183
183
|
};
|
|
184
184
|
|
|
185
|
-
pub const MAX_FRONTIER =
|
|
185
|
+
pub const MAX_FRONTIER = 83;
|
|
186
186
|
|
|
187
187
|
pub fn transitions(
|
|
188
188
|
battle: anytype,
|
|
@@ -216,7 +216,7 @@ pub fn transitions(
|
|
|
216
216
|
const p1 = b.side(.P1);
|
|
217
217
|
const p2 = b.side(.P2);
|
|
218
218
|
|
|
219
|
-
var p: Rational(
|
|
219
|
+
var p: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
220
220
|
try frontier.append(opts.chance.actions);
|
|
221
221
|
|
|
222
222
|
// zig fmt: off
|
|
@@ -231,6 +231,8 @@ pub fn transitions(
|
|
|
231
231
|
const f = frontier.items[i];
|
|
232
232
|
const saved = stats.saved;
|
|
233
233
|
|
|
234
|
+
var r: Rational(u256) = .{ .p = 0, .q = 1 };
|
|
235
|
+
|
|
234
236
|
try debug(writer, f, .{
|
|
235
237
|
.shape = true,
|
|
236
238
|
.color = i,
|
|
@@ -251,8 +253,8 @@ pub fn transitions(
|
|
|
251
253
|
for (Rolls.disable(f.p2, d.p2, p2_slp)) |p2_dis| { a.p2.disable = p2_dis;
|
|
252
254
|
for (Rolls.attacking(f.p1, d.p1, p1_slp)) |p1_atk| { a.p1.attacking = p1_atk;
|
|
253
255
|
for (Rolls.attacking(f.p2, d.p2, p2_slp)) |p2_atk| { a.p2.attacking = p2_atk;
|
|
254
|
-
for (Rolls.confusion(f.p1, d.p1, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
255
|
-
for (Rolls.confusion(f.p2, d.p2, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
256
|
+
for (Rolls.confusion(f.p1, d.p1, tie, p1_atk, p1_slp)) |p1_cfz| { a.p1.confusion = p1_cfz;
|
|
257
|
+
for (Rolls.confusion(f.p2, d.p2, tie, p2_atk, p2_slp)) |p2_cfz| { a.p2.confusion = p2_cfz;
|
|
256
258
|
for (Rolls.confused(f.p1, p1_cfz)) |p1_cfzd| { a.p1.confused = p1_cfzd;
|
|
257
259
|
for (Rolls.confused(f.p2, p2_cfz)) |p2_cfzd| { a.p2.confused = p2_cfzd;
|
|
258
260
|
for (Rolls.paralyzed(f.p1, p1_cfzd)) |p1_par| { a.p1.paralyzed = p1_par;
|
|
@@ -298,9 +300,9 @@ pub fn transitions(
|
|
|
298
300
|
const p1_max: u9 = if (p2_dmg.min != p2_min)
|
|
299
301
|
p1_dmg.min
|
|
300
302
|
else
|
|
301
|
-
|
|
303
|
+
Rolls.coalesce(.P1, @as(u8, @intCast(p1_dmg.min)), summaries, cap);
|
|
302
304
|
const p2_max: u9 =
|
|
303
|
-
|
|
305
|
+
Rolls.coalesce(.P2, @as(u8, @intCast(p2_dmg.min)), summaries, cap);
|
|
304
306
|
|
|
305
307
|
if (opts.chance.actions.matches(f)) {
|
|
306
308
|
if (!opts.chance.actions.relax().eql(a)) {
|
|
@@ -343,6 +345,8 @@ pub fn transitions(
|
|
|
343
345
|
q.reduce();
|
|
344
346
|
try p.add(q);
|
|
345
347
|
p.reduce();
|
|
348
|
+
try r.add(q);
|
|
349
|
+
r.reduce();
|
|
346
350
|
|
|
347
351
|
stats.saved += 1;
|
|
348
352
|
|
|
@@ -384,14 +388,15 @@ pub fn transitions(
|
|
|
384
388
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
385
389
|
|
|
386
390
|
assert(stats.saved > saved);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
391
|
+
// TODO: ziglang/zig#22667
|
|
392
|
+
// const old = @hasDecl(std.fmt, "formatFloatDecimal");
|
|
393
|
+
// if (!old and @TypeOf(writer) != @TypeOf(std.io.null_writer)) {
|
|
394
|
+
// try writer.print(
|
|
395
|
+
// " {} ({d:.2}%)\n = ──────────\n {} ({d:.2}%)\n\n",
|
|
396
|
+
// .{r, 100 * @as(f128, @floatFromInt(r.p)) / @as(f128, @floatFromInt(r.q)),
|
|
397
|
+
// p, 100 * @as(f128, @floatFromInt(p.p)) / @as(f128, @floatFromInt(p.q))},
|
|
398
|
+
// );
|
|
399
|
+
// }
|
|
395
400
|
|
|
396
401
|
}
|
|
397
402
|
|
|
@@ -668,11 +673,13 @@ pub const Rolls = struct {
|
|
|
668
673
|
const CFZ_CONTINUING = [_]Optional(Confusion){.continuing};
|
|
669
674
|
const CFZ_ENDED = [_]Optional(Confusion){.ended};
|
|
670
675
|
const CFZ = [_]Optional(Confusion){ .continuing, .ended };
|
|
676
|
+
const CFZ_TIE = [_]Optional(Confusion){ .started, .continuing, .ended };
|
|
671
677
|
|
|
672
678
|
/// FIXME
|
|
673
679
|
pub fn confusion(
|
|
674
680
|
action: Action,
|
|
675
681
|
duration: Duration,
|
|
682
|
+
tie: Optional(Player),
|
|
676
683
|
sibling: Optional(Observation),
|
|
677
684
|
parent: Optional(Observation),
|
|
678
685
|
) []const Optional(Confusion) {
|
|
@@ -686,6 +693,8 @@ pub const Rolls = struct {
|
|
|
686
693
|
&CFZ_ENDED
|
|
687
694
|
else if ((parent == .continuing and parent == .ended) or duration.confusion < 2)
|
|
688
695
|
&CFZ_CONTINUING
|
|
696
|
+
else if (tie != .None)
|
|
697
|
+
&CFZ_TIE
|
|
689
698
|
else
|
|
690
699
|
&CFZ,
|
|
691
700
|
};
|
|
@@ -744,7 +753,7 @@ pub const Rolls = struct {
|
|
|
744
753
|
|
|
745
754
|
/// Returns the max damage roll which will produce the same damage as `roll`
|
|
746
755
|
/// given the base damage in `summaries`.
|
|
747
|
-
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool)
|
|
756
|
+
pub fn coalesce(player: Player, roll: u8, summaries: *Summaries, cap: bool) u8 {
|
|
748
757
|
if (roll == 0) return roll;
|
|
749
758
|
|
|
750
759
|
const dmg = summaries.get(player.foe()).damage;
|
|
@@ -854,12 +863,12 @@ test "Rolls.damage" {
|
|
|
854
863
|
|
|
855
864
|
test "Rolls.coalesce" {
|
|
856
865
|
var summaries = Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
|
|
857
|
-
try expectEqual(@as(u8, 0),
|
|
858
|
-
try expectEqual(@as(u8, 241),
|
|
859
|
-
try expectEqual(@as(u8, 255),
|
|
866
|
+
try expectEqual(@as(u8, 0), Rolls.coalesce(.P2, 0, &summaries, false));
|
|
867
|
+
try expectEqual(@as(u8, 241), Rolls.coalesce(.P2, 238, &summaries, false));
|
|
868
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 238, &summaries, true));
|
|
860
869
|
summaries.p1.damage.final = 74;
|
|
861
|
-
try expectEqual(@as(u8, 217),
|
|
862
|
-
try expectEqual(@as(u8, 255),
|
|
870
|
+
try expectEqual(@as(u8, 217), Rolls.coalesce(.P2, 217, &summaries, false));
|
|
871
|
+
try expectEqual(@as(u8, 255), Rolls.coalesce(.P2, 217, &summaries, true));
|
|
863
872
|
}
|
|
864
873
|
|
|
865
874
|
test "Rolls.hit" {
|
|
@@ -98,7 +98,7 @@ pub const Type = enum(u4) {
|
|
|
98
98
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/// The precedence order of Type `
|
|
101
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
102
102
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
103
103
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
104
104
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
@@ -618,17 +618,10 @@ fn beforeMove(
|
|
|
618
618
|
return .err;
|
|
619
619
|
}
|
|
620
620
|
}
|
|
621
|
-
// Pokémon Showdown incorrectly changes the "target" of the confusion self-hit based
|
|
622
|
-
// on the targeting behavior of the confused Pokémon's selected move which results
|
|
623
|
-
// in the wrong behavior with respect to the Substitute + Confusion glitch
|
|
624
|
-
const target = if (showdown and Move.get(side.last_selected_move).target == .Self)
|
|
625
|
-
player
|
|
626
|
-
else
|
|
627
|
-
player.foe();
|
|
628
621
|
|
|
629
622
|
const uncapped = battle.last_damage;
|
|
630
623
|
// Skipping adjustDamage / randomizeDamage / checkHit
|
|
631
|
-
_ = try applyDamage(battle, player,
|
|
624
|
+
_ = try applyDamage(battle, player, player.foe(), .Confusion, options);
|
|
632
625
|
// Pokémon Showdown thinks that confusion damage is uncapped ¯\_(ツ)_/¯
|
|
633
626
|
if (showdown) battle.last_damage = uncapped;
|
|
634
627
|
|
|
@@ -1170,7 +1163,7 @@ fn calcDamage(
|
|
|
1170
1163
|
// encountered so we need to make sure that in the event of a miss we commit the status of the
|
|
1171
1164
|
// crit roll. The alternative to doing this extra work is to *always* commit the crit roll but
|
|
1172
1165
|
// that results in counter intuitive probabilities in the common case
|
|
1173
|
-
if (pkmn.options.chance and !pkmn.options.mod) {
|
|
1166
|
+
if (pkmn.options.chance and !pkmn.options.mod and move.effect != .Explode) {
|
|
1174
1167
|
atk = attack(side, !crit, special);
|
|
1175
1168
|
def = defense(target, !crit, special, false);
|
|
1176
1169
|
if ((atk > 255 or def > 255) and (def / 4) & 255 == 0) options.chance.glitch();
|
|
@@ -1304,7 +1297,7 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1304
1297
|
}
|
|
1305
1298
|
|
|
1306
1299
|
// NOTE: llvm/llvm-project#58557
|
|
1307
|
-
battle.last_damage = if (comptime builtin.target.isWasm())
|
|
1300
|
+
battle.last_damage = if (comptime builtin.target.cpu.arch.isWasm())
|
|
1308
1301
|
@max(battle.last_damage *% 2, std.math.maxInt(u16))
|
|
1309
1302
|
else
|
|
1310
1303
|
battle.last_damage *| 2;
|
|
@@ -1453,7 +1446,16 @@ fn moveHit(
|
|
|
1453
1446
|
break :miss true;
|
|
1454
1447
|
}
|
|
1455
1448
|
if (!showdown) {
|
|
1456
|
-
if (move.effect == .Swift)
|
|
1449
|
+
if (move.effect == .Swift) {
|
|
1450
|
+
// If Swift could cause a division-by-zero freeze (which doesn't exist on Pokémon
|
|
1451
|
+
// Showdown, hence why the block above doesn't include this code), we need the
|
|
1452
|
+
// damage roll to be committed. However, the commit code will not save this roll if
|
|
1453
|
+
// the glitch occurs and the move was not marked as having hit. To work around this
|
|
1454
|
+
// we flag that the move hit (as Swift trivially does), but pass the probability of
|
|
1455
|
+
// the hit as zero to ensure it doesn't actually get saved.
|
|
1456
|
+
try options.chance.hit(player, true, 0);
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1457
1459
|
if (foe.active.volatiles.Invulnerable) break :miss true;
|
|
1458
1460
|
}
|
|
1459
1461
|
|
package/src/lib/gen2/calc.zig
CHANGED
|
@@ -172,9 +172,12 @@ pub const Type = enum(u8) {
|
|
|
172
172
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
/// The precedence order of Type `
|
|
176
|
-
pub fn precedence(
|
|
177
|
-
return
|
|
175
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
176
|
+
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
177
|
+
// The Ice vs. Fire matchup is out of order - return a higher
|
|
178
|
+
// number than anything else so that it sorts last correctly
|
|
179
|
+
if (t1 == .Ice and t2 == .Fire) return 18;
|
|
180
|
+
return PRECEDENCE[@intFromEnum(t2)];
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
/// The internal index of this Type used by Present.
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -449,8 +449,9 @@ test Types {
|
|
|
449
449
|
try expect(!Type.Steel.special());
|
|
450
450
|
try expect(Type.Dark.special());
|
|
451
451
|
|
|
452
|
-
try expectEqual(@as(u8, 3), Type.Water.
|
|
453
|
-
try expect(Type.
|
|
452
|
+
try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
|
|
453
|
+
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
454
|
+
try expect(Type.precedence(.Ice, .Fire) > Type.precedence(.Ice, .Water));
|
|
454
455
|
|
|
455
456
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
456
457
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
|
|
@@ -682,7 +682,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
682
682
|
|
|
683
683
|
// Type effectiveness matchup precedence only matters with (NVE, SE)
|
|
684
684
|
if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
|
|
685
|
-
types.type1.precedence(
|
|
685
|
+
Type.precedence(state.type, types.type1) > Type.precedence(state.type, types.type2))
|
|
686
686
|
{
|
|
687
687
|
assert(eff2 != neutral);
|
|
688
688
|
d = d *% eff2 / 10;
|
|
@@ -2841,7 +2841,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2841
2841
|
|
|
2842
2842
|
// NOTE: llvm/llvm-project#58557
|
|
2843
2843
|
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
-
if (!comptime builtin.target.isWasm()) return a *| b;
|
|
2844
|
+
if (!comptime builtin.target.cpu.arch.isWasm()) return a *| b;
|
|
2845
2845
|
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
2846
|
}
|
|
2847
2847
|
|
package/src/lib/node.zig
CHANGED
|
@@ -1,129 +1,14 @@
|
|
|
1
|
+
const c = @import("common/napi.zig");
|
|
1
2
|
const js = @import("common/js.zig");
|
|
2
|
-
const
|
|
3
|
+
const node = @import("bindings/node.zig");
|
|
3
4
|
const std = @import("std");
|
|
4
5
|
|
|
5
6
|
const assert = std.debug.assert;
|
|
6
7
|
|
|
7
|
-
const c = @cImport({
|
|
8
|
-
@cDefine("NAPI_VERSION", "8");
|
|
9
|
-
@cInclude("node_api.h");
|
|
10
|
-
});
|
|
11
|
-
|
|
12
8
|
export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
|
|
13
9
|
const properties = [_]c.napi_property_descriptor{
|
|
14
|
-
js.Property.init("engine", .{ .value = register(env) }),
|
|
10
|
+
js.Property.init("engine", .{ .value = node.register(env) }),
|
|
15
11
|
};
|
|
16
12
|
assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
|
|
17
13
|
return exports;
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
pub fn register(env: c.napi_env) c.napi_value {
|
|
21
|
-
const object = js.Object.init(env);
|
|
22
|
-
const properties = [_]c.napi_property_descriptor{
|
|
23
|
-
js.Property.init("options", .{ .value = options(env) }),
|
|
24
|
-
js.Property.init("bindings", .{ .value = bindings(env) }),
|
|
25
|
-
};
|
|
26
|
-
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
27
|
-
return object;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
fn options(env: c.napi_env) c.napi_value {
|
|
31
|
-
const object = js.Object.init(env);
|
|
32
|
-
const properties = [_]c.napi_property_descriptor{
|
|
33
|
-
js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
|
|
34
|
-
js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
|
|
35
|
-
js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
|
|
36
|
-
js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
|
|
37
|
-
};
|
|
38
|
-
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
39
|
-
return object;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
fn bindings(env: c.napi_env) c.napi_value {
|
|
43
|
-
const array = js.Array.init(env, .{ .length = 1 });
|
|
44
|
-
js.Array.set(env, array, 0, bind(env, pkmn.gen1));
|
|
45
|
-
return array;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
fn bind(env: c.napi_env, gen: anytype) c.napi_value {
|
|
49
|
-
const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
|
|
50
|
-
const logs_size: u32 = @intCast(gen.LOGS_SIZE);
|
|
51
|
-
const object = js.Object.init(env);
|
|
52
|
-
const properties = [_]c.napi_property_descriptor{
|
|
53
|
-
js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
|
|
54
|
-
js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
|
|
55
|
-
js.Property.init("update", .{ .method = update(gen) }),
|
|
56
|
-
js.Property.init("choices", .{ .method = choices(gen) }),
|
|
57
|
-
};
|
|
58
|
-
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
59
|
-
return object;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fn update(gen: anytype) c.napi_callback {
|
|
63
|
-
return struct {
|
|
64
|
-
fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
|
|
65
|
-
var argc: usize = 4;
|
|
66
|
-
var argv: [4]c.napi_value = undefined;
|
|
67
|
-
assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
|
|
68
|
-
assert(argc == 4);
|
|
69
|
-
|
|
70
|
-
var data: ?*anyopaque = undefined;
|
|
71
|
-
var len: usize = 0;
|
|
72
|
-
assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
|
|
73
|
-
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
74
|
-
|
|
75
|
-
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
76
|
-
const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
|
|
77
|
-
const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
|
|
78
|
-
|
|
79
|
-
var vtype: c.napi_valuetype = undefined;
|
|
80
|
-
assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
|
|
81
|
-
const result = switch (vtype) {
|
|
82
|
-
c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
|
|
83
|
-
else => result: {
|
|
84
|
-
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
85
|
-
assert(len == gen.LOGS_SIZE);
|
|
86
|
-
|
|
87
|
-
const buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
|
|
88
|
-
var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
|
|
89
|
-
// TODO: extract out
|
|
90
|
-
var opts = pkmn.battle.options(
|
|
91
|
-
pkmn.protocol.FixedLog{ .writer = stream.writer() },
|
|
92
|
-
gen.chance.NULL,
|
|
93
|
-
gen.calc.NULL,
|
|
94
|
-
);
|
|
95
|
-
break :result battle.update(c1, c2, &opts);
|
|
96
|
-
},
|
|
97
|
-
} catch unreachable;
|
|
98
|
-
|
|
99
|
-
return js.Number.init(env, @as(u8, @bitCast(result)));
|
|
100
|
-
}
|
|
101
|
-
}.call;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
fn choices(gen: anytype) c.napi_callback {
|
|
105
|
-
return struct {
|
|
106
|
-
fn call(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
|
|
107
|
-
var argc: usize = 4;
|
|
108
|
-
var argv: [4]c.napi_value = undefined;
|
|
109
|
-
assert(c.napi_get_cb_info(env, info, &argc, &argv, null, null) == c.napi_ok);
|
|
110
|
-
assert(argc == 4);
|
|
111
|
-
|
|
112
|
-
var data: ?*anyopaque = undefined;
|
|
113
|
-
var len: usize = 0;
|
|
114
|
-
assert(c.napi_get_arraybuffer_info(env, argv[0], &data, &len) == c.napi_ok);
|
|
115
|
-
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
116
|
-
|
|
117
|
-
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
118
|
-
const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
|
|
119
|
-
const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
|
|
120
|
-
|
|
121
|
-
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
122
|
-
assert(len == gen.CHOICES_SIZE);
|
|
123
|
-
|
|
124
|
-
const out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
|
|
125
|
-
const n = battle.choices(player, request, out);
|
|
126
|
-
return js.Number.init(env, @as(u8, @bitCast(n)));
|
|
127
|
-
}
|
|
128
|
-
}.call;
|
|
129
|
-
}
|
package/src/lib/pkmn.zig
CHANGED
|
@@ -129,6 +129,7 @@ pub const gen2 = struct {
|
|
|
129
129
|
// NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
|
|
130
130
|
pub const js = if (options.internal) @import("common/js.zig") else {};
|
|
131
131
|
pub const bindings = if (options.internal) struct {
|
|
132
|
-
pub const
|
|
133
|
-
pub const
|
|
132
|
+
pub const c = @import("bindings/c.zig");
|
|
133
|
+
pub const node = @import("bindings/node.zig");
|
|
134
|
+
pub const wasm = @import("bindings/wasm.zig");
|
|
134
135
|
} else {};
|
package/src/lib/wasm.zig
CHANGED
|
@@ -1,48 +1,17 @@
|
|
|
1
|
-
const pkmn = @import("pkmn.zig");
|
|
2
1
|
const std = @import("std");
|
|
2
|
+
const wasm = @import("bindings/wasm.zig");
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
export const SHOWDOWN = wasm.options.showdown;
|
|
5
|
+
export const LOG = wasm.options.log;
|
|
6
|
+
export const CHANCE = wasm.options.chance;
|
|
7
|
+
export const CALC = wasm.options.calc;
|
|
5
8
|
|
|
6
|
-
const
|
|
9
|
+
export const GEN1_CHOICES_SIZE = wasm.gen(1).CHOICES_SIZE;
|
|
10
|
+
export const GEN1_LOGS_SIZE = wasm.gen(1).LOGS_SIZE;
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
export const LOG = pkmn.options.log;
|
|
10
|
-
export const CHANCE = pkmn.options.chance;
|
|
11
|
-
export const CALC = pkmn.options.calc;
|
|
12
|
+
const exportable = @hasDecl(std.zig, "Zir") and !@hasField(std.zig.Zir.Inst.Tag, "export_value");
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export fn GEN1_update(
|
|
19
|
-
battle: *pkmn.gen1.Battle(pkmn.gen1.PRNG),
|
|
20
|
-
c1: pkmn.Choice,
|
|
21
|
-
c2: pkmn.Choice,
|
|
22
|
-
options: ?[*]u8,
|
|
23
|
-
) pkmn.Result {
|
|
24
|
-
return (if (options) |o| result: {
|
|
25
|
-
const buf = @as([*]u8, @ptrCast(o))[0..pkmn.gen1.LOGS_SIZE];
|
|
26
|
-
var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
|
|
27
|
-
// TODO: extract out
|
|
28
|
-
var opts = pkmn.battle.options(
|
|
29
|
-
pkmn.protocol.FixedLog{ .writer = stream.writer() },
|
|
30
|
-
pkmn.gen1.chance.NULL,
|
|
31
|
-
pkmn.gen1.calc.NULL,
|
|
32
|
-
);
|
|
33
|
-
break :result battle.update(c1, c2, &opts);
|
|
34
|
-
} else battle.update(c1, c2, &pkmn.gen1.NULL)) catch unreachable;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export fn GEN1_choices(
|
|
38
|
-
battle: *pkmn.gen1.Battle(pkmn.gen1.PRNG),
|
|
39
|
-
player: u8,
|
|
40
|
-
request: u8,
|
|
41
|
-
buf: [*]u8,
|
|
42
|
-
) u8 {
|
|
43
|
-
assert(player <= @field(@typeInfo(pkmn.Player), @tagName(Enum)).fields.len);
|
|
44
|
-
assert(request <= @field(@typeInfo(pkmn.Choice.Type), @tagName(Enum)).fields.len);
|
|
45
|
-
|
|
46
|
-
const len = GEN1_CHOICES_SIZE;
|
|
47
|
-
return battle.choices(@enumFromInt(player), @enumFromInt(request), @ptrCast(buf[0..len]));
|
|
48
|
-
}
|
|
14
|
+
usingnamespace if (exportable) struct {
|
|
15
|
+
export const GEN1_update = wasm.gen(1).update;
|
|
16
|
+
export const GEN1_choices = wasm.gen(1).choices;
|
|
17
|
+
} else wasm.exports();
|
package/src/tools/display/dom.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @license MIT modified from Vadim Demedes's https://github.com/vadimdemedes/dom-chef */
|
|
2
2
|
type Attributes = JSX.IntrinsicElements['div'];
|
|
3
|
-
type
|
|
3
|
+
type DocumentFragmentFunction = (props?: any) => DocumentFragment;
|
|
4
4
|
type ElementFunction = (props?: any) => HTMLElement;
|
|
5
5
|
|
|
6
6
|
declare global {
|
|
@@ -22,9 +22,6 @@ interface Fragment {
|
|
|
22
22
|
// https://github.com/preactjs/preact/blob/1bbd687c/src/constants.js#L3
|
|
23
23
|
const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
|
|
24
24
|
|
|
25
|
-
const isFragment = (type: DocumentFragmentConstructor | ElementFunction):
|
|
26
|
-
type is DocumentFragmentConstructor => type === DocumentFragment;
|
|
27
|
-
|
|
28
25
|
const setCSSProps = (element: HTMLElement, style: CSSStyleDeclaration) => {
|
|
29
26
|
for (const [name, value] of Object.entries(style)) {
|
|
30
27
|
if (name.startsWith('-')) {
|
|
@@ -57,12 +54,12 @@ const addChildren = (parent: Element | DocumentFragment, children: Node[]) => {
|
|
|
57
54
|
const FALSIFIABLE_ATTRIBUTES = ['contentEditable', 'draggable', 'spellCheck', 'value'];
|
|
58
55
|
|
|
59
56
|
export const h = (
|
|
60
|
-
type:
|
|
57
|
+
type: DocumentFragmentFunction | ElementFunction | string,
|
|
61
58
|
attributes?: Attributes,
|
|
62
59
|
...children: Node[]
|
|
63
60
|
) => {
|
|
64
61
|
if (typeof type !== 'string') {
|
|
65
|
-
const element =
|
|
62
|
+
const element = type(attributes);
|
|
66
63
|
addChildren(element, children);
|
|
67
64
|
return element;
|
|
68
65
|
}
|
|
@@ -92,5 +89,5 @@ export const h = (
|
|
|
92
89
|
return element;
|
|
93
90
|
};
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
93
|
+
export const Fragment = (unused: any) => document.createDocumentFragment();
|