Change directory structure

This commit is contained in:
2023-02-14 14:05:45 -05:00
parent acad4291b6
commit def6387ca3
323 changed files with 161 additions and 19581 deletions

21
luprex/lua/basics.lua Normal file
View File

@@ -0,0 +1,21 @@
makeclass('NilIsZero') -- This is intended to be used as a metatable
makeclass('login')
function NilIsZero.__index(t,k)
return 0
end
function NilIsZero.__newindex(t,k,v)
if v~=nil and v~=0 then rawset(t,k,v) end
end
function seq(a,b,c) return a<=b and b<=c end
function bound(a,b,c)
if b<a then return b elseif b>c then return c else return b end
end
function lerp(a,b,c,d,e)
return d+(e-d)*(a-b)/(c-b)
end

11
luprex/lua/control.lst Normal file
View File

@@ -0,0 +1,11 @@
#
# A listing of all the lua files in the control directory,
# in the order that they're supposed to be loaded.
#
ut-table.lua
ut-globaldb.lua
ut-tablecmp.lua
basics.lua
uglyglobals.lua
login.lua

349
luprex/lua/horps.lua Normal file
View File

@@ -0,0 +1,349 @@
makeclass('player')
makeclass('army')
makeclass('orchard')
setmetatable(player,army)
--
-- HORPS game: Walk around the board collecting armies and buffs
-- Captured armies eat each turn
-- Attacking armies select order (random by default)
-- Defending armies are stronger
-- Command: Tame Creature Stack
-- Command: Free Creature Stack
--
--
-- Old Data Structure
-- Army.Count[kind]=number Where kind is r,p,s
-- Player.Count[kind]=number Where kind is r,p,s
--
-- New Data Structure:
-- Player.Stack={ 1=kind, 2=qty, 3=kind, 4=qty,... }
-- Player.Food[kind]=Number
-- orchard.crop='r'
-- orchard.size=integer -- Max size it can grow to
-- orchard.rate=integer -- How many grow per unit time()
-- orchard.pick=integer -- time() it was last harvested
--
function mapable(a)
if isa(a,login) then return false end
return not isa(a,player)
end
function orchard:available()
return math.min(self.size,self.rate*(time()-self.pick))
end
function bound(a,b,c)
if b<a then return b elseif b>c then return c else return b end
end
function lerp(a,b,c,d,e)
return d+(e-d)*(a-b)/(c-b)
end
function orchard:harvest(n)
local a=self:available()
if n>=a then self.pick=time() return a end
self.pick=lerp(n,0,a,self.pick,time())
return n
end
function player.interface(actor, place)
gui.menu_item("cb_north" ,"Go North")
gui.menu_item("cb_south" ,"Go South")
gui.menu_item("cb_east" ,"Go East")
gui.menu_item("cb_west" ,"Go West")
gui.menu_item("cb_map" ,"Show the Map")
gui.menu_item("cb_conjurerock" ,"Conjure Rock")
gui.menu_item("cb_conjurepaper" ,"Conjure Paper")
gui.menu_item("cb_conjurescissor" ,"Conjure Scissor")
gui.menu_item("cb_droprock" ,"Drop Rock")
gui.menu_item("cb_droppaper" ,"Drop Paper")
gui.menu_item("cb_dropscissor" ,"Drop Scissor")
end
function player:cb_conjurerock() self:conjure('r',1) end
function player:cb_conjurepaper() self:conjure('p',1) end
function player:cb_conjurescissor() self:conjure('s',1) end
function player:conjure(k,c)
for i=1,#self.stack,2 do if self.stack[i]==k then self.stack[i+1]=self.stack[i+1]+c self:cb_map() return end end
self.stack[#self.stack+1]=k
self.stack[#self.stack+1]=c
self:cb_map()
end
function army.interface(actor,place)
if place.owner==actor then gui_menu_item("cb_recruit","Recruit");
else gui.menu_item("cb_fight" ,"Fight");
end end
-- Rock dulls Scissors 3
-- Rock cripples Lizard 2
-- Paper disproves Spock 3
-- Paper covers Rock 2
-- Scissors decapitates Lizard 3
-- Scissors cuts Paper 2
-- Lizard eats Paper 3
-- Lizard poisons Spock 2
-- Spock vaporizes Rock 3
-- Spock dismantles Scissors 2
armytypes={'r','p','s'}
armynames={r="Rock Golem" ,p="Paper Dragon",s="Scissor Beast",l="Fire Lizzard",v="Mr. Spock"}
foodtypes={'r','p','s','l','v'}
foodnames={r="Raspberry" ,p="Pomegranite" ,s="Strawberry" ,l="Lemon" ,v="Mango" }
--advantage={ r={ r=1, p=1/2, s=3, l=2, v=1/3 },
-- p={ r=2, p=1, s=1/2, l=1/3, v=3 },
-- s={ r=1/3, p=2, s=1, l=3, v=1/2 },
-- l={ r=1/2, p=3, s=1/3, l=1, v=2 },
-- v={ r=3, p=1/3, s=2, l=1/2, v=1 } }
advantage={ r={ r=1, p=1/2, s=2 },
p={ r=2, p=1, s=1/2 },
s={ r=1/2, p=2, s=1 } }
function shuffle(t)
local s={}
for i=1, #t do s[i]=t[i] end
for i=#t,2,-1 do
local j=math.random(i)
s[i],s[j]=s[j],s[i]
end
return s
end
function MakeMap()
local rad=6
for x=-rad,rad do for y=-rad,rad do
itemkind=math.random(1,7)
if itemkind==1 then
local nt=tangible.build{class='army',x=x,y=y,z=0,plane='main',graphic='army'}
local stack={}
local shuf=shuffle(armytypes)
for i=1,#shuf do if math.random(1,2)==1 then
stack[1+#stack]=shuf[i]
stack[1+#stack]=math.random(1,3)
end end
nt.stack=stack
elseif itemkind==2 then
local nt=tangible.build{class='orchard',x=x,y=y,z=0,plane='main',graphic='orchard'}
nt.crop=foodtypes[math.random(1,#foodtypes)]
nt.size=10
nt.rate=0.1
nt.pick=time()
end
end end
end
--
-- For each creature type, select the optimal target. Select randomly among identical targets.
--
function fight0(ak,ac,dk,dc,tweekdefender) -- returns number of attacker casualties, defender casualties
local adv0=advantage[ak][dk]
local adv1=advantage[dk][ak]
if not tweekdefender then tweekdefender=function(adv) return adv end end
adv1=tweekdefender(adv1)
local losstotal=(ac+dc)/2
local power0=ac*adv0
local power1=dc*adv1
local powertotal=power0+power1
local loss0=math.min(ac,math.floor(losstotal*power1/powertotal))
local loss1=math.min(dc,math.floor(losstotal*power0/powertotal))
if loss0+loss1==0 then loss0=loss0+1 end
return loss0,loss1
end
function player:near_iter(rad,kind)
local lis=tangible.near(self,rad,true,true)
local i=1
return function()
while i<=#lis do
i=i+1
if not kind or isa(lis[i-1],kind) then return lis[i-1] end end end
end
function stack_iter(t1)
local i=0
return function()
if i+2>#t1 then return end
i=i+2
return t1[i-1],t1[i]
end end
function fight_iter(t1,t2) -- Returns quads: AttackerKind,AttackerCount,DefenderKind,DefenderCount
local i=0
return function()
if i+2>#t1 or i+2>#t2 then return end
i=i+2
return t1[i-1],t1[i],t2[i-1],t2[i]
end end
function compactstack(s)
local index=1
while index<=#s do
if s[index+1]==0 then table.remove(s,index) table.remove(s,index)
else index=index+2 end
end
end
function army:counttroops()
local rval=0
for k,c in stack_iter(self.stack) do rval=rval+c end
return rval
end
function army:die()
print("You defeat the enemy!")
tangible.delete(self)
end
function army:fight(enemy)
for i=1,math.min(#self.stack,#enemy.stack),2 do
loss1,loss2=fight0(self.stack[i],self.stack[i+1],enemy.stack[i],enemy.stack[i+1])
print("When "..self.stack[i+1].." "..self.stack[i].." fight "..enemy.stack[i+1].." "..enemy.stack[i].." they kill "..loss2.." and suffer "..loss1)
self.stack[i+1]=self.stack[i+1]-loss1
enemy.stack[i+1]=enemy.stack[i+1]-loss2
end
compactstack(self.stack)
compactstack(enemy.stack)
if enemy:counttroops()==0 then enemy:die() return false
else return true end
end
function player:cb_droprock(actor) player:droparmy(actor,'r') end
function player:cb_droppaper(actor) player:droparmy(actor,'p') end
function player:cb_dropscissor(actor) player:droparmy(actor,'s') end
function player:droparmy(actor,kind)
if actor.Count[kind]<=0 then print("No armies of type "..kind.." available") return end
local lis=tangible.near(actor,0,true,true)
if #lis>1 then print("Multiple Tangibles Nearby") return end
local t=lis[1]
if t==nil then t=player:buildarmy(actor) end
t.Count[kind]=t.Count[kind]+1
end
function player:pickfruit(ft)
local kind=ft.crop
local qty=ft:harvest(ft:available())
self.food[kind]=self.food[kind]+qty
print("You harvest "..qty.." "..foodnames[kind])
end
function player:newlocation() -- return false if the player should bounce back, else return true
local lis=tangible.near(self,0,true,true)
local count={} setmetatable(count,NilIsZero)
local count0=0
local bounce=false
for _,t in ipairs(lis) do
if isa(t,army) then bounce=bounce or self:fight(t)
elseif isa(t,orchard) then self:pickfruit(t) end
end
return bounce
end
function player:printanimstate()
local graphic,plane,x,y,z,facing = tangible.animstate(self)
print("Resulting state: ", graphic, plane, x, y, z, facing)
end
function player:walk(dx,dy)
tangible.animate(self,{action='walk',dx=dx,dy=dy})
if self:newlocation() then tangible.animate(self,{action='walk',dx=-dx,dy=-dy}) end
self:cb_map()
end
function player:cb_north()
self:walk(0,1)
end
function player:cb_south()
self:walk(0,-1)
end
function player:cb_east()
self:walk(1,0)
end
function player:cb_west()
self:walk(-1,0)
end
function player:buildarmy(actor)
local _,pl,ax,ay=tangible.animstate(actor)
t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='army'}
local nt=tangible.build(t)
nt.Count={}
setmetatable(nt.Count,{__index=function(t,k) return 0 end,__newindex=function(t,k,v) if v~=nil and v~=0 then rawset(t,k,v) end end})
return nt
end
makeclass('army')
function seq(a,b,c) return a<=b and b<=c or false end
function num2(a) if a<=9 then return " "..a else return a end end
function where()
local x,y,z=tangible.xyz(tangible.actor())
print("You are at "..x.." "..y)
end
function mapcelltext(lis)
if lis==nil then return ' ' end
if #lis>1 then return '++++++' end
local rval=""
local prefix=""
local suffix=""
if isa(lis[1],player) then prefix="\27[91;7m" suffix="\27[0m" end
if isa(lis[1],army) then
for k,c in stack_iter(lis[1].stack) do rval=rval..k..(c>9 and '+' or tostring(c)) end
rval=rval..string.sub(' ',1,6-string.len(rval))
elseif isa(lis[1],orchard) then
prefix="\27[32;1m" suffix="\27[0m"
rval=rval..lis[1].crop..string.format("%-5d",lis[1]:available())
end
return prefix..rval..suffix
end
function player.cb_map(actor,place,dialog)
print("\27[s\27[0;0f")
local rad=4
local ax,ay=tangible.xyz(actor)
scratch={}
local lis=tangible.near(actor,1.5*rad,true,false)
for _,t in pairs(lis) do
local c=tangible.getclass(t)
local dx,dy=tangible.xyz(t)
local offset=(-dy+ay+rad)*(rad*2+1)+dx-ax+rad
local cl=tangible.getclass(t)
if seq(ax-rad,dx,ax+rad) and seq(ay-rad,dy,ay+rad) and (cl=='player' or cl=='army' or cl=='orchard') then
if not scratch[offset] then scratch[offset]={} end
scratch[offset][1+#scratch[offset]]=t
end end
for dy=-rad,rad do
local lbuf=""
for dx=-rad,rad do
local offset=(dy+rad)*(rad*2+1)+dx+rad
lbuf=lbuf..'|'..mapcelltext(scratch[offset])
end
lbuf=lbuf..'|'
print(lbuf)
end
print("\27[u")
end

44
luprex/lua/login.lua Normal file
View File

@@ -0,0 +1,44 @@
makeclass('login')
function login.interface(actor, place)
gui.menu_item("cb_becomeplayer", "Become a Player")
gui.menu_item("cb_p123", "Print 1, 2, 3")
gui.menu_item("cb_p123_nopredict", "Print 1, 2, 3 nopredict")
gui.menu_item("cb_uglytimedaemon","Start the Time Daemon")
gui.menu_item("cb_seedprng","Seed the PRNG")
end
function login.cb_seedprng(actor,place,dialog)
local result=http.get{method='GET',host='www.random.org',path='/cgi-bin/randbyte',params={nbytes='32',format='f'},verifycertificate=false}
pprint(result)
end -- "GET /cgi-bin/randbyte?nbytes=16384&format=f HTTP/1.0\nHost: www.random.org\n\n"
function login.cb_becomeplayer(actor, place, dialog)
actor.kind='P'
actor.Count={} setmetatable(actor.Count,NilIsZero)
actor.food={} setmetatable(actor.food,NilIsZero)
actor.spectra={} setmetatable(actor.spectra,NilIsZero)
actor.stack={}
actor.team='red'
tangible.setclass(actor, player)
tangible.animate(actor,{action="warp",plane="main",x=0,y=0,z=0})
end
function login.cb_p123(actor, place, dialog)
print(1)
wait(1)
print(2)
wait(1)
print(3)
end
function login.cb_p123_nopredict(actor, place, dialog)
nopredict()
print(1)
wait(1)
print(2)
wait(1)
print(3)
end

7
luprex/lua/prng.lua Normal file
View File

@@ -0,0 +1,7 @@
makeclass('prng')
function prng.seed(n)
ug.set(prngseed,n)
end
function

162
luprex/lua/spectra.lua Normal file
View File

@@ -0,0 +1,162 @@
makeclass('player')
makeclass('light')
freqcolor={ azure=152, black=35200, blue=59300, brown=20000, chocolate=1840, cream=8610,
crimson=575, fawn=272, gold=17300, green=34200, grey=17300, lemon=445,
lilac=1020, mauve=719, ochre=132, olive=1910, orange=22500, peach=3860,
pink=27200, purple=13700, red=52200, rose=4310, ruby=994, russet=68,
scarlet=315, silver=13200, violet=3590, white=45100, yellow=24400 }
function player:near_iter(rad,kind1,kind2,kind3)
local lis=tangible.near(self,rad,true,true)
local i=1
return function()
while i<=#lis do
i=i+1
if not kind1 or isa(lis[i-1],kind1,kind2,kind3) then return lis[i-1] end end end
end
function tandist(t1,t2)
local _,t1p,t1x,t1y=tangible.animstate(t1)
local _,t2p,t2x,t2y=tangible.animstate(t2)
return math.sqrt((t1x-t2x)*(t1x-t2x)+(t1y-t2y)*(t1y-t2y))
end
function player:radius_iter(rad,kind)
local lis=tangible.near(self,rad,true,true)
local i=1
return function()
while i<=#lis do
i=i+1
if (not kind or isa(lis[i-1],kind)) and tandist(self,lis[i-1])<=rad then return lis[i-1] end end end
end
function player:interface(place)
gui.menu_item("cb_dir0" ,"Go North")
gui.menu_item("cb_dir180" ,"Go South")
gui.menu_item("cb_dir270" ,"Go West")
gui.menu_item("cb_dir90" ,"Go East")
gui.menu_item("cb_map" ,"Map")
gui.menu_item("cb_spectra" ,"Show Spectra")
end
function player:cb_spectra()
for k,v in pairs(self.spectra) do print(string.format("%-6.6s:%s",k,string.sub('+++++++++++++++++++++++++',1,v))) end
end
function player:cb_dir0() self:move(0) end
function player:cb_dir45() self:move(45) end
function player:cb_dir90() self:move(90) end
function player:cb_dir135() self:move(135) end
function player:cb_dir180() self:move(180) end
function player:cb_dir215() self:move(215) end
function player:cb_dir270() self:move(270) end
function player:cb_dir315() self:move(315) end
function player:power()
local rval=0
for k,v in pairs(self.spectra) do if v>0 then rval=rval+1 end end
return rval
end
function player:respawn()
tangible.animate(self,{action='warp',plane='main',x=math.random(-40,40),y=math.random(-40,40),z=0})
self:cb_map()
print("You just died!") --
end
function player:fight(enemy)
local common={}
for k,v in pairs(self.spectra) do if enemy.spectra[k]>0 then common[k]=1 end end
local p0=self:power()
local p1=enemy:power()
local winner
if p0==p1 then
for k,v in pairs(common) self.spectra[k]=nil enemy.spectra[k]=nil end
else if p0>p1 then
for k,v in pairs(common) enemy.spectra[k]=nil end
else
for k,v in pairs(common) self.spectra[k]=nil end
end
p0=self:power()
p1=enemy:power()
if p1==0 then self.kills =self.kills+1 enemy.deaths=enemy.deaths+1 enemy:respawn() end
if p0==0 then self.deaths=self.deaths+1 enemy.kills =enemy.kills+1 self:respawn() end
end
function player:newlocation()
for lt in self:radius_iter(3.1,light) do self.spectra[lt.color]=25 end
for lt in self:radius_iter(3.1,player) do if self.team==lt.team then
local nspec={} setmetatable(nspec,NilIsZero)
for k,v in pairs(self.spectra) do nspec[k]=v end
for k,v in pairs(lt.spectra) do nspec[k]=math.max(v,nspec[k]) end
for k,v in pairs(nspec) do self.spectra[k]=v lt.spectra[k]=v end
end end
for lt in self:radius_iter(3.1,player) do if self.team~=lt.team then p1:fight(p2) end end
for k,v in pairs(self.spectra) do self.spectra[k]=self.spectra[k]-1 if self.spectra[k]<=0 then self.spectra[k]=nil end end
end
function player:move(degrees)
dx= math.cos((90-degrees)*math.pi/180)
dy=-math.sin((90-degrees)*math.pi/180)
tangible.animate(self,{action='walk',dx=dx,dy=dy})
self:newlocation()
self:map(12)
end
function wrcolor()
local acc=0
local rval
for k,v in pairs(freqcolor) do acc=acc+v if math.random(1,acc)<=v then rval=k end end
return rval
end
function makemap()
for i=1,100 do
local x=math.random(-50,50)
local y=math.random(-50,50)
local nt=tangible.build{class='light',x=x,y=y,z=0,plane='main',graphic='light'}
nt.color=wrcolor()
end
end
function setcell(array,x,y,val)
if not array[y] then array[y]={} end
array[y][x]=val
return array
end
function player:cb_map(place,dialog)
self:map(10)
end
teamcolor={ red='\27[91m', green='\27[92m', yellow='\27[93m', blue='\27[94m', magenta='\27[95m', cyan='\27[96m' }
function player:map(radius)
local radius=10
radius=radius or 10
local cells={}
local px,py=tangible.xyz(tangible.actor())
for lt in self:near_iter(1.5*radius,light,player) do
local lx,ly=tangible.xyz(lt)
lx=lx-px+radius
ly=ly-py+radius
if seq(0,ly,1+2*radius) and seq(0,lx,1+2*radius) then
local celltext
if isa(lt,light) then celltext=string.format("%-6.6s",lt.color)
elseif isa(lt,player) then celltext=teamcolor[lt.team].."Pl:"..string.format("%-3.0f",lt:power()).."\27[0m"
end
setcell(cells,math.floor(0.5+lx),math.floor(0.5+ly),celltext) end
end
setcell(cells,radius,radius,teamcolor[self.team]..string.format("Me:%-3.0f",self:power()).."\27[0m")
for ly=0,1+2*radius do
local line='|'
for lx=0,1+2*radius do
if cells[ly] and cells[ly][lx] then line=line..cells[ly][lx]..'|' else line=line..' |' end
end
print(line)
end
end

41
luprex/lua/teppygame.lua Normal file
View File

@@ -0,0 +1,41 @@
makeclass('grass')
if global.once("build grass") then
for y=0,4 do for x=0,y%2==1 and 3 or 4 do
tangible.build{class="grass",x=y%2==1 and 2*x+1 or 2*x+1,y=2*y+1,z=0,plane="main",graphic="grass"}
end end
end
if global.once("walkable") then global.walkable={} end
if global.once("walkable main") then global.walkable["main"]={} end
function walkable(pl,x,y)
if not global.walkable then return false end
if not global.walkable[pl] then return false end
if not global.walkable[pl][y] then return false end
return global.walkable[pl][y][x] or false
end
function setwalkable(pl,x,y,val)
if not global.walkable[pl][y] then global.walkable[pl][y]={} end
global.walkable[pl][y][x]=val
end
if global.once("walkablexy") then for y=0,4 do for x=0,4 do setwalkable("main",x,y,true) end end end
function player.move(actor,place,dx,dy)
local _,pl,x,y,_,_=tangible.animstate(actor)
print("Checking walkable "..pl.." "..x.." "..y.." is "..(walkable(pl,x+dx,y+dy) and "true" or "false"))
if not walkable(pl,x+dx,y+dy) then print("Not walkable") else
tangible.animate(place, {action="walk", dx=dx,dy=dy})
local lis=tangible.near(actor,1,false,true)
if #lis==0 then print("The area is empty")
else for _,t in pairs(lis) do
print("There is a "..tangible.getclass(t).." here")
end end
end
_,pl,x,y,_,_=tangible.animstate(actor)
print("Your location is "..x.." "..y)
end

View File

@@ -0,0 +1,52 @@
makeclass('ug')
function ug.the()
return tangible.scan('globals',0,0,0,true)[1]
end
lis=tangible.scan('globals',0,0,0,true)
if #lis==0 then
local ugid=tangible.build{class='ug',x=0,y=0,z=0,plane='globals',graphic='box'}
print("The global table is "..tangible.id(ugid))
end
function ug.set(var,val)
ug.the()[var]=val
end
function ug.get(var)
return ug.the()[var]
end
function login.cb_uglytimedaemon()
if not ug.get('time') then ug.set('time',0) end
while true do
wait(1)
ug.set('time',ug.get('time')+1)
end
end
function time() return ug.get('time') end
function isa(k1,k2,k3,k4)
if k1==k2 or k1==k3 or k1==k4 then return true end
if k1==nil then return false end
local mt=getmetatable(k1)
if not mt then return false end
return isa(mt.__index,k2,k3,k4)
end
function tabcat(t1,t2)
for i=1,#t2 do t1[#t1+1]=t2[i] end
end
-- Example: multistart(function(t) return tangible.id(t)%3==0 end,function() print("Tangible "..tangible.id(tangible.place()).." here") end)
function multistart(fil,closure)
local lis=tangible.scan('nowhere',0,0,100,false)
tabcat(lis,tangible.scan('main',0,0,100,true))
local filter=type(fil)=='function' and fil or function(t) return isa(t,fil) end
for _,t in ipairs(lis) do if filter(t) then tangible.start(t,closure) end end
end

View File

@@ -0,0 +1,13 @@
makeclass("unittests")
function unittests.globaldb()
local g1a = global.table("unittest-g1")
local g2a = global.table("unittest-g2")
local g1b = global.table("unittest-g1")
local g2b = global.table("unittest-g2")
assert(g1a == g1b)
assert(g2a == g2b)
assert(g1a.__global == "unittest-g1")
assert(g2a.__global == "unittest-g2")
end

85
luprex/lua/ut-table.lua Normal file
View File

@@ -0,0 +1,85 @@
makeclass("unittests")
function unittests.tables()
-- check table.count
assert(table.count({}) == 0)
assert(table.count({a=1,b=2}) == 2)
assert(table.count({[2]=5,[5]=3}) == 2)
-- check table.clear
local t = { a = 1, b = 2 }
table.clear(t, true)
assert(t.a == nil)
assert(t.b == nil)
assert(table.count(t) == 0)
-- check table.empty
assert(table.empty({}) == true)
assert(table.empty({1}) == false)
assert(table.empty({a=1}) == false)
-- check table.equal
assert(table.equal({},{}))
assert(not table.equal({}, {1}))
assert(not table.equal({1}, {}))
assert(table.equal({1,2,3}, {1,2,3}))
assert(not table.equal({1,2,3}, {1,5,3}))
assert(not table.equal({1,2}, {1,2,3}))
assert(not table.equal({1,2,3}, {1,2}))
assert(table.equal({a=1,b=2},{a=1,b=2}))
assert(not table.equal({a=1,b=3},{a=1,b=2}))
-- check table.push
t = {}
table.push(t, 1)
assert(table.equal(t, {1}))
table.push(t, 2)
assert(table.equal(t, {1,2}))
table.push(t, 3)
assert(table.equal(t, {1,2,3}))
-- check table.findremove
t = {1,2,3,4,5,1,2,3,4,5}
table.findremove(t, 2)
assert(table.equal(t, {1,3,4,5,1,3,4,5}))
table.findremove(t, 5)
assert(table.equal(t, {1,3,4,1,3,4}))
table.findremove(t, 1)
assert(table.equal(t, {3,4,3,4}))
end
function unittests.deque()
local d = deque.create()
for i=1,7 do
for j=1,i do
d:pushr(j)
end
for j=1,i do
assert(d:nthl(j) == j)
end
for j=1,i do
assert(d:popl() == j)
end
end
for i=1,7 do
for j=1,i do
d:pushl(j)
end
for j=1,i do
assert(d:nthr(j) == j)
end
for j=1,i do
assert(d:popr() == j)
end
end
for i=1,7 do
for j=1,i do
d:pushr(j)
end
for j=1,i do
assert(d:popr() == i+1-j)
end
end
end

145
luprex/lua/ut-tablecmp.lua Normal file
View File

@@ -0,0 +1,145 @@
-- the tdc function calculates diffs, and returns those
-- diffs as a human-readable string.
local tdc = table.diffcompare
-- the tda function calculates diffs, applies the diffs to the second
-- table, and then returns true if the second table equals the first.
local tda = table.diffapply
function unittests.diffcompare()
local mtab = nil
local rtab = nil
-- No differences in these simple-valued tables.
assert(tdc({}, {a=true}, {}, {a=true}) == "")
assert(tdc({}, {a=5}, {}, {a=5}) == "");
assert(tdc({}, {a="foo"}, {}, {a="foo"}) == "")
-- Test transmission of missing simple values.
assert(tdc({}, {a=true}, {}, {}) == "a=true;")
assert(tdc({}, {a=5}, {}, {}) == "a=5;");
assert(tdc({}, {a="foo"}, {}, {}) == "a=foo;")
-- Test the replacement of simple values.
assert(tdc({}, {a=true}, {}, {a=false}) == "a=true;")
assert(tdc({}, {a=5}, {}, {a=4}) == "a=5;");
assert(tdc({}, {a="foo"}, {}, {a="bar"}) == "a=foo;")
-- Test the clearing of values.
assert(tdc({}, {}, {}, {a=true}) == "a=nil;")
assert(tdc({}, {}, {}, {a=5}) == "a=nil;");
assert(tdc({}, {}, {}, {a="foo"}) == "a=nil;")
-- Try boolean keys.
assert(tdc({}, {[true]=3}, {}, {}) == "true=3;")
assert(tdc({}, {}, {}, {[true]=3}) == "true=nil;")
-- Try number keys.
assert(tdc({}, {[7]=3}, {}, {}) == "7=3;")
assert(tdc({}, {}, {}, {[7]=3}) == "7=nil;")
-- Try a table with multiple keys.
assert(tdc({}, {a=4, b=5, c=6}, {}, {b=5, c=7, d=8}) == "a=4;c=6;d=nil;")
-- Nonsortable keys should be ignored (no diffs).
assert(tdc({}, {[{}]=3}, {}, {}) == "")
-- Try a table containing a class.
assert(tdc({}, {a=deque}, {}, {}) == "a=class deque;")
-- Try a table containing a pointer to the global environment.
assert(tdc({}, {a=_G}, {}, {}) == "a=globals;")
-- GlobalDB tables should be forced to NIL.
assert(tdc({}, {a=global.table("foo")}, {}, {a=global.table("foo")}) == "a=nil;");
assert(tdc({}, {}, {}, {a=global.table("foo")}) == "a=nil;");
assert(tdc({}, {a=global.table("foo")}, {}, {}) == "");
-- Set up some numbered tables for tests involving such.
local mtab10 = {}
local stab10 = {}
local mtnmap = {}
local stnmap = {}
mtnmap[mtab10] = 10
stnmap[stab10] = 10
-- confirm that numbered tables are being transmitted.
assert(tdc(mtnmap, {a=mtab10}, stnmap, {}) == "a=table 10;")
assert(tdc(mtnmap, {a=mtab10}, stnmap, {a=stab10}) == "")
assert(tdc(mtnmap, {a=3}, stnmap, {a=stab10}) == "a=3;")
assert(tdc(mtnmap, {}, stnmap, {a=stab10}) == "a=nil;")
-- confirm that unnumbered tables are forced to nil.
assert(tdc(mtnmap, {a={}}, stnmap, {}) == "")
assert(tdc(mtnmap, {a={}}, stnmap, {a=3}) == "a=nil;")
-- transmit a correction to the metatable.
mtab={}
stab={}
setmetatable(mtab, mtab10)
setmetatable(stab, stab10)
assert(tdc(mtnmap, mtab, stnmap, {}) == "nil=table 10;")
assert(tdc(mtnmap, mtab, stnmap, stab) == "")
assert(tdc(mtnmap, {}, stnmap, stab) == "nil=nil;")
-- we're not going to test tangibles
-- creating tangibles here is too difficult.
end
function unittests.diffapply()
local tab10={id=tab10}
local tab11={id=tab11}
local tnmap={}
tnmap[tab10] = 10
tnmap[tab11] = 11
local mtab=nil
local stab=nil
-- verify some simple values.
assert(tda(tnmap, {a=1}, {}))
assert(tda(tnmap, {[true]="foo"}, {}))
assert(tda(tnmap, {[3]=false}, {}))
-- verify a table with multiple simple values.
assert(tda(tnmap, {a=1, b=2, c=3}, {}))
-- verify that it can remove or replace wrong values.
assert(tda(tnmap, {a=1,b=2}, {b=3,c=4}))
-- verify a table containing another table.
assert(tda(tnmap, {a=tab10, b=tab11}, {}))
-- verify a table containing a class.
assert(tda(tnmap, {a=deque, b=table}, {}))
-- verify a table containing the global environment.
assert(tda(tnmap, {a=_G}, {}))
-- GlobalDB tables should be forced to NIL.
rtab={a=3}
assert(not tda({}, {a=global.table("foo")}, rtab))
assert(rtab.a == nil)
-- Unnumbered tables should be forced to NIL.
rtab={a=3}
assert(not tda({}, {a={}}, rtab))
assert(rtab.a == nil)
-- transmit a correction to the metatable
mtab={}
rtab={}
setmetatable(mtab, tab10)
assert(tda(tnmap, mtab, rtab))
assert(getmetatable(rtab) == tab10)
-- transmit a clearing of the metatable
mtab={}
rtab={}
setmetatable(rtab, tab10)
assert(tda(tnmap, mtab, rtab))
assert(getmetatable(rtab) == nil)
-- don't test tangibles.
end

0
luprex/lua/util.lua Normal file
View File