From 5e6b98523be3de71363eb17aeabbbc305569487f Mon Sep 17 00:00:00 2001 From: Kistaro Windrider Date: Mon, 23 Jun 2025 00:47:02 -0700 Subject: [PATCH] delete linked lists and use optimized array impls. Also removes "suppress" table from collider, use candidate.dead instead because the set of dead ships and the set of non-colliding ships is identical. --- vacuum_gambit.p8 | 205 +++++++++++++++++------------------------------ 1 file changed, 72 insertions(+), 133 deletions(-) diff --git a/vacuum_gambit.p8 b/vacuum_gambit.p8 index 6e3e997..67852b2 100644 --- a/vacuum_gambit.p8 +++ b/vacuum_gambit.p8 @@ -59,90 +59,48 @@ function mknew(tt) return tt end --- intrusive singly-linked list. --- cannot be nested or crossed! -linked_list = mknew{ - is_linked_list=true, - init = function(x) - x.next=nil - x.tail=x - end, -} - -function linked_list:push_back(x) - self.tail.next = x - self.tail = x -end - -function linked_list:push_front(x) - if (not self.next) self.tail = x - x.next = self.next - self.next = x -end - --- vore eats another linked list --- by appending its contents. --- the ingested linked is empty. -function linked_list:vore(x) - if (not x.next) return - self.tail.next = x.next - self.tail = x.tail - x.next = nil - x.tail = x -end - --- strip calls f(x) for each --- node, removing each node for --- which f(x) returns true. -function linked_list:strip(f) - local p, n = self, self.next - while n do - if f(n) then - p.next = n.next - else - p = n +-- call f on everything on list +-- and return a new list of +-- items for which f was false. +function stripped(list, f) + local ret, n = {}, 0 + for i=1,#list do + local v = list[i] + if not f(v) then + n += 1 + ret[n] = v end - n = n.next end - self.tail = p + return ret end --- stripmove calls x:move() for --- each node, removing each node --- for which x:move() is true. -function linked_list:stripmove() - local p, n = self, self.next - while n do - if n:move() then - p.next = n.next - else - p = n +-- call :move on everything on +-- src and dump everything +-- for which it returned false +-- onto dest. +function appendmove(dest, src) + local n = #dest + for i=1, #src do + local v = src[i] + if not v:move() then + n += 1 + dest[n] = v end - n = n.next - end - self.tail = p -end - --- optimized special case - --- could be done with strip but --- this avoids extra function --- calls and comparisions since --- draw isn't allowed to kill --- the item -function linked_list:draw() - local n = self.next - while n do - n:draw() - n = n.next end end - -function linked_list:pop_front() - local ret = self.next - if (not ret) return - self.next = ret.next - if (not ret.next) ret.tail = nil +-- like stripped, but calls +-- :move on stuff in the list +-- instead of taking a func arg. +function stripmoved(list) + local ret, n = {}, 0 + for i=1,#list do + local v = list[i] + if not v:move() then + n += 1 + ret[n] = v + end + end return ret end @@ -156,12 +114,12 @@ function _init() end function once_next_frame(f) - new_events:push_back{ + add(new_events, { move = function() f() return true end, - } + }) end -- health gradients for 1..5 hp @@ -182,13 +140,13 @@ function wipe_game() xpwhoosh = nil primary_ship = player.new() init_hpcols() - eships = linked_list.new() - pbullets = linked_list.new() - ebullets = linked_list.new() - intangibles_fg = linked_list.new() - intangibles_bg = linked_list.new() - events = linked_list.new() - new_events = linked_list.new() + eships = {} + pbullets ={} + ebullets ={} + intangibles_fg = {} + intangibles_bg = {} + events = {} + new_events = {} primary_ship.main_gun = zap_gun_p.new() primary_ship.main_gun:peel() gframe = 0 @@ -241,10 +199,11 @@ function updategame() current_wave = flotilla.new() current_wave:load(rnd() > 0.5 and 7 or 0, 0, min(ones(waves_complete)\2, 4)) end - events:vore(new_events) - for _, lst in ipairs{events, intangibles_bg, eships} do - lst:stripmove() - end + events = stripmoved(events) + appendmove(events, new_events) + new_events = {} + intangibles_big = stripmoved(intangibles_bg) + eships = stripmoved(eships) -- eship collider will be used -- both for pship and pbullets. @@ -255,9 +214,9 @@ function updategame() local pbox = hurtbox(ps) for es in eship_collider:iterate_collisions(pbox) do ps:hitship(es) - if(es:hitship(ps)) eship_collider:yoink(es) + es:hitship(ps) end - ebullets:strip(function(eb) + ebullets = stripped(ebullets, function(eb) if (eb:move()) return true if (not collides(pbox, hurtbox(eb))) return ps:hitbullet(eb) @@ -267,17 +226,17 @@ function updategame() ebullets:stripmove() end - pbullets:strip(function(pb) + pbullets = stripped(pbullets, function(pb) if (pb:move()) return true for es in eship_collider:iterate_collisions(hurtbox(pb)) do - if (es:hitbullet(pb)) eship_collider:yoink(es) + es:hitbullet(pb) if (pb:hitship(es)) return true end end) intangibles_fg:stripmove() - if waves_complete == 32767 and not eships.next and not ebullets.next and not events.next then + if waves_complete == 32767 and #eships == 0 and #ebullets == 0 and #events == 0 then game_state = win end if (ps.dead) game_state = lose @@ -345,15 +304,6 @@ function puke(item, indent, seen, hidekey) end local xpfx = pfx.." " - if item.is_linked_list then - local ret,n = "linked_list <",0 - item:strip(function(x) - n += 1 - ret ..= xpfx..tostr(n)..": "..puke(x, indent+2, seen, "next") - end) - return ret..pfx..">" - end - local ret = "{" for k, v in pairs(item) do if (k ~= hidekey) ret ..= xpfx..tostr(k)..": "..puke(v, indent+2, seen) @@ -373,8 +323,10 @@ end function drawgame() clip(0,0,112,128) rectfill(0,0,112,128,0) - for drawable in all{intangibles_bg, pbullets, primary_ship, eships, ebullets, intangibles_fg} do - drawable:draw() + for drawables in all{intangibles_bg, pbullets, {primary_ship}, eships, ebullets, intangibles_fg} do + for item in all(drawables) do + item:draw() + end end clip(0,0,128,128) drawhud() @@ -586,6 +538,7 @@ function ship_m:constrain(p, dp, pmin, pmax, want) end function ship_m:move() + if (self.dead) return true; self:refresh_shield() local dx, dy, shoot_spec1, shoot_spec2 = self:act() local sg, xm, ym = self.special_guns, self.xmomentum, self.ymomentum @@ -842,7 +795,7 @@ end function bullet_base:spawn_at(x, y) self.x = x - self.x_off self.y = y - self.y_off - self.category():push_back(self) + add(self.category(), self) end function gun_base:shoot(x, y) @@ -954,7 +907,7 @@ blast = mknew(bullet_base.new{ if self.damage > 0 and not self.awaitcancel then self.awaitcancel = true once_next_frame(function() - new_events:push_back{ + add(new_events, { wait = 4, obj = self, saved_dmg = self.damage, @@ -965,7 +918,7 @@ blast = mknew(bullet_base.new{ return true end end, - } + }) self.damage = 0 self.awaitcancel = false end) @@ -1418,10 +1371,10 @@ end collider = mknew{ init = function(x) - x.suppress = {} - local p, n = x.from, x.from.next - while n do + local from = x.from + for k=1,#from do -- insert + local n = from[k] for i in all(collider_indexes(hurtbox(n))) do local a = x[i] if not a then @@ -1430,35 +1383,21 @@ collider = mknew{ end add(a, n) end - -- prepare yoink - n.prev = p - p = n - n = n.next end end, } function collider_indexes(box) - local ret = {} + local ret,i = {}, 0 for x = box.x\8, (box.x+box.width)\8 do for y = box.y\8, (box.y+box.height)\8 do - add(ret, x+256*y) + i += 1 + ret[i] = x+256*y end end return ret end -function collider:yoink(item) - self.suppress[item]=true - local p,n = item.prev,item.next - p.next = n - if n then - n.prev = p - else - self.from.tail = p - end -end - function collider:iterate_collisions(box) local seen = { } local bucket_ids = collider_indexes(box) @@ -1474,7 +1413,7 @@ function collider:iterate_collisions(box) bi += 1 if not seen[candidate] then seen[candidate] = true - if (not self.suppress[candidate] and collides(box, hurtbox(candidate))) return candidate + if (not candidate.dead and collides(box, hurtbox(candidate))) return candidate end end -- done with this bucket bi=1 @@ -1561,7 +1500,7 @@ function flotilla:load(ulc_cx, ulc_cy, lvl) for s in all(row) do counts[s.ship_t] += 1 s.x,s.y=rnd_spawn_loc() - eships:push_back(s) + add(eships, s) end add(rows, row) end @@ -1675,7 +1614,7 @@ end function blip(obj, col) obj.fx_pal = blip_pals[col] if (obj.___fx_pal_event) obj.___fx_pal_event:abort() - events:push_back(blip_fx.new{frames=3, obj=obj}) + add(events, blip_fx.new{frames=3, obj=obj}) end bossspark = split"7,7,10,10,9,9,9,8,8,8,2,2,5,5" @@ -1712,14 +1651,14 @@ end function spark(sprs, x, y, dx, dy, odds, fg) if (sprs==nil or flr(rnd(odds) or (abs(dx) < 0.5 and abs(dy))) ~= 0) return local target = fg and intangibles_fg or intangibles_bg - target:push_back(spark_particle.new{ + target[#target+1] = spark_particle.new{ x = x + rnd(4) - 2, y = y + rnd(4) - 2, sprs = sprs, sidx = 1, dx = dx * rnd(2), dy = dy * rnd(2), - }) + } end -->8 -- powerups