This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
GopherAtl's profile picture

turtlex v0.5.2 - smart turtle api - inventory + position tracking

Started by GopherAtl, 07 September 2012 - 03:25 PM
GopherAtl #1
Posted 07 September 2012 - 05:25 PM
0.5.2:VERY minor tweaks, nothing you would even notice, made for increased compatibility with the native turtle API (ex, turtlex.turnLeft() returns true now, like the built-in turtle api function) and with my current project, ctrl, a higher-level turtle interface system that adds waypoint-based pathfinding and simple macro-style scripting, which will be released eventually.

0.5.1:minor tweaks only; added "REQ" header identifying inventory as a requirement (which I needed for my own automatic api loader, which has not been released), and added a hackish version of transferTo. Proper version coming.

More importantly, releasing a utility program for use with turtlex, called inv. Run from the shell, inv displays the contents of the turtle's inventory as a menu. It provides a method of manually setting up the inventory type names without doing it by calling turtle.setItemType from lua. See below for info on using it and a pastebin link to the program.

—-
This is an api - well, a pair of them actually - I've been working on as part of a larger project. It replicates most of the functionality of the built-in turtle API, but adds code to most methods to provide position, direction, and inventory tracking.


Installation
You'll need both the inventory API and the turtlex APIs installed and loaded (os.loadAPI), and you'll want to load them in your startup file or at the start of the program that uses them. Inventory must be loaded first or turtlex will throw an error.

Usage
Spoilerinventory creates and manipulates table objects that describe the contents of an inventory. By itself, you must call functions to explicitly tell it of any changes in the inventory. I plan on making it more robust, but for now it's mainly meant to be used internally by the turtlex api.

turtlex has the same functions as turtle, but most have additional optional parameters. I'm going to make a single installer program you can download which will set up not only the APIs but also the in-game help files, but for now I'm just pasting them in the spoilers below.

Inventory
All functions which modify the inventory of the turtle will update the intenal tables, and if a new slot contains items, they will compare it to existing slots. Type names are assigned initially only by the calling code, using the turtlex.setItemType command, which gives the turtle a name for items in the slot. If setItemType is not called, all objects are assigned names beginning with "unknown," which are numbered to distinguish non-matching stacks - ex, if an empty turtle uses turtlex.dig and gets cobblestone in slot 1, it will name it "unknown." If it then digs some dirt, that will be named "unknown2." If it gets more cobblestone in a new slot, it will recognize it as the same as slot 1 and also call it "unknown." A single call to setItemType("cobblestone", 1) would rename both slot 1 and slot 3's items to "cobblestone".

Most of the basic functions that involve items (place, compare, drop, etc) have been modified to take an optional index parameter. By default they use the current slot, but you can pass as an index either a slot number and it will select and use that slot (re-selecting the previous selected slot before returning), or a type name. If a type name is given, it checks it's inventory for an item of that type and uses the first slot it finds that matches it. In either case if an invalid slot or type is given, it returns failure without taking action.

Movement
The movement commands are extended more. forward(), back(), up(), and down() all take an optional distance parameter - defaulting to one - and a second optional parameter telling it what to do if it hits something. This second parameter is a string and may be "stop", "return", or "dig". "stop" is the default, and does just that, it stops when it hits something. "return" attempts to return to the point where it started this move from, and "dig" will dig, repeatedly if necessary, until it either finds something undiggable or reaches it's destination. These have been tested quite a bit and are quite robust. As an example, if you give the command

turtlex.forward(20,"dig")

the turtle will repeatedly dig and move forward; if it encounters a stack of sand or gravel, it will detect the failures to move into the cleared space and dig again, so it will still correctly move 20 blocks forward regardless of how many times falling blocks get in the way. Additionally, for convenience, left() and right() functions are added, which turn to face the direction, call forward with the same params, and then turn back.

In addition to these enhanced basic commands, there are also move and goto commands for absolute and relative movement. These take x,y,z parameters and the same optional "onBlocked" parameter as the basic commands, so they can be told to dig, stop, or return. They do not pathfind, so are not guaranteed to find a path, but they do repeated application, so they can handle basic corners and things like stairs.

Lastly for movement is the position stack; you can call pushPos() to save the current position and direction to a stack, then popPos() causes the turtle to return to the last pushed position using goto(). This can be useful, for example, if digging a tunnel and you encounter a vein of ore; you can push the current position, call a separate function to follow and mine the vein, then pop the position back and resume executing your tunnel-digging routines.


help files
detailed info on all functions in the api and their parameters and return values. This is rather out of date, and omits many of the newer functions. Will get around to updating this eventually!
Spoilermain help index:
Spoilerturtlex
turtleX
Replaces the standard turtle API, adding position tracking, more advanced movement functions, and inventory monitoring.
functions:

basic movement - for more info, help turtlex.basic
forward back up down left right turnLeft turnRight turnAround

positional movement - for more info, help turtlex.move
move goto

position stack - for more info, help turtlex.stack
pushPos popPos topPos getPosStackSize getPosStackItem

compare commands - for more info, help turtlex.compare
compare compare compareUp compareDown compareLeft compareRight compareBack compareTo compareAll

drop commands - for more info, help turtlex.drop
drop dropUp dropDown dropLeft dropRight dropBack

