Fixed broken tests, and corrected public export

This commit is contained in:
Timon Post 2019-04-04 18:45:47 +02:00
parent 805ec959c1
commit 6cdce1bc25
12 changed files with 133 additions and 148 deletions

View File

@ -29,6 +29,8 @@ members = [
"crossterm_terminal", "crossterm_terminal",
"crossterm_input", "crossterm_input",
"crossterm_screen", "crossterm_screen",
"examples/program_examples/snake",
"examples/program_examples/first_depth_search"
] ]
[dependencies] [dependencies]
@ -50,3 +52,7 @@ path = "examples/program_examples/logging.rs"
[[example]] [[example]]
name = "command_bar" name = "command_bar"
path = "examples/program_examples/command_bar.rs" path = "examples/program_examples/command_bar.rs"
[[example]]
name = "snake"
path = "examples/program_examples/snake"

View File

@ -1,6 +1,6 @@
extern crate crossterm; extern crate crossterm;
use crossterm::{cursor, input, ClearType, Crossterm, Screen, Terminal, TerminalCursor}; use crossterm::{input, ClearType, Crossterm, Screen, Terminal, TerminalCursor, InputEvent, KeyEvent};
use std::io::Read; use std::io::Read;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -22,22 +22,20 @@ fn main() {
thread::spawn(move || { thread::spawn(move || {
let input = input(); let input = input();
let mut stdin = input.read_async().bytes(); let mut stdin = input.read_async();
loop { loop {
let a = stdin.next(); match stdin.next() {
Some(InputEvent::Keyboard(KeyEvent::Char('\n'))) => {
match a {
Some(Ok(13)) => {
input_buf.lock().unwrap().clear(); input_buf.lock().unwrap().clear();
} }
Some(Ok(val)) => { Some(InputEvent::Keyboard(KeyEvent::Char(character))) => {
input_buf.lock().unwrap().push(val as char); input_buf.lock().unwrap().push(character as char);
} }
_ => {} _ => {}
} }
thread::sleep(time::Duration::from_millis(100)); thread::sleep(time::Duration::from_millis(10));
count += 1; count += 1;
} }
}) })
@ -73,7 +71,7 @@ fn log(input_buf: Arc<Mutex<String>>, screen: &Screen) -> Vec<thread::JoinHandle
&cursor, &cursor,
term_height, term_height,
); );
thread::sleep(time::Duration::from_millis(300)); thread::sleep(time::Duration::from_millis(100));
} }
}); });

View File

