View difference between Paste ID: 5wY1BLjM and ea4pcXZw
SHOW: | | - or go back to the newest paste.
1
--------------------------------------------------------------------------------
2-
-- Mining Robot with Remote Commands and Configuration
2+
-- Wireless Robot Control Program (Handheld) with Configuration UI
3
--
4-
-- On startup, the turtle (named "Minerobot <ID>") connects to rednet and waits
4+
-- This program runs on your handheld remote computer. It:
5-
-- for configuration from the handheld. The configuration sets the mine size
5+
6-
-- (width and height) and the turtle’s starting facing direction.
6+
-- 1. Listens for robots to check in (they send "checkin:<name>").
7
-- 2. Displays a main list of robots as colored buttons.
8-
-- After configuration, the turtle responds to these remote commands:
8+
-- 3. When a robot button is touched, shows a submenu with options:
9-
--   "stats" - Sends its current coordinates.
9+
--      Stats, Start, Stop, Home, Config.
10-
--   "start" - Begins mining with the configured parameters.
10+
-- 4. The Stats option sends "stats" and waits for a reply.
11-
--   "stop"  - Stops mining after finishing the current layer.
11+
-- 5. The Config option brings up a configuration screen that lets you
12-
--   "home"  - Returns to the starting position.
12+
--     adjust the mine width, mine height, and starting facing direction.
13-
--   "config:<width>:<height>:<facing>" - Updates configuration.
13+
--     (Facing: 0=East, 1=South, 2=West, 3=North.)
14
-- 6. The configuration is sent to the robot in the format:
15-
-- ASSUMPTIONS:
15+
--     "config:<width>:<height>:<facing>"
16-
--   - Starting position is (0,0,0).
16+
17-
--   - A chest is behind the starting point.
17+
-- Press 'r' to refresh the robot list.
18-
--   - Wireless modem attached on side "back" (adjust if needed).
18+
19
20
-- Open rednet on the modem on the specified side.
21-
-- CONFIGURATION defaults (will be updated via remote command)
21+
local modemSide = "back"
22-
local areaWidth = 16
22+
23-
local areaHeight = 16
23+
24-
local startFacing = 0  -- 0=East, 1=South, 2=West, 3=North
24+
25
26-
local sleepTime = 0.1
26+
local robots = {}  -- key: robot ID, value: {name, lastCheckin}
27
28-
-- Position and orientation tracking (starting at (0,0,0))
28+
29-
local posX, posY, posZ = 0, 0, 0
29+
-- Helper: Draw Button
30-
local facing = startFacing  -- will be updated with config
30+
31
local function drawButton(button)
32-
local startX, startY, startZ = 0, 0, 0
32+
  term.setBackgroundColor(button.bgColor)
33
  term.setTextColor(button.textColor)
34-
-- Control flags
34+
  for y = button.y1, button.y2 do
35-
local miningActive = false
35+
    term.setCursorPos(button.x1, y)
36-
local stopMining = false
36+
    term.write(string.rep(" ", button.x2 - button.x1 + 1))
37
  end
38-
-- Modem configuration
38+
  local textLength = #button.text
39-
local modemSide = "left"
39+
  local btnWidth = button.x2 - button.x1 + 1
40
  local startX = button.x1 + math.floor((btnWidth - textLength) / 2)
41
  local centerY = math.floor((button.y1 + button.y2) / 2)
42
  term.setCursorPos(startX, centerY)
43
  term.write(button.text)
44-
local robotName = "Minerobot " .. os.getComputerID()
44+
45-
rednet.broadcast("checkin:" .. robotName, "control")
45+
46-
print("Checked in as " .. robotName)
46+
local function inButton(x, y, button)
47-
print("Waiting for configuration...")
47+
  return x >= button.x1 and x <= button.x2 and y >= button.y1 and y <= button.y2
48
end
49
50-
-- INVENTORY & FUEL FUNCTIONS
50+
51
-- Rednet Check-In Listener
52-
local function inventoryFull()
52+
53-
  for slot = 1, 14 do
53+
local function handleRednetMessages()
54-
    if turtle.getItemCount(slot) == 0 then
54+
55-
      return false
55+
    local sender, message, protocol = rednet.receive()
56
    if type(message) == "string" then
57
      local cmd, data = message:match("^(%w+):?(.*)")
58-
  return true
58+
      if cmd == "checkin" then
