What the bot playing the game says about itself: the zbanks/alttp C bot
(packages/zbanks), read from its own globals once a UI pass.
Plain data in, so this crate needs neither the bot nor its C and still
compiles for wasm; the app fills a [Bot] from zbanks::Report.
7use egui::{FontId, RichText};
9use crate::palette::{FAINT, GOAL, GOOD, GREY, PLAIN, RED, SECTION};
One goal on the bot's goal list (ap_plan.c's struct ap_goal).
12pub struct Goal<'a> {
GOAL_* without the prefix: EXPLORE, CHEST, PICKUP, NPC, ITEM, SCRIPT.
14 pub kind: &'a str,
The node the goal is about, in the bot's own naming.
16 pub node: &'a str,
The score the planner last gave it, as words when it is not a cost
("complete", "unsatisfiable", "over limit" - zbanks::score_words).
22 pub score: String,
Whether that score is one the planner will not pick.
28pub struct Bot<'a> {
The frame last passed to ap_tick.
30 pub frame: u32,
ap_info_string: the line upstream's Snes9x drew over the picture.
32 pub info: &'a str,
The task list, current task first, in ap_print_task's words.
All goals the bot has, of which goals are the first.
assert_bp failures so far (each a trap the host stepped over, as gdb
would), and the frame of the last.
base() requests that mapped to nothing; should stay 0.
44 pub stray_reads: u64,
Goals the bot gave up on, waiting for Link's possessions to change, and how many the host has put back so far.
Set by the host's stall detector (packages/zbanks/src/stall.rs):
the bot has not chosen a goal, or has not moved, for its threshold's
worth of frames, or the planner has given up for good. None means
not stalled - never omitted quietly, so the panel cannot look normal
while the bot has stopped playing.
54 pub stalled: Option<&'a str>,
The host's own recovery from a stall (packages/zbanks/src/recovery.rs):
the last attempt's line, and whether recovery has given up for good
(shown in red, like stalled, instead of the ordinary attention
colour) - once true it stays true for the life of the process, same
as stalled's own ManualMode used to before recovery could clear
it.
61 pub recovery: Option<(&'a str, bool)>,
A small header used by more than one panel in this crate. pub(crate)
so jev.rs's "History" section can use the exact same look.
The bot's line, its tasks and its goals.
74pub fn show(ui: &mut egui::Ui, bot: &Bot) { 75 ui.heading("Bot"); 76 if let Some(stalled) = bot.stalled { 77 ui.add(egui::Label::new(RichText::new(stalled).color(RED).strong()).wrap()); 78 } 79 if let Some((line, gave_up)) = bot.recovery { 80 let colour = if gave_up { RED } else { GOAL }; 81 ui.add(egui::Label::new(RichText::new(line).color(colour).strong()).wrap()); 82 } 83 ui.label(RichText::new("zbanks/alttp, C").color(GREY)); 84 let info = if bot.info.is_empty() { "(nothing yet)" } else { bot.info }; 85 ui.add(egui::Label::new(RichText::new(info).font(FontId::monospace(12.0)).color(GOAL)).wrap()); 86 if bot.paused_by_x { 87 ui.label(RichText::new("X held: the bot steps aside, the pad is yours").color(RED)); 88 } 89 let mut facts = format!( 90 "frame {} link {:#06x},{:#06x} goals {}", 91 bot.frame, bot.link.0, bot.link.1, bot.goal_count 92 ); 93 if bot.traps > 0 { 94 facts.push_str(&format!(" traps {} (last frame {})", bot.traps, bot.last_trap_frame)); 95 } 96 if bot.given_up > 0 || bot.retried > 0 { 97 facts.push_str(&format!(" given up {} (put back {})", bot.given_up, bot.retried)); 98 } 99 ui.label(RichText::new(facts).color(GREY)); 100 if bot.stray_reads > 0 { 101 ui.label(RichText::new(format!("{} reads of unmapped memory", bot.stray_reads)).color(RED)); 102 } 103 104 egui::ScrollArea::vertical().auto_shrink([false, false]).show(ui, |ui| { 105 section(ui, "Tasks"); 106 if bot.tasks.is_empty() { 107 ui.label(RichText::new("none").color(GREY)); 108 } 109 for (i, task) in bot.tasks.iter().enumerate() { 110 let colour = if i == 0 { GOOD } else { PLAIN }; 111 ui.add( 112 egui::Label::new(RichText::new(task).font(FontId::monospace(11.0)).color(colour)) 113 .wrap(), 114 ); 115 } 116 section(ui, "Goals"); 117 for goal in bot.goals { 118 let colour = if goal.complete { 119 GOOD 120 } else if goal.out_of_reach { 121 FAINT 122 } else { 123 PLAIN 124 }; 125 let line = format!( 126 "{:<7} {:>12} {} on {}{}", 127 goal.kind, 128 goal.score, 129 goal.node, 130 goal.screen, 131 if goal.attempts > 0 { format!(" ({} tries)", goal.attempts) } else { String::new() }, 132 ); 133 ui.add(egui::Label::new(RichText::new(line).font(FontId::monospace(11.0)).color(colour)).wrap()); 134 } 135 if bot.goal_count > bot.goals.len() { 136 ui.label( 137 RichText::new(format!("... and {} more", bot.goal_count - bot.goals.len())).color(GREY), 138 ); 139 } 140 }); 141}