suck commands - for more info, help turtlex.suck
suck suckUp suckDown suckLeft suckRight suckBack

dig commands - for more info, help turtlex.dig
dig digUp digDown digLeft digRight digBack

place commands - for more info, help turtlex.place
place placeUp placeDown placeLeft placeRight placeBack

inventory commands - for more info, help turtlex.inventory
getItemCount getItemType setItemType findItemSlot getSelected updateInventory

attack commands - for more info, help turtlex.attack
attack, attackUp, attackDown, attackLeft, attackRight, attackBack

turtlex.basic:
SpoilerBasic Movement Commands
With no parameters, these behave exactly like the standard turtle commands. Internally, they detect failure to move and update the turtle's position and direction.

forward([dist[, onBlocked]])
back([dist[, onBlocked]])
up([dist[, onBlocked]])
down([dist[, onBlocked]])
left([dist[, onBlocked]])
right([dist[, onBlocked]])

dist: number of blocks to move, default 1
onBlocked: what to do on failure, options: "stop", "return", "dig" defaults to "stop"

returns: success, trueDist
success: True if moved specified distance, otherwise false.
trueDist: Number of blocks actually moved from start. On success, always dist. If onBlocked was "return," returns 0 if successfully returned to start point, otherwise final distance from start.

The new commands left and right turn to face the specified direction, move forward, and then turn back to face the original direction. Always turns to face the original direction, regardless of the parameters or success of the move.

turnLeft()
turnRight()
turnAround()
Turns the turtle to face left, right, or backwards. Stored direction is updated.

turtlex.move:
SpoilerPositional Movement Commands

move(x,y,z[,onBlocked="stop"])
x,y,z : offsets from current position to move; ex, move(1,1,1) would move one block in each direction.
onBlocked: what to do when obstructed on all sides, "stop", "return", "dig", default "stop"

Moves the turtle to x, y, z relative to current position. Attempts to move x first, then y, then z. If any are obstructed, this order repeats until it reaches the target or is blocked in all three directions. onBlocked behavior occurs only when blocked in all three directions.

Returns true if successful, otherwise false.

goto(x,y,z[,onFail="true"])
same as move, but x,y,z are an absolute position rather than a relative one.

face(direction)
direction: direction to face. "north", "south", "east", or "west".

turtlex.compare
Spoilercompare(index)
compareUp(index)
compareDown(index)
compareBack(index)
compareLeft(index)
compareRight(index)
compareAll(index)
Index can be a slot number or an item type (string) to compare to. When passing an item, returns nil and an error message if a sample of the type is not in the turtle's inventory. If index is not specified, uses the block in the currently selected slot.

Left, Right, and Back versions turn first, compare forward, and then turn back to their original orientation.

compareAll compares all six surrounding positions to the sample and returns two values. The first return value is the number of matches found, the second is an array containing strings identifying the matching directions, "up","down","left","right","forward", or "back".

turtlex.dig:
Spoilerdig([slot=currentSlot])
digUp([slot=currentSlot])
digDown([slot=currentSlot])
digLeft([slot=currentSlot])
digRight([slot=currentSlot])
digBack([slot=currentSlot])

Digs to the specified slot or, if none is specified, the current slot. Note that, even if slot is specified, if the slot is full or contains a different kind of item, items may wind up in other slots!
Left, Right, and Back versions turn first, dig, and then turn back. Automatically calls updateInventory if any items are collected.

returns success, items
success is true if it was able to dig, otherwise false.
Items is an array of tables for each item dropped in the dig. Each element in the array has members "type" and "count"

turtlex.drop:
Spoilerdrop([quantity=all[,index=currentSlot]])
dropUp([quantity=all[,index=currentSlot]])
dropDown([quantity=all[,index=currentSlot]])
dropLeft([quantity=all[,index=currentSlot]])
dropRight([quantity=all[,index=currentSlot]])
dropBack([quantity=all[,index=currentSlot]])

Drops up to quantity items (if unspecified or nil, the whole stack) from the specified slot. If provided, index can be a slot number or the name of an item type in the inventory.

Left, Right, and Back versions turn first, drop, and then turn back. Automatically calls updateInventory.

Returns true if it was able to drop any amount of the specified item, otherwise false.

turtlex.inventory:
Spoilerselect(slot)
Selects the specified slot.
return: success, type
success: true if the slot was valid and is now selected, otherwise false.
err: if success was false, the second param is the error message, otherwise nil

getSelected()
Returns two values, the index of the currently selected slot followed by the type of item in that slot, or 0 if the slot is empty. Unidentified types are assigned names in the pattern "unknown#", with # assigned based on comparisons to currently unidentified types.


getItemType([slot=selected])
Returns the type of the item in the specified slot; if not specified, uses current slot.

getItemCount([slot=selected])
Returns the number of items in the specified slot or, if no parameters, the current slot.

setItemType(type[, slot=selected])
Sets the type name of an item in a given slot. Slot defaults to the current selected slot. If multiple
slots contain items with the same current type, all slots are updated to the new type.

getItemSpace([slot=selected])
Returns how many more items of the same type the current slot can hold. If empty, returns 64.