59
        local name = (data ~= "" and data) or ("Robot " .. sender)
60
        robots[sender] = { name = name, lastCheckin = os.time() }
61-
local function dumpInventory()
61+
        print("Robot checked in: " .. sender .. " (" .. name .. ")")
62-
  for slot = 1, 14 do
62+
63-
    turtle.select(slot)
63+
64-
    turtle.drop()
64+
65
end
66-
  turtle.select(1)
66+
67
--------------------------------------------------------------------------------
68
-- Draw Main List of Robots as Buttons
69-
local function ensureFuel()
69+
70-
  if turtle.getFuelLevel() == "unlimited" then return end
70+
local function drawMainList()
71-
  local threshold = 10
71+
  term.clear()
72-
  if turtle.getFuelLevel() < threshold then
72+
  term.setCursorPos(1,1)
73-
    print("Low fuel (" .. turtle.getFuelLevel() .. "). Refueling...")
73+
  term.setBackgroundColor(colors.black)
74-
    for slot = 1, 16 do
74+
  term.setTextColor(colors.white)
75-
      turtle.select(slot)
75+
  print("Robot Control Main List (touch a robot)")
76-
      turtle.refuel(1)
76+
  print("Press 'r' to refresh")
77-
      if turtle.getFuelLevel() >= threshold then break end
77+
  local btns = {}
78
  local btnY = 4
79-
    if turtle.getFuelLevel() < threshold then
79+
  local btnHeight = 3
80-
      error("Not enough fuel. Please add more fuel and restart.")
80+
  local btnMargin = 1
81
  local index = 1
82
  for id, info in pairs(robots) do
83
    local btn = {
84
      x1 = 2,
85
      y1 = btnY,
86-
-- TURNING & FACING FUNCTIONS
86+
      x2 = term.getSize() - 2,
87
      y2 = btnY + btnHeight - 1,
88-
local function turnLeft()
88+
      bgColor = colors.blue,
89-
  turtle.turnLeft()
89+
      textColor = colors.white,
90-
  facing = (facing + 3) % 4
90+
      text = info.name .. " (ID:" .. id .. ")"
91
    }
92
    drawButton(btn)
93-
local function turnRight()
93+
    btns[index] = { id = id, btn = btn }
94-
  turtle.turnRight()
94+
    btnY = btnY + btnHeight + btnMargin
95-
  facing = (facing + 1) % 4
95+
    index = index + 1
96
  end
97
  return btns
98-
local function turnAround()
98+
99-
  turtle.turnLeft()
99+
100-
  turtle.turnLeft()
100+
101-
  facing = (facing + 2) % 4
101+
-- Draw Submenu for a Selected Robot (Stats, Start, Stop, Home, Config)
102
--------------------------------------------------------------------------------
103
local function drawRobotMenu(robotId)
104-
local function faceDirection(dir)
104+
  term.clear()
105-
  local diff = (dir - facing) % 4
105+
  term.setCursorPos(1,1)
106-
  if diff == 1 then
106+
  term.setBackgroundColor(colors.black)
107-
    turnRight()
107+
  term.setTextColor(colors.white)
108-
  elseif diff == 2 then
108+
  local robot = robots[robotId]
109-
    turnAround()
109+
  print("Robot: " .. robot.name .. " (ID:" .. robotId .. ")")
110-
  elseif diff == 3 then
110+
  print("Select a command:")
111-
    turnLeft()
111+
  local btnWidth = math.floor((term.getSize() - 6) / 2)
112
  local btnHeight = 3
113
  local startX = 2
114
  local startY = 4
115
  local cmds = {
116-
-- MOVEMENT FUNCTIONS
116+
    {text="Stats", cmd="stats", bgColor=colors.lime},
117
    {text="Start", cmd="start", bgColor=colors.green},
118-
local function mineForwardStep()
118+
    {text="Stop",  cmd="stop",  bgColor=colors.red},
119-
  if inventoryFull() then
119+
    {text="Home",  cmd="home",  bgColor=colors.orange},
120-
    return false
120+
    {text="Config", cmd="config", bgColor=colors.cyan}
121
  }
122-
  ensureFuel()
122+
  local btns = {}
123-
  turtle.dig()
123+
  for i = 1, #cmds do
124-
  while not turtle.forward() do
124+
    local col = ((i - 1) % 2)
