class SnakeGame(w, h) { 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], ) define BG_COLOR = "on_black" define FOOD_COLOR = ("red" + " " + BG_COLOR) define SNAKE_COLOR = ("bold green" + " " + BG_COLOR) define SLEEP_SEC = 0.02 const ( A_VOID = ansi.colored(' ', BG_COLOR), A_FOOD = ansi.colored('❇', FOOD_COLOR), A_BLOCK = ansi.colored('■', SNAKE_COLOR), ) has dir = LEFT has grid = [[]] has head_pos = [0, 0] has tail_pos = [0, 0] method init { grid = h.of { w.of { [VOID] } } head_pos = [h//2, w//2] 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 self.make_food() } method 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 } method display { print("\033[H", grid.map { |row| row.map { |cell| given (cell[0]) { when (VOID) { A_VOID } when (FOOD) { A_FOOD } default { A_BLOCK } } }.join('') }.join("\n") ) } method 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; self.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] } } method play { STDOUT.autoflush(true) readkey.ReadMode(3) try { loop { var key while (!defined(key = readkey.ReadLine(-1))) { self.move() self.display() Sys.sleep(SLEEP_SEC) } 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 } } } } } catch { readkey.ReadMode(0) } } } var w = `tput cols`.to_i var h = `tput lines`.to_i SnakeGame(w || 80, h || 24).play