diff --git a/docs/formats/p3rd_ai_script.md b/docs/formats/p3rd_ai_script.md index 28ba0ae2..5e1e8ab7 100644 --- a/docs/formats/p3rd_ai_script.md +++ b/docs/formats/p3rd_ai_script.md @@ -119,6 +119,12 @@ move another pair plays), `P3_WHEN`, `P3_WHEN_IGNORED`, a `P3_HISTORY` for a call before the read) and notes (`P3_DROPPED`, `P3_WEIGHT`, `P3_SPLIT`, an `info` `P3_FOLDED`). +A host pair needs what the host's own script does around it. em75 enters (1, 0) only after a +destination is set (`set_target kind=2`, a `walk_next` step) and, mostly, a turn (1, 2) toward it; +(1, 0) then walks there. Entered without one, it waits for good, and the script with it. So map a +donor pair to a host pair only where the host enters that pair the same way (`mhfu script ` +shows each site); a donor walk is derived (`"own"`). + ### Own moves A `[brain.pairs]` value may name an own move of the port's `[moves]` (`"7,13" = "slam"`) instead of diff --git a/packages/mhfu-port/tests/test_zinogre_brain.py b/packages/mhfu-port/tests/test_zinogre_brain.py index 8ed9331c..22633755 100644 --- a/packages/mhfu-port/tests/test_zinogre_brain.py +++ b/packages/mhfu-port/tests/test_zinogre_brain.py @@ -64,7 +64,7 @@ def test_the_manifest_carries_its_brain(zinogre): assert (b.donor, b.splice, b.entry, b.fatigue, b.charge) == (40, "base_3", "base_3", True, True) assert b.when == {"if_target_set": True, "if_target_state": True} assert manifest.loads(manifest.dumps(m)) == m - assert len(b.pairs) == 104 and len(b.derived()) == len(own.pairs(b)) == 34 + assert len(b.pairs) == 104 and len(b.derived()) == len(own.pairs(b)) == 39 def test_a_donor_pair_plays_the_authored_move_where_it_is_the_donors(zinogre): @@ -87,6 +87,26 @@ def test_a_wrapper_turns_and_plays_the_move_of_the_pair_it_leads_to(zinogre): assert (7, 13) in b.shared() and b.targets((7, 84)) == [TURN, "p3_7_13"] +WALKS = { + (2, 12): "p3_2_12", + (2, 13): "p3_2_12", + (2, 14): "p3_2_12", + (2, 16): "p3_2_16", + (2, 17): "p3_2_17", + (2, 21): "p3_2_21", + (2, 23): "p3_2_23", + (2, 24): "p3_2_23", + (2, 25): "p3_2_23", +} +"""The donor pairs that walk, with the move each plays (the host's (1, 0) parked live: D1).""" + + +def test_the_walks_play_own_moves_not_the_host_pair_1_0(zinogre): + b = zinogre.m.brain + assert not [k for k in b.pairs if (1, 0) in b.targets(manifest.pair_of(k))] + assert {p: b.targets(p) for p in WALKS} == {p: [name] for p, name in WALKS.items()} + + def chain_of(m: manifest.Manifest, name: str) -> list[tuple[object, ...]]: """What a derived move plays, step by step: clip, attacks, steer, length.""" out = [] @@ -106,7 +126,7 @@ def test_the_pairs_that_share_a_move_derive_the_same_one(zinogre, data): named = {k: v for k, v in b.pairs.items() if isinstance(v, str)} twins = {k: v for k, v in named.items() if v in b.derived()} twins |= {"7,71": "p3_7_13", "7,82": "p3_7_13", "7,119": "p3_7_13"} - assert len(twins) == 17 + assert len(twins) == 21 apart = manifest.load(PORT) apart.brain.pairs |= {k: manifest.OWN for k in twins} again, _ = own.expand(apart, data) @@ -210,6 +230,20 @@ def test_zinogre_reads_tired_from_gauge_a(zinogre): assert not any(n.op.name in ("if_tired", "if_stamina_low") for n in tests) +def test_zinogre_walks_enter_their_own_moves(zinogre): + s, _ = splice_of(zinogre, zinogre.m.brain) + for pair, name in WALKS.items(): + assert s.uses[pair, name] > 0 and not [t for (p, t) in s.uses if p == pair and t != name] + nodes = [n for i in s.edits.inserts.values() for n in edit.everything(i.nodes)] + nodes += [n for r in s.edits.routines.values() for n in edit.everything(r)] + pairs = { + (n.args["main"], n.args["sub"]) + for n in nodes + if isinstance(n, ir.Insn) and n.op.name == "enter" + } + assert (1, 0) not in pairs and set(WALKS.values()) <= set(s.slots) + + def test_zinogre_enters_its_own_moves(zinogre): s, built = splice_of(zinogre, zinogre.m.brain) own_enters = { @@ -331,7 +365,7 @@ def test_the_own_moves_fit_the_framework(zinogre, module_text): _, built = module_text names = [n for n, mv in zinogre.m.moves.items() if mv.own] keys = sum(len(moves.curve(zinogre.m, n, built.layout) or "") // 4 for n in names) - assert len(names) == 83 and keys == 4367 + assert len(names) == 89 and keys == 4642 assert len(names) <= moves.SLOTS and keys <= moves.POOL assert not moves.problems(zinogre.m, built.layout, moves.records(Data.find().fu, 75)) @@ -342,4 +376,5 @@ def test_the_cli_splices_the_port(data, capsys): out = capsys.readouterr().out assert "routines added" in out and "worst run" in out assert " charge: charge_up 40/55/65/100, p3_1_15 25/35/40/55, " in out + assert " pair 2,12 -> own p3_2_12 (slot " in out and " pair 2,25 -> own p3_2_23 (slot " in out assert " pair 7,23 -> 1,2 x" in out and " pair 7,23 -> own p3_7_0 (slot " in out diff --git a/ports/README.md b/ports/README.md index 9fb6e4e2..b4b8a881 100644 --- a/ports/README.md +++ b/ports/README.md @@ -204,12 +204,13 @@ port with them; `mhfu move play --own NAME [--force]` plays one of its own moves The Zinogre's moves are the donor's: `[brain]` splices MHP3rd's own script (`base_3`, its charged picker `base_6` and its normal one `base_5`) into em75's brain, so what he picks, when he charges up (the helper's gauge, `[brain] charge`) and when he is tired (`[brain] fatigue`) is the donor's. -A donor pair plays a host pair (a turn, a walk), the authored move that is the donor's +A donor pair plays a host pair (a turn), the authored move that is the donor's (`charge_up`, `dash`, `dash_stop`, `break_howl`) or an own move derived from the donor (`"own"`); a pair that only turns, then enters another, turns on em75's (1, 2) and plays the move of the pair it leads to; pairs whose derived moves come out identical (twins) share the -first one's. His 83 own moves, the 14 authored and the 69 derived, take 4367 of the registry's -5632 turn keys and 83 of its 96 slots. +first one's. The donor's walks are derived moves on their own walk clips: em75's (1, 0) walks to a +destination its own script sets first (docs/formats/p3rd_ai_script.md). His 89 own moves, the 14 authored and the 75 derived, take 4642 of +the registry's 5632 turn keys and 89 of its 96 slots. What stays of the hand-built rules, after the brain's own picks (2 plays nothing and so runs first): diff --git a/ports/zinogre.toml b/ports/zinogre.toml index cced5303..06bd6088 100644 --- a/ports/zinogre.toml +++ b/ports/zinogre.toml @@ -1113,43 +1113,16 @@ charge = true ] "2,10" = "own" "2,11" = "p3_2_10" -"2,12" = [ - 1, - 0, -] -"2,13" = [ - 1, - 0, -] -"2,14" = [ - 1, - 0, -] -"2,16" = [ - 1, - 0, -] -"2,17" = [ - 1, - 0, -] -"2,21" = [ - 1, - 0, -] +"2,12" = "own" +"2,13" = "p3_2_12" +"2,14" = "p3_2_12" +"2,16" = "own" +"2,17" = "own" +"2,21" = "own" "2,22" = "own" -"2,23" = [ - 1, - 0, -] -"2,24" = [ - 1, - 0, -] -"2,25" = [ - 1, - 0, -] +"2,23" = "own" +"2,24" = "p3_2_23" +"2,25" = "p3_2_23" "2,26" = [ 1, 2,