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

Self-Replicating Turtle Tutorial

Started by Inksaver, 14 May 2014 - 09:55 AM
Inksaver #1
Posted 14 May 2014 - 11:55 AM
This tutorial is a companion to the post about making a self-replicating turtle posted here: Self-Replicating Turtle Script

Part 2 is here: Tutorial Part 2

Part 3 is here: Tutorial Part 3

It assumes you are already familiar with the turtle api. You already know how to get the turtle to dig using:

turtle.dig()
If not you will need to read the excellent tutorials on this forum before you continue.
So you all know the competition rules: place an empty crafty mining turtle in front of a tree, with no fuel, and get it to reproduce itself.

Easy!


There is just one line of code in the script:

main()
You will find it at the bottom of the script, around line no 11,567. That's it!

But surely, I hear you say, the first line of code should be:

turtle.dig()
Using that, you dig the bottom log of the tree, then use

turtle.refuel()
That will eat the log and you are good to go…
Well…Yes…and no…. We will get back to turtle.dig() a bit later. Well a lot later. Lets continue:

OK so there are 11566 other lines containing functions, classes and objects, but those are all ignored until they are called. So let's have a look at the function main() which is the last function in the script. The rest are all in alphabetical order so it is easier to find the one you are looking for, with the exception of initialise(), which is the first function, and debug(), which is last but one.

With a large project like this, it is important to organise it properly, otherwise the code gets very messy, things go wrong and are difficult to track. To help with organising I put all my functions first, in alphabetical order, and then use a function called main() which runs everything. This is a method developed before Lua was born, and still works well. As functions need to be declared before they are used, it makes sense to put main() at the bottom of the script. Let's have a look at main(). Hopefully we will find our first line - turtle.dig() within main()

function main()				
	   initialise()		 -- Setup global variables
	   --deBug("logFile")   -- uncomment for debugging and starting at different stages
	   getCoords()		  -- Get input from player
	   firstTree()		  -- Harvests first tree, fuels, makes a chest
	   findCobble()		 -- Dig down far enough to find cobble
	   checkGravel()		-- If gravel found, test to make sure
	   confirmCobble()	  -- Suspected cobble found so confirm, and place furnace
	   clearBase()		  -- Clear area around first tree 4X4
	   --deBug("AllTrees")  -- uncomment for debugging and starting at different stages
	   harvestAllTrees()	-- Harvest trees in 32 x 32 area until enough resources found for deep mining
	   prepareMining()	  -- Enough resources found to go diamond mining. Prepare mining area.
	   mineForDiamonds()	-- Mining area prepared. Mine 3 layers, then to bedrock until min 6 diamonds found
	   craftMiningTurtle()  -- All resources mined. Build new turtle(s) if minimum 3 diamonds found
end
You will notice there are 11 functions in main(), which do all the work. There are two commented out, which were used during debugging, so the script could be re-started at different stages.
So already you can see a structure to the program, it has a function called main(), whose sole purpose is to consecutively call 11 other functions. As you have probably already guessed, each of these 11 calls further functions as and when needed. Disappointingly turtle.dig() does not yet appear!
The first one is initialise(), so lets have a look at that one:

function initialise()
	   -- create classes/objects
	   createCoordObject()
	   createFurnaceObject()
	   createChestObject()
	   createSlotObject()
	   createLogfileObject()
	   createTreeFarmObject()
	   createDigObject()
	   createPlaceStorageObject()
	   --Edit out other stuff here
end

Still no sign of turtle.dig()!

So let's look at one of the functions in this lot: creatSlotObject()

Wait, I hear you say, objects is getting nerdy. All I want to do is turtle.dig() Right?
Well don't let objects frighten you. You are already using them.

turtle is an object, and .dig() is something that object can do: It is a method of the turtle object
Before discovering Computercraft, I always used to think of an object as a black box filled with gears and levers, with buttons on the outside to make it do things, tools on the opposite side controlled by the buttons. On another side of the box are labels, meters and gauges tell you what is going on inside the box, and a hole in the top you can drop instructions in if you wanted to change what the labels and meters are displaying. It would also be touch-sensitive, and have various gadgets, to see, hear, or otherwise detect what is going on around it. It would be able to respond to events such as phone calls, knocking on the outside of the box etc.
Then along came Computercraft and turtles, and the object appears as a grey box, with tools on its side, can tell you how much fuel it has, responds to right-clicking on it, or redstone signals etc….Uncanny
The buttons are invisible, but you can press them using code: turtle.dig() as an example
The labels and meters are also invisible, but you can read them with code:

turtle.getFuelLevel()
will give you the current fuel level. This is a Property of the turtle object. You can sometimes set the value of a property. eg . turtle.setFuelLevel(10000) would be great, equivalent to dropping a note into the top of the box telling it there is now 10000 fuel ready to use. Unfortunately this has not been put in place….