findItemType(type[, afterSlot])
Returns the index of the first slot containing an object of the specified type. If afterSlot is included, begins comparing to afterSlot+1.

updateInventory()
compares tracked inventory to actual turtle inventory. Counts for all items are updated. New items are compared to the existing inventory and assigned a type, or an unknown type if there are no matches.

turtlex.place:
Spoilerplace([index=currentSlot])
placeUp([index=currentSlot])
placeDown([index=currentSlot])
placeLeft([index=currentSlot])
placeRight([index=currentSlot])
placeBack([index=currentSlot])

index can be a slot number or a type name. If it is a type name, first slot containing this type is used. If nil, defaults to the current slot.
Left, Right, and Back versions turn first, place, and then turn back. Automatically calls updateInventory.

returns true if it was able to place the specified item, otherwise false.

turtlex.attack:
Spoilerattack([untilFalse=false])
attackUp([untilFalse=false])
attackDown([untilFalse=false])
attackLeft([untilFalse=false])
attackRight([untilFalse=false])
attackBack([untilFalse=false])

Attacks. Left, back, and right versions turn first, then return to the starting position. If untilFalse is specified and true, attacks repeatedly until attack returns false.

returns true if attack returned true on the first call, followed by a table of any new items added to the inventory.

turtlex.stack:
SpoilerpushPos()
pushes the current position to the position stack

popPos([go=true])
pops the last position from the stack. Optional parameter go defaults to true. If go is true, immediately moves to the position.

getPosStackSize()
returns the size of the position stack

topPos()
returns the position on the top of the stack, but leaves position on the stack and doesn't move

getPosStackItem(n)
returns the position on the stack at place n. 0 is the oldest item on the stack, getPosStackSize() returns the index of the newest item.

clearPosStack()
Resets the position stack, removing all entries.

turtlex.suck():
Spoilersuck([slot=currentSlot])
suckUp([slot=currentSlot])
suckDown([slot=currentSlot])
suckLeft([slot=currentSlot])
suckRight([slot=currentSlot])
suckBack([slot=currentSlot])

Sucks to the specified slot or, if none is specified, the current slot. Note that, even if slot is specified, if the slot is full or contains a different kind of item, items may wind up in other slots. Left, Right, and Back versions turn first, suck, and then turn back. Automatically calls updateInventory if any items are collected.

Returns success, items
success is true if it was able to suck some items, otherwise false.
items is an array of tables for each item collected by the suck. Each element in the array has members "type", "slot", and "count"


Version History
Spoilerinventory api
v0.1 - http://pastebin.com/yp00iTji

turtlex api - (inventory api must be loaded before loading turtlex!)
v0.5 http://pastebin.com/SYhqiJTk
v0.42 http://pastebin.com/YMqUDiv6
v0.41 http://pastebin.com/34VLLqeb
v0.4 - http://pastebin.com/ZEpCiYtD
v0.3 - http://pastebin.com/45xZc4jV
v0.2 - http://pastebin.com/9xS0JT3i
v0.1 - http://pastebin.com/vMGUKLHW

changelog
0.42
All variants of turtlex.drop() have been extended. When you specify a 2nd parameter as a type name to tell it what to drop, it will iterate over multiple slots containing that type if necessary to drop the requested quantity.

0.41
fixed a bug in the drop methods that caused dropUp and dropDown to drop forward instead.

0.4
new features
-Basic move functions (forward, back, left, right, up, and down) all now take an optional 3rd parameter, which is a function to be called after each successful step. If onFail is "return," and it hits an obstacle, it does not call the function each step on the way back.
-place() can now take a parameter to set the text when placing a sign in addition to the index parameter that specifies what to place. Parameters can be in any order, to both maintain compatibility with earlier versions of turtlex and improve compatibility with the standard turtle api. Note: it attempts to match the first parameter as an index first, then if that fails, the second; the result is, if you're placing a sign with text and that text matches exactly the name of an item in the turtle's inventory, you MUST specify both parameters, and the index must be first. If you want to use the item in the selected slot rather than specify another slot or type, you can just pass nil as the first parameter, though. Should only come up in rare cases.

bug fixes:
-fixed bug with gps that caused it to attempt to get position even after failing in locate() call. Also made it check for presence of a modem first, so if no modem is attached you don't have to wait for it to fail on loading the API.
-tweaked sevaral functions to behave more like their standard turtle counterparts - drop works without a parameter to drop all, refuel with no params uses all fuel in current slot instead of just one, etc

0.3
new features

setMonitorId(id) - specifies a computer ID to send position update notices to. Is saved and reloaded on restart. Currently there's no special monitoring program.

attackXXX([untilFalse=false])
available in all 6 directions like the other turtlex commands - attack, attackDown, attackLeft, attackBack, etc. Attacks in the direction. The parameter is optional; if true, it causes the turtle to attack repeatedly in a loop until attack returns false. For automated farms, this will cause it to attack until all mobs are dead. When repeating, returns true if attack returned true at least once, otherwise false. Updates inventory only if attack returned true, and only once in calls where untilFalse==true.

place functions now take an index rather than a slot, so, for example, if you are carrying known dirt you can call turtlex.place("dirt")

