summaryrefslogtreecommitdiff
path: root/module/tex/context/third/simpleslides/automata/automata-life.lua
diff options
context:
space:
mode:
Diffstat (limited to 'module/tex/context/third/simpleslides/automata/automata-life.lua')
-rw-r--r--module/tex/context/third/simpleslides/automata/automata-life.lua420
1 files changed, 420 insertions, 0 deletions
diff --git a/module/tex/context/third/simpleslides/automata/automata-life.lua b/module/tex/context/third/simpleslides/automata/automata-life.lua
new file mode 100644
index 0000000..01fc97b
--- /dev/null
+++ b/module/tex/context/third/simpleslides/automata/automata-life.lua
@@ -0,0 +1,420 @@
+--
+--------------------------------------------------------------------------------
+-- FILE: automata-life.lua
+-- DESCRIPTION: Conway's Game of Life?
+-- REQUIREMENTS: ConTeXt MkIV / Simple Slides module
+-- BUGS: almost infinite but not quite
+-- AUTHOR: Philipp Gesang (Phg), <megas.kapaneus@gmail.com>
+-- VERSION: 1.0
+-- CREATED: 05/08/10 12:03:11 CEST
+--------------------------------------------------------------------------------
+--
+
+gol = {}
+
+gol.helpers = {}
+
+local C, Cs, Ct, P, R, S, match = lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.P, lpeg.R, lpeg.S, lpeg.match
+
+
+-- http://lua-users.org/lists/lua-l/2009-06/msg00343.html
+gol.helpers.utfchar = R("\000\127") +
+ R("\194\223") * R("\128\191") +
+ R("\224\240") * R("\128\191") * R("\128\191") +
+ R("\241\244") * R("\128\191") * R("\128\191") * R("\128\191")
+
+
+-- by Roberto, captures everything in a table
+-- http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html
+function gol.helpers.split (s, sep)
+ sep = P(sep)
+ local elem = C((1 - sep)^0)
+ local p = Ct(elem * (sep * elem)^0) -- make a table capture
+ return match(p, s)
+end
+
+-- Modified to match left and right of one separator only.
+function gol.helpers.split_once (s, sep)
+ local utfchar = gol.helpers.utfchar
+ sep = S(sep)
+ local left = C((1 - sep)^0)
+ local right = C((utfchar)^0)
+ local p = Ct(left * sep * right)
+ return p:match(s)
+end
+
+-- Modified stripper from Roberto to support trimming other chars than whitespace
+function gol.helpers.strip(str, chars)
+ local chars = chars or " \t\v\n"
+ chars = S(chars)
+ local nochars = 1 - chars
+ local stripper = chars^0 * C((chars^0 * nochars^1)^0)
+
+ return match(stripper,str) or ""
+end
+
+function gol.helpers.dead_row(len)
+ local row = ""
+ local dead = "0"
+ for i=1, len, 1 do
+ row = row .. dead
+ end
+ return row
+end
+
+-- Read rules of type "B3[/.-]S23"
+-- => “birth” of a new cell if exactly 3 adjacent cells are “alive”
+-- => “staying alive” of cells with two or three adjacent “live” cells
+function gol.parse_rule (raw_rule)
+ local help = gol.helpers
+ local b_s = help.split_once (raw_rule, "/.-")
+ local tmp_b = help.strip(b_s[1], "B")
+ local tmp_s = help.strip(b_s[2], "S")
+
+ local b, s = {}, {}
+
+ -- single digits express birth/stay conditions
+ local n = 1
+ repeat
+ table.insert( b, tonumber(tmp_b:sub(n,n)) )
+ n = n + 1
+ until n > string.len(tmp_b)
+
+ n = 1
+ repeat
+ table.insert( s, tonumber(tmp_s:sub(n,n)) )
+ n = n + 1
+ until n > string.len(tmp_s)
+
+ return { birth = b, stay = s }
+end
+
+function gol.apply_rule (cell, cnt, rule, fade, keep)
+ --local live, dead = "1", "0"
+ --local new = dead
+ local new = 0
+ local live = "1"
+ local stay = rule.stay
+ local birth = rule.birth
+
+ -- checking if cnt matches the numbers from the conditions list
+ if cell == live then
+ for _, cond in ipairs(stay) do
+ if cnt == cond then
+ new = "1"
+ break
+ end
+ end
+ else -- ==dead
+ for _, cond in ipairs(birth) do
+ if cnt == cond then
+ new = "1"
+ break
+ end
+ end
+ end
+
+ if fade then
+ if not (live == new) then
+ local add = tonumber (cell)
+ if not add then -- == "D"
+ add = "D"
+ else
+ if add and add < 9 and add ~= 0 then
+ add = add + 1
+ elseif keep and add and add == 9 then -- marking dead cells once alive
+ add = "D"
+ else
+ add = 0
+ end
+ end
+ new = tostring(add)
+ end
+ end
+
+ return new
+end
+
+gol.formats = {}
+
+gol.formats[".gol"] = function (fname)
+ local tmp = {} -- return an array
+ local len -- check for equal line length
+
+ if fname:sub(-4,-1) ~= ".gol" then
+ fname = fname .. ".gol"
+ end
+
+ local file = assert(io.open(fname, "r"), "Not a file: " .. fname)
+ local data = file:read("*all")
+ file:close()
+
+ local cell = R"09" + P"D"
+ local nl = Cs(P"\n") / ""
+ local line = Cs(cell^1) / function (l)
+ if not len then len = string.len(l) end
+ if len ~= string.len(l) then
+ -- inconsistent horizontal sizes; kill off program
+ return nil
+ else
+ table.insert(tmp,l)
+ end
+ end
+
+ local gol_parser = Cs(line * (nl + line)^0)
+
+ if gol_parser:match(data) then
+ if context then
+ context.writestatus("simpleslides", "Sucessfully loaded frame from "..fname..".")
+ end
+ return tmp
+ else
+ return false
+ end
+end
+
+gol.formats[".cell"] = function (fname)
+ return false -- stub
+end
+
+gol.formats[".rle"] = function (fname)
+ environment.loadluafile( "parser-rle" )
+ return gol.rleparser(fname)
+end
+
+function gol.extend_area(rows)
+ local tmp = {}
+ local c = mplife.setup.current
+
+ context.writestatus("simpleslides","Extending area horizontally by "..c.extendx.."cells.")
+ context.writestatus("simpleslides","Extending area vertically by "..c.extendy.."cells.")
+
+ if c.extendy > 0 then
+ local row = ""
+ for i=1, rows[1]:len(), 1 do -- buildling an empty row
+ row = row.."0"
+ end
+
+ for i=1, c.extendy, 1 do
+ table.insert(tmp, row)
+ end
+
+ for _, r in ipairs(rows) do
+ table.insert(tmp, r)
+ end
+
+ for i=1, c.extendy, 1 do
+ table.insert(tmp, row)
+ end
+ end
+
+ if c.extendx > 0 then
+ local add = ""
+ for i=1, c.extendx, 1 do -- building an empty pre- and suffix
+ add = add .. "0"
+ end
+
+ for n, r in ipairs(tmp) do
+ tmp[n] = add .. r .. add
+ end
+ end
+
+ return tmp
+end
+
+function gol.parse_file (fname)
+ local p_suf = P"." * (1-P".")^3 * -P(1)
+ local p_fn = (1-p_suf)^1 * C(p_suf)
+ local suffix = p_fn:match(fname) or ".gol" -- assume .gol format as default
+
+ local tmp = gol.formats[suffix] ( fname )
+ if mplife and mplife.setup.current and (
+ mplife.setup.current.extendx > 0 or
+ mplife.setup.current.extendy > 0) then
+ tmp = gol.extend_area(tmp)
+ end
+ return tmp
+end
+
+
+--- Computing single lines and whole frames and intervals thereof
+
+function gol.next_line (rows, rule)
+ local live = "1"
+
+ local fade = false
+ if mplife then
+ fade = mplife.setup.current.fade
+ end
+
+ local n = 1
+ local max = string.len(rows[2])
+
+ local cell = R("09") + P("D")
+ local nocell = 1-cell
+ local ce = Cs(cell) / function (current)
+
+ local env = {}
+ local lpos, rpos -- positions left of / right of current
+
+ -- toroidal, flips over at borders
+ if n == 1 then
+ lpos = max
+ else
+ lpos = n - 1
+ end
+
+ if n == max then
+ rpos = 1
+ else
+ rpos = n + 1
+ end
+
+ -- +---+---+---+
+ -- |nw | n | ne|
+ -- +---+---+---+
+ -- |w | c | e| env[ironment] of current
+ -- +---+---+---+
+ -- |sw | s | se|
+ -- +---+---+---+
+
+ env.nw = string.sub( rows[1], lpos, lpos )
+ env.n = string.sub( rows[1], n , n )
+ env.ne = string.sub( rows[1], rpos, rpos )
+
+ env.w = string.sub( rows[2], rpos, rpos )
+ env.e = string.sub( rows[2], lpos, lpos )
+
+ env.sw = string.sub( rows[3], lpos, lpos )
+ env.s = string.sub( rows[3], n , n )
+ env.se = string.sub( rows[3], rpos, rpos )
+
+ -- counting live cells in the environment
+ local cnt = 0
+ for _, chr in pairs(env) do
+ if chr == live then
+ cnt = cnt + 1
+ end
+ end
+
+ n = n + 1
+ --
+ -- adding new cell according to ruleset
+ return gol.apply_rule(current, cnt, rule, fade, true)
+ end
+
+ local noce = Cs(nocell) / ""
+ local c = Cs(ce * (noce + ce)^0)
+
+ return c:match(rows[2])
+end
+
+function gol.next_frame (old, rule)
+ local new = {}
+
+ local n = 1
+
+ repeat
+ local rows = {}
+
+ rows[2] = old[n] -- current
+
+ -- toroidal
+ rows[1] = old[n-1] or old[#old] -- last
+ rows[3] = old[n+1] or old[1] -- next
+
+ new[n] = gol.next_line( rows, rule )
+
+ n = n + 1
+ until n > #old
+
+ return new
+end
+
+-- get the nth frame starting from init
+function gol.frame (init, rule, n)
+ local frame = init
+ local gnext = gol.next_frame
+
+ for i=1, n, 1 do
+ frame = gnext( frame, rule )
+ end
+
+ return frame
+end
+
+-- get array of frames until nth [from offset]
+function gol.frames (init, rule, n, offset)
+ local frames = {}
+ local last
+ local gnext = gol.next_frame
+
+ -- “fast forward”
+ if offset then
+ last = gol.frame( init, rule, offset )
+ else
+ last = init
+ end
+
+ for i=1, n, 1 do
+ frames[i] = last
+ last = gnext( last, rule )
+ end
+
+ return frames
+end
+
+--- pretty printing frames (ascii)
+
+-- pp a heading: len should exceed length of mark + 4
+function gol.pre_section (len, mark)
+ local mark = mark or "Frame"
+ local t = "-"
+
+ local s = t..t.." "..mark.." "
+ if s:len() < len then
+ for n=1, len - s:len(), 1 do
+ s = s..t
+ end
+ end
+
+ io.write ("\n\n"..s.."\n")
+end
+
+function gol.pre_frame (frame)
+ local repl = {
+ ["0"] = "·",
+ ["1"] = "O",
+ ["2"] = "2",
+ ["3"] = "3",
+ ["4"] = "4",
+ ["5"] = "5",
+ ["6"] = "6",
+ ["7"] = "7",
+ ["8"] = "8",
+ ["9"] = "9",
+ }
+
+ local cell = R"09" + P"D"
+ local nocell = 1-cell
+ local ce = Cs(cell) / repl
+ local noce = Cs(nocell) / ""
+ local c = Cs(ce * (noce + ce)^0)
+
+ for j, row in ipairs(frame) do
+ io.write("\n" .. c:match(row))
+ end
+end
+
+function gol.pre_movie (frames, section)
+ for i, frame in ipairs(frames) do
+ if section then
+ local l = string.len(frame[1])
+ gol.pre_section( l, "Nr. " .. tostring(i) )
+ end
+ gol.pre_frame( frame )
+ end
+ io.write("\n")
+end
+
+return gol