You can of course get round this by being a bit sneaky. There is a back door on the turtle, which you open by right-clicking on it, and a monitor screen and shelf rack appears, consisting of 16 partitions grouped in a 4 x 4 grid. Right-Clicking on the turtle triggers an event, which displays the inventory and screen. So you have already been using methods, properties and events of an object. You are a fully qualified nerd already!

If you stick some wood into one of the shelves in the back of the turtle, you can then set the fuelLevel() property with a bit more code. Let's say you put a log into slot 16 (bottom right shelf)

turtle.select(16) -- a method
turtle.refuel()	  -- a method
The refuel() method will act internally by getting a small person inside to carry it away, so it disappears from the shelf, put it in the fuel tank, then write on the label on the outside to display the current level….1 log gives 15 units of fuel, so :

print(turtle.getFuelLevel())
will output 15 onto the screen, all this without you knowing what is going on inside the object.

Now you are ready to make you own objects. Remember: a black box, tools and buttons(methods), eyes and ears (events), and a noticeboard(properties) For the purposes of this program, you will not need events, only properties and methods. Here we go with a very important one, the slot object:

function createSlotObject()
	   clsSlot = {}
	   clsSlot.__index = clsSlot
	
	   function clsSlot.new(self) --creates new instance of slot object
			  local self = setmetatable({}, clsSlot)
			  self.slotContains = {}
			  self.slotCount = {}
			  self.slotStatus = {}
			  self.index = 1
			  self.itemTested = {}
			  self.firstUnknownItem = ""
			  self.lastUnknownItem = ""
			  self.freeUnknownItem = "item1"
			  self.numUnknownItems = 0
			  -- initialise variables
			  for i = 1, 16 do
					 self.slotContains[i] = ""
					 self.slotCount[i] = 0
					 self.slotStatus[i] = "empty"
					 self.itemTested[i] = ""
			  end
			
			  return self		
	   end
	   Other stuff here edited out
end

We do not need to go into the purpose of the first few lines in this tutorial, just accept it as part of the process of making a "class", which is a blueprint for creating objects. All this self. stuff and metatables makes my head spin.
This function is used to create a new object. In nerdspeak we are creating "an instance of the class" Note the dot syntax clsSlot.new() . Any functions within this object to manipulate methods or properties should follow the same pattern. If this was a class to create turtles, it would create a new, empty turtle, with no fuel, which would work independently from any other turtles in the world. This time we are using it to create a slot object. If you look at the line at the end of the createSlotObject() function you will see:

slot = clsSlot.new()

This line is creating an object called slot by invoking the clsSlot.new() function. This is how it works:
Call the function:

clsSlot.new()
create the object:

local self = setmetatable({}, clsSlot)

Set the default properties of the object:

self.slotContains = {}
self.slotCount = {}
self.slotStatus = {}
self.index = 1
self.itemTested = {}
self.firstUnknownItem = ""
self.lastUnknownItem = ""
self.freeUnknownItem = "item1"
self.numUnknownItems = 0
for i = 1, 16 do
	   self.slotContains[i] = ""
	   self.slotCount[i] = 0
	   self.slotStatus[i] = "empty"
	   self.itemTested[i] = ""
end
Finally, and very important not to forget this line:

return self
This completes the creation of the object. Great, but why? What's the point?

Think about what we are trying to do. As the turtle goes on its business, it digs blocks into its 16 slots, but it has no way of identifying what the blocks are. By using an object which keeps track of what is dug, where it is stored, and how much there is at any time, we are in full control of the turtle contents.

Now that was not too difficult was it? All that's left is to write some getters and setters, and a couple of methods while we are at it. To keep it simple we will just write a "getter". We already used one on the turtle:-

turtle.getItemCount()
Now let's write our own

function clsSlot.getSlotContains(self, slotNo)
	   return self.slotContains[slotNo]
end
This will return the contents of the slot requested as a string e.g. if the turtle had wood in slot 1

print(slot.getSlotContains(self, 1))
>  "wood"
Luckily there is a shortcut, to avoid using self all over the place:

print(slot:getSlotContains(1))
>  "wood"
Note the colon instead of dot
The other functions createXXXObject() all do similar things, but we will skip over them for the time being.

The remaining code in initialise() just sets up some global variables

-- set global variables. As each item is identified, set objectives["item"] = true
objectives = {}
local items = {}
items = {"wood","wood2","dirt","cobble","stone"}
						
for key, value in ipairs(items) do
	   objectives[value] = false
end
The table items = {"wood","wood2","dirt","cobble","stone"} has been edited to just 5 items for clarity

We want to set:


objectives["wood"] = false
objectives["wood2"] = false
objectives["dirt"] = false
objectives["cobble"] = false
objectives["stone"] = false
The loop for key, value in ipairs(items) do is a special type of for loop. It iterates the entire list of values stored in the table "items" which we set to {"wood","wood2","dirt","cobble","stone"}
and sets objectives[value] = false, where value starts as "wood" and ends as "stone". It does the same as the 5 lines of code above, but in 3 lines. If we have 50 values stored in items{} it would take 50 lines of code
If we add more text to items{} in edit mode, the for loop creates new objectives[] to suit in run mode.

