first commit
This commit is contained in:
commit
ed502bdf2a
6 changed files with 780 additions and 0 deletions
BIN
assets/cards.png
Normal file
BIN
assets/cards.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
5
conf.lua
Normal file
5
conf.lua
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
function love.conf(t)
|
||||||
|
t.console = true
|
||||||
|
t.window.resizable = true
|
||||||
|
end
|
||||||
|
|
||||||
7
libs/clasp.lua
Normal file
7
libs/clasp.lua
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- evolbug 2017, MIT License
|
||||||
|
-- clasp - class library
|
||||||
|
|
||||||
|
local class = { init = function()end; extend = function(self, proto) local meta = {}
|
||||||
|
local proto = setmetatable(proto or {},{__index=self, __call=function(_,...) local o=setmetatable({},meta) return o,o:init(...) end})
|
||||||
|
meta.__index = proto ; for k,v in pairs(proto.__ or {}) do meta['__'..k]=v end ; return proto end }
|
||||||
|
return setmetatable(class, { __call = class.extend })
|
||||||
584
libs/pprint.lua
Normal file
584
libs/pprint.lua
Normal file
|
|
@ -0,0 +1,584 @@
|
||||||
|
local pprint = { VERSION = '0.1' }
|
||||||
|
|
||||||
|
local depth = 1
|
||||||
|
|
||||||
|
pprint.defaults = {
|
||||||
|
-- If set to number N, then limit table recursion to N deep.
|
||||||
|
depth_limit = false,
|
||||||
|
-- type display trigger, hide not useful datatypes by default
|
||||||
|
-- custom types are treated as table
|
||||||
|
show_nil = true,
|
||||||
|
show_boolean = true,
|
||||||
|
show_number = true,
|
||||||
|
show_string = true,
|
||||||
|
show_table = true,
|
||||||
|
show_function = true,
|
||||||
|
show_thread = false,
|
||||||
|
show_userdata = false,
|
||||||
|
-- additional display trigger
|
||||||
|
show_metatable = false, -- show metatable
|
||||||
|
show_all = false, -- override other show settings and show everything
|
||||||
|
use_tostring = false, -- use __tostring to print table if available
|
||||||
|
filter_function = nil, -- called like callback(value[,key, parent]), return truty value to hide
|
||||||
|
object_cache = 'local', -- cache blob and table to give it a id, 'local' cache per print, 'global' cache
|
||||||
|
-- per process, falsy value to disable (might cause infinite loop)
|
||||||
|
-- format settings
|
||||||
|
indent_size = 2, -- indent for each nested table level
|
||||||
|
level_width = 80, -- max width per indent level
|
||||||
|
wrap_string = true, -- wrap string when it's longer than level_width
|
||||||
|
wrap_array = false, -- wrap every array elements
|
||||||
|
string_is_utf8 = true, -- treat string as utf8, and count utf8 char when wrapping, if possible
|
||||||
|
sort_keys = true, -- sort table keys
|
||||||
|
}
|
||||||
|
|
||||||
|
local TYPES = {
|
||||||
|
['nil'] = 1, ['boolean'] = 2, ['number'] = 3, ['string'] = 4,
|
||||||
|
['table'] = 5, ['function'] = 6, ['thread'] = 7, ['userdata'] = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
-- seems this is the only way to escape these, as lua don't know how to map char '\a' to 'a'
|
||||||
|
local ESCAPE_MAP = {
|
||||||
|
['\a'] = '\\a', ['\b'] = '\\b', ['\f'] = '\\f', ['\n'] = '\\n', ['\r'] = '\\r',
|
||||||
|
['\t'] = '\\t', ['\v'] = '\\v', ['\\'] = '\\\\',
|
||||||
|
}
|
||||||
|
|
||||||
|
-- generic utilities
|
||||||
|
local tokenize_string = function(s)
|
||||||
|
local t = {}
|
||||||
|
for i = 1, #s do
|
||||||
|
local c = s:sub(i, i)
|
||||||
|
local b = c:byte()
|
||||||
|
local e = ESCAPE_MAP[c]
|
||||||
|
if (b >= 0x20 and b < 0x80) or e then
|
||||||
|
local s = e or c
|
||||||
|
t[i] = { char = s, len = #s }
|
||||||
|
else
|
||||||
|
t[i] = { char = string.format('\\x%02x', b), len = 4 }
|
||||||
|
end
|
||||||
|
if c == '"' then
|
||||||
|
t.has_double_quote = true
|
||||||
|
elseif c == "'" then
|
||||||
|
t.has_single_quote = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
local tokenize_utf8_string = tokenize_string
|
||||||
|
|
||||||
|
local has_lpeg, lpeg = pcall(require, 'lpeg')
|
||||||
|
|
||||||
|
if has_lpeg then
|
||||||
|
local function utf8_valid_char(c)
|
||||||
|
return { char = c, len = 1 }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function utf8_invalid_char(c)
|
||||||
|
local b = c:byte()
|
||||||
|
local e = ESCAPE_MAP[c]
|
||||||
|
if (b >= 0x20 and b < 0x80) or e then
|
||||||
|
local s = e or c
|
||||||
|
return { char = s, len = #s }
|
||||||
|
else
|
||||||
|
return { char = string.format('\\x%02x', b), len = 4 }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cont = lpeg.R('\x80\xbf')
|
||||||
|
local utf8_char =
|
||||||
|
lpeg.R('\x20\x7f') +
|
||||||
|
lpeg.R('\xc0\xdf') * cont +
|
||||||
|
lpeg.R('\xe0\xef') * cont * cont +
|
||||||
|
lpeg.R('\xf0\xf7') * cont * cont * cont
|
||||||
|
|
||||||
|
local utf8_capture = (((utf8_char / utf8_valid_char) + (lpeg.P(1) / utf8_invalid_char)) ^ 0) * -1
|
||||||
|
|
||||||
|
tokenize_utf8_string = function(s)
|
||||||
|
local dq = s:find('"')
|
||||||
|
local sq = s:find("'")
|
||||||
|
local t = table.pack(utf8_capture:match(s))
|
||||||
|
t.has_double_quote = not not dq
|
||||||
|
t.has_single_quote = not not sq
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_plain_key(key)
|
||||||
|
return type(key) == 'string' and key:match('^[%a_][%a%d_]*$')
|
||||||
|
end
|
||||||
|
|
||||||
|
local CACHE_TYPES = {
|
||||||
|
['table'] = true, ['function'] = true, ['thread'] = true, ['userdata'] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
-- cache would be populated to be like:
|
||||||
|
-- {
|
||||||
|
-- function = { `fun1` = 1, _cnt = 1 }, -- object id
|
||||||
|
-- table = { `table1` = 1, `table2` = 2, _cnt = 2 },
|
||||||
|
-- visited_tables = { `table1` = 7, `table2` = 8 }, -- visit count
|
||||||
|
-- }
|
||||||
|
-- use weakrefs to avoid accidentall adding refcount
|
||||||
|
local function cache_apperance(obj, cache, option)
|
||||||
|
if not cache.visited_tables then
|
||||||
|
cache.visited_tables = setmetatable({}, {__mode = 'k'})
|
||||||
|
end
|
||||||
|
local t = type(obj)
|
||||||
|
|
||||||
|
-- TODO can't test filter_function here as we don't have the ix and key,
|
||||||
|
-- might cause different results?
|
||||||
|
-- respect show_xxx and filter_function to be consistent with print results
|
||||||
|
if (not TYPES[t] and not option.show_table)
|
||||||
|
or (TYPES[t] and not option['show_'..t]) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if CACHE_TYPES[t] or TYPES[t] == nil then
|
||||||
|
if not cache[t] then
|
||||||
|
cache[t] = setmetatable({}, {__mode = 'k'})
|
||||||
|
cache[t]._cnt = 0
|
||||||
|
end
|
||||||
|
if not cache[t][obj] then
|
||||||
|
cache[t]._cnt = cache[t]._cnt + 1
|
||||||
|
cache[t][obj] = cache[t]._cnt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if t == 'table' or TYPES[t] == nil then
|
||||||
|
if cache.visited_tables[obj] == false then
|
||||||
|
-- already printed, no need to mark this and its children anymore
|
||||||
|
return
|
||||||
|
elseif cache.visited_tables[obj] == nil then
|
||||||
|
cache.visited_tables[obj] = 1
|
||||||
|
else
|
||||||
|
-- visited already, increment and continue
|
||||||
|
cache.visited_tables[obj] = cache.visited_tables[obj] + 1
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for k, v in pairs(obj) do
|
||||||
|
cache_apperance(k, cache, option)
|
||||||
|
cache_apperance(v, cache, option)
|
||||||
|
end
|
||||||
|
local mt = getmetatable(obj)
|
||||||
|
if mt and option.show_metatable then
|
||||||
|
cache_apperance(mt, cache, option)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- makes 'foo2' < 'foo100000'. string.sub makes substring anyway, no need to use index based method
|
||||||
|
local function str_natural_cmp(lhs, rhs)
|
||||||
|
while #lhs > 0 and #rhs > 0 do
|
||||||
|
local lmid, lend = lhs:find('%d+')
|
||||||
|
local rmid, rend = rhs:find('%d+')
|
||||||
|
if not (lmid and rmid) then return lhs < rhs end
|
||||||
|
|
||||||
|
local lsub = lhs:sub(1, lmid-1)
|
||||||
|
local rsub = rhs:sub(1, rmid-1)
|
||||||
|
if lsub ~= rsub then
|
||||||
|
return lsub < rsub
|
||||||
|
end
|
||||||
|
|
||||||
|
local lnum = tonumber(lhs:sub(lmid, lend))
|
||||||
|
local rnum = tonumber(rhs:sub(rmid, rend))
|
||||||
|
if lnum ~= rnum then
|
||||||
|
return lnum < rnum
|
||||||
|
end
|
||||||
|
|
||||||
|
lhs = lhs:sub(lend+1)
|
||||||
|
rhs = rhs:sub(rend+1)
|
||||||
|
end
|
||||||
|
return lhs < rhs
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cmp(lhs, rhs)
|
||||||
|
local tleft = type(lhs)
|
||||||
|
local tright = type(rhs)
|
||||||
|
if tleft == 'number' and tright == 'number' then return lhs < rhs end
|
||||||
|
if tleft == 'string' and tright == 'string' then return str_natural_cmp(lhs, rhs) end
|
||||||
|
if tleft == tright then return str_natural_cmp(tostring(lhs), tostring(rhs)) end
|
||||||
|
|
||||||
|
-- allow custom types
|
||||||
|
local oleft = TYPES[tleft] or 9
|
||||||
|
local oright = TYPES[tright] or 9
|
||||||
|
return oleft < oright
|
||||||
|
end
|
||||||
|
|
||||||
|
-- setup option with default
|
||||||
|
local function make_option(option)
|
||||||
|
if option == nil then
|
||||||
|
option = {}
|
||||||
|
end
|
||||||
|
for k, v in pairs(pprint.defaults) do
|
||||||
|
if option[k] == nil then
|
||||||
|
option[k] = v
|
||||||
|
end
|
||||||
|
if option.show_all then
|
||||||
|
for t, _ in pairs(TYPES) do
|
||||||
|
option['show_'..t] = true
|
||||||
|
end
|
||||||
|
option.show_metatable = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return option
|
||||||
|
end
|
||||||
|
|
||||||
|
-- override defaults and take effects for all following calls
|
||||||
|
function pprint.setup(option)
|
||||||
|
pprint.defaults = make_option(option)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- format lua object into a string
|
||||||
|
function pprint.pformat(obj, option, printer)
|
||||||
|
option = make_option(option)
|
||||||
|
local buf = {}
|
||||||
|
local function default_printer(s)
|
||||||
|
table.insert(buf, s)
|
||||||
|
end
|
||||||
|
printer = printer or default_printer
|
||||||
|
|
||||||
|
local cache
|
||||||
|
if option.object_cache == 'global' then
|
||||||
|
-- steal the cache into a local var so it's not visible from _G or anywhere
|
||||||
|
-- still can't avoid user explicitly referentce pprint._cache but it shouldn't happen anyway
|
||||||
|
cache = pprint._cache or {}
|
||||||
|
pprint._cache = nil
|
||||||
|
elseif option.object_cache == 'local' then
|
||||||
|
cache = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local last = '' -- used for look back and remove trailing comma
|
||||||
|
local status = {
|
||||||
|
indent = '', -- current indent
|
||||||
|
len = 0, -- current line length
|
||||||
|
printed_something = false, -- used to remove leading new lines
|
||||||
|
}
|
||||||
|
|
||||||
|
local wrapped_printer = function(s)
|
||||||
|
status.printed_something = true
|
||||||
|
printer(last)
|
||||||
|
last = s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _indent(d)
|
||||||
|
status.indent = string.rep(' ', d + #(status.indent))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _n(d)
|
||||||
|
if not status.printed_something then return end
|
||||||
|
wrapped_printer('\n')
|
||||||
|
wrapped_printer(status.indent)
|
||||||
|
if d then
|
||||||
|
_indent(d)
|
||||||
|
end
|
||||||
|
status.len = 0
|
||||||
|
return true -- used to close bracket correctly
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _p(s, nowrap)
|
||||||
|
status.len = status.len + #s
|
||||||
|
if not nowrap and status.len > option.level_width then
|
||||||
|
_n()
|
||||||
|
wrapped_printer(s)
|
||||||
|
status.len = #s
|
||||||
|
else
|
||||||
|
wrapped_printer(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local formatter = {}
|
||||||
|
local function format(v)
|
||||||
|
local f = formatter[type(v)]
|
||||||
|
f = f or formatter.table -- allow patched type()
|
||||||
|
if option.filter_function and option.filter_function(v, nil, nil) then
|
||||||
|
return ''
|
||||||
|
else
|
||||||
|
return f(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tostring_formatter(v)
|
||||||
|
return tostring(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function number_formatter(n)
|
||||||
|
return n == math.huge and '[[math.huge]]' or tostring(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function nop_formatter(v)
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_fixed_formatter(t, has_cache)
|
||||||
|
if has_cache then
|
||||||
|
return function (v)
|
||||||
|
return string.format('[[%s %d]]', t, cache[t][v])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return function (v)
|
||||||
|
return '[['..t..']]'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function string_formatter(s, force_long_quote)
|
||||||
|
local tokens = option.string_is_utf8 and tokenize_utf8_string(s) or tokenize_string(s)
|
||||||
|
local string_len = 0
|
||||||
|
local escape_quotes = tokens.has_double_quote and tokens.has_single_quote
|
||||||
|
for _, token in ipairs(tokens) do
|
||||||
|
if escape_quotes and token.char == '"' then
|
||||||
|
string_len = string_len + 2
|
||||||
|
else
|
||||||
|
string_len = string_len + token.len
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local quote_len = 2
|
||||||
|
local long_quote_dashes = 0
|
||||||
|
local function compute_long_quote_dashes()
|
||||||
|
local keep_looking = true
|
||||||
|
while keep_looking do
|
||||||
|
if s:find('%]' .. string.rep('=', long_quote_dashes) .. '%]') then
|
||||||
|
long_quote_dashes = long_quote_dashes + 1
|
||||||
|
else
|
||||||
|
keep_looking = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if force_long_quote then
|
||||||
|
compute_long_quote_dashes()
|
||||||
|
quote_len = 2 + long_quote_dashes
|
||||||
|
end
|
||||||
|
if quote_len + string_len + status.len > option.level_width then
|
||||||
|
_n()
|
||||||
|
-- only wrap string when is longer than level_width
|
||||||
|
if option.wrap_string and string_len + quote_len > option.level_width then
|
||||||
|
if not force_long_quote then
|
||||||
|
compute_long_quote_dashes()
|
||||||
|
quote_len = 2 + long_quote_dashes
|
||||||
|
end
|
||||||
|
-- keep the quotes together
|
||||||
|
local dashes = string.rep('=', long_quote_dashes)
|
||||||
|
_p('[' .. dashes .. '[', true)
|
||||||
|
local status_len = status.len
|
||||||
|
local line_len = 0
|
||||||
|
local line = ''
|
||||||
|
for _, token in ipairs(tokens) do
|
||||||
|
if line_len + token.len + status_len > option.level_width then
|
||||||
|
_n()
|
||||||
|
_p(line, true)
|
||||||
|
line_len = token.len
|
||||||
|
line = token.char
|
||||||
|
else
|
||||||
|
line_len = line_len + token.len
|
||||||
|
line = line .. token.char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return line .. ']' .. dashes .. ']'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if tokens.has_double_quote and tokens.has_single_quote and not force_long_quote then
|
||||||
|
for i, token in ipairs(tokens) do
|
||||||
|
if token.char == '"' then
|
||||||
|
tokens[i].char = '\\"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local flat_table = {}
|
||||||
|
for _, token in ipairs(tokens) do
|
||||||
|
table.insert(flat_table, token.char)
|
||||||
|
end
|
||||||
|
local concat = table.concat(flat_table)
|
||||||
|
|
||||||
|
if force_long_quote then
|
||||||
|
local dashes = string.rep('=', long_quote_dashes)
|
||||||
|
return '[' .. dashes .. '[' .. concat .. ']' .. dashes .. ']'
|
||||||
|
-- elseif tokens.has_single_quote then
|
||||||
|
-- -- use double quote
|
||||||
|
-- return '"' .. concat .. '"'
|
||||||
|
else
|
||||||
|
-- use single quote
|
||||||
|
-- return "'" .. concat .. "'"
|
||||||
|
return concat
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function table_formatter(t)
|
||||||
|
if option.use_tostring then
|
||||||
|
local mt = getmetatable(t)
|
||||||
|
if mt and mt.__tostring then
|
||||||
|
return string_formatter(tostring(t), true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local print_header_ix = nil
|
||||||
|
local ttype = type(t)
|
||||||
|
if option.object_cache then
|
||||||
|
local cache_state = cache.visited_tables[t]
|
||||||
|
local tix = cache[ttype][t]
|
||||||
|
-- FIXME should really handle `cache_state == nil`
|
||||||
|
-- as user might add things through filter_function
|
||||||
|
if cache_state == false then
|
||||||
|
-- already printed, just print the the number
|
||||||
|
return string_formatter(string.format('%s %d', ttype, tix), true)
|
||||||
|
elseif cache_state > 1 then
|
||||||
|
-- appeared more than once, print table header with number
|
||||||
|
print_header_ix = tix
|
||||||
|
cache.visited_tables[t] = false
|
||||||
|
else
|
||||||
|
-- appeared exactly once, print like a normal table
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local limit = tonumber(option.depth_limit)
|
||||||
|
if limit and depth > limit then
|
||||||
|
if print_header_ix then
|
||||||
|
return string.format('[[%s %d]]...', ttype, print_header_ix)
|
||||||
|
end
|
||||||
|
return string_formatter(tostring(t), true)
|
||||||
|
end
|
||||||
|
|
||||||
|
local tlen = #t
|
||||||
|
local wrapped = false
|
||||||
|
_p('{')
|
||||||
|
_indent(option.indent_size)
|
||||||
|
_p(string.rep(' ', option.indent_size - 1))
|
||||||
|
if print_header_ix then
|
||||||
|
_p(string.format('--[[%s %d]] ', ttype, print_header_ix))
|
||||||
|
end
|
||||||
|
for ix = 1,tlen do
|
||||||
|
local v = t[ix]
|
||||||
|
if formatter[type(v)] == nop_formatter or
|
||||||
|
(option.filter_function and option.filter_function(v, ix, t)) then
|
||||||
|
-- pass
|
||||||
|
else
|
||||||
|
if option.wrap_array then
|
||||||
|
wrapped = _n()
|
||||||
|
end
|
||||||
|
depth = depth+1
|
||||||
|
_p(format(v)..', ')
|
||||||
|
depth = depth-1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- hashmap part of the table, in contrast to array part
|
||||||
|
local function is_hash_key(k)
|
||||||
|
if type(k) ~= 'number' then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local numkey = math.floor(tonumber(k))
|
||||||
|
if numkey ~= k or numkey > tlen or numkey <= 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function print_kv(k, v, t)
|
||||||
|
-- can't use option.show_x as obj may contain custom type
|
||||||
|
if formatter[type(v)] == nop_formatter or
|
||||||
|
formatter[type(k)] == nop_formatter or
|
||||||
|
(option.filter_function and option.filter_function(v, k, t)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
wrapped = _n()
|
||||||
|
if is_plain_key(k) then
|
||||||
|
_p(k, true)
|
||||||
|
else
|
||||||
|
_p('[')
|
||||||
|
-- [[]] type string in key is illegal, needs to add spaces inbetween
|
||||||
|
local k = format(k)
|
||||||
|
if string.match(k, '%[%[') then
|
||||||
|
_p(' '..k..' ', true)
|
||||||
|
else
|
||||||
|
_p(k, true)
|
||||||
|
end
|
||||||
|
_p(']')
|
||||||
|
end
|
||||||
|
_p(' = ', true)
|
||||||
|
depth = depth+1
|
||||||
|
_p(format(v), true)
|
||||||
|
depth = depth-1
|
||||||
|
_p(',', true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if option.sort_keys then
|
||||||
|
local keys = {}
|
||||||
|
for k, _ in pairs(t) do
|
||||||
|
if is_hash_key(k) then
|
||||||
|
table.insert(keys, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(keys, cmp)
|
||||||
|
for _, k in ipairs(keys) do
|
||||||
|
print_kv(k, t[k], t)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
if is_hash_key(k) then
|
||||||
|
print_kv(k, v, t)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if option.show_metatable then
|
||||||
|
local mt = getmetatable(t)
|
||||||
|
if mt then
|
||||||
|
print_kv('__metatable', mt, t)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_indent(-option.indent_size)
|
||||||
|
-- make { } into {}
|
||||||
|
last = string.gsub(last, '^ +$', '')
|
||||||
|
-- peek last to remove trailing comma
|
||||||
|
last = string.gsub(last, ',%s*$', ' ')
|
||||||
|
if wrapped then
|
||||||
|
_n()
|
||||||
|
end
|
||||||
|
_p('}')
|
||||||
|
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set formatters
|
||||||
|
formatter['nil'] = option.show_nil and tostring_formatter or nop_formatter
|
||||||
|
formatter['boolean'] = option.show_boolean and tostring_formatter or nop_formatter
|
||||||
|
formatter['number'] = option.show_number and number_formatter or nop_formatter -- need to handle math.huge
|
||||||
|
formatter['function'] = option.show_function and make_fixed_formatter('function', option.object_cache) or nop_formatter
|
||||||
|
formatter['thread'] = option.show_thread and make_fixed_formatter('thread', option.object_cache) or nop_formatter
|
||||||
|
formatter['userdata'] = option.show_userdata and make_fixed_formatter('userdata', option.object_cache) or nop_formatter
|
||||||
|
formatter['string'] = option.show_string and string_formatter or nop_formatter
|
||||||
|
formatter['table'] = option.show_table and table_formatter or nop_formatter
|
||||||
|
|
||||||
|
if option.object_cache then
|
||||||
|
-- needs to visit the table before start printing
|
||||||
|
cache_apperance(obj, cache, option)
|
||||||
|
end
|
||||||
|
|
||||||
|
_p(format(obj))
|
||||||
|
printer(last) -- close the buffered one
|
||||||
|
|
||||||
|
-- put cache back if global
|
||||||
|
if option.object_cache == 'global' then
|
||||||
|
pprint._cache = cache
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- pprint all the arguments
|
||||||
|
function pprint.pprint( ... )
|
||||||
|
local args = {...}
|
||||||
|
-- select will get an accurate count of array len, counting trailing nils
|
||||||
|
local len = select('#', ...)
|
||||||
|
for ix = 1,len do
|
||||||
|
pprint.pformat(args[ix], nil, io.write)
|
||||||
|
io.write('\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(pprint, {
|
||||||
|
__call = function (_, ...)
|
||||||
|
pprint.pprint(...)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
return pprint
|
||||||
96
main.lua
Normal file
96
main.lua
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
local class = require "libs.clasp"
|
||||||
|
local utils = require "utils"
|
||||||
|
local term = utils.term
|
||||||
|
local info = utils.log.info
|
||||||
|
local warn = utils.log.warn
|
||||||
|
local err = utils.log.err
|
||||||
|
local fatal = utils.log.fatal
|
||||||
|
|
||||||
|
function enum(vals)
|
||||||
|
local out = {}
|
||||||
|
local count = 0
|
||||||
|
for k,v in ipairs(vals) do
|
||||||
|
out[v] = k
|
||||||
|
out[k] = v
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
out._Count = count
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
local Vec2 = class {
|
||||||
|
init = function(self, x, y)
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
local CardSeed = enum {
|
||||||
|
'Heart',
|
||||||
|
'Spade',
|
||||||
|
'Diamond',
|
||||||
|
'Club',
|
||||||
|
}
|
||||||
|
|
||||||
|
local card_size = Vec2(20, 29)
|
||||||
|
|
||||||
|
local Card = class {
|
||||||
|
init = function(self, n, s, tex)
|
||||||
|
self.image = tex
|
||||||
|
self.number = n
|
||||||
|
self.seed = s
|
||||||
|
self.x = 0
|
||||||
|
self.y = 0
|
||||||
|
|
||||||
|
local offx = n - 1
|
||||||
|
local offy = s - 1
|
||||||
|
self.quad = love.graphics.newQuad(
|
||||||
|
offx * card_size.x,
|
||||||
|
offy * card_size.y,
|
||||||
|
card_size.x,
|
||||||
|
card_size.y,
|
||||||
|
tex
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
draw = function(self)
|
||||||
|
love.graphics.draw(
|
||||||
|
self.image,
|
||||||
|
self.quad,
|
||||||
|
self.x,
|
||||||
|
self.y
|
||||||
|
)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
local CardData = class {
|
||||||
|
init = function(self, img_path)
|
||||||
|
self.image = love.graphics.newImage(img_path)
|
||||||
|
self.cards = {}
|
||||||
|
|
||||||
|
for s=1,CardSeed._Count do
|
||||||
|
local seed = {}
|
||||||
|
for n=1,10 do
|
||||||
|
seed[n] = Card(n, s, self.image)
|
||||||
|
end
|
||||||
|
self.cards[s] = seed
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local card_data = null
|
||||||
|
|
||||||
|
function love.load()
|
||||||
|
love.graphics.setDefaultFilter('nearest', 'nearest', 0)
|
||||||
|
card_data = CardData('assets/cards.png')
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.update(dt)
|
||||||
|
if love.keyboard.isDown('q') or
|
||||||
|
love.keyboard.isDown('escape')
|
||||||
|
then
|
||||||
|
love.event.quit(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
88
utils.lua
Normal file
88
utils.lua
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
local pprint = require 'libs.pprint'
|
||||||
|
|
||||||
|
local utils = {}
|
||||||
|
|
||||||
|
utils.term = {
|
||||||
|
reset = "\x1b[m",
|
||||||
|
bold = "\x1b[1m",
|
||||||
|
italic = "\x1b[3m",
|
||||||
|
fg = {
|
||||||
|
rgb = function(r, g, b)
|
||||||
|
return "\x1b[38;2;"
|
||||||
|
.. tostring(r) .. ";"
|
||||||
|
.. tostring(g) .. ";"
|
||||||
|
.. tostring(b) .. "m"
|
||||||
|
end,
|
||||||
|
default = "\x1b[39m",
|
||||||
|
black = "\x1b[38;5;16m",
|
||||||
|
red = "\x1b[31m",
|
||||||
|
green = "\x1b[32m",
|
||||||
|
yellow = "\x1b[33m",
|
||||||
|
blue = "\x1b[38;5;27m",
|
||||||
|
magenta = "\x1b[35m",
|
||||||
|
cyan = "\x1b[36m",
|
||||||
|
white = "\x1b[37m",
|
||||||
|
dark_grey = "\x1b[90m",
|
||||||
|
light_red = "\x1b[91m",
|
||||||
|
light_green = "\x1b[92m",
|
||||||
|
light_yellow = "\x1b[93m",
|
||||||
|
light_blue = "\x1b[94m",
|
||||||
|
light_magenta = "\x1b[95m",
|
||||||
|
light_cyan = "\x1b[96m",
|
||||||
|
orange = "\x1b[38;5;202m",
|
||||||
|
light_orange = "\x1b[38;5;208m",
|
||||||
|
purple = "\x1b[38;5;93m",
|
||||||
|
light_purple = "\x1b[38;5;99m",
|
||||||
|
},
|
||||||
|
bg = {
|
||||||
|
default = "\x1b[49m",
|
||||||
|
black = "\x1b[40m",
|
||||||
|
red = "\x1b[41m",
|
||||||
|
green = "\x1b[42m",
|
||||||
|
yellow = "\x1b[43m",
|
||||||
|
blue = "\x1b[44m",
|
||||||
|
magenta = "\x1b[45m",
|
||||||
|
cyan = "\x1b[46m",
|
||||||
|
white = "\x1b[47m",
|
||||||
|
dark_grey = "\x1b[100m",
|
||||||
|
light_red = "\x1b[101m",
|
||||||
|
light_green = "\x1b[102m",
|
||||||
|
light_yellow = "\x1b[103m",
|
||||||
|
light_blue = "\x1b[104m",
|
||||||
|
light_magenta = "\x1b[105m",
|
||||||
|
light_cyan = "\x1b[106m",
|
||||||
|
orange = "\x1b[48;5;202m",
|
||||||
|
light_orange = "\x1b[48;5;208m",
|
||||||
|
purple = "\x1b[48;5;93m",
|
||||||
|
light_purple = "\x1b[48;5;99m",
|
||||||
|
light_gray = "\x1b[48;5;234m",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local function log_impl(prefix, prefix_col)
|
||||||
|
return function(...)
|
||||||
|
local args = {...}
|
||||||
|
local len = select('#', ...)
|
||||||
|
io.write(
|
||||||
|
utils.term.fg[prefix_col] ..
|
||||||
|
prefix ..
|
||||||
|
utils.term.reset
|
||||||
|
)
|
||||||
|
for ix = 1,len do
|
||||||
|
pprint.pformat(args[ix], nil, io.write)
|
||||||
|
io.write(' ')
|
||||||
|
end
|
||||||
|
io.write('\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
utils.log = {
|
||||||
|
print = pprint.pprint,
|
||||||
|
info = log_impl('[INFO]: ', 'green'),
|
||||||
|
debug = log_impl('[DEBUG]: ', 'blue'),
|
||||||
|
warn = log_impl('[WARN]: ', 'yellow'),
|
||||||
|
err = log_impl('[ERR]: ', 'red'),
|
||||||
|
fatal = log_impl('[FATAL]: ', 'red'),
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils
|
||||||
Loading…
Add table
Add a link
Reference in a new issue