local board={} local bw,bh=10,10 local g=love.graphics local font=g.newFont(9,"mono") font:setFilter("nearest") g.setFont(font) local colors={ function(a,b) g.setColor(1,0,0,b*a) g.polygon("fill",0.5,0, 1,0.5, 0.5,1, 0,0.5) g.setColor(1,0,0,a) g.polygon("line",0.5,0, 1,0.5, 0.5,1, 0,0.5) end, function(a,b) local w=0.5^0.5 local p=(1-w)/2 g.setColor(1,1,0,b*a) g.rectangle("fill",p,p,w,w) g.setColor(1,1,0,a) g.rectangle("line",p,p,w,w) end, function(a,b) g.setColor(0,1,0,b*a) g.circle("fill",0.5,0.5,0.45,8) g.setColor(0,1,0,a) g.circle("line",0.5,0.5,0.45,8) end, function(a,b) g.translate(0,0.15) g.translate(0.5,0.5) g.rotate(math.pi/2*3) g.translate(-0.5,-0.5) g.setColor(0,1,1,b*a) g.circle("fill",0.5,0.5,0.5,3) g.setColor(0,1,1,a) g.circle("line",0.5,0.5,0.5,3) end, function(a,b) g.translate(0.5,0.5) g.rotate(math.pi/2*3) g.translate(-0.5,-0.5) g.setColor(1,0,1,b*a) g.circle("fill",0.5,0.5,0.45,6) g.setColor(1,0,1,a) g.circle("line",0.5,0.5,0.45,6) end, function(a,b) g.setColor(0,0,1,b*a) g.setColor(0,0,1,a) g.line(0.1,0.4, 0.5,0, 0.9,0.4) g.line(0.1,1-0.4, 0.5,1-0, 0.9,1-0.4) g.line(0.3,0.5, 0.7,0.5) end, } function pairs(t) return next,t,nil end local gobjs={} local cgobjs={} local lgobjs={} local mgobjs={} for k,v in ipairs(colors) do gobjs[k]={c=k} cgobjs[k]={c=k,super="charge"} lgobjs[k]={c=k,super="lines"} mgobjs[k]={c=k,super="color"} end local ltest=0 debug.sethook(function() ltest=ltest+1 if ltest>=10000 then print("!! we are in a freezer !!",debug.traceback()) ltest=0 end end,"",1000) local cc=3 local tt=0 local bat=1 local batd=bat local score=0 local tochar=0 local scored=score local function znoise(...) return love.math.noise(...)*2-1 end local function eudist(x,y) return (x*x+y*y)^0.5 end local function norm(x1,y1) local d=eudist(x,y) if d==0 then return 0,0 end return x/d,y/d end local function dlbolt(x,y,x1,y1) end local function drawfig(n,a) g.push("all") g.scale(2-(a or 1)) g.translate(-0.5,-0.5) g.translate(0.1,0.1) g.scale(0.8) colors[n.c](a or 1,n.super=="charge" and ((math.sin(tt*math.pi*2)*0.5+0.5)*0.4+0.1) or 0) if n.super=="lines" then local hps,vps={},{} local det=10 for n=0,det do local dmul=(det-math.abs(n-det/2))/det*4 hps[n*2+1],hps[n*2+2]=n/det,0.5+znoise(tt,583,-775,n)*0.1*dmul vps[n*2+1],vps[n*2+2]=0.5+znoise(tt,-492,444,n)*0.1*dmul,n/det end g.line(hps) g.line(vps) elseif n.super=="color" then local l={} local det=100 for n=0,det do l[n*2+1],l[n*2+2]=0.5+znoise(tt,55,51,n*math.pi/det*2),0.5+znoise(tt,44,25,n*math.pi/det*2) end g.setLineJoin("none") g.polygon("line",l) end g.scale(0.02) g.pop() end local function pti(x,y) return y+(x-1)*bh end local trans=love.math.newTransform() local anims={} local falling={} local booms={} local function smtar(x,y,tx,ty) local i=pti(x,y) local anim if not anims[i] then if x==tx and y==ty then return end anims[i]={ cx=x,cy=y, tx=tx,ty=ty, x=x,y=y } end anim=anims[i] anim.tx,anim.ty=tx,ty end local function setbsize(x,y,w,h) trans:reset() trans:translate(x,y) trans:scale(w/bw,h/bh) end local gemrng local function randgem() tochar=tochar-1 local objl=gobjs if tochar<=0 then tochar=gemrng:random((bw*bh)*0.5,(bw*bh)*1.5) objl=cgobjs end return objl[gemrng:random(math.floor(cc))] end local function nextgem(o) local c=o.c%math.floor(cc)+1 return (o.super and cgobjs or gobjs)[c] end local function isgem(o) return type(o)=="table" and colors[o.c] end local function eqgem(a,b) return isgem(a) and isgem(b) and a.c==b.c end local function badboigemcheck(c,am,ac,rm) c=isgem(c) and c.c if not c then return end if ac~=5 and ac~=4 then return end local aa={} for k,v in pairs(am) do local cc=board[v[1]][v[2]] if isgem(cc) and (cc.super and cc.super~="charge") then return end aa[#aa+1]=k end local e=aa[gemrng:random(#aa)] local rc=ac==5 and mgobjs[c] or lgobjs[c] rm[e][3]=rc end local function megascan(x,y,c,rm) local ogc=c rm=rm or {} if not isgem(ogc) then return next(rm) and rm end if ogc.super~="color" then return next(rm) and rm end for x=1,bw do for y=1,bh do local i=pti(x,y) local c=board[x][y] if eqgem(c,ogc) then rm[i]=rm[i] or {x,y} end end end return next(rm) and rm end local function check(x,y,rm,oam,obm) local c=board[x][y] if not c then return rm,oam,obm end local ac,bc=1,1 local am,bm={},{} oam,obm=oam or {}, obm or {} do local i=pti(x,y) local v={x,y} am[i],bm[i]=v,v end rm=rm or {} for d,v in ipairs{{-1,0},{1,0},{0,-1},{0,1}} do local ox,oy=unpack(v) local x,y=x+ox,y+oy while x>=1 and x<=bw and y>=1 and y<=bh do local i=pti(x,y) if eqgem(board[x][y],c) then if d>2 and not oam[i] then ac=ac+1 am[i]={x,y} oam[i]=true elseif d<=2 and not obm[i] then bc=bc+1 bm[i]={x,y} obm[i]=true end else break end x,y=x+ox,y+oy end end if ac>=3 then for k,v in pairs(am) do rm[k]=v end badboigemcheck(c,am,ac,rm) end if bc>=3 then for k,v in pairs(bm) do rm[k]=v end badboigemcheck(c,bm,bc,rm) end return next(rm) and rm,oam,obm end local falltest=false local function drawboard(x,y,w,h) g.push("all") g.applyTransform(trans) g.setLineWidth(0.1) g.stencil(function() g.rectangle("fill",0,0,bw,bh) end) g.push("all") g.translate(0.1,-1-0.05) local bat=math.min(batd,1) g.setColor((1-bat^2),bat^0.5,math.max(0,batd-1)^0.5) g.rectangle("line",0,0,bw/2-0.1,0.8) g.rectangle("fill",0.2,0.2,(bw/2-0.5)*bat,0.4) g.rectangle("line",bw/2-0.09,0.2,0.2,0.4) g.pop() g.push("all") local sctext=("%.1i"):format(math.floor(scored+.5)) local xx,yy=trans:transformPoint(0,0) local tty=0.8/font:getHeight() local ttx=font:getWidth(sctext)*tty g.translate(bw-ttx,-1.1) g.scale(tty) g.setColor(1,1,1) g.print(sctext) g.pop() if not falltest then g.setStencilTest("greater",0) end for x=1,bw do for y=1,bh do g.push() local tx,ty=x,y local anim=anims[pti(x,y)] if anim then tx,ty=anim.cx or x, anim.cy or y end g.translate(tx-0.5,ty-0.5) if isgem(board[x][y]) then drawfig(board[x][y]) end g.pop() end end for x,falling in ipairs(falling) do for fall,_ in pairs(falling) do for i,c in ipairs(fall.figs) do g.push("all") local tx,ty=fall.x,fall.y-i+1 g.translate(tx-0.5,ty-0.5) if isgem(c) then drawfig(c) end g.pop() end end end for v,_ in pairs(booms) do g.push("all") local tx,ty=v.x,v.y g.translate(tx-0.5,ty-0.5) drawfig(v.c,v.a) g.pop() end g.setStencilTest() g.setColor(0.2,0.2,0.2) g.rectangle("line",0,0,bw,bh) g.pop() end local function fall(l) local cols={} for k,v in pairs(l) do local x,y=unpack(v) cols[x]=cols[x] or {l={},m={}} cols.l[#cols+1]=y cols.m[y]=true end end local killall local function kill(x,y,rc) if not board[x][y] then print("Whar") return end if board[x][y].super=="charge" then bat=math.min(2,bat+0.1) end do local tx,ty=x,y local anim=anims[pti(x,y)] if anim then tx,ty=anim.cx,anim.cy end booms[{ x=tx,y=ty, a=1,c=board[x][y] or mgobjs[4] }]=true end if rc then board[x][y]=rc return true end board[x][y]=nil local fall={} for y=y-1,gem and 1 or (-bh+1),-1 do if board[x][y] then if not fall.figs then fall.x,fall.y=x,y fall.figs={} fall.vel=0 falling[x][fall]=true end fall.figs[#fall.figs+1]=board[x][y] board[x][y]=nil elseif fall.figs then fall={} end end local my,mfall=math.huge local unfell={} local yls={} local cx=x for x=1,bw do local yl for y=bh,0,-1 do if not board[x][y] then yl=y break end end unfell[x]={} local tt={} for k,v in pairs(falling[x]) do tt[#tt+1]=k end table.sort(tt,function(a,b) return a.y>b.y end) for _,fall in ipairs(tt) do if cx==x and fall.ya[2] or b[1]>b[1] end) local checks={} for k,v in ipairs(tt) do local a=kill(unpack(v)) if a then checks[k]=v end end local rm,oam,obm={},{},{} for k,v in pairs(checks) do local x,y=unpack(v) check(x,y,rm,oam,obm) end if next(rm) then killall(rm) end score=score+#tt end local function dropall(off,die) for x=1,bw do falling[x]=falling[x] or {} local fall={ x=x,y=bh+(off or 0), vel=0, figs={} } falling[x][fall]=true for y=bh,1,-1 do fall.figs[#fall.figs+1]=board[x][y] board[x][y]=nil end end if die then for x,falling in pairs(falling) do for fall,_ in pairs(falling) do fall.die=true end end end end local function fillboard() for x=1,bw do board[x]={} for y=1,bh do local a=randgem() board[x][y]=a end end for n=1,math.huge do local good=true local rm={} local kills for x=1,bw do for y=1,bh do kills=check(x,y,rm) or kills end end if not next(rm) then break end for k,v in pairs(rm) do local x,y=unpack(v) board[x][y]=randgem() end end dropall(-bh) end local function resboard() cc=3 score=0 bat=1 gemrng=love.math.newRandomGenerator(991) tochar=bw*bh+gemrng:random(bw) fillboard() end local dbg=false local playdbg=false function love.load() resboard() end local grabbed=nil local dying=false local running=false function love.mousepressed(x,y,b) x,y=trans:inverseTransformPoint(x,y) x,y=x+1-0.5,y+1-0.5 local ix,iy=math.floor(x+.5),math.floor(y+.5) if b==2 and dbg then for ix=ix-50,iy+50 do for iy=iy-50,iy+50 do if board[ix] and board[ix][iy] and math.random()>0.1 then kill(ix,iy) end end end end if b~=1 then return end running=true if ix<1 or ix>bw or iy<1 or iy>bh then return end if grabbed then return end grabbed={ ix,iy, ix-x,iy-y, } end function love.mousereleased(x,y,b) if b~=1 then return end if grabbed then local ix,iy,ox,oy,px,py=unpack(grabbed) if ix and px and (ix~=px or iy~=py) then board[ix][iy],board[px][py]= board[px][py],board[ix][iy] local kills=check(ix,iy,check(px,py)) if kills and board[ix][iy] and board[px][py] then megascan(ix,iy,board[ix][iy],megascan(px,py,board[px][py],kills)) local ii,pi=pti(ix,iy),pti(px,py) local ania,anib=anims[ii],anims[pi] anims[ii],anims[pi]=anib,ania ania=ania or {x=ix,y=iy} anib=anib or {x=px,y=py} ania.x,anib.x=anib.x,ania.x ania.y,anib.y=anib.y,ania.y grabbed=nil killall(kills) return else board[ix][iy],board[px][py]= board[px][py],board[ix][iy] end end grabbed=nil smtar(ix,iy,ix,iy) if px then smtar(px,py,px,py) end return end end local gacc=50 -- i forgor how intersections work so here's a thingy to figure it out -- A1-----A2 -- B1-----B2 -- -- B1-----B2 -- A1-----A2 local function inters(a1,a2,b1,b2) return (a1<=b1 and a2>=b1) or (b1<=a1 and b2>=a1) end local function negdiff(a,b) return math.abs(a-b)<0.01 end local function sign(a) return a>0 and 1 or (a<0 and -1 or 0) end local function interp(a,b,dt) return a+sign(b-a)*math.min((b-a)*sign(b-a),math.max(dt,(b-a)*sign(b-a)*math.max(dt,0.01)*10)) end local animc=0 local entc=0 local boomc=0 local slow=1 local accumdt=0 local tps=120 local speeds={1,0.05,10} local plchint=1 local toplchi=plchint function love.mousemoved() if grabbed then local x,y,ox,oy,px,py=unpack(grabbed) local mx,my=love.mouse.getPosition() mx,my=trans:inverseTransformPoint(mx,my) mx,my=mx+1-0.5+ox,my+1-0.5+oy mx,my=math.floor(mx+.5),math.floor(my+.5) if math.abs(x-mx)>math.abs(y-my) then mx,my=math.min(bw,math.max(1,x+sign(mx-x))),y elseif math.abs(y-my)>0 then my,mx=math.min(bh,math.max(1,y+sign(my-y))),x end if px then smtar(px,py,px,py) end grabbed[5],grabbed[6]=mx,my if not board[x][y] then elseif board[mx][my] then smtar(mx,my,x,y) smtar(x,y,mx,my) end end end function love.update(dt) ltest=0 dt=math.min(dt,0.2)*(speeds[slow]) accumdt=math.min(0.6,accumdt+dt) local fcc=0 while accumdt>1/tps do fcc=fcc+1 local dt=1/tps accumdt=accumdt-dt if running and playdbg then local gc=0 local tc=0 for x=1,bw do for y=1,bh do gc=gc+(board[x][y] and 1 or 0) tc=tc+1 end end if gc/tc>0.4 then local goods={} for _=1,1000 do local ix,iy=math.random(1,bw),math.random(1,bh) local px,py=0,0 while px<1 or py<1 or px>bw or py>bh do local ox,oy=unpack(({{1,0},{-1,0},{0,1},{0,-1}})[math.random(1,4)]) px,py=ix+ox,iy+oy end board[ix][iy],board[px][py]= board[px][py],board[ix][iy] local kills=check(ix,iy,check(px,py)) local kc=0 for k,v in pairs(kills or {}) do if v[3] and v[3].super=="color" then kc=kc+50 elseif v[3] then kc=kc+20 end kc=kc+1 end if kills and board[ix][iy] and board[px][py] then goods[#goods+1]={ix,iy,px,py,kills,kc} end board[ix][iy],board[px][py]= board[px][py],board[ix][iy] end table.sort(goods,function(a,b) return a[6]>b[6] end) if #goods>0 then local ix,iy,px,py,kills=unpack(goods[1]) board[ix][iy],board[px][py]= board[px][py],board[ix][iy] megascan(ix,iy,board[ix][iy],megascan(px,py,board[px][py],kills)) killall(kills) end end end if running then bat=math.max(0,bat-dt*0.015*(math.max(bat,0.0625)^0.5)) batd=interp(batd,bat,dt) scored=interp(scored,score,dt) end if bat==0 and not dying then dying=true dropall(0,true) end animc=0 entc=0 boomc=0 for k,v in pairs(anims) do animc=animc+1 v.cx=interp(v.cx,v.tx,dt*2) v.cy=interp(v.cy,v.ty,dt*2) if negdiff(v.cx,v.x) and negdiff(v.cy,v.y) then anims[k]=nil end end local tk={} if running then for x,falling in ipairs(falling) do for fall,_ in pairs(falling) do entc=entc+1 fall.vel=fall.vel+dt*gacc fall.y=fall.y+fall.vel*dt local iy=math.floor(fall.y) for fallb,_ in pairs(falling) do if fall~=fallb and inters( fall.y-#fall.figs+1-0.5,fall.y+0.5, fallb.y-#fallb.figs+1-0.5,fallb.y+0.5 ) and fall.die==fallb.die then local wa,wb=#fall.figs,#fallb.figs fall.vel=(fall.vel*wa+fallb.vel*wb)/(wa+wb) if fallb.y=bh} or {iy>=bh+#fall.figs})[1] then falling[fall]=nil if not fall.die then local off=math.min(iy,bh) for i,fig in ipairs(fall.figs) do while board[x][off] do off=off-1 end if off<=bh and off>=1 then board[x][off]=fig end off=off-1 end for i=off+1,off+#fall.figs do tk[#tk+1]={x,i} end end end end end end if entc==0 and dying then dying=false running=false resboard() end if entc==0 and running then toplchi=toplchi-dt if toplchi<0 then toplchi=toplchi+plchint local good=false for ix=1,bw do for iy=1,bh do for _,a in pairs{{1,0},{-1,0},{0,1},{0,-1}} do local px,py=ix+a[1],iy+a[2] if px>=1 and py>=1 and px<=bw and py<=bh then board[ix][iy],board[px][py]= board[px][py],board[ix][iy] good=good or check(ix,iy,check(px,py)) board[ix][iy],board[px][py]= board[px][py],board[ix][iy] end end end end if not good then dropall(0,true)fillboard() end end else toplchi=plchint end local oam,obm,rm={},{},{} for k,v in pairs(tk) do local x,y=unpack(v) check(x,y,rm,oam,obm) end killall(rm) for v,_ in pairs(booms) do boomc=boomc+1 v.a=v.a-dt/math.abs(v.a)*4 if v.a<=0.01 then booms[v]=nil end end tt=tt+dt end end local yes=true function love.keypressed(k) local x,y=love.mouse.getPosition() x,y=trans:inverseTransformPoint(x,y) x,y=math.floor(x)+1,math.floor(y)+1 x=math.max(1,math.min(bw,x)) y=math.max(1,math.min(bh,y)) if dbg and tonumber(k) then print(gobjs[tonumber(k)],tonumber(k)) board[x][y]=gobjs[tonumber(k)] or board[x][y] end if dbg and isgem(board[x][y]) then local c=board[x][y].c if k=="h" then board[x][y]=cgobjs[c] elseif k=="j" then board[x][y]=lgobjs[c] elseif k=="k" then board[x][y]=mgobjs[c] end end if k=="f2" then yes=not yes elseif k=="f3" then dbg=not dbg elseif k=="f4" then local unfell={} local yls={} local cx=x for x=1,bw do local yl for y=bh,0,-1 do if not board[x][y] then yl=y break end end unfell[x]={} local tt={} for k,v in pairs(falling[x]) do tt[#tt+1]=k end table.sort(tt,function(a,b) return a.y>b.y end) for _,fall in ipairs(tt) do falling[x][fall]=nil for k,v in ipairs(fall.figs) do unfell[x][yl]={board[x][yl]} board[x][yl]=v yl=yl-1 end end yls[x]=yl end elseif k=="f5" then slow=slow%3+1 elseif k=="f7" then falltest=not falltest elseif k=="f8" then bat=bat*0.25 elseif k=="f9" then bat=bat*2 elseif k=="f10" then playdbg=not playdbg elseif k=="f11" then love.window.setFullscreen(not love.window.getFullscreen()) end end function love.draw() if dbg then g.push("all") g.scale(2,2) love.graphics.print(("FPS: %i; dt: %.2f; speed: %f; anims: %i; fallings: %i; booms: %i"):format( love.timer.getFPS(), math.floor(love.timer.getDelta()*100000)/100, speeds[slow], animc, entc, boomc )) g.pop() end local sw,sh=g.getDimensions() local ww=math.min(sw,sh) local y if falltest then ww=math.min(sw,sh)*0.5 y=(sh)/2 end local x,y=(sw-ww)/2,y or (sh-ww)/2 setbsize(x+ww*0.1,y+ww*0.1+ww/bh*0.2,ww*0.8,ww*0.8) if yes then drawboard() end end