So that has got initialise() sorted

NOW can we get to turtle.dig()

Patience, please. We are nearly there. The next function in main() is getCoords()
This asks the player for coordinates, and whether a logFile should be produced. Getting the coordinates is very useful in the debugging process as the exact location where something goes wrong can be written to the logFile. Also the y coordinate is essential for the later stages of the program, when deep mining uses the depth of the turtle to excavate areas between 0 and 14 for diamonds.

If tutorials on player input, or writing logFiles are required, let me know with a reply to this post.

Now we can go to the function we have all been waiting for: firstTree()
Function firstTree(). At last we use turtle.dig()


function firstTree()
	   local quantity = 0
	   local tempyCoord = 0
	   local emptySlot = 0
	   local dugItem = ""
	   local dugSlot = 0
	   local success = false
	   local isJungle = false --check if next to a jungle tree
	   local isBranched = false
	   local numLogs = 0
	   local treeSide = ""
	   local sTime = os.time()
	
	   term.clear()
	   term.setCursorPos(1,1)
	   --break lower block of tree
	   saveToLog("firstTree: Chopping tree base", true)
	   dig.digNew{self = dig, expectedItem = "wood", callFrom = "firstTree"}
	   --craft to 4 planks in slot 2
	   saveToLog("firstTree: Crafting planks for fuel", true)
	   craft{craftItem = "planks", craftQuantity = 4, sourceItem1 = "wood", destSlot = 2, chest = "doNotUse", doSort = false}
	   --use all planks as fuel
	   turtle.select(slot:getItemSlot("planks"))
	   saveToLog("firstTree: "..turtle.getItemCount(2).." planks used for fuel", true)
	   turtle.refuel(turtle.getItemCount(2))
	   slot.update{self = slot, item = "planks", delete = true}
	   forward(1) --now under tree trunk - on level 0

	   other stuff here edited out

There it is:

The 17th line down:


dig.digNew{self = dig, expectedItem = "wood", callFrom = "firstTree"}

WTF!!!

Where is turtle.dig()???

If anybody wants a continuation let me know by replying below……
Enjoy
Edited on 20 May 2014 - 10:52 AM
H4X0RZ #2
Posted 14 May 2014 - 02:45 PM
This is amazing!
I really like the program and now you are making a tutorial for it!

I love it, 1UP
Skillz #3
Posted 14 May 2014 - 06:48 PM
Nice job!
flaghacker #4
Posted 14 May 2014 - 09:00 PM
I really like this style of tutorial, were you don't explain all the details, but just enough to understand everything. And with that cliffhanger I can't wait until the next tutorial:
Please keep going!
Edited on 14 May 2014 - 07:01 PM
Inksaver #5
Posted 15 May 2014 - 01:48 PM
Turtle replication continues….

Just to remind you, we left off part one with the line:


dig.digNew{self = dig, expectedItem = "wood", callFrom = "firstTree"}

instead of the expected


turtle.dig()

The reason for this is that dig() is now a fully fledged object. It has a method .digNew()
and 4 properties


.getSuccess()   -- returns true or false
.getDugSlot()   -- returns slot 1 to 16 where the block dug ends up
.getDugItem()   -- returns the item dug e.g "wood"
.getDugAmount() -- returns the amount dug eg 1

Before we dive in to the object itself, a brief explanation of the syntax used in the line above.
It uses named arguments e.g. expectedItem = "wood". There are two reasons for this.

Firstly my memory is so poor, after I have written a function such as


function anyFunction(name, age, IQ)
	-- code here to make use of supplied arguments
end
When I come to call it, I will probably forget the order of the arguments and use something like


