From c2eff08c9f5e64fe86aaf2bbb05b7491f7b8e249 Mon Sep 17 00:00:00 2001 From: Uttarayan Mondal Date: Wed, 20 Jan 2021 20:52:17 +0530 Subject: [PATCH] Added way to print the snake with ascii extended But ncurses doesn't seem to support ascii extended so it looks bizzare --- .gitignore | 1 + Cargo.lock | 4 +-- Cargo.toml | 2 +- README.md | 19 +++++++++++ src/game/backend.rs | 18 +++++++++++ src/game/frontend.rs | 77 +++++++++++++++++++++++++++++++++++--------- src/game/mod.rs | 4 +-- src/main.rs | 4 +++ 8 files changed, 109 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..96ef6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index f6aaef8..c8091a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,9 @@ checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" [[package]] name = "ncurses" -version = "5.99.0" +version = "5.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82" +checksum = "a7db07ca287f6f4fb267e8b2ab0f9eb68f5a311a97315aaee845afa98f8a416b" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index 98b4b7d..ab75090 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ncurses = "5.71.1" +ncurses = "5.100.0" rand = "0.8.0" diff --git a/README.md b/README.md index ec8f171..f1197ac 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,22 @@ Click the image for asciinema #### Maybe in the future - [ ] Autoplay the game using a simple pathfinding algorithm (to show on [r/unixporn](https://reddit.com/r/unixporn) of course :clown_face:) + +#### Notes + +A few notes about the complexity of the game and how I should improve the game + +
+Read More + +The complexity of the program is O(n) every tick (time which changes relative to the speed) + +However the place, I can improve is the redrawing of the game + +As of commit +f9be68e +the game redraws the total board and the total snake every tick. + +I think this can be improved by only drawing the parts of the snake and the board when needed + +
diff --git a/src/game/backend.rs b/src/game/backend.rs index e3addda..83d2ef7 100644 --- a/src/game/backend.rs +++ b/src/game/backend.rs @@ -2,6 +2,7 @@ extern crate rand; use core::iter::Iterator; use rand::Rng; use std::collections::LinkedList; +use std::ops::Sub; use std::thread::sleep; use std::time::Duration; pub enum Direction { @@ -58,6 +59,12 @@ impl PartialEq for Cell { } } } +impl Sub for Cell { + type Output = (i32, i32); + fn sub(self, rhs: Cell) -> (i32, i32) { + return (self.line - rhs.line, self.col - rhs.col); + } +} impl Cell { pub fn new(l: i32, c: i32, t: CellType) -> Cell { Cell { @@ -80,6 +87,15 @@ impl Cell { pub fn chtype(&mut self, ctype: CellType) { self.ctype = ctype; } + pub fn is_adjacent(&self, other: &Cell) -> Option { + match *self - *other { + (0, 1) => Some(Direction::Left), + (1, 0) => Some(Direction::Down), + (-1, 0) => Some(Direction::Right), + (0, -1) => Some(Direction::Up), + _ => None, + } + } } impl Copy for CellType {} @@ -136,6 +152,7 @@ impl Board { let mut snake_iter = snake.iter(); snake_iter.next(); for snake_cell in snake_iter { + // O(n) ; don't know how to reduce this complexity if snake.posyx() == snake_cell.posyx() { self.gamestate = GameState::Failed(FailState::Snake); return true; @@ -167,6 +184,7 @@ impl Board { let mut snake_iter = snake.iter(); for snake_cell in snake_iter.next() { if *snake_cell == food { + // O(n) ; I think this is nessacary/ I don't know how to reduce the order // if food collides with the snake body then set food to a new random position and set spawned food to false // so that the snake_iter is started again from the front of the snake food = Cell::random(self.maxlines, self.maxcols); diff --git a/src/game/frontend.rs b/src/game/frontend.rs index 6a11723..5cccbf7 100644 --- a/src/game/frontend.rs +++ b/src/game/frontend.rs @@ -1,7 +1,8 @@ // use ncurses::*; -use crate::game::backend::{Board, Snake}; +use crate::game::backend::{Board, Cell, Direction, Snake}; use ncurses::{ - box_, delwin, keypad, mvwaddstr, newwin, stdscr, wborder, wclrtobot, wmove, wrefresh, WINDOW, + box_, delwin, keypad, mvwaddstr, newwin, stdscr, waddstr, wborder, wclrtobot, wmove, wrefresh, + WINDOW, }; pub fn game_window(mlines: i32, mcols: i32, vmargin: i32, hmargin: i32) -> WINDOW { let game_win: WINDOW; @@ -30,13 +31,58 @@ pub fn destroy_window(win: WINDOW) { pub fn draw_snake(snake: &Snake, game_win: WINDOW) { // let mut snake_iter = snake.iter(); + let (mut prev, mut current, _next): (&Cell, &Cell, &Cell); + let mut snake_iter = snake.iter(); wmove(game_win, 0, 0); wclrtobot(game_win); box_(game_win, 0, 0); - for snake_cell in snake.iter() { - let (snake_l, snake_c): (i32, i32) = snake_cell.posyx(); - mvwaddstr(game_win, snake_l, snake_c, "o"); + + // I want to draw the snake as ascii box charachters + // So I'll need to know the last and next cell of the snake to draw the current snake_cell + // For some reason the snake goes invisible after the first food + prev = snake_iter.next().unwrap(); // currently this should be head. On initial run this should be the only snake_cell + mvwaddstr(game_win, prev.posyx().0, prev.posyx().1, &format!("H")); + let _current = snake_iter.next(); + // current = match _current { + // Some(cell) => cell, + // None => return, + // }; + if _current.is_some() { + current = _current.unwrap(); + } else { + return; + } // the match is equivalent to this block of code + // this should be none in the initial run + // as of now this will panic as soon as the game starts + // I need to match this with Some(Cell) and None and if + // none then just straight exit from the function + + // The head is never in current so the head is not being printed as of now + for next in snake_iter { + // O(n) the whole snake is redrawn every single tick + let (snake_l, snake_c): (i32, i32) = current.posyx(); + // mvwaddstr(game_win, snake_l, snake_c, "o"); + let snake_char = match ( + prev.is_adjacent(current).unwrap(), + next.is_adjacent(current).unwrap(), + ) { + (Direction::Up, Direction::Down) | (Direction::Down, Direction::Up) => "║", //186, //boxdraw double vertical line ║ code 186 + (Direction::Up, Direction::Left) | (Direction::Left, Direction::Left) => "╝", //188, // ╝ + (Direction::Up, Direction::Right) | (Direction::Right, Direction::Up) => "╚", //200, // ╚ + (Direction::Down, Direction::Left) | (Direction::Left, Direction::Down) => "╗", //187, // ╗ + (Direction::Down, Direction::Right) | (Direction::Right, Direction::Down) => "╔", //,201, // ╔ + (Direction::Left, Direction::Right) | (Direction::Right, Direction::Left) => "═", //205 ═ + _ => " ", + }; + mvwaddstr( + game_win, snake_l, snake_c, // &format!("{}", snake_char as char), + // &format!("{}", snake_char), + "o", + ); + prev = current; + current = next; } + mvwaddstr(game_win, current.posyx().0, current.posyx().1, "t"); wrefresh(game_win); } @@ -60,15 +106,16 @@ pub fn _log(snake: &Snake, board: &Board) { // ), // ); - // for snake_cell in snake.iter() { - // let (scl, scc): (i32, i32) = snake_cell.posyx(); - // waddstr(stdscr(), &format!("cell: {} {} ", scl, scc)); - // } - // mvwaddstr( - // stdscr(), - // 2, - // 0, - // &format!("snake_size {}", snake.iter().size_hint().0), - // ); + wmove(stdscr(), 2, 0); + for snake_cell in snake.iter() { + let (scl, scc): (i32, i32) = snake_cell.posyx(); + waddstr(stdscr(), &format!("cell: {} {} ", scl, scc)); + } + mvwaddstr( + stdscr(), + 3, + 0, + &format!("snake_size {}", snake.iter().size_hint().0), + ); wrefresh(stdscr()); } diff --git a/src/game/mod.rs b/src/game/mod.rs index 9fb1595..f2c30b6 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -22,9 +22,9 @@ pub fn start() { let mut board = Board::new(mlines - vmargin * 2, mcols - hmargin * 2); nodelay(game_win, true); loop { - frontend::draw_snake(&snake, game_win); + frontend::draw_snake(&snake, game_win); // always draw snake before board because the snake will clear the game win frontend::draw_board(&board, game_win); - // frontend::_log(&snake, &board); + frontend::_log(&snake, &board); if board.check_collision(&snake) { // Add stuff here to show the score and // how You lose screen diff --git a/src/main.rs b/src/main.rs index 9ae1405..51ba3f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,10 @@ fn main() { noecho(); let (mut mlines, mut mcols): (i32, i32) = (0, 0); getmaxyx(stdscr(), &mut mlines, &mut mcols); + // let (mlines, mcols) = match getmaxyx(stdscr()) { + // Ok(size) => (size.lines, size.columns), + // Err(e) => panic!(e), + // }; if (mlines < 20) || (mcols < 35) { refresh(); endwin();