yumetodo

cc_replacer.lua

May 13th, 2021 (edited)
432
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- 指定した方向のブロックをcompare_fnが返すスロットのアイテムと置換する
  2. ---@param direction integer 方向。0=正面,1=上,2=下,3=右,4=左
  3. ---@param compare_fn "fun(re: boolean, v: table|string, direction: number):integer" 比較関数。inspectの結果(bool, any)が渡される。設置に使用するアイテムのあるスロット番号か設置しない場合nilを返す
  4. local function replace(direction, compare_fn)
  5.     ---@param inspect_fn "fun():boolean, table|string"
  6.     ---@param dig_fn "fun(toolSide:? integer): boolean, string|nil"
  7.     ---@param place_fn any
  8.     ---@return boolean
  9.     ---@return string|nil
  10.     local function impl(inspect_fn, dig_fn, place_fn)
  11.         local inspect_re, inspect_table = inspect_fn()
  12.         local use_slot = compare_fn(inspect_re, inspect_table, direction)
  13.         if not (nil ~= use_slot) then
  14.             print("replace:impl:: skip replace", use_slot)
  15.             return true
  16.         end
  17.         -- 砂のような落下してくるブロック対策
  18.         repeat
  19.             local dig_re, dig_message = dig_fn()
  20.             inspect_re, inspect_table = inspect_fn()
  21.             print(dig_re, inspect_re)
  22.             -- 空気ブロックの採掘にエラーを出さない
  23.             if (not dig_re) and (inspect_re) then
  24.                 print("replace:impl:: unexpected block!")
  25.                 return false, dig_message
  26.             end
  27.         until inspect_re == false
  28.         local slot = turtle.getSelectedSlot() -- 退避
  29.         turtle.select(use_slot)
  30.         local place_re, place_message = place_fn()
  31.         turtle.select(slot) -- recover
  32.         if not place_re then
  33.             print("replace:impl:: fail to place")
  34.             return false, place_message
  35.         end
  36.         print("replace:impl:: return true")
  37.         return true
  38.     end
  39.     if (direction == 0) then
  40.         return impl(turtle.inspect, turtle.dig, turtle.place)
  41.     elseif (direction == 1) then
  42.         return impl(turtle.inspectUp, turtle.digUp, turtle.placeUp)
  43.     elseif (direction == 2) then
  44.         return impl(turtle.inspectDown, turtle.digDown, turtle.placeDown)
  45.     elseif (direction == 3) then
  46.         turtle.turnRight()
  47.         local re, message = impl(turtle.inspect, turtle.dig, turtle.place)
  48.         turtle.turnLeft()
  49.         return re, message
  50.     elseif (direction == 4) then
  51.         turtle.turnLeft()
  52.         local re, message = impl(turtle.inspect, turtle.dig, turtle.place)
  53.         turtle.turnRight()
  54.         return re, message
  55.     end
  56.     return false, string.format("unknown direction: %d", direction)
  57. end
  58. local function turn_back()
  59.     turtle.turnLeft()
  60.     turtle.turnLeft()
  61. end
  62. -- 通路断面を左下から初めて時計回りに置換する
  63. ---@param height integer 通路の高さ
  64. ---@param width integer 通路の幅
  65. ---@param compare_fn "fun(re: boolean, v: table|string):integer" 比較関数。inspectの結果(bool, any)が渡される。設置に使用するアイテムのあるスロット番号か設置しない場合nilを返す
  66. local function ring_replace(height, width, compare_fn)
  67.     ---@param direction integer 方向。0=正面,1=上,2=下,3=右,4=左
  68.     local function impl(direction, size, detect_fn, dig_fn, move_fn, detect_cond_fn)
  69.         print(string.format("ring_replace:impl %d", size))
  70.         for i = 1, size do
  71.             local re, message = replace(direction, compare_fn)
  72.             if not re then return re, message end
  73.             if (i == size) then
  74.                 print("ring_replace:impl:: chnage direction")
  75.                 return true
  76.             end
  77.             if detect_fn() then
  78.                 dig_fn()
  79.             end
  80.             print(string.format("ring_replace:impl:: i: %d(%s), size: %d(%s)", i, type(i), size, type(size)))
  81.             re, message = move_fn()
  82.             if not re then return re, message end
  83.         end
  84.         return true
  85.     end
  86.     local re, message = impl(0, height, turtle.detectUp, turtle.digUp, turtle.up)
  87.     if not re then return re, message end
  88.     turn_back()
  89.     re, message = impl(1, width, turtle.detect, turtle.dig, turtle.forward)
  90.     if not re then return re, message end
  91.     re, message = impl(0, height, turtle.detectDown, turtle.digDown, turtle.down)
  92.     if not re then return re, message end
  93.     turn_back()
  94.     re, message = impl(2, width, turtle.detect, turtle.dig, turtle.forward)
  95.     if not re then return re, message end
  96.     return true
  97. end
  98.  
  99. -- 通路壁面を置換する。障害物は採掘される。左下壁面を向いている状態でスタート
  100. ---@param height integer 通路の高さ
  101. ---@param width integer 通路の幅
  102. ---@param depth integer 通路の奥行き
  103. ---@param compare_fn "fun(re: boolean, v: table|string):integer" 比較関数。inspectの結果(bool, any)が渡される。設置に使用するアイテムのあるスロット番号か設置しない場合nilを返す
  104. local function tunnel_replacer(height, width, depth, compare_fn)
  105.     for i = 1, depth do
  106.         local re, message = ring_replace(height, width, compare_fn)
  107.         if not re then return re, message end
  108.         turtle.turnRight()
  109.         if turtle.detect() then
  110.             print("tunnel_replacer:: block detect")
  111.             re, message = turtle.dig()
  112.             if not re then return re, message end
  113.         end
  114.         re, message = turtle.forward()
  115.         if not re then return re, message end
  116.         turtle.turnLeft()
  117.     end
  118.     return true
  119. end
  120.  
  121. -- 燃料の総消費量を計算する
  122. ---@param height integer 通路の高さ
  123. ---@param width integer 通路の幅
  124. ---@param depth integer 通路の奥行き
  125. local function calc_total_fuel_consumption(height, width, depth)
  126.     return (height * 2 + (width - 2) * 2) * depth
  127. end
  128.  
  129. -- 燃料の確認。条件を満たさなければassert
  130. ---@param required integer 必要燃料消費量
  131. local function assert_fuel(required)
  132.     assert(required <= turtle.getFuelLimit(), string.format("fuel limit exceed.(limit: %d, required: %d)", turtle.getFuelLimit(), required))
  133.     assert(required <= turtle.getFuelLevel(), string.format("not enough fuel.(current: %d, required: %d)", turtle.getFuelLevel(), required))
  134. end
  135.  
  136. local function assert_inventory()
  137.     local slot_1 = turtle.getItemDetail(1)
  138.     assert((slot_1 ~= nil) and (slot_1.name == "minecraft:stone"), string.format("unexpected inventory item detected.(expected: minecraft:stone, actual: %s)", slot_1.name))
  139.     local slot_2 = turtle.getItemDetail(2)
  140.     assert((slot_2 ~= nil) and (slot_2.name == "minecraft:glass"), string.format("unexpected inventory item detected.(expected: minecraft:glass, actual: %s)", slot_2.name))
  141. end
  142. ---焼石とガラスのあるスロットを列挙する
  143. ---@return integer[]
  144. ---@return integer[]
  145. local function init_inventory_serch()
  146.     assert_inventory()
  147.     local init_slot = turtle.getSelectedSlot() -- 退避
  148.     ---@type integer[]
  149.     local stone_slots = { 1 }
  150.     ---@type integer[]
  151.     local glass_slots = { 2 }
  152.     for i = 3, 16 do
  153.         turtle.select(i)
  154.         if turtle.compareTo(1) then
  155.             print(string.format("slot %d is stone", i))
  156.             table.insert(stone_slots, i)
  157.         end
  158.         if turtle.compareTo(2) then
  159.             print(string.format("slot %d is glass", i))
  160.             table.insert(glass_slots, i)
  161.         end
  162.     end
  163.     return stone_slots, glass_slots
  164. end
  165.  
  166. ---@class InspectResultState
  167. ---@field variant string
  168.  
  169. ---@class InspectResult
  170. ---@field state InspectResultState|nil
  171. ---@field name string
  172. ---@field metadata integer
  173.  
  174. -------------------------------------------------------------
  175.  
  176. local height, width, depth = ...
  177. assert_fuel(calc_total_fuel_consumption(tonumber(height), tonumber(width), tonumber(depth)))
  178. local stone_slots, glass_slots = init_inventory_serch()
  179.  
  180. function table_back(t)
  181.     return t[#t]
  182. end
  183.  
  184. function wait_block_supply_and_select(get_item_slots)
  185.     if #get_item_slots() == 1 and turtle.getItemCount(table_back(get_item_slots())) < 2 then
  186.         print(string.format("please add %s to turtle inventry", turtle.getItemDetail(get_item_slots()[0]).name))
  187.     end
  188.     while #get_item_slots() == 1 and turtle.getItemCount(table_back(get_item_slots())) < 2 do
  189.         print(string.format("waiting %s...(slot: %d, count: %d)", turtle.getItemDetail(table_back(get_item_slots())).name, table_back(get_item_slots()), #get_item_slots()))
  190.         stone_slots, glass_slots = init_inventory_serch()
  191.         sleep(1)
  192.     end
  193.     -- 最後の一つのアイテムは残す
  194.     if turtle.getItemCount(table_back(get_item_slots())) == (#get_item_slots() == 1 and 1 or 0) then
  195.         table.remove(get_item_slots(), #get_item_slots())
  196.     end
  197.     return table_back(get_item_slots())
  198. end
  199.  
  200. ---replace_cond
  201. ---@param i_re boolean
  202. ---@param i_v InspectResult|string
  203. ---@param direction number
  204. ---@return integer|nil
  205. function replace_cond(i_re, i_v, direction)
  206.     if (not i_re) or (not i_v.name) then
  207.         -- 床は石にする
  208.         return wait_block_supply_and_select(function() return direction == 2 and stone_slots or glass_slots end)
  209.     end
  210.     if ((i_v.name == "minecraft:stone") and (i_v.state.variant == "stone")) or (i_v.name == "minecraft:glass") then
  211.         return nil
  212.     end
  213.     return wait_block_supply_and_select(function() return stone_slots end)
  214. end
  215.  
  216. tunnel_replacer(tonumber(height), tonumber(width), tonumber(depth), replace_cond)
  217.  
RAW Paste Data