diff --git a/polymer/eduke32/Makefile b/polymer/eduke32/Makefile index cccbb40fd..ded1b4baa 100644 --- a/polymer/eduke32/Makefile +++ b/polymer/eduke32/Makefile @@ -167,7 +167,8 @@ ifneq (0,$(LUNATIC)) GAMEOBJS+= $(OBJ)/luaJIT_BC_con_lang.$o \ $(OBJ)/luaJIT_BC_lunacon.$o \ $(OBJ)/luaJIT_BC_geom.$o \ - $(OBJ)/luaJIT_BC_randgen.$o + $(OBJ)/luaJIT_BC_randgen.$o \ + $(OBJ)/luaJIT_BC_stat.$o # now, take care of having the necessary symbols (sector, wall, etc.) in the # executable no matter what the debugging level diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index 0c8937554..282ed66ea 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -547,6 +547,7 @@ local allowed_modules = { randgen = require("randgen"), geom = require("geom"), + stat = require("stat"), } local package_loaded = {} diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist index 850ea2212..94f26c96e 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist +++ b/polymer/eduke32/source/lunatic/dynsymlist @@ -42,6 +42,7 @@ luaJIT_BC_lunacon; luaJIT_BC_con_lang; luaJIT_BC_geom; luaJIT_BC_randgen; +luaJIT_BC_stat; rand_jkiss_u32; rand_jkiss_dbl; diff --git a/polymer/eduke32/source/lunatic/foreachmap.lua b/polymer/eduke32/source/lunatic/foreachmap.lua index c05b45416..1118fb994 100755 --- a/polymer/eduke32/source/lunatic/foreachmap.lua +++ b/polymer/eduke32/source/lunatic/foreachmap.lua @@ -1,4 +1,4 @@ -#!/usr/local/bin/luajit +#!/usr/bin/env luajit -- Generic map iterator. diff --git a/polymer/eduke32/source/lunatic/mapastats.lua b/polymer/eduke32/source/lunatic/mapastats.lua index 493792a62..e5a6f1b45 100644 --- a/polymer/eduke32/source/lunatic/mapastats.lua +++ b/polymer/eduke32/source/lunatic/mapastats.lua @@ -6,6 +6,10 @@ local string = require "string" local math = require "math" local print = print +local type = type + +local stat = require "stat" + module(...) @@ -14,37 +18,29 @@ local function printf(fmt, ...) print(string.format(fmt, ...)) end - -local N = 0 - local sumnumsectors = 0 local sumnumwalls = 0 -local sumratio = 0 -local sumsqratio = 0 +local s = stat.new() function success(map, fn) local ns = map.numsectors local nw = map.numwalls - N = N+1 - - sumratio = sumratio + nw/ns - sumsqratio = sumsqratio + (nw/ns)^2 + s:add(nw/ns) sumnumsectors = sumnumsectors+ns sumnumwalls = sumnumwalls+nw end function finish() - printf("%d maps\n", N) + res = s:getstats() + + printf("%d maps\n", res.n) printf("total sectors: %d", sumnumsectors) printf("total walls: %d", sumnumwalls) printf("total walls / total sectors: %.02f", sumnumwalls/sumnumsectors) printf("") printf("Walls/sector") - - local meanwpers = sumratio/N - printf(" mean: %.02f", meanwpers) - printf(" stdev: %.02f", math.sqrt(sumsqratio/N - meanwpers^2)) + print(res) end diff --git a/polymer/eduke32/source/lunatic/stat.lua b/polymer/eduke32/source/lunatic/stat.lua new file mode 100644 index 000000000..c70a0a700 --- /dev/null +++ b/polymer/eduke32/source/lunatic/stat.lua @@ -0,0 +1,89 @@ +-- Statistics module for Lunatic. + +local ffi = require("ffi") + +local math = require("math") +local string = require("string") + + +module(...) + + +ffi.cdef[[ +typedef struct { + double n; + double m, s; + double min, max; +} runningstat_t; + +typedef struct { + const double n; + const double mean, var, std; + const double min, max; +} runningstat_res_t; +]] + + +local res_mt = { + __tostring = function(s) + return + string.format("N=%d; mean=%.5g, std=%.5g; min=%.5g, max=%.5g", + s.n, s.mean, s.std, s.min, s.max) + end +} +local rstatres = ffi.metatype("runningstat_res_t", res_mt) + + +local mt = { + __tostring = function(s) + -- XXX: with this and the other serializing of ffi types, take care of + -- NaN and Infs when reading back (e.g. by "nan=0/0" in that context) + return "stat.new("..s.n..","..s.m..","..s.s..","..s.min..","..s.max..")" + end, + + -- See: Accurately computing running variance, by John D. Cook + -- http://www.johndcook.com/standard_deviation.html + __index = { + add = function(s, num) + if (s.n > 0) then + -- N>0, recurrence + s.n = s.n+1 + + local lastm = s.m + s.m = lastm + (num-lastm)/s.n + s.s = s.s + (num-lastm)*(num-s.m) + + if (num < s.min) then s.min = num end + if (num > s.max) then s.max = num end + else + -- N==0, initialization + s.n = 1 + + s.m = num + s.s = 0 + + s.min = num + s.max = num + end + end, + + getstats = function(s) + local var = s.n > 1 and s.s/(s.n-1) or 0/0 + return rstatres(s.n, s.m, var, math.sqrt(var), s.min, s.max) + end, + }, +} +local rstat = ffi.metatype("runningstat_t", mt) + +function new(n,m,s, min,max) + if (n == nil) then + -- initialization containing no elements + return rstat(0, 0, 0/0, 0/0, 0/0) + elseif (m == nil) then + -- same as initialization with N==0 above (one element) + return rstat(1, n, 0, n, n) + else + -- generic initialization (internal use only) + return rstat(n,m,s, min,max) + end +end diff --git a/polymer/eduke32/source/lunatic/test.elua b/polymer/eduke32/source/lunatic/test.elua index 5468fd7eb..ffb2c3280 100644 --- a/polymer/eduke32/source/lunatic/test.elua +++ b/polymer/eduke32/source/lunatic/test.elua @@ -229,6 +229,9 @@ gameevent(gv.EVENT_ENTERLEVEL, end ) +local stat = require("stat") +local hs = stat.new() + gameactor(1680, -- LIZTROOP function(i, playeri, dist) sprite[i].pal = math.random(32) @@ -239,8 +242,13 @@ gameactor(1680, -- LIZTROOP local x,y,z = spr.x, spr.y, spr.z local t = gv.gethitickms() local hit = hitscan(x,y,z, spr.sectnum, 10, 10, 0, gv.CLIPMASK0) - printf("hitscan took %.03f us, sec=%d, wal=%d, spr=%d", 1000*(gv.gethitickms()-t), - hit.hitsect, hit.hitwall, hit.hitsprite) + + hs:add(1000*(gv.gethitickms()-t)) + + if (hs.n == 300) then + printf("hitscan: %s", tostring(hs:getstats())) + hs = stat.new() + end end )