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

Turtle Intermediate 3

Started by theoriginalbit, 04 September 2013 - 06:34 AM
theoriginalbit #1
Posted 04 September 2013 - 08:34 AM
This is a tutorial in the Ask a Pro Renewal Project series.

Pre-requisite tutorials:
Turtle Basics
— Turtle Intermediate 1

Preface

As you may have discovered so far, the default Turtle movement functions and mining don't always work too well, and there are many things that can effect how your Turtle operates, including, but not limited to, causing movements counts to be off. So in this tutorial we will cover the areas that cause these problems and how to fix them. The areas we will cover are; Players and mobs, running out of fuel, falling blocks, and detecting bedrock.

This tutorial will be entirely text-based, it is suggested however that you setup a Turtle to test the various pieces of code as we go along. Lets begin.


Step One

The first thing we should do to make a fail-proof movement function is to create a new function that we will call each time we wish to move the Turtle, this function will be the backbone of each section below and all sections will be combined together in the final section. Lets create the basic function now


local function moveForward()
  while not turtle.forward() do
	print("Failed to move.")
  end
end

In the code above we have defined a new function called moveForward this is what you would invoke each time we wish to move the Turtle forwards. We then start a loop which invokes the turtle.forward function, as we know from the Turtle Basics tutorial, this function will return a boolean based on whether it has moved. We then check to see if it didn't move with not and the result of the movement (for an explanation as to how the not keyword works, see "Boolean Algebra/Logic" in this thread). In the event the Turtle hasn't moved it will print that it failed to move, this is the line where we will be placing our code in the later sections. If the Turtle does move then the loop exits without running anything in the loop.


Blocks & Falling Blocks

A common reason that a Turtle is not able to move is a block is in its way. Whether it be that the Turtle hasn't dug the block in its way yet, or if it was a falling block that has caused it to no be able to move. Either way we can use the knowledge gained from Turtle Intermediate 1, and knowledge of Minecraft, to detect these blocks and be able to handle falling blocks. As we learnt in Turtle Intermediate 1 the turtle.detect function returns a boolean based on whether there is a block in front of it or not. As such we are able to dig this block when it is detected, like so


local function moveForward()
  while not turtle.forward() do
	if turtle.detect() then
	  turtle.dig()
	end
  end
end

Now we have a simple detection system that if we cannot move, checks to see if there is a block there and digs the block. However since blocks such as sand and gravel fall, this is not a 100% ideal solution, the loop will be running more often than it actually needs to. Via knowledge of Minecraft we know that a block of sand or gravel takes 0.8 seconds to fall, as such to make this loop run less often we can insert a sleep into the code like so


local function moveForward()
  while not turtle.forward() do
	if turtle.detect() then
	  turtle.dig()
	  sleep(0.8)
	end
  end
end

The above function will no longer run as often as possible, but the next problem that occurs is that it will wait 0.8 seconds with every block it digs, which means that mining has suddenly just got a lot slower. So we dive into our knowledge of Minecraft again and remember that the sand falling is actually just an animation, just like the Turtle moving, it is actually already occupying the new block. So knowing this we can make the following modification.


local function moveForward()
  while not turtle.forward() do
	if turtle.detect() then
	  turtle.dig()
	  if turtle.detect() then
		sleep(0.8)
	  end
	end
  end
end

Now the function is working a lot nicer. To follow through what it is doing again: if the Turtle cannot move, it first checks if a block was the cause, if a block was the reason it could not move then it will dig the block. It then checks to see if there is another block in that location, if there isn't it will exit the loop (because turtle.forward will return true) and move on. However if there is another block in it's place we know that we cannot mine it for another 0.8 seconds, so the Turtle will wait for 0.8 seconds and then loop, testing all these conditions again removing all the falling sand or gravel until it can move.


Out Of Fuel

Another common reason for a Turtle failing to move is that it is out of fuel. As such our movement functions must be aware of fuel levels and then deal with it accordingly. So if a Turtle cannot move this means that the fuel level has reached zero (0) so lets check for that now


local function moveForward()
  while not turtle.forward() do
	if turtle.getFuelLevel() == 0 then
	  --# refuel accordingly
	end
  end
end

So here we are getting the fuel level and if it is equal to zero (0) we would do something appropriately. What happens when it runs out of fuel is up to you, you could have it search it's inventory for fuel, you could have it send a message to a player via Rednet (assuming you have a modem), you could just have it sit there and wait for a player to come and give it more fuel, the options are vast and it really comes down to however you want to do it.

The above function works nicely, however there is one problem with it, and that is when we come to someone having disabled fuel requirements, since "unlimited" does not equal to zero (0) our Turtle would error when attempting to refuel. So we can make the following adjustment to our if statement to allow for people having turned off fuel usage.


local function moveForward()
  while not turtle.forward() do
	if turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() == 0 then
	  --# refuel accordingly
	end
  end
end

Now the Turtle will be equipped for when it cannot move and the player has disabled the need for it to have fuel.


Players & Mobs

One major problem that you can encounter when dealing with mining programs is mobs or players. For example it is quite often for a mob to spawn at the bottom of a quarry that your Turtle is mining, or for a player to be walking along the tunnel the Turtle is digging. As such we need to add a way for dealing with these problems when they occur.

