feat(viz): enter a specific seed (S to type, Enter to apply)

Press S to begin seed entry, type digits (HUD shows the buffer),
Enter applies and regenerates, Esc cancels, Backspace edits. Reroll
still increments. Digit-only input; buffer capped under u64's range.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Parley Hatch 2026-05-28 22:53:03 -06:00
parent ab195306a5
commit c13fc18d8f

View file

@ -201,7 +201,10 @@ fn draw_edges(snapshot: &Snapshot, layout: &Layout) {
}
/// Draws the on-screen HUD: seed, current stage, and the controls hint.
fn draw_hud(dungeon: &Dungeon) {
///
/// When `seed_input` is `Some`, the user is typing a seed: the top line becomes
/// an entry prompt showing the buffer, and the hint switches to the entry keys.
fn draw_hud(dungeon: &Dungeon, seed_input: Option<&str>) {
let stage_count = dungeon.snapshots.len();
let stage_label = dungeon
.current()
@ -209,7 +212,17 @@ fn draw_hud(dungeon: &Dungeon) {
.unwrap_or("(empty)");
let is_final = dungeon.stage + 1 == stage_count;
let seed_line = format!("seed: {}", dungeon.seed);
// The top line doubles as the seed-entry prompt while typing.
let (top_line, hint) = match seed_input {
Some(buf) => (
format!("set seed: {buf}_"),
"Enter: apply Esc: cancel (digits only)",
),
None => (
format!("seed: {}", dungeon.seed),
"Space: reroll Left/Right: scrub stages S: set seed",
),
};
let stage_line = format!(
"stage {}/{}: {}{}",
dungeon.stage + 1,
@ -217,11 +230,11 @@ fn draw_hud(dungeon: &Dungeon) {
stage_label,
if is_final { " (final)" } else { "" },
);
let hint = "Space: reroll Left/Right: scrub stages";
// A dark plate behind the text so it stays readable over light floor tiles.
draw_rectangle(0.0, 0.0, screen_width(), 64.0, Color::new(0.0, 0.0, 0.0, 0.55));
draw_text(&seed_line, 12.0, 22.0, 24.0, WHITE);
let top_color = if seed_input.is_some() { GOLD } else { WHITE };
draw_text(&top_line, 12.0, 22.0, 24.0, top_color);
draw_text(&stage_line, 12.0, 44.0, 22.0, SKYBLUE);
draw_text(hint, 12.0, 60.0, 18.0, LIGHTGRAY);
}
@ -234,18 +247,57 @@ async fn main() {
// determinism is unaffected — same seed always rebuilds the same dungeon.
let mut seed: u64 = 0;
let mut dungeon = Dungeon::generate(seed);
// `Some(buffer)` while the user is typing a seed; `None` in normal mode.
let mut seed_input: Option<String> = None;
loop {
// --- input ---
if is_key_pressed(KeyCode::Space) {
seed = seed.wrapping_add(1);
dungeon = Dungeon::generate(seed);
}
if is_key_pressed(KeyCode::Left) {
dungeon.scrub(-1);
}
if is_key_pressed(KeyCode::Right) {
dungeon.scrub(1);
if seed_input.is_none() {
// Normal mode: reroll, scrub, or begin seed entry.
if is_key_pressed(KeyCode::Space) {
seed = seed.wrapping_add(1);
dungeon = Dungeon::generate(seed);
}
if is_key_pressed(KeyCode::Left) {
dungeon.scrub(-1);
}
if is_key_pressed(KeyCode::Right) {
dungeon.scrub(1);
}
if is_key_pressed(KeyCode::S) {
// Begin seed entry; digits are collected on the following frames.
seed_input = Some(String::new());
}
} else {
// Seed-entry mode: collect digits, apply on Enter, cancel on Esc.
let mut exit = false;
{
let buf = seed_input.as_mut().expect("in seed-entry mode");
// Drain this frame's typed chars; keep digits only (so the `S`
// that opened the mode and any other keys are ignored). Cap the
// length well under u64's 20 digits so it always parses.
while let Some(c) = get_char_pressed() {
if c.is_ascii_digit() && buf.len() < 19 {
buf.push(c);
}
}
if is_key_pressed(KeyCode::Backspace) {
buf.pop();
}
if is_key_pressed(KeyCode::Enter) {
// Empty or unparseable buffer simply cancels (no change).
if let Ok(parsed) = buf.parse::<u64>() {
seed = parsed;
dungeon = Dungeon::generate(seed);
}
exit = true;
} else if is_key_pressed(KeyCode::Escape) {
exit = true;
}
}
if exit {
seed_input = None;
}
}
// --- draw ---
@ -271,7 +323,7 @@ async fn main() {
);
}
draw_hud(&dungeon);
draw_hud(&dungeon, seed_input.as_deref());
next_frame().await;
}