@ -1,6 +1,6 @@
//! Implementation of the first depth search algorithm //! Implementation of the first depth search algorithm
use super::map::Map; use super::map::Map;
use super::messages::END_MESSAGE;
use super::variables::{Direction, Position}; use super::variables::{Direction, Position};
use crossterm::{Color, Crossterm, Screen}; use crossterm::{Color, Crossterm, Screen};
@ -8,7 +8,7 @@ use crossterm::{Color, Crossterm, Screen};
use super::rand; use super::rand;
use super::rand::distributions::{IndependentSample, Range}; use super::rand::distributions::{IndependentSample, Range};
use std::io::{stdout, Write}; use std::io::Write;
use std::{thread, time}; use std::{thread, time};
pub struct FirstDepthSearch<'screen> { pub struct FirstDepthSearch<'screen> {
@ -28,7 +28,7 @@ impl<'screen> FirstDepthSearch<'screen> {
) -> FirstDepthSearch<'screen> { ) -> FirstDepthSearch<'screen> {
FirstDepthSearch { FirstDepthSearch {
direction: Direction::Up, direction: Direction::Up,
map: map, map,
stack: Vec::new(), stack: Vec::new(),
root_pos: start_pos, root_pos: start_pos,
is_terminated: false, is_terminated: false,

View File

@ -6,7 +6,7 @@ mod map;
mod messages; mod messages;
mod variables; mod variables;
use self::crossterm::{terminal, ClearType, Color, Crossterm, Screen}; use self::crossterm::{Color, Crossterm, Screen, ClearType, InputEvent, KeyEvent};
use self::messages::WELCOME_MESSAGE; use self::messages::WELCOME_MESSAGE;
use self::variables::{Position, Size}; use self::variables::{Position, Size};
@ -62,7 +62,11 @@ fn print_welcome_screen() {
// clear the screen and print the welcome message. // clear the screen and print the welcome message.
terminal.clear(ClearType::All); terminal.clear(ClearType::All);
cursor.goto(0, 0); cursor.goto(0, 0);
terminal.write(WELCOME_MESSAGE.join("\r\n"));
crossterm
.style(format!("{}", messages::WELCOME_MESSAGE.join("\n\r")))
.with(Color::Cyan)
.paint(&screen.stdout);
cursor.hide(); cursor.hide();
cursor.goto(0, 10); cursor.goto(0, 10);
@ -71,13 +75,11 @@ fn print_welcome_screen() {
cursor.goto(0, 11); cursor.goto(0, 11);
terminal.write("Press `q` to abort the program"); terminal.write("Press `q` to abort the program");
let mut stdin = input.read_async().bytes(); let mut stdin = input.read_async();
// print some progress example. // print some progress example.
for i in (1..5).rev() { for i in (1..5).rev() {
let a = stdin.next(); if let Some(InputEvent::Keyboard(KeyEvent::Char('q'))) = stdin.next() {
if let Some(Ok(b'q')) = a {
drop(screen); drop(screen);
terminal.exit(); terminal.exit();
break; break;
@ -95,3 +97,4 @@ fn print_welcome_screen() {
thread::sleep(time::Duration::from_secs(1)); thread::sleep(time::Duration::from_secs(1));
} }
} }

View File

@ -1,7 +1,5 @@
use super::variables::{Cell, Position, Size}; use super::variables::{Cell, Position, Size};
use crossterm::{cursor, Color, Crossterm, ObjectStyle, Screen, StyledObject}; use crossterm::{cursor, Color, Crossterm, Screen};
use std::fmt::Display;
pub struct Map { pub struct Map {
pub map: Vec<Vec<Cell>>, pub map: Vec<Vec<Cell>>,
@ -38,7 +36,7 @@ impl Map {
} }
Map { Map {
map: map, map,
size: Size::new(map_size.width, map_size.height), size: Size::new(map_size.width, map_size.height),
} }
} }

View File

@ -1,11 +1,7 @@
extern crate crossterm; extern crate crossterm;
use self::crossterm::{terminal, ClearType};
use self::crossterm::{Color, ObjectStyle, StyledObject}; use self::crossterm::{Color, ObjectStyle, StyledObject};
use std::fmt;
use std::fmt::Debug;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Direction { pub enum Direction {
Up = 0, Up = 0,

View File

@ -2,10 +2,7 @@ extern crate crossterm;
use crossterm::Screen; use crossterm::Screen;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::Write; use std::sync::{Arc, Mutex, mpsc::{self, Receiver, Sender}};
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::Arc;
use std::sync::Mutex;
use std::thread::{self, JoinHandle}; use std::thread::{self, JoinHandle};
/// This is an que that could be shared between threads safely. /// This is an que that could be shared between threads safely.
@ -98,11 +95,6 @@ fn main() {
// a thread that will log all logs in the queue. // a thread that will log all logs in the queue.
handle_incoming_logs(more_jobs_rx.clone(), queue.clone()); handle_incoming_logs(more_jobs_rx.clone(), queue.clone());
// for handle in thread_handles
// {
// handle.join();
// }
} }
fn handle_incoming_logs(more_jobs_rx: SyncFlagRx, queue: WorkQueue<String>) { fn handle_incoming_logs(more_jobs_rx: SyncFlagRx, queue: WorkQueue<String>) {

View File

@ -6,122 +6,133 @@ mod messages;
mod snake; mod snake;
mod variables; mod variables;
use self::crossterm::{input, terminal, ClearType, Color, Crossterm, Screen}; use self::crossterm::{
AsyncReader, ClearType, Color, Colorize, Crossterm, InputEvent, KeyEvent, Screen,
};
use map::Map; use map::Map;
use snake::Snake; use snake::Snake;
use variables::{Direction, Position, Size}; use variables::{Direction, Position, Size};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Read;
use std::io::Write; use std::io::Write;
use std::iter::Iterator; use std::iter::Iterator;
use std::{thread, time}; use std::{thread, time};
fn main() { fn main() {
let map_size = title_screen(); let map_size = ask_size();
{ // screen has to be in raw mode in order for the key presses not to be printed to the screen.
let mut screen = Screen::new(true); let screen = Screen::new(true);
let crossterm = Crossterm::from_screen(&screen); let crossterm = Crossterm::from_screen(&screen);
let cursor = crossterm.cursor();
let mut input = crossterm.input();
cursor.hide(); crossterm.cursor().hide();
let mut stdin = input.read_async().bytes(); // initialize free positions for the game map.
let mut free_positions: HashMap<String, Position> =
HashMap::with_capacity((map_size.width * map_size.height) as usize);
let mut free_positions: HashMap<String, Position> = // render the map
HashMap::with_capacity((map_size.width * map_size.height) as usize); let mut map = Map::new(map_size);
map.render_map(&screen, &mut free_positions);
let mut map = Map::new(map_size.clone()); let mut snake = Snake::new();
map.render_map(&screen, &mut free_positions);
let mut direction = Direction::Right; // remove snake coords from free positions.
for part in snake.get_parts().iter() {
let mut snake = Snake::new(map_size.clone()); free_positions.remove_entry(format!("{},{}", part.position.x, part.position.y).as_str());
for part in snake.get_parts().iter() {
free_positions
.remove_entry(format!("{},{}", part.position.x, part.position.y).as_str());
}
map.spawn_food(&free_positions, &screen);
loop {
thread::sleep(time::Duration::from_millis(200));
let pressed_key = stdin.next();
if let Some(Ok(key)) = pressed_key {
match key as char {
'w' => direction = Direction::Up,
'a' => direction = Direction::Left,
's' => direction = Direction::Down,
'd' => direction = Direction::Right,
_ => {}
}
}
snake.move_snake(&direction, &screen, &mut free_positions);
if map.is_out_of_bounds(snake.snake_parts[0].position) {
break;
}
snake.draw_snake(&screen);
if snake.has_eaten_food(map.foot_pos) {
map.spawn_food(&free_positions, &screen);
}
}
drop(screen);
} }
map.spawn_food(&free_positions, &screen);
let mut stdin = crossterm.input().read_async();
let mut snake_direction = Direction::Right;
// start the game loop; draw, move snake and spawn food.
loop {
if let Some(new_direction) = update_direction(&mut stdin) {
snake_direction = new_direction;
}
snake.move_snake(&snake_direction, &screen, &mut free_positions);
if map.is_out_of_bounds(snake.snake_parts[0].position) {
break;
}
snake.draw_snake(&screen);
if snake.has_eaten_food(map.foot_pos) {
map.spawn_food(&free_positions, &screen);
}
thread::sleep(time::Duration::from_millis(400));
}
game_over_screen(); game_over_screen();
} }
fn title_screen() -> Size { fn update_direction(reader: &mut AsyncReader) -> Option<Direction> {
let pressed_key = reader.next();
if let Some(InputEvent::Keyboard(KeyEvent::Char(character))) = pressed_key {
return Some(match character {
'w' => Direction::Up,
'a' => Direction::Left,
's' => Direction::Down,
'd' => Direction::Right,
_ => return None,
});
} else if let Some(InputEvent::Keyboard(key)) = pressed_key {
return Some(match key {
KeyEvent::Up => Direction::Up,
KeyEvent::Left => Direction::Left,
KeyEvent::Down => Direction::Down,
KeyEvent::Right => Direction::Right,
_ => return None,
});
}
None
}
fn ask_size() -> Size {
let crossterm = Crossterm::new(); let crossterm = Crossterm::new();
crossterm.terminal().clear(ClearType::All);
let cursor = crossterm.cursor(); let cursor = crossterm.cursor();
let terminal = crossterm.terminal().clear(ClearType::All);
println!("{}", messages::SNAKERS.join("\n\r")); println!(
"{}",
crossterm
.style(format!("{}", messages::END_MESSAGE.join("\n\r")))
.with(Color::Cyan)
);
// read width
cursor.goto(0, 15); cursor.goto(0, 15);
println!("Enter map width:"); println!("{}", "Enter map width:".green().on_yellow());
cursor.goto(17, 15); cursor.goto(17, 15);
// read height
let width = crossterm.input().read_line().unwrap(); let width = crossterm.input().read_line().unwrap();
println!("\r\nEnter map height:"); println!("{}", "\r\nEnter map height:".green().on_yellow());
cursor.goto(17, 17); cursor.goto(17, 17);
let height = crossterm.input().read_line().unwrap(); let height = crossterm.input().read_line().unwrap();
// parse input
let parsed_width = width.parse::<usize>().unwrap(); let parsed_width = width.parse::<usize>().unwrap();
let parsed_height = height.parse::<usize>().unwrap(); let parsed_height = height.parse::<usize>().unwrap();
let terminal = crossterm.terminal().clear(ClearType::All); crossterm.terminal().clear(ClearType::All);
return Size::new(parsed_width, parsed_height); return Size::new(parsed_width, parsed_height);
} }
fn print_game_stats(map_size: Size, snake_lenght: usize, food_aten: usize, screen: &mut Screen) {
let crossterm = Crossterm::new();
let cursor = crossterm.cursor();
let terminal = crossterm.terminal().clear(ClearType::All);
screen.write(format!("Snake Lenght: {}\n\r", snake_lenght).as_ref());
screen.write(format!("Food aten: {}\n\r", snake_lenght).as_ref());
cursor.goto(0, map_size.height as u16);
cursor.goto(0, map_size.height as u16);
}
fn game_over_screen() { fn game_over_screen() {
let crossterm = Crossterm::new(); let crossterm = Crossterm::new();
let cursor = crossterm.cursor(); crossterm.terminal().clear(ClearType::All);
let terminal = crossterm.terminal();
terminal.clear(ClearType::All);
println!( println!(
"{}", "{}",
@ -129,6 +140,6 @@ fn game_over_screen() {
.style(format!("{}", messages::END_MESSAGE.join("\n\r"))) .style(format!("{}", messages::END_MESSAGE.join("\n\r")))
.with(Color::Red) .with(Color::Red)
); );
// cursor.goto()
cursor.show(); crossterm.cursor().show();
} }

View File

@ -1,16 +1,10 @@
use super::snake::Snake; use super::variables::{Position, Size};
use super::variables::{Direction, Position, Size};
use crossterm::{ use crossterm::{style, Color, Colorize, Crossterm, Screen, TerminalCursor};
style, Color, ColorType, Crossterm, ObjectStyle, Screen, StyledObject, TerminalCursor,
};
use rand::distributions::{IndependentSample, Range}; use rand::distributions::{IndependentSample, Range};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;
use std::ops::Index;
use rand; use rand;
@ -30,14 +24,14 @@ impl Map {
// render the map on the screen. // render the map on the screen.
pub fn render_map(&mut self, screen: &Screen, free_positions: &mut HashMap<String, Position>) { pub fn render_map(&mut self, screen: &Screen, free_positions: &mut HashMap<String, Position>) {
let crossterm = Crossterm::from_screen(screen); let crossterm = Crossterm::from_screen(screen);
let mut cursor = crossterm.cursor(); let cursor = crossterm.cursor();
let mut terminal = crossterm.terminal(); let terminal = crossterm.terminal();
for y in 0..self.size.height { for y in 0..self.size.height {
for x in 0..self.size.height { for x in 0..self.size.height {
if (y == 0 || y == self.size.height - 1) || (x == 0 || x == self.size.width - 1) { if (y == 0 || y == self.size.height - 1) || (x == 0 || x == self.size.width - 1) {
cursor.goto(x as u16, y as u16); cursor.goto(x as u16, y as u16);
terminal.write(""); "".magenta().paint(&screen.stdout);
} else { } else {
free_positions.insert(format!("{},{}", x, y), Position::new(x, y)); free_positions.insert(format!("{},{}", x, y), Position::new(x, y));
} }
@ -68,7 +62,6 @@ impl Map {
fn draw_food(&self, screen: &Screen) { fn draw_food(&self, screen: &Screen) {
let cursor = TerminalCursor::from_output(&screen.stdout); let cursor = TerminalCursor::from_output(&screen.stdout);
cursor.goto(self.foot_pos.x as u16, self.foot_pos.y as u16); cursor.goto(self.foot_pos.x as u16, self.foot_pos.y as u16);
style("$").with(Color::Green).paint(&screen.stdout); "$".green().paint(&screen.stdout);
screen.stdout.flush();
} }
} }

View File

@ -1,5 +1,5 @@
use super::variables::{Direction, Position, Size}; use super::variables::{Direction, Position, Size};
use crossterm::{Color, Crossterm, Screen}; use crossterm::{Crossterm, Screen};
use std::collections::HashMap; use std::collections::HashMap;
@ -18,16 +18,14 @@ impl Part {
pub struct Snake { pub struct Snake {
pub snake_parts: Vec<Part>, pub snake_parts: Vec<Part>,
pub parent_pos: Position, pub parent_pos: Position,
map_size: Size,
} }
impl Snake { impl Snake {
pub fn new(map_size: Size) -> Snake { pub fn new() -> Snake {
return Snake { Snake {
map_size,
snake_parts: vec![Part::new(9, 10), Part::new(8, 10)], snake_parts: vec![Part::new(9, 10), Part::new(8, 10)],
parent_pos: Position::new(0, 0), parent_pos: Position::new(0, 0),
}; }
} }
pub fn move_snake( pub fn move_snake(
@ -41,7 +39,6 @@ impl Snake {
let terminal = crossterm.terminal(); let terminal = crossterm.terminal();
let count = self.snake_parts.len(); let count = self.snake_parts.len();
let is_food_eaten = false;
for (index, ref mut snake_part) in self.snake_parts.iter_mut().enumerate() { for (index, ref mut snake_part) in self.snake_parts.iter_mut().enumerate() {
if index == count - 1 { if index == count - 1 {
@ -74,7 +71,7 @@ impl Snake {
} }
pub fn draw_snake(&mut self, screen: &Screen) { pub fn draw_snake(&mut self, screen: &Screen) {
for (index, ref mut snake_part) in self.snake_parts.iter_mut().enumerate() { for snake_part in self.snake_parts.iter_mut() {
snake_part.position.draw("", screen); snake_part.position.draw("", screen);
} }
} }
@ -94,9 +91,3 @@ impl Snake {
return &self.snake_parts; return &self.snake_parts;
} }
} }
pub enum SnakeState {
MovedOutMap,
Moved,
AteFood,
}

View File

@ -1,11 +1,6 @@
extern crate crossterm; extern crate crossterm;
use self::crossterm::{ use self::crossterm::{style, Color, Crossterm, Screen, TerminalCursor};
cursor, style, ClearType, Color, Crossterm, ObjectStyle, Screen, StyledObject, TerminalCursor,
};
use std::fmt;
use std::fmt::Debug;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Direction { pub enum Direction {

View File

@ -17,7 +17,9 @@ mod crossterm;
#[cfg(feature = "cursor")] #[cfg(feature = "cursor")]
pub use self::crossterm_cursor::{cursor, TerminalCursor}; pub use self::crossterm_cursor::{cursor, TerminalCursor};
#[cfg(feature = "input")] #[cfg(feature = "input")]
pub use self::crossterm_input::{input, AsyncReader, KeyEvent, TerminalInput}; pub use self::crossterm_input::{
input, AsyncReader, InputEvent, KeyEvent, MouseButton, MouseEvent, SyncReader, TerminalInput,
};
#[cfg(feature = "screen")] #[cfg(feature = "screen")]
pub use self::crossterm_screen::{AlternateScreen, Screen}; pub use self::crossterm_screen::{AlternateScreen, Screen};
#[cfg(feature = "style")] #[cfg(feature = "style")]