setPosition(x,y,z) - sets the turtle's internal position value to (x,y,z)
setDirection(dir) - sets the turtle's facing direction. dir can be one of turtlex's numeric direction values - 0==north, 1==east, 2==south, 3==west - or a string matching "east", "west", "north", or "south". The strings are not case-sensitive, and can be abbreviated, i.e. setDirection("E") would set the direction to east.
bug fixes:
-forward() and certain other basic functions no longer throw an error if called with no parameters
-drop() correctly takes an index (slot or type) instead of just a slot as optional 2nd parameter

Utility programs
inv
inv displays the inventory of the turtle and allows changing the item type names. The controls are displayed on-screen, but for reference:

arrow keys - move the selection around in the inventory.
enter - edit the name of the current item. Enter again saves the new name. Entering a blank line will cancel the edit.
space - forces an update of the inventory, useful when you have added, removed, or transfered items in the turtle's inventory while running inv.
tab - exits to the shell




Download
inventory API - http://pastebin.com/yp00iTji
turtlex API - http://pastebin.com/SYhqiJTk
inv (utility program) - http://pastebin.com/BTTRKswG
go http://pastebin.com/EpLB5vrH replacement for standard go command, identical except uses turtlex, so that your turtle doesn't lose track of it's position when caled.

Sample installation process
SpoilerThis is just a suggestion for those who don't know how to load APIs. If you do, feel free to ignore them, though note that inventory must be loaded first, and must be named inventory. If inventory is renamed, turtlex must be modified to refer to the new name instead of inventory.

In the shell, type the following commands:

> mkdir turtlex
> mkdir turtlex/apis
> pastebin get yp00iTji turtlex/apis/inventory
> pastebin get SYhqiJTk turtlex/apis/turtlex
> pastebin get BTTRKswG turtlex/inv
pastebin get EpLB5vrH turtlex/go

Add these lines to the top of your startup file.


--load the inventory and turtlex apis - order matters here!
os.loadAPI("turtlex/apis/inventory")
os.loadAPI("turtlex/apis/turtlex")

--add the turtlex directory to your path, so you can run inv and go directly
--added to the start of the path, so turtlex/go is found before rom/programs/turtle/go
shell.setPath("turtlex:"..shell.path())

If you don't want the apis loaded on startup, you can add the os.loadAPI commands to the top of your program instead, but you will not be able to run the inv or go programs.
Sebra #2
Posted 15 September 2012 - 07:13 AM
Seems good but…
Where are no functions to tell turtle it's current position? What if you want it GPS synchronized with others?
Why attack() functions ignored? They can change inventory.
GopherAtl #3
Posted 15 September 2012 - 08:12 AM
I am developing this for a specific project, which requires robust position tracking without gps or human intervention, hence those omissions. Attack functions, I've never even called before, so yeah, just didn't think to implement them. All of these things will be addressed eventually, got sidetracked with the logo project and now building some other code to go back and rewrite this with. Things I've implemented in the last two nights:
  • surefile - a simple api that provides routines for writing of state information to files in a verifiable way - so you're guaranteed to load back a valid status even if the program is interrupted in the middle of an update, and if an update was interrupted during save, know that on loading so you can compensate for any error it would've introduced.
  • goroutine API - for managing coroutines, which adds several features over the basic parallel api, including…
    • spawning additional coroutines on the fly
    • methods to wait not just for an event type but to filter those by any of the event's parameters as well (so no looping over pullEvent checking if it's YOUR timer, you can just do waitForEvent("timer", myTimer)
    • passing parameters to the initial call of the coroutine function
    • methods to assign a type of events exclusively to one coroutine over the others.
  • worker API - I'm not gonna try to explain this right now because it's late and I'm tired, but it will provide a common interface for the assignment and execution of tasks to and by turtles, including managing a task queue, reporting on the current status, and state saving for resuming where it left off.
Anyway, will eventually be rewriting turtlex to use surefile and to be compatible with (but not require) the worker API. I was possibly a bit premature in sharing this library, though I have tested it with a couple of mining programs. There's been no comments, so I assumed nobody was trying to use it, or I would've made a higher priority of adding missing features like gps support.
jasonb42 #4
Posted 17 September 2012 - 04:50 AM
This API is amazing! I've done something similar but this exceeds where I've gotten, plus the code clean and well formatted.
There are two enhancements I am interested in:

Optional function parameter for each move command
Each individual successful move would call the passed in function.

GPS verification of location at startup
This would verify current position and direction, as long as the signal strength is strong enough and the turtle can move in any x/y direction.

Would you be interested in collaborating on some of these enhancements? I am pretty busy for the next couple days but could work on it after that.
-JB
GopherAtl #5
Posted 17 September 2012 - 06:30 AM
Thanks! Im pretty pleased with the code so far. I've deliberately put off gps support so I can really get the self tracking as robust as possible. The posted version was almost guaranteed to be off on position or direction if a program was interrupted while moving, but no more! Rewrote the position saving this weekend, think its about as reliable as possible now, so I'll probably add gps support this week. Also added some new functionality, I'll update the post with a link to the new source and list of the new features soon. Includes enhancements to drop and getItemCount, plus an iterator for looping over all slots containing items of a given type.

The optional function to run after each move is a good idea, thanks for the suggestion! As for collaboration, feel free to make any encancements you like and share the code here. New features that really add something valuable I may incorporate into my version, crediting you of course!

