iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
Software Development

在麥塊的農場裡寫 Lua系列 第 28

Day28 小烏龜自動掘井挖隧道

介紹過 CC: Tweaked Turtle 的特性和指令後
今天來直接看看,怎麼讓 Mining Turtle 挖礦龜為我們工作
假設你還不知道怎麼寫程式讓 Turtle 工作
可以先參考在 /rom/programs/turtle 有兩支專為挖礦龜寫好的指令
excavate.lua 和 tunnel.lua
tunnel 是用來挖隧道用的,隧道寬度固定為 3 格,高度固定為 2 格,長度可自訂
例如輸入

tunnel 5

就是讓小龜往前挖出 寬3 x 高2 x 長5 的隧道,其中長度 5 是從目前小龜所在那一格開始算起
這個程式很方便我們快速挖出一條通道
而如果你嫌高度 2 格不夠怎辦?
那就放兩隻小龜同時挖啊 XD
接下來看看 tunnel.lua 的程式碼

local collected = 0

local function collect()
    collected = collected + 1
    if math.fmod(collected, 25) == 0 then
        print("Mined " .. collected .. " items.")
    end
end

每次開挖,都會呼叫 collect(),計算目前挖到的物品總數,並且每 25 個回報一次

local function tryDig()
    while turtle.detect() do
        if turtle.dig() then
            collect()
            sleep(0.5)
        else
            return false
        end
    end
    return true
end

挖掘函數有 tryDig, tryDigUp, tryDigDown 我們看 tryDig 就夠了
turtle.detect() 就是判斷前方是否可以挖,回傳 true / false,相關函數有 turtle.detectUp(), turtle.detectDown()
turtle.dig() 當然就是挖掘前面方塊了,回傳 true / false,相關函數有 turtle.digUp(), turtle.digDown()

local function refuel()
    local fuelLevel = turtle.getFuelLevel()
    if fuelLevel == "unlimited" or fuelLevel > 0 then
        return
    end

    local function tryRefuel()
        for n = 1, 16 do
            if turtle.getItemCount(n) > 0 then
                turtle.select(n)
                if turtle.refuel(1) then
                    turtle.select(1)
                    return true
                end
            end
        end
        turtle.select(1)
        return false
    end

    if not tryRefuel() then
        print("Add more fuel to continue.")
        while not tryRefuel() do
            os.pullEvent("turtle_inventory")
        end
        print("Resuming Tunnel.")
    end
end

refuel 就是補充燃料了
首先判斷是否有足夠燃料,不夠的話才會試著尋找儲物箱內,是否有適合當燃料的物品
可以看出補充燃料的方式,就是要先選擇某一格
turtle.select(n)
然後 turtle.refuel(1),這裡設計成一次只補充一份,因為我們不知道到底還有多少燃料可以拿來用

然後當燃料不夠,就會持續等著物品放進小烏龜儲物箱的事件 turtle_inventory 發生
此時會再次試著補充讓料

local function tryUp()
    refuel()
    while not turtle.up() do
        if turtle.detectUp() then
            if not tryDigUp() then
                return false
            end
        elseif turtle.attackUp() then
            collect()
        else
            sleep(0.5)
        end
    end
    return true
end

再來看看 tryUp,相關函數有 tryDown, tryForward
try 函數每次都先補充燃料確保可以動作
接下來 turtle.up() 試著往上移動
如果失敗了,就用 turtle.detectUp() 判斷上方是否有方塊?如果有,就試著挖開
如果不是方塊,那麼就可能是怪物了,就試著攻擊

最後來看看主程式

for n = 1, length do
    turtle.placeDown()
    tryDigUp()
    turtle.turnLeft()
    tryDig()
    tryUp()
    tryDig()
    turtle.turnRight()
    turtle.turnRight()
    tryDig()
    tryDown()
    tryDig()
    turtle.turnLeft()

    if n < length then
        tryDig()
        if not tryForward() then
            print("Aborting Tunnel.")
            break
        end
    else
        print("Tunnel complete.")
    end

end

turtle.placeDown() 一開始先在下方放置任意方塊,相關函數有 place(), placeUp()
接下來挖掘方塊的順序就是上、左、左上、右上、右下
然後轉正準備挖下一個深度,如果這時侯挖掘失敗了,就會中斷,否則就會一直挖開到玩家指定的長度

看完 tunnel.lua 之後,其實就幾乎學會小烏龜 80% 的函數了!
足以撰寫大部分的挖掘指令
這是實際挖隧道時小烏龜的電腦畫面
CC: Tweaked Mining Turtle tunnel

小烏龜掘深井

接下來看看 excavate.lua
tunnel.lua 是往前挖出隧道,excavate.lua 則是往下挖井,長寬為玩家自訂
會挖得非常深,直達地底遇到基岩才停止
接著就會自動往上移動回到地面上

這是執行過程小烏龜的電腦畫面
CC: Tweaked Mining Turtle excavate

假設輸入

excavate 3

就是往下挖出 3 x 3 的深井直達地底
來看看 excavate 的程式碼

local function unload(_bKeepOneFuelStack)
    print("Unloading items...")
    for n = 1, 16 do
        local nCount = turtle.getItemCount(n)
        if nCount > 0 then
            turtle.select(n)
            local bDrop = true
            if _bKeepOneFuelStack and turtle.refuel(0) then
                bDrop = false
                _bKeepOneFuelStack = false
            end
            if bDrop then
                turtle.drop()
                unloaded = unloaded + nCount
            end
        end
    end
    collected = 0
    turtle.select(1)
end

這是小烏龜挖井結束後,返回到地面時,把挖到的東西都「吐」出來的函數
就是這個畫面 XD
CC: Tweaked Mining Turtle excavate finish
這段程式碼邏輯其實很簡單,就是把小烏龜儲物箱的16格一一檢查過
如果有東西就 drop 到地上
而中間有一段用到 _bKeepOneFuelStack 變數,是告訴小烏龜卸貨時,是否保留一格燃料,畢竟燃料還要用嘛!

local function returnSupplies()
    local x, y, z, xd, zd = xPos, depth, zPos, xDir, zDir
    print("Returning to surface...")
    goTo(0, 0, 0, 0, -1)

    local fuelNeeded = 2 * (x + y + z) + 1
    if not refuel(fuelNeeded) then
        unload(true)
        print("Waiting for fuel")
        while not refuel(fuelNeeded) do
            os.pullEvent("turtle_inventory")
        end
    else
        unload(true)
    end

    print("Resuming mining...")
    goTo(x, y, z, xd, zd)
end

這個 returnSupplies 函數則是讓小烏龜「回到」地面後,等待更多燃料並卸貨
然後回到原本的位置繼續開挖
從下面 tryForwards(), tryDown() 可以看出,小烏龜有兩種情況會做這件事

  1. 燃料不夠
  2. 儲物箱滿了(所以必須回到地面把東西吐出來)

goTo() 則很明顯是直接移動小烏龜到指定位置的函數,用的是相對座標
goTo(0, 0, 0, 0, 1) 便是回到一開始的位置和方向

最後來個掘井畫面
CC: Tweaked Mining Turtle excavate

以上是今天對 Mining Turtle 的分享
下一回我會繼續探索更多小烏龜的操作~


上一篇
Day27 小烏龜動工的基本指令集
下一篇
Day29 自動合成物品的小烏龜與指令
系列文
在麥塊的農場裡寫 Lua30

尚未有邦友留言

立即登入留言