195 lines
6.3 KiB
Lua
195 lines
6.3 KiB
Lua
|
--[[
|
||
|
Movescript - A simplified robot script for ComputerCraft.
|
||
|
|
||
|
Author: Andrew Lalis <andrewlalisofficial@gmail.com>
|
||
|
|
||
|
Movescript provides a simpler, conciser way to program "turtles" (robots), so
|
||
|
that you don't need to get tired of typing "turtle.forward()" over and over.
|
||
|
|
||
|
]]--
|
||
|
local t = turtle
|
||
|
|
||
|
-- The movescript module. Functions defined within this table are exported.
|
||
|
local movescript = {}
|
||
|
|
||
|
local function debug(msg, settings)
|
||
|
if settings and settings.debug then
|
||
|
print("[MS] " .. msg)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Helper function for turtle to dig backwards.
|
||
|
function t.digBack(side)
|
||
|
t.turnRight()
|
||
|
t.turnRight()
|
||
|
t.dig(side)
|
||
|
t.turnRight()
|
||
|
t.turnRight()
|
||
|
end
|
||
|
|
||
|
-- Helper function for turtle to detect backwards.
|
||
|
function t.detectBack()
|
||
|
t.turnRight()
|
||
|
t.turnRight()
|
||
|
local result = t.detect()
|
||
|
t.turnRight()
|
||
|
t.turnRight()
|
||
|
return result
|
||
|
end
|
||
|
|
||
|
-- The default settings to apply to any robot movement, if none are specified.
|
||
|
local defaultMovementSettings = {
|
||
|
safe = true,
|
||
|
destructive = false,
|
||
|
fuels = {"minecraft:coal", "minecraft:charcoal"}
|
||
|
}
|
||
|
|
||
|
local function goDirection(dirFunction, digFunction, detectFunction, settings)
|
||
|
settings = settings or defaultMovementSettings
|
||
|
safe = settings.safe or defaultMovementSettings.safe
|
||
|
destructive = settings.destructive or defaultMovementSettings.destructive
|
||
|
local success = dirFunction()
|
||
|
if not safe then return end
|
||
|
while not success do
|
||
|
debug("Unable to move.", settings)
|
||
|
if destructive and detectFunction() then
|
||
|
debug("Detected a block in the way; attempting to remove it.", settings)
|
||
|
digFunction()
|
||
|
end
|
||
|
success = dirFunction()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function goUp(settings)
|
||
|
debug("Moving up.", settings)
|
||
|
goDirection(t.up, t.digUp, t.detectUp, settings)
|
||
|
end
|
||
|
|
||
|
local function goDown(settings)
|
||
|
debug("Moving down.", settings)
|
||
|
goDirection(t.down, t.digDown, t.detectDown, settings)
|
||
|
end
|
||
|
|
||
|
local function goForward(settings)
|
||
|
debug("Moving forward.", settings)
|
||
|
goDirection(t.forward, t.dig, t.detect, settings)
|
||
|
end
|
||
|
|
||
|
local function goBack(settings)
|
||
|
debug("Moving back.", settings)
|
||
|
goDirection(t.back, t.digBack, t.detectBack, settings)
|
||
|
end
|
||
|
|
||
|
local function goRight(settings)
|
||
|
debug("Turning right.", settings)
|
||
|
t.turnRight()
|
||
|
end
|
||
|
|
||
|
local function goLeft(settings)
|
||
|
debug("Turning left.", settings)
|
||
|
t.turnLeft()
|
||
|
end
|
||
|
|
||
|
local actionMap = {
|
||
|
["U"] = {f = goUp, needsFuel = true},
|
||
|
["D"] = {f = goDown, needsFuel = true},
|
||
|
["L"] = {f = goLeft, needsFuel = false},
|
||
|
["R"] = {f = goRight, needsFuel = false},
|
||
|
["F"] = {f = goForward, needsFuel = true},
|
||
|
["B"] = {f = goBack, needsFuel = true},
|
||
|
["P"] = {f = t.place, needsFuel = false},
|
||
|
["Pu"] = {f = t.placeUp, needsFuel = false},
|
||
|
["Pd"] = {f = t.placeDown, needsFuel = false},
|
||
|
["A"] = {f = t.attack, needsFuel = false},
|
||
|
["Au"] = {f = t.attackUp, needsFuel = false},
|
||
|
["Ad"] = {f = t.attackDown, needsFuel = false}
|
||
|
}
|
||
|
|
||
|
-- Tries to refuel the turtle from all slots that contain a valid fuel.
|
||
|
-- Returns a boolean indicating if at least one piece of fuel was consumed.
|
||
|
local function refuelAll(settings)
|
||
|
debug("Refueling...", settings)
|
||
|
local fuels = settings.fuels or defaultMovementSettings.fuels
|
||
|
local refueled = false
|
||
|
for slot = 1, 16 do
|
||
|
local item = t.getItemDetail(slot)
|
||
|
for _, fuelName in pairs(fuels) do
|
||
|
if item.name == fuelName then
|
||
|
t.select(i)
|
||
|
if t.refuel(item.count) then refueled = true end
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return refueled
|
||
|
end
|
||
|
|
||
|
-- Blocks until the turtle's fuel level is at least at the required level.
|
||
|
local function refuelToAtLeast(requiredLevel, settings)
|
||
|
refuelAll(settings)
|
||
|
while t.getFuelLevel < requiredLevel do
|
||
|
print(
|
||
|
"[MS] Fuel level is too low. Level: " .. t.getFuelLevel() .. ". Required: " .. requiredLevel ..
|
||
|
". Please add some of the following fuels:"
|
||
|
)
|
||
|
local fuels = settings.fuels or defaultMovementSettings.fuels
|
||
|
for _, fuelName in pairs(fuels) do
|
||
|
print(" - " .. fuelName)
|
||
|
end
|
||
|
local fuelUpdated = false
|
||
|
while not fuelUpdated do
|
||
|
os.pullEvent("turtle_inventory")
|
||
|
fuelUpdated = refuelAll()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Executes a single instruction. An instruction is a table with an "action"
|
||
|
-- and some attributes, such as if it needs fuel or not.
|
||
|
local function executeInstruction(instruction, settings)
|
||
|
local action = actionMap[instruction.action]
|
||
|
if action then
|
||
|
debug("Executing action \"" .. instruction.action .. "\" " .. instruction.count .. " times.", settings)
|
||
|
if action.needsFuel and instruction.count > t.getFuelLevel() then
|
||
|
local fuelRequired = instruction.count
|
||
|
refuelToAtLeast(fuelRequired, settings)
|
||
|
end
|
||
|
for i = 1, instruction.count do action.f() end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Parses a movescript script into a series of instruction tables.
|
||
|
local function parseScript(script, settings)
|
||
|
local instructions = {}
|
||
|
for instruction in string.gfind(script, "%W*(%d*%u%l*)%W*") do
|
||
|
local countIdx, countIdxEnd = string.find(instruction, "%d+")
|
||
|
local actionIdx, actionIdxEnd = string.find(instruction, "%u%l*")
|
||
|
local count = 1
|
||
|
if countIdx ~= nil then
|
||
|
count = tonumber(string.sub(instruction, countIdx, countIdxEnd))
|
||
|
end
|
||
|
local action = string.sub(instruction, actionIdx, actionIdxEnd)
|
||
|
if count < 1 or count > t.getFuelLimit() then
|
||
|
error("Instruction at index " .. actionIdx .. " has an invalid count of " .. count .. ". It should be >= 1 and <= " .. t.getFuelLimit())
|
||
|
end
|
||
|
if actionMap[action] == nil then
|
||
|
error("Instruction at index " .. actionIdx .. ", \"" .. action .. "\", does not refer to a valid action.")
|
||
|
end
|
||
|
table.insert(instructions, {action = action, count = count})
|
||
|
debug("Parsed instruction: " .. instruction, settings)
|
||
|
end
|
||
|
return instructions
|
||
|
end
|
||
|
|
||
|
function movescript.run(script, settings)
|
||
|
settings = settings or defaultMovementSettings
|
||
|
script = script or ""
|
||
|
debug("Executing script: " .. script, settings)
|
||
|
local instructions = parseScript(script, settings)
|
||
|
for idx, instruction in pairs(instructions) do
|
||
|
executeInstruction(instruction, settings)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return movescript
|