fix: fixed timing on loop

This commit is contained in:
2026-04-10 15:25:05 +02:00
parent d5b4c35635
commit 0f7708b66a

View File

@@ -6,12 +6,16 @@ use crossterm::{
ExecutableCommand, ExecutableCommand,
}; };
use std::{ 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; use rand::random_range;
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Copy, Clone)]
enum Direction { enum Direction {
Up, Up,
Down, Down,
@@ -19,14 +23,26 @@ enum Direction {
Right, Right,
} }
fn read_inputs() -> Option<KeyCode> { fn spawn_input_handler(tx: mpsc::Sender<KeyCode>) {
if event::poll(Duration::from_millis(300)).unwrap() { thread::spawn(move || {
loop {
if event::poll(Duration::from_millis(50)).unwrap() {
if let Event::Key(KeyEvent { code, .. }) = event::read().unwrap() { if let Event::Key(KeyEvent { code, .. }) = event::read().unwrap() {
return Some(code); if tx.send(code).is_err() {
break;
} }
} }
}
}
});
}
None fn wait_for_restart(rx: &mpsc::Receiver<KeyCode>) {
loop {
if let Ok(KeyCode::Char('r')) = rx.recv() {
break;
}
}
} }
fn draw_gui(max_x: u16, max_y: u16) { fn draw_gui(max_x: u16, max_y: u16) {
@@ -39,7 +55,7 @@ fn draw_gui(max_x: u16, max_y: u16) {
for i in 2..max_y + 3 { for i in 2..max_y + 3 {
stdout.execute(cursor::MoveTo(0, i)).unwrap(); 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();
@@ -49,37 +65,31 @@ fn draw_gui(max_x: u16, max_y: u16) {
stdout.flush().unwrap(); stdout.flush().unwrap();
} }
fn win() { fn win(rx: &mpsc::Receiver<KeyCode>) {
let mut stdout = stdout(); let mut stdout = stdout();
stdout.execute(cursor::MoveTo(0, 0)).unwrap(); stdout.execute(cursor::MoveTo(0, 0)).unwrap();
println!("WIN"); println!("WIN");
wait_for_restart(rx);
loop {
match read_inputs() {
Some(KeyCode::Char('r')) => break,
_ => {},
}
}
} }
fn game_over() { fn game_over(rx: &mpsc::Receiver<KeyCode>) {
loop { let mut stdout = stdout();
match read_inputs() { stdout.execute(cursor::MoveTo(0, 0)).unwrap();
Some(KeyCode::Char('r')) => break, println!("LOSE");
_ => {}, wait_for_restart(rx);
}
}
} }
fn main() { fn main() {
const MAX_X: u16 = 7; const MAX_X: u16 = 7;
const MAX_Y: u16 = 7; const MAX_Y: u16 = 7;
const TICK_RATE: Duration = Duration::from_millis(300);
let mut head_x: u16 = 4; let mut head_x: u16 = 4;
let mut head_y: u16 = 4; let mut head_y: u16 = 4;
let mut body_length: usize = 0; 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 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_x: u16 = random_range(0..MAX_X + 1);
let mut apple_y: u16 = random_range(0..MAX_Y + 1); let mut apple_y: u16 = random_range(0..MAX_Y + 1);
@@ -88,17 +98,46 @@ fn main() {
terminal::enable_raw_mode().unwrap(); terminal::enable_raw_mode().unwrap();
stdout.execute(cursor::Hide).unwrap(); stdout.execute(cursor::Hide).unwrap();
let (tx, rx) = mpsc::channel();
spawn_input_handler(tx);
let mut last_tick = Instant::now();
loop { loop {
match read_inputs() { if let Ok(code) = rx.try_recv() {
Some(KeyCode::Up) => direction = if direction != Direction::Down {Direction::Up} else {direction}, match code {
Some(KeyCode::Down) => direction = if direction != Direction::Up {Direction::Down} else {direction}, KeyCode::Up => next_direction = Direction::Up,
Some(KeyCode::Left) => direction = if direction != Direction::Right {Direction::Left} else {direction}, KeyCode::Down => next_direction = Direction::Down,
Some(KeyCode::Right) => direction = if direction != Direction::Left {Direction::Right} else {direction}, KeyCode::Left => next_direction = Direction::Left,
Some(KeyCode::Char('q')) => break, 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 // 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 { if body.len() > body_length {
body.pop_back(); body.pop_back();
} }
@@ -111,18 +150,16 @@ fn main() {
Direction::Right => head_x = min(head_x + 1, MAX_X), Direction::Right => head_x = min(head_x + 1, MAX_X),
} }
// CHECKS // 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) { if (head_x, head_y) == (apple_x, apple_y) {
body_length += 1; body_length += 1;
loop { loop {
apple_x = random_range(0..MAX_X + 1); apple_x = random_range(0..MAX_X + 1);
apple_y = random_range(0..MAX_Y + 1); apple_y = random_range(0..MAX_Y + 1);
if !body.contains(&(apple_x, apple_y)) && (apple_x, apple_y) != (head_x, head_y) {
if !body.contains(&(apple_x, apple_y)) &&
(apple_x, apple_y) != (head_x, head_y) {
break; break;
} }
} }
@@ -132,12 +169,10 @@ fn main() {
stdout.execute(Clear(ClearType::All)).unwrap(); stdout.execute(Clear(ClearType::All)).unwrap();
draw_gui(MAX_X, MAX_Y); 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(); stdout.execute(SetForegroundColor(Color::Red)).unwrap();
print!(""); 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(); stdout.execute(SetForegroundColor(Color::Green)).unwrap();
match direction { match direction {
@@ -149,15 +184,10 @@ fn main() {
for (x, y) in &body { for (x, y) in &body {
stdout.execute(cursor::MoveTo((x + 1) * 2, y + 2)).unwrap(); stdout.execute(cursor::MoveTo((x + 1) * 2, y + 2)).unwrap();
print!("") print!("");
} }
stdout.execute(ResetColor).unwrap(); stdout.execute(ResetColor).unwrap();
stdout.flush().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();
} }