diff --git a/README.md b/README.md
index f1197ac..552ccea 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-## Snake in rust using [ncurses](https://docs.rs/ncurses)
+## Snake in rust using [ncurses](https://docs.rs/ncurses) crate
The game is playable
@@ -18,7 +18,19 @@ esc or p to pause
Click the image for asciinema
-[
](https://asciinema.org/a/PtMG7dghPAEZ7tNgx70sKplKq?autoplay=1)
+[
](https://asciinema.org/a/FVB1DZmA7lVK4BILFkzkreMQ9?autoplay=1)
+
+#### Need unicode support in terminal to print the snake
+
+The unicode characters used are:
+
+- 0x0298 ʘ
+- 0x2550 ═
+- 0x2551 ║
+- 0x2554 ╔
+- 0x2557 ╗
+- 0x255a ╚
+- 0x255d ╝
#### Todo :construction:
@@ -28,13 +40,16 @@ Click the image for asciinema
- [ ] Implement Highscore System
- [ ] Make the ui
- [ ] Internal Implementation
+- [x] Use unicode charachters to draw the snake
#### Bugs :bug:
- ~~Snake going through the walls~~
- ~~Food spawning in the walls~~
+- ~~Boxdraw characters not rendering properly~~
- Remove all the logging in the ui
- Pausing delayed if esc is pressed but not if p is pressed.
+- The game flickers a lot after sometime (probably due to the constant redrawing of the whole board every tick)
#### Maybe in the future
@@ -42,19 +57,32 @@ Click the image for asciinema
#### Notes
-A few notes about the complexity of the game and how I should improve the game
+A few notes about the game and how I should improve the game
-Read More
+A few notes about time complexity and redrawing with ncurses
-The complexity of the program is O(n) every tick (time which changes relative to the speed)
+> 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
+> 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.
+> At the time of writing the in commit
+> f9be68e
+> the game still 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
+> I think this can be improved by only drawing the parts of the snake and the board when needed
+
+
+
+
+
+
+Rust specific stuff I learned
+
+> [char](https://doc.rust-lang.org/stable/std/primitive.char.html) and [std::char](https://doc.rust-lang.org/stable/std/char/index.html) are different
+
+> [char](https://doc.rust-lang.org/stable/std/primitive.char.html) is the primitive type [std::char](https://doc.rust-lang.org/stable/std/char/index.html) is the char module. All the functions are not completely same for both (as of rust v1.49.0)
+
+> [ncursesw](httsp://docs.rs/ncursesw) is not needed to print unicode characters. I have no clue where I got that idea from.
diff --git a/images/screenshot.png b/images/screenshot.png
index 2515d7d..80bcdac 100644
Binary files a/images/screenshot.png and b/images/screenshot.png differ
diff --git a/src/game/backend.rs b/src/game/backend.rs
index 83d2ef7..bc5b578 100644
--- a/src/game/backend.rs
+++ b/src/game/backend.rs
@@ -5,6 +5,7 @@ use std::collections::LinkedList;
use std::ops::Sub;
use std::thread::sleep;
use std::time::Duration;
+#[derive(Debug)]
pub enum Direction {
Up,
Down,
@@ -39,6 +40,20 @@ impl Direction {
}
}
}
+#[derive(Debug)]
+// impl std::fmt::Debug for Direction {
+// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+// // write!(f,)
+// write!(
+// f,
+// "{}",
+// match *self {
+// Self::Up => (),
+// _ => (),
+// }
+// );
+// }
+// }
pub enum CellType {
Food,
@@ -89,10 +104,10 @@ impl Cell {
}
pub fn is_adjacent(&self, other: &Cell) -> Option {
match *self - *other {
- (0, 1) => Some(Direction::Left),
+ (0, 1) => Some(Direction::Right),
(1, 0) => Some(Direction::Down),
- (-1, 0) => Some(Direction::Right),
- (0, -1) => Some(Direction::Up),
+ (-1, 0) => Some(Direction::Up),
+ (0, -1) => Some(Direction::Left),
_ => None,
}
}
diff --git a/src/game/frontend.rs b/src/game/frontend.rs
index 5cccbf7..df6475b 100644
--- a/src/game/frontend.rs
+++ b/src/game/frontend.rs
@@ -10,8 +10,6 @@ pub fn game_window(mlines: i32, mcols: i32, vmargin: i32, hmargin: i32) -> WINDO
let (starty, startx): (i32, i32);
lines = mlines - vmargin * 2;
cols = mcols - hmargin * 2;
- // starty = (mlines - lines) / 2;
- // startx = (mcols - cols) / 2;
starty = vmargin;
startx = hmargin;
game_win = newwin(lines, cols, starty, startx);
@@ -41,54 +39,67 @@ pub fn draw_snake(snake: &Snake, game_win: WINDOW) {
// 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"));
+ mvwaddstr(
+ game_win,
+ prev.posyx().0,
+ prev.posyx().1,
+ &format!("{}", std::char::from_u32(0x0298).unwrap_or('O')),
+ );
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
+ current = match _current {
+ Some(cell) => cell,
+ None => return,
+ };
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 (
+ let snake_char: u32 = 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 ═
- _ => " ",
+ (Direction::Up, Direction::Down) | (Direction::Down, Direction::Up) => 0x2551, //"║"
+ (Direction::Up, Direction::Left) | (Direction::Left, Direction::Up) => 0x255d, //"╝"
+ (Direction::Up, Direction::Right) | (Direction::Right, Direction::Up) => 0x255a, //"╚"
+ (Direction::Down, Direction::Left) | (Direction::Left, Direction::Down) => 0x2557, // "╗"
+ (Direction::Down, Direction::Right) | (Direction::Right, Direction::Down) => 0x2554, //"╔"
+ (Direction::Left, Direction::Right) | (Direction::Right, Direction::Left) => 0x2550, //"═"
+ _ => 0x20,
};
mvwaddstr(
- game_win, snake_l, snake_c, // &format!("{}", snake_char as char),
- // &format!("{}", snake_char),
- "o",
+ game_win,
+ snake_l,
+ snake_c,
+ &format!("{}", std::char::from_u32(snake_char).unwrap_or('o')),
);
prev = current;
current = next;
}
- mvwaddstr(game_win, current.posyx().0, current.posyx().1, "t");
+
+ mvwaddstr(
+ game_win,
+ current.posyx().0,
+ current.posyx().1,
+ &format!(
+ "{}",
+ std::char::from_u32(match current.is_adjacent(prev).unwrap() {
+ Direction::Up | Direction::Down => 0x2551,
+ Direction::Left | Direction::Right => 0x2550,
+ })
+ .unwrap_or('o')
+ ),
+ );
wrefresh(game_win);
}
pub fn draw_board(board: &Board, game_win: WINDOW) {
let (food_l, food_c): (i32, i32) = board.food_posyx();
- mvwaddstr(game_win, food_l, food_c, "F");
+ mvwaddstr(
+ game_win,
+ food_l,
+ food_c,
+ &format!("{}", std::char::from_u32(0x0298).unwrap_or('F')),
+ );
}
pub fn _log(snake: &Snake, board: &Board) {
@@ -96,26 +107,16 @@ pub fn _log(snake: &Snake, board: &Board) {
let (bfl, bfc): (i32, i32) = board.food_posyx();
mvwaddstr(stdscr(), 0, 0, &format!("snake:head: {} {} ", shl, shc));
mvwaddstr(stdscr(), 1, 0, &format!("board:food: {} {} ", bfl, bfc));
- // mvwaddstr(
- // stdscr(),
- // 2,
- // 0,
- // &format!(
- // "board:maxlines {} maxcols {}",
- // board.maxlines, board.maxcols
- // ),
- // );
-
wmove(stdscr(), 2, 0);
for snake_cell in snake.iter() {
- let (scl, scc): (i32, i32) = snake_cell.posyx();
- waddstr(stdscr(), &format!("cell: {} {} ", scl, scc));
+ waddstr(
+ stdscr(),
+ &format!(
+ "",
+ snake_cell.posyx().0,
+ snake_cell.posyx().1
+ ),
+ );
}
- 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 f2c30b6..f3e6a20 100644
--- a/src/game/mod.rs
+++ b/src/game/mod.rs
@@ -24,7 +24,7 @@ pub fn start() {
loop {
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 51ba3f0..55434c9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,11 +5,13 @@ mod menu;
// use game::{Cell, Snake};
// use ncurses::*;
use ncurses::{
- curs_set, endwin, getmaxyx, initscr, keypad, noecho, raw, refresh, stdscr, CURSOR_VISIBILITY,
+ curs_set, endwin, getmaxyx, initscr, keypad, noecho, raw, refresh, setlocale, stdscr,
+ LcCategory, CURSOR_VISIBILITY,
};
fn main() {
// let (lines, cols): (i32, i32) = (0, 0);
+ setlocale(LcCategory::all, "");
initscr();
raw();
keypad(stdscr(), true);
@@ -17,10 +19,6 @@ 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();
|