· 7 years ago · Sep 27, 2018, 06:28 AM
1-- Harvest Program for robots. Uses a hoe and geolyzer for optimal harvesting.
2--[[
3Author: Andrew Lalis
4File: harvest.lua
5Version: 1.0
6Last Modified: 27-09-2018
7
8Description:
9This script enables a robot to harvest fields of crops quickly and efficiently.
10The robot will traverse the field and only harvest crops considered 'done' by
11their crop definition.
12--]]
13
14local robot = require("robot")
15local component = require("component")
16local fs = component.filesystem
17local serial = require("serialization")
18local geolyzer = component.geolyzer
19local ic = component.inventory_controller
20local sides = require("sides")
21
22local CONFIG_FILE = "harvest.conf"
23
24local LEFT = 1
25local RIGHT = 0
26
27-- List of crops which will be harvested.
28local crop_definitions = {
29 ["harvestcraft:pamsoybeancrop"] = {
30 growth_limit = 0.42,
31 item_name = "harvestcraft:soybeanitem"
32 },
33 ["harvestcraft:pamspiceleafcrop"] = {
34 growth_limit = 0.42,
35 item_name = "harvestcraft:spiceleafitem"
36 }
37}
38
39-- Repeats the given function until it returns true.
40local function doUntilSuccess(func)
41 local success = func()
42 while (not success) do
43 success = func()
44 end
45end
46
47-- Pre-defined path from turtle docking bay to start of harvest area (first crop).
48local function goToStart(rows, columns)
49 doUntilSuccess(robot.forward)
50end
51
52-- Pre-defined path back to the turtle docking bay.
53local function goBack(rows, columns)
54 for i=1,(columns-1) do
55 doUntilSuccess(robot.back)
56 end
57 robot.turnRight()
58 for i=1,(rows-1) do
59 doUntilSuccess(robot.back)
60 end
61 robot.turnLeft()
62 doUntilSuccess(robot.back)
63end
64
65--[[
66Select an item, given its name and damage value.
67item_name - string: The id string for an item.
68item_data - number: The damage value, or variation of an item. Defaults to zero.
69return - boolean: True if at least one slot contains the item. That slot is now
70selected.
71--]]
72local function selectItemByName(item_name, item_data)
73 for i=1,16 do
74 local stack = ic.getStackInInternalSlot(i)
75 if (stack ~= nil and stack.name == item_name and stack.damage == item_data) then
76 robot.select(i)
77 return true
78 end
79 end
80 return false
81end
82
83--[[
84Checks if the hoe is equipped. Meant to be done before starting a harvest.
85return - boolean: True if a hoe is equipped, or false if not.
86--]]
87local function isHoeEquipped()
88 for i=1,16 do
89 local item_stack = ic.getStackInInternalSlot(i)
90 if (item_stack == nil) then
91 robot.select(i)
92 ic.equip()
93 new_item_stack = ic.getStackInInternalSlot(i)
94 if (new_item_stack ~= nil and string.match(new_item_stack.name, "_hoe")) then
95 return true
96 end
97 return false
98 end
99 end
100 return false
101end
102
103--[[
104Tries to harvest a plant, if it is one of the crops defined in the crop
105definitions table above.
106return - boolean: True if a plant was harvested, false otherwise.
107--]]
108local function harvestPlant()
109 local plant_data = geolyzer.analyze(sides.bottom)
110 local crop_definition = crop_definitions[plant_data.name]
111 if (crop_definition == nil) then
112 return false
113 end
114 if (plant_data.growth >= crop_definition.growth_limit) then
115 robot.swingDown()
116 selectItemByName(crop_definition.item_name, 0)
117 robot.placeDown()
118 return true
119 else
120 return false
121 end
122end
123
124--[[
125Harvests one row of crops.
126length - int: The number of plants in this row.
127return - int: The number of crops that were harvested.
128--]]
129local function harvestRow(length)
130 local harvests = 0
131 for i=1,length do
132 if (i > 1) then
133 doUntilSuccess(robot.forward)
134 end
135 if (harvestPlant()) then
136 harvests = harvests + 1
137 end
138 end
139 return harvests
140end
141
142--[[
143At the end of the row, the robot must rotate into the next row, and this is
144dependent on where the start location is.
145current_row_index - int: The row the robot is on prior to turning.
146start_location - int: Whether the robot starts at the left or right.
147--]]
148local function turnToNextRow(current_row_index, start_location)
149 if (current_row_index % 2 == start_location) then
150 robot.turnRight()
151 else
152 robot.turnLeft()
153 end
154 doUntilSuccess(robot.forward)
155 if (current_row_index % 2 == start_location) then
156 robot.turnRight()
157 else
158 robot.turnLeft()
159 end
160end
161
162--[[
163Harvests a two dimensional area defined by rows and columns. The robot starts
164by moving forward down the first row.
165rows - int: The number of rows to harvest.
166columns - int: The number of columns to harvest.
167start_location - int: 1 for LEFT, 0 for RIGHT.
168return - int: The total number of crops harvested.
169--]]
170local function harvestField(rows, columns, start_location)
171 goToStart(rows, columns)
172 -- Begin harvesting.
173 local harvests = 0
174 for i=1,rows do
175 harvests = harvests + harvestRow(columns)
176 -- Do not turn to the next row on the last row.
177 if (i < rows) then
178 turnToNextRow(i, start_location)
179 end
180 end
181 goBack(rows, columns)
182 return harvests
183end
184
185--[[
186Drops all carried items into an inventory below the robot.
187return - int: The number of items dropped.
188--]]
189local function dropItems()
190 local item_count = 0
191 for i=1,16 do
192 robot.select(i)
193 local stack = ic.getStackInInternalSlot(i)
194 if (stack ~= nil) then
195 doUntilSuccess(robot.dropDown)
196 item_count = item_count + stack.size
197 end
198 end
199 return item_count
200end
201
202--[[
203Reads config from a file.
204filename - string: The string path/filename.
205return - table|nil: The table defined in config, or nil if the file does not
206exist or another error occurs.
207--]]
208local function loadConfig(filename)
209 if (fs.exists(filename) and not fs.isDirectory(filename)) then
210 -- Config file exists.
211 file = io.open(filename, "r")
212 local t = serial.unserialize(file:read())
213 file:close()
214 return t
215 else
216 print("No config file " .. filename .. "exists. Please create it before continuing.")
217 return nil
218 end
219end
220
221--[[
222Guides the user in creating a new config.
223--]]
224local function createConfig(filename)
225 local config = {}
226 print("Does your robot start on the left or right of the field?")
227 local input = io.read()
228 if (input == "left") then
229 config.START_LOCATION_RELATIVE = LEFT
230 elseif (input == "right") then
231 config.START_LOCATION_RELATIVE = RIGHT
232 else
233 print("Invalid choice. Should be either left or right.")
234 return nil
235 end
236 print("Enter number of rows.")
237 config.ROWS = tonumber(io.read())
238 print("Enter number of columns.")
239 config.COLS = tonumber(io.read())
240
241 print("How many crops are being harvested?")
242 for i=1,tonumber(io.read()) do
243 print("Crop "..i..": What is the block name? (Use geolyzer to analyze it)")
244 local name = io.read()
245 config.crop_definitions[name] = {}
246 print(" What is the growth threshold for harvesting?")
247 config.crop_definitions[name].growth_limit = tonumber(io.read())
248 print(" What is the item name of this crop?")
249 config.crop_definitions[name].item_name = io.read()
250 end
251 file = io.open(filename, "w")
252 file:write(serial.serialize(config))
253 file:close()
254end
255
256local function main()
257 local config = loadConfig(CONFIG_FILE)
258 if (config == nil) then
259 config = createConfig(CONFIG_FILE)
260 end
261 crop_definitions = config.crop_definitions
262 local harvest_count = harvestField(config.ROWS, config.COLS, config.START_LOCATION_RELATIVE)
263 local drop_count = dropItems()
264 print(harvest_count..", "..drop_count)
265end