From 9b56dee0628de0b71ed1d0a330ac3c198bcc1280 Mon Sep 17 00:00:00 2001 From: 0xadk <0xadk@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:11:20 -0800 Subject: [PATCH 1/3] run clippy --fix --- src/app.rs | 42 +++++++++++++++++++++--------------------- src/binary_numbers.rs | 18 +++++++++--------- src/utils.rs | 4 ++-- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/app.rs b/src/app.rs index cdee587..9911b9c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -127,29 +127,29 @@ fn render_start_screen(state: &mut StartMenuState, area: Rect, buf: &mut Buffer) } fn handle_crossterm_events(app_state: &mut AppState) -> color_eyre::Result<()> { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - // global exit via Ctrl+C - KeyCode::Char('c') | KeyCode::Char('C') - if key.modifiers == KeyModifiers::CONTROL => - { - *app_state = AppState::Exit; - } + if let Event::Key(key) = event::read()? + && key.kind == KeyEventKind::Press + { + match key.code { + // global exit via Ctrl+C + KeyCode::Char('c') | KeyCode::Char('C') + if key.modifiers == KeyModifiers::CONTROL => + { + *app_state = AppState::Exit; + } - // state-specific input handling - _ => { - *app_state = match std::mem::replace(app_state, AppState::Exit) { - AppState::Start(mut menu) => { - handle_start_input(&mut menu, key) - .unwrap_or(AppState::Start(menu)) - } - AppState::Playing(mut game) => { - game.handle_input(key); - AppState::Playing(game) - } - AppState::Exit => AppState::Exit, + // state-specific input handling + _ => { + *app_state = match std::mem::replace(app_state, AppState::Exit) { + AppState::Start(mut menu) => { + handle_start_input(&mut menu, key) + .unwrap_or(AppState::Start(menu)) } + AppState::Playing(mut game) => { + game.handle_input(key); + AppState::Playing(game) + } + AppState::Exit => AppState::Exit, } } } diff --git a/src/binary_numbers.rs b/src/binary_numbers.rs index 98857c4..a3cbb58 100644 --- a/src/binary_numbers.rs +++ b/src/binary_numbers.rs @@ -168,7 +168,7 @@ impl WidgetRef for BinaryNumbersPuzzle { Block::bordered().border_type(border_type).fg(border_color).render(area, buf); let suggestion_str = format!("{suggestion}"); - Paragraph::new(format!("{}", suggestion_str)) + Paragraph::new(suggestion_str.to_string()) .white() .when(show_correct_number && is_correct_number, |p| p.light_green().underlined()) .alignment(Center) @@ -241,7 +241,7 @@ impl WidgetRef for BinaryNumbersPuzzle { Block::bordered().dark_gray().render(result_area, buf); - let instruction_spans: Vec = vec![ + let instruction_spans: Vec = [ hotkey_span("Left Right", "select "), hotkey_span("Enter", "confirm "), hotkey_span("S", "skip "), @@ -293,7 +293,7 @@ impl MainScreenWidget for BinaryNumbersGame { self.refresh_stats_snapshot(); } - fn handle_input(&mut self, input: KeyEvent) -> () { self.handle_game_input(input); } + fn handle_input(&mut self, input: KeyEvent) { self.handle_game_input(input); } fn is_exit_intended(&self) -> bool { self.exit_intended } } @@ -632,8 +632,7 @@ impl Widget for &mut BinaryNumbersGame { // Simple ASCII gauge renderer to avoid variable glyph heights from Unicode block elements fn render_ascii_gauge(area: Rect, buf: &mut Buffer, ratio: f64, color: Color) { - let clamped = if ratio < 0.0 { 0.0 } else if ratio > 1.0 { 1.0 } else { ratio }; - let fill_width = ((area.width as f64) * clamped).round().min(area.width as f64) as u16; + let fill_width = ((area.width as f64) * ratio.clamp(0.0, 1.0)).round().min(area.width as f64) as u16; if area.height == 0 { return; } for x in 0..area.width { let filled = x < fill_width; @@ -664,10 +663,11 @@ impl HighScores { let mut contents = String::new(); if file.read_to_string(&mut contents).is_ok() { for line in contents.lines() { - if let Some((k,v)) = line.split_once('=') { - if let (Ok(bits), Ok(score)) = (k.trim().parse::(), v.trim().parse::()) { - hs.scores.insert(bits, score); - } + if let Some((k,v)) = line.split_once('=') + && let Ok(bits) = k.trim().parse::() + && let Ok(score) = v.trim().parse::() + { + hs.scores.insert(bits, score); } } } diff --git a/src/utils.rs b/src/utils.rs index f83daec..f0d0b04 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -89,8 +89,8 @@ impl Widget for AsciiArtWidget { pub fn center(area: Rect, horizontal: Constraint) -> Rect { let [area] = Layout::horizontal([horizontal]).flex(Flex::Center).areas(area); - let area = vertically_center(area); - area + + vertically_center(area) } pub fn vertically_center(area: Rect) -> Rect { From ebc1ce12256f8b861a6201c71cd06279280dabbd Mon Sep 17 00:00:00 2001 From: 0xadk <0xadk@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:20:12 -0800 Subject: [PATCH 2/3] refactor keybind checking into it's own module --- src/app.rs | 13 +++++++------ src/binary_numbers.rs | 29 +++++++++++++++-------------- src/keybinds.rs | 25 +++++++++++++++++++++++++ src/main.rs | 1 + 4 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 src/keybinds.rs diff --git a/src/app.rs b/src/app.rs index 9911b9c..e5cab3b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,5 @@ use crate::binary_numbers::{BinaryNumbersGame, Bits}; +use crate::keybinds; use crate::main_screen_widget::MainScreenWidget; use crate::utils::{AsciiArtWidget, AsciiCells}; use crossterm::event; @@ -37,16 +38,16 @@ enum AppState { } fn handle_start_input(state: &mut StartMenuState, key: KeyEvent) -> Option { - match key.code { - KeyCode::Up => state.select_previous(), - KeyCode::Down => state.select_next(), - KeyCode::Enter => { + match key { + x if keybinds::is_up(x) => state.select_previous(), + x if keybinds::is_down(x) => state.select_next(), + x if keybinds::is_select(x) => { let bits = state.selected_bits(); // Store the current selection before entering the game set_last_selected_index(state.selected_index()); return Some(AppState::Playing(BinaryNumbersGame::new(bits))); } - KeyCode::Esc => return Some(AppState::Exit), + x if keybinds::is_exit(x) => return Some(AppState::Exit), _ => {} } None @@ -132,7 +133,7 @@ fn handle_crossterm_events(app_state: &mut AppState) -> color_eyre::Result<()> { { match key.code { // global exit via Ctrl+C - KeyCode::Char('c') | KeyCode::Char('C') + KeyCode::Char('c' | 'C') if key.modifiers == KeyModifiers::CONTROL => { *app_state = AppState::Exit; diff --git a/src/binary_numbers.rs b/src/binary_numbers.rs index a3cbb58..724679b 100644 --- a/src/binary_numbers.rs +++ b/src/binary_numbers.rs @@ -1,3 +1,4 @@ +use crate::keybinds; use crate::main_screen_widget::{MainScreenWidget, WidgetRef}; use crate::utils::{center, When}; use crossterm::event::{KeyCode, KeyEvent}; @@ -394,7 +395,7 @@ impl BinaryNumbersGame { } pub fn handle_game_input(&mut self, input: KeyEvent) { - if input.code == KeyCode::Esc { self.exit_intended = true; return; } + if keybinds::is_exit(input) { self.exit_intended = true; return; } if self.game_state == GameState::GameOver { self.handle_game_over_input(input); return; } match self.puzzle.guess_result { @@ -403,10 +404,10 @@ impl BinaryNumbersGame { } } - fn handle_game_over_input(&mut self, input: KeyEvent) { - match input.code { - KeyCode::Enter => { self.reset_game_state(); } - KeyCode::Esc => { self.exit_intended = true; } + fn handle_game_over_input(&mut self, key: KeyEvent) { + match key { + x if keybinds::is_select(x) => { self.reset_game_state(); } + x if keybinds::is_exit(x) => { self.exit_intended = true; } _ => {} } } @@ -426,8 +427,8 @@ impl BinaryNumbersGame { } fn handle_no_result_yet(&mut self, input: KeyEvent) { - match input.code { - KeyCode::Right => { + match input { + x if keybinds::is_right(x) => { // select the next suggestion if let Some(selected) = self.puzzle.selected_suggestion { let current_index = self.puzzle.suggestions.iter().position(|&x| x == selected); @@ -440,7 +441,7 @@ impl BinaryNumbersGame { self.puzzle.selected_suggestion = Some(self.puzzle.suggestions[0]); } } - KeyCode::Left => { + x if keybinds::is_left(x) => { // select the previous suggestion if let Some(selected) = self.puzzle.selected_suggestion { let current_index = self.puzzle.suggestions.iter().position(|&x| x == selected); @@ -454,7 +455,7 @@ impl BinaryNumbersGame { } } } - KeyCode::Enter => { + x if keybinds::is_select(x) => { if let Some(selected) = self.puzzle.selected_suggestion { if self.puzzle.is_correct_guess(selected) { self.puzzle.guess_result = Some(GuessResult::Correct); @@ -464,7 +465,7 @@ impl BinaryNumbersGame { self.finalize_round(); } } - KeyCode::Char('s') | KeyCode::Char('S') => { + KeyEvent { code: KeyCode::Char('s' | 'S'), .. } => { // Skip puzzle counts as timeout self.puzzle.guess_result = Some(GuessResult::Timeout); self.finalize_round(); @@ -473,9 +474,9 @@ impl BinaryNumbersGame { } } - fn handle_result_available(&mut self, input: KeyEvent) { - match input.code { - KeyCode::Enter => { + fn handle_result_available(&mut self, key: KeyEvent) { + match key { + x if keybinds::is_select(x) => { match self.game_state { GameState::PendingGameOver => { // reveal summary @@ -491,7 +492,7 @@ impl BinaryNumbersGame { GameState::Active => { /* shouldn't be here */ } } } - KeyCode::Esc => self.exit_intended = true, + x if keybinds::is_exit(x) => self.exit_intended = true, _ => {} } } diff --git a/src/keybinds.rs b/src/keybinds.rs new file mode 100644 index 0000000..b4c0452 --- /dev/null +++ b/src/keybinds.rs @@ -0,0 +1,25 @@ +use crossterm::event::{KeyCode, KeyEvent}; + +pub(crate) fn is_up(key: KeyEvent) -> bool { + matches!(key.code, KeyCode::Up) +} + +pub(crate) fn is_down(key: KeyEvent) -> bool { + matches!(key.code, KeyCode::Down) +} + +pub(crate) fn is_left(key: KeyEvent) -> bool { + matches!(key.code, KeyCode::Left) +} + +pub(crate) fn is_right(key: KeyEvent) -> bool { + matches!(key.code, KeyCode::Right) +} + +pub(crate) fn is_select(key: KeyEvent) -> bool { + matches!(key.code, KeyCode::Enter) +} + +pub(crate) fn is_exit(key: KeyEvent) -> bool { + matches!(key.code, KeyCode::Esc) +} diff --git a/src/main.rs b/src/main.rs index 0d7a760..498b768 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod app; mod binary_numbers; +mod keybinds; mod main_screen_widget; mod utils; From c43e37553e0f64eb20101fe8119bb1681643a3e0 Mon Sep 17 00:00:00 2001 From: 0xadk <0xadk@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:33:02 -0800 Subject: [PATCH 3/3] add vim keybindings --- README.md | 4 ++-- src/keybinds.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b9727fb..2c08c2a 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ There is one file for linux and one for windows (.exe). - run the game: `./binbreak-linux` ## Controls -- use the arrow keys for navigation +- use the arrow or vim keys for navigation - press Enter to confirm choices -- press Esc to exit a game mode or the game. CTRL+C also works to exit the game. +- press Esc or Q to exit a game mode or the game. CTRL+C also works to exit the game. ## Recommended terminals The game should run fine in any terminal. If you want retro CRT effects, here are some recommendations: diff --git a/src/keybinds.rs b/src/keybinds.rs index b4c0452..88a4599 100644 --- a/src/keybinds.rs +++ b/src/keybinds.rs @@ -1,19 +1,19 @@ use crossterm::event::{KeyCode, KeyEvent}; pub(crate) fn is_up(key: KeyEvent) -> bool { - matches!(key.code, KeyCode::Up) + matches!(key.code, KeyCode::Up | KeyCode::Char('k')) } pub(crate) fn is_down(key: KeyEvent) -> bool { - matches!(key.code, KeyCode::Down) + matches!(key.code, KeyCode::Down | KeyCode::Char('j')) } pub(crate) fn is_left(key: KeyEvent) -> bool { - matches!(key.code, KeyCode::Left) + matches!(key.code, KeyCode::Left | KeyCode::Char('h')) } pub(crate) fn is_right(key: KeyEvent) -> bool { - matches!(key.code, KeyCode::Right) + matches!(key.code, KeyCode::Right | KeyCode::Char('l')) } pub(crate) fn is_select(key: KeyEvent) -> bool { @@ -21,5 +21,5 @@ pub(crate) fn is_select(key: KeyEvent) -> bool { } pub(crate) fn is_exit(key: KeyEvent) -> bool { - matches!(key.code, KeyCode::Esc) + matches!(key.code, KeyCode::Esc | KeyCode::Char('q' | 'Q')) }