138 lines
3.4 KiB
Plaintext
138 lines
3.4 KiB
Plaintext
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
|