diff --git a/advent.tlv b/advent.tlv index 61cf3d9..5a7f5be 100644 --- a/advent.tlv +++ b/advent.tlv @@ -1,253 +1,200 @@ -teliva_program = { - { - __teliva_timestamp = [==[ -original]==], - norm = [==[ -function norm() - window:attrset(curses.A_NORMAL) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - bold = [==[ -function bold() - window:attron(curses.A_BOLD) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - rv = [==[ -function rv() - window:attron(curses.A_REVERSE) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - color = [==[ -function color(i) - window:attron(curses.color_pair(i)) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - abbreviations = [==[ -clear = curses.clear -refresh = curses.refresh -getch = curses.getch -addch = curses.addch -mvaddch = curses.mvaddch -pr = curses.addstr -mpr = curses.mvaddstr -str = tostring -num = tonumber -]==], - }, - { - __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 string.sub(str,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 string.sub(str,i,j) - else - local t={} - for k,v in ipairs(i) do - t[k]=string.sub(str,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 - --- TODO: backport utf-8 support from Lua 5.3 -]==], - }, - { - __teliva_timestamp = [==[ -original]==], - prn = [==[ --- functional form of 'print' --- use this in map/reduce/filter -function prn(...) - print(unpack(arg)) - return arg[1] -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - add = [==[ -add = table.insert]==], - }, - { - __teliva_timestamp = [==[ -original]==], - map = [==[ --- only for arrays -function map(l, f) - result = {} - for _, x in q(l) do - add(result, f(x)) - end - return result -end -]==], - }, - { - __teliva_timestamp = [==[ -original]==], - reduce = [==[ --- only for arrays -function reduce(l, f, init) - result = init - for _, x in q(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 q(l) do - if f(x) then - add(result, x) - end - end - return result -end -]==], - }, - { - __teliva_timestamp = [==[ -original]==], - find_index = [==[ -function find_index(arr, x) - for n, y in q(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 - add(result, match); - end - return result -end -]==], - }, - { - __teliva_timestamp = [==[ -original]==], - window = [==[ -window = curses.stdscr()]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render = [==[ -function render(window) - clear() - -- draw stuff to screen here - for line in io.lines("input") do - pr(line) - pr("\n") - end - refresh() -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - menu = [==[ -menu = {}]==], - }, - { - __teliva_timestamp = [==[ -original]==], - update = [==[ -function update(window) - local key = curses.getch() - -- process key here -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - init_colors = [==[ -function init_colors() - for i=0,7 do - curses.init_pair(i, i, -1) - end - curses.init_pair(8, 7, 0) - curses.init_pair(9, 7, 1) - curses.init_pair(10, 7, 2) - curses.init_pair(11, 7, 3) - curses.init_pair(12, 7, 4) - curses.init_pair(13, 7, 5) - curses.init_pair(14, 7, 6) - curses.init_pair(15, -1, 15) - curses.init_pair(255, 15, 1) -- reserved for Teliva error messages -end]==], - }, - { - main = [==[ -function main() - init_colors() - - while true do - render(window) - update(window) - end -end -]==], - __teliva_timestamp = [==[ -original]==], - }, -} +# .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 + norm: + >function norm() + > window:attrset(curses.A_NORMAL) + >end +- __teliva_timestamp: original + bold: + >function bold() + > window:attron(curses.A_BOLD) + >end +- __teliva_timestamp: original + rv: + >function rv() + > window:attron(curses.A_REVERSE) + >end +- __teliva_timestamp: original + color: + >function color(i) + > window:attron(curses.color_pair(i)) + >end +- __teliva_timestamp: original + abbreviations: + >clear = curses.clear + >refresh = curses.refresh + >getch = curses.getch + >addch = curses.addch + >mvaddch = curses.mvaddch + >pr = curses.addstr + >mpr = curses.mvaddstr + >str = tostring + >num = tonumber +- __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 string.sub(str,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 string.sub(str,i,j) + > else + > local t={} + > for k,v in ipairs(i) do + > t[k]=string.sub(str,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 + > + >-- TODO: backport utf-8 support from Lua 5.3 +- __teliva_timestamp: original + prn: + >-- functional form of 'print' + >-- use this in map/reduce/filter + >function prn(...) + > print(unpack(arg)) + > return arg[1] + >end +- __teliva_timestamp: original + add: + >add = table.insert +- __teliva_timestamp: original + map: + >-- only for arrays + >function map(l, f) + > result = {} + > for _, x in q(l) do + > add(result, f(x)) + > end + > return result + >end +- __teliva_timestamp: original + reduce: + >-- only for arrays + >function reduce(l, f, init) + > result = init + > for _, x in q(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 q(l) do + > if f(x) then + > add(result, x) + > end + > end + > return result + >end +- __teliva_timestamp: original + find_index: + >function find_index(arr, x) + > for n, y in q(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 + > add(result, match); + > end + > return result + >end +- __teliva_timestamp: original + window: + >window = curses.stdscr() +- __teliva_timestamp: original + render: + >function render(window) + > clear() + > -- draw stuff to screen here + > for line in io.lines("input") do + > pr(line) + > pr("\n") + > end + > refresh() + >end +- __teliva_timestamp: original + menu: + >menu = {} +- __teliva_timestamp: original + update: + >function update(window) + > local key = curses.getch() + > -- process key here + >end +- __teliva_timestamp: original + init_colors: + >function init_colors() + > for i=0,7 do + > curses.init_pair(i, i, -1) + > end + > curses.init_pair(8, 7, 0) + > curses.init_pair(9, 7, 1) + > curses.init_pair(10, 7, 2) + > curses.init_pair(11, 7, 3) + > curses.init_pair(12, 7, 4) + > curses.init_pair(13, 7, 5) + > curses.init_pair(14, 7, 6) + > curses.init_pair(15, -1, 15) + > curses.init_pair(255, 15, 1) -- reserved for Teliva error messages + >end +- main: + >function main() + > init_colors() + > + > while true do + > render(window) + > update(window) + > end + >end + __teliva_timestamp: original diff --git a/chesstv.tlv b/chesstv.tlv index e64b3e2..36b1a7e 100644 --- a/chesstv.tlv +++ b/chesstv.tlv @@ -1,298 +1,263 @@ -teliva_program = { - { - __teliva_timestamp = [==[ -original]==], - window = [==[ -window = curses.stdscr() --- animation-based app -window:nodelay(true) -lines, cols = window:getmaxyx()]==], - }, - { - __teliva_timestamp = [==[ -original]==], - current_game = [==[ -current_game = {}]==], - }, - { - __teliva_timestamp = [==[ -original]==], - piece_glyph = [==[ -piece_glyph = { - -- for legibility, white pieces also use unicode glyphs for black pieces - -- we rely on colors to distinguish them - K = 0x265a, - Q = 0x265b, - R = 0x265c, - B = 0x265d, - N = 0x265e, - P = 0x265f, - k = 0x265a, - q = 0x265b, - r = 0x265c, - b = 0x265d, - n = 0x265e, - p = 0x265f, -}]==], - }, - { - __teliva_timestamp = [==[ -original]==], - top_player = [==[ -function top_player(current_game) - if current_game.players[1].color == "black" then - return current_game.players[1] - end - return current_game.players[2] -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - bottom_player = [==[ -function bottom_player(current_game) - if current_game.players[1].color == "white" then - return current_game.players[1] - end - return current_game.players[2] -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_player = [==[ -function render_player(y, x, player) - curses.mvaddstr(y, x, player.user.name) - curses.addstr(" · ") - curses.addstr(tostring(player.rating)) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_square = [==[ -function render_square(current_game, rank, file, highlighted_squares) - -- decide whether to highlight - local hl = 0 - if (rank == highlighted_squares.from.rank and file == highlighted_squares.from.file) - or (rank == highlighted_squares.to.rank and file == highlighted_squares.to.file) then - hl = 4 - end - if (rank+file)%2 == 1 then - -- light square - curses.attrset(curses.color_pair(1+hl)) - else - -- dark square - curses.attrset(curses.color_pair(3+hl)) - end - curses.mvaddstr((8 - rank + 1)*3, file*5, " ") - curses.mvaddstr((8 - rank + 1)*3+1, file*5, " ") - curses.mvaddstr((8 - rank + 1)*3+2, file*5, " ") - curses.attrset(curses.A_NORMAL) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_fen_rank = [==[ -function render_fen_rank(rank, fen_rank, highlighted_squares) - local file = 1 - for x in fen_rank:gmatch(".") do - if x:match("%d") then - file = file + tonumber(x) - else - -- decide whether to highlight - local hl = 0 - if (rank == highlighted_squares.from.rank and file == highlighted_squares.from.file) - or (rank == highlighted_squares.to.rank and file == highlighted_squares.to.file) then - hl = 4 - end - if (rank+file)%2 == 1 then - if x < 'Z' then - -- white piece on light square - curses.attrset(curses.color_pair(1+hl)) - else - -- black piece on light square - curses.attrset(curses.color_pair(2+hl)) - end - else - if x < 'Z' then - -- white piece on dark square - curses.attrset(curses.color_pair(3+hl)) - else - -- black piece on dark square - curses.attrset(curses.color_pair(4+hl)) - end - end - curses.mvaddstr((8 - rank + 1)*3+1, file*5+2, utf8(piece_glyph[x])) - curses.attrset(curses.A_NORMAL) - file = file + 1 - end - end -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_time = [==[ -function render_time(y, x, seconds) - if seconds == nil then return end - curses.mvaddstr(y, x, tostring(math.floor(seconds/60))) - curses.addstr(string.format(":%02d", seconds%60)) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_board = [==[ -function render_board(current_game) ---? curses.mvaddstr(1, 50, dump(current_game.fen)) ---? curses.mvaddstr(6, 50, dump(current_game.previously_moved_squares)) - render_player(2, 5, top_player(current_game)) - render_time(2, 35, current_game.bc) - for rank=8,1,-1 do - for file=1,8 do - render_square(current_game, rank, file, current_game.previously_moved_squares) - end - render_fen_rank(rank, current_game.fen_rank[8-rank+1], current_game.previously_moved_squares) - end - render_player(27, 5, bottom_player(current_game)) - render_time(27, 35, current_game.wc) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - parse_lm = [==[ -function parse_lm(move) ---? curses.mvaddstr(4, 50, move) - local file1 = string.byte(move:sub(1, 1)) - 96 -- 'a'-1 - local rank1 = string.byte(move:sub(2, 2)) - 48 -- '0' - local file2 = string.byte(move:sub(3, 3)) - 96 -- 'a'-1 - local rank2 = string.byte(move:sub(4, 4)) - 48 -- '0' ---? curses.mvaddstr(5, 50, dump({{rank1, file1}, {rank2, file2}})) - return {from={rank=rank1, file=file1}, to={rank=rank2, file=file2}} -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render = [==[ -function render(chunk) - local o = json.decode(chunk) - if o.t == "featured" then - current_game = o.d ---? current_game.lm = "__" - current_game.previously_moved_squares = {from={rank=0, file=0}, to={rank=0, file=0}} -- no highlight - else - current_game.fen = o.d.fen - current_game.wc = o.d.wc - current_game.bc = o.d.bc ---? current_game.lm = o.d.lm - current_game.previously_moved_squares = parse_lm(o.d.lm) ---? window:nodelay(false) ---? curses.mvaddstr(3, 50, "paused") - end - current_game.fen_rank = split(current_game.fen, "%w+") - render_board(current_game) - curses.refresh() -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - init_colors = [==[ -function init_colors() - -- colors - local light_piece = 1 - local dark_piece = 0 - local light_square = 252 - local dark_square = 242 - local light_last_moved_square = 229 - local dark_last_moved_square = 226 - -- initialize colors - curses.init_pair(1, light_piece, light_square) - curses.init_pair(2, dark_piece, light_square) - curses.init_pair(3, light_piece, dark_square) - curses.init_pair(4, dark_piece, dark_square) - curses.init_pair(5, light_piece, light_last_moved_square) - curses.init_pair(6, dark_piece, light_last_moved_square) - curses.init_pair(7, light_piece, dark_last_moved_square) - curses.init_pair(8, dark_piece, dark_last_moved_square) - curses.init_pair(255, 15, 1) -- reserved for Teliva error messages -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - main = [==[ -function main() - init_colors() - local request = { - url = "https://lichess.org/api/tv/feed", - sink = function(chunk, err) - if chunk then - curses.clear() - render(chunk) - curses.getch() - end - return 1 - end, - } - http.request(request) -end]==], - }, - { - __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]==], - split = [==[ -function split(s, pat) - result = {} - for x in s:gmatch(pat) do - table.insert(result, x) - end - return result -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - dump = [==[ --- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console -function dump(o) - if type(o) == 'table' then - local s = '{ ' - for k,v in pairs(o) do - if type(k) ~= 'number' then k = '"'..k..'"' end - s = s .. '['..k..'] = ' .. dump(v) .. ',' - end - return s .. '} ' - else - return tostring(o) - end -end]==], - }, -} +# .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 + window: + >window = curses.stdscr() + >-- animation-based app + >window:nodelay(true) + >lines, cols = window:getmaxyx() +- __teliva_timestamp: original + current_game: + >current_game = {} +- __teliva_timestamp: original + piece_glyph: + >piece_glyph = { + > -- for legibility, white pieces also use unicode glyphs for black pieces + > -- we rely on colors to distinguish them + > K = 0x265a, + > Q = 0x265b, + > R = 0x265c, + > B = 0x265d, + > N = 0x265e, + > P = 0x265f, + > k = 0x265a, + > q = 0x265b, + > r = 0x265c, + > b = 0x265d, + > n = 0x265e, + > p = 0x265f, + >} +- __teliva_timestamp: original + top_player: + >function top_player(current_game) + > if current_game.players[1].color == "black" then + > return current_game.players[1] + > end + > return current_game.players[2] + >end +- __teliva_timestamp: original + bottom_player: + >function bottom_player(current_game) + > if current_game.players[1].color == "white" then + > return current_game.players[1] + > end + > return current_game.players[2] + >end +- __teliva_timestamp: original + render_player: + >function render_player(y, x, player) + > curses.mvaddstr(y, x, player.user.name) + > curses.addstr(" · ") + > curses.addstr(tostring(player.rating)) + >end +- __teliva_timestamp: original + render_square: + >function render_square(current_game, rank, file, highlighted_squares) + > -- decide whether to highlight + > local hl = 0 + > if (rank == highlighted_squares.from.rank and file == highlighted_squares.from.file) + > or (rank == highlighted_squares.to.rank and file == highlighted_squares.to.file) then + > hl = 4 + > end + > if (rank+file)%2 == 1 then + > -- light square + > curses.attrset(curses.color_pair(1+hl)) + > else + > -- dark square + > curses.attrset(curses.color_pair(3+hl)) + > end + > curses.mvaddstr((8 - rank + 1)*3, file*5, " ") + > curses.mvaddstr((8 - rank + 1)*3+1, file*5, " ") + > curses.mvaddstr((8 - rank + 1)*3+2, file*5, " ") + > curses.attrset(curses.A_NORMAL) + >end +- __teliva_timestamp: original + render_fen_rank: + >function render_fen_rank(rank, fen_rank, highlighted_squares) + > local file = 1 + > for x in fen_rank:gmatch(".") do + > if x:match("%d") then + > file = file + tonumber(x) + > else + > -- decide whether to highlight + > local hl = 0 + > if (rank == highlighted_squares.from.rank and file == highlighted_squares.from.file) + > or (rank == highlighted_squares.to.rank and file == highlighted_squares.to.file) then + > hl = 4 + > end + > if (rank+file)%2 == 1 then + > if x < 'Z' then + > -- white piece on light square + > curses.attrset(curses.color_pair(1+hl)) + > else + > -- black piece on light square + > curses.attrset(curses.color_pair(2+hl)) + > end + > else + > if x < 'Z' then + > -- white piece on dark square + > curses.attrset(curses.color_pair(3+hl)) + > else + > -- black piece on dark square + > curses.attrset(curses.color_pair(4+hl)) + > end + > end + > curses.mvaddstr((8 - rank + 1)*3+1, file*5+2, utf8(piece_glyph[x])) + > curses.attrset(curses.A_NORMAL) + > file = file + 1 + > end + > end + >end +- __teliva_timestamp: original + render_time: + >function render_time(y, x, seconds) + > if seconds == nil then return end + > curses.mvaddstr(y, x, tostring(math.floor(seconds/60))) + > curses.addstr(string.format(":%02d", seconds%60)) + >end +- __teliva_timestamp: original + render_board: + >function render_board(current_game) + >--? curses.mvaddstr(1, 50, dump(current_game.fen)) + >--? curses.mvaddstr(6, 50, dump(current_game.previously_moved_squares)) + > render_player(2, 5, top_player(current_game)) + > render_time(2, 35, current_game.bc) + > for rank=8,1,-1 do + > for file=1,8 do + > render_square(current_game, rank, file, current_game.previously_moved_squares) + > end + > render_fen_rank(rank, current_game.fen_rank[8-rank+1], current_game.previously_moved_squares) + > end + > render_player(27, 5, bottom_player(current_game)) + > render_time(27, 35, current_game.wc) + >end +- __teliva_timestamp: original + parse_lm: + >function parse_lm(move) + >--? curses.mvaddstr(4, 50, move) + > local file1 = string.byte(move:sub(1, 1)) - 96 -- 'a'-1 + > local rank1 = string.byte(move:sub(2, 2)) - 48 -- '0' + > local file2 = string.byte(move:sub(3, 3)) - 96 -- 'a'-1 + > local rank2 = string.byte(move:sub(4, 4)) - 48 -- '0' + >--? curses.mvaddstr(5, 50, dump({{rank1, file1}, {rank2, file2}})) + > return {from={rank=rank1, file=file1}, to={rank=rank2, file=file2}} + >end +- __teliva_timestamp: original + render: + >function render(chunk) + > local o = json.decode(chunk) + > if o.t == "featured" then + > current_game = o.d + >--? current_game.lm = "__" + > current_game.previously_moved_squares = {from={rank=0, file=0}, to={rank=0, file=0}} -- no highlight + > else + > current_game.fen = o.d.fen + > current_game.wc = o.d.wc + > current_game.bc = o.d.bc + >--? current_game.lm = o.d.lm + > current_game.previously_moved_squares = parse_lm(o.d.lm) + >--? window:nodelay(false) + >--? curses.mvaddstr(3, 50, "paused") + > end + > current_game.fen_rank = split(current_game.fen, "%w+") + > render_board(current_game) + > curses.refresh() + >end +- __teliva_timestamp: original + init_colors: + >function init_colors() + > -- colors + > local light_piece = 1 + > local dark_piece = 0 + > local light_square = 252 + > local dark_square = 242 + > local light_last_moved_square = 229 + > local dark_last_moved_square = 226 + > -- initialize colors + > curses.init_pair(1, light_piece, light_square) + > curses.init_pair(2, dark_piece, light_square) + > curses.init_pair(3, light_piece, dark_square) + > curses.init_pair(4, dark_piece, dark_square) + > curses.init_pair(5, light_piece, light_last_moved_square) + > curses.init_pair(6, dark_piece, light_last_moved_square) + > curses.init_pair(7, light_piece, dark_last_moved_square) + > curses.init_pair(8, dark_piece, dark_last_moved_square) + > curses.init_pair(255, 15, 1) -- reserved for Teliva error messages + >end +- __teliva_timestamp: original + main: + >function main() + > init_colors() + > local request = { + > url = "https://lichess.org/api/tv/feed", + > sink = function(chunk, err) + > if chunk then + > curses.clear() + > render(chunk) + > curses.getch() + > end + > return 1 + > end, + > } + > http.request(request) + >end +- __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 + split: + >function split(s, pat) + > result = {} + > for x in s:gmatch(pat) do + > table.insert(result, x) + > end + > return result + >end +- __teliva_timestamp: original + dump: + >-- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console + >function dump(o) + > if type(o) == 'table' then + > local s = '{ ' + > for k,v in pairs(o) do + > if type(k) ~= 'number' then k = '"'..k..'"' end + > s = s .. '['..k..'] = ' .. dump(v) .. ',' + > end + > return s .. '} ' + > else + > return tostring(o) + > end + >end diff --git a/counter.tlv b/counter.tlv index 0c16d75..a4b41a5 100644 --- a/counter.tlv +++ b/counter.tlv @@ -1,62 +1,61 @@ -teliva_program = { - { - __teliva_timestamp = [==[ -original]==], - window = [==[ -window = curses.stdscr()]==], - }, - { - __teliva_timestamp = [==[ -original]==], - n = [==[ -n = 0]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render = [==[ -function render(window) - window:clear() - window:attron(curses.A_BOLD) - window:attron(curses.color_pair(6)) - window:mvaddstr(10, 10, " ") - window:mvaddstr(10, 11, n) - window:attroff(curses.color_pair(6)) - window:attroff(curses.A_BOLD) - curses.refresh() -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - menu = [==[ -menu = {Enter="increment"}]==], - }, - { - __teliva_timestamp = [==[ -original]==], - update = [==[ -function update(window) - local key = curses.getch() - if key == 10 then - n = n+1 - end -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - main = [==[ -function main() - for i=1,7 do - curses.init_pair(i, 0, i) - end - curses.init_pair(255, 15, 1) -- reserved for Teliva error messages - - while true do - render(window) - update(window) - end -end]==], - }, -} +# .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 + window: + >window = curses.stdscr() +- __teliva_timestamp: original + n: + >n = 0 +- __teliva_timestamp: original + render: + >function render(window) + > window:clear() + > window:attron(curses.A_BOLD) + > window:attron(curses.color_pair(6)) + > window:mvaddstr(10, 10, " ") + > window:mvaddstr(10, 11, n) + > window:attroff(curses.color_pair(6)) + > window:attroff(curses.A_BOLD) + > curses.refresh() + >end + __teliva_note: foo +- __teliva_timestamp: original + menu: + >menu = {Enter="increment"} +- __teliva_timestamp: original + update: + >function update(window) + > local key = curses.getch() + > if key == 10 then + > n = n+1 + > end + >end +- __teliva_timestamp: original + main: + >function main() + > for i=1,7 do + > curses.init_pair(i, 0, i) + > end + > curses.init_pair(255, 15, 1) -- reserved for Teliva error messages + > + > while true do + > render(window) + > update(window) + > end + >end diff --git a/hanoi.tlv b/hanoi.tlv index 23ac9d5..79326da 100644 --- a/hanoi.tlv +++ b/hanoi.tlv @@ -1,144 +1,123 @@ -teliva_program = { - { - __teliva_timestamp = [==[ -original]==], - render = [==[ -function render(window) - window:clear() - local lines, cols = window:getmaxyx() - local line = math.floor(lines/2) - local col = math.floor(cols/4) - for i,t in ipairs(tower) do - render_tower(window, line, i*col, i, t) - end - curses.refresh() -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - lines = [==[ -function lines(window) - local lines, cols = window:getmaxyx() - return lines -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - pop = [==[ -function pop(array) - return table.remove(array) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - window = [==[ -window = curses.stdscr()]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_tower = [==[ -function render_tower(window, line, col, tower_index, tower) - window:attron(curses.A_BOLD) - window:mvaddch(line+2, col, string.char(96+tower_index)) - window:attroff(curses.A_BOLD) - window:attron(curses.color_pair(15)) - window:mvaddstr(line+1, col-6, " ") - window:attroff(curses.color_pair(15)) - for i, n in ipairs(tower) do - render_disk(window, line, col, n) - line = line - 1 - end - for i=1,5-len(tower)+1 do - window:attron(curses.color_pair(15)) - window:mvaddstr(line, col, " ") - window:attroff(curses.color_pair(15)) - line = line - 1 - end -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - tower = [==[ -tower = {{6, 5, 4, 3, 2}, {}, {}}]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render_disk = [==[ -function render_disk(window, line, col, size) - col = col-size+1 - for i=1,size do - window:attron(curses.color_pair(size)) - window:mvaddstr(line, col, " ") - window:attroff(curses.color_pair(size)) - col = col+2 - end -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - main = [==[ -function main() - curses.assume_default_colors(250, 139) - for i=1,7 do - curses.init_pair(i, 0, i) - end - curses.init_pair(15, 0, 250) -- tower frames - curses.init_pair(255, 15, 1) -- reserved for Teliva error messages - - while true do - render(window) - update(window) - end -end -]==], - }, - { - __teliva_timestamp = [==[ -original]==], - len = [==[ -function len(array) - local result = 0 - for k in pairs(array) do - result = result+1 - end - return result -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - update = [==[ -function update(window) - window:mvaddstr(lines(window)-2, 5, "tower to remove top disk from? ") - local from = curses.getch() - 96 - window:mvaddstr(lines(window)-1, 5, "tower to stack it on? ") - local to = curses.getch() - 96 - make_move(from, to) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - make_move = [==[ -function make_move(from, to) - local disk = pop(tower[from]) - table.insert(tower[to], disk) -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - cols = [==[ -function cols(window) - local lines, cols = window:getmaxyx() - return cols -end]==], - }, -} +# .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 + render: + >function render(window) + > window:clear() + > local lines, cols = window:getmaxyx() + > local line = math.floor(lines/2) + > local col = math.floor(cols/4) + > for i,t in ipairs(tower) do + > render_tower(window, line, i*col, i, t) + > end + > curses.refresh() + >end +- __teliva_timestamp: original + lines: + >function lines(window) + > local lines, cols = window:getmaxyx() + > return lines + >end +- __teliva_timestamp: original + pop: + >function pop(array) + > return table.remove(array) + >end +- __teliva_timestamp: original + window: + >window = curses.stdscr() +- __teliva_timestamp: original + render_tower: + >function render_tower(window, line, col, tower_index, tower) + > window:attron(curses.A_BOLD) + > window:mvaddch(line+2, col, string.char(96+tower_index)) + > window:attroff(curses.A_BOLD) + > window:attron(curses.color_pair(15)) + > window:mvaddstr(line+1, col-6, " ") + > window:attroff(curses.color_pair(15)) + > for i, n in ipairs(tower) do + > render_disk(window, line, col, n) + > line = line - 1 + > end + > for i=1,5-len(tower)+1 do + > window:attron(curses.color_pair(15)) + > window:mvaddstr(line, col, " ") + > window:attroff(curses.color_pair(15)) + > line = line - 1 + > end + >end +- __teliva_timestamp: original + tower: + >tower = {{6, 5, 4, 3, 2}, {}, {}} +- __teliva_timestamp: original + render_disk: + >function render_disk(window, line, col, size) + > col = col-size+1 + > for i=1,size do + > window:attron(curses.color_pair(size)) + > window:mvaddstr(line, col, " ") + > window:attroff(curses.color_pair(size)) + > col = col+2 + > end + >end +- __teliva_timestamp: original + main: + >function main() + > curses.assume_default_colors(250, 139) + > for i=1,7 do + > curses.init_pair(i, 0, i) + > end + > curses.init_pair(15, 0, 250) -- tower frames + > curses.init_pair(255, 15, 1) -- reserved for Teliva error messages + > + > while true do + > render(window) + > update(window) + > end + >end +- __teliva_timestamp: original + len: + >function len(array) + > local result = 0 + > for k in pairs(array) do + > result = result+1 + > end + > return result + >end +- __teliva_timestamp: original + update: + >function update(window) + > window:mvaddstr(lines(window)-2, 5, "tower to remove top disk from? ") + > local from = curses.getch() - 96 + > window:mvaddstr(lines(window)-1, 5, "tower to stack it on? ") + > local to = curses.getch() - 96 + > make_move(from, to) + >end +- __teliva_timestamp: original + make_move: + >function make_move(from, to) + > local disk = pop(tower[from]) + > table.insert(tower[to], disk) + >end +- __teliva_timestamp: original + cols: + >function cols(window) + > local lines, cols = window:getmaxyx() + > return cols + >end diff --git a/life.tlv b/life.tlv index ba4f1e7..2abfd08 100644 --- a/life.tlv +++ b/life.tlv @@ -1,342 +1,308 @@ -teliva_program = { - { - __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) -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() - curses.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 - curses.attroff(curses.color_pair(1)) - curses.refresh() -end -]==], - }, - { - __teliva_timestamp = [==[ -original]==], - state = [==[ -function state(line, col) - if line < 1 or line > table.getn(grid) or col < 1 or col > table.getn(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,table.getn(grid) do - new_grid[line] = {} - for col=1,table.getn(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]==], - file_exists = [==[ -function file_exists(filename) - local f = io.open(filename, "r") - if f ~= nil then - io.close(f) - return true - else - return false - end -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - load_file = [==[ -function load_file(window, filename) - io.input(filename) - local line_index = lines - for line in io.lines() do - 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(255, 15, 1) -- reserved for Teliva error messages - 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 - elseif file_exists(arg[1]) then - -- 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, arg[1]) - end - - -- main loop - while true do - render(window) - c = curses.getch() - update(window, c) - step() - end -end]==], - }, -} +# .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 + 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) + >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() + > curses.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 + > curses.attroff(curses.color_pair(1)) + > curses.refresh() + >end +- __teliva_timestamp: original + state: + >function state(line, col) + > if line < 1 or line > table.getn(grid) or col < 1 or col > table.getn(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,table.getn(grid) do + > new_grid[line] = {} + > for col=1,table.getn(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 + file_exists: + >function file_exists(filename) + > local f = io.open(filename, "r") + > if f ~= nil then + > io.close(f) + > return true + > else + > return false + > end + >end +- __teliva_timestamp: original + load_file: + >function load_file(window, filename) + > io.input(filename) + > local line_index = lines + > for line in io.lines() do + > 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(255, 15, 1) -- reserved for Teliva error messages + > 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 + > elseif file_exists(arg[1]) then + > -- 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, arg[1]) + > end + > + > -- main loop + > while true do + > render(window) + > c = curses.getch() + > update(window, c) + > step() + > end + >end diff --git a/src/tlv.c b/src/tlv.c index adf21c3..5800418 100644 --- a/src/tlv.c +++ b/src/tlv.c @@ -89,7 +89,9 @@ static void teliva_load_definition(lua_State* L, FILE* in) { strcpy(line, "-\n"); } else { - assert(fgets(line, 1024, in)); + memset(line, '\0', 1024); +//? printf("%d\n", feof(in)); + fgets(line, 1024, in); //? printf("new line: %s", line); } } diff --git a/src/x.tlv b/src/x.tlv deleted file mode 100644 index c7f5247..0000000 --- a/src/x.tlv +++ /dev/null @@ -1,43 +0,0 @@ -- __teliva_timestamp: foo1 - window: - >window = curses.stdscr() -- __teliva_timestamp: foo2 - n: - >n = 0 -- __teliva_timestamp: foo3 - render: - >function render(window) - > window:clear() - > window:attron(curses.A_BOLD) - > window:attron(curses.color_pair(6)) - > window:mvaddstr(10, 10, " ") - > window:mvaddstr(10, 11, n) - > window:attroff(curses.color_pair(6)) - > window:attroff(curses.A_BOLD) - > curses.refresh() - >end - __teliva_note: foo -- __teliva_timestamp: foo4 - menu: - >menu = {Enter="increment"} -- __teliva_timestamp: foo5 - update: - >function update(window) - > local key = curses.getch() - > if key == 10 then - > n = n+1 - > end - >end -- __teliva_timestamp: foo6 - main: - >function main() - > for i=1,7 do - > curses.init_pair(i, 0, i) - > end - > curses.init_pair(255, 15, 1) -- reserved for Teliva error messages - > - > while true do - > render(window) - > update(window) - > end - >end diff --git a/template.tlv b/template.tlv index faf4131..a8b3cb9 100644 --- a/template.tlv +++ b/template.tlv @@ -1,72 +1,69 @@ -teliva_program = { - { - __teliva_timestamp = [==[ -original]==], - window = [==[ -window = curses.stdscr()]==], - }, - { - __teliva_timestamp = [==[ -original]==], - render = [==[ -function render(window) - window:clear() - -- draw stuff to screen here - window:attron(curses.A_BOLD) - window:mvaddstr(1, 5, "example app") - window:attrset(curses.A_NORMAL) - for i=0,15 do - window:attrset(curses.color_pair(i)) - window:mvaddstr(3+i, 5, "========================") - end - curses.refresh() -end]==], - }, - { - __teliva_timestamp = [==[ -original]==], - menu = [==[ -menu = {}]==], - }, - { - __teliva_timestamp = [==[ -original]==], - update = [==[ -function update(window) - local key = curses.getch() - -- process key here -end]==], - }, - { - init_colors = [==[ -function init_colors() - for i=0,7 do - curses.init_pair(i, i, -1) - end - curses.init_pair(8, 7, 0) - curses.init_pair(9, 7, 1) - curses.init_pair(10, 7, 2) - curses.init_pair(11, 7, 3) - curses.init_pair(12, 7, 4) - curses.init_pair(13, 7, 5) - curses.init_pair(14, 7, 6) - curses.init_pair(15, -1, 15) -end]==], - __teliva_timestamp = [==[ -original]==], - }, - { - main = [==[ -function main() - init_colors() - - while true do - render(window) - update(window) - end -end -]==], - __teliva_timestamp = [==[ -original]==], - }, -} +# .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 + window: + >window = curses.stdscr() +- __teliva_timestamp: original + render: + >function render(window) + > window:clear() + > -- draw stuff to screen here + > window:attron(curses.A_BOLD) + > window:mvaddstr(1, 5, "example app") + > window:attrset(curses.A_NORMAL) + > for i=0,15 do + > window:attrset(curses.color_pair(i)) + > window:mvaddstr(3+i, 5, "========================") + > end + > curses.refresh() + >end +- __teliva_timestamp: original + menu: + >menu = {} +- __teliva_timestamp: original + update: + >function update(window) + > local key = curses.getch() + > -- process key here + >end +- init_colors: + >function init_colors() + > for i=0,7 do + > curses.init_pair(i, i, -1) + > end + > curses.init_pair(8, 7, 0) + > curses.init_pair(9, 7, 1) + > curses.init_pair(10, 7, 2) + > curses.init_pair(11, 7, 3) + > curses.init_pair(12, 7, 4) + > curses.init_pair(13, 7, 5) + > curses.init_pair(14, 7, 6) + > curses.init_pair(15, -1, 15) + >end + __teliva_timestamp: original +- main: + >function main() + > init_colors() + > + > while true do + > render(window) + > update(window) + > end + >end + __teliva_timestamp: original