snake-rs/src/main.rs

196 lines
5.5 KiB
Rust
Raw Normal View History

2023-06-29 23:25:32 +00:00
use crossterm::{*, cursor::*, event::*, style::*, terminal::*};
use std::io::{stdout, Stdout, Write};
use std::time::Duration;
use rand::{Rng, thread_rng};
#[derive(Copy, Clone)]
struct Point {
x: u16,
y: u16
}
#[derive(Copy, Clone, std::fmt::Debug, PartialEq, Eq)]
enum Direction {
Up,
Down,
Left,
Right
}
fn init_screen() -> Result<Stdout> {
let mut stdout: Stdout = stdout();
enable_raw_mode()?;
stdout.queue(Clear(ClearType::All))?;
stdout.queue(Hide)?;
stdout.queue(MoveTo(0, 0))?;
stdout.flush()?;
Ok(stdout)
}
fn cleanup(stdout: &mut Stdout) -> Result<()> {
stdout.queue(Clear(ClearType::All))?;
stdout.queue(Show)?;
stdout.queue(MoveTo(0, 0))?;
disable_raw_mode()?;
stdout.flush()?;
Ok(())
}
fn move_snake(snake: &mut Vec<Point>, direction: Direction, cols: u16, rows: u16) {
for i in (0..(*snake).len()).rev() {
if i == 0 {
match direction {
Direction::Up => {
if snake[0].y == 0 {
snake[0].y = rows-1;
} else {
snake[0].y -= 1;
}
},
Direction::Down => {
if snake[0].y == rows-1 {
snake[0].y = 0;
} else {
snake[0].y += 1;
}
},
Direction::Left => {
if snake[0].x == 0 {
snake[0].x = cols-1;
} else {
snake[0].x -= 1;
}
},
Direction::Right => {
if snake[0].x == cols-1 {
snake[0].x = 0;
} else {
snake[0].x += 1;
}
}
}
} else {
snake[i] = snake[i-1];
}
}
}
fn print_snake(snake: &Vec<Point>, screen: &mut Stdout) -> Result<()> {
for seg in (*snake).iter() {
(*screen).queue(MoveTo(seg.x, seg.y))?;
(*screen).queue(Print("#".to_string()))?;
}
Ok(())
}
fn main() -> Result<()> {
let mut screen = init_screen()?;
let (cols, rows) = size()?;
let mut snake: Vec<Point> = Vec::new();
let mut apples: Vec<Point> = Vec::new();
let inital_length = 5;
let num_apples = 10;
let mut head_x = cols/2 - inital_length/2;
let mut head_y = rows/2;
let mut direction: Direction = Direction::Left;
let mut score = 0;
let mut lost = false;
screen.queue(MoveTo(head_x, head_y))?;
// create snake
for i in 0..inital_length {
snake.push(Point { x: head_x + i, y: head_y });
screen.queue(Print("#".to_string()))?;
}
// create apples
for i in 0..num_apples {
apples.push(Point { x: thread_rng().gen_range(0..cols), y: thread_rng().gen_range(0..rows-1)});
screen.queue(MoveTo(apples[i].x, apples[i].y))?;
screen.queue(Print("*".to_string()))?;
}
'main: loop {
screen.queue(MoveTo(head_x, head_y))?;
match direction {
Direction::Up => {move_snake(&mut snake, direction, cols, rows);},
Direction::Down => {move_snake(&mut snake, direction, cols, rows);},
Direction::Left => {move_snake(&mut snake, direction, cols, rows);},
Direction::Right => {move_snake(&mut snake, direction, cols, rows);}
}
head_x = snake[0].x;
head_y = snake[0].y;
for i in 1..snake.len() {
if head_x == snake[i].x && head_y == snake[i].y {
lost = true;
break 'main;
}
}
for i in 0..num_apples {
if head_x == apples[i].x && head_y == apples[i].y {
score += 1;
apples[i] = Point { x: thread_rng().gen_range(0..cols), y: thread_rng().gen_range(0..rows)};
snake.push(Point { x: 0, y: 0 });
}
screen.queue(MoveTo(apples[i].x, apples[i].y))?;
screen.queue(Print("*".to_string()))?;
}
print_snake(&snake, &mut screen)?;
screen.queue(MoveTo(0, rows))?;
screen.queue(Print(format!("SCORE: {} | <q> to quit | arrow keys to move", score)))?;
screen.flush()?;
screen.queue(Clear(ClearType::All))?;
if poll(Duration::from_millis(150))? {
match read()? {
Event::Key(e) => match e.code {
KeyCode::Char('q') => break 'main,
KeyCode::Up => direction =
if direction != Direction::Down { Direction::Up }
else { direction },
KeyCode::Down => direction =
if direction != Direction::Up { Direction::Down }
else { direction },
KeyCode::Left => direction =
if direction != Direction::Right { Direction::Left }
else { direction },
KeyCode::Right => direction =
if direction != Direction::Left { Direction::Right }
else { direction },
_ => ()
},
_ => ()
}
}
}
cleanup(&mut screen)?;
if lost {
println!("You loose. Final Score: {}", score);
} else {
println!("Goodbye! Final Score: {}", score);
}
Ok(())
}