Despite what I said last post, after thinking about it, I'm going to try to avoid making turtlex depend on any of my other APIs at all. I'm using it in more elaborate projects, but I want it to remain usable as just a replacement for the turtle API as well.

Anyway, new version will be posted soon, and thanks for the feedback and suggestions!
Sebra #6
Posted 17 September 2012 - 06:37 PM
Optional function parameter for each move command
Each individual successful move would call the passed in function.
I think using events is quite better.
GopherAtl #7
Posted 17 September 2012 - 06:42 PM
That's on the feature list as well, but if that function is going to have any turtle commands, event handlers in a separate coroutine would not work. Simple example, if you called forward(10) and passed a function that digs up, down, left, and right, you'd wind up with two coroutines giving conflicting commands to the turtle and the whole program would break, so there's cases for both approaches.
jasonb42 #8
Posted 17 September 2012 - 07:02 PM
That's on the feature list as well, but if that function is going to have any turtle commands, event handlers in a separate coroutine would not work. Simple example, if you called forward(10) and passed a function that digs up, down, left, and right, you'd wind up with two coroutines giving conflicting commands to the turtle and the whole program would break, so there's cases for both approaches.

To clarify why I specified a passed function (events would be useful for other cases) here is one example use case:
You specify a movement path around your tree farm. Every move calls the function harvestTree() which:
  • If a tree has grown in the next spot:
    • pushPop() the current position
    • Move up harvesting the tree, plant a new sapling, etc.
    • popPos() to return to it's start position
  • Returns, so program can continue moving around tree farm
You could also potentially use the function in a mining turtle to drop off inventory/refuel but there might be better ways to handle those cases.
Sebra #9
Posted 17 September 2012 - 07:53 PM
You tell it to go 10 m forward and each meter it should check for a tree? Find a better solution.
GopherAtl #10
Posted 17 September 2012 - 08:09 PM
no need to be snarky, it was just an example. Point is, if the per-step action involves any turtle code, a callback function is required instead of an event.
Sebra #11
Posted 18 September 2012 - 06:36 PM
In my opinion if calling program knows what to do on each step it should move step by step.
Events are usable by other handlers. For movement tracking, world exploring, boundaries checking…
In other words calling program do not know about it.
GopherAtl #12
Posted 18 September 2012 - 07:00 PM
eeh, you could just loop calling turtlex.forward(1) n times instead of turtlex.forward(n), but you would lose some of forward's functionality, like the onFail="return" option that makes the turtle return to it's starting point in the event of a failure, and for turtlex.goto or turtlex.move, you lose even more, since you'd have to reproduce the complex iterative structures it does internally if you were moving one step at a time.

There will, as I said, also be events, for cases where events will do the job, but anything involving the turtle is an exception. Some more examples include laying track or pipe along the path from point A to B or examining the blocks around it and mining any that match carried samples.
dadmob18 #13
Posted 04 October 2012 - 05:13 AM
I really like your concept here! Wrapping the turtle functions to track position and inventory is such a generally useful utility for almost any other turtle task. I was starting to do it myself when I found your post. One angle I was taking was to get rid of all those Up/Down/Forward/Back variations on the methods and add a first argument instead, one of "U", "D", "B", "F", so you just need to write move, detect, dig, etc. once instead of three or four times. Anyway, just a thought. It seemed to help clean up my functions.

I can't tell if you also can track inventory in chests with your library, or just the turtles.
Edit: oh yes, I see you can inventory.create(nSlots) for a chest or any other inventory, nice.

Keep up the good work!
GopherAtl #14
Posted 12 October 2012 - 04:29 PM
The inventory api is separate because I eventually intend to use it for chest inventory management as well, I haven't really tried using it that way even myself, and the dropTo and suckFrom methods on an inventory object are not very thoroughly tested yet. In theory, dropTo updates the inventory object's contents based on the logic the game uses to place items in slots, though naturally it depends on the inventory being up-to-date and the item types all being consistent; if you dropped "unknown1" into a chest that already contained an "unknown1" it would expect them to stack, and if one was dirt and the other cobble, obviously they wouldn't. Also, if you dropped dirt labeled "unknown1" into a chest that had dirt labeled "dirt" it would NOT expect them to stack. These are part of the reason I haven't been using the inventory api except for turtles, who can do compareTo to identify and correct this sort of issues.

I considered taking parameters to specify directions, but decided instead to mirror the turtle api's approach. Eventually, I am considering setting it up to override the turtle api, so that existing programs written for turtle will use turtlex instead.

Should be uploading a new version of this tonight. I've added support for gps - it attempts to verify it's position with gps every 8 moves, as well as checking position on startup, but otherwise continues to use it's own tracking, so it won't be affected by storms or moving out of range of the gps hosts. Also added an optional monitor server, which it reports it's position to at fixed intervals; by default it's nil, and it sends no reports, but you can call turtlex.setMonitorIID() with the id of a computer (or other turtle) and it will send periodic updates to them. Currently only sends its position every 8 moves. Before I upload it, I also plan on finally fixing the issue with many of the commands, including forward(), that causes them to throw an error if called with no parameters, when they should be falling back to on the standard turtle api behavior (in the case of forward, moving 1).