print( anyFunction(180, "Inksaver")

So firstly I got the string (name) and number (age) the wrong way round and forgot the IQ completely! The function would probably not behave as expected. Hang on…. Let me look at those function arguments again…

By using named arguments, I could use instead:


print( anyFunction{ IQ = 56, age = 180, name = “Inksaver” }

This time it does not matter which way round I put the arguments, or how many arguments are supplied. The function will get the right information. Note the arguments are supplied as a table, using{}, not as a list using(). Note to self: checkout the function arguments again. Something not right there…

The first line in dig.digNew{} is:


function clsDig.digNew(arg)
Now the code nazis are jumping up and down, frothing at the mouth because I used (arg). By convention I should use (tArgs) or similar. But it works, so who cares?

The table supplied by the function call is filtered to get the correct input. The following lines set default values for all possible arguments


local self = arg.self -- no filter here you MUST supply the object name
self.direction = arg.direction or "forward"
self.slotNo = arg.slotNo or 1
self.expectedItem = arg.expectedItem or ""
self.checkForItems = arg.checkForItems or ""
self.callFrom = arg.callFrom or ""
self.waitForGravel = arg.waitForGravel or false
self.suck = arg.suck or false
So if I simply called

dig.digNew{self = dig} -- minimum argument supplied
It would be the equivalent of using:

dig.digNew{self = dig, direction = "forward", slotNo = 1, expectedItem = "", checkForItems = "", callFrom = "", waitForGravel = false, suck = false}

The dig() object is over 2000 lines, so it would be impossible to list it out here, but the essential features can be covered fairly quickly. Its purpose is to dig or suck blocks from any direction, analyse them if possible, and use the slot object to keep track of the inventory. When a slot gets close to the maximum quantity it can hold, blocks are dropped.

Back to the first tree, and digging the first log. Because we know the first block to be dug will be wood, the dig object can be told this is what it is about to get, by using the argument expectedItem = "wood". The function calling the dig object is also given: callFrom = "firstTree". We do not need to specify which slot to dig into, as it defaults to 1

Let’s follow the code through


dig.digNew{self = dig, expectedItem = "wood", callFrom = "firstTree"}

This calls the function method dig.digNew() in the object dig:


function clsDig.digNew(arg)
	   -- reset variables
	   local self = arg.self
			
	   self.callFrom = ""
	   self.checkForItems = ""
	   self.cobblePlaced = false
	   -- code edited out here
	   local result = false
			
	   -- set up array of current quantities in each turtle slot
	   for i = 1, 16 do
			  self.itemCount[i] = turtle.getItemCount(i)
	   end
			
	   self.direction = arg.direction or "forward"
	   self.slotNo = arg.slotNo or 1
	   self.expectedItem = arg.expectedItem or ""
	   self.checkForItems = arg.checkForItems or ""
	   self.callFrom = arg.callFrom or ""
	   self.waitForGravel = arg.waitForGravel or false
	   self.suck = arg.suck or false
	   if self.suck then --turtle is sucking
			  clsDig.suck(self) -- will set self.success
	   else -- turtle is digging, not sucking, so check for mobs
			  --remove any mobs
			  clsDig.attack(self)
			  --mobs disposed of, now get on with digging
			  clsDig.doDig(self) -- will set self.success  
	   end
	   -- code edited out here
end
The first lines of code resets all the objects internal variables to default values
The array of current turtle quantities gives a baseline to check which slot (if any) changes after a successful dig. If the turtle had 1 wood in slot 1 and 14 dirt in slot 3, The result of running the loop:


for i = 1, 16 do
	self.itemCount[i] = turtle.getItemCount(i)
end
would give:

self.itemCount[1] = 1
self.itemCount[2] = 0
self.itemCount[3] = 14
Then if the turtle dug a cobble, with slot 1 selected, the cobble would go into the first empty slot, which is slot 2
Later in the object code, using another loop will discover which slot was used during the dig. We will come to that later.

The next step, surprisingly, is not turtle.dig(), but an internal function to attack mobs. The reason for this is if a mob is in front of the turtle it would prevent a successful dig, so make sure the mob is chased away or destroyed first. This is the function:

clsDig.attack(self)
We do not need to check it out at this stage, and move on to the next function:

clsDig.doDig(self)
This is the function that actually instructs the turtle to dig:


function clsDig.doDig(self) -- check if next block is gravel, then carry out dig operation
	local lookForGravel = false
	local testDig = false

	self.success = false				  
	turtle.select(self.slotNo)

	--Compare next block with gravel (if already found)
	if slot:getItemSlot("gravel") > 0 then
		turtle.select(slot:getItemSlot("gravel"))
		lookForGravel = true
	end
	if self.direction == "up" then
		-- code here
	elseif self.direction == "down" then
		-- code here
	else
	if turtle.detect() then
   	 self.detect = true
		if turtle.compare() and lookForGravel then
			self.isGravel = true
		end
	end
	turtle.select(self.slotNo)
		if turtle.dig() then
			self.success = true
		end
	end
	return self.success --turtle.digX() succeeded but may not have increased items onboard.
end
Gravel is a unique block. When dug, it will either return gravel, or flint. There is no apparent method to this madness, but you have to deal with it. Luckily gravel in the turtle slot does compare with gravel in the world, so once identified it can be recognised before digging it:


if slot:getItemSlot("gravel") > 0 then
	   turtle.select(slot:getItemSlot("gravel"))
	   lookForGravel = true
end
You will notice in this block the use of the slot object we mentioned in Part 1 of this tutorial:

slot:getItemSlot("gravel")
This returns either 0 if gravel has not yet been identified, or the slot number it is held in. If found the slot is selected and a turtle.compare() method used later in the code. At this stage a flag is set as a reminder:

lookForGravel = true
The next block of code is dependant on the argument direction. By default direction = “forward”, so the next bit of code used by our instruction:

dig.digNew{self = dig, expectedItem = "wood", callFrom = "firstTree"}
is:


else
	if turtle.detect() then
		self.detect = true
		if turtle.compare() and lookForGravel then
			self.isGravel = true
		end
	end
	turtle.select(self.slotNo)
	if turtle.dig() then
		self.success = true
	end
end

As we have a tree in front, then turtle.detect() will be true.
As we have not yet discovered gravel, isGravel remains at default false
self.slotNo is the default value of 1, so this slot is selected ready to dig

And finally….
After all this time…
We have:

turtle,dig()

See. I told you we would get to it eventually.

You will note it is called within an if end block. This will give a return value of true if something is actually dug, otherwise false. So if you dig in the air, you get false. If you dig a tree you get true

To help identify dug items


grass: turtle.detect() = false, turtle.dig() = true, sometimes if seeds returned
leaves:	   turtle.detect() = true, turtle.dig() = true, nothing returned
water: turtle.detect() = false, turtle.dig() = true, nothing returned
lava:  turtle.detect() = false, turtle.dig() = true, nothing returned
bedrock: turtle.detect() = true, turtle.dig() = false, nothing returned
In the case of our tree digging, self.success = true

We now leave the doDig() function and return to where we left off in the digNew() function


if self.success then --process dig/suck
	clsDig.setDugSlotAndItem(self)
	-- other code edited out
end
Let's take a look at clsDig.setDugSlotAndItem(self)


function clsDig.setDugSlotAndItem(self) -- set self.dugSlot. 0 = no dig
	local isWater = true
	local success = false
	local testDig = false
	local emptySlot = 0
	local doContinue = true
			
	self.dugSlot = 0
	for i = 1, 16 do --find slot item dug into (if any)
		if turtle.getItemCount(i) > self.itemCount[i] then
			if self.dugSlot == 0 then -- default value
				self.dugSlot = i --first slot with additional items in it
				self.dugAmount = turtle.getItemCount(i) - self.itemCount[i]
			else
				self.dugSlot2 = i --another slot with additional items
			end
		end
	end
	-- other code edited out
end
This block compares the amount of items in each slot if the dig was successful, and sets the self.dugSlot to the correct value. In the odd case where more than one slot has been affected, such as when digging redstone, 8 redstone are dug, and the slot already has 60 in it, then a second slot will be used. This is stored in self.dugSlot2

The next block checks for expectedItem


if self.detect then --block detected before dig
	if self.dugSlot == 0 then --nothing dug, leaves, grass
		   self.dugItem = "leaves"
	else
		if self.dugAmount > 2 then
			--code here
		else -- dugAmount = 1
			if self.dugSlot2 > 0 then
				--code here
			end
			if self.expectedItem ~= "" then --allocate dugItem to expectedItem
				if clsDig.checkExpectedItem(self) then-- check expected item
					self.itemKnown = true
					self.updateType = "newItem"
				end
			else
				-- code here
			end
			success = true -- item dug
		end
	end
end
The line if self.expectedItem ~= "" then will not be an empty string, we set it to "wood", so it will call

clsDig.checkExpectedItem(self)


function clsDig.checkExpectedItem(self)
	local success = false
	self.itemCount[self.dugSlot] = turtle.getItemCount(self.dugSlot) -- 1 (wood)
	self.dugItem = slot:getSlotContains(self.dugSlot) -- "" (empty string)
	if turtle.getItemCount(self.dugSlot) == 1 then -- 1 item dug into new slot.
		if self.expectedItem ~= "" then -- expected item is "wood"
			self.dugItem = self.expectedItem -- self.dugItem = "wood"
			success = true
		end
	end
	return success
end

To recap, the turtle.dig() put a block of "wood" into slot 1, and the dig object now has
self.dugSlot = 1
self.dugAmount = 1
self.dugItem = "wood"

Control now returns back to where we left off in digNew()


if self.dugSlot > 0 then
	clsDig.confirmGravel(self) -- checks for gravel, flint, only if self.isGravel
	if self.dugItem == "flint" then -- item identified by confirmGravel only
		-- code here
	else --self.dugItem = itemX, or known item. expectedItem already set
		if string.find(self.dugItem, "item") then
			--code here
		else
			--item already known, update quantity
			slot.update{self = slot, slotNo = self.dugSlot, item = self.dugItem}
		end
	end
end
The final step is to use the slot object again, to update the status of slot 1


slot.update{self = slot, slotNo = self.dugSlot, item = self.dugItem}

This translates to

slot.update{self = slot, slotNo = 1, item = "wood"}

I can see the eyes are glazing over, so one final look into the slot object's update method and we are done for now.


function clsSlot.update(arg) -- update turtle slot with item contents
	local quantity = 0
	local self = arg.self
	local tempItem = ""
	local tempSlotNo = arg.slotNo
	local count = 0
	--code here to check arguments
	if arg.newItem ~= nil then -- naming unknown. delete is assumed, arg.slotNo, arg.item calculated above
		-- code here to change e.g. "item1" to "coal"
	else --not changing an item, just update
		quantity = turtle.getItemCount(arg.slotNo)
		if quantity == 0 and arg.delete then --turtle slot empty - remove all references
			self.slotContains[arg.slotNo] = ""
			self.itemTested[arg.slotNo] = ""
			self.slotCount[arg.slotNo] = 0
			self.slotStatus[arg.slotNo] = "empty"
			saveToLog("	slot.update: Deleting "..arg.item.." Slot "..arg.slotNo.." is empty", false, true)
		else --create or maintain selected slot, eg when wood2 detected and about to be harvested
			self.slotCount[arg.slotNo] = quantity
			self.slotContains[arg.slotNo] = arg.item
			if string.find(arg.item, "item") ~= nil then --"item1", item2 etc
				self.slotStatus[arg.slotNo] = "unknown"
			else
				self.slotStatus[arg.slotNo] = "known"
				objectives[arg.item] = true
			end
			saveToLog("	slot.update: Slot "..arg.slotNo.." contains "..self.slotCount[arg.slotNo].." "..arg.item.." status = "..self.slotStatus[arg.slotNo], false)
		end
	end
end
The slot object now knows that slot 1 has 1 wood in it

We are back to the firstTree function and are now ready to eat the log we just dug, convert it to fuel and then we can carry on harvesting the tree. So we are looking for


turtle.refuel()

Right?

Er…Well…

Not yet. 1 log only gives 15 fuel, but if we craft it to 4 planks we get 60 fuel. It's a no-brainer


--craft to 4 planks in slot 2
saveToLog("firstTree: Crafting planks for fuel", true)
craft{craftItem = "planks", craftQuantity = 4, sourceItem1 = "wood", destSlot = 2, chest = "doNotUse", doSort = false}
Here we go again, one of those new-fangled named argument thingies. Have a look at the craft() function if you like, its only 649 lines….

Skipping over that for the time being, we can finally fuel our turtle


turtle.select(slot:getItemSlot("planks"))
turtle.refuel()
slot.update{self = slot, item = "planks", delete = true}
Don't forget to update the slot object, as you have just emptied out the slot, and planks don't exist any more

There we go folks, 2 tutorials, the use of around 3000 lines of code, and all we have done is


turtle.dig()
turtle.refuel()
Nothing like keeping things simple.

For part 3 (? final part?) I will show the methods used to identify the blocks as the turtle progresses through the world. I do not think it would be very helpful to explain all the functions used throughout the whole script, as it is fairly well commented, but if anybody needs an explanation of a particular area, let me know.
Konlab #6
Posted 16 May 2014 - 07:06 PM
Self replacing turtle?
Huh?
apemanzilla #7
Posted 16 May 2014 - 07:51 PM
Self replacing turtle?
Huh?
Self-replicating turtle. Basically the companion program to this thread allows you to set a crafty mining turtle at the base of a tree in a forest and it will mine, work, and make more turtles.
Inksaver #8
Posted 16 May 2014 - 08:09 PM
Original Post edited with font size 18 and bold, to emphasise:

This tutorial is a companion to the post about making a self-replicating turtle posted here:
http://www.computerc...rtle-revisited/
apemanzilla #9
Posted 16 May 2014 - 08:21 PM
Original Post edited with font size 18 and bold, to emphasise:

This tutorial is a companion to the post about making a self-replicating turtle posted here:
http://www.computerc...rtle-revisited/
Thanks :)/>

You should also add an index/TOC near the top, linking to each post. (Click the number in the top right section of each post for a link to it)
Edited on 16 May 2014 - 06:22 PM
Inksaver #10
Posted 16 May 2014 - 08:32 PM
Thanks apemanzilla

I have used that to make a direct link
Is there any way to neaten the link so it appears as here for example. Some forums allow the use of BB code to do this
apemanzilla #11
Posted 16 May 2014 - 08:34 PM
Thanks apemanzilla

I have used that to make a direct link
Is there any way to neaten the link so it appears as here for example. Some forums allow the use of BB code to do this
Yes, with BBCode. In the top corner of the editor (one of them) press the "switch" button (looks like a light switch) to switch from the rich editor to the BBCode editor. I'm assuming you already know how to use BBCode.
Inksaver #12
Posted 16 May 2014 - 08:48 PM
Sorted!

Thanks I did not see that option before
apemanzilla #13
Posted 16 May 2014 - 08:50 PM
Sorted!

Thanks I did not see that option before
No problem!

(Psst - you said "here" twice at the top of the post :P/> )
Inksaver #14
Posted 20 May 2014 - 12:46 PM
Self-Replicating turtle Part 3 – Identifying blocks

This part of the tutorial shows the methods used to identify new blocks as they are mined.
The items needed to complete the mission are:
  • cobble >- make a furnace and smelt to stone for crafting computers and disk drive
  • sand >- smelt to glass for crafting glass panes for computers
  • wood >- planks to craft chests and crafting tables for the turtles plus fuel and charcoal
  • ironore >- smelt to iron for crafting turtles and a bucket to get lava for fuel
  • redstone >- for computers, floppy disk, disk drive
  • sugar cane >- craft to paper >- craft to floppy disk
In the process of getting all these items, the following are also needed
  • saplings >- to grow a tree farm
  • coal >- for fuel and/or torches
During the earlier stages, the turtle keeps the following in its inventory:

chests, wood, wood2, saplings, saplings2, dirt, cobble, stone, gravel, sand, coal, ironore

When harvesting trees, torches and signs are also onboard if there is enough space, which are used to mark the entrances and exits to mineshafts.

In order to know what is onboard all the time, it is essential to identify the blocks mined, and dispose of anything which is of no use, such as flowers, seeds, mushrooms, flint, clay and apples.
While mining, having cobble, stone, dirt, sand and gravel onboard allows for comparisons to be made and avoid wasting time digging non-valuable items.
  • wood
This one is easy. The turtle is in front of a tree, and the first block it digs is wood.
  • dirt
Also easy, on the firstTree() function, after cutting enough wood to fuel and make a chest, the turtle returns to ground and mines the dirt originally under the tree.
  • leaves, apples and saplings
As the turtle climbs up the tree, it also rotates and digs any leaves growing against the trunk, and continues going up until nothing is detected above. Digging leaves can result in one of three items:

	nothing:  dig:getDugSlot() = 0,  dig:getDugItem() = 0, dig:getSuccess() = 0
	apple:	 dig:getDugSlot() = 1,  dig:getDugItem() = “item1”, dig:getSuccess() = true
	sapling:  dig:getDugSlot() = 1,  dig:getDugItem() = “item1”, dig:getSuccess() = true
As soon as “item1” is discovered, it is tested by the following function

function clsDig.checkForItem(self)
	if self.dugAmount > 2 then
		-- can only be melon, clay, lapis, redstone as more than 1 drop
		-- code edited out
	else
		useItem = self.dugItem
		if self.callFrom == "reposition" then
			-- code edited out
		else --not called from reposition
			if string.find(self.checkForItems,"saplings") ~= nil then -- called from checkTree(), firstTree()
				turtle.select(self.dugSlot)
				--start at arbitrary level 0
				if self.callFrom == "firstTree" then
You can now see why the arguments are set in the line:

dig.digNew{self = dig, checkForItems = "saplings", callFrom = "firstTree"}
gets through the function clsDig.checkForItem() to the exact point where a specific test for saplings/apples is carried out after the line: if self.callFrom == "firstTree" then

The method for testing all remaining blocks, including saplings and apples is dependant on
  • The location of the block. Check the y coordinate of the turtle
  • The function calling .digNew() e.g. firstTree() would only expect wood, saplings and apples
  • Whether more than 1 block is available. Some tests need a minimum of 2 blocks
To check for saplings / apples the following pseudo code is applied:
  1. Place a dirt block
  2. Move up 1 block
  3. Try placing “item1” on the dirt block
  4. If .placeDown() = false then it must be apple, so dump it
  5. If .placeDown() = true then it must be sapling, so recover it.
  6. Retrieve the dirt block

The real code for this is:


down(1) --now at level -1
turtle.select(slot:getItemSlot("dirt"))
turtle.placeDown()
up(1) --now at level 0
turtle.select(self.dugSlot)
if turtle.placeDown() then --saplings/2 found
	turtle.digDown() --recover sapling
	self.dugItem = "saplings" --test for saplings
	success = true
else --apples so dump
	turtle.drop()
	saveToLog("dig.checkForItem: dumping ? apples from "..self.dugSlot, false)
	self.dugItem = ""
	self.dugSlot = 0
end
down(1) --now at level -1
turtle.select(1)
turtle.digDown() --recover dirt
up(1) --now at level 0
  • chests, planks, sticks and other crafted items
Any item crafted, such as chests, planks, sticks, signs, torches will automatically be known as they are successfully crafted. The only problem occurs if during mining, a disused mine is encountered, which could have wooden items, rails etc, or mossy cobblestone from a dungeon. As these are only encountered at later stages, code has been written to deal with these when encountered. We will return to this later
  • cobble
After firstTree() the items known are wood, dirt, crafted items, possibly saplings, apples. The next stage is to find cobble, as it is needed to craft a furnace. The findCobble() function does just that.
  1. Dig down 10 blocks, rotate and dig items in front as descending.
  2. Move forward three blocks
  3. Return to the surface with rotation, but stop 5 blocks below the starting level
  4. Move back to the original shaft
  5. Return to original level. This method causes minimal disturbance of the ground layout
  6. At this stage a guess can be made on which item is cobble, as it will probably be the item with most blocks collected. It is also possible that gravel, coal and ironore could have been encountered.
Back on the surface the function confirmCobble() attempts to craft a furnace.

Pseudo code:
  1. Loop through any blocks identified as “itemx” where x is a number 1 to 8, starting with the block with the highest itemCount
  2. Check if it will place(). If not move to the next “itemx”
  3. If place() = true then dig it up, provisionally identify it as “cobble” and attempt to craft a furnace
  4. If success crafting a furnace break the loop and return
  5. If not successful, rename back to “itemx”, move to the next “itemx”

Real code:


function confirmCobble()
	local foundCobble = false
	local useItem = ""
	sortInventory(true)
	if slot:getUnknownItemCount() > 0 then
		for i = slot:getMostUnknownItemSlot(), slot:getLeastUnknownItemSlot(), - 1 do
			turtle.select(i)
			if turtle.place() then --cobble, gravel, sand, iron ore, clay
				turtle.dig()
				if slot:getItemSlot("cobble") == 0 then
					useItem = slot:getSlotContains(i)
					slot.update{self = slot, slotNo = i, newItem = "cobble"}
					saveToLog("confirmCobble: attempting to craft furnace", true)
					if craft{craftItem = "furnaces", craftQuantity = 1, sourceItem1 = "cobble", destSlot = 0} then
						foundCobble = true
						break
					else
						slot.update{self = slot, slotNo = i, newItem = useItem}
					end
				end
			end
		end
	end
	return foundCobble
end
  • coal
Coal is fairly straightforward. All you need to do is wait until at least 2 items have been mined then use if turtle.refuel(1) then. If one item is used, and returns true, then coal is identified. Again the context is taken into account, so during findCobble() and goMining1() coal is specifically checked for.
  • Sand and gravel
These two really need to be checked together. they are both easy to identify using the following method:
  1. Try to place() or placeDown() after confirming there is no block below.
  2. If place() is successful, check if block is still present. If it has dropped, then it must be sand or gravel
  3. Repeatedly place() and dig(), up to 100x. If flint produced, break the loop and confirm gravel found, else sand found.
This procedure is carried out both as a separate function checkGravel(), and as a method in the dig() object: clsDig.checkSandGravel(self, useSlot, useItem)
  • melon and clay
These two items, along with lapis and redstone, give multiple drops when mined.
Detection above level 40 would not check for redstone or lapis, so if on the ground, or within around 20 blocks from the surface, multiple drops would have to be from either of these. As neither is required, they can be dumped and forgotten about straight away.
  • flowers , mushrooms and grass
Grass is not detected unless it contains seeds. When dug, the seeds are treated the same as flowers and mushrooms, with an attempt to placeUp().

If dug while on a harvestAllTrees() run, they are detected in front, the function checkSugarCane(returnToGround) is called, which calculates only one block rise is achieved, the item is dug, and if placeUp() does not succeed, it is dumped.

If mushrooms are collected during mining operations, they are tested after returning to base, and dumped
  • ironore
If dirt, cobble, stone, sand, gravel and coal have all been identified, ironore is given a provisional identification as "?ironore". When 5 have been collected, 4 are smelted to iron, and a bucket crafted. The bucket is used on deep mining to refuel from lava, the spare iron is stored. Again the depth the item was collected from helps with identification.
  • redstone and lapis
Both these items give multiple drops when mined, and will only be found below level 32. Redstone can be identified easily as it will not placeUp(), but if place() is successful, it will not compare() with redstone onboard. Once redstone has been identified, lapis can be automatically identified when mined due to its multiple drop. This is done in the dig() object:


if self.dugAmount > 2 and slot:getItemSlot("redstone") > 0 and slot:getItemSlot("coal") > 0 then
	self.oldItem = self.dugItem
	self.dugItem = "lapis"
	itemStatus = "newItem"
	success = true
else
  • sugar cane
Sugar cane is fragile. If the turtle digs at the base, it breaks up and scatters all over the place. For this reason, during the harvestAllTrees() function, where the turtle works over the landscape looking for wood and sugar cane, it uses a function called checkSugarCane() Whenever it detects a block in front of it, rather than dig it and see what it finds, it instead climbs vertically until there is no block in front, moves forward, then digs down.

To identify sugar cane, it can be placeDown() but does not compareDown(). This is similar to dirt > - grass block and cobble >- stone. As cobble/stone and dirt/grass are already known and checked it must be sugar cane.
  • diamonds
The means of identifying are:
  • depth they were mined
  • can they be placed()?
  • attempt to craft into a diamond pickaxe as a final test
  • goldore, obsidian, mossy cobblestone
These items are not required, so do not need to be identified, but functions have been written to do it anyway.

Goldore has one item smelted, crafted to 9 gold nuggets to confirm
Mossy cobblestone has 6 blocks crafted to mossy cobblestone walls to confirm
Obsidian is pretty much all that is left once everything else that will place() has been identified.

The function identifyItems() has the following tasks:
  1. Check for fences, planks and other combustible items by using turtle.refuel(1)
  2. Check for redstone if not yet identified
  3. Call checkMetal() to test for goldore and ironore if not yet identified
  4. Check for mossy cobblestone
  5. Check for diamonds


No specific method for identifying emerald has been written, but as they are so rare, this should not be a problem