Added harvest script.
This commit is contained in:
parent
bab6ca719b
commit
4f1db31164
|
@ -0,0 +1,41 @@
|
||||||
|
# harvest.lua
|
||||||
|
Script for smart harvesting of rectangular farms of multiple crops.
|
||||||
|
|
||||||
|
## Pastebin
|
||||||
|
[ytYCVGsc](https://pastebin.com/ytYCVGsc)
|
||||||
|
|
||||||
|
## Module Requirements
|
||||||
|
* geolyzer
|
||||||
|
* inventory_controller
|
||||||
|
* *equipped hoe*
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
To operate the program, you simply need to run the program. If no `harvest.conf` config file exists, the program will guide you through the creation of it. For it, you'll need the following information:
|
||||||
|
|
||||||
|
* On what side does the robot start harvesting (left or right)?
|
||||||
|
* How many rows are in the field?
|
||||||
|
* How many columns are in the field?
|
||||||
|
* What crops will be grown?
|
||||||
|
|
||||||
|
For each crop that will be grown, you will need the following pieces of information:
|
||||||
|
|
||||||
|
1. The crop's block name (can be found using the `geolyzer` component.
|
||||||
|
2. The floating point value at which the crop is ready to be harvested. Can also be found with the `geolyzer`.
|
||||||
|
3. The name of the item used to replant the crop. This can be found in the minecraft inventory after pressing `F3 + H` to enable more detailed information for displayed items.
|
||||||
|
|
||||||
|
Once all this information is entered, running the program will harvest the defined area, and drop all gathered items into a chest below the robot's resting point.
|
||||||
|
|
||||||
|
### Diagram of setup
|
||||||
|
The below diagram shows how farms should be set up: the robot faces into the first row, and has a charger behind it to replenish energy after each harvest. A chest or hopper can be placed below the robot for item collection.
|
||||||
|
|
||||||
|
```
|
||||||
|
CR---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
---------
|
||||||
|
```
|
|
@ -0,0 +1,265 @@
|
||||||
|
-- Harvest Program for robots. Uses a hoe and geolyzer for optimal harvesting.
|
||||||
|
--[[
|
||||||
|
Author: Andrew Lalis
|
||||||
|
File: harvest.lua
|
||||||
|
Version: 1.0
|
||||||
|
Last Modified: 27-09-2018
|
||||||
|
|
||||||
|
Description:
|
||||||
|
This script enables a robot to harvest fields of crops quickly and efficiently.
|
||||||
|
The robot will traverse the field and only harvest crops considered 'done' by
|
||||||
|
their crop definition.
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local robot = require("robot")
|
||||||
|
local component = require("component")
|
||||||
|
local fs = component.filesystem
|
||||||
|
local serial = require("serialization")
|
||||||
|
local geolyzer = component.geolyzer
|
||||||
|
local ic = component.inventory_controller
|
||||||
|
local sides = require("sides")
|
||||||
|
|
||||||
|
local CONFIG_FILE = "harvest.conf"
|
||||||
|
|
||||||
|
local LEFT = 1
|
||||||
|
local RIGHT = 0
|
||||||
|
|
||||||
|
-- List of crops which will be harvested.
|
||||||
|
local crop_definitions = {
|
||||||
|
["harvestcraft:pamsoybeancrop"] = {
|
||||||
|
growth_limit = 0.42,
|
||||||
|
item_name = "harvestcraft:soybeanitem"
|
||||||
|
},
|
||||||
|
["harvestcraft:pamspiceleafcrop"] = {
|
||||||
|
growth_limit = 0.42,
|
||||||
|
item_name = "harvestcraft:spiceleafitem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Repeats the given function until it returns true.
|
||||||
|
local function doUntilSuccess(func)
|
||||||
|
local success = func()
|
||||||
|
while (not success) do
|
||||||
|
success = func()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pre-defined path from turtle docking bay to start of harvest area (first crop).
|
||||||
|
local function goToStart(rows, columns)
|
||||||
|
doUntilSuccess(robot.forward)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pre-defined path back to the turtle docking bay.
|
||||||
|
local function goBack(rows, columns)
|
||||||
|
for i=1,(columns-1) do
|
||||||
|
doUntilSuccess(robot.back)
|
||||||
|
end
|
||||||
|
robot.turnRight()
|
||||||
|
for i=1,(rows-1) do
|
||||||
|
doUntilSuccess(robot.back)
|
||||||
|
end
|
||||||
|
robot.turnLeft()
|
||||||
|
doUntilSuccess(robot.back)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Select an item, given its name and damage value.
|
||||||
|
item_name - string: The id string for an item.
|
||||||
|
item_data - number: The damage value, or variation of an item. Defaults to zero.
|
||||||
|
return - boolean: True if at least one slot contains the item. That slot is now
|
||||||
|
selected.
|
||||||
|
--]]
|
||||||
|
local function selectItemByName(item_name, item_data)
|
||||||
|
for i=1,16 do
|
||||||
|
local stack = ic.getStackInInternalSlot(i)
|
||||||
|
if (stack ~= nil and stack.name == item_name and stack.damage == item_data) then
|
||||||
|
robot.select(i)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Checks if the hoe is equipped. Meant to be done before starting a harvest.
|
||||||
|
return - boolean: True if a hoe is equipped, or false if not.
|
||||||
|
--]]
|
||||||
|
local function isHoeEquipped()
|
||||||
|
for i=1,16 do
|
||||||
|
local item_stack = ic.getStackInInternalSlot(i)
|
||||||
|
if (item_stack == nil) then
|
||||||
|
robot.select(i)
|
||||||
|
ic.equip()
|
||||||
|
new_item_stack = ic.getStackInInternalSlot(i)
|
||||||
|
if (new_item_stack ~= nil and string.match(new_item_stack.name, "_hoe")) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Tries to harvest a plant, if it is one of the crops defined in the crop
|
||||||
|
definitions table above.
|
||||||
|
return - boolean: True if a plant was harvested, false otherwise.
|
||||||
|
--]]
|
||||||
|
local function harvestPlant()
|
||||||
|
local plant_data = geolyzer.analyze(sides.bottom)
|
||||||
|
local crop_definition = crop_definitions[plant_data.name]
|
||||||
|
if (crop_definition == nil) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if (plant_data.growth >= crop_definition.growth_limit) then
|
||||||
|
robot.swingDown()
|
||||||
|
selectItemByName(crop_definition.item_name, 0)
|
||||||
|
robot.placeDown()
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Harvests one row of crops.
|
||||||
|
length - int: The number of plants in this row.
|
||||||
|
return - int: The number of crops that were harvested.
|
||||||
|
--]]
|
||||||
|
local function harvestRow(length)
|
||||||
|
local harvests = 0
|
||||||
|
for i=1,length do
|
||||||
|
if (i > 1) then
|
||||||
|
doUntilSuccess(robot.forward)
|
||||||
|
end
|
||||||
|
if (harvestPlant()) then
|
||||||
|
harvests = harvests + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return harvests
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
At the end of the row, the robot must rotate into the next row, and this is
|
||||||
|
dependent on where the start location is.
|
||||||
|
current_row_index - int: The row the robot is on prior to turning.
|
||||||
|
start_location - int: Whether the robot starts at the left or right.
|
||||||
|
--]]
|
||||||
|
local function turnToNextRow(current_row_index, start_location)
|
||||||
|
if (current_row_index % 2 == start_location) then
|
||||||
|
robot.turnRight()
|
||||||
|
else
|
||||||
|
robot.turnLeft()
|
||||||
|
end
|
||||||
|
doUntilSuccess(robot.forward)
|
||||||
|
if (current_row_index % 2 == start_location) then
|
||||||
|
robot.turnRight()
|
||||||
|
else
|
||||||
|
robot.turnLeft()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Harvests a two dimensional area defined by rows and columns. The robot starts
|
||||||
|
by moving forward down the first row.
|
||||||
|
rows - int: The number of rows to harvest.
|
||||||
|
columns - int: The number of columns to harvest.
|
||||||
|
start_location - int: 1 for LEFT, 0 for RIGHT.
|
||||||
|
return - int: The total number of crops harvested.
|
||||||
|
--]]
|
||||||
|
local function harvestField(rows, columns, start_location)
|
||||||
|
goToStart(rows, columns)
|
||||||
|
-- Begin harvesting.
|
||||||
|
local harvests = 0
|
||||||
|
for i=1,rows do
|
||||||
|
harvests = harvests + harvestRow(columns)
|
||||||
|
-- Do not turn to the next row on the last row.
|
||||||
|
if (i < rows) then
|
||||||
|
turnToNextRow(i, start_location)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
goBack(rows, columns)
|
||||||
|
return harvests
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Drops all carried items into an inventory below the robot.
|
||||||
|
return - int: The number of items dropped.
|
||||||
|
--]]
|
||||||
|
local function dropItems()
|
||||||
|
local item_count = 0
|
||||||
|
for i=1,16 do
|
||||||
|
robot.select(i)
|
||||||
|
local stack = ic.getStackInInternalSlot(i)
|
||||||
|
if (stack ~= nil) then
|
||||||
|
doUntilSuccess(robot.dropDown)
|
||||||
|
item_count = item_count + stack.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return item_count
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Reads config from a file.
|
||||||
|
filename - string: The string path/filename.
|
||||||
|
return - table|nil: The table defined in config, or nil if the file does not
|
||||||
|
exist or another error occurs.
|
||||||
|
--]]
|
||||||
|
local function loadConfig(filename)
|
||||||
|
if (fs.exists(filename) and not fs.isDirectory(filename)) then
|
||||||
|
-- Config file exists.
|
||||||
|
file = io.open(filename, "r")
|
||||||
|
local t = serial.unserialize(file:read())
|
||||||
|
file:close()
|
||||||
|
return t
|
||||||
|
else
|
||||||
|
print("No config file " .. filename .. "exists. Please create it before continuing.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Guides the user in creating a new config.
|
||||||
|
--]]
|
||||||
|
local function createConfig(filename)
|
||||||
|
local config = {}
|
||||||
|
print("Does your robot start on the left or right of the field?")
|
||||||
|
local input = io.read()
|
||||||
|
if (input == "left") then
|
||||||
|
config.START_LOCATION_RELATIVE = LEFT
|
||||||
|
elseif (input == "right") then
|
||||||
|
config.START_LOCATION_RELATIVE = RIGHT
|
||||||
|
else
|
||||||
|
print("Invalid choice. Should be either left or right.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
print("Enter number of rows.")
|
||||||
|
config.ROWS = tonumber(io.read())
|
||||||
|
print("Enter number of columns.")
|
||||||
|
config.COLS = tonumber(io.read())
|
||||||
|
|
||||||
|
print("How many crops are being harvested?")
|
||||||
|
for i=1,tonumber(io.read()) do
|
||||||
|
print("Crop "..i..": What is the block name? (Use geolyzer to analyze it)")
|
||||||
|
local name = io.read()
|
||||||
|
config.crop_definitions[name] = {}
|
||||||
|
print(" What is the growth threshold for harvesting?")
|
||||||
|
config.crop_definitions[name].growth_limit = tonumber(io.read())
|
||||||
|
print(" What is the item name of this crop?")
|
||||||
|
config.crop_definitions[name].item_name = io.read()
|
||||||
|
end
|
||||||
|
file = io.open(filename, "w")
|
||||||
|
file:write(serial.serialize(config))
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function main()
|
||||||
|
local config = loadConfig(CONFIG_FILE)
|
||||||
|
if (config == nil) then
|
||||||
|
config = createConfig(CONFIG_FILE)
|
||||||
|
end
|
||||||
|
crop_definitions = config.crop_definitions
|
||||||
|
local harvest_count = harvestField(config.ROWS, config.COLS, config.START_LOCATION_RELATIVE)
|
||||||
|
local drop_count = dropItems()
|
||||||
|
print(harvest_count..", "..drop_count)
|
||||||
|
end
|
Loading…
Reference in New Issue