From 0f7708b66aa43d40034ffca0ece390f0661e400c Mon Sep 17 00:00:00 2001 From: Ryujin42 Date: Fri, 10 Apr 2026 15:25:05 +0200 Subject: [PATCH] fix: fixed timing on loop --- src/main.rs | 162 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 96 insertions(+), 66 deletions(-) diff --git a/src/main.rs b/src/main.rs index 22816a0..0fe1a44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,12 +6,16 @@ use crossterm::{ ExecutableCommand, }; use std::{ - cmp::min, collections::VecDeque, io::{Write, stdout} + cmp::min, + collections::VecDeque, + io::{Write, stdout}, + sync::mpsc, + thread, + time::{Duration, Instant}, }; -use std::time::Duration; use rand::random_range; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Copy, Clone)] enum Direction { Up, Down, @@ -19,14 +23,26 @@ enum Direction { Right, } -fn read_inputs() -> Option { - if event::poll(Duration::from_millis(300)).unwrap() { - if let Event::Key(KeyEvent { code, .. }) = event::read().unwrap() { - return Some(code); +fn spawn_input_handler(tx: mpsc::Sender) { + thread::spawn(move || { + loop { + if event::poll(Duration::from_millis(50)).unwrap() { + if let Event::Key(KeyEvent { code, .. }) = event::read().unwrap() { + if tx.send(code).is_err() { + break; + } + } + } + } + }); +} + +fn wait_for_restart(rx: &mpsc::Receiver) { + loop { + if let Ok(KeyCode::Char('r')) = rx.recv() { + break; } } - - None } fn draw_gui(max_x: u16, max_y: u16) { @@ -37,93 +53,114 @@ fn draw_gui(max_x: u16, max_y: u16) { stdout.execute(SetForegroundColor(Color::White)).unwrap(); println!("┌{}┐", "──".repeat(max_x as usize + 2)); - for i in 2..max_y+3 { + for i in 2..max_y + 3 { stdout.execute(cursor::MoveTo(0, i)).unwrap(); - println!("│{}│", " ".repeat((max_x as usize+2) * 2)) + println!("│{}│", " ".repeat((max_x as usize + 2) * 2)); } - stdout.execute(cursor::MoveTo(0, max_y+3)).unwrap(); + stdout.execute(cursor::MoveTo(0, max_y + 3)).unwrap(); println!("└{}┘", "──".repeat(max_x as usize + 2)); stdout.execute(ResetColor).unwrap(); stdout.flush().unwrap(); } -fn win() { +fn win(rx: &mpsc::Receiver) { let mut stdout = stdout(); stdout.execute(cursor::MoveTo(0, 0)).unwrap(); println!("WIN"); - - loop { - match read_inputs() { - Some(KeyCode::Char('r')) => break, - _ => {}, - } - } + wait_for_restart(rx); } -fn game_over() { - loop { - match read_inputs() { - Some(KeyCode::Char('r')) => break, - _ => {}, - } - } +fn game_over(rx: &mpsc::Receiver) { + let mut stdout = stdout(); + stdout.execute(cursor::MoveTo(0, 0)).unwrap(); + println!("LOSE"); + wait_for_restart(rx); } fn main() { - const MAX_X : u16 = 7; + const MAX_X: u16 = 7; const MAX_Y: u16 = 7; + const TICK_RATE: Duration = Duration::from_millis(300); let mut head_x: u16 = 4; let mut head_y: u16 = 4; let mut body_length: usize = 0; - let mut body: VecDeque<(u16,u16)> = Default::default(); + let mut body: VecDeque<(u16, u16)> = Default::default(); let mut direction: Direction = Direction::Up; + let mut next_direction: Direction = Direction::Up; - let mut apple_x: u16 = random_range(0..MAX_X+1); - let mut apple_y: u16 = random_range(0..MAX_Y+1); + let mut apple_x: u16 = random_range(0..MAX_X + 1); + let mut apple_y: u16 = random_range(0..MAX_Y + 1); let mut stdout = stdout(); terminal::enable_raw_mode().unwrap(); stdout.execute(cursor::Hide).unwrap(); + let (tx, rx) = mpsc::channel(); + spawn_input_handler(tx); + + let mut last_tick = Instant::now(); + loop { - match read_inputs() { - Some(KeyCode::Up) => direction = if direction != Direction::Down {Direction::Up} else {direction}, - Some(KeyCode::Down) => direction = if direction != Direction::Up {Direction::Down} else {direction}, - Some(KeyCode::Left) => direction = if direction != Direction::Right {Direction::Left} else {direction}, - Some(KeyCode::Right) => direction = if direction != Direction::Left {Direction::Right} else {direction}, - Some(KeyCode::Char('q')) => break, - _ => {}, + if let Ok(code) = rx.try_recv() { + match code { + KeyCode::Up => next_direction = Direction::Up, + KeyCode::Down => next_direction = Direction::Down, + KeyCode::Left => next_direction = Direction::Left, + KeyCode::Right => next_direction = Direction::Right, + KeyCode::Char('q') => { + stdout.execute(Clear(ClearType::All)).unwrap(); + stdout.execute(cursor::MoveTo(0, 0)).unwrap(); + stdout.execute(cursor::Show).unwrap(); + terminal::disable_raw_mode().unwrap(); + return; + } + _ => {} + } + while rx.try_recv().is_ok() {} } + if last_tick.elapsed() < TICK_RATE { + thread::sleep(Duration::from_millis(1)); + continue; + } + last_tick = Instant::now(); + // MOVE + direction = match (&next_direction, &direction) { + (Direction::Up, Direction::Down) => Direction::Down, + (Direction::Down, Direction::Up) => Direction::Up, + (Direction::Left, Direction::Right) => Direction::Right, + (Direction::Right, Direction::Left) => Direction::Left, + _ => next_direction, // valid move, accept it + }; + + next_direction = direction; if body.len() > body_length { body.pop_back(); } body.push_front((head_x, head_y)); - + match direction { - Direction::Up => head_y = head_y.saturating_sub(1), - Direction::Down => head_y = min(head_y+1, MAX_Y), - Direction::Left => head_x = head_x.saturating_sub(1), - Direction::Right => head_x = min(head_x+1, MAX_X), + Direction::Up => head_y = head_y.saturating_sub(1), + Direction::Down => head_y = min(head_y + 1, MAX_Y), + Direction::Left => head_x = head_x.saturating_sub(1), + Direction::Right => head_x = min(head_x + 1, MAX_X), } - // CHECKS - if body_length == MAX_X as usize * MAX_Y as usize { win(); } + if body_length == MAX_X as usize * MAX_Y as usize { + win(&rx); + } if (head_x, head_y) == (apple_x, apple_y) { body_length += 1; - loop { - apple_x = random_range(0..MAX_X+1); - apple_y = random_range(0..MAX_Y+1); - - if !body.contains(&(apple_x, apple_y)) && - (apple_x, apple_y) != (head_x, head_y) { - break; + apple_x = random_range(0..MAX_X + 1); + apple_y = random_range(0..MAX_Y + 1); + if !body.contains(&(apple_x, apple_y)) && (apple_x, apple_y) != (head_x, head_y) { + break; } } } @@ -132,32 +169,25 @@ fn main() { stdout.execute(Clear(ClearType::All)).unwrap(); draw_gui(MAX_X, MAX_Y); - // Apple - stdout.execute(cursor::MoveTo((apple_x+1)*2, apple_y+2)).unwrap(); + stdout.execute(cursor::MoveTo((apple_x + 1) * 2, apple_y + 2)).unwrap(); stdout.execute(SetForegroundColor(Color::Red)).unwrap(); print!("⯀"); - // Snake - stdout.execute(cursor::MoveTo((head_x+1)*2, head_y+2)).unwrap(); + stdout.execute(cursor::MoveTo((head_x + 1) * 2, head_y + 2)).unwrap(); stdout.execute(SetForegroundColor(Color::Green)).unwrap(); match direction { - Direction::Up => print!("⯅"), - Direction::Down => print!("⯆"), - Direction::Left => print!("⯇"), + Direction::Up => print!("⯅"), + Direction::Down => print!("⯆"), + Direction::Left => print!("⯇"), Direction::Right => print!("⯈"), } for (x, y) in &body { - stdout.execute(cursor::MoveTo((x+1)*2, y+2)).unwrap(); - print!("⯀") + stdout.execute(cursor::MoveTo((x + 1) * 2, y + 2)).unwrap(); + print!("⯀"); } stdout.execute(ResetColor).unwrap(); stdout.flush().unwrap(); } - - stdout.execute(Clear(ClearType::All)).unwrap(); - stdout.execute(cursor::MoveTo(0, 0)).unwrap(); - stdout.execute(cursor::Show).unwrap(); - terminal::disable_raw_mode().unwrap(); }