196 lines
5.5 KiB
Rust
196 lines
5.5 KiB
Rust
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(())
|
|
|
|
}
|