inital
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "term_snake"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
crossterm = "0.27.0"
|
||||
rand = "0.8.5"
|
||||
233
src/main.rs
Normal file
233
src/main.rs
Normal file
@@ -0,0 +1,233 @@
|
||||
use crossterm::{
|
||||
event::{poll, read, Event, KeyCode},
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const WIDTH: usize = 20;
|
||||
const HEIGHT: usize = 20;
|
||||
const FRAME_TIME: Duration = Duration::from_millis(100);
|
||||
const SNAKE_COLOR: Color = Color { r: 0, g: 255, b: 0 };
|
||||
const APPLE_COLOR: Color = Color { r: 255, g: 0, b: 0 };
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
enum CrashLocation {
|
||||
Snake,
|
||||
North,
|
||||
South,
|
||||
East,
|
||||
West,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut snake: VecDeque<(usize, usize)> = vec![(0, 0), (1, 0), (2, 0)].into();
|
||||
let mut direction = Direction::Right;
|
||||
let mut length = snake.len();
|
||||
let mut rng = thread_rng();
|
||||
let mut apple = (rng.gen_range(0..WIDTH), rng.gen_range(0..WIDTH));
|
||||
|
||||
loop {
|
||||
let now = Instant::now();
|
||||
|
||||
render(&snake, apple, length);
|
||||
enable_raw_mode().unwrap();
|
||||
change_dir(&mut direction);
|
||||
disable_raw_mode().unwrap();
|
||||
if let Some(crash) = move_snake(&mut snake, &direction, length) {
|
||||
match crash {
|
||||
CrashLocation::North => println!("You crashed in the North wall!"),
|
||||
CrashLocation::South => println!("You crashed in the South wall!"),
|
||||
CrashLocation::East => println!("You crashed in the East wall!"),
|
||||
CrashLocation::West => println!("You crashed in the West wall!"),
|
||||
CrashLocation::Snake => println!("You crashed in yourself!"),
|
||||
}
|
||||
break;
|
||||
}
|
||||
eat(&snake, &mut apple, &mut length, &mut rng);
|
||||
|
||||
while now.elapsed() < FRAME_TIME {}
|
||||
}
|
||||
}
|
||||
|
||||
fn eat(
|
||||
snake: &VecDeque<(usize, usize)>,
|
||||
apple: &mut (usize, usize),
|
||||
length: &mut usize,
|
||||
rng: &mut ThreadRng,
|
||||
) {
|
||||
if snake.contains(&apple) {
|
||||
*apple = (rng.gen_range(0..WIDTH), rng.gen_range(0..WIDTH));
|
||||
*length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn change_dir(direction: &mut Direction) {
|
||||
if let Some(k) = key_input(FRAME_TIME) {
|
||||
match k {
|
||||
'd' => {
|
||||
if *direction != Direction::Left {
|
||||
*direction = Direction::Right;
|
||||
}
|
||||
}
|
||||
'a' => {
|
||||
if *direction != Direction::Right {
|
||||
*direction = Direction::Left;
|
||||
}
|
||||
}
|
||||
'w' => {
|
||||
if *direction != Direction::Down {
|
||||
*direction = Direction::Up;
|
||||
}
|
||||
}
|
||||
's' => {
|
||||
if *direction != Direction::Up {
|
||||
*direction = Direction::Down;
|
||||
}
|
||||
}
|
||||
'q' => {
|
||||
disable_raw_mode().unwrap();
|
||||
std::process::exit(0);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_input(timeout: Duration) -> Option<char> {
|
||||
if poll(timeout).ok()? {
|
||||
if let Event::Key(k) = read().ok()? {
|
||||
if let KeyCode::Char(c) = k.code {
|
||||
return Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn move_snake(
|
||||
snake: &mut VecDeque<(usize, usize)>,
|
||||
direction: &Direction,
|
||||
length: usize,
|
||||
) -> Option<CrashLocation> {
|
||||
if length == snake.len() {
|
||||
snake.pop_front();
|
||||
}
|
||||
let mut a = *snake.iter().last().unwrap();
|
||||
|
||||
match direction {
|
||||
Direction::Right => {
|
||||
if a.0 + 1 < WIDTH {
|
||||
a.0 += 1
|
||||
} else {
|
||||
return Some(CrashLocation::East);
|
||||
}
|
||||
}
|
||||
Direction::Left => {
|
||||
if a.0 > 0 {
|
||||
a.0 -= 1
|
||||
} else {
|
||||
return Some(CrashLocation::West);
|
||||
}
|
||||
}
|
||||
Direction::Up => {
|
||||
if a.1 + 1 < WIDTH {
|
||||
a.1 += 1
|
||||
} else {
|
||||
return Some(CrashLocation::North);
|
||||
}
|
||||
}
|
||||
Direction::Down => {
|
||||
if a.1 > 0 {
|
||||
a.1 -= 1
|
||||
} else {
|
||||
return Some(CrashLocation::South);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if snake.contains(&a) {
|
||||
return Some(CrashLocation::Snake);
|
||||
}
|
||||
|
||||
snake.push_back(a);
|
||||
None
|
||||
}
|
||||
|
||||
fn render(snake: &VecDeque<(usize, usize)>, apple: (usize, usize), length: usize) {
|
||||
let mut s = String::new();
|
||||
s.push_str("\u{001b}c");
|
||||
s.push_str(&format!("length: {length}\n"));
|
||||
s.push('┏');
|
||||
s.push_str(&"━".repeat(WIDTH * 2));
|
||||
s.push_str("┓\n");
|
||||
for y in (0..HEIGHT).rev() {
|
||||
s.push('┃');
|
||||
for x in 0..WIDTH {
|
||||
if snake.contains(&(x, y)) {
|
||||
s.push_str(&" ".bg(SNAKE_COLOR));
|
||||
} else if (x, y) == apple {
|
||||
s.push_str(&" ".bg(APPLE_COLOR));
|
||||
} else {
|
||||
s.push_str(" ");
|
||||
}
|
||||
}
|
||||
s.push_str("┃\n");
|
||||
}
|
||||
s.push('┗');
|
||||
s.push_str(&"━".repeat(WIDTH * 2));
|
||||
s.push('┛');
|
||||
println!("{s}");
|
||||
}
|
||||
|
||||
struct Color {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
}
|
||||
|
||||
trait Colorize {
|
||||
fn bg(&self, color: Color) -> String;
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Display> Colorize for T {
|
||||
fn bg(&self, color: Color) -> String {
|
||||
format!(
|
||||
"\x1b[48;2;{};{};{}m{self}\x1b[0m",
|
||||
color.r, color.g, color.b
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn move_test() {
|
||||
let mut snake: VecDeque<(usize, usize)> = vec![(16, 16), (17, 16), (18, 16)].into();
|
||||
let mut direction = Direction::Up;
|
||||
let length = snake.len();
|
||||
move_snake(&mut snake, &direction, length);
|
||||
assert_eq!(snake, vec![(17, 16), (18, 16), (18, 17)]);
|
||||
|
||||
direction = Direction::Right;
|
||||
move_snake(&mut snake, &direction, length);
|
||||
assert_eq!(snake, vec![(18, 16), (18, 17), (19, 17)]);
|
||||
|
||||
direction = Direction::Down;
|
||||
move_snake(&mut snake, &direction, length);
|
||||
assert_eq!(snake, vec![(18, 17), (19, 17), (19, 16)]);
|
||||
|
||||
direction = Direction::Left;
|
||||
move_snake(&mut snake, &direction, length);
|
||||
assert_eq!(snake, vec![(19, 17), (19, 16), (18, 16)]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user