-- miner_startup.lua
-- Ultra-dumb miner. Verbose local console + heartbeats.
-- Requires: wireless (ender) modem; label set via: label set miner_alpha
local PROTOCOL = "minesys:v1"
local HEARTBEAT_SEC = 2
local modem = peripheral.find("modem", function(_, p) return p.isWireless() end)
if not modem then error("No wireless modem") end
if not turtle then error("Run on a turtle") end
rednet.open(peripheral.getName(modem))
-- ---------------- console/status ----------------
local state = {
status = "waiting",
extra = nil,
facing = "N",
pos = nil,
fuel = turtle.getFuelLevel(),
lastDraw = 0,
}
local function invStats()
local empty = 0
for i=1,16 do if turtle.getItemCount(i)==0 then empty = empty + 1 end end
local pct = (empty/16)*100
return { freeSlots = empty, freePct = pct }
end
local function now() return os.epoch("utc") end
local function gpsPos()
local x, y, z = gps.locate(1)
if x then state.pos = {x=x,y=y,z=z} end
return x, y, z
end
local function setStatus(s, extra)
state.status = s or state.status
state.extra = extra or nil
end
local function drawConsole(force)
local t = now()
if not force and (t - state.lastDraw) < 100 then return end -- 0.1s throttle
state.lastDraw = t
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1,1)
local lbl = os.getComputerLabel() or ("id"..os.getComputerID())
print(("Miner %s"):format(lbl))
print(("Fuel: %s"):format(tostring(state.fuel)))
local p = state.pos
print(("Pos: %s"):format(p and ("("..p.x..","..p.y..","..p.z..")") or "unknown"))
print(("Facing: %s"):format(state.facing))
print(("Status: %s"):format(state.status))
if state.extra then
-- Render compact progress lines
if state.extra.kind=="move" then
print(("- Move %s: %d/%d"):format(state.extra.axis, state.extra.step, state.extra.total))
elseif state.extra.kind=="clear" then
print(("- Clear LxWxH: %d×%d×%d"):format(state.extra.L, state.extra.W, state.extra.H))
print((" Layer: %d/%d Row: %d/%d Step: %d/%d"):format(
state.extra.layer, state.extra.H,
state.extra.row, state.extra.W,
state.extra.step, state.extra.L
))
elseif state.extra.kind=="face" then
print(("- Face: %s"):format(state.extra.dir))
elseif state.extra.kind=="note" and state.extra.msg then
print(("- Note: %s"):format(state.extra.msg))
end
end
end
local function hb()
state.fuel = turtle.getFuelLevel()
gpsPos()
local msg = {
t = now(),
op = "status",
id = os.getComputerID(),
label = os.getComputerLabel(),
pos = state.pos,
fuel = state.fuel,
facing = state.facing,
status = state.status,
extra = state.extra,
inv = invStats(),
}
rednet.broadcast(msg, PROTOCOL)
end
local function report(s, extra, forceDraw)
setStatus(s, extra)
drawConsole(forceDraw)
hb()
end
-- ---------------- motion primitives ----------------
local DIRS = {N=0, E=1, S=2, W=3}
local dirNames = {"N","E","S","W"}
local function setFacing(newDir)
if not DIRS[newDir] then return false, "bad dir" end
if state.facing == newDir then
report("waiting", {kind="face", dir=newDir})
return true
end
local idx = DIRS[state.facing]; local tgt = DIRS[newDir]
local diff = (tgt - idx) % 4
report("waiting", {kind="face", dir=newDir}, true)
if diff == 1 then turtle.turnRight()
elseif diff == 2 then turtle.turnRight(); turtle.turnRight()
elseif diff == 3 then turtle.turnLeft()
end
state.facing = newDir
report("waiting", {kind="face", dir=newDir})
return true
end
local function digForward()
while turtle.detect() do
turtle.dig()
sleep(0.15)
end
end
local function forward(n)
n = math.max(0, n or 1)
for i=1, n do
report("moving to", {kind="move", axis="fwd", step=i, total=n})
digForward()
local tries=0
while not turtle.forward() do
digForward()
tries=tries+1
if tries>50 then
report("waiting", {kind="note", msg="blocked fwd"})
return false, "blocked"
end
sleep(0.15)
end
end
report("waiting")
return true
end
local function up(n)
n = math.max(0, n or 1)
for i=1, n do
report("moving to", {kind="move", axis="up", step=i, total=n})
while turtle.detectUp() do turtle.digUp(); sleep(0.15) end
local tries=0
while not turtle.up() do
while turtle.detectUp() do turtle.digUp(); sleep(0.15) end
tries=tries+1
if tries>50 then
report("waiting", {kind="note", msg="blocked up"})
return false, "blocked"
end
sleep(0.15)
end
end
report("waiting")
return true
end
local function down(n)
n = math.max(0, n or 1)
for i=1, n do
report("moving to", {kind="move", axis="down", step=i, total=n})
while turtle.detectDown() do turtle.digDown(); sleep(0.15) end
local tries=0
while not turtle.down() do
while turtle.detectDown() do turtle.digDown(); sleep(0.15) end
tries=tries+1
if tries>50 then
report("waiting", {kind="note", msg="blocked down"})
return false, "blocked"
end
sleep(0.15)
end
end
report("waiting")
return true
end
-- ---------------- clear area ----------------
local function clearArea(length, width, height, startDir)
local L = math.max(1, length or 1)
local W = math.max(1, width or 1)
local H = math.max(1, height or 1)
setFacing(startDir or state.facing)
local function sweepRow(len, layerIdx, rowIdx)
for i=1,len do
report("clearing area", {
kind="clear", L=L, W=W, H=H,
layer=layerIdx, row=rowIdx, step=i
})
digForward()
if i<len then assert(forward(1)) end
end
end
for h=1,H do
setFacing(startDir or state.facing)
for w=1,W do
sweepRow(L, h, w)
if w<W then
if (w % 2 == 1) then
turtle.turnRight(); digForward(); assert(forward(1)); turtle.turnRight()
-- update facing tag
if state.facing=="N" then state.facing="E"
elseif state.facing=="E" then state.facing="S"
elseif state.facing=="S" then state.facing="W"
elseif state.facing=="W" then state.facing="N" end
else
turtle.turnLeft(); digForward(); assert(forward(1)); turtle.turnLeft()
if state.facing=="N" then state.facing="W"
elseif state.facing=="W" then state.facing="S"
elseif state.facing=="S" then state.facing="E"
elseif state.facing=="E" then state.facing="N" end
end
end
end
if h<H then assert(up(1)) end
end
report("waiting")
return true
end
-- ---------------- heartbeat + loop ----------------
report("waiting", {kind="note", msg="boot"}, true)
parallel.waitForAny(
function()
-- command loop
while true do
local sender, msg, proto = rednet.receive(PROTOCOL, HEARTBEAT_SEC)
if sender and proto==PROTOCOL and type(msg)=="table" and msg.op=="cmd" then
local ok, err
if msg.act=="face" then
ok, err = setFacing(msg.dir)
elseif msg.act=="forward" then
ok, err = forward(msg.n or 1)
elseif msg.act=="up" then
ok, err = up(msg.n or 1)
elseif msg.act=="down" then
ok, err = down(msg.n or 1)
elseif msg.act=="clear_area" then
ok, err = clearArea(msg.length or 1, msg.width or 1, msg.height or 1, msg.dir or state.facing)
elseif msg.act=="set_facing_tag" then
state.facing = msg.dir or state.facing
report("waiting", {kind="face", dir=state.facing})
ok = true
else
ok, err = false, "unknown act"
report("waiting", {kind="note", msg="unknown act"})
end
rednet.send(sender, {op="ack", ok=ok, err=err, ref=msg.ref}, PROTOCOL)
else
report("waiting") -- idle tick keeps console fresh
end
end
end,
function()
while true do
sleep(HEARTBEAT_SEC)
report("waiting")
end
end
)
Comments