local Y=require("yaru.core") local gfx=require("yaru.gfx") local ui=require("yaru.ui") local computer=require("computer") local M={} local LEVELS={{"Small",8,8,10},{"Medium",16,16,40},{"Large",30,16,99}} local NUMC={0x0049BF,0x009200,0xCC0000,0x330080,0x992400,0x009280,0x000000,0x787878} function M.open(level) local st={} local win=Y.newWindow{app="mines",title="Mines",w=40,h=14,fixed=true} local function new(li) local L=LEVELS[li or st.level or 1] st.level=li or st.level or 1 st.w,st.h,st.n=L[2],L[3],L[4] st.mine,st.open,st.flag={},{},{} st.placed,st.over,st.won,st.t0,st.t1=false,false,false,nil,nil st.opened,st.flags=0,0 win.w,win.h=math.max(40,st.w*2+4),st.h+4 gfx.damageAll() end local function idx(x,y)return(y-1)*st.w+x end local function around(x,y,fn) for dy=-1,1 do for dx=-1,1 do local nx,ny=x+dx,y+dy if(dx~=0 or dy~=0)and nx>=1 and ny>=1 and nx<=st.w and ny<=st.h then fn(nx,ny)end end end end local function count(x,y)local n=0 around(x,y,function(a,b)if st.mine[idx(a,b)]then n=n+1 end end)return n end local function place(sx,sy) local free={} for y=1,st.h do for x=1,st.w do if math.abs(x-sx)>1 or math.abs(y-sy)>1 then free[#free+1]=idx(x,y)end end end for _=1,st.n do st.mine[table.remove(free,math.random(#free))]=true end st.placed,st.t0=true,computer.uptime() end local function reveal(x,y) local stack={{x,y}} while#stack>0 do local p=table.remove(stack) local i=idx(p[1],p[2]) if not st.open[i]and not st.flag[i]then st.open[i]=true st.opened=st.opened+1 if count(p[1],p[2])==0 then around(p[1],p[2],function(a,b)stack[#stack+1]={a,b}end)end end end end local function finish(won) st.over,st.won,st.t1=true,won,computer.uptime() if Y.cfg.beeps then computer.beep(won and 1200 or 200,0.2)end Y.notify(won and"You won!"or"Boom!",won and string.format("Cleared in %d s",math.floor(st.t1-st.t0))or"A mine exploded", {actionLabel="Play again",action=function()new()end}) end local function click(x,y,btn) if st.over then return end local i=idx(x,y) if btn==1 then if not st.open[i]then st.flag[i]=not st.flag[i];st.flags=st.flags+(st.flag[i]and 1 or-1)end return end if st.flag[i]then return end if not st.placed then place(x,y)end if st.open[i]then local f=0 around(x,y,function(a,b)if st.flag[idx(a,b)]then f=f+1 end end) if f==count(x,y)then around(x,y,function(a,b) local j=idx(a,b) if not st.flag[j]and not st.open[j]then if st.mine[j]then st.open[j]=true;finish(false)else reveal(a,b)end end end)end elseif st.mine[i]then st.open[i]=true;finish(false) else reveal(x,y)end if not st.over and st.opened==st.w*st.h-st.n then finish(true)end end function win.header(w,hw) local x=2 for i,L in ipairs(LEVELS)do x=x+ui.button(w,x,1,L[1],function()new(i)end,i==st.level and nil or"flat")+1 end end function win.draw(w,cw,ch) local t=Y.theme w.tickInterval=(st.placed and not st.over)and 1 or nil local time=st.t0 and math.floor((st.t1 or computer.uptime())-st.t0)or 0 local info=string.format("● %d ◷ %d s",st.n-st.flags,time) gfx.text((cw-#info+4)//2,1,info,st.over and(st.won and t.success or t.danger)or t.fg) local ox=(cw-st.w*2)//2+1 for y=1,st.h do for x=1,st.w do local i=idx(x,y) local px,py=ox+(x-1)*2,y+2 local shade=(x+y)%2==0 if st.open[i]then local bg=shade and(t.dark and 0x2D2D2D or 0xE1E1E1)or(t.dark and 0x383838 or 0xF0F0F0) if st.mine[i]then gfx.fill(px,py,2,1,st.won and bg or 0xCC0000);gfx.text(px,py,"●",0x000000) else local n=count(x,y) gfx.fill(px,py,2,1,bg) if n>0 then gfx.text(px,py,tostring(n),t.dark and(n==1 and 0x6D92FF or n==2 and 0x33DB40 or n==3 and 0xFF4940 or 0xFFB640)or NUMC[n])end end else local bg=shade and 0x66DB40 or 0x33B640 if st.over and st.mine[i]and not st.flag[i]then bg=0x992400 end gfx.fill(px,py,2,1,bg) if st.flag[i]then gfx.pset(px,py*2-1,0xFF2400);gfx.pset(px+1,py*2-1,0xFF2400);gfx.pset(px,py*2,0x2D2D2D)end end ui.hit(w,px,py,2,1,function(_,_,_,btn)click(x,y,btn)end) end end end function win.tick(w)Y.redraw(w)end new(level) return win end return M