As we learnt in Turtle Basics the turtle.attack function returns a boolean based on whether it was able to attack something in front of it, as such we can use this function call as a player/mob detection method.


local function moveForward()
  while not turtle.forward() do
	if turtle.attack() then
	  print("A mob was the cause")
	end
  end
end

Assume that a Zombie gets in the way of the Turtle, the Turtle is unable to move until the mob is out of the way. As such the code will loop each time it cannot move, attacking the Zombie in front of it. Assuming that the Zombie had no knock-back when hit by the Turtle it would take 3 loops for the Turtle to be able to move. However since a little bit of knock-back does occur when a mob or player is hit by a Turtle it can make the mob hop out of the way or act as a warning for the player.


Bedrock

If you are doing vertical mining, or you have your Turtle mining down at bedrock levels, it is essential that you program into your Turtle a method of detecting bedrock. Now if you had a GPS network and you were doing vertical mining, this could easily be achieved via knowing the Turtle's y position, however lets assume we do not have a system such as this setup. Bedrock is the only block in Vanilla Minecraft that a Turtle cannot dig, as such we can combine the knowledge of knowing a block exists in our way, and not being able to mine it, to know that it is bedrock.


local function moveForward()
  while not turtle.forward() do
	if turtle.detect() then
	  if not turtle.dig() then
		print("Bedrock has been encountered")
		--# deal with it appropriately
	  end
  end
end

In the above example we detect that a block is in the Turtle's way, so we attempt to mine it, if the block cannot be mined then it was bedrock. Now depending on the type of program you have made you would deal with encountering bedrock differently, whether it be to turn around and go to the home location, or whether it attempt to go around the bedrock, that is up to you.


Stopping a Turtle from Mining Another Turtle

Prerequisites:
Peripheral Basics

SpoilerSometimes it we wish to have multiple Turtles performing a task in an area, however it's not fun when one (1) Turtle destroys another which performing it's task. Via knowledge we have gained in Peripheral Basics we can create a function that checks whether any ComputerCraft block, except Network Cables, is in the way of the Turtle like so.


local function moveForward()
  while not turtle.forward() do
	if peripheral.getType("front") then
	  print("Something ComputerCraft is in my way")
	end
  end
end

Now we can expand this by checking if it is a Turtle, if it is we can just have our Turtle wait 0.8 seconds (as we discovered before this is the time it takes for a block in Minecraft to move) and then attempt to move again.


local function moveForward()
  local waited = 0
  while not turtle.forward() do
	local periphType = peripheral.getType("front")
	if periphType == "turtle" then
	  if waited > 3 then
		print("This Turtle isn't moving, we must be in a head-on collision")
	  end
	  print("A Turtle is in my way, waiting for it to move")
	  sleep(0.8)
	  waited = waited + 1
	elseif periphType then
	  print("Something ComputerCraft is in my way, and it cannot move out of my way")
	end
  end
end

In the above code we use the peripheral.getType function to check if it is something ComputerCraft in our way. We then check if it is a Turtle, if it is we increment a waiting counter and wait 0.8 seconds for it to move out of the way. The purpose of the waiting counter is to check how many loops have happened where we have been waiting for the Turtle to move, if the counter is above 3 then chances are we have reached the flaw to this system where the Turtles are "staring" at each other, in this case we must devise a way, just like with all other ComputerCraft blocks, on how to avoid them without destroying them.

I would suggest that the best way for Turtles is that they each turn left, and go around as opposed to going up to go around, as if we tell the Turtles to move up, both will be moving up and never avoiding each other. Whereas telling them to turn left, they both turn the opposite direction to each other.


All Together Now

Here is our function from all of these sections brought together into one code block. (as the section above requires a pre-requsite it has been excluded from this complete solution)


local function moveForward()
  while not turtle.forward() do
	--# check if a block was the cause
	if turtle.detect() then
	  if not turtle.dig() then
	   print("An unbreakable block has been encountered, bedrock is that you?")
	  elseif turtle.detect() then
		print("Sand/Gravel is falling!")
		sleep(0.8)
	  else
		print("A block was in my way")
	  end
	--# check if fuel was the cause
	elseif turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() == 0 then
	  print("I am out of fuel... HELP!")
	elseif turtle.attack() then
	  print("Get out of my way mob/player!")
	end
  end
end

The above function is now able to detect any condition as to why Turtle cannot move and is able to deal with appropriately.


Conclusion

In this tutorial we have learnt how to create a fail-proof function to move the Turtle, allowing for things such as running out of fuel, players or mobs getting in the way and falling blocks.

Thank you for joining me in this tutorial, thanks for reading, happy coding, and see you next time!
Lyqyd #2
Posted 04 September 2013 - 01:01 PM
This is a good start, but I think it would be better to evolve our function throughout to expand its capabilities. This approach allows for explanation of why the different pieces are added together in the way they are. Building onto the function seems like a much more natural progression than resetting the function each time. It also means that users following along in-game aren't deleting code only to re-add it at the end.

There are a few minor typological errors that I'll probably go through and fix if I get bored today. No big deal there.