123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- #!/usr/bin/ruby
- # Author: Daniel "Trizen" Șuteu
- # License: GPLv3
- # Date: 25 June 2015
- # Website: https://github.com/trizen
- # The snake game. (with colors + Unicode)
- const readkey = frequire('Term::ReadKey');
- const ansi = frequire('Term::ANSIColor');
- enum(
- VOID,
- HEAD,
- BODY,
- TAIL,
- FOOD,
- );
- define (
- LEFT = [+0, -1],
- RIGHT = [+0, +1],
- UP = [-1, +0],
- DOWN = [+1, +0],
- );
- const BG_COLOR = "on_black";
- const FOOD_COLOR = ("red" + " " + BG_COLOR);
- const SNAKE_COLOR = ("bold green" + " " + BG_COLOR);
- const (
- U_HEAD = ansi.colored('▲', SNAKE_COLOR),
- D_HEAD = ansi.colored('▼', SNAKE_COLOR),
- L_HEAD = ansi.colored('◀', SNAKE_COLOR),
- R_HEAD = ansi.colored('▶', SNAKE_COLOR),
- );
- const (
- U_BODY = ansi.colored('╹', SNAKE_COLOR),
- D_BODY = ansi.colored('╻', SNAKE_COLOR),
- L_BODY = ansi.colored('╴', SNAKE_COLOR),
- R_BODY = ansi.colored('╶', SNAKE_COLOR),
- );
- const (
- U_TAIL = ansi.colored('╽', SNAKE_COLOR),
- D_TAIL = ansi.colored('╿', SNAKE_COLOR),
- L_TAIL = ansi.colored('╼', SNAKE_COLOR),
- R_TAIL = ansi.colored('╾', SNAKE_COLOR),
- );
- const A_VOID = ansi.colored(' ', BG_COLOR);
- const A_FOOD = ansi.colored('❇', FOOD_COLOR);
- var sleep = 0.02; # sleep duration between updates
- var food_num = 10; # number of initial food sources
- var w = `tput cols`.to_i;
- var h = `tput lines`.to_i;
- var r = "\033[H";
- var dir = LEFT;
- var grid = h.of { w.of { Array.new(VOID) } };
- var head_pos = [h>>1, w>>1];
- var tail_pos = [head_pos[0], head_pos[1]+1];
- grid[head_pos[0]][head_pos[1]] = [HEAD, dir]; # head
- grid[tail_pos[0]][tail_pos[1]] = [TAIL, dir]; # tail
- func make_food {
- var (food_x, food_y);
- do {
- food_x = w.rand.int;
- food_y = h.rand.int;
- } while (grid[food_y][food_x][0] != VOID);
- grid[food_y][food_x][0] = FOOD;
- }
- { make_food() } * food_num;
- func display {
- static i = 0;
- static s = [UP, DOWN, LEFT, RIGHT];
- print(r, grid.map { |row|
- row.map { |cell|
- if (cell[0] != VOID) {
- i = s.index(cell[1])
- }
- given (cell[0]) {
- when (VOID) { A_VOID }
- when (FOOD) { A_FOOD }
- when (BODY) { [U_BODY, D_BODY, L_BODY, R_BODY][i] }
- when (HEAD) { [U_HEAD, D_HEAD, L_HEAD, R_HEAD][i] }
- when (TAIL) { [U_TAIL, D_TAIL, L_TAIL, R_TAIL][i] }
- }
- }.join('')
- }.join("\n")
- );
- }
- func move {
- var grew = false;
- # Move the head
- var (y, x) = head_pos...;
- var new_y = (y+dir[0] % h);
- var new_x = (x+dir[1] % w);
- var cell = grid[new_y][new_x];
- given (cell[0]) {
- when (BODY) { die "\nYou just bit your own body!\n" }
- when (TAIL) { die "\nYou just bit your own tail!\n" }
- when (FOOD) { grew = true; make_food() }
- }
- # Create a new head
- grid[new_y][new_x] = [HEAD, dir];
- # Replace the current head with body
- grid[y][x] = [BODY, dir];
- # Update the head position
- head_pos = [new_y, new_x];
- # Move the tail
- if (!grew) {
- var (y, x) = tail_pos...;
- var pos = grid[y][x][1];
- var new_y = (y+pos[0] % h);
- var new_x = (x+pos[1] % w);
- grid[y][x][0] = VOID; # erase the current tail
- grid[new_y][new_x][0] = TAIL; # create a new tail
- tail_pos = [new_y, new_x];
- }
- }
- readkey.ReadMode(3);
- STDOUT.autoflush(true);
- loop {
- var key;
- while (!defined(key = readkey.ReadLine(-1))) {
- move();
- display();
- Sys.sleep(sleep);
- }
- given (key) {
- when ("\e[A") { if (dir != DOWN ) { dir = UP } }
- when ("\e[B") { if (dir != UP ) { dir = DOWN } }
- when ("\e[C") { if (dir != LEFT ) { dir = RIGHT } }
- when ("\e[D") { if (dir != RIGHT) { dir = LEFT } }
- }
- }
|