Joint work with Sumeet Agarwal. In the process we discovered several bugs in our fake window helpers for tests. Thanks Sumeet! Previous prototypes: - https://archive.org/details/akkartik-2min-2020-12-06 - https://merveilles.town/@akkartik/105759816342173743
683 lines
21 KiB
Lua
683 lines
21 KiB
Lua
# .tlv file generated by https://github.com/akkartik/teliva
|
|
# You may edit it if you are careful; however, you may see cryptic errors if you
|
|
# violate Teliva's assumptions.
|
|
#
|
|
# .tlv files are representations of Teliva programs. Teliva programs consist of
|
|
# sequences of definitions. Each definition is a table of key/value pairs. Keys
|
|
# and values are both strings.
|
|
#
|
|
# Lines in .tlv files always follow exactly one of the following forms:
|
|
# - comment lines at the top of the file starting with '#' at column 0
|
|
# - beginnings of definitions starting with '- ' at column 0, followed by a
|
|
# key/value pair
|
|
# - key/value pairs consisting of ' ' at column 0, containing either a
|
|
# spaceless value on the same line, or a multi-line value
|
|
# - multiline values indented by more than 2 spaces, starting with a '>'
|
|
#
|
|
# If these constraints are violated, Teliva may unceremoniously crash. Please
|
|
# report bugs at http://akkartik.name/contact
|
|
- __teliva_timestamp: original
|
|
str_helpers:
|
|
>-- some string helpers from http://lua-users.org/wiki/StringIndexing
|
|
>
|
|
>-- index characters using []
|
|
>getmetatable('').__index = function(str,i)
|
|
> if type(i) == 'number' then
|
|
> return str:sub(i,i)
|
|
> else
|
|
> return string[i]
|
|
> end
|
|
>end
|
|
>
|
|
>-- ranges using (), selected bytes using {}
|
|
>getmetatable('').__call = function(str,i,j)
|
|
> if type(i)~='table' then
|
|
> return str:sub(i,j)
|
|
> else
|
|
> local t={}
|
|
> for k,v in ipairs(i) do
|
|
> t[k]=str:sub(v,v)
|
|
> end
|
|
> return table.concat(t)
|
|
> end
|
|
>end
|
|
>
|
|
>-- iterate over an ordered sequence
|
|
>function q(x)
|
|
> if type(x) == 'string' then
|
|
> return x:gmatch('.')
|
|
> else
|
|
> return ipairs(x)
|
|
> end
|
|
>end
|
|
>
|
|
>-- insert within string
|
|
>function string.insert(str1, str2, pos)
|
|
> return str1:sub(1,pos)..str2..str1:sub(pos+1)
|
|
>end
|
|
>
|
|
>function string.remove(s, pos)
|
|
> return s:sub(1,pos-1)..s:sub(pos+1)
|
|
>end
|
|
>
|
|
>-- TODO: backport utf-8 support from Lua 5.3
|
|
- __teliva_timestamp: original
|
|
debugy:
|
|
>debugy = 5
|
|
- __teliva_timestamp: original
|
|
dbg:
|
|
>-- helper for debug by print; overlay debug information towards the right
|
|
>-- reset debugy every time you refresh screen
|
|
>function dbg(window, s)
|
|
> local oldy = 0
|
|
> local oldx = 0
|
|
> oldy, oldx = window:getyx()
|
|
> window:mvaddstr(debugy, 60, s)
|
|
> debugy = debugy+1
|
|
> window:mvaddstr(oldy, oldx, '')
|
|
>end
|
|
- __teliva_timestamp: original
|
|
check:
|
|
>function check(x, msg)
|
|
> if x then
|
|
> Window:addch('.')
|
|
> else
|
|
> print('F - '..msg)
|
|
> print(' '..str(x)..' is false/nil')
|
|
> teliva_num_test_failures = teliva_num_test_failures + 1
|
|
> -- overlay first test failure on editors
|
|
> if teliva_first_failure == nil then
|
|
> teliva_first_failure = msg
|
|
> end
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
check_eq:
|
|
>function check_eq(x, expected, msg)
|
|
> if eq(x, expected) then
|
|
> Window:addch('.')
|
|
> else
|
|
> print('F - '..msg)
|
|
> print(' expected '..str(expected)..' but got '..str(x))
|
|
> teliva_num_test_failures = teliva_num_test_failures + 1
|
|
> -- overlay first test failure on editors
|
|
> if teliva_first_failure == nil then
|
|
> teliva_first_failure = msg
|
|
> end
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
eq:
|
|
>function eq(a, b)
|
|
> if type(a) ~= type(b) then return false end
|
|
> if type(a) == 'table' then
|
|
> if #a ~= #b then return false end
|
|
> for k, v in pairs(a) do
|
|
> if b[k] ~= v then
|
|
> return false
|
|
> end
|
|
> return true
|
|
> end
|
|
> end
|
|
> return a == b
|
|
>end
|
|
- __teliva_timestamp: original
|
|
str:
|
|
>-- smarter tostring
|
|
>-- slow; used only for debugging
|
|
>function str(x)
|
|
> if type(x) == 'table' then
|
|
> local result = ''
|
|
> result = result..#x..'{'
|
|
> for k, v in pairs(x) do
|
|
> result = result..str(k)..'='..str(v)..', '
|
|
> end
|
|
> result = result..'}'
|
|
> return result
|
|
> elseif type(x) == 'string' then
|
|
> return '"'..x..'"'
|
|
> end
|
|
> return tostring(x)
|
|
>end
|
|
- __teliva_timestamp: original
|
|
map:
|
|
>-- only for arrays
|
|
>function map(l, f)
|
|
> result = {}
|
|
> for _, x in ipairs(l) do
|
|
> table.insert(result, f(x))
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
reduce:
|
|
>-- only for arrays
|
|
>function reduce(l, f, init)
|
|
> result = init
|
|
> for _, x in ipairs(l) do
|
|
> result = f(result, x)
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
filter:
|
|
>-- only for arrays
|
|
>function filter(l, f)
|
|
> result = {}
|
|
> for _, x in ipairs(l) do
|
|
> if f(x) then
|
|
> table.insert(result, x)
|
|
> end
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
find_index:
|
|
>function find_index(arr, x)
|
|
> for n, y in ipairs(arr) do
|
|
> if x == y then
|
|
> return n
|
|
> end
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
trim:
|
|
>function trim(s)
|
|
> return s:gsub('^%s*', ''):gsub('%s*$', '')
|
|
>end
|
|
- __teliva_timestamp: original
|
|
split:
|
|
>function split(s, d)
|
|
> result = {}
|
|
> for match in (s..d):gmatch("(.-)"..d) do
|
|
> table.insert(result, match);
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
sort_letters:
|
|
>function sort_letters(s)
|
|
> tmp = {}
|
|
> for i=1,#s do
|
|
> table.insert(tmp, s[i])
|
|
> end
|
|
> table.sort(tmp)
|
|
> local result = ''
|
|
> for _, c in pairs(tmp) do
|
|
> result = result..c
|
|
> end
|
|
> return result
|
|
>end
|
|
>
|
|
>function test_sort_letters(s)
|
|
> check_eq(sort_letters(''), '', 'test_sort_letters: empty')
|
|
> check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')
|
|
> check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')
|
|
>end
|
|
- __teliva_timestamp: original
|
|
count_letters:
|
|
>function count_letters(s)
|
|
> local result = {}
|
|
> for i=1,s:len() do
|
|
> local c = s[i]
|
|
> if result[c] == nil then
|
|
> result[c] = 1
|
|
> else
|
|
> result[c] = result[c] + 1
|
|
> end
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
append:
|
|
>-- concatenate list 'elems' into 'l', modifying 'l' in the process
|
|
>function append(l, elems)
|
|
> for i=1,#elems do
|
|
> l[#l+1] = elems[i]
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
menu:
|
|
>-- To show app-specific hotkeys in the menu bar, add hotkey/command
|
|
>-- arrays of strings to the menu array.
|
|
>menu = {}
|
|
- __teliva_timestamp: original
|
|
window:
|
|
>-- constructor for fake screen and window
|
|
>-- call it like this:
|
|
>-- local w = window{
|
|
>-- kbd=kbd('abc'),
|
|
>-- scr=scr{h=5, w=4},
|
|
>-- }
|
|
>-- eventually it'll do everything a real ncurses window can
|
|
>function window(h)
|
|
> h.__index = h
|
|
> setmetatable(h, h)
|
|
> h.__index = function(table, key)
|
|
> return rawget(h, key)
|
|
> end
|
|
> h.attrset = function(self, x)
|
|
> self.scr.attrs = x
|
|
> end
|
|
> h.attron = function(self, x)
|
|
> -- currently same as attrset since Lua 5.1 doesn't have bitwise operators
|
|
> -- doesn't support multiple attrs at once
|
|
>-- local old = self.scr.attrs
|
|
>-- self.scr.attrs = old|x
|
|
> self.scr.attrs = x
|
|
> end
|
|
> h.attroff = function(self, x)
|
|
> -- currently borked since Lua 5.1 doesn't have bitwise operators
|
|
> -- doesn't support multiple attrs at once
|
|
>-- local old = self.scr.attrs
|
|
>-- self.scr.attrs = old & (~x)
|
|
> self.scr.attrs = curses.A_NORMAL
|
|
> end
|
|
> h.getch = function(self)
|
|
> local c = table.remove(h.kbd, 1)
|
|
> if c == nil then return c end
|
|
> return string.byte(c) -- for verisimilitude with ncurses
|
|
> end
|
|
> h.addch = function(self, c)
|
|
> local scr = self.scr
|
|
> if c == '\n' then
|
|
> scr.cursy = scr.cursy+1
|
|
> scr.cursx = 0
|
|
> return
|
|
> end
|
|
> if scr.cursy <= scr.h then
|
|
> scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}
|
|
> scr.cursx = scr.cursx+1
|
|
> if scr.cursx > scr.w then
|
|
> scr.cursy = scr.cursy+1
|
|
> scr.cursx = 1
|
|
> end
|
|
> end
|
|
> end
|
|
> h.addstr = function(self, s)
|
|
> for i=1,s:len() do
|
|
> self:addch(s[i])
|
|
> end
|
|
> end
|
|
> h.mvaddch = function(self, y, x, c)
|
|
> self.scr.cursy = y
|
|
> self.scr.cursx = x
|
|
> self:addch(c)
|
|
> end
|
|
> h.mvaddstr = function(self, y, x, s)
|
|
> self.scr.cursy = y
|
|
> self.scr.cursx = x
|
|
> self:addstr(s)
|
|
> end
|
|
> h.clear = function(self)
|
|
> clear_scr(self.scr)
|
|
> end
|
|
> h.refresh = function(self)
|
|
> -- nothing
|
|
> end
|
|
> return h
|
|
>end
|
|
- __teliva_timestamp: original
|
|
kbd:
|
|
>function kbd(keys)
|
|
> local result = {}
|
|
> for i=1,keys:len() do
|
|
> table.insert(result, keys[i])
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
scr:
|
|
>function scr(props)
|
|
> props.cursx = 1
|
|
> props.cursy = 1
|
|
> clear_scr(props)
|
|
> return props
|
|
>end
|
|
- __teliva_timestamp: original
|
|
clear_scr:
|
|
>function clear_scr(props)
|
|
> props.cursy = 1
|
|
> props.cursx = 1
|
|
> for y=1,props.h do
|
|
> props[y] = {}
|
|
> for x=1,props.w do
|
|
> props[y][x] = {data=' ', attrs=curses.A_NORMAL}
|
|
> end
|
|
> end
|
|
> return props
|
|
>end
|
|
- __teliva_timestamp: original
|
|
check_screen:
|
|
>function check_screen(window, contents, message)
|
|
> local x, y = 1, 1
|
|
> for i=1,contents:len() do
|
|
> check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)
|
|
> x = x+1
|
|
> if x > window.scr.w then
|
|
> y = y+1
|
|
> x = 1
|
|
> end
|
|
> end
|
|
>end
|
|
>
|
|
>-- putting it all together, an example test of both keyboard and screen
|
|
>function test_check_screen()
|
|
> local lines = {
|
|
> c='123',
|
|
> d='234',
|
|
> a='345',
|
|
> b='456',
|
|
> }
|
|
> local w = window{
|
|
> kbd=kbd('abc'),
|
|
> scr=scr{h=3, w=5},
|
|
> }
|
|
> local y = 1
|
|
> while true do
|
|
> local b = w:getch()
|
|
> if b == nil then break end
|
|
> w:mvaddstr(y, 1, lines[string.char(b)])
|
|
> y = y+1
|
|
> end
|
|
> check_screen(w, '345 '..
|
|
> '456 '..
|
|
> '123 ',
|
|
> 'test_check_screen')
|
|
>end
|
|
- __teliva_timestamp: original
|
|
grid:
|
|
>-- main data structure
|
|
>grid = {}
|
|
>for i=1,lines*4 do
|
|
> grid[i] = {}
|
|
> for j=1,cols*2 do
|
|
> grid[i][j] = 0
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
Window:
|
|
>Window = curses.stdscr()
|
|
>-- animation-based app
|
|
>Window:nodelay(true)
|
|
>curses.curs_set(0)
|
|
>lines, cols = Window:getmaxyx()
|
|
- __teliva_timestamp: original
|
|
grid_char:
|
|
>-- grab a 4x2 chunk of grid
|
|
>function grid_char(line, col)
|
|
> result = {}
|
|
> for l, row in ipairs({unpack(grid, (line-1)*4+1, line*4)}) do
|
|
> result[l] = {unpack(row, (col-1)*2+1, col*2)}
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
print_grid_char:
|
|
>function print_grid_char(window, x)
|
|
> result = {}
|
|
> for l, row in ipairs(x) do
|
|
> for c, val in ipairs(row) do
|
|
> window:mvaddstr(l, c, val)
|
|
> end
|
|
> end
|
|
> return result
|
|
>end
|
|
- __teliva_timestamp: original
|
|
glyph:
|
|
>-- look up the braille pattern corresponding to a 4x2 chunk of grid
|
|
>-- https://en.wikipedia.org/wiki/Braille_Patterns
|
|
>-- not obviously programmatic because Unicode added 4x2 after 3x2
|
|
>glyph = {
|
|
> 0x2800, 0x2801, 0x2802, 0x2803, 0x2804, 0x2805, 0x2806, 0x2807, 0x2840, 0x2841, 0x2842, 0x2843, 0x2844, 0x2845, 0x2846, 0x2847,
|
|
> 0x2808, 0x2809, 0x280a, 0x280b, 0x280c, 0x280d, 0x280e, 0x280f, 0x2848, 0x2849, 0x284a, 0x284b, 0x284c, 0x284d, 0x284e, 0x284f,
|
|
> 0x2810, 0x2811, 0x2812, 0x2813, 0x2814, 0x2815, 0x2816, 0x2817, 0x2850, 0x2851, 0x2852, 0x2853, 0x2854, 0x2855, 0x2856, 0x2857,
|
|
> 0x2818, 0x2819, 0x281a, 0x281b, 0x281c, 0x281d, 0x281e, 0x281f, 0x2858, 0x2859, 0x285a, 0x285b, 0x285c, 0x285d, 0x285e, 0x285f,
|
|
> 0x2820, 0x2821, 0x2822, 0x2823, 0x2824, 0x2825, 0x2826, 0x2827, 0x2860, 0x2861, 0x2862, 0x2863, 0x2864, 0x2865, 0x2866, 0x2867,
|
|
> 0x2828, 0x2829, 0x282a, 0x282b, 0x282c, 0x282d, 0x282e, 0x282f, 0x2868, 0x2869, 0x286a, 0x286b, 0x286c, 0x286d, 0x286e, 0x286f,
|
|
> 0x2830, 0x2831, 0x2832, 0x2833, 0x2834, 0x2835, 0x2836, 0x2837, 0x2870, 0x2871, 0x2872, 0x2873, 0x2874, 0x2875, 0x2876, 0x2877,
|
|
> 0x2838, 0x2839, 0x283a, 0x283b, 0x283c, 0x283d, 0x283e, 0x283f, 0x2878, 0x2879, 0x287a, 0x287b, 0x287c, 0x287d, 0x287e, 0x287f,
|
|
>
|
|
> 0x2880, 0x2881, 0x2882, 0x2883, 0x2884, 0x2885, 0x2886, 0x2887, 0x28c0, 0x28c1, 0x28c2, 0x28c3, 0x28c4, 0x28c5, 0x28c6, 0x28c7,
|
|
> 0x2888, 0x2889, 0x288a, 0x288b, 0x288c, 0x288d, 0x288e, 0x288f, 0x28c8, 0x28c9, 0x28ca, 0x28cb, 0x28cc, 0x28cd, 0x28ce, 0x28cf,
|
|
> 0x2890, 0x2891, 0x2892, 0x2893, 0x2894, 0x2895, 0x2896, 0x2897, 0x28d0, 0x28d1, 0x28d2, 0x28d3, 0x28d4, 0x28d5, 0x28d6, 0x28d7,
|
|
> 0x2898, 0x2899, 0x289a, 0x289b, 0x289c, 0x289d, 0x289e, 0x289f, 0x28d8, 0x28d9, 0x28da, 0x28db, 0x28dc, 0x28dd, 0x28de, 0x28df,
|
|
> 0x28a0, 0x28a1, 0x28a2, 0x28a3, 0x28a4, 0x28a5, 0x28a6, 0x28a7, 0x28e0, 0x28e1, 0x28e2, 0x28e3, 0x28e4, 0x28e5, 0x28e6, 0x28e7,
|
|
> 0x28a8, 0x28a9, 0x28aa, 0x28ab, 0x28ac, 0x28ad, 0x28ae, 0x28af, 0x28e8, 0x28e9, 0x28ea, 0x28eb, 0x28ec, 0x28ed, 0x28ee, 0x28ef,
|
|
> 0x28b0, 0x28b1, 0x28b2, 0x28b3, 0x28b4, 0x28b5, 0x28b6, 0x28b7, 0x28f0, 0x28f1, 0x28f2, 0x28f3, 0x28f4, 0x28f5, 0x28f6, 0x28f7,
|
|
> 0x28b8, 0x28b9, 0x28ba, 0x28bb, 0x28bc, 0x28bd, 0x28be, 0x28bf, 0x28f8, 0x28f9, 0x28fa, 0x28fb, 0x28fc, 0x28fd, 0x28fe, 0x28ff,
|
|
>}
|
|
- __teliva_timestamp: original
|
|
utf8:
|
|
>-- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua
|
|
>function utf8(decimal)
|
|
> local bytemarkers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }
|
|
> if decimal<128 then return string.char(decimal) end
|
|
> local charbytes = {}
|
|
> for bytes,vals in ipairs(bytemarkers) do
|
|
> if decimal<=vals[1] then
|
|
> for b=bytes+1,2,-1 do
|
|
> local mod = decimal%64
|
|
> decimal = (decimal-mod)/64
|
|
> charbytes[b] = string.char(128+mod)
|
|
> end
|
|
> charbytes[1] = string.char(vals[2]+decimal)
|
|
> break
|
|
> end
|
|
> end
|
|
> return table.concat(charbytes)
|
|
>end
|
|
- __teliva_timestamp: original
|
|
grid_char_to_glyph_index:
|
|
>-- convert a chunk of grid into a number
|
|
>function grid_char_to_glyph_index(g)
|
|
> return g[1][1] + g[2][1]*2 + g[3][1]*4 + g[4][1]*8 +
|
|
> g[1][2]*16 + g[2][2]*32 + g[3][2]*64 + g[4][2]*128 +
|
|
> 1 -- 1-indexing
|
|
>end
|
|
- __teliva_timestamp: original
|
|
render:
|
|
>function render(window)
|
|
> window:clear()
|
|
> window:attron(curses.color_pair(1))
|
|
> for line=1,lines do
|
|
> for col=1,cols do
|
|
> window:addstr(utf8(glyph[grid_char_to_glyph_index(grid_char(line, col))]))
|
|
> end
|
|
> end
|
|
> window:attroff(curses.color_pair(1))
|
|
> window:refresh()
|
|
>end
|
|
- __teliva_timestamp: original
|
|
state:
|
|
>function state(line, col)
|
|
> if line < 1 or line > #grid or col < 1 or col > #grid[1] then
|
|
> return 0
|
|
> end
|
|
> return grid[line][col]
|
|
>end
|
|
- __teliva_timestamp: original
|
|
num_live_neighbors:
|
|
>function num_live_neighbors(line, col)
|
|
> return state(line-1, col-1) + state(line-1, col) + state(line-1, col+1) +
|
|
> state(line, col-1) + state(line, col+1) +
|
|
> state(line+1, col-1) + state(line+1, col) + state(line+1, col+1)
|
|
>end
|
|
- __teliva_timestamp: original
|
|
step:
|
|
>function step()
|
|
> local new_grid = {}
|
|
> for line=1,#grid do
|
|
> new_grid[line] = {}
|
|
> for col=1,#grid[1] do
|
|
> local n = num_live_neighbors(line, col)
|
|
> if n == 3 then
|
|
> new_grid[line][col] = 1
|
|
> elseif n == 2 then
|
|
> new_grid[line][col] = grid[line][col]
|
|
> else
|
|
> new_grid[line][col] = 0
|
|
> end
|
|
> end
|
|
> end
|
|
> grid = new_grid
|
|
>end
|
|
- __teliva_timestamp: original
|
|
sleep:
|
|
>function sleep(a)
|
|
> local sec = tonumber(os.clock() + a);
|
|
> while (os.clock() < sec) do
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
load_file:
|
|
>function load_file(window, fs, filename)
|
|
> local infile = start_reading(fs, filename)
|
|
> if infile == nil then return end
|
|
> local line_index = lines -- quarter of the way down in pixels
|
|
> while true do
|
|
> local line = infile.read()
|
|
> if line == nil then break end
|
|
> if line:sub(1,1) ~= '!' then -- comment; plaintext files can't have whitespace before comments
|
|
> local col_index = cols
|
|
> for c in line:gmatch(".") do
|
|
> if c == '\r' then break end -- DOS line ending
|
|
> if c == '.' then
|
|
> grid[line_index][col_index] = 0
|
|
> else
|
|
> grid[line_index][col_index] = 1
|
|
> end
|
|
> col_index = col_index+1
|
|
> end
|
|
> line_index = line_index+1
|
|
> end
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
update:
|
|
>menu = {{"arrow", "pan"}}
|
|
>
|
|
>function update(window, c)
|
|
> if c == curses.KEY_LEFT then
|
|
> for i=1,lines*4 do
|
|
> for j=2,cols*2 do
|
|
> grid[i][j-1] = grid[i][j]
|
|
> end
|
|
> grid[i][cols*2] = 0
|
|
> end
|
|
> elseif c == curses.KEY_DOWN then
|
|
> for i=lines*4-1,1,-1 do
|
|
> for j=1,cols*2 do
|
|
> grid[i+1][j] = grid[i][j]
|
|
> end
|
|
> end
|
|
> for j=1,cols*2 do
|
|
> grid[1][j] = 0
|
|
> end
|
|
> elseif c == curses.KEY_UP then
|
|
> for i=2,lines*4 do
|
|
> for j=1,cols*2 do
|
|
> grid[i-1][j] = grid[i][j]
|
|
> end
|
|
> end
|
|
> for j=1,cols*2 do
|
|
> grid[lines*4][j] = 0
|
|
> end
|
|
> elseif c == curses.KEY_RIGHT then
|
|
> for i=1,lines*4 do
|
|
> for j=cols*2-1,1,-1 do
|
|
> grid[i][j+1] = grid[i][j]
|
|
> end
|
|
> grid[i][1] = 0
|
|
> end
|
|
> end
|
|
>end
|
|
- __teliva_timestamp: original
|
|
main:
|
|
>function main()
|
|
> curses.init_pair(1, 22, 189)
|
|
>
|
|
> -- initialize grid based on commandline args
|
|
> if (#arg == 0) then
|
|
> -- by default, start from a deterministically random state
|
|
> for i=1,lines*4 do
|
|
> for j=1,cols*2 do
|
|
> grid[i][j] = math.random(0, 1)
|
|
> end
|
|
> end
|
|
> elseif arg[1] == "random" then
|
|
> -- start from a non-deterministically random start state
|
|
> math.randomseed(os.time())
|
|
> for i=1,lines*4 do
|
|
> for j=1,cols*2 do
|
|
> grid[i][j] = math.random(0, 1)
|
|
> end
|
|
> end
|
|
> -- shortcuts for some common patterns
|
|
> elseif arg[1] == "pentomino" then
|
|
> -- https://www.conwaylife.com/wiki/Pentomino
|
|
> grid[83][172] = 1
|
|
> grid[83][173] = 1
|
|
> grid[84][173] = 1
|
|
> grid[84][174] = 1
|
|
> grid[85][173] = 1
|
|
> elseif arg[1] == "glider" then
|
|
> -- https://www.conwaylife.com/wiki/Glider
|
|
> grid[5][4] = 1
|
|
> grid[6][5] = 1
|
|
> grid[7][3] = 1
|
|
> grid[7][4] = 1
|
|
> grid[7][5] = 1
|
|
> elseif arg[1] == "blinker" then
|
|
> -- https://www.conwaylife.com/wiki/Blinker
|
|
> grid[7][3] = 1
|
|
> grid[7][4] = 1
|
|
> grid[7][5] = 1
|
|
> elseif arg[1] == "block" then
|
|
> -- https://www.conwaylife.com/wiki/Block
|
|
> grid[5][4] = 1
|
|
> grid[5][5] = 1
|
|
> grid[6][4] = 1
|
|
> grid[6][5] = 1
|
|
> elseif arg[1] == "loaf" then
|
|
> -- https://www.conwaylife.com/wiki/Loaf
|
|
> grid[5][4] = 1
|
|
> grid[5][5] = 1
|
|
> grid[6][6] = 1
|
|
> grid[7][6] = 1
|
|
> grid[8][5] = 1
|
|
> grid[7][4] = 1
|
|
> grid[6][3] = 1
|
|
> else
|
|
> -- Load a file in the standard "plaintext" format: https://www.conwaylife.com/wiki/Plaintext
|
|
> --
|
|
> -- Each pattern page at https://www.conwaylife.com/wiki provides its
|
|
> -- plaintext representation in a block called "Pattern Files" on the right.
|
|
> --
|
|
> -- For example, check out the list of Important Patterns at
|
|
> -- https://www.conwaylife.com/wiki/Category:Patterns_with_Catagolue_frequency_class_0
|
|
> load_file(Window, nil, arg[1])
|
|
> end
|
|
>
|
|
> -- main loop
|
|
> while true do
|
|
> render(Window)
|
|
> c = Window:getch()
|
|
> update(Window, c)
|
|
> step()
|
|
> end
|
|
>end
|
|
- __teliva_timestamp:
|
|
>Thu Feb 17 19:58:19 2022
|
|
doc:blurb:
|
|
>Conway's Game of Life
|
|
>
|
|
>To get around limitations of text mode we use the braille character set to render 8 cells per character.
|
|
>
|
|
>By default it initializes the space with a random state of cells. You can also start it up with .cells files from https://conwaylife.com/wiki, or with a few special names:
|
|
> $ src/teliva life.tlv block
|
|
> $ src/teliva life.tlv loaf
|
|
> $ src/teliva life.tlv blinker
|
|
> $ src/teliva life.tlv glider
|
|
> $ src/teliva life.tlv pentomino
|