125-
    if turtle.detect() then turtle.dig() end
125+
    local row = math.floor((i - 1) / 2)
126-
    sleep(sleepTime)
126+
    local btn = {
127
      x1 = startX + col * (btnWidth + 2),
128-
  if facing == 0 then
128+
      y1 = startY + row * (btnHeight + 1),
129-
    posX = posX + 1
129+
      x2 = startX + col * (btnWidth + 2) + btnWidth - 1,
130-
  elseif facing == 1 then
130+
      y2 = startY + row * (btnHeight + 1) + btnHeight - 1,
131-
    posZ = posZ + 1
131+
      bgColor = cmds[i].bgColor,
132-
  elseif facing == 2 then
132+
      textColor = colors.white,
133-
    posX = posX - 1
133+
      text = cmds[i].text
134-
  elseif facing == 3 then
134+
    }
135-
    posZ = posZ - 1
135+
    drawButton(btn)
136
    btns[i] = { cmd = cmds[i].cmd, btn = btn }
137-
  turtle.digUp()
137+
138-
  turtle.digDown()
138+
  local backBtn = {
139-
  return true
139+
    x1 = 2,
140
    y1 = term.getSize() - 3,
141
    x2 = term.getSize() - 2,
142-
local function moveForwardNoDump()
142+
    y2 = term.getSize() - 1,
143-
  ensureFuel()
143+
    bgColor = colors.gray,
144-
  turtle.dig()
144+
    textColor = colors.white,
145-
  while not turtle.forward() do
145+
    text = "Back"
146-
    if turtle.detect() then turtle.dig() end
146+
  }
147-
    sleep(sleepTime)
147+
  drawButton(backBtn)
