initial commit
This commit is contained in:
parent
8288f3f628
commit
6198043d76
4 changed files with 213 additions and 1 deletions
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "snake-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
crossterm = "0.26.1"
|
||||||
|
rand = "0.8.5"
|
|
@ -1,3 +1,10 @@
|
||||||
# snake-rs
|
# snake-rs
|
||||||
|
|
||||||
Classic snake game with terminal graphics written in rust.
|
Classic snake game with terminal graphics written in rust.
|
||||||
|
|
||||||
|
This is my first project in rust so the code is probably great.
|
||||||
|
|
||||||
|
Play the game:
|
||||||
|
```
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
195
src/main.rs
Normal file
195
src/main.rs
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
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(())
|
||||||
|
|
||||||
|
}
|
0
src/snake.rs
Normal file
0
src/snake.rs
Normal file
Loading…
Reference in a new issue