136 lines
2.8 KiB
Text
136 lines
2.8 KiB
Text
/* Snake.wren */
|
|
|
|
import "random" for Random
|
|
import "./dynamic" for Enum, Lower
|
|
|
|
foreign class Window {
|
|
construct initscr() {}
|
|
|
|
foreign nodelay(bf)
|
|
}
|
|
|
|
class Ncurses {
|
|
foreign static cbreak()
|
|
|
|
foreign static noecho()
|
|
|
|
foreign static refresh()
|
|
|
|
foreign static getch()
|
|
|
|
foreign static mvaddch(y, x, ch)
|
|
|
|
foreign static endwin()
|
|
}
|
|
|
|
class C {
|
|
foreign static usleep(usec)
|
|
}
|
|
|
|
var Dir = Enum.create("Dir", ["N", "E", "S", "W"])
|
|
var State = Enum.create("State", ["space", "food", "border"])
|
|
|
|
var w = 80
|
|
var h = 40
|
|
var board = List.filled(w * h, 0)
|
|
var rand = Random.new()
|
|
var head
|
|
var dir
|
|
var quit
|
|
|
|
// ASCII values
|
|
var hash = 35
|
|
var at = 64
|
|
var dot = 46
|
|
var spc = 32
|
|
|
|
/* negative values denote the snake (a negated time-to-live in given cell) */
|
|
|
|
// reduce a time-to-live, effectively erasing the tail
|
|
var age = Fn.new {
|
|
for (i in 0...w * h) {
|
|
if (board[i] < 0) board[i] = board[i] + 1
|
|
}
|
|
}
|
|
|
|
// put a piece of food at random empty position
|
|
var plant = Fn.new {
|
|
var r
|
|
while (true) {
|
|
r = rand.int(w * h)
|
|
if (board[r] = State.space) break
|
|
}
|
|
board[r] = State.food
|
|
}
|
|
|
|
// initialize the board, plant a very first food item
|
|
var start = Fn.new {
|
|
for (i in 0...w) board[i] = board[i + (h - 1) * w] = State.border
|
|
for (i in 0...h) board[i * w] = board[i * w + w - 1] = State.border
|
|
head = (w * (h - 1 - h % 2) / 2).floor // screen center for any h
|
|
board[head] = -5
|
|
dir = Dir.N
|
|
quit = false
|
|
plant.call()
|
|
}
|
|
|
|
var step = Fn.new {
|
|
var len = board[head]
|
|
if (dir == Dir.N) {
|
|
head = head - w
|
|
} else if (dir == Dir.S) {
|
|
head = head + w
|
|
} else if (dir == Dir.W) {
|
|
head = head - 1
|
|
} else if (dir == Dir.E) {
|
|
head = head + 1
|
|
}
|
|
|
|
if (board[head] == State.space) {
|
|
board[head] = len - 1 // keep in mind len is negative
|
|
age.call()
|
|
} else if (board[head] == State.food) {
|
|
board[head] = len - 1
|
|
plant.call()
|
|
} else {
|
|
quit = true
|
|
}
|
|
}
|
|
|
|
var show = Fn.new {
|
|
var symbol = [spc, at, dot]
|
|
for (i in 0...w*h) {
|
|
Ncurses.mvaddch((i/w).floor, i % w, board[i] < 0 ? hash : symbol[board[i]])
|
|
}
|
|
Ncurses.refresh()
|
|
}
|
|
|
|
var initScreen = Fn.new {
|
|
var win = Window.initscr()
|
|
Ncurses.cbreak()
|
|
Ncurses.noecho()
|
|
win.nodelay(true)
|
|
}
|
|
|
|
initScreen.call()
|
|
start.call()
|
|
while (true) {
|
|
show.call()
|
|
var ch = Ncurses.getch()
|
|
if (ch == Lower.i) {
|
|
dir = Dir.N
|
|
} else if (ch == Lower.j) {
|
|
dir = Dir.W
|
|
} else if (ch == Lower.k) {
|
|
dir = Dir.S
|
|
} else if (ch == Lower.l) {
|
|
dir = Dir.E
|
|
} else if (ch == Lower.q) {
|
|
quit = true
|
|
}
|
|
step.call()
|
|
C.usleep(300 * 1000) // 300 ms is a reasonable delay
|
|
if (quit) break
|
|
}
|
|
C.usleep(999 * 1000)
|
|
Ncurses.endwin()
|