From f145f5a17e12f0616eb81d4cd710991416c1117f Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Sun, 30 Sep 2018 11:30:23 +0200 Subject: [PATCH] Updated movescript and added lumber farm. --- libs/move_script/movescript.lua | 74 +++++++- scripts/lumber_farm/README.md | 41 +++++ scripts/lumber_farm/TreeFarm.lua | 180 ++++++++++++++++++++ scripts/lumber_farm/lumber_farm.lua | 255 ++++++++++++++++++++++++++++ 4 files changed, 542 insertions(+), 8 deletions(-) create mode 100644 scripts/lumber_farm/README.md create mode 100644 scripts/lumber_farm/TreeFarm.lua create mode 100644 scripts/lumber_farm/lumber_farm.lua diff --git a/libs/move_script/movescript.lua b/libs/move_script/movescript.lua index 52f4eac..610d871 100644 --- a/libs/move_script/movescript.lua +++ b/libs/move_script/movescript.lua @@ -7,21 +7,76 @@ Last Modified: 27-09-2018 Description: This library enables string representation of robot movement, for easier robotic control without repeating functions many times. + +Begin a script with "d_" to tell the robot to attempt to destroy blocks in the +way of the path of movement. --]] local r = require("robot") local movescript = {} +local destructive = true + +local function doUntilSuccess(f) + local success = f() + while (not success) do + success = f() + end +end + +local function up() + while (destructive and r.detectUp()) do + r.swingUp() + end + doUntilSuccess(r.up) +end + +local function down() + while (destructive and r.detectDown()) do + r.swingDown() + end + doUntilSuccess(r.down) +end + +local function forward() + while (destructive and r.detect()) do + r.swing() + end + doUntilSuccess(r.forward) +end + +local function back() + if (destructive) then + r.turnAround() + while (r.detect()) do + r.swing() + end + r.turnAround() + end + doUntilSuccess(r.back) +end + local functionMap = { - ["U"] = r.up, - ["D"] = r.down, + ["U"] = up, + ["D"] = down, ["L"] = r.turnLeft, ["R"] = r.turnRight, - ["F"] = r.forward, - ["B"] = r.back + ["F"] = forward, + ["B"] = back, + ["P"] = r.place, + ["S"] = r.swing } +--[[ +Determines if a string starts with a certain string. +str - string: The string to check the prefix of. +start - string: The prefix to look for. +--]] +local function starts_with(str, start) + return str:sub(1, #start) == start +end + --[[ Executes a single instruction once. c - character: One uppercase character to translate into movement. @@ -31,10 +86,7 @@ local function executeChar(c) if (f == nil) then return end - local success = f() - while (not success) do - success = f() - end + f() end --[[ @@ -60,6 +112,12 @@ Executes a given script. script - string: The script to execute. --]] function movescript.execute(script) + if (starts_with(script, "d_")) then + destructive = true + script = string.sub(script, 3) + else + destructive = false + end while (script ~= nil and script ~= "") do -- Matches the next instruction, possibly prefixed by an integer value. local next_instruction = string.match(script, "%d*%u") diff --git a/scripts/lumber_farm/README.md b/scripts/lumber_farm/README.md new file mode 100644 index 0000000..7817ec1 --- /dev/null +++ b/scripts/lumber_farm/README.md @@ -0,0 +1,41 @@ +# lumber_farm.lua +Automatically chop an array of spruce trees. + +## Pastebin +[dB0XwcAY](https://pastebin.com/dB0XwcAY) + +## Module Requirements +* tractor_beam +* inventory_controller +* movescript library [4c2AN8Jw](https://pastebin.com/4c2AN8Jw) +* A lumber axe of obscenely high durability, or unbreakable. + +## Instructions +First, install *movescript* to `/lib/movescript.lua`. + +Then, download this script, and `edit` the downloaded file to set some constants. + +* `ROWS`: The number of rows in the farm. +* `COLS`: The number of columns in the farm. +* `TREE_SPACING`: The number of blocks between trees. +* `DELAY`: The time, in tens of seconds, to wait between chopping and picking up items. +* `move_to_start`: A *movescript* describing how to get from the robot's base station to the first tree. +* `return_from_start`: A *movescript* describing how to get back to the robot's base station from the first tree. Should usually be the opposite of `move_to_start`. + +Make sure you have a very powerful lumber axe, or one which is unbreakable, and give it to the robot. + +### Farm Setup +The construction of the farm should be as follows: + +``` + R-1 R-2 R-3 +| [T] | [T] | [T] | Column 1 +| [T] | [T] | [T] | Column 2 +| [T] | [T] | [T] | Column 3 +| [T] | [T] | [T] | Column 4 + X +``` + +Where `[T]` denotes a 2x2 tree, `X` denotes the starting location for the robot. + +Each tree should be separated from those adjacent to it by `TREE_SPACING` blocks. \ No newline at end of file diff --git a/scripts/lumber_farm/TreeFarm.lua b/scripts/lumber_farm/TreeFarm.lua new file mode 100644 index 0000000..ce9a41f --- /dev/null +++ b/scripts/lumber_farm/TreeFarm.lua @@ -0,0 +1,180 @@ +--[[ +Author: Andrew Lalis +File: TreeFarm.lua +Version: 1.0 +Last Modified: 12-06-2018 + +Description: +This script lets a robot, equipped with a tractor_beam and inventory controller +module, chop trees with a Tinker's Construct lumber axe. It will automatically +stop when it runs out of saplings or bonemeal, or when its axe is close to +breaking. It also can either chop a certain number of trees, or simply chop +until its resources are depleted. +--]] + +--Require statements and componenent definitions. +local robot = require("robot") +local component = require("component") +local tractor_beam = component.tractor_beam +local ic = component.inventory_controller + +--Runtime Constants defined for this robot. +local SAPLING_NAME = "minecraft:sapling" +local SAPLING_DATA = 0 +local BONEMEAL_NAME = "minecraft:dye" +local BONEMEAL_DATA = 15 + +--Global configuration variables. +--Flag for if program should run until out of resources. +local continuous = false + +--[[ +Exits the program. +--]] +local function quit() + print("#--------------------------------#") + print("# Tree Chopping Program exited. #") + os.exit() +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 + +--[[ +Select an item, similar to selectItemByName, but if the item cannot be found, +the user will be prompted to add it to the robot's inventory and press enter to +continue. +item_name - string: The id string for an item. +item_data - number: The damage value, or variation of an item. Defaults to zero. +return - nil: If set to be continuous, then if the item cannot be found, then +the program will exit. If not, it will loop until the item is provided by the +user. +--]] +local function selectSafely(item_name, item_data) + local success = selectItemByName(item_name, item_data) + if continuous and not success then + print("Out of "..item_name..", exiting.") + quit() + end + while not success do + print("Cannot find "..item_name.." in inventory. Please add some, and press enter.") + io.read() + success = selectItemByName(item_name, item_data) + end +end + +--[[ +Plants a sapling, and if it can't place one at first, loops until it is +possible. +--]] +local function plantSapling() + selectSafely(SAPLING_NAME, SAPLING_DATA) + local success = robot.place() + while not success do + print("Unable to place the sapling. Please remove any blocks in front of the robot, and press enter.") + io.read() + success = robot.place() + end +end + +--[[ +Repeatedly applies bonemeal to the sapling until either the sapling has grown, +or the robot runs out of bonemeal. +--]] +local function applyBonemeal() + local success, block_type = robot.detect() + while block_type ~= "solid" do + selectSafely(BONEMEAL_NAME, BONEMEAL_DATA) + robot.place() + success, block_type = robot.detect() + end +end + +--[[ +Uses the robot's axe to chop a tree, and quits if the lumber axe provided has +less than 10% durability. +--]] +local function chopTree() + local durability = robot.durability() + if continuous and (durability == nil or durability < 0.1) then + print("Inadequate tool to chop trees, exiting.") + quit() + end + while (durability == nil) or (durability < 0.1) do + print("Please ensure that a lumber axe with at least 10% durability is equipped in the tool slot, and press enter.") + io.read() + durability = robot.durability() + end + robot.swing() +end + +--[[ +Uses the tractor_beam module to repeatedly pick up items until there are no +more to pick up. +--]] +local function pickupItems() + local success = tractor_beam.suck() + while success do + success = tractor_beam.suck() + end +end + +--[[ +Grows a tree by planting a sapling and applying bonemeal until it is grown. +--]] +local function growTree() + plantSapling() + applyBonemeal() +end + +--[[ +The entire cycle of the farm. Grows a tree, harvests the wood, and picks up +the items. +--]] +local function farmTree() + growTree() + chopTree() + os.sleep(2) + pickupItems() +end + +--[[ +Main function in which the iterations of the cycle are performed. +--]] +local function main() + print("# Andrew's Tree Chopping Program #") + print("# Copyright 2018 Andrew Lalis #") + print("#--------------------------------#") + print("Please enter the number of trees to chop, or -1 to chop until out of resources.") + local choice = tonumber(io.read()) + if (choice == nil or choice == -1) then + continuous = true + print(" Chopping trees until out of resources.") + while continuous do + farmTree() + end + else + print(" Chopping "..choice.." trees.") + for i=1,choice do + farmTree() + end + end + quit() +end + +main() \ No newline at end of file diff --git a/scripts/lumber_farm/lumber_farm.lua b/scripts/lumber_farm/lumber_farm.lua new file mode 100644 index 0000000..9d44012 --- /dev/null +++ b/scripts/lumber_farm/lumber_farm.lua @@ -0,0 +1,255 @@ +--[[ +Author: Andrew Lalis +File: lumber_farm.lua +Version: 1.0 +Last Modified: 30-09-2018 + +Description: +This script will automate the farming of large spruce trees, and manages an +array of 2x2 trees which it will replant and collect the saplings of. + +The robot should be given an 'unbreakable' tool with 5 reinforced upgrades. +--]] + +--Require statements and componenent definitions. +local robot = require("robot") +local component = require("component") +local ms = require("movescript") +local tractor_beam = component.tractor_beam +local ic = component.inventory_controller + +local move_to_start = "5F" +local return_from_start = "5B" + +local ROWS = 3 +local COLS = 2 +local TREE_SPACING = 3 +local DELAY = 15 + +-- Global counter. +local TREES_CHOPPED = 0 + +--Runtime Constants defined for this robot. +local SAPLING_NAME = "minecraft:sapling" +local SAPLING_DATA = 1 + +--[[ +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. +min_count - number: The minimum number of items to have. +return - boolean: True if at least one slot contains the item. That slot is now +selected. +--]] +local function selectItemByName(item_name, item_data, min_count) + for i=1,16 do + local stack = ic.getStackInInternalSlot(i) + if (stack ~= nil and stack.name == item_name and stack.damage == item_data and stack.size >= min_count) then + robot.select(i) + return true + end + end + return false +end + +--[[ +Select an item, similar to selectItemByName, but if the item cannot be found, +the user will be prompted to add it to the robot's inventory and press enter to +continue. +item_name - string: The id string for an item. +item_data - number: The damage value, or variation of an item. Defaults to zero. +min_count - number: The minimum number of items to have. +--]] +local function selectSafely(item_name, item_data, min_count) + local success = selectItemByName(item_name, item_data, min_count) + while not success do + print("Cannot find "..min_count.."x "..item_name.." in inventory. Please add some, and press enter.") + io.read() + success = selectItemByName(item_name, item_data, min_count) + end +end + +--[[ +Gets the total number of items with the given data. +item_name - string: The id string for an item. +item_data - number: The damage value, or variation of an item. Defaults to zero. +--]] +local function getItemCount(item_name, item_data) + local count = 0 + for i=1,16 do + local stack = ic.getStackInInternalSlot(i) + if (stack ~= nil and stack.name == item_name and stack.damage == item_data) then + count = count + stack.size + end + end + return count +end + +--[[ +return - bool: True if the tree is grown, false otherwise. +--]] +local function isTreeGrown() + local success, str = robot.detect() + return (success and str == "solid") +end + +--[[ +Uses the tractor_beam module to repeatedly pick up items until there are no +more to pick up. +--]] +local function pickupItems() + local success = tractor_beam.suck() + while success do + success = tractor_beam.suck() + end +end + +--[[ +Uses the robot's axe to chop a tree, and quits if the lumber axe provided has +less than 10% durability. +return - integer: 1 if the tree was chopped, 0 otherwise. +--]] +local function chopTree() + if (isTreeGrown()) then + local durability = robot.durability() + while (durability == nil) or (durability < 0.1) do + print("Please ensure that a lumber axe with at least 10% durability is equipped in the tool slot, and press enter.") + io.read() + durability = robot.durability() + end + ms.execute("SF") + os.sleep(1) + pickupItems() + ms.execute("B") + return 1 + end + return 0 +end + +--[[ +Plants a sapling, and if it can't place one at first, loops until it is +possible. Assumes the robot is at the starting position: +OO +OO +R +Where O=dirt, R=robot. +--]] +local function plantTree() + --Pick up any remaining items. + pickupItems() + local success, str = robot.detect() + if (success and (str == "passable" or str == "solid" or str == "replaceable")) then + return + end + selectSafely(SAPLING_NAME, SAPLING_DATA, 1) + ms.execute("2FRP") + selectSafely(SAPLING_NAME, SAPLING_DATA, 1) + ms.execute("LBP") + selectSafely(SAPLING_NAME, SAPLING_DATA, 1) + ms.execute("RP") + selectSafely(SAPLING_NAME, SAPLING_DATA, 1) + ms.execute("LBP") +end + +--[[ +Moves to the next tree in a row. +current_col - integer: The current column. +col_count - integer: The total number of columns. +--]] +local function moveToNextTree(current_col, col_count) + if (current_col < col_count) then + ms.execute("d_LFR3FRFL"..(TREE_SPACING - 1).."F") + end +end + +--[[ +Moves to the next row. +current_row - integer: The row that was just finished. +row_count - integer: The total number of rows. +col_count - integer: The total number of columns. +--]] +local function moveToNextRow(current_row, row_count, col_count) + local script = "d_LFL"..((TREE_SPACING + 2) * (col_count - 1)).."FLF" + if (current_row < row_count) then + script = script..(TREE_SPACING + 2).."FL" + else + script = script.."L" + end + ms.execute(script) +end + +--[[ +Moves back to the start of the orchard. +row_count - integer: The total number of rows. +--]] +local function moveToOrchardStart(row_count) + ms.execute("d_L"..((TREE_SPACING + 2) * (row_count - 1)).."FR") +end + +--[[ +Performs a function at each tree in the orchard. +rows - integer: The total number of rows. +cols - integer: The total number of columns. +func - function: The function to execute at each position. +--]] +local function doForEachTree(rows, cols, func) + ms.execute(move_to_start) + for i=1,rows do + for k=1,cols do + func() + moveToNextTree(k, cols) + end + moveToNextRow(i, rows, cols) + end + moveToOrchardStart(rows) + ms.execute(return_from_start) +end + +--[[ +Chops an array of trees. The robot starts facing the first row. +--]] +local function chopOrchard(rows, cols) + doForEachTree(rows, cols, chopTree) +end + +--[[ +Collects items from an array of trees. +--]] +local function collectItems(rows, cols) + doForEachTree(rows, cols, pickupItems) +end + +--[[ +Plants saplings for the array of trees. +--]] +local function plantSaplings(rows, cols) + local saplings_needed = TREES_CHOPPED * 4 + if (getItemCount(SAPLING_NAME, SAPLING_DATA) < saplings_needed) then + print("Not enough saplings. Needed: "..saplings_needed..". Add some and press ENTER.") + io.read() + end + doForEachTree(rows, cols, plantTree) + TREES_CHOPPED = 0 +end + +--[[ +Deposits all items into a chest below the robot. +--]] +local function depositItems() + for i=1,16 do + robot.select(i) + robot.dropDown() + end + robot.select(1) +end + +chopOrchard(ROWS, COLS) +depositItems() +print("Orchard chopped. Waiting 2.5 min before collecting saplings...") +for i=1,(DELAY) do + os.sleep(10) + print(i*10) +end +collectItems(ROWS, COLS) +plantSaplings(ROWS, COLS) +depositItems()