Anyway, glad you like the api, thanks for the feedback!
ChunLing #15
Posted 13 October 2012 - 10:51 PM
Why not just use an array of single chests, each chest only having a single item type, for the work of ensuring consistent loadouts on the turtles? Then you can have "dump" chests for excess/unidentifiable items. You can have sorting turtles that have single item stacks filling their inventory and can go over the dumps, sucking up anything that stacks with their existing inventory, then go to the chests of identified items and turtle.drop(turtle.getItemCount(slotNum)-1) so that they keep a single item in the identified stack.

That way most of the turtles can just dump stuff off in the dump chests, then the sorting turtles sort it all out.

You can have a central database that gets a rednet message every time a turtle dumps, saying how much in how many stacks, so that the sorting turtles can just check dumps that are getting full, to save on movement/fuel. Or you can have 9 turtles surrounding the chest, with 144 different item types, sucking on the chest whenever a dump occurs and returning to the identified chest array when they are full of something. Or you can use a combination of both techniques.

Regular (mining) turtles will have about half their inventory 'reserved' for specific item types and half left open for unidentifiable (to that particular turtle) items. When they start getting items in slot 16 they visit the nearest dump chest. For efficiency, you could have the dump chests be chest carts on rails with a powered cart on either side of the train. When the train was getting full/emptied, a turtle would just place a lump of fuel in the powered cart at the end to send the train up to 500 meters (much more efficient then having a turtle carry stuff all the way back, particularly since each chest cart can carry more and you can have a whole train of them).
CoolisTheName007 #16
Posted 15 October 2012 - 12:26 AM
I am re-writing a program to control a reactor and a breeder simultaneously, and your goroutine API is just what I need to use virtual RP2 timers correctly. Do you still have it somewhere?
GopherAtl #17
Posted 15 October 2012 - 04:49 AM
Chum, yes, separate chests are actually what I tend to use, which is why I haven't worked on the inventory API anymore.

I'll eventually release the goroutines API, there's a couple of things I wanted to change in it first. I might go ahead and release the current version later, though, if there's interest.
Nommy #18
Posted 26 October 2012 - 08:52 PM
I'd also be interested in seeing/using the goroutines API and the other stuff you've done GopherAtl. Thanks for posting what you have already. I briefly started something a little while back using turtlex and thought it was great - clean and clear compared to other scripts. In fact the I only came was here to see if you'd posted any updates or new stuff since a lot of the stuff you talked about having done or working on sounded similar to things I had in mind and I was hoping to find some more goodies. I've still got my fingers crossed on that one :D/>/>

Also I had one suggestion / idea while I was trying out the turtlex before: In gpsUpdate(), check for air before moving into that space, so as to not accidentally destroy water/lava source blocks.

Thanks again, looking forward to seeing more.
ChunLing #19
Posted 26 October 2012 - 10:41 PM
Water blocks aren't such a big deal, but some people do find lava valuable. The solution I prefer is to use a water source above a deep excavation, so that all the lava is turned into obsidian, but in tunnels and such this is not as feasible (it's still possible, just not very easy).
Nommy #20
Posted 27 October 2012 - 01:09 AM
No, it's certainly not a big deal, just a suggestion. I was thinking more along the lines of water used in sugarcane farms or in fountains etc which you wouldn't want accidentally destroyed if gpsUpdate() happens to be run by a turtle going past when the server restarts or you quit single player.

The turtlex.place() functions did not seem to include any method of specifying sign text so I added a parameter in each:
Spoiler
local function placeHelp(index,placeFunc,signText)
  local slot,err=indexHelper(index)
  if not slot then
	return nil,err
  end

  local prev=selectedSlot
  if slot~=prev then
	select(slot)
  end

  res=placeFunc(signText)
  if res then
	--successfully placed
	updateInventory()
  end

  --restore selected slot
  if slot~=prev then
	select(prev)
  end
  return res

end

function place(index,signText)
  return placeHelp(index,native.place,signText)
end

function placeUp(index,signText)
  return placeHelp(index,native.placeUp,signText)
end

function placeDown(index,signText)
  return placeHelp(index,native.placeDown,signText)
end

function placeBack(slot,signText)
  turnAround()
  res=place(slot,signText)
  turnAround()
  return res
end

function placeLeft(slot,signText)
  turnLeft()
  res=place(slot,signText)
  turnRight()
  return res
end

function placeRight(slot,signText)
  turnRight()
  res=place(slot,signText)
  turnLeft()
  return res
end
I only briefly tested turtlex.place("Sign", "message"), and I'm new to this, so keep that in mind. It seemed to work ok though.
GopherAtl #21
Posted 05 November 2012 - 07:01 PM
oh, I'd forgotten about that functionality, because I've never found cause to use it. Good catch, I'll make the same change before the next release.

I'm feeling like I've fixed some bug since I posted this, but I'm terrible about keeping notes when I'm playing minecraft (and yes, even coding this 1300-and-counting-line api falls into the category of "playing" to me), so I can't for the life of me think of what it might've been…

