mod rand; mod sc; use rand::Gen; use sc::{Key, Screen}; use std::error::Error; use std::io::{stderr, stdout, Write}; use std::time::{Duration, SystemTime}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Pos { row: u16, col: u16, } #[derive(Debug)] struct Snake { body: Vec, direction: Key, growing: i64, } impl Snake { fn next(&self) -> Pos { let mut pos = self.body[0]; match self.direction { Key::Up => pos.row -= 1, Key::Down => pos.row += 1, Key::Left => pos.col -= 1, Key::Right => pos.col += 1, } pos } fn erase(&self, screen: &Screen) -> Result<(), Box> { let pos = self.next(); if screen.in_bounds(pos.row, pos.col) { sc::move_cursor(pos.row, pos.col)?; write!(stdout(), " ")?; } for &pos in self.body.iter() { sc::move_cursor(pos.row, pos.col)?; write!(stdout(), " ")?; } Ok(()) } fn print(&self, g: &mut Gen, screen: &Screen) -> Result<(), Box> { let next = self.next(); if (self.direction == Key::Left || self.direction == Key::Right) && screen.in_bounds(next.row, next.col) && g.rand(40) == 0 { sc::move_cursor(next.row, next.col)?; write!(stdout(), "~")?; } for (i, &pos) in self.body.iter().enumerate() { sc::move_cursor(pos.row, pos.col)?; if i == 0 { write!(stdout(), "O")?; continue; } let prev = self.body[i - 1]; if i == self.body.len() - 1 { if prev.row < pos.row { write!(stdout(), "v")?; continue; } if prev.row > pos.row { write!(stdout(), "^")?; continue; } if prev.col < pos.col { write!(stdout(), ">")?; continue; } write!(stdout(), "<")?; continue; } let next = self.body[i + 1]; if prev.col == next.col { write!(stdout(), "|")?; continue; } if prev.row == next.row { write!(stdout(), "=")?; continue; } let (a, b) = if prev.row == pos.row { (prev, next) } else { (next, prev) }; if (a.col < pos.col) == (b.row < pos.row) { write!(stdout(), "/")?; continue; } write!(stdout(), "\\")?; } Ok(()) } fn step(&mut self) { let pos = self.next(); if self.growing > 0 { self.body.push(self.body[self.body.len() - 1]); self.growing -= 1; } for i in (1..self.body.len()).rev() { self.body[i] = self.body[i - 1]; } self.body[0] = pos; } } struct Fruit { pos: Pos, } impl Fruit { fn print(&self) -> Result<(), Box> { sc::move_cursor(self.pos.row, self.pos.col)?; write!(stdout(), "@")?; Ok(()) } fn new_pos(&mut self, g: &mut Gen, screen: &Screen) { self.pos.row = g.rand(i32::from(screen.rows) - 4) as u16 + 2; self.pos.col = g.rand(i32::from(screen.cols) - 4) as u16 + 2; } } fn snake() -> Result> { let screen = Screen::init()?; let mut g = rand::new( SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_nanos() as i64, ); let mut snake = Snake { body: vec![Pos { row: screen.rows / 2, col: screen.cols / 2, }], direction: Key::Right, growing: 0, }; let mut fruit = Fruit { pos: Pos { row: 0, col: 0 }, }; fruit.new_pos(&mut g, &screen); let mut score: i64 = 0; let mut real_score: i64 = 0; let mut delay = Duration::from_millis(200); loop { sc::move_cursor(0, 3)?; write!(stdout(), " Score: {} ", score)?; snake.print(&mut g, &screen)?; fruit.print()?; sc::move_cursor(screen.rows - 1, screen.cols - 1)?; stdout().flush()?; let mut d = delay; if snake.direction == Key::Up || snake.direction == Key::Down { d = d * 5 / 4; } std::thread::sleep(d); snake.erase(&screen)?; if let Some(key) = screen.read_key() { if !(snake.direction == Key::Up && key == Key::Down || snake.direction == Key::Down && key == Key::Up || snake.direction == Key::Left && key == Key::Right || snake.direction == Key::Right && key == Key::Left) || snake.body.len() == 1 { snake.direction = key; } }; snake.step(); if snake.body[0] == fruit.pos { snake.growing += 6; loop { fruit.new_pos(&mut g, &screen); if !snake.body.contains(&fruit.pos) { break; } } let h = real_score / 2; score += h * h * h * h * h - h + 1; real_score += 1; delay = delay * 14 / 15; } if !screen.in_bounds(snake.body[0].row, snake.body[0].col) || snake.body[1..].contains(&snake.body[0]) { return Ok(score); } } } fn main() { let score = match snake() { Ok(s) => s, Err(err) => { let _ = writeln!(stderr(), "FAIL: {}", err); std::process::exit(1); } }; let _ = sc::clear(); let _ = sc::move_cursor(0, 0); let _ = writeln!(stdout(), "Score: {}", score); }