148
  btns[#cmds+1] = { cmd = "back", btn = backBtn }
149-
  if facing == 0 then
149+
  return btns
150-
    posX = posX + 1
150+
151-
  elseif facing == 1 then
151+
152-
    posZ = posZ + 1
152+
153-
  elseif facing == 2 then
153+
-- Configuration UI for a Robot
154-
    posX = posX - 1
154+
-- Allows setting mine width, height, and starting facing direction.
155-
  elseif facing == 3 then
155+
156-
    posZ = posZ - 1
156+
local function configureRobot(robotId)
157
  local configWidth = 16
158
  local configHeight = 16
159
  local configFacing = 0  -- 0=East, 1=South, 2=West, 3=North
160-
local function moveUpNoDump()
160+
  local directions = {"East", "South", "West", "North"}
161-
  ensureFuel()
161+
  
162-
  turtle.digUp()
162+
  local function redrawConfig()
163-
  while not turtle.up() do
163+
    term.clear()
164-
    turtle.digUp()
164+
    term.setCursorPos(1,1)
165-
    sleep(sleepTime)
165+
    print("Configure Robot " .. robotId)
166
    print("---------------------")
167-
  posY = posY + 1
167+
    print("Mine Width: " .. configWidth)
168
    print("Mine Height: " .. configHeight)
169
    print("Facing: " .. directions[configFacing+1])
170-
local function moveDownNoDump()
170+
171-
  ensureFuel()
171+
  
172-
  turtle.digDown()
172+
  redrawConfig()
173-
  while not turtle.down() do
173+
  -- We'll use a simple loop waiting for key presses to adjust values.
174-
    turtle.digDown()
174+
  -- For touch, we define colored buttons.
175-
    sleep(sleepTime)
175+
  local btns = {}
176
  local screenWidth, screenHeight = term.getSize()
177-
  posY = posY - 1
177+
  -- Define buttons for width (- and +)
178
  btns[1] = {cmd="wdec", btn = {x1=2, y1=6, x2=12, y2=8, bgColor=colors.red, textColor=colors.white, text="- Width"}}
179
  btns[2] = {cmd="winc", btn = {x1=14, y1=6, x2=screenWidth-2, y2=8, bgColor=colors.green, textColor=colors.white, text="+ Width"}}
180
  -- Buttons for height
181-
-- 3D NAVIGATION FUNCTION
181+
  btns[3] = {cmd="hdec", btn = {x1=2, y1=9, x2=12, y2=11, bgColor=colors.red, textColor=colors.white, text="- Height"}}
182
  btns[4] = {cmd="hinc", btn = {x1=14, y1=9, x2=screenWidth-2, y2=11, bgColor=colors.green, textColor=colors.white, text="+ Height"}}
183-
local function navigateTo3DNoDump(tx, ty, tz)
183+
  -- Buttons for facing
184-
  while posY < ty do
184+
  btns[5] = {cmd="fdec", btn = {x1=2, y1=12, x2=12, y2=14, bgColor=colors.red, textColor=colors.white, text="- Facing"}}
185-
    moveUpNoDump()
185+
  btns[6] = {cmd="finc", btn = {x1=14, y1=12, x2=screenWidth-2, y2=14, bgColor=colors.green, textColor=colors.white, text="+ Facing"}}
186
  -- Send and Cancel buttons
187-
  while posY > ty do
187+
  btns[7] = {cmd="send", btn = {x1=2, y1=screenHeight-4, x2=math.floor(screenWidth/2)-1, y2=screenHeight-2, bgColor=colors.lime, textColor=colors.white, text="Send Config"}}
188-
    moveDownNoDump()
188+
  btns[8] = {cmd="cancel", btn = {x1=math.floor(screenWidth/2)+1, y1=screenHeight-4, x2=screenWidth-2, y2=screenHeight-2, bgColor=colors.gray, textColor=colors.white, text="Cancel"}}
189
  
190-
  if posX < tx then
190+
  -- Draw all buttons
191-
    faceDirection(0)
191+
  for i = 1, #btns do
192-
    while posX < tx do
192+
    drawButton(btns[i].btn)
193-
      moveForwardNoDump()
193+
194
  
195-
  elseif posX > tx then
195+
196-
    faceDirection(2)
196+
    local event, button, x, y = os.pullEvent("mouse_click")
197-
    while posX > tx do
197+
    for i = 1, #btns do
198-
      moveForwardNoDump()
198+
      if inButton(x, y, btns[i].btn) then
199
        local cmd = btns[i].cmd
200
        if cmd == "wdec" then
201-
  if posZ < tz then
201+
          if configWidth > 1 then configWidth = configWidth - 1 end
202-
    faceDirection(1)
202+
        elseif cmd == "winc" then
203-
    while posZ < tz do
203+
          configWidth = configWidth + 1
204-
      moveForwardNoDump()
204+
        elseif cmd == "hdec" then
205
          if configHeight > 1 then configHeight = configHeight - 1 end
206-
  elseif posZ > tz then
206+
        elseif cmd == "hinc" then
207-
    faceDirection(3)
207+
          configHeight = configHeight + 1
208-
    while posZ > tz do
208+
        elseif cmd == "fdec" then
209-
      moveForwardNoDump()
209+
          configFacing = (configFacing - 1) % 4
210
        elseif cmd == "finc" then
211
          configFacing = (configFacing + 1) % 4
212
        elseif cmd == "send" then
213
          -- Send configuration command to robot
214
          local configCmd = "config:" .. configWidth .. ":" .. configHeight .. ":" .. configFacing
215-
-- DUMP INVENTORY ROUTINE
215+
          rednet.send(robotId, configCmd)
216
          term.setCursorPos(1,screenHeight-1)
217-
local function manageInventoryDump()
217+
          print("Config sent: " .. configCmd)
218-
  local savedX, savedY, savedZ = posX, posY, posZ
218+
          os.sleep(2)
219-
  local savedFacing = facing
219+
          return
220-
  print("Inventory full. Going to dump...")
220+
        elseif cmd == "cancel" then
221-
  navigateTo3DNoDump(startX, startY, startZ)
221+
          return
222-
  faceDirection(2)  -- face the chest
222+
223-
  dumpInventory()
223+
        redrawConfig()
224-
  faceDirection(0)
224+
        for j = 1, #btns do drawButton(btns[j].btn) end
225-
  print("Dump complete. Returning to mining spot...")
225+
226-
  navigateTo3DNoDump(savedX, savedY, savedZ)
226+
227-
  faceDirection(savedFacing)
227+
228
end
229
230
--------------------------------------------------------------------------------
231-
-- DESCEND TO NEXT LAYER FUNCTION
231+
-- Wait for a button press from a set of buttons; returns the command.
232
--------------------------------------------------------------------------------
233-
local function descendToNextLayer()
233+
local function waitForButtonPress(buttons)
234-
  navigateTo3DNoDump(startX, posY, startZ)
234+
235-
  for i = 1, 3 do
235+
    local event, btn, x, y = os.pullEvent("mouse_click")
236-
    turtle.digDown()
236+
    for _, data in ipairs(buttons) do
237-
    while not turtle.down() do
237+
      if inButton(x, y, data.btn) then
238-
      turtle.digDown()
238+
        return data.cmd, data.id
239-
      sleep(sleepTime)
239+
240
    end
241-
    posY = posY - 1
241+
242
end
243-
  return true
243+
244
--------------------------------------------------------------------------------
245
-- Main UI Loop
246
--------------------------------------------------------------------------------
247-
-- MINE ONE LAYER (Serpentine based on configured areaWidth and areaHeight)
247+
local function main()
248
  while true do
249-
local function mineLayer()
249+
    local mainButtons = drawMainList()
250-
  for row = 0, areaHeight - 1 do
250+
    local event, btn, x, y = os.pullEvent("mouse_click")
251-
    if row % 2 == 0 then
251+
    for _, data in ipairs(mainButtons) do
252-
      faceDirection( (startFacing + 0) % 4 )  -- use starting direction for even rows
252+
      if inButton(x, y, data.btn) then
253-
      for i = 1, areaWidth - 1 do
253+
        local robotId = data.id
254-
        if not mineForwardStep() then
254+
        local submenuButtons = drawRobotMenu(robotId)
255-
          manageInventoryDump()
255+
        while true do
256-
          mineForwardStep()
256+
          local cmd, _ = waitForButtonPress(submenuButtons)
257
          if cmd == "back" then
258
            break
259-
    else
259+
          elseif cmd == "stats" then
260-
      faceDirection( (startFacing + 2) % 4 )  -- opposite direction for odd rows
260+
            rednet.send(robotId, "stats")
261-
      for i = 1, areaWidth - 1 do
261+
            term.setCursorPos(2, term.getSize()-5)
262-
        if not mineForwardStep() then
262+
            term.setBackgroundColor(colors.black)
263-
          manageInventoryDump()
263+
            term.setTextColor(colors.white)
264-
          mineForwardStep()
264+
            write("Waiting for stats...")
265
            local sender, reply, protocol = rednet.receive(nil, 3)
266
            term.clearLine()
267
            if sender == robotId and reply then
268-
    if row < areaHeight - 1 then
268+
              write("Stats: " .. reply)
269-
      faceDirection( (startFacing + 1) % 4 )  -- move to next row (assumes southward shift)
269+
            else
270-
      if not mineForwardStep() then
270+
              write("No stats received from robot " .. robotId)
271-
        manageInventoryDump()
271+
            end
272-
        mineForwardStep()
272+
            write(" -- Touch to continue.")
273
            os.pullEvent("mouse_click")
274
            submenuButtons = drawRobotMenu(robotId)
275
          elseif cmd == "config" then
276-
  navigateTo3DNoDump(0, posY, 0)
276+
            configureRobot(robotId)
277-
  faceDirection(startFacing)
277+
            submenuButtons = drawRobotMenu(robotId)
278
          else
279
            rednet.send(robotId, cmd)
280
            term.setCursorPos(2, term.getSize()-5)
281-
-- MINING LOOP
281+
            term.setBackgroundColor(colors.black)
282
            term.setTextColor(colors.white)
283-
local function miningLoop()
283+
            write("Sent command '" .. cmd .. "' to robot " .. robotId)
284-
  print("Mining started.")
284+
            os.sleep(1)
285-
  while miningActive and not stopMining do
285+
            submenuButtons = drawRobotMenu(robotId)
286-
    mineLayer()
286+
          end
287-
    local success, data = turtle.inspectDown()
287+
288-
    if success and data.name == "minecraft:bedrock" then
288+
        break
289-
      print("Bedrock below. Stopping mining.")
289+
290-
      miningActive = false
290+
291-
      break
291+
292
end
293-
    print("Layer complete. Descending...")
293+
294-
    if not descendToNextLayer() then
294+
295-
      print("Cannot descend further. Stopping mining.")
295+
-- MAIN: Run the UI and rednet listener in parallel.
296-
      miningActive = false
296+
297-
      break
297+
parallel.waitForAny(main, handleRednetMessages)
298
299-
    sleep(0.1)  -- yield frequently
299+
300
  rednet.close(modemSide)
301-
  print("Mining stopped.")
301+
302