survey: the route probe prints the room whole, its people, and the rung at the end

The catch dump prints a small map whole with its people drawn and off
the screen, each person's ledger entries and whether a route reaches
them; outcomes are keyed by the map they finished on, the trace line
carries the seam's bytes (wCurOpponent included), and the drive ends
with the rank. How row 58's mechanism was read.
This commit is contained in:
acamilo 2026-09-23 10:34:33 +00:00
parent ddf0743395
commit 28bd980e65

View file

@ -1355,9 +1355,9 @@ fn dialog_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64
/// [`PokemonPalette`]: flybrain_gb::pokemon_red::macros::PokemonPalette /// [`PokemonPalette`]: flybrain_gb::pokemon_red::macros::PokemonPalette
fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64) { fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64) {
use flybrain_gb::MacroPalette; use flybrain_gb::MacroPalette;
use flybrain_gb::pokemon_red::macros::cartridge::{MacroState, TargetKey}; use flybrain_gb::pokemon_red::macros::cartridge::{FACINGS, MacroState, TalkTarget, TargetKey};
use flybrain_gb::pokemon_red::macros::path::Way; use flybrain_gb::pokemon_red::macros::path::Way;
use flybrain_gb::pokemon_red::macros::{PokemonPalette, palette, path}; use flybrain_gb::pokemon_red::macros::{PokemonPalette, Tile, palette, path};
let budget = env_usize("FLY_PROBE_FRAMES", 240_000); let budget = env_usize("FLY_PROBE_FRAMES", 240_000);
let mut rng = env_usize("FLY_PROBE_RNG", 20_260_923) as u32 | 1; let mut rng = env_usize("FLY_PROBE_RNG", 20_260_923) as u32 | 1;
@ -1484,7 +1484,12 @@ fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64)
} }
} }
if let Some((name, outcome)) = macros.take_finished() { if let Some((name, outcome)) = macros.take_finished() {
*outcomes.entry(format!("{name} {outcome:?}")).or_default() += 1; *outcomes
.entry(format!(
"{name} {outcome:?} on {:?}",
state::player(gb).map(|p| p.map)
))
.or_default() += 1;
if !matches!(outcome, flybrain_gb::Outcome::Done) { if !matches!(outcome, flybrain_gb::Outcome::Done) {
println!( println!(
"f{frame:<6} {:?} {name} {outcome:?}", "f{frame:<6} {:?} {name} {outcome:?}",
@ -1495,11 +1500,12 @@ fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64)
since_decision += 1; since_decision += 1;
if frame < trace_frames { if frame < trace_frames {
println!( println!(
" t{frame:<5} {:?} mask {mask:#04x} running {:?} marks {:?} stood {}", " t{frame:<5} {:?} mask {mask:#04x} running {:?} marks {:?} stood {} | {}",
state::player(gb).map(|p| (p.map, p.x, p.y, p.facing)), state::player(gb).map(|p| (p.map, p.x, p.y, p.facing)),
macros.running(), macros.running(),
macros.fences().1, macros.fences().1,
macros.stood() macros.stood(),
scene::why_unknown(gb)
); );
} }
gb.set_buttons(mask); gb.set_buttons(mask);
@ -1531,6 +1537,11 @@ fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64)
} }
} }
println!("```\n"); println!("```\n");
let progress = adapter.progress();
println!(
"- at the end: rank {} ({}), badges {}, unique tiles {}",
progress.rank, progress.rank_label, progress.counter, progress.unique_locations
);
println!("- refusals: {refusals:?}"); println!("- refusals: {refusals:?}");
println!("- outcomes: {outcomes:?}"); println!("- outcomes: {outcomes:?}");
let Some(frame) = caught_at else { let Some(frame) = caught_at else {
@ -1557,11 +1568,13 @@ fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64)
println!("- objective: {:?}", state.objective()); println!("- objective: {:?}", state.objective());
println!("- `objective_goals`: {:?}", palette::objective_goals(state)); println!("- `objective_goals`: {:?}", palette::objective_goals(state));
println!("- `objective_targets`: {:?}", palette::objective_targets(state)); println!("- `objective_targets`: {:?}", palette::objective_targets(state));
for (tile, target) in path::person_targets(state) { let drawn = path::person_targets(state);
for (tile, target) in drawn.iter().copied().chain(path::offscreen_person_targets(state)) {
println!( println!(
" - person {target:?} at ({:2},{:2}): talked {}, blocked {}, reached {}", " - person {target:?} at ({:2},{:2}) {}: talked {}, blocked {}, reached {}",
tile.x, tile.x,
tile.y, tile.y,
if drawn.contains(&(tile, target)) { "drawn" } else { "off the screen" },
state.talked(target), state.talked(target),
state.blocked(TargetKey::Thing(target)), state.blocked(TargetKey::Thing(target)),
state.reached(TargetKey::Thing(target)) state.reached(TargetKey::Thing(target))
@ -1591,16 +1604,44 @@ fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64)
reach reach
); );
} }
println!("\n### The fly's own neighbourhood (pushed = `P`, player = `@`)\n\n```"); // A room small enough to print whole is printed whole, with its people on it (row 58:
for y in player.y.saturating_sub(3)..=player.y.saturating_add(3) { // the gym's leader is twelve rows from the door).
let row: String = (player.x.saturating_sub(6)..=player.x.saturating_add(6)) let size = state.map_size().expect("a loaded map");
let whole = size.width <= 24 && size.height <= 24;
let people: Vec<(Tile, TalkTarget)> = path::person_targets(state)
.into_iter()
.chain(path::offscreen_person_targets(state))
.collect();
let (rows, columns) = if whole {
(0..=size.height - 1, 0..=size.width - 1)
} else {
(
player.y.saturating_sub(3)..=player.y.saturating_add(3),
player.x.saturating_sub(6)..=player.x.saturating_add(6),
)
};
let grid = state.map_grid();
println!(
"\n### The fly's {} (pushed = `P`, player = `@`, a person = `N`; grid {})\n\n```",
if whole { "whole map" } else { "own neighbourhood" },
grid.is_some()
);
for y in rows {
let row: String = columns
.clone()
.map(|x| { .map(|x| {
let walk = match grid.as_deref() {
Some(grid) => grid.walkable(x, y),
None => state.walkable(x, y),
};
if x == player.x && y == player.y { if x == player.x && y == player.y {
'@' '@'
} else if people.iter().any(|(tile, _)| *tile == Tile::new(x, y)) {
'N'
} else if state.pushed_tile(x, y) { } else if state.pushed_tile(x, y) {
'P' 'P'
} else { } else {
match state.walkable(x, y) { match walk {
flybrain_gb::pokemon_red::macros::state::Walkable::Yes => '.', flybrain_gb::pokemon_red::macros::state::Walkable::Yes => '.',
flybrain_gb::pokemon_red::macros::state::Walkable::No => '#', flybrain_gb::pokemon_red::macros::state::Walkable::No => '#',
flybrain_gb::pokemon_red::macros::state::Walkable::Unknown => '?', flybrain_gb::pokemon_red::macros::state::Walkable::Unknown => '?',
@ -1608,9 +1649,14 @@ fn route_survey(gb: &mut Emulator, adapter: &mut PokemonRedReward, ms: &mut f64)
} }
}) })
.collect(); .collect();
println!("y{y:2} x{:2}.. {row}", player.x.saturating_sub(6)); println!("y{y:2} x{:2}.. {row}", if whole { 0 } else { player.x.saturating_sub(6) });
} }
println!("```"); println!("```");
for (tile, target) in &people {
let aims: Vec<Tile> = FACINGS.iter().filter_map(|facing| tile.step(*facing)).collect();
let reach = path::route(state, &aims).map(|route| route.goal);
println!("- a route to {target:?} at ({:2},{:2}): {reach:?}", tile.x, tile.y);
}
}); });
} }