Skip to content

Instantly share code, notes, and snippets.

@Xavura
Created October 17, 2012 10:36
Show Gist options
  • Save Xavura/3904888 to your computer and use it in GitHub Desktop.
Save Xavura/3904888 to your computer and use it in GitHub Desktop.
-- XAPI v0.1
-- A turtle API
-- By Xavura
--[[
TODO:
* Figure out if we can reliably overwrite turtle methods with our own.
]]--
local _turtle = {}
local backups = {}
local methods = {
'craft',
'turnLeft', 'turnRight',
'refuel', 'getFuelLevel',
'dig', 'digUp', 'digDown',
'drop', 'dropUp', 'dropDown',
'suck', 'suckUp', 'suckDown',
'up', 'down', 'back', 'forward',
'place', 'placeUp', 'placeDown',
'attack', 'attackUp', 'attackDown',
'detect', 'detectUp', 'detectDown',
'select', 'getItemCount', 'getItemSpace',
'compare', 'compareUp', 'compareDown', 'compareTo'
}
for k, v in ipairs(methods) do
backups[v] = turtle[v]
end
local _turtleMeta = {
__index = function(tbl, key)
if backups[key] then
return backups[key]
end
end
}
setmetatable(_turtle, _turtleMeta)
-- Constants
local DEBUG = true
local VERSION = {0, 1, 0}
local NORTH = 1
local EAST = 2
local SOUTH = 3
local WEST = 4
local UP = 5
local DOWN = 6
local UPDATE_IDENT = '3924697'
local SOURCE_IDENT = '3904888'
local DIRECTIONS = {[1]="north", [2]="east",
[3]="south", [4]="west",
[5]="up", [6]="down"}
local GPS_TIMEOUT = 3
--[[
local MOVE_MODES = {
REMOVE_BLOCKS = 1, -- Remove BLOCKS that block path(s)
ATTACK_ENTITIES = 2, -- Attack ENTITIES that block path(s)
FIND_ALTERNATE = 4 -- Attempt to find an alternate route that avoids blocks and entities.
}
local MOVE_OPTIONS = {
REMOVE_ATTEMPTS = 2, -- Attempts before giving up removal (because e.g. Bedrock)
ATTACK_ATTEMPTS = 6, -- Attempts before giving up attacking
REMOVE_ONLY = {} -- Remove only blocks in these inventory slots.
}
--]]
-- Math Helpers
function vEqual(v1, v2)
return v1[1] == v2[1] and v1[2] == v2[2] and v1[3] == v2[3]
end
-- State
local position = {nil, nil, nil}
local direction = nil
--local movementMode = bit.band(MOVE_MODE['REMOVE_BLOCKS'], MOVE_MODE['ATTACK_ENTITIES'])
-- State Access
function getPos()
return position
end
function getDir()
return direction
end
-- Debugging
function debugVec(vec, str)
str = str or "Vector"
if type(vec) ~= "table" then
return print(str..": Not a vector.")
end
print(str..": "..vec[1]..", "..vec[2]..", "..vec[3])
end
function debugPos(pos)
return debugVec(position, "Position")
end
function debugDir(dir)
if dir == nil then
dir = direction
end
if DIRECTIONS[dir] then
print("Direction: "..DIRECTIONS[dir])
else
print("Direction: INVALID ("..dir..")")
end
end
-- Setup
local function locate()
rednet.open("right")
local cX, cY, cZ = gps.locate(GPS_TIMEOUT)
rednet.close("right")
if cX == nil then
return false
end
return {cX, cY, cZ}
end
local function determineCardinalDirection()
_turtle.forward()
local facing = 0
local newPosition = locate()
if newPosition == false then
print("Fatal: Unable to connect to GPS @ determineCardinalDirection()")
return shell.exit()
end
if newPosition[1] > position[1] then
facing = EAST
elseif newPosition[1] < position[1] then
facing = WEST
elseif newPosition[3] > position[3] then
facing = SOUTH
elseif newPosition[3] < position[3] then
facing = NORTH
end
_turtle.back()
--for i = 1, 2 do turtle.turnRight() end
--turtle.forward()
--for i = 1, 2 do turtle.turnLeft() end
return facing
end
local function locateManually()
local pos = {}
local dir = nil
local tmp = nil
local line = 1
term.clear()
term.setCursorPos(1, 1)
textutils.slowPrint('Unable to connect to GPS network!')
textutils.slowPrint('Please enter turtle coordinates.')
textutils.slowPrint('Note: F3 Y shows head position.')
textutils.slowPrint('Subtract 1 to get foot position!')
os.sleep(2)
term.clear()
for i = 1, 3 do
repeat
term.clearLine()
term.setCursorPos(1, line)
print(string.char(i+119)..': ')
term.setCursorPos(4, line)
tmp = tonumber(io.read())
if tmp == nil then
term.setCursorPos(4, line)
term.write('Invalid input, please try again.')
os.sleep(1)
end
until tmp ~= nil
table.insert(pos, math.floor(tmp))
line = line + 1
end
term.clear()
term.setCursorPos(1, 1)
textutils.slowPrint('Please enter direction.')
textutils.slowPrint('Use debug (F3) screen, F value.')
os.sleep(2)
term.clear()
term.setCursorPos(1, 1)
repeat
print('f: ')
term.setCursorPos(4, 1)
tmp = tonumber(io.read())
if tmp == nil then
term.setCursorPos(4, 1)
term.write('Invalid input, please try again.')
os.sleep(1)
end
until tmp >= 0 and tmp <= 4
-- Convert F value 2, 3, 0, 1 -> 1, 2, 3, 4
dir = math.ceil( (tmp + 3) % 4.1)
return pos, dir
end
local function setup()
position = locate()
if position == false then
position, direction = locateManually()
else
direction = determineCardinalDirection()
end
debugPos()
debugDir()
end
local function updateCheck()
local request = http.get('https://raw.github.com/gist/'..UPDATE_IDENT)
local response = nil
local version = nil
term.clear()
term.setCursorPos(1, 1)
if request then
response = request.readAll()
request.close()
version = (function()
local result = {}
string.gsub(response, '([^,]+)', function(m) table.insert(result, tonumber(m)) end)
return result
end)()
print('Current Version: v'..table.concat(VERSION, '.'))
if version[1] > VERSION[1] or version[2] > VERSION[2] or version[3] > VERSION[3] then
print('A new version (v'..table.concat(version, '.')..') is available!')
print('Update now (y/n): ')
term.setCursorPos(19, 3)
if io.read() == 'y' then
shell.run('gist', 'get', 'xapi.tmp', SOURCE_IDENT)
os.unloadAPI('xapi')
shell.run('rm', 'xapi')
shell.run('mv', 'xapi.tmp', 'xapi')
os.loadAPI('xapi')
end
else
print('You are running the latest version.')
end
else
print('Error: Unable to check for updates.')
end
end
-- Turning
local function updateDirection(change)
direction = direction + change
if direction == 0 then
direction = 4
elseif direction == 5 then
direction = 1
end
end
function turnLeft(count)
count = count or 1
if count > 1 then
for i = 1, count do
turnLeft(1)
end
end
_turtle.turnLeft()
updateDirection(-1)
end
function turnRight()
count = count or 1
if count > 1 then
for i = 1, count do
turnRight(1)
end
end
_turtle.turnRight()
updateDirection(1)
end
local function faceDirection(dir)
if dir == direction then
return
end
local reversed = false
if dir - direction == 3 or direction - dir == 3 then
reversed = true
end
while true do
if dir > direction then
if reversed == false then
turnRight()
else
turnLeft()
end
else
if reversed == false then
turnLeft()
else
turnRight()
end
end
if dir == direction then
break
end
end
--debugDir()
end
function faceNorth()
return faceDirection(1)
end
function faceEast()
return faceDirection(2)
end
function faceSouth()
return faceDirection(3)
end
function faceWest()
return faceDirection(4)
end
-- Basic Movement
local function updatePosition(backwards)
--backwards = backwards or false
local dir = direction
local index = 0
local change = 1
--[[if backwards then
dir = (dir + 2) % 4
if dir == 0 then
dir = 4
end
end]]--
if dir == 1 or dir == 4 then
change = -1
end
if dir == 2 or dir == 4 then
index = 1
else
index = 3
end
position[index] = position[index] + change
--debugPos()
end
function up(count, force)
count = count or 1
force = force or false
if count > 1 then
for i = 1, count do
up(1, force)
end
return
end
if force and _turtle.detectUp() then
_turtle.digUp()
end
local success = _turtle.up()
if success then
position[2] = position[2] + 1
end
return success
end
function down(count, force)
count = count or 1
force = force or false
if count > 1 then
for i = 1, count do
down(1, force)
end
return
end
if force and _turtle.detectDown() then
_turtle.digDown()
end
local success = _turtle.down()
if success then
position[2] = position[2] - 1
end
return success
end
function forward(count, force)
count = count or 1
force = force or false
if count > 1 then
for i = 1, count do
forward(1, force)
end
return
end
local dug = nil
if force and _turtle.detect() then
dug = _turtle.dig()
end
local success = _turtle.forward()
if not success then
repeat
_turtle.attack()
success = _turtle.forward()
until success
end
if success then
updatePosition()
end
return success
end
function back(count, force)
count = count or 1
force = force or false
if count > 1 then
for i = 1, count do
back(1, force)
end
return
end
--[[ Meh solution:
if force then
rotate(2)
if turtle.detect() then
turtle.dig()
end
rotate(2)
end
--]]
local success = _turtle.back()
if success then
updatePosition(true)
end
return success
end
-- Advanced Movement
local function getMovementVector(movement)
local vector = {0, 0, 0}
if movement[1] > 0 then
vector[1] = WEST
else
vector[1] = EAST
end
if movement[2] > 0 then
vector[2] = DOWN
else
vector[2] = UP
end
if movement[3] > 0 then
vector[3] = NORTH
else
vector[3] = SOUTH
end
return vector
end
function move(target, force) -- Relative movement.
end
function goto(target, force) -- Absolute movement.
local move = nil
local force = force or false
local moves = {position[1] - target[1],
position[2] - target[2],
position[3] - target[3]}
local movement = getMovementVector(moves)
--print("Face "..DIRECTIONS[movement[1]].." then move "..math.abs(moves[1]))
--print("Move "..DIRECTIONS[movement[2]].." "..math.abs(moves[2]).. " times")
--print("Face "..DIRECTIONS[movement[3]].." then move "..math.abs(moves[3]))
if movement[1] == WEST then
faceWest()
else
faceEast()
end
for i = 1, math.abs(moves[1]) do
move = forward(1, force)
if not move then
return print("Aborted in east-west loop.")
end
end
for i = 1, math.abs(moves[2]) do
if movement[2] == UP then
move = up(1, force)
else
move = down(1, force)
end
if not move then
return print("Aborted in up-down loop.")
end
end
if movement[3] == NORTH then
faceNorth()
else
faceSouth()
end
for i = 1, math.abs(moves[3]) do
move = forward(1, force)
if not move then
return print("Aborted in north-south loop.")
end
end
end
if shell ~= nil then
local args = {...}
if args[1] == 'update' then
updateCheck()
end
else
setup()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment