-- Mine a hole of depth by width by heigth, starting from 0, 0, -1 relative to -- current position and facing -- +x = right of start, +y = up of start, +z = forward of start local FUEL_RESERVE = 800 local FUEL_REFUEL_UNIT = 8 -- how many items to refuel with at a time local FULL_CHECK_SLOT = 14 -- slot to check for "fullness" local V_ZERO = vector.new(0, 0, 0) local UNIT_X = vector.new(1, 0, 0) local UNIT_Y = vector.new(0, 1, 0) local UNIT_Z = vector.new(0, 0, 1) local FORWARD, RIGHT, BACK, LEFT = 0, 1, 2, 3 local mode = { v = vector.new(0, 0, 0), facing = FORWARD } mode.mine = { forward = true } ---------------------- BLOCK MATH ------------------------ function copy_v(v) return vector.new( v.x, v.y, v.z, ) end local bmetatable -- what is this? a type that holds a starting position and a dimention as -- two vectors -- TODO learn about metatables local Block = { -- orient our corner and offset to a vector orient = function (self, pos) -- TODO similar to "normalize" error("not yet implemented") local dims = { x = { self.v1.x, self.v1.x + self.v2.x } y = { self.v1.y, self.v1.y + self.v2.y } z = { self.v1.z, self.v1.z + self.v2.z } } for dim, points in pairs(dims) do -- pick the closer point -- return to point-offset format end end, take = function (self, amt, dir) -- TODO if dir ~= "x" and dir ~= "y" and dir ~= "z" then return nil end local new, remainder = self:copy(), self new.v1[dir] = amt remainder.v1[dir] = remainder.v1[dir] + amt remainder.v2[dir] = remainder.v2[dir] - amt return new, remainder end, copy = function (self) return Block.new( vector.new(self.v1.x, self.v1.y, self.v1.z), vector.new(self.v2.x, self.v2.y, self.v2.z), ) end } bmetatable = { __name = "Block", __index = Block, } local new_block = function (v1, v2) return setmetatable( { v1 = v1, v2 = v2 } , bmetatable ) end ---------------------- BASIC MOVEMENT --------------------- function mineSides () if mode.mine.up then turtle.digUp() end if mode.mine.down then turtle.digDown() end end function moveInLine (delta) mineSides() while delta > 0 do if mode.mine.forward then turtle.dig() end local success = turtle.forward() if success then mineSides() delta = delta - 1 end end end function moveUpDown (delta) while delta > 0 do if mode.mine.forward then turtle.digUp() end local success = turtle.up() if success then delta = delta - 1 end end while delta < 0 do if mode.mine.forward then turtle.digDown() end local success = turtle.down() if success then delta = delta + 1 end end end function turnToFace (new_direction) local delta = new_direction - mode.facing if delta > 2 then delta = delta - 4 elseif delta < -2 then delta = delta + 4 end while delta > 0 do turtle.turnRight() delta = delta - 1 end while delta < 0 do turtle.turnLeft() delta = delta + 1 end mode.facing = new_direction end function getCurrentPos() return mode.v end -- move to a specific point function moveAbs (v) -- print(string.format("Moving to (%d, %d, %d)", v.x, v.y, v.z)) move(v - getCurrentPos()) end -- move relative to the turtle's current location function move (v) -- x if v.x > 0 then turnToFace(RIGHT) moveInLine(v.x) elseif v.x < 0 then turnToFace(LEFT) moveInLine(-v.x) end mode.v = mode.v + UNIT_X -- z if v.z > 0 then turnToFace(FORWARD) moveInLine(v.z) end if v.z < 0 then turnToFace(BACK) moveInLine(-v.z) end mode.v = mode.v + UNIT_Z -- y moveUpDown(v.y) mode.v = mode.v + UNIT_Y end -------------- DROPPING OFF AND REFUELING ------------------- function refuelUntil (amt) while turtle.getFuelLevel() < amt do if not turtle.refuel(FUEL_REFUEL_UNIT) then break end end end function dropOffItems (starting_point, go_back) -- Turn off mining local mine_state = mode.mine mode.mine = { forward = true } -- Note current position (assuming facing FORWARD) local pos_state = vector.new(mode.v.x, mode.v.y, mode.v.z) -- Return to origin, facing BACK moveAbs(starting_point) moveAbs(V_ZERO) turnToFace(BACK) -- deposit items for slot = 2, 16 do turtle.select(slot) turtle.drop() end turtle.select(1) -- Return to current position, facing forward if go_back then moveAbs(starting_point) moveAbs(pos_state) end -- Turn mining back on, if it was on mode.mine = mine_state end function inventoryFull () -- leave a little extra space just in case return turtle.getItemCount(FULL_CHECK_SLOT) > 0 end function moveAndCheck (v, starting_point) move(v) if inventoryFull() then dropOffItems(starting_point, true) end refuelUntil(FUEL_RESERVE) end function mine (block) -- TODO adjust for new paradigm -- assumptions: block is mineable in a single pass -- rough algo draft: -- 1. go to "starting point" at one end of block -- a. don't move vertically if you don't have to -- b. refuel beforehand -- 2. move to the ending point -- 3. drop off items if necessary move(start_v) move(end_v) end function canMine (block) local notTooHigh = block.v2.y > 3 -- we can only mine in a 1-wide strip at a time local notTooWide = block.v2.x > 1 and block.v2.z > 1 return not (tooHigh or tooWide) end function splitHorizontal (block) -- TODO make this less naive? return block:take(1, "x") end function split (block) local tooHigh = function (block) return block.v2.y > 3 end block = block:orient(getCurrentPos()) if tooHigh(block) then return block:take(3, "y") else return splitHorizontal(block) end end function process (stack, starting_point) -- START code taken from `mine` -- fuel and go to starting point refuelUntil(FUEL_RESERVE) mode.mine = { forward = true } moveAbs(starting_point) -- END -- TODO if #stack == 0 then return nil end local working = table.remove(stack) if canMine(working) then mine(working) else local new, remainder = split(working, mode.v) table.insert(stack, remainder) table.insert(stack, new) end return process (stack) end function calculateBlock (v1, v2) -- Using the coordinates, construct a block of certain dimensions, a -- certain distance away. local block = {} local normalize = function (dim, c1, c2) -- pick the closest point -- offset goes to the other point local corner, offset if math.abs(c1) <= math.abs(c2) then corner, offset = c1, c2 - c1 else corner, offset = c2, c1 - c2 end block[dim] = corner block[dim .. "off"] = offset end normalize("x", v1.x, v2.x) normalize("y", v1.y, v2.y) normalize("z", v1.z, v2.z) return new_block( vector.new(block.x, block.y, block.z) vector.new(block.xoff, block.yoff, block.zoff) ) end function run (v1, v2) -- TODO calculate starting_point, the place turtle will move to before -- moving to the first corner of block local block = new_block(v1, v2 - v1) process({ block }) -- return to base dropOffItems(starting_point) turnToFace(FORWARD) end function usage () print("Usage: mine x1 y1 z1 x2 y2 z2 OR mine x1 y1 z1 OR mine x1 y1 z1 x2 y2 z2 x_turtle y_turtle z_turtle.") print("Mine from the first set of coordinates to the second in a \ rectangular prism.") print("The coordinates are relative to the facing of the turtle at the \ start of the program.") print("If invoked with only three arguments, act as if they are the last \ three arguments and substitute 0 0 0 for the first three.") print("If invoked with nine arguments, interpret the first two sets as world coordinates of the rectangular prism and the third set as the coordinates of the turtle's starting position, ASSUMING FACING NORTH.") print("Fuel goes in the top-left slot of the turtle's inventory.") end function argsToNumbers () local num_args = {} for i, v in ipairs(arg) do num_args[i] = tonumber(v) end return num_args end local arg = argsToNumbers() if #arg == 3 then run(V_ZERO, vector.new(arg[1], arg[2], arg[3])) elseif #arg == 6 then run(vector.new(arg[1], arg[2], arg[3]), vector.new(arg[4], arg[5], arg[6])) elseif #arg == 9 then local turtle_pos = vector.new(arg[7], arg[8], arg[9]) local v1, v2 = vector.new(arg[1], arg[2], arg[3]) , vector.new(arg[4], arg[5], arg[6]) run(v1 - turtle_pos, v2 - turtle_pos) else usage() end