From 665d59554d270d8a56a43a2007981463581a055d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 15 Aug 2010 20:14:01 +0200 Subject: restructured directory layout and renamed most files. --- .../third/simpleslides/automata/automata-life.lua | 420 +++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 module/tex/context/third/simpleslides/automata/automata-life.lua (limited to 'module/tex/context/third/simpleslides/automata/automata-life.lua') 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), +-- 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 -- cgit v1.2.3