Compare commits

...

13 Commits

Author SHA1 Message Date
2a2d5edc98 More (modest) optimizations to level rebuild 2023-01-02 21:08:40 -08:00
2c29327db2 Use gsv in level build 2023-01-02 20:57:00 -08:00
20a1feb2bb Reduce CPU hit of level rebuild 2023-01-02 20:51:06 -08:00
9960aafd71 Simplify pinch detector 2023-01-02 20:43:59 -08:00
3911ae6eee Save just a few more tokens 2023-01-02 20:11:25 -08:00
64c8bca3c0 Save some tokens in _rast 2023-01-02 20:04:29 -08:00
617e7b3948 Remove inorder 2023-01-02 20:00:02 -08:00
9b1fc9ac94 Elude a few minor tokens 2023-01-02 19:59:34 -08:00
5482f900f0 Remove old frame tracker 2023-01-02 18:40:28 -08:00
c549f881a0 Move "blocked" preview further from tiles 2023-01-02 18:39:15 -08:00
70ffbd2465 Save a few more tokens here and there 2023-01-02 18:27:36 -08:00
be864f06bb Save a lot of tokens on segmenting the rope 2023-01-02 18:00:36 -08:00
4562128fa6 Assorted token golf in rope logic (#25)
Remove unused vars, convert tug_crate arg to table

The tug_crate conversion is for performance. `foreach(tbl, predefined_func)` is substantially faster than a standard `for` loop using the `all` iterator. However, if the function inside the foreach is defined inline, it's much slower due to closure-construction overhead (even though nothing is being closed over). Converting `tug_crate` to take a table as an argument allows foreach to feed right into it, and it also naturally suggests a rewrite a few lines down to get rid of duplicative listing of `mx0,my0,dmx,dmy`, saving several tokens.

I'm going to take a look at can_move to see if it's worth making iits mx0,my0,dmx,dmy arguments into a table as well.

can_move also takes a rope operation table

this is approximately token-neutral but performance-saving. each function parameter makes its call cost worse. When can_move is called inside a loop, we already have a table and we unpack to call can_move; moving the unpack into can_move saves us marshalling cost. It requires us to construct a table in a different spot (where we were not previously doing so) but that spot is not in a loop.

calc_push op loop golf

Reorganizing conditionals saves tokens here.

Fix syntax errors.

This also saves a few tokens and cycles by turning level:tug_crate into a free function. It's not _pretty_ but it's the least bad option.

_calc_push golf redux

Removed unnecessary variable declarations and conditional cases by using an "assume, alternate, verify assumption" pattern and reusing ax0/ay0 when they would never be referenced again.

Fix syntax errors.

Slightly more efficient fix to level_tug_crate.

Reviewed-on: pyrex/chameleonic#25
Co-authored-by: Kistaro Windrider <kistaro@gmail.com>
Co-committed-by: Kistaro Windrider <kistaro@gmail.com>
2023-01-03 01:06:57 +00:00

View File

@ -5,15 +5,13 @@ __lua__
modules={}
real_modules={}
frame=0
function _init()
-- printh("restarting")
_doall("init")
end
function _update()
frame+=1
if (frame%1==0) _doall("update") end
_doall("update") end
function _draw()
_doall("draw") end
@ -97,11 +95,10 @@ function linefill(ax,ay,bx,by,r,c)
local _x1,_y1=x1,y1
if(y0>y1) x0,y0,x1,y1=x1,y1,x0,y0
local dx=(x1-x0)/(y1-y0)
if(y0<0) x0-=y0*dx y0=-1
local cy0=y0\1+1
-- sub-pix shift
x0+=(cy0-y0)*dx
for y=y0\1+1,min(y1\1,127) do
for y=cy0,min(y1\1,127) do
-- open span?
local span=spans[y]
if (span) rectfill(x0,y,span,y)
@ -125,15 +122,6 @@ function sgn0(x)
return x!=0 and sgn(x) or 0
end
function inorder(tbl)
local prev
for v in all(tbl) do
if (prev and v < prev) return
prev = v
end
return true
end
function _mnmx(x,y)
if (x>y)return y,x
return x,y
@ -141,18 +129,16 @@ end
function _rast(
xs,ys,x0,y0,x1,y1
xys,x0,y0,x1,y1
)
local function _add()
local n=#xs
if (n==0 or xs[n]!=x0 or ys[n]!=y0) add(xs,x0) add(ys,y0)
local n=#xys
local xy0={x0,y0}
if (n==0 or not _anch_eq(xys[n],xy0)) add(xys,xy0)
end
local dx,dy=abs(x1-x0),abs(y1-y0)
local sx=-1
local sy=-1
if (x0<x1) sx=1
if (y0<y1) sy=1
local sx,sy=sgn(x1-x0),sgn(y1-y0)
local done,err
if dx>dy then
@ -430,8 +416,9 @@ end
function level:recollide_reanchor()
self._coll={}
for mx=0,15 do
local kmx=mx..","
for my=0,15 do
local mxy=_mix{mx,my}
local mxy=kmx..my
self._coll[mxy]=
fget(self:_mget(mx,my),7) or
self._crates[mxy]
@ -439,18 +426,24 @@ function level:recollide_reanchor()
end
local anch_new={}
for dxy in all{{-1,-1},{1,-1},{-1,1},{1,1}} do
for dxy in all(gsv[[-1`-1
1`-1
-1`1
1`1]]) do
local dx,dy=unpack(dxy)
assert(dx!=0 and dy!=0)
local c=self._coll
for mx0=0,15 do
local mx1=mx0+dx
local kmx0,kmx1=mx0..",",mx1..","
for my0=0,15 do
local mx1,my1=mx0+dx,my0+dy
local my1=my0+dy
-- bypass mcoll for MEGA SPEED
if (
self:mcoll(mx0,my0) and not self:get_crate(mx0,my0) and
not self:mcoll(mx0,my1) and
not self:mcoll(mx1,my0) and
not self:mcoll(mx1,my1)
c[kmx0..my0] and not self:get_crate(mx0,my0) and
not c[kmx0..my1] and
not c[kmx1..my0] and
not c[kmx1..my1]
) then
local key=_mix{"GEOM",mx0,my0,dx,dy}
anch_new[key]= {
@ -494,10 +487,6 @@ function level:recollide_reanchor()
if player.rope then
player.rope:experience_anchor_moves(moves)
end
for point in self:anchor_points() do
point.moved=nil
end
end
function level:win_at(mx,my)
@ -558,7 +547,8 @@ function level:spawn_exit()
assert(spawned)
end
function level:mcoll(mx,my)
function level:mcoll(mxy)
local mx,my=unpack(mxy)
if ((mx | my) & 0xFFF0!=0) return true
return self._coll[_mix{mx,my}]
end
@ -633,15 +623,17 @@ end
ropecheck=split"-0.6,0.4,0.4"
-- argument "o" is a rope operation:
-- array of [mx0,my0,dmx,dmy]
function level:can_move(
is_player,
mx0,my0,dmx,dmy,exclude_src,exclude_dst
is_player,o,exclude_src,exclude_dst
)
local mx0,my0,dmx,dmy=unpack(o)
local mx1,my1=mx0+dmx,my0+dmy
if (is_player and self:win_at(mx1,my1)) return true
if (is_player and self:get_open_pit(mx1,my1)) return wrongbleep:adequately_warned()
if (self:mcoll(mx1,my1) or player.x==mx1 and player.y==my1) return
if (self:mcoll{mx1,my1} or player.x==mx1 and player.y==my1) return
if player.rope then
local w,h=1.2,0.2
@ -652,7 +644,12 @@ function level:can_move(
return true
end
function level:tug_crate(mx0,my0,dmx,dmy)
-- argument is a rope operation:
-- array of [mx0,my0,dmx,dmy]
-- must be a free function
-- to use as a foreach target
function level_tug_crate(t)
local self,mx0,my0,dmx,dmy=level,unpack(t)
local mxy0=_mix{mx0,my0}
local existing=self._crates[mxy0]
if (not existing) return
@ -734,7 +731,7 @@ function player:update()
else
local x,y=self.x,self.y
local function try_move(dx,dy,f)
if level:can_move(true,x,y,dx,dy,0,2) then
if level:can_move(true,{x,y,dx,dy},0,2) then
self.todo=f
self.cooldown=3
local t=f[#f]
@ -756,7 +753,7 @@ function player:update()
local dx,dy=self.orientx,self.orienty
if (dy!=0) dx=0
while not level:mcoll(x,y) do x+=dx y+=dy end
while not level:mcoll{x,y} do x+=dx y+=dy end
self.rope=rope:new(
{x+0.5-dx*0.5,y+0.5-dy*0.5},
@ -872,7 +869,7 @@ function player:draw()
if (self.orientx==1) rx+=6
if self.rope then
local rx_adj,ry_adj=self.rope:affected_src_xy(rx,ry)
local rx_adj,ry_adj=self.rope:affected_src_xy{rx,ry}
if rx_adj then
local drx,dry=rx_adj-rx,ry_adj-ry
rx,ry=rx+drx,ry+dry
@ -880,17 +877,16 @@ function player:draw()
end
end
setpal()
if self.orientx==-1 then
setpal()
spr(16,px+6,py-2,1,1)
spr(17,px+1,py,1,1)
if (self.rope and invis_level<=0.25) pal() self.rope:draw(self.x*8+self.px+1,self.y*8+self.py+2) setpal()
if (self.rope and invis_level<=0.25) pal() self.rope:draw{self.x*8+self.px+1,self.y*8+self.py+2} setpal()
spr(head,px-3,py-3,1,1)
else
setpal()
spr(16,px-6,py-2,1,1,true)
spr(17,px-1,py,1,1,true)
if (self.rope and invis_level<=0.25) pal() self.rope:draw(self.x*8+self.px+7,self.y*8+self.py+2) setpal()
if (self.rope and invis_level<=0.25) pal() self.rope:draw{self.x*8+self.px+7,self.y*8+self.py+2} setpal()
spr(head,px+3,py-3,1,1,true)
end
pal()
@ -937,13 +933,7 @@ function rope:update()
elseif self.state.name=="latched" then
if (not self.latch) wrongbleep:bleep(5) self:destroy() return
if self.latch.rec then
if self.latch.rec.dead==true then
self:destroy()
end
end
if (self.latch.rec and self.latch.rec.dead) self:destroy()
if (not self:_check_pinch()) self:destroy()
elseif self.state.name=="destroy" then -- destroy
@ -959,125 +949,100 @@ function rope:destroy(reelin)
self.state={name="destroy",frame=0,reelin=reelin}
end
function rope:affected_src_xy(artificial_px,artificial_py)
-- this is the loop from :draw but simplified
if (not self.state.reelin) return
perc_to_show=(1.0-self.state.frame/8)^2
local points=self:_anchors_simplified()
points[#points]={x=artificial_px,y=artificial_py}
local len=0
for i=1,#points-1 do len+=distance(points[i],points[i+1]) end
local len_to_show=perc_to_show*len
local len_cumulative=0
for i=1,#points-1 do
local src=points[i]
local dst=points[i+1]
local x,y=dst.x,dst.y
local dx,dy=src.x-x,src.y-y
local len_here=len_to_show-len_cumulative
local dist_base=distance_dxy(dx,dy)
len_cumulative+=dist_base
if len_here>0 and dist_base>0 then
local coef=min(len_here/dist_base,1.0)
return x+dx-dx*coef,y+dy-dy*coef
end
end
return points[1]
end
function rope:draw(artificial_px,artificial_py)
local points,highlight,hypo_ops,hypo_blocks=self:_tug(true)
local n,perc_to_show,from_end = self.state.name,1.0
function rope:_resegment(points,artificial_pxy,cb)
local n,perc_to_show,from_end=self.state.name,1.0
if (n=="done") return
if (n=="cast") perc_to_show=self.state.frame/2
if (n=="destroy") perc_to_show=(1.0-self.state.frame/4)^2
if (self.state.reelin) from_end=true
local artificial_px,artificial_py=unpack(artificial_pxy)
points[#points]={x=artificial_px,y=artificial_py}
local len=0
for i=1,#points-1 do
len+=distance(points[i],points[i+1])
end
local len_to_show=perc_to_show*len
local len_to_show,ia,iz,istep=perc_to_show*len,#points-1,1,-1
if (from_end) ia,iz,istep=iz,ia,1
local len_cumulative=0
local ia,iz,istep=#points-1,1,-1
if (from_end) ia,iz,istep=1,#points-1,1
for i=ia,iz,istep do
local src,dst=points[i],points[i+1]
local x,y=dst.x,dst.y
local dx,dy=src.x-x,src.y-y
local dist_base=sqrt(dx*dx+dy*dy)
local coef=min(len_to_show/dist_base,1.0)
len_to_show-=dist_base
dx,dy=dx*coef,dy*coef
if (from_end) x,y=src.x-dx,src.y-dy
local v0,v1=cb(x,y,dx,dy,i)
if (coef<1) return v0,v1
end
end
function rope:affected_src_xy(artificial_pxy)
if (not self.state.reelin) return
return self:_resegment(
self:_anchors_simplified(),artificial_pxy,
function(x,y) return x,y end
)
end
TONGUE_SEGS=gsv[[0`0.25`1
0.25`0.9`0.5
0.9`1`1]]
function rope:draw(artificial_pxy)
local points,highlight,hypo_ops,hypo_blocks=self:_tug(true)
local function colorh(ix)
color(8)
if (highlight==ix) color(12)
end
for i=ia,iz,istep do
local src=points[i]
local dst=points[i+1]
local x,y=dst.x,dst.y
local dx,dy=src.x-x,src.y-y
local len_here=len_to_show-len_cumulative
local dist_base=distance_dxy(dx,dy)
len_cumulative+=dist_base
if len_here>0 and dist_base>0 then
local coef=min(len_here/dist_base,1.0)
if from_end then
x,y=x+dx-dx*coef,y+dy-dy*coef
end
dx,dy=dx*coef,dy*coef
self:_resegment(points,artificial_pxy,
function(x,y,dx,dy,i)
colorh(i)
local function lf(d0,d1,w)
foreach(TONGUE_SEGS,function(row)
local d0,d1,w=unpack(row)
linefill(x+d0*dx,y+d0*dy,x+d1*dx,y+d1*dy,w)
end
lf(0,0.25,1.0)
lf(0.25,1,0.5)
lf(0.9,1,1.0)
circfill(x+dx+0.5,y+dy+0.5,1.0)
end)
circfill(x+dx,y+dy,1)
end
end
)
-- draw latch
local l=self.latch
if l and l.rec and (perc_to_show>=1.0 or from_end) then
if l and l.rec and (self.state.reelin or self:latched()) then
local function rfsplit(x) rectfill(unpack(split(x))) end
local ldx,ldy=l.dx,l.dy
colorh(0)
camera(-l.rec.px,-l.rec.py)
if (ldx==-1) rfsplit"0,3,2,4"
if (ldx==1) rfsplit"5,3,7,4"
if (ldy==-1) rfsplit"3,0,4,2"
if (ldy==1) rfsplit"3,5,4,7"
if (l.dx==-1) rfsplit"0,3,2,4"
if (l.dx==1) rfsplit"5,3,7,4"
if (l.dy==-1) rfsplit"3,0,4,2"
if (l.dy==1) rfsplit"3,5,4,7"
camera()
color()
end
-- hypothetical
local time=t()-self.flicker_t
if n=="latched" and time>0 and not level:busy() then
if time%0.5>0.25 then
for o in all(hypo_ops) do
local flicker_on=time%0.5>0.25
if self:latched() and time>0 and not level:busy() then
if flicker_on then
foreach(hypo_ops, function(o)
local mx0,my0,dmx,dmy=unpack(o)
local px1,py1=(mx0+dmx)*8,(my0+dmy)*8
spr(14,px1,py1)
end
spr(14,(mx0+dmx)*8,(my0+dmy)*8)
end)
end
for o in all(hypo_blocks) do
foreach(hypo_blocks, function(o)
local x,y,dx,dy=unpack(o)
spr(53,8*x+4*dx,8*y+4*dy,1,1,time%0.5>0.25)
end
spr(34,8*x+4*dx,8*y+4*dy,1,1,flicker_on)
end)
end
-- debug
@ -1171,60 +1136,43 @@ function rope:_check_pinch()
local n0=self.src
local qxs,qys={},{}
local qxys={}
while true do
local n1=n0.next
if (not n1) break
local n0ax,n0ay=_anch_unpack(n0)
local n1ax,n1ay=_anch_unpack(n1)
_rast(qxs,qys,flr(n0ax*2),flr(n0ay*2),flr(n1ax*2),flr(n1ay*2))
_rast(qxys,n0ax\0.5,n0ay\0.5,n1ax\0.5,n1ay\0.5)
n0=n1
end
local function _possible_tiles(qx,qy)
local mx0=(qx-1)\2
local mx1=qx\2
local my0=(qy-1)\2
local my1=qy\2
local function _possible_tiles(t)
local qx,qy=unpack(qxys[t])
local poss={}
for mx=mx0,mx1 do
for my=my0,my1 do
add(poss,{mx=mx,my=my})
for mx=(qx-1)\2,qx\2 do
for my=(qy-1)\2,qy\2 do
if (not level:mcoll{mx,my}) add(poss,{mx,my})
end
end
return poss
end
local function _blocked(qx,qy)
for i in all(_possible_tiles(qx,qy)) do
if (not level:mcoll(i.mx,i.my)) return
end
return true
return all(poss)
end
-- find cases where i move through an impassable zone
for i=1,#qxs do
if (_blocked(qxs[i],qys[i])) return
for i=1,#qxys do
if (not _possible_tiles(i)()) return
end
-- find cases where i am cut off diagonally
for i=3,#qxs do
local qx1,qy1=qxs[i-1],qys[i-1]
if qx1%2==0 and qy1%2==0 then
for i=3,#qxys do
local qx1,qy1=unpack(qxys[i-1])
if (qx1|qy1)&1==0 then
local ok
for m0 in all(_possible_tiles(qxs[i-2],qys[i-2])) do
for m2 in all(_possible_tiles(qxs[i],qys[i])) do
local mx0,my0=m0.mx,m0.my
local mx2,my2=m2.mx,m2.my
if not (level:mcoll(mx0,my0) or level:mcoll(mx2,my2)) then
local dmx,dmy=abs(mx2-mx0),abs(my2-my0)
if dmx==1 and dmy==1 and level:mcoll(mx0,my2) and level:mcoll(mx2,my0) then
else
ok=true
end
end
for m0 in _possible_tiles(i-2) do
for m2 in _possible_tiles(i) do
local mx0,my0=unpack(m0)
local mx2,my2=unpack(m2)
if (mx0==mx2 or my0==my2 or not level:mcoll{mx0,my2} or not level:mcoll{mx2,my0}) ok=true
end
end
@ -1454,10 +1402,6 @@ function _which_side(xy,x0y0,x1y1)
return sgn0((x1-x0)*(y-y0) - (y1-y0)*(x-x0))
end
function distance_dxy(dx,dy)
return sqrt(dx*dx+dy*dy)
end
function distance(p1,p2)
local dx=p2.x-p1.x
local dy=p2.y-p1.y
@ -1540,22 +1484,19 @@ function rope:_tug(hypothetically)
local blocks = {}
for i=#ancs-1,2,-1 do
local ops_before_trash,blocks_before_trash=self:_calc_push(ancs[i+1],ancs[i],ancs[i-1],ancs[i-2])
local ops_to_do,corners={}
local ops = {}
for b in all(blocks_before_trash) do add(blocks, b) end
if #ops_before_trash>0 then
ops_to_do=ops_before_trash
ops=ops_before_trash
else
local ops_after_trash,blocks_after_trash=self:_calc_push(ancs[i-2],ancs[i-1],ancs[i],ancs[i+1])
ops_to_do=ops_after_trash
ops=ops_after_trash
for b in all(blocks_after_trash) do add(blocks,b) end
end
local ops=ops_to_do
if #ops>0 then
if (hypothetically) return ancs,i-1,ops,blocks
for o in all(ops) do level:tug_crate(unpack(o)) end
foreach(ops, level_tug_crate)
return true
end
end
@ -1604,18 +1545,19 @@ function rope:_tug(hypothetically)
end
if not invalid_move then
if level:can_move(false,mx0,my0,dmx,dmy,1,0) then
if (hypothetically) return ancs,0,{{mx0,my0,dmx,dmy}},blocks
local mv = {mx0,my0,dmx,dmy}
if level:can_move(false,mv,1,0) then
if (hypothetically) return ancs,0,{mv},blocks
level:tug_crate(mx0,my0,dmx,dmy)
level_tug_crate(mv)
return true
else
add(blocks, {mx0,my0,dmx,dmy})
add(blocks, mv)
end
end
end
if (hypothetically) return ancs,0,{},blocks
if (hypothetically) return ancs,32767,{},blocks -- invalid
return
end
@ -1640,21 +1582,16 @@ function rope:_calc_push(
smy=-smy
end
local mx,dmx
local dmx=1 -- maybe push right?
if anch.adx==-1 and a0.x>an.x+7 then
-- push left
mx=ax0-1
dmx=-1
elseif anch.adx==1 and a0.x<an.x-7 then
-- push right
mx=ax0
dmx=1
else
ax0, dmx=ax0-1,-1
elseif anch.adx!=1 or a0.x>=an.x-7 then
return {}
end
for my=my0,my1,smy do
add(ops,{mx,my,dmx,0})
add(ops,{ax0,my,dmx,0})
end
end
@ -1666,38 +1603,26 @@ function rope:_calc_push(
smx=-smx
end
local my,dmy
local dmy=1 -- maybe push down?
if anch.ady==-1 and a0.y>an.y+6 then
-- push up
my=ay0-1
dmy=-1
elseif anch.ady==1 and a0.y<an.y-6 then
-- push down
my=ay0
dmy=1
else
ay0,dmy=ay0-1,-1
elseif anch.ady!=1 or a0.y>=an.y-6 then
return {}
end
for mx=mx0,mx1,smx do
add(ops,{mx,my,0,dmy})
add(ops,{mx,ay0,0,dmy})
end
end
local ops2,blocked={},{}
for o in all(ops) do
local mx,my,dmx,dmy=unpack(o)
if not level:mcoll(mx,my) then
-- great!
else
local crate=level:get_crate(mx,my)
if crate then
if not level:can_move(false,mx,my,dmx,dmy,0,0) then
add(blocked,o)
break
end
else
local mx,my=unpack(o)
if level:mcoll{mx,my} then
if (not level:get_crate(mx, my)) break
if not level:can_move(false,o,0,0) then
add(blocked,o)
break
end
add(ops2,o)
@ -1975,20 +1900,20 @@ __gfx__
00000ee00c44455500aaaa00efe33eee1ff11ff1eee33efeeeeeeeeeeeeeee5efffffffffff11111ff1ff1ff11111fff88888888888888888888888888888888
00eeee000c004005000aa000efe33e5e11ffff11e5e33efee5555e555e555e5effffffffffffffffff1ff1ffffffffff88888888888888888855885588558855
eeee0000cc04405500444400efeeee5e11111111e5eeeefeeeeeeeeeeeeeeeeeffffffffffffffffff1ff1ffffffffff88888888888888885555555555555555
00000000000a90005bbbbbb3efe33eeeeeeeeeeeeee33efeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000aaaaaaa9100bbbbbbbbefe33e5555e555e555e33efeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
0000aaaaaa1a9110bbb7aabbefe33eeeeeeeeeeeeee33efeff1ff1ff11111111ff1111ff00000000000000000000000000000000000000000000000000000000
0aaaaaaaaa1a9111bbbaabbbefe333e3333e333e33333efeff1ff1ffffffffffff1ff1ff00000000000000000000000000000000000000000000000000000000
0aaaaaaaa41a91a1bbaaabbbefee33e3333e333e3333eefeff1ff1ffffffffffff1ff1ff00000000000000000000000000000000000000000000000000000000
0a000aa4441a91a1bbabbbbbeffeeeeeeeeeeeeeeeeeeffeff1ff1ff11111111ff1111ff00000000000000000000000000000000000000000000000000000000
00a0044449a110a1bbbbbbbbeeffffffffffffffffffffeeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
000aa111991111103bbbbbb3eeeeeeeeeeeeeeeeeeeeeeeeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000000000a900000000000efe33eeeeeeeeeeeeee33efeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000aaaaaaa910000000000efe33e5555e555e555e33efeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
0000aaaaaa1a911000a00200efe33eeeeeeeeeeeeee33efeff1ff1ff11111111ff1111ff00000000000000000000000000000000000000000000000000000000
0aaaaaaaaa1a9111000a2000efe333e3333e333e33333efeff1ff1ffffffffffff1ff1ff00000000000000000000000000000000000000000000000000000000
0aaaaaaaa41a91a10002a000efee33e3333e333e3333eefeff1ff1ffffffffffff1ff1ff00000000000000000000000000000000000000000000000000000000
0a000aa4441a91a100200a00effeeeeeeeeeeeeeeeeeeffeff1ff1ff11111111ff1111ff00000000000000000000000000000000000000000000000000000000
00a0044449a110a100000000eeffffffffffffffffffffeeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
000aa1119911111000000000eeeeeeeeeeeeeeeeeeeeeeeeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
0000000099100000f765000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111999999111111111
00000000990000007700000000000000000000000000000000000000000000000000000000000000000000000000000019911991999999911999999119999999
00000000990000006060000000000000000bc0000090020000000000000000000000000000000000000000000000000019977991999999911999999119999999
00000000090000005005000000bbcc00000bc0000009200000000000000000000000000000000000000000000000000019911991999117111991199111711999
00000000aa0000000000000000ccbb00000cb0000002900000000000000000000000000000000000000000000000000019911991999117111991199111711999
0000000077a000000000000000000000000cb0000020090000000000000000000000000000000000000000000000000019999991999999911997799119999999
00000000990000006060000000000000000000000000000000000000000000000000000000000000000000000000000019977991999999911999999119999999
00000000090000005005000000000000000000000000000000000000000000000000000000000000000000000000000019911991999117111991199111711999
00000000aa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000019911991999117111991199111711999
0000000077a000000000000000000000000000000000000000000000000000000000000000000000000000000000000019999991999999911997799119999999
00000007777a00000000000000000000000000000000000000000000000000000000000000000000000000000000000019999991999999911991199119999999
00044444444444000000000000000000000000000000000000000000000000000000000000000000000000000000000019999991111111111111111111111111
44444444444004444444444444400444444444444440044444444444444004444444444444400444444444444440044444444444444004444444444444400444
@ -2218,7 +2143,7 @@ __label__
77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
__gff__
000000c0c0c0c0c0c0c0c0c0c0c00000000000c0c0c0c0c0c0c0c0c0202020200040c0c0c0c0c0c0c008080800000000404000000000080808080808c0c0c0c000000000080808080808080800000008000000000808080808080808000000000008080808080808080808080000000000080808080808080808080800000000
000000c0c0c0c0c0c0c0c0c0c0c00000000000c0c0c0c0c0c0c0c0c020202020004000c0c0c0c0c0c008080800000000404000000000080808080808c0c0c0c000000000080808080808080800000008000000000808080808080808000000000008080808080808080808080000000000080808080808080808080800000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d1203040404050d0d0d0d010d0d0d0d0d0d0d0d0d0d0d0d0d0d120d0d0d0d0d0d0d0d0d0d0d0d0d03043e0a040404050d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d