Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Note: This is basically finished. I may tweak it here or there, which is why I reccomend using my program
- -- That I wrote to automatically update this. (See the bottom of this verbose description)
- -- This is an algorithm to automatically breed seeds in Minecraft's Agricraft Modpack,
- -- which have 3 integer values (x, y, z) as a tuple.
- -- I want to write a turtle AI to be "efficient enough" in breeding my Agricraft seeds so I can give it a 1/1/1 seed,
- -- and come back later and eventually, have 1 or more 10/10/10 seeds of that type.
- -- It will NOT actually breed new seed types; that's up to the players to create and provide for the turtle bot, but
- -- Eventually, once it's complete, this turtlebot AI should eventually be able to, given 2 seeds of any value,
- -- Breed them into a 10/10/10 seed.
- -- I started this algorithm on November 15, 2016, and pretty much finished it by November 20th, with a few tweaks since then.
- -- 04.28.2017 - Now saves settings into a file MatthewC/Agricraft/Data/SeedBreederSettings; you can now override the default settings by changing values within this file. This way, if emerald or lapis are too expensive, you can replace with cheaper (but unique!) materials.
- -- This algorithm was downloaded from: http://pastebin.com/V1MQRVQv
- -- A more visual example of the setup expected for the turtle is located: http://imgur.com/gallery/gPpK4
- -- The program to automatically download and run this file is located: http://pastebin.com/Vuxsn2mX
- -- A youtube video explaining this and demonstrating it in action is located: https://www.youtube.com/watch?v=-dN5wSv5SsY
- --
- --
- --
- -- Note: THANKS to AndyWilliams757 (Minecraft tag), I was able to test a few cases for the turtle, and improbe it a bit.
- -- Now, the turtle will query the player to give it fuel if it starts running without any fuel.
- -- Additionally, seeds can now be retrieved case - insensitive, so if a seed is called "Seed" instead of "seed",
- -- or "SeEd", or other mixed-cases, the turtle will handle that crap.
- --
- -- ********************************WARNING*******************************************
- -- PLANTS WHICH PRODUCE ITEMS WITH THE NAME "SEED" IN THEM CAN BREAK THE TURTLE. I PLAN ON EVENTUALLY CORRECTING THIS,
- -- BUT HAVEN'T GOTTEN AROUND TO IT YET>
- -- ITEMS KNOWN TO CAUSE PROBLEMS: "Mustard Seed",
- --------------------------------------------------------------------------------------------------
- -- Settings
- --------------------------------------------------------------------------------------------------
- local numberOfPerfectSeedsToBreed = 30 -- Stores the total number of perfect seeds to breed.
- local blockNameBorderRight = "minecraft:iron_block" -- The name of the block on the right side of the setup.
- local blockNameBorderLeft = "minecraft:gold_block" -- The name of the block on the left side of the setup.
- local blockNameBorderFront = "minecraft:netherrack" -- The name of the block on the forward side of the setup.
- local blockNameBorderBack = "minecraft:nether_brick" -- the name of the block on the back side of the setup.
- local blockNameSeedAnalyzer = "AgriCraft:peripheral" -- The name of the Seed Analyzer
- local itemNameCropSticks = "AgriCraft:cropsItem" -- The name of the crop sticks
- local itemNameFuel = "minecraft:coal" -- The name of the fuel the turtle will be using (Can only accept 1 type of fuel.)
- local itemNameBoneMeal = "minecraft:dye" -- The name of bonemeal. (really, the fertilizer, if the turtle can use it... I'm just only familiar with bonemeal...) Note: This is odd, because the turtle detect's the name as "minecraft:dye:"...
- local itemMetaDataBoneMeal = 15 -- This is the metadata value associated with bonemeal. This is necessary because bone meal registers as dye, so I use metadata to differentiate between bonemeal and other dyes.
- local frontCompassDirection = "NORTH" -- The direction of the front of the workspace. Valid Values are: "SOUTH", "NORTH", "EAST", "WEST".
- local useBoneMealOnCenter = false -- Whether to use bone meal on center cross crop.
- --------------------------------------------------------------------------------------------------
- -- Variables
- --------------------------------------------------------------------------------------------------
- local seed1 = {X = 1, Y = 1, Z = 1, Fitness = nil} -- Stores the Left seed or "parent"
- local seed2 = {X = 1, Y = 1, Z = 1, Fitness = nil} -- Stores the Right seed or "parent"
- local generation = 1 -- Stores the number of generations it took to get to our current set of parents.
- local leftSeedName = "" -- Stores the name of the seed type we are currently breeding.
- local rightSeedName = "" -- Stores the name of the right seed type we are currently breeding.
- local childSeedName = "" -- Stores the name of the child seed type we are currently breeding.
- local perfectFitnessValue = nil -- Stores perfect Fitness value that is attainable. calulated in post-initialization.
- local currentNumberOfPerfectSeedsBred = 0 -- Stores the number of seeds we have bred so far in our current work.
- local xPosition = 1 -- 1 = far left, 2 = 2 in, 3 = 3 middle, 4 = 4 in, 5 = far right
- local zPosition = 1 -- 1 = far back, 2 = 2 in, 3 = 2 in, 4 = far front
- local cropStickPosition = nil -- Stores the position of crop sticks in the inventory. If nil, we need to scan for them.
- local boneMealPosition = nil -- Stores the position of bonemeal in inventory. If nil, we need to scan for them.
- local loadedData = false -- True if we have loaded data and are resuming our old state, false if not.
- local hasFailedOnRetrievingBoneMealLastTime = false -- If this is true, we won't bother trying to go stock up on boneMeal unless we're forced to.
- --------------------------------------------------------------------------------------------------
- -- Function Definitions
- --------------------------------------------------------------------------------------------------
- -- Provided a number array, returns the minimum value in the array.
- -- Inputs:
- -- int[] numArray : The number array / table to retrieve the minimum value of.
- -- Outputs:
- -- int minVal : The value of the minimum value within the provided array.
- local GetMinValueFromNumberArray = nil
- -- Calculates the fitness of a provided seed.
- -- Note: I tested this fitness value with an algorithm that predicts a potential offspring of the parent-seeds,
- -- And determined it to be the best of 5 other algorithms, with an average Total Generation count of 72.29.
- -- This calculation was based on a breeding algorithm which averages the X,Y, and Z scores of two parent seeds to produce
- -- An offspring, with a 40% chance to increment each individual stat of the seed.
- -- (10/10/10 + 1/1/1 = 5/5/5 with a 40% chance of Any individual stat being a 6 instead.)
- -- Inputs:
- -- Table { int X, int Y, int Z, int Fitness } : The table representing a singular seed.
- -- Outputs:
- -- int fitness : The fitness for the seed; 4 is the worst possible, 40 is the best possible.
- local GetSeedFitness = nil
- -- Returns the index of the item in the inventory by the name provided. Returns nil if it can't be found.
- -- Note: If you are searching for a nil name, will return the first nil position.
- -- Inputs:
- -- string name : The name of the seed to search for. Performs a string.match, so partial names are counted.
- -- bool caseInsensitive : Whether to perform the search with a case-sensitive or case-insensitive search.
- -- Outputs:
- -- int inventoryLocation : The inventory location of the item, or nil if it doesn't exist in the inventory.
- local ScanForItemInInventoryByName = nil
- -- Purges the inventory of items deemed unecessary (seeds and such)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local PurgeInventoryOfExtraneousItems = nil
- -- Tries to reset the Turtle Bot to the center (top level, middle horizontally) position.
- -- Attempts this based off of various Blocks (bottom / top blocks)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local ResetLocationToCenter = nil
- -- Send the turtle to the specified location.
- -- xPos : 1 = far left, 2 = 1 in, 3 = middle, 4 = 3 in, 5 = far right
- -- zPos : 1 = far back, 2 = 1 forward, 3 = 2 forward, 4 = far front
- -- Inputs:
- -- int xPos : The X-position to travel to.
- -- int yPos : The Y-position to travel to.
- -- Outputs:
- -- N/A
- local TravelToPosition = nil
- -- Throws an item away at the index provided (1 - 16 inclusive)
- -- Inputs:
- -- int index : The index of the stack to throw away.
- -- Outputs:
- -- N/A
- local ThrowStackAway = nil
- -- Equips Crop Sticks in the inventory.
- -- If we are out, we will pick them up from the crop stick input station, and then return to our original location.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local EquipCropSticks = nil
- -- Equips bonemeal
- -- Inputs:
- -- bool allowRefill : True if you want to allow the turtle to refill if it's out, false if you don't.
- -- Outputs:
- -- N/A
- local EquipBoneMeal = nil
- -- If the turtle is below 10% fuel, it needs to go to the chest to refuel.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local HandleFuel = nil
- -- Stores the item in the noted index at the storage location.
- -- Inputs:
- -- int index : The index of the stack to save.
- -- Outputs:
- -- N/A
- local SaveStack = nil
- -- Create a redstone impulse at .9 seconds on the bottom of the turtle.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local redstoneImpulseOnBottom = nil
- -- Sucks up a new seed.
- -- Inputs:
- -- int numOfSeedsToSuckUp : The number of seeds to suck up.
- -- Outputs:
- -- boolean true if it sucked up 2 seeds, 1 if it sucked up 1 seed.
- local SuckUpNewSeed = nil
- -- Plant seed on the left side.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local PlantSeedOnLeft = nil
- -- Plant seed on right side.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local PlantSeedOnRight = nil
- -- Employs the Computer Controlled Seed Analyzer at the current location to
- -- Analyze the current seed, and return a table for the seed's properties.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- Table { int X, int Y, int Z, int Fitness }
- local AnalyzeSeedAtCurrentLocation = nil
- -- If it is determined that the state of the board no longer matches what we expect,
- -- let's reset our save data.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local ResetSaveData = nil
- -- Prepares for planting, if this station isn't already prepared.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local PrepareForPlanting = nil
- -- Has the turtle wait until it can detect that an offspring has been produced.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local WaitForOffspring = nil
- -- Function to eliminate parent 1, and replace with the current offspring.
- -- Inputs:
- -- Table offspring { int X, int Y, int Z, int Fitness }
- -- Outputs:
- -- N/A
- local PlantOffSpringOnSeed1 = nil
- -- Function to eliminate parent 2, and replace with the current offspring.
- -- Inputs:
- -- Table offspring { int X, int Y, int Z, int Fitness }
- -- Outputs:
- -- N/A
- local PlantOffSpringOnSeed2 = nil
- -- Function to reject our current offspring (Probably throw it away in a trash can or something...)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local RejectOffspring = nil
- -- Function to save our offspring (Probably put into a chest or something...)
- -- Call this if the offspring, as well as both parents, are 10/10/10 seeds.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local SaveOffspring = nil
- -- Function to analyze the offspring seed, and determine if it should replace either parent or be rejected.
- -- At this point, the seed has already been harvested/analyzed,
- -- and we just need to determine whether we reject it, or replace one of our parents with it.
- -- Inputs:
- -- Table offspring { int X, int Y, int Z, int Fitness }
- -- Outputs:
- -- N/A
- local HarvestedOffspring = nil
- -- Function to harvest the current offspring.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local HarvestOffspring = nil
- -- Function to clean up after the turtle has finished.
- -- (Basically just reinitializes the data.)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local CleanUp = nil
- -- Loads pertinent Data.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local Load = nil
- -- Saves the pertinent Data.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local Save = nil
- -- Main program entry point.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- local MainExecutionLoop = nil
- --------------------------------------------------------------------------------------------------
- -- Functions
- --------------------------------------------------------------------------------------------------
- -- Provided a number array, returns the minimum value in the array.
- -- Inputs:
- -- int[] numArray : The number array / table to retrieve the minimum value of.
- -- Outputs:
- -- int minVal : The value of the minimum value within the provided array.
- GetMinValueFromNumberArray =
- function(array)
- local min = 999999
- for i = 1, #array, 1 do
- if array[i] < min then
- min = array[i]
- end
- end
- return min
- end
- -- Calculates the fitness of a provided seed.
- -- Note: I tested this fitness value with an algorithm that predicts a potential offspring of the parent-seeds,
- -- And determined it to be the best of 5 other algorithms, with an average Total Generation count of 72.29.
- -- This calculation was based on a breeding algorithm which averages the X,Y, and Z scores of two parent seeds to produce
- -- An offspring, with a 40% chance to increment each individual stat of the seed.
- -- (10/10/10 + 1/1/1 = 5/5/5 with a 40% chance of Any individual stat being a 6 instead.)
- -- Inputs:
- -- Table{ int X, int Y, int Z, int Fitness } : The table representing a singular seed.
- -- Outputs:
- -- int fitness : The fitness for the seed; 4 is the worst possible, 40 is the best possible.
- GetSeedFitness =
- function(inputSeed)
- local fitness = inputSeed["X"] + inputSeed["Y"] + inputSeed["Z"]
- + GetMinValueFromNumberArray({inputSeed["X"], inputSeed["Y"], inputSeed["Z"]})
- inputSeed["Fitness"] = fitness
- return fitness
- end
- -- Returns the index of the item in the inventory by the name provided. Returns nil if it can't be found.
- -- Note: If you are searching for a nil name, will return the first nil position.
- -- Inputs:
- -- string name : The name of the seed to search for. Performs a string.match, so partial names are counted.
- -- bool caseInsensitive : Whether to perform the search with a case-sensitive or case-insensitive search.
- -- Outputs:
- -- int inventoryLocation : The inventory location of the item, or nil if it doesn't exist in the inventory.
- ScanForItemInInventoryByName =
- function(name, caseInsensitive)
- for i = 1, 16, 1 do
- local item = turtle.getItemDetail(i)
- if item then
- local itemName = item.name
- if caseInsensitive then
- itemName = string.lower(itemName)
- name = string.lower(name)
- end
- if name and string.match(itemName, name) and (name ~= itemNameBoneMeal or (name == itemNameBoneMeal and item.damage == itemMetaDataBoneMeal)) then
- return i
- end
- end
- if item == nil and name == nil then
- return i
- end
- end
- return nil
- end
- -- Purges the inventory of items deemed unecessary (seeds and such)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- PurgeInventoryOfExtraneousItems =
- function()
- local archivedXPos = xPosition
- local archivedZPos = zPosition
- for i = 1, 16 do
- local item = turtle.getItemDetail(i)
- if item then
- if item.name == itemNameCropSticks then
- elseif item.name == itemNameBoneMeal and item.damage == itemMetaDataBoneMeal then
- else
- TravelToPosition(5, 2)
- ThrowStackAway(i)
- end
- end
- end
- TravelToPosition(archivedXPos, archivedZPos)
- end
- -- Tries to reset the Turtle Bot to the center (top level, middle horizontally) position.
- -- Attempts this based off of various Blocks (bottom / top blocks)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- ResetLocationToCenter =
- function()
- print("Resetting Location To Center")
- -- Move forward 4 times to guarantee we are facing a wall of the border.
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- local success, item = turtle.inspect()
- if item.name == blockNameBorderRight then -- We are on the far right side facing the border.
- -- Turn right, head all the way to the back, and then turn right, recenter, turn right.
- turtle.turnRight()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.turnRight()
- turtle.forward()
- turtle.forward()
- turtle.turnRight()
- xPosition = 3
- zPosition = 1
- elseif item.name == blockNameBorderFront then -- We are at far front side facing the border
- -- Turn right, head all the way to the right, and then go back, back, left, back, back, back, back
- turtle.turnRight()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.back()
- turtle.back()
- turtle.turnLeft()
- turtle.back()
- turtle.back()
- turtle.back()
- xPosition = 3
- yPosition = 1
- elseif item.name == blockNameBorderLeft then -- We are at far left side facing the border
- -- Turn Left, go all the way forward (towards the back), turn right, go back 2
- turtle.turnLeft()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.turnRight()
- turtle.back()
- turtle.back()
- turtle.turnRight()
- xPosition = 3
- yPosition = 1
- elseif item.name == blockNameBorderBack then -- We are that the far back facing the border
- -- Turn Left, goo all the way to the end, go back twice, turn right
- turtle.turnRight()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.forward()
- turtle.back()
- turtle.back()
- turtle.turnRight()
- xPosition = 3
- yPosition = 1
- else
- print("\r\n\r\nUnable to find position based off of block in front by name of: "..item.name.."\r\n\r\n")
- end
- end
- -- Send the turtle to the specified location.
- -- xPos : 1 = far left, 2 = 1 in, 3 = middle, 4 = 3 in, 5 = far right
- -- zPos : 1 = far back, 2 = 1 forward, 3 = 2 forward, 4 = far front
- -- Inputs:
- -- int xPos : The X-position to travel to.
- -- int yPos : The Y-position to travel to.
- -- Outputs:
- -- N/A
- TravelToPosition =
- function(xPos, zPos)
- -- Manipulate xPos and zPos to guarantee we end up somewhere in the field (even if not where expected)
- if xPos < 0 then xPos = -xPos end
- if zPos < 0 then zPos = -zPos end
- xPos = xPos % 6
- zPos = zPos % 5
- if xPos ~= xPosition then
- if xPos < xPosition then -- We need to move to the left.
- local difference = xPosition - xPos
- turtle.turnLeft()
- for i = 1, difference, 1 do
- turtle.forward()
- end
- turtle.turnRight()
- else -- We need to move to the right
- local difference = xPos - xPosition
- turtle.turnRight()
- for i = 1, difference, 1 do
- turtle.forward()
- end
- turtle.turnLeft()
- end
- xPosition = xPos
- end
- if zPos ~= zPosition then
- if zPos < zPosition then -- We need to move backward
- local difference = zPosition - zPos
- for i = 1, difference, 1 do
- turtle.back()
- end
- else
- local difference = zPos - zPosition
- for i = 1, difference, 1 do
- turtle.forward()
- end
- end
- zPosition = zPos
- end
- end
- -- Throws an item away at the index provided (1 - 16 inclusive)
- -- Inputs:
- -- int index : The index of the stack to throw away.
- -- Outputs:
- -- N/A
- ThrowStackAway =
- function(index)
- if index ~= nil and turtle.getItemDetail(index) ~= nil then
- local archivedX = xPosition
- local archivedZ = zPosition
- TravelToPosition(5, 2)
- turtle.select(index)
- turtle.dropDown()
- TravelToPosition(archivedX, archivedZ)
- end
- end
- -- Equips Crop Sticks in the inventory.
- -- If we are out, we will pick them up from the crop stick input station, and then return to our original location.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- EquipCropSticks =
- function()
- if cropStickPosition == nil then
- cropStickPosition = ScanForItemInInventoryByName(itemNameCropSticks)
- end
- local cropSticks
- if cropStickPosition ~= nil then
- cropSticks = turtle.getItemDetail(cropStickPosition)
- turtle.select(cropStickPosition)
- end
- if cropSticks == nil then
- -- We need to refill on cropSticks. Archive our current location.
- local archivedX = xPosition
- local archivedZ = zPosition
- TravelToPosition(2, 1) -- Travel to position in order to pick up crop sticks.
- turtle.suckDown()
- TravelToPosition(archivedX, archivedZ)
- cropStickPosition = ScanForItemInInventoryByName(itemNameCropSticks)
- if cropStickPosition ~= nil then
- turtle.select(cropStickPosition)
- end
- end
- end
- -- Equips bonemeal
- -- Inputs:
- -- bool allowRefill : True if you want to allow the turtle to refill if it's out, false if you don't.
- -- Outputs:
- -- N/A
- EquipBoneMeal =
- function(allowRefill)
- if allowRefill == nil then allowRefill = false end
- if boneMealPosition == nil then
- boneMealPosition = ScanForItemInInventoryByName(itemNameBoneMeal)
- end
- local boneMeal
- if boneMealPosition ~= nil then
- boneMeal = turtle.getItemDetail(boneMealPosition)
- turtle.select(boneMealPosition)
- if not boneMeal then
- boneMealPosition = nil
- end
- end
- if boneMeal == nil and (hasFailedOnRetrievingBoneMealLastTime == true and allowRefill == true or hasFailedOnRetrievingBoneMealLastTime == false) then
- -- We need to refill on boneMeal. Archive our current location.
- local archivedX = xPosition
- local archivedZ = zPosition
- TravelToPosition(1, 1) -- Travel to position in order to pick up boneMeal.
- turtle.suckUp()
- TravelToPosition(archivedX, archivedZ)
- boneMealPosition = ScanForItemInInventoryByName(itemNameBoneMeal)
- if boneMealPosition ~= nil then
- turtle.select(boneMealPosition)
- hasFailedOnRetrievingBoneMealLastTime = false
- else
- hasFailedOnRetrievingBoneMealLastTime = true
- end
- end
- end
- -- If the turtle is below 10% fuel, it needs to go to the chest to refuel.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- HandleFuel =
- function()
- if turtle.getFuelLevel() / turtle.getFuelLimit() < .1 then
- if turtle.getFuelLevel() > 0 then
- print("Turtle is low on fuel - Refueling...")
- local archivedX = xPosition
- local archivedZ = zPosition
- TravelToPosition(1, 2) -- Travel to position in order to pick up fuel.
- turtle.suckDown() -- Suck up 3 times
- turtle.suckDown()
- turtle.suckDown()
- TravelToPosition(archivedX, archivedZ) -- Return to original location.
- end
- local fuelPosition = ScanForItemInInventoryByName(itemNameFuel)
- while fuelPosition ~= nil do
- turtle.select(fuelPosition)
- turtle.refuel()
- fuelPosition = ScanForItemInInventoryByName(itemNameFuel)
- end
- end
- end
- -- Stores the item in the noted index at the storage location.
- -- Inputs:
- -- int index : The index of the stack to save.
- -- Outputs:
- -- N/A
- SaveStack =
- function(index)
- if index ~= nil and turtle.getItemDetail(index) ~= nil then
- local archivedX = xPosition
- local archivedZ = zPosition
- TravelToPosition(4, 1)
- turtle.select(index)
- turtle.dropDown()
- TravelToPosition(archivedX, archivedZ)
- end
- end
- -- Create a redstone impulse at .9 seconds on the bottom of the turtle.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- redstoneImpulseOnBottom =
- function()
- redstone.setOutput("bottom", true)
- os.sleep(.9)
- redstone.setOutput("bottom", false)
- end
- -- Sucks up a new seed.
- -- Inputs:
- -- int numOfSeedsToSuckUp : The number of seeds to suck up.
- -- Outputs:
- -- boolean true if it sucked up 2 seeds, 1 if it sucked up 1 seed.
- SuckUpNewSeed =
- function(numOfSeedsToSuckUp)
- TravelToPosition(5, 1) -- Travel to the seed input chest
- turtle.select(ScanForItemInInventoryByName(nil)) -- Select the first empty position.
- local suckSuccess = turtle.suckUp(numOfSeedsToSuckUp) -- suck at most numOfSeedsToSuckUp items from the chest.
- while suckSuccess == false do
- print("Waiting for "..textutils.serialize(numOfSeedsToSuckUp).." input seeds. Place a seed in the chest above the turtle.")
- os.sleep(3)
- suckSuccess = turtle.suckUp(numOfSeedsToSuckUp)
- end
- return turtle.getItemCount() > 1 -- Evaluate whether we sucked up 1 or more seeds, and return true/false accordingly.
- end
- -- Plant seed on the left side.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- PlantSeedOnLeft =
- function()
- TravelToPosition(1, 3)
- turtle.dropDown(1)
- redstoneImpulseOnBottom()
- EquipBoneMeal()
- -- Have turtle place bone meal below itself 5 times.
- if boneMealPosition ~= nil then
- TravelToPosition(2, 3)
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- end
- end
- -- Plant seed on right side.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- PlantSeedOnRight =
- function()
- TravelToPosition(5, 3)
- turtle.dropDown(1)
- redstoneImpulseOnBottom()
- EquipBoneMeal()
- -- Have turtle place bone meal below itself 5 times.
- if boneMealPosition ~= nil then
- TravelToPosition(4, 3)
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- if boneMealPosition ~= nil then turtle.placeDown() end
- end
- end
- -- Employs the Computer Controlled Seed Analyzer at the current location to
- -- Analyze the current seed, and return a table for the seed's properties.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- Table { int X, int Y, int Z, int Fitness }
- AnalyzeSeedAtCurrentLocation =
- function()
- local analyzer = peripheral.wrap("bottom")
- turtle.dropDown(1)
- analyzer.analyze()
- while not analyzer.isAnalyzed() do
- os.sleep(.5)
- end
- local x, y, z = analyzer.getSpecimenStats()
- local retData = {X = x, Y = y, Z = z, Fitness = nil }
- retData["Fitness"] = GetSeedFitness(retData)
- turtle.suckDown() -- Pick the seed back up
- return retData
- end
- -- If it is determined that the state of the board no longer matches what we expect,
- -- let's reset our save data.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- ResetSaveData =
- function()
- if loadedData == true then
- generation = 2
- currentNumberOfPerfectSeedsBred = 0
- seed1 = {X = 1, Y = 1, Z = 1, Fitness = nil}
- seed2 = {X = 1, Y = 1, Z = 1, Fitness = nil}
- leftSeedName = ""
- rightSeedName = ""
- childSeedName = ""
- loadedData = false
- end
- end
- -- Prepares for planting, if this station isn't already prepared.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- PrepareForPlanting =
- function()
- redstone.setOutput("bottom", false) -- deactivate redstone output in case it's been left on...
- HandleFuel()
- EquipCropSticks()
- TravelToPosition(2, 3)
- local success, item = turtle.inspectDown()
- if not success then
- turtle.placeDown()
- end
- TravelToPosition(3, 3)
- local success, item = turtle.inspectDown()
- if not success then
- EquipCropSticks()
- turtle.placeDown()
- EquipCropSticks() -- Call equipCropSticks a 2nd time just in case we run out before.
- -- Since turtles cannot actually place the double-crop sticks,
- -- I need to have this turtle take advantage of the autonomous activator I placed a while ago.
- turtle.forward()
- turtle.dropDown(1)
- redstoneImpulseOnBottom()
- turtle.back()
- end
- TravelToPosition(4, 3)
- local success, item = turtle.inspectDown()
- if not success then
- EquipCropSticks()
- turtle.placeDown()
- end
- -- At this point, we will have crop sticks properly placed on the ground. Let's check for the existance of seeds on the left/middle/right
- TravelToPosition(2, 2)
- local analyzer = peripheral.wrap("bottom")
- local leftHasPlant = analyzer.hasPlant(frontCompassDirection)
- TravelToPosition(3, 2)
- local analyzer = peripheral.wrap("bottom")
- local middleHasPlant = analyzer.hasPlant(frontCompassDirection)
- TravelToPosition(4, 2)
- local analyzer = peripheral.wrap("bottom")
- local rightHasPlant = analyzer.hasPlant(frontCompassDirection)
- print("Left Has Plant: "..textutils.serialise(leftHasPlant))
- print("Mid Has Plant: "..textutils.serialise(middleHasPlant))
- print("Right Has Plant: "..textutils.serialise(rightHasPlant))
- if not leftHasPlant then
- SuckUpNewSeed(1)
- if leftSeedName ~= turtle.getItemDetail().name then
- ResetSaveData()
- end
- leftSeedName = turtle.getItemDetail().name
- print("Left Seed Name: "..textutils.serialise(leftSeedName))
- TravelToPosition(2, 2)
- seed1 = AnalyzeSeedAtCurrentLocation()
- PlantSeedOnLeft()
- end
- if not rightHasPlant then
- SuckUpNewSeed(1)
- if rightSeedName ~= turtle.getItemDetail().name then
- ResetSaveData()
- end
- rightSeedName = turtle.getItemDetail().name
- print("Right Seed Name: "..textutils.serialise(rightSeedName))
- TravelToPosition(4, 2)
- seed2 = AnalyzeSeedAtCurrentLocation()
- PlantSeedOnRight()
- end
- end
- -- Has the turtle wait until it can detect that an offspring has been produced.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- WaitForOffspring =
- function()
- TravelToPosition(3, 2) -- Travel to analyzer that detects if a plant is there.
- local analyzer = peripheral.wrap("bottom")
- local offspringFound = analyzer.hasPlant(frontCompassDirection)
- while not offspringFound do
- os.sleep(5)
- offspringFound = analyzer.hasPlant(frontCompassDirection)
- if not offspringFound then -- If we can't find the offspring, let's confirm it's possible, by checking if it's a cross crop. If it isn't, we need to fix it.
- local isCrossCrop = analyzer.isCrossCrop(frontCompassDirection)
- if not isCrossCrop then
- print("Crop Sticks missing at offspring location. Correcting.")
- TravelToPosition(3, 4)
- EquipCropSticks()
- turtle.dropDown(1)
- redstoneImpulseOnBottom()
- TravelToPosition(3, 2)
- end
- if useBoneMealOnMiddlePosition then
- EquipBoneMeal()
- -- Have turtle place bone meal below itself 10 times.
- if boneMealPosition ~= nil then
- TravelToPosition(3, 3)
- for i = 1, 10, 1 do
- if boneMealPosition ~= nil then turtle.placeDown() end
- EquipBoneMeal()
- end
- TravelToPosition(3, 2)
- end
- end
- end
- end
- print("Found Offspring!")
- end
- -- Function to eliminate parent 1, and replace with the current offspring.
- -- Inputs:
- -- Table offspring { int X, int Y, int Z, int Fitness }
- -- Outputs:
- -- N/A
- PlantOffSpringOnSeed1 =
- function(offspring)
- local index = ScanForItemInInventoryByName("seed", true)
- seed1 = offspring
- TravelToPosition(2, 3)
- EquipCropSticks()
- if turtle.getItemCount() == 64 then turtle.drop(1) end -- We have 64 crop sticks, so drop 1 to avoid making 2 stacks.
- turtle.digDown()
- turtle.placeDown()
- turtle.select(index)
- PlantSeedOnLeft()
- PurgeInventoryOfExtraneousItems()
- end
- -- Function to eliminate parent 2, and replace with the current offspring.
- -- Inputs:
- -- Table offspring { int X, int Y, int Z, int Fitness }
- -- Outputs:
- -- N/A
- PlantOffSpringOnSeed2 =
- function(offspring)
- local index = ScanForItemInInventoryByName("seed", true)
- seed2 = offspring
- TravelToPosition(4, 3)
- EquipCropSticks()
- if turtle.getItemCount() == 64 then turtle.drop(1) end -- We have 64 crop sticks, so drop 1 to avoid making 2 stacks.
- turtle.digDown()
- turtle.placeDown()
- turtle.select(index)
- PlantSeedOnRight()
- PurgeInventoryOfExtraneousItems()
- end
- -- Function to reject our current offspring (Probably throw it away in a trash can or something...)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- RejectOffspring =
- function()
- local index = ScanForItemInInventoryByName("seed", true)
- ThrowStackAway(index)
- end
- -- Function to save our offspring (Probably put into a chest or something...)
- -- Call this if the offspring, as well as both parents, are 10/10/10 seeds.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- SaveOffspring =
- function()
- currentNumberOfPerfectSeedsBred = currentNumberOfPerfectSeedsBred + 1
- local index = ScanForItemInInventoryByName("seed", true)
- SaveStack(index)
- end
- -- Function to analyze the offspring seed, and determine if it should replace either parent or be rejected.
- -- At this point, the seed has already been harvested/analyzed,
- -- and we just need to determine whether we reject it, or replace one of our parents with it.
- -- Inputs:
- -- Table offspring { int X, int Y, int Z, int Fitness }
- -- Outputs:
- -- N/A
- HarvestedOffspring =
- function(offspring)
- print("HarvestedOffspring -> Get Fitness on Seed 1.")
- GetSeedFitness(seed1)
- print("HarvestedOffspring -> Get Fitness on Seed 2.")
- GetSeedFitness(seed2)
- print("HarvestedOffspring -> Get Fitness on Seed 3.")
- GetSeedFitness(offspring)
- -- If we have perfect genetics in both parents and our offspring, save our offspring.
- if seed1["Fitness"] == perfectFitnessValue and seed2["Fitness"] == perfectFitnessValue and offspring["Fitness"] == perfectFitnessValue then
- print("We have seeds with perfect genetics! Saving Seed.")
- SaveOffspring()
- return
- end
- -- This means that we have found ourselves a seed which needs to replace either of it's parents.
- if offspring["Fitness"] > seed1["Fitness"] or offspring["Fitness"] > seed2["Fitness"] then
- if seed1["Fitness"] <= seed2["Fitness"] then
- -- Seed 1 is worse than or equal to Seed 2, so let's replace Seed 1.
- print("Replacing Seed 1. Fitness: "..seed1["Fitness"].." -> "..offspring["Fitness"])
- PlantOffSpringOnSeed1(offspring)
- else
- -- Seed 2 is worse than seed 1, so let's replace Seed 2.
- print("Replacing Seed 2. Fitness: "..seed2["Fitness"].." -> "..offspring["Fitness"])
- PlantOffSpringOnSeed2(offspring)
- end
- generation = generation + 1
- print("\tGeneration: "..generation)
- return
- end
- if offspring["Fitness"] <= seed1["Fitness"] or offspring["Fitness"] <= seed2["Fitness"] then
- -- This means we have a bad seed, so let's reject it.
- generation = generation + 1
- print("Rejected Seed.\r\n\tGeneration: "..generation)
- RejectOffspring()
- end
- end
- -- Function to harvest the current offspring.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- HarvestOffspring =
- function()
- EquipCropSticks()
- if turtle.getItemCount() == 64 then turtle.drop(1) end -- Drop 1 crop stick since we need room for 1 when we harvest this seed. This is to avoid accidentally having 2 stacks of crop sticks.
- turtle.forward()
- turtle.digDown()
- turtle.placeDown()
- turtle.forward()
- turtle.dropDown(1)
- redstoneImpulseOnBottom()
- turtle.back()
- turtle.back()
- end
- -- Function to clean up after the turtle has finished.
- -- (Basically just reinitializes the data.)
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- CleanUp =
- function()
- currentNumberOfPerfectSeedsBred = 0
- seed1 = {X = 1, Y = 1, Z = 1, Fitness = nil} -- Stores the Left seed or "parent"
- seed2 = {X = 1, Y = 1, Z = 1, Fitness = nil} -- Stores the Right seed or "parent"
- generation = 1 -- Stores the number of generations it took to get to our current set of parents.
- leftSeedName = "" -- Stores the name of the seed type we are currently breeding.
- rightSeedName = "" -- Stores the name of the right seed type we are currently breeding.
- childSeedName = "" -- Stores the name of the child seed type we are currently breeding.
- EquipCropSticks() -- Equip Crop Sticks before we clean up after ourselves, to try to prevent accidentally creating duplicate stacks. This is because digging items puts them into the turtle's inventory at the slot that's currently selected.
- TravelToPosition(2, 3)
- turtle.digDown()
- TravelToPosition(3, 3)
- turtle.digDown()
- TravelToPosition(4, 3)
- turtle.digDown()
- PurgeInventoryOfExtraneousItems()
- end
- -- Loads pertinent Data.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- Load =
- function()
- if not fs.exists("MatthewC/Agricraft/Data/SeedBreederData") then return end
- local h = fs.open("MatthewC/Agricraft/Data/SeedBreederData", "r") -- "r" is read only
- local data = h.readAll()
- if data ~= nil then
- local parsedData = textutils.unserialize(data)
- currentNumberOfPerfectSeedsBred = parsedData["CurrentNumberOfPerfectSeedsBred"]
- seed1 = parsedData["Seed1"]
- seed2 = parsedData["Seed2"]
- generation = parsedData["Generation"]
- leftSeedName = parsedData["LeftSeedName"]
- rightSeedName = parsedData["RightSeedName"]
- childSeedName = parsedData["ChildSeedName"]
- loadedData = true
- else
- print("Attempted to load --- Data was nil")
- end
- -- No need to call close on h if it's read mode.
- h = fs.open("MatthewC/Agricraft/Data/SeedBreederSettings", "r") -- "r" is read only
- data = h.readAll()
- if data ~= nil then
- local parsedData = textutils.unserialize(data)
- if parsedData["NumberOfPerfectSeedsToBreed"] then numberOfPerfectSeedsToBreed = parsedData["NumberOfPerfectSeedsToBreed"] end
- if parsedData["BlockNameBorderRight"] then blockNameBorderRight = parsedData["BlockNameBorderRight"] end
- if parsedData["BlockNameBorderLeft"] then blockNameBorderLeft = parsedData["BlockNameBorderLeft"] end
- if parsedData["BlockNameBorderFront"] then blockNameBorderFront = parsedData["BlockNameBorderFront"] end
- if parsedData["BlockNameBorderBack"] then blockNameBorderBack = parsedData["BlockNameBorderBack"] end
- if parsedData["BlockNameSeedAnalyzer"] then blockNameSeedAnalyzer = parsedData["BlockNameSeedAnalyzer"] end
- if parsedData["ItemNameCropSticks"] then itemNameCropSticks = parsedData["ItemNameCropSticks"] end
- if parsedData["ItemNameFuel"] then itemNameFuel = parsedData["ItemNameFuel"] end
- if parsedData["ItemNameBoneMeal"] then itemNameBoneMeal = parsedData["ItemNameBoneMeal"] end
- if parsedData["ItemMetaDataBoneMeal"] then itemMetaDataBoneMeal = parsedData["ItemMetaDataBoneMeal"] end
- if parsedData["FrontCompassDirection"] then frontCompassDirection = parsedData["FrontCompassDirection"] end
- if parsedData["UseBoneMealOnCenter"] then useBoneMealOnCenter = parsedData["UseBoneMealOnCenter"] end
- end
- Save()
- end
- -- Saves the pertinent Data.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- Save =
- function()
- -- Create appropriate directories as necessary.
- if not fs.exists("MatthewC") then fs.makeDir("MatthewC") end
- if not fs.exists("MatthewC/Agricraft") then fs.makeDir("MatthewC/Agricraft") end
- if not fs.exists("MatthewC/Agricraft/Data") then fs.makeDir("MatthewC/Agricraft/Data") end
- local dataToSave =
- {
- CurrentNumberOfPerfectSeedsBred = currentNumberOfPerfectSeedsBred,
- Seed1 = seed1,
- Seed2 = seed2,
- Generation = generation,
- LeftSeedName = leftSeedName,
- RightSeedName = rightSeedName,
- ChildSeedName = childSeedName
- }
- local writer = fs.open("MatthewC/Agricraft/Data/SeedBreederData", "w") -- "w" is write Mode
- writer.write(textutils.serialise(dataToSave))
- writer.close()
- dataToSave =
- {
- NumberOfPerfectSeedsToBreed = numberOfPerfectSeedsToBreed,
- BlockNameBorderRight = blockNameBorderRight,
- BlockNameBorderLeft = blockNameBorderLeft,
- BlockNameBorderFront = blockNameBorderFront,
- BlockNameBorderBack = blockNameBorderBack,
- BlockNameSeedAnalyzer = blockNameSeedAnalyzer,
- ItemNameCropSticks = itemNameCropSticks,
- ItemNameFuel = itemNameFuel,
- ItemNameBoneMeal = itemNameBoneMeal,
- ItemMetaDataBoneMeal = itemMetaDataBoneMeal,
- FrontCompassDirection = frontCompassDirection,
- UseBoneMealOnCenter = useBoneMealOnCenter
- }
- writer = fs.open("MatthewC/Agricraft/Data/SeedBreederSettings", "w") -- "w" is write Mode
- writer.write(textutils.serialise(dataToSave))
- writer.close()
- end
- -- Main program entry point.
- -- Inputs:
- -- N/A
- -- Outputs:
- -- N/A
- MainExecutionLoop =
- function()
- Load()
- -- Special case to handle if the turtle was turned on the first time, search for coal in inventory and refuel off it.
- if turtle.getFuelLevel() == 0 then
- print("We are out of fuel. Attempting to refuel.")
- HandleFuel()
- if turtle.getFuelLevel() == 0 then
- print("\r\n\r\n***WARNING!!! NO FUEL DETECTED!!!***")
- print("\r\nPlease put coal or charcoal in my inventory, and restart the program.")
- return
- end
- end
- ResetLocationToCenter()
- while true do
- PurgeInventoryOfExtraneousItems()
- PrepareForPlanting()
- print("Current Number of bred Perfect Seeds: "..textutils.serialize(currentNumberOfPerfectSeedsBred))
- print("Number of perfect seeds to breed: "..textutils.serialize(numberOfPerfectSeedsToBreed))
- while currentNumberOfPerfectSeedsBred < numberOfPerfectSeedsToBreed do
- EquipBoneMeal(true) -- Call equip bonemeal, while allowing for refilling.
- print("Left Seed="..textutils.serialise(seed1))
- print("Right Seed="..textutils.serialise(seed2))
- print("Generation: "..textutils.serialise(generation))
- print("Number of perfect seeds bred: "..textutils.serialise(currentNumberOfPerfectSeedsBred))
- print("Waiting for Offspring")
- WaitForOffspring() -- Wait until we can harvest the offspring.
- print("Harvesting Offspring")
- HarvestOffspring()
- print("Detecting index in inventory that offspring was placed in")
- local offspringIndex = ScanForItemInInventoryByName("seed", true) -- Determine where in the turtle's inventory the seed is at. Note: performs a case-insensitive search.
- if offspringIndex ~= nil then
- TravelToPosition(3, 2) -- Travel above a Seed Analyzer
- turtle.select(offspringIndex) -- select the index that the seed is at.
- print("Analyzing Offspring")
- local offspringData = AnalyzeSeedAtCurrentLocation() -- analyze the seed at the current location.
- if offspringData ~= nil then
- print("Evaluating offspring")
- print("offspring="..textutils.serialize(offspringData))
- HarvestedOffspring(offspringData)
- else
- print("Anomaly detected: Offspring was nil. Purging Inventory and continuing.")
- PurgeInventoryOfExtraneousItems()
- end
- end
- os.sleep(.1)
- loadedData = false -- Set our flag for loadedData to false here...
- Save()
- end
- CleanUp()
- end
- end
- --------------------------------------------------------------------------------------------------
- -- Unit Tests
- -- Collection of test functions to confirm that this software works properly.
- --------------------------------------------------------------------------------------------------
- -- Test function to tavel to all possible locations.
- -- Pass in true to require user intervention.
- function TestTravelToPosition(waitForUserIntevention)
- print("Running Traveling Diagnostics")
- for x = 1, 5, 1 do
- for z = 1, 4, 1 do
- print("Traveling to X="..x..", Z="..z)
- TravelToPosition(x, z)
- if waitForUserInvervention then
- print("Press Any Key to Continue")
- os.pullEvent("key")
- end
- end
- end
- end
- --ResetLocationToCenter() -- IF ANY TEST METHODS ARE CALLED, THIS MUST BE CALLED FIRST!!!
- --TestTravelToPosition()
- --ThrowStackAway(15)
- --SaveStack(16)
- --EquipCropSticks()
- --PrepareForPlanting()
- --HandleFuel()
- --WaitForOffspring()
- --local data = ScanForItemInInventoryByName("seed", true)
- --print("Data: "..textutils.serialise(data))
- --os.sleep(10)
- --------------------------------------------------------------------------------------------------
- -- Post-Initialization
- -- Initializes the rest of the data after our functions have been defined
- --------------------------------------------------------------------------------------------------
- -- Call GetSeedFitness to initialize the property in seed1 and seed2.
- GetSeedFitness(seed1)
- GetSeedFitness(seed2)
- -- Calculate the perfect Fitness Value attainable.
- perfectFitnessValue = GetSeedFitness({X = 10, Y = 10, Z = 10, Fitness = nil})
- --------------------------------------------------------------------------------------------------
- -- Execution
- -- Kicks off the actual program which needs to be executed.
- --------------------------------------------------------------------------------------------------
- print("Seed Breeder Initialized ---")
- print("Perfect Fitness Allowable: "..perfectFitnessValue)
- print("Perfect Seeds to breed: "..numberOfPerfectSeedsToBreed)
- print("\r\n\r\nBeginning Main execution Loop: -- Breeding seeds.")
- MainExecutionLoop()
- print("\r\nProgram Terminated.")
- print("Press any key to continue")
- os.pullEvent("key")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement