Add unicode snake printing
Removed a lot of unused and commented code
This commit is contained in:
@@ -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
|
The game is playable
|
||||||
|
|
||||||
@@ -18,7 +18,19 @@ esc or p to pause
|
|||||||
|
|
||||||
Click the image for asciinema
|
Click the image for asciinema
|
||||||
|
|
||||||
[<img src="images/screenshot.png" width="650" />](https://asciinema.org/a/PtMG7dghPAEZ7tNgx70sKplKq?autoplay=1)
|
[<img src="images/screenshot.png" width="650" />](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:
|
#### Todo :construction:
|
||||||
|
|
||||||
@@ -28,13 +40,16 @@ Click the image for asciinema
|
|||||||
- [ ] Implement Highscore System
|
- [ ] Implement Highscore System
|
||||||
- [ ] Make the ui
|
- [ ] Make the ui
|
||||||
- [ ] Internal Implementation
|
- [ ] Internal Implementation
|
||||||
|
- [x] Use unicode charachters to draw the snake
|
||||||
|
|
||||||
#### Bugs :bug:
|
#### Bugs :bug:
|
||||||
|
|
||||||
- ~~Snake going through the walls~~
|
- ~~Snake going through the walls~~
|
||||||
- ~~Food spawning in the walls~~
|
- ~~Food spawning in the walls~~
|
||||||
|
- ~~Boxdraw characters not rendering properly~~
|
||||||
- Remove all the logging in the ui
|
- Remove all the logging in the ui
|
||||||
- Pausing delayed if esc is pressed but not if p is pressed.
|
- 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
|
#### Maybe in the future
|
||||||
|
|
||||||
@@ -42,19 +57,32 @@ Click the image for asciinema
|
|||||||
|
|
||||||
#### Notes
|
#### 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
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Read More</summary>
|
<summary>A few notes about time complexity and redrawing with ncurses</summary>
|
||||||
|
|
||||||
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
|
> At the time of writing the in commit
|
||||||
<a href="https://github.com/uttarayan21/snake/commit/de66f7d249a56f883dd632598a4178b1bd1320ba">f9be68e</a>
|
> <a href="https://github.com/uttarayan21/snake/commit/de66f7d249a56f883dd632598a4178b1bd1320ba">f9be68e</a>
|
||||||
the game redraws the total board and the total snake every tick.
|
> 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
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>Rust specific stuff I learned</summary>
|
||||||
|
|
||||||
|
> [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.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 57 KiB |
+18
-3
@@ -5,6 +5,7 @@ use std::collections::LinkedList;
|
|||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Up,
|
Up,
|
||||||
Down,
|
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 {
|
pub enum CellType {
|
||||||
Food,
|
Food,
|
||||||
@@ -89,10 +104,10 @@ impl Cell {
|
|||||||
}
|
}
|
||||||
pub fn is_adjacent(&self, other: &Cell) -> Option<Direction> {
|
pub fn is_adjacent(&self, other: &Cell) -> Option<Direction> {
|
||||||
match *self - *other {
|
match *self - *other {
|
||||||
(0, 1) => Some(Direction::Left),
|
(0, 1) => Some(Direction::Right),
|
||||||
(1, 0) => Some(Direction::Down),
|
(1, 0) => Some(Direction::Down),
|
||||||
(-1, 0) => Some(Direction::Right),
|
(-1, 0) => Some(Direction::Up),
|
||||||
(0, -1) => Some(Direction::Up),
|
(0, -1) => Some(Direction::Left),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-49
@@ -10,8 +10,6 @@ pub fn game_window(mlines: i32, mcols: i32, vmargin: i32, hmargin: i32) -> WINDO
|
|||||||
let (starty, startx): (i32, i32);
|
let (starty, startx): (i32, i32);
|
||||||
lines = mlines - vmargin * 2;
|
lines = mlines - vmargin * 2;
|
||||||
cols = mcols - hmargin * 2;
|
cols = mcols - hmargin * 2;
|
||||||
// starty = (mlines - lines) / 2;
|
|
||||||
// startx = (mcols - cols) / 2;
|
|
||||||
starty = vmargin;
|
starty = vmargin;
|
||||||
startx = hmargin;
|
startx = hmargin;
|
||||||
game_win = newwin(lines, cols, starty, startx);
|
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
|
// 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
|
// 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
|
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();
|
let _current = snake_iter.next();
|
||||||
// current = match _current {
|
current = match _current {
|
||||||
// Some(cell) => cell,
|
Some(cell) => cell,
|
||||||
// None => return,
|
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 {
|
for next in snake_iter {
|
||||||
// O(n) the whole snake is redrawn every single tick
|
// O(n) the whole snake is redrawn every single tick
|
||||||
let (snake_l, snake_c): (i32, i32) = current.posyx();
|
let (snake_l, snake_c): (i32, i32) = current.posyx();
|
||||||
// mvwaddstr(game_win, snake_l, snake_c, "o");
|
// mvwaddstr(game_win, snake_l, snake_c, "o");
|
||||||
let snake_char = match (
|
let snake_char: u32 = match (
|
||||||
prev.is_adjacent(current).unwrap(),
|
prev.is_adjacent(current).unwrap(),
|
||||||
next.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::Down) | (Direction::Down, Direction::Up) => 0x2551, //"║"
|
||||||
(Direction::Up, Direction::Left) | (Direction::Left, Direction::Left) => "╝", //188, // ╝
|
(Direction::Up, Direction::Left) | (Direction::Left, Direction::Up) => 0x255d, //"╝"
|
||||||
(Direction::Up, Direction::Right) | (Direction::Right, Direction::Up) => "╚", //200, // ╚
|
(Direction::Up, Direction::Right) | (Direction::Right, Direction::Up) => 0x255a, //"╚"
|
||||||
(Direction::Down, Direction::Left) | (Direction::Left, Direction::Down) => "╗", //187, // ╗
|
(Direction::Down, Direction::Left) | (Direction::Left, Direction::Down) => 0x2557, // "╗"
|
||||||
(Direction::Down, Direction::Right) | (Direction::Right, Direction::Down) => "╔", //,201, // ╔
|
(Direction::Down, Direction::Right) | (Direction::Right, Direction::Down) => 0x2554, //"╔"
|
||||||
(Direction::Left, Direction::Right) | (Direction::Right, Direction::Left) => "═", //205 ═
|
(Direction::Left, Direction::Right) | (Direction::Right, Direction::Left) => 0x2550, //"═"
|
||||||
_ => " ",
|
_ => 0x20,
|
||||||
};
|
};
|
||||||
mvwaddstr(
|
mvwaddstr(
|
||||||
game_win, snake_l, snake_c, // &format!("{}", snake_char as char),
|
game_win,
|
||||||
// &format!("{}", snake_char),
|
snake_l,
|
||||||
"o",
|
snake_c,
|
||||||
|
&format!("{}", std::char::from_u32(snake_char).unwrap_or('o')),
|
||||||
);
|
);
|
||||||
prev = current;
|
prev = current;
|
||||||
current = next;
|
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);
|
wrefresh(game_win);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_board(board: &Board, game_win: WINDOW) {
|
pub fn draw_board(board: &Board, game_win: WINDOW) {
|
||||||
let (food_l, food_c): (i32, i32) = board.food_posyx();
|
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) {
|
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();
|
let (bfl, bfc): (i32, i32) = board.food_posyx();
|
||||||
mvwaddstr(stdscr(), 0, 0, &format!("snake:head: {} {} ", shl, shc));
|
mvwaddstr(stdscr(), 0, 0, &format!("snake:head: {} {} ", shl, shc));
|
||||||
mvwaddstr(stdscr(), 1, 0, &format!("board:food: {} {} ", bfl, bfc));
|
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);
|
wmove(stdscr(), 2, 0);
|
||||||
for snake_cell in snake.iter() {
|
for snake_cell in snake.iter() {
|
||||||
let (scl, scc): (i32, i32) = snake_cell.posyx();
|
waddstr(
|
||||||
waddstr(stdscr(), &format!("cell: {} {} ", scl, scc));
|
stdscr(),
|
||||||
|
&format!(
|
||||||
|
"<cell y:{} x:{}>",
|
||||||
|
snake_cell.posyx().0,
|
||||||
|
snake_cell.posyx().1
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
mvwaddstr(
|
|
||||||
stdscr(),
|
|
||||||
3,
|
|
||||||
0,
|
|
||||||
&format!("snake_size {}", snake.iter().size_hint().0),
|
|
||||||
);
|
|
||||||
wrefresh(stdscr());
|
wrefresh(stdscr());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ pub fn start() {
|
|||||||
loop {
|
loop {
|
||||||
frontend::draw_snake(&snake, game_win); // always draw snake before board because the snake will clear the 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::draw_board(&board, game_win);
|
||||||
frontend::_log(&snake, &board);
|
// frontend::_log(&snake, &board);
|
||||||
if board.check_collision(&snake) {
|
if board.check_collision(&snake) {
|
||||||
// Add stuff here to show the score and
|
// Add stuff here to show the score and
|
||||||
// how You lose screen
|
// how You lose screen
|
||||||
|
|||||||
+3
-5
@@ -5,11 +5,13 @@ mod menu;
|
|||||||
// use game::{Cell, Snake};
|
// use game::{Cell, Snake};
|
||||||
// use ncurses::*;
|
// use ncurses::*;
|
||||||
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() {
|
fn main() {
|
||||||
// let (lines, cols): (i32, i32) = (0, 0);
|
// let (lines, cols): (i32, i32) = (0, 0);
|
||||||
|
setlocale(LcCategory::all, "");
|
||||||
initscr();
|
initscr();
|
||||||
raw();
|
raw();
|
||||||
keypad(stdscr(), true);
|
keypad(stdscr(), true);
|
||||||
@@ -17,10 +19,6 @@ fn main() {
|
|||||||
noecho();
|
noecho();
|
||||||
let (mut mlines, mut mcols): (i32, i32) = (0, 0);
|
let (mut mlines, mut mcols): (i32, i32) = (0, 0);
|
||||||
getmaxyx(stdscr(), &mut mlines, &mut mcols);
|
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) {
|
if (mlines < 20) || (mcols < 35) {
|
||||||
refresh();
|
refresh();
|
||||||
endwin();
|
endwin();
|
||||||
|
|||||||
Reference in New Issue
Block a user