2018-09-27 06:26:24 +00:00
|
|
|
-- 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.
|
2018-09-30 06:17:45 +00:00
|
|
|
local crop_definitions = {}
|
2018-09-27 06:26:24 +00:00
|
|
|
|
|
|
|
-- 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.
|
2018-09-30 06:17:45 +00:00
|
|
|
robot.select(1)
|
2018-09-27 06:26:24 +00:00
|
|
|
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)
|
2018-09-30 06:17:45 +00:00
|
|
|
-- Config file exists.
|
|
|
|
local f = io.open(filename, "r")
|
|
|
|
if (f == nil) then
|
2018-09-27 06:37:40 +00:00
|
|
|
print("No config file " .. filename .. " exists. Please create it before continuing.")
|
2018-09-27 06:26:24 +00:00
|
|
|
return nil
|
|
|
|
end
|
2018-09-30 06:17:45 +00:00
|
|
|
local t = serial.unserialize(f:read())
|
|
|
|
f:close()
|
|
|
|
return t
|
2018-09-27 06:26:24 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
Guides the user in creating a new config.
|
2018-09-27 06:37:40 +00:00
|
|
|
return - table: The config created.
|
2018-09-27 06:26:24 +00:00
|
|
|
--]]
|
|
|
|
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?")
|
2018-09-27 06:37:40 +00:00
|
|
|
config.crop_definitions = {}
|
2018-09-27 06:26:24 +00:00
|
|
|
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()
|
2018-09-27 06:37:40 +00:00
|
|
|
return config
|
2018-09-27 06:26:24 +00:00
|
|
|
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
|
2018-09-27 06:37:40 +00:00
|
|
|
|
|
|
|
main()
|