3

I have a program for a turtle in ComputerCraft that is meant to be a storage unit. I input an item, and it should read a file to find where to put the item. If there is a new item, it adds the information to a file. When I do

blockTypes = {}

local file = fs.open(blockTable","r")
line = file.readAll()
table.insert(blockTypes,line)
file.close()

The information gets put in as a string. This means that I can't do

print(blockTypes[1][1])

And recieve the value that would normally be in that position.

Here's my code:

blockAmount = 3
highestVal = {2,0,5}
blockTypes = {}
function addBlock()
    local file = fs.open("blockTable", "a")
    file.write(name)
    file.write(" = {")
    file.write(highestVal[1])
    file.write(",")
    file.write(highestVal[2])
    file.write(",")
    file.write(highestVal[3])
    file.writeLine("};")
    file.close()
end
function getBlock()
    local file = fs.open("blockTable","r")
    line = file.readAll()
    table.insert(blockTypes,line)
    file.close()
end
--This was used to test before implementing the new file system that I am trying to get to work
blockTypesOld = {
    log = {2.0,0,1};
    dirt = {2,0,2};
    cobblestone = {2,0,3};
    iron_ingot = {2,0,4};
    planks = {2,0,5};
}
pos = {0,0,0}
looking = 0
function fuel()
    if turtle.getFuelLevel() < 20 then
        turtle.select(16)
        turtle.refuel(1)
    end
end
function left()
    turtle.turnLeft()
    looking = looking - 1
    if looking < 0 then
        looking = 3
    end
end
function right()
    turtle.turnRight()
    looking = looking + 1
    if looking > 3 then
        looking = 0
    end
end
function forward()
    fuel()
        if turtle.forward() then
            if looking == 0 then
                pos[1] = pos[1] - 1
            elseif looking == 1 then
                pos[3] = pos[3] - 1 
            elseif looking == 2 then
                pos[1] = pos[1] + 1
            elseif looking == 3 then
                pos[3] = pos[3] + 1
            else
            end
        end

end
function up()
    fuel()
    turtle.up()
    pos[2] = pos[2] + 1
end
function down()
    fuel()
    turtle.down()
    pos[2] = pos[2] - 1
end
function goHome()
    while pos[3] > 0 do
        while  looking > 1 do
            left()
        end
        forward()
    end
    while  pos[2] > 0 do
        down()
    end
    while  pos[1] > 0 do
        while  looking > 0 do
            left()
        end
        forward()
    end
end
function goTo(a,b,c)
    goHome()
    while looking < 2 or looking > 2 do
        right()
    end
    for i = pos[1],a do
        forward()
    end
    while looking > 3 or looking < 3 do
        right()
    end
    for i = pos[3],c do
        forward()
    end
    for i = pos[2],b do
        up()
    end
    while looking < 2 or looking > 2 do
        left()
    end
end
while true do
    turtle.select(15)
    while not turtle.suck() do
        sleep(1)
    end
    itemDetails = turtle.getItemDetail()
    --Finding what mod item is from and removing corresponding labels--
    --EX: "minecraft:log" becomes "log"
    if itemDetails.name:match("^ae2stuff:(.+)$") then
        name = itemDetails.name:match("^ae2stuff:(.+)$")
    elseif itemDetails.name:match("^minecraft:(.+)$") then
        name = itemDetails.name:match("^minecraft:(.+)$")
    elseif itemDetails.name:match("^appliedenergistics2:(.+)$") then
        name = itemDetails.name:match("^appliedenergistics2:(.+)$")
    elseif itemDetails.name:match("^buildcraftbuilders:(.+)$") then
        name = itemDetails.name:match("^buildcraftbuilders:(.+)$")
    elseif itemDetails.name:match("^forge:(.+)$") then
        name = itemDetails.name:match("^forge:(.+)$")
    elseif itemDetails.name:match("^buildcraftenergy:(.+)$") then
        name = itemDetails.name:match("^buildcraftenergy:(.+)$")
    elseif itemDetails.name:match("^buildcraftfactory:(.+)$") then
        name = itemDetails.name:match("^buildcraftfactory:(.+)$")
    elseif itemDetails.name:match("^buildcraftsilicon:(.+)$") then
        name = itemDetails.name:match("^buildcraftsilicon:(.+)$")
    elseif itemDetails.name:match("^buildcrafttransport:(.+)$") then
        name = itemDetails.name:match("^buildcrafttransport:(.+)$")
    elseif itemDetails.name:match("^buildcraftcore:(.+)$") then
        name = itemDetails.name:match("^buildcraftcore:(.+)$")
    elseif itemDetails.name:match("^buildcraftlib:(.+)$") then
        name = itemDetails.name:match("^buildcraftlib:(.+)$")
    elseif itemDetails.name:match("^computercraft:(.+)$") then
        name = itemDetails.name:match("^computercraft:(.+)$")
    elseif itemDetails.name:match("^enderstorage:(.+)$") then
        name = itemDetails.name:match("^enderstorage:(.+)$")
    elseif itemDetails.name:match("^extracells:(.+)$") then
        name = itemDetails.name:match("^extracells:(.+)$")
    elseif itemDetails.name:match("^thermaldynamics:(.+)$") then
        name = itemDetails.name:match("^thermaldynamics:(.+)$")
    elseif itemDetails.name:match("^thermalexpansion:(.+)$") then
            name = itemDetails.name:match("^thermalexpansion:(.+)$")
    elseif itemDetails.name:match("^thermalfoundation:(.+)$") then
        name = itemDetails.name:match("^thermalfoundation:(.+)$")
    elseif itemDetails.name:match("^tconstruct:(.+)$") then
        name = itemDetails.name:match("^tconstruct:(.+)$")
    elseif itemDetails.name:match("^webdisplays:(.+)$") then
        name = itemDetails.name:match("^webdisplays:(.+)$")
    elseif itemDetails.name:match("^ironchest:(.+)$") then
        name = itemDetails.name:match("^ironchest:(.+)$")
    else
        print("ERROR MOD NOT FOUND")
    end
    getBlock()
    local elem = blockTypes[name]
    --Gets fuel from fuel chest
    right()
    turtle.select(16)
    turtle.suck(5)
    turtle.select(15)
    if elem then
    --If the item does exist, the turtle goes to it's chest and places it in the chest
        goTo(elem[1]-1, elem[2]-1, elem[3]-1)
        turtle.select(15)
        turtle.drop()
        goHome()
        right()
        turtle.select(16)
        turtle.drop()
        turtle.select(15)
        left()
    else
        --Creates information for new item--
        addBlock()
        blockAmount = blockAmount + 1
        highestVal[3] = highestVal[3] + 1
        if highestVal[3] > 5 then
            highestVal[3] = 1
            highestVal[2] = highestVal[2] + 1
        end
        if highestVal[2] > 4 then
            highestVal[2] = 0
            highestVal[1] = highestVal[1] + 2
        end
        blockTypes[blockAmount] = name
        blockTypes[name] = {highestVal[1],highestVal[2],highestVal[3]}
        local elem = blockTypes[name]
        left()
        turtle.select(15)
        turtle.drop()
        right()
        turtle.select(16)
        turtle.suck(2)
        fuel()
        turtle.drop()

        goTo(1,-1,4)
        --Crafts an Iron Chest for the New Item
        for i = 1,3 do
            turtle.select(i)
            turtle.suck(1)
        end
        turtle.select(5)
        turtle.suck(1)
        turtle.select(7)
        turtle.suck(1)
        for i = 9,11 do
            turtle.select(i)
            turtle.suck(1)
        end
        turtle.select(6)
        turtle.craft()
        goTo(1,-1,3)
        for i = 1,3 do
            turtle.select(i)
            turtle.suck(1)
        end
        turtle.select(5)
        turtle.suck(1)
        turtle.select(7)
        turtle.suck(1)
        for i = 9,11 do
            turtle.select(i)
            turtle.suck(1)
        end
        turtle.select(1)
        turtle.craft()
        goHome()
        right()
        turtle.select(16)
        turtle.suck(5)
        goTo(elem[1]-1, elem[2]-1, elem[3]-1)
        turtle.select(1)
        turtle.place()
        goHome()
    end
end

The file containing the coordinates for the items is called blockTable and consist of this:

--blockName = {xCoord,yCoord,zCoord};--
oak_stairs = {2.0,0.0,5.0};
iron_ingot = {2.0,0.0,4.0};
turtle = {2.0,0.0,5.0};

When I put a new item in, it registers the item, makes a chest, and puts it in. When I put the same item in, it goes straight to the chest without crafting a new one. However, when I reboot the turtle and put the same item in, it creates another new chest and tries to place it for the new item. I want it to be able to keep its information even after a reboot. I've been struggling with this program for a while now, any help will be appreciated. Thank you!

4

2 回答 2

2

Reading / Writing the entire file every time you add a new block seems a bit messy, specially if you consider that you might restart the turtle in the process of writing the new file and end up with half your data being lost.

You do have a file system, so why not use that instead? Create a directory and write a new file for every block name, then save the coordinates in it. This also allows you to store more than one chest for each block-type, in case you get filthy rich happens sooner than you'd expect when using turtles to excavate huge chunks of the map.

I just realized that I didn't actually answer the question, so here goes that:

When you have a file file, you can easily read a single line with file.readLine(). This returns either the line it read as a string, or nil if you're at the end of a file. Assuming you just write three coordinates separated by spaces after the block name, you can then parse them into a table like this:

local file = fs.open('chests')
local chests = {}
while true do
  local line = file.readLine()
  if line then
    local name, x, y, z = line:match("(%a+) ([%d.+-]+) ([%d.+-]+) ([%d.+-]+)")
    chests[name] = {
      tonumber(x),
      tonumber(y),
      tonumber(z)
    }
  else
    break
  end
end

Wrap this all up in a function for extra neatness.

Side note:

Computercraft is somewhat inconvenient for dealing with files. In regular Lua I would have done this:

local function map(f, elem, ...)
    if elem then return f(elem), map(f, ...) end
end

local function readchests(file)
    local res = {}
    for line in io.open(file):lines() do
        local name, x, y, z = line:match("(%a+)"..(" [%d.-+]+"):rep(3))
        res[name]={map(tonumber, x, y, z)}
    end
    return res
end

If you want to keep the format of storing data as valid Lua code, you could read the entire file with file.readAll(), add "return {" at the start and "}" at the end and then just load and execute that string. In general I don't recommend that though. Id't be better if the entire file was just valid Lua code that you can read and run.

于 2019-01-25T13:00:44.193 回答
0

While it is not advisable to read the file every time you add to a new block as stated by DarkWiiPlayer.

To answer your question, you can read the table in and the execute the code from the string using loadstring.

To do this using the existing strings in the file you provided there is some extra work that is needed. This extra work can be removed by adjusting your the string in the file.

blockTypes = {}

line = "oak_stairs = {2.0,0.0,5.0};"
table_name = line:match("[%a_]+%s")
do_line = assert(loadstring('local ' .. line .. ' return ' .. table_name))
table = do_line();
table.insert(blockTypes,table)

Here we get the name of the table being loaded using match. Create a string for loadstring that builds and returns the table. Execute the loaded string, and insert it to the blockTypes.


Alternatively you can adjust the file you are saving to that it acts as a module.

local blocktypes = {
    oak_stairs = {2.0,0.0,5.0},
    iron_ingot = {2.0,0.0,4.0},
    turtle = {2.0,0.0,5.0},
}
return blockTypes

Then you would this to load the data:

blockTypes = require("blockTable")
于 2019-01-25T16:36:09.707 回答