oh! I remember! when I added a gps check on startup, I forgot to retest it in a world without gps servers. It errors out on load if it can't find one. Pretty serious problem, will make a priority of uploading the fixed version, plus the parameter to place signs. That'll be tricky, I want to retain perfect turtle compatibility, meaning the text for the sign should be optional as the first and only parameter, while I've added the optional parameter for specifying the item type, which can also be a string and the only parameter… harumph. I might just have to break with compatibility on this one, let it be the 2nd parameter. Only other option is, if the first and only param is a string, try to match it to an inventory item and, if that fails, pass that string as a parameter to turtle.place. In fact, I think I'll go ahead and do that, because I really want to keep the ability, in the future, to make this actually replace the turtle api with itself, and existing turtle programs to work perfectly with it.

In the middle of something else right now (assembler + emulator for the DCPU-16, written in computercraft), and won't be up much later tonight I don't think, so it'll probably be tomorrow before I get a new version up.
GopherAtl #22
Posted 07 November 2012 - 07:03 AM
Bump for v0.4, added support for specifying text when placing signs, optional onStep callback function to basic move parameters that is called after each step, a few bug fixes, and some tweaks to make it more perfectly compatible with the native turtle methods.
trondaron #23
Posted 08 November 2012 - 04:37 AM
Your code inspired me to make a smaller more limited relativePosition tracking system for my turtles. Thank you GopherAtl. It also acted as my intro to the LUA structures.
GopherAtl #24
Posted 09 November 2012 - 01:06 PM
Nice to hear, good luck with your program!

Couple of minor updates yesterday. Fixed a bug that, for the life of me, I can't figure out how got through this long - dropUp and dropDown were dropping forward, meaning you couldn't use furnaces or anything. Which is the odd part, I'm certain I've used furnaces with turtlex; I can only guess I fixed the file in-place and somehow the change never got pushed up to my saved version from that particular world.

Also upgraded the drop commands so that, if you pass the optional second parameter as a named item type, and have multiple stacks of that type, it will iterate over stacks as necessary to drop the requested amount. This means if you're carrying 5 full stacks of cobblestone (and you've told the turtle it's cobblestone with turtlex.setItemType()), you can call "turtlex.drop(150,"cobblestone") and it will drop the first 2 full stacks plus another 22 from the third in order to drop the full 150 requested.
Lyqyd #25
Posted 09 November 2012 - 02:17 PM
Only other option is, if the first and only param is a string, try to match it to an inventory item and, if that fails, pass that string as a parameter to turtle.place.

This option does have the distinct disadvantage of making automated material-label sign placement for storage zones significantly more interesting.
GopherAtl #26
Posted 09 November 2012 - 02:48 PM
Not really; the way it was finally implemented, if there are two parameters, it only uses the first as the sign label if the first does not match an item, so as long as the signs are labeled, turtlex.place("sign","cobblestone") would place a sign labeled cobblestone.
Lyqyd #27
Posted 12 November 2012 - 07:53 AM
Ah, that makes sense. It does make me wonder, though, what would happen if it were to have an inventory of signs but not know it? It assumes it has no signs in the inventory, so passes the first argument? Or does it bypass the first argument if it's "sign" regardless of what it thinks it's placing? Hmm. Time to poke through the code!
GopherAtl #28
Posted 12 November 2012 - 12:50 PM
it's not special-cased to a parameter called "sign." If you pass 2 params, it tries to match the first to an inventory item, either as a type name or as slot number; if that fails, it tries the second; if that also fails, it returns an error. Otherwise, it uses the first param that was a valid identifier to select what to place, and the other parameter is passed to turtle.place()
GopherAtl #29
Posted 22 November 2012 - 08:39 AM
v0.5! Not much for new features, but quite a few bug fixes in this update. Getting down to the short list of changes before I stop adding new features and start working on the v1.0 package, which will have an optional installer to set up help files for in-game help and upate your startup file, setting the help path, loading the APIs, and, optionally, setting up turtlex as a replacement for turtle, so that any existing program calling turtle commands will call the turtlex versions instead. The latter will have the advantage of allowing turtlex to continue correctly tracking position and inventory even when running programs not written with turtlex.
GopherAtl #30
Posted 05 February 2013 - 11:44 AM
:bump: minor update to turtlex, but new long-overdue utility program for editing the turtle's inventory.

http://pastebin.com/BTTRKswG

From the shell, with turtlex loaded, just run this program and you will be presented with a display of the turtle's inventory. Slots can be selected with arrow keys, and by pressing enter, you will be able to edit the type name for any non-empty slot. Pressing space will update the turtlex inventory, causing the display to update for items added or removed while in the inv program.
xInDiGo #31
Posted 05 February 2013 - 06:04 PM
wow this looks awesome! i'll have to give this a try! :D/>
GopherAtl #32
Posted 01 June 2013 - 02:40 PM
bump for pretty minor update. I may well have fixed other bugs in the last months, I haven't been keeping a proper log, just using the api in all my turtle projects and fixing what comes up, but I know I've made several minor tweaks that increase compatibility with the built-in turtle API, none of which should directly affect any programs.

One added feature, can pass negative counts to the drop functions, which will cause it to drop all but that number, ex, -5 will drop all but 5. If you specify what to drop by slot number, will drop all but that many from that slot; if you're using the inventory features and labeling types, specifying what to drop by type name will drop all but that many, ex:

Assume you start with full stacks of cobblestone in slots 1, 2, and 3, and have told the turtle that it is "Cobblestone" (with the inv program, or "turtlex.setItemType("Cobblestone",1)" ).


-- this would drop all but 1 cobblestone from slot 1; slots 2 and 3 would still contain full stacks.
turtlex.drop(-1,1)

-- this would drop all from stacks 2 and 3, keeping only 1 cobblestone in slot 1.
turtlex.drop(-1,"Cobblestone")

no progress on the help files or installer for a proper 1.0 release, because, well… they will be a fair bit of tedious work and not benefit me personally in any way…<_</>

ctrl, a program I started a long time back (you may have seen an early version in my last cc challenge video), and just recently started working on again, will be released soon, probably in this thread. At that point I will at least set up an installer, though probably without help files, that will automatically install ctrl, the required APIs (turtlex and inventory), and some other utility programs, as well as turtlex-compliant replacements for the standard turtle programs (like "go" and "excavate"), setting up your startup file as needed.
OrionDude #33
Posted 18 July 2013 - 01:06 AM
Hello there I am a complete noob at this and I need some help. When I try to load the files from pastebin I get an error: pastebin:92: attempt to index ? (a nil value) for both codes yp00iTji and SYhqiJTk. Am I doing something wrong? I'm typing it in exactly as it is.
GopherAtl #34
Posted 18 July 2013 - 01:12 AM
wow, sorry, that sample code was 3 kinds of broken. I've just updated it. It was telling you pastebin the files to turtlex/apis/, but it didn't tell you to create the turtlex/apis/ directory, just /turtlex. Then it also told you to have the startup file load them from just turtlex/, which would've errored as well.

I must've written that at the end of a very long day… Both are fixed, gonna verify right now

:edit: verified installation example is working now. Sorry 'bout that!

Also added my go replacement, which is exactly like the builtin go command, but calls turtlex, so your turtle doesn't lose track of it's position and orientation when you use go from the shell.
OrionDude #35
Posted 18 July 2013 - 01:21 AM
You are a gentleman and a scholar, thank you very much for your quick response and all your hard work, keep it up my friend!
OrionDude #36
Posted 22 July 2013 - 06:03 PM
So now that I've got everything installed and for the most part using it successfully, I am quite puzzled on how to use the position tracking capabilities. In a single player creative world when I tell it to go to say: turtlex,goto(1,1,1,"dig") it went to somewhere closer to 649,1,-638. The two larger numbers are very close so I'm assuming that it's almost working but an indepth explanation of the position tracking would be great.
GopherAtl #37
Posted 22 July 2013 - 11:00 PM
Unless you have gps set up, it has no idea where it is in the world or what direction it is facing. The first time turtlex is loaded, it will just arbitrarily call it's current location 1,1,1, and it's current direction north. I rarely use goto myself, favoring the relative version, move, or more explicit movements with forward(), etc. To override this without a gps signal, you have to call turtlex.setPosition and turtlex.setDirection; I should really a simple program to prompt for those and set them for you, and have that run on startup if gps is not available. In fact, I will do that.

In the mean time, the simplest thing to do is, after placing it, run lua and enter the following:


turtlex.setPosition(100,72,-96) --naturally replace these #s with the turtle's real x,y,z position
turtlex.setDirection("west") -- again, change "west" to the direction it's actually facing.

You should only have to do that when you are placing the turtle, including if you break it and place it somewhere different. Otherwise, it will track movements and remember it's position and direction even if rebooted, the server restarts, the chunk is unloaded, etc.
Also #38
Posted 23 July 2013 - 08:13 PM
  • function popPos(go)

​parameter is missing. In turtlex api
GopherAtl #39
Posted 23 July 2013 - 11:57 PM
Talk about wierd timing, I was just coming here to say that I'd found that issue and fixed it.

Fixed, in any event!
Also #40
Posted 27 July 2013 - 06:25 PM
gutils.serialize(posStack)
are you serious? maybe
textutils.serialize(posStack)
?
in turtlex.savePosStack()
GopherAtl #41
Posted 27 July 2013 - 06:29 PM
gutils.serialize(posStack)
are you serious? maybe
textutils.serialize(posStack)
?
in turtlex.savePosStack()

lol, oops.

gutils is one of my own APIs, I'm so used to having it I forgot it's not included with the turtlex distribution…

a change to textutils.serialize broke push/popPos, because it thought the posStack table was a recursive table, and so wouldn't write it. In a quick fix I switched it to use my serialize/unserialize, which don't break. I will make a new fix that doesn't add a new dependency and update.

Sorry!

:edit: there, fixed.
hermanoid #42
Posted 06 January 2017 - 04:00 AM
Did you ever get the installer figured out? Might I suggest packman by Lyqyd? I'm currently coming up with a tangled mess of dependencies in my programming adventures, and (thank goodness) I found his program before trying to come up with my own. It would be a fairly simple matter: Create that special package type (I think it's "meta" or something like that) and have dependencies to your two Pastebin links.
hermanoid #43
Posted 07 January 2017 - 03:51 AM
Hmm… I'd do well to look at the Packman Thread for recommending that. I see you're already aware of that particular tool. My Bad.