Advertisement
tmkop

Vein Miner

Apr 22nd, 2025
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 35.87 KB | None | 0 0
  1. ---
  2.  
  3. --- "veinm" version 1.0 by Qendolin
  4.  
  5. --- 22.09.2023 for CC: Restiched 1.100.8
  6.  
  7. --- License: MPL 2.0
  8.  
  9. --- Contact: 'Qendolin' on https://forums.computercraft.cc/
  10.  
  11. ---
  12.  
  13. --- Usage: veinm <pattern>... [options...]
  14.  
  15. ---
  16.  
  17. --- Options:
  18.  
  19. ---  -help
  20.  
  21. ---  -junk <junk_pattern>...
  22.  
  23. ---  -safe <width> [height] [depth]
  24.  
  25. ---  -debug <filters>...
  26.  
  27. ---
  28.  
  29. --- Specify at least one <pattern> to match against the block ids which should get mined.
  30.  
  31. --- The turtle will find and mine all connected blocks of these types.
  32.  
  33. --- Example: `veinm granite$ andesite$` will mine all blocks ending with "granite" and "andesite".
  34.  
  35. ---
  36.  
  37. --- Specify zero or more <junk_pattern> to match against item ids in the turtle's inventory
  38.  
  39. ---   which will get tossed out when the turtle is full.
  40.  
  41. --- Example: `-junk stone$` will toss out any items ending with "stone" such as cobblestone.
  42.  
  43. ---
  44.  
  45. --- Specify a safe zone which the turtle will not enter.
  46.  
  47. --- By default the safe zone extends infinitely behind the turtle starting position.
  48.  
  49. --- Any dimension that is not specified is assumed to be infinite.
  50.  
  51. --- Example: `-safe inf 11 10` will exclude an area behind the turtle that
  52.  
  53. ---   is `infinitely` wide, 5 blocks up, 5 blocks down and 10 blocks deep
  54.  
  55. ---
  56.  
  57. --- All patterns use the lua pattern syntax: https://www.lua.org/pil/20.2.html
  58.  
  59. ---
  60.  
  61. --- When the turtle cannot find any more blocks to mine it will return to its starting position.
  62.  
  63. --- It will also return to its starting position when it runs low on fuel.
  64.  
  65. ---
  66.  
  67. --- Setup:
  68.  
  69. --- 1. Place the turtle facing away from a chest. In this chest the turtle will deposit the collected items.
  70.  
  71. --- 2. Make sure a block that you wish to mine is directly in front of the turtle.
  72.  
  73. --- 3. Place enough coal in the first slot of the turtle to fill it 50%. I recommend Blocks of Coal.
  74.  
  75. --- 4. Run `veinm`
  76.  
  77. --- 5. Make sure to keep the turtle loaded!!
  78.  
  79. ---
  80.  
  81.  
  82.  
  83. FuelItem = "coal"
  84.  
  85.  
  86.  
  87.  
  88.  
  89. ---@class Direction
  90.  
  91. ---@field dx number
  92.  
  93. ---@field dy number
  94.  
  95. ---@field dz number
  96.  
  97. ---@field index number
  98.  
  99. ---@field name string
  100.  
  101. Direction = {}
  102.  
  103.  
  104.  
  105. ---
  106.  
  107. ---@param dx number
  108.  
  109. ---@param dy number
  110.  
  111. ---@param dz number
  112.  
  113. ---@param index number
  114.  
  115. ---@return Direction
  116.  
  117. function Direction:new(dx, dy, dz, index, name)
  118.  
  119.     local o = {}
  120.  
  121.     setmetatable(o, self)
  122.  
  123.     self.__index = self
  124.  
  125.     o.dx = dx
  126.  
  127.     o.dy = dy
  128.  
  129.     o.dz = dz
  130.  
  131.     o.index = index
  132.  
  133.     o.name = name
  134.  
  135.     return o
  136.  
  137. end
  138.  
  139.  
  140.  
  141. Directions = {
  142.  
  143.     None = Direction:new(0, 0, 0, -1, "none"),
  144.  
  145.     Forward = Direction:new(1, 0, 0, 0, "+F"),
  146.  
  147.     Right = Direction:new(0, 0, 1, 1, "+R"),
  148.  
  149.     Backward = Direction:new(-1, 0, 0, 2, "-F"),
  150.  
  151.     Left = Direction:new(0, 0, -1, 3, "-R"),
  152.  
  153.     Up = Direction:new(0, 1, 0, -1, "+U"),
  154.  
  155.     Down = Direction:new(0, -1, 0, -1, "-U"),
  156.  
  157. }
  158.  
  159.  
  160.  
  161. Directions[0] = Directions.Forward
  162.  
  163. Directions[1] = Directions.Right
  164.  
  165. Directions[2] = Directions.Backward
  166.  
  167. Directions[3] = Directions.Left
  168.  
  169.  
  170.  
  171. ---
  172.  
  173. ---@param rotation Rotation
  174.  
  175. ---@return Direction
  176.  
  177. function Direction:add(rotation)
  178.  
  179.     if self.index == -1 then
  180.  
  181.         return self
  182.  
  183.     end
  184.  
  185.  
  186.  
  187.     local index = self.index + rotation.index
  188.  
  189.     index = math.fmod(math.fmod(index, 4) + 4, 4)
  190.  
  191.     return Directions[index]
  192.  
  193. end
  194.  
  195.  
  196.  
  197. ---
  198.  
  199. ---@param other Direction
  200.  
  201. ---@return Rotation
  202.  
  203. function Direction:sub(other)
  204.  
  205.     if self.index == -1 or other.index == -1 then
  206.  
  207.         return Rotations.None
  208.  
  209.     end
  210.  
  211.  
  212.  
  213.     local index = other.index - self.index
  214.  
  215.     index = math.fmod(math.fmod(index, 4) + 4, 4)
  216.  
  217.     return Rotations[index]
  218.  
  219. end
  220.  
  221.  
  222.  
  223. ---
  224.  
  225. ---@return string
  226.  
  227. function Direction:toString()
  228.  
  229.     return string.format("%d,%d,%d", self.dx, self.dy, self.dz)
  230.  
  231. end
  232.  
  233.  
  234.  
  235. ---@class Rotation
  236.  
  237. ---@field a number
  238.  
  239. ---@field b number
  240.  
  241. ---@field c number
  242.  
  243. ---@field d number
  244.  
  245. ---@field index number
  246.  
  247. Rotation = {}
  248.  
  249.  
  250.  
  251. ---
  252.  
  253. ---@param a number
  254.  
  255. ---@param b number
  256.  
  257. ---@param c number
  258.  
  259. ---@param d number
  260.  
  261. ---@param index number
  262.  
  263. ---@return Rotation
  264.  
  265. function Rotation:new(a, b, c, d, index)
  266.  
  267.     local o = {}
  268.  
  269.     setmetatable(o, self)
  270.  
  271.     self.__index = self
  272.  
  273.     o.a = a
  274.  
  275.     o.b = b
  276.  
  277.     o.c = c
  278.  
  279.     o.d = d
  280.  
  281.     o.index = index
  282.  
  283.     return o
  284.  
  285. end
  286.  
  287.  
  288.  
  289. Rotations = {
  290.  
  291.     None = Rotation:new(1, 0, 0, 1, 0),
  292.  
  293.     Right = Rotation:new(0, -1, 1, 0, 1),
  294.  
  295.     Reverse = Rotation:new(-1, 0, 0, -1, 2),
  296.  
  297.     Left = Rotation:new(0, 1, -1, 0, 3),
  298.  
  299. }
  300.  
  301.  
  302.  
  303. Rotations[0] = Rotations.None
  304.  
  305. Rotations[1] = Rotations.Right
  306.  
  307. Rotations[2] = Rotations.Reverse
  308.  
  309. Rotations[3] = Rotations.Left
  310.  
  311.  
  312.  
  313. ---@param other Rotation
  314.  
  315. function Rotation:add(other)
  316.  
  317.     local index = self.index + other.index
  318.  
  319.     index = math.fmod(math.fmod(index, 4) + 4, 4)
  320.  
  321.     return Rotations[index]
  322.  
  323. end
  324.  
  325.  
  326.  
  327. ---@class Vector
  328.  
  329. ---@field x number
  330.  
  331. ---@field y number
  332.  
  333. ---@field z number
  334.  
  335. Vector = {
  336.  
  337.     maxx = 1024,
  338.  
  339.     maxy = 512,
  340.  
  341.     maxz = 1024,
  342.  
  343.     minx = -1023,
  344.  
  345.     miny = -511,
  346.  
  347.     minz = -1023,
  348.  
  349. }
  350.  
  351.  
  352.  
  353. ---
  354.  
  355. ---@param x number?
  356.  
  357. ---@param y number?
  358.  
  359. ---@param z number?
  360.  
  361. ---@return Vector
  362.  
  363. function Vector:new(x, y, z)
  364.  
  365.     local o = {}
  366.  
  367.     setmetatable(o, self)
  368.  
  369.     self.__index = self
  370.  
  371.     o.x = x or 0
  372.  
  373.     o.y = y or 0
  374.  
  375.     o.z = z or 0
  376.  
  377.     return o
  378.  
  379. end
  380.  
  381.  
  382.  
  383. Zero = Vector:new()
  384.  
  385.  
  386.  
  387. ---
  388.  
  389. ---@param other Vector
  390.  
  391. ---@return boolean
  392.  
  393. function Vector:equals(other)
  394.  
  395.     if self == nil and other == nil then
  396.  
  397.         return true
  398.  
  399.     end
  400.  
  401.     if self == nil or other == nil then
  402.  
  403.         return false
  404.  
  405.     end
  406.  
  407.     return self.x == other.x and self.y == other.y and self.z == other.z
  408.  
  409. end
  410.  
  411.  
  412.  
  413. ---
  414.  
  415. ---@param other Vector | Direction
  416.  
  417. ---@return Vector
  418.  
  419. function Vector:add(other)
  420.  
  421.     if getmetatable(other) == Direction then
  422.  
  423.         return Vector:new(self.x + other.dx, self.y + other.dy, self.z + other.dz)
  424.  
  425.     end
  426.  
  427.     return Vector:new(self.x + other.x, self.y + other.y, self.z + other.z)
  428.  
  429. end
  430.  
  431.  
  432.  
  433. ---
  434.  
  435. ---@param other Vector | Direction
  436.  
  437. ---@return Vector
  438.  
  439. function Vector:sub(other)
  440.  
  441.     if getmetatable(other) == Direction then
  442.  
  443.         return Vector:new(self.x - other.dx, self.y - other.dy, self.z - other.dz)
  444.  
  445.     end
  446.  
  447.     return Vector:new(self.x - other.x, self.y - other.y, self.z - other.z)
  448.  
  449. end
  450.  
  451.  
  452.  
  453. ---
  454.  
  455. ---@param rotation Rotation
  456.  
  457. ---@return Vector
  458.  
  459. function Vector:rotate(rotation)
  460.  
  461.     local x = self.x * rotation.a + self.z * rotation.b
  462.  
  463.     local z = self.x * rotation.c + self.z * rotation.d
  464.  
  465.     return Vector:new(x, self.y, z)
  466.  
  467. end
  468.  
  469.  
  470.  
  471. ---
  472.  
  473. ---@param direction Direction
  474.  
  475. ---@return Vector
  476.  
  477. function Vector:relative(direction)
  478.  
  479.     return self:add(direction)
  480.  
  481. end
  482.  
  483.  
  484.  
  485. ---
  486.  
  487. ---@param other Vector
  488.  
  489. ---@return number
  490.  
  491. function Vector:distance(other)
  492.  
  493.     local dx = self.x - other.x
  494.  
  495.     local dy = self.y - other.y
  496.  
  497.     local dz = self.z - other.z
  498.  
  499.     return math.sqrt(dx * dx + dy * dy + dz * dz)
  500.  
  501. end
  502.  
  503.  
  504.  
  505. ---
  506.  
  507. ---@param other Vector
  508.  
  509. ---@return number
  510.  
  511. function Vector:distanceSquare(other)
  512.  
  513.     local dx = self.x - other.x
  514.  
  515.     local dy = self.y - other.y
  516.  
  517.     local dz = self.z - other.z
  518.  
  519.     return dx * dx + dy * dy + dz * dz
  520.  
  521. end
  522.  
  523.  
  524.  
  525. ---
  526.  
  527. ---@param other Vector
  528.  
  529. ---@return number
  530.  
  531. function Vector:distanceManhattan(other)
  532.  
  533.     local dx = self.x - other.x
  534.  
  535.     local dy = self.y - other.y
  536.  
  537.     local dz = self.z - other.z
  538.  
  539.     return math.abs(dx) + math.abs(dy) + math.abs(dz)
  540.  
  541. end
  542.  
  543.  
  544.  
  545. ---packs the vector into a 32bit int
  546.  
  547. ---the allowed x and z range is -1023 to 1024
  548.  
  549. ---the allowed y range is -511 to 512
  550.  
  551. ---@alias VectorKey number
  552.  
  553. ---@return VectorKey
  554.  
  555. function Vector:toKey()
  556.  
  557.     local k = 0
  558.  
  559.     k = bit.bor(k, bit32.lshift(bit32.band(self.x, 0x7FF), 21))
  560.  
  561.     k = bit.bor(k, bit32.lshift(bit32.band(self.z, 0x7FF), 10))
  562.  
  563.     k = bit.bor(k, bit.band(self.y, 0x3FF), 16)
  564.  
  565.     return k
  566.  
  567. end
  568.  
  569.  
  570.  
  571. ---
  572.  
  573. ---@return string
  574.  
  575. function Vector:toString()
  576.  
  577.     return string.format("%d,%d,%d", self.x, self.y, self.z)
  578.  
  579. end
  580.  
  581.  
  582.  
  583. ---@alias QueueState "unknown" | "queued" | "visited" | "irrelevant" | "avoid"
  584.  
  585.  
  586.  
  587. ---@class TargetMap
  588.  
  589. ---@field states table<VectorKey, QueueState>
  590.  
  591. ---@field queue table<VectorKey, Vector>
  592.  
  593. ---@field queuedCount number
  594.  
  595. ---@field visitedCount number
  596.  
  597. ---@field mindedCount number
  598.  
  599. ---@field recent VectorKey[]
  600.  
  601. ---@field recentIndex number
  602.  
  603. TargetMap = {
  604.  
  605.     states = {},
  606.  
  607.     queue = {},
  608.  
  609.     queuedCount = 0,
  610.  
  611.     visitedCount = 0,
  612.  
  613.     minedCount = 0,
  614.  
  615.     recent = {},
  616.  
  617.     recentIndex = 0
  618.  
  619. }
  620.  
  621.  
  622.  
  623. --
  624.  
  625. ---@return TargetMap
  626.  
  627. function TargetMap:new()
  628.  
  629.     local o = {}
  630.  
  631.     setmetatable(o, self)
  632.  
  633.     self.__index = self
  634.  
  635.     o.states = {}
  636.  
  637.     o.queue = {}
  638.  
  639.     o.queuedCount = 0
  640.  
  641.     o.visitedCount = 0
  642.  
  643.     o.minedCount = 0
  644.  
  645.     o.discoveredCount = 0
  646.  
  647.     o.recent = {}
  648.  
  649.     o.recentIndex = 0
  650.  
  651.     return o
  652.  
  653. end
  654.  
  655.  
  656.  
  657. ---
  658.  
  659. ---@param pos Vector
  660.  
  661. ---@return QueueState
  662.  
  663. function TargetMap:get(pos)
  664.  
  665.     local state = self.states[pos:toKey()]
  666.  
  667.     if state == nil then
  668.  
  669.         return "unknown"
  670.  
  671.     end
  672.  
  673.     return state
  674.  
  675. end
  676.  
  677.  
  678.  
  679. ---
  680.  
  681. ---@param pos Vector
  682.  
  683. ---@param state QueueState
  684.  
  685. function TargetMap:set(pos, state)
  686.  
  687.     local key = pos:toKey()
  688.  
  689.     local prev = self.states[key]
  690.  
  691.     self.states[key] = state
  692.  
  693.     if state == "queued" and prev ~= "queued" then
  694.  
  695.         self.queue[key] = pos
  696.  
  697.         self.recent[self.recentIndex] = key
  698.  
  699.         self.recentIndex = math.fmod(self.recentIndex + 1, 4)
  700.  
  701.         self.queuedCount = self.queuedCount + 1
  702.  
  703.     end
  704.  
  705.     if state ~= "queued" and prev == "queued" then
  706.  
  707.         self.queue[key] = nil
  708.  
  709.         self.queuedCount = self.queuedCount - 1
  710.  
  711.     end
  712.  
  713.     if state == "visited" and prev ~= "visited" then
  714.  
  715.         self.visitedCount = self.visitedCount + 1
  716.  
  717.     end
  718.  
  719.     if state ~= nil and prev == nil then
  720.  
  721.         self.discoveredCount = self.discoveredCount + 1
  722.  
  723.     end
  724.  
  725.     if state == "visited" and prev == "queued" then
  726.  
  727.         self.minedCount = self.minedCount + 1
  728.  
  729.     end
  730.  
  731. end
  732.  
  733.  
  734.  
  735. ---
  736.  
  737. ---@param from Vector
  738.  
  739. ---@param facing Direction
  740.  
  741. ---@return Vector | nil
  742.  
  743. function TargetMap:findNextTarget(from, facing)
  744.  
  745.     -- look in recent additions first, because there is a high
  746.  
  747.     -- chance that it's right next to us
  748.  
  749.     local bestPos = nil
  750.  
  751.     local bestValue = math.huge
  752.  
  753.     for index = 4, 1, -1 do
  754.  
  755.         local pos = self.queue[self.recent[index]]
  756.  
  757.         if pos ~= nil then
  758.  
  759.             local value = self:evaluateTarget(from, pos, facing)
  760.  
  761.             if value < bestValue then
  762.  
  763.                 bestValue = value
  764.  
  765.                 bestPos = pos
  766.  
  767.             end
  768.  
  769.         end
  770.  
  771.     end
  772.  
  773.     if bestValue <= 1 then
  774.  
  775.         return bestPos
  776.  
  777.     end
  778.  
  779.     for _, pos in pairs(self.queue) do
  780.  
  781.         if pos ~= false then
  782.  
  783.             local value = self:evaluateTarget(from, pos, facing)
  784.  
  785.             if value <= 1 then
  786.  
  787.                 return pos
  788.  
  789.             end
  790.  
  791.  
  792.  
  793.             if value < bestValue then
  794.  
  795.                 bestValue = value
  796.  
  797.                 bestPos = pos
  798.  
  799.             end
  800.  
  801.         end
  802.  
  803.     end
  804.  
  805.     return bestPos
  806.  
  807. end
  808.  
  809.  
  810.  
  811. ---
  812.  
  813. ---@param from Vector
  814.  
  815. ---@param pos Vector
  816.  
  817. ---@param facing Direction
  818.  
  819. ---@return number
  820.  
  821. function TargetMap:evaluateTarget(from, pos, facing)
  822.  
  823.     local value = from:distanceManhattan(pos) + Zero:distance(pos) * 0.5
  824.  
  825.     local discovery = self:evaluateDiscovery(pos, facing)
  826.  
  827.     value = value - discovery * 0.35
  828.  
  829.     if discovery <= 1 and pos.x == from.x + facing.dx and pos.z == from.z + facing.dz and pos.y == from.y then
  830.  
  831.         -- straigt ahead bonus
  832.  
  833.         value = value - 1
  834.  
  835.     end
  836.  
  837.     return value
  838.  
  839. end
  840.  
  841.  
  842.  
  843. ---
  844.  
  845. ---@param pos Vector
  846.  
  847. ---@param facing Direction
  848.  
  849. ---@return number
  850.  
  851. function TargetMap:evaluateDiscovery(pos, facing)
  852.  
  853.     local value = 0
  854.  
  855.     local p = Vector:new(pos.x, pos.y + 1, pos.z)
  856.  
  857.     if self.states[p:toKey()] == nil then
  858.  
  859.         value = value + 1
  860.  
  861.     end
  862.  
  863.  
  864.  
  865.     p.y = pos.y - 1
  866.  
  867.     if self.states[p:toKey()] == nil then
  868.  
  869.         value = value + 1
  870.  
  871.     end
  872.  
  873.  
  874.  
  875.     p.y = pos.y
  876.  
  877.     p.x = pos.x + facing.dx
  878.  
  879.     p.z = pos.z + facing.dz
  880.  
  881.     if self.states[p:toKey()] == nil then
  882.  
  883.         value = value + 1
  884.  
  885.     end
  886.  
  887.  
  888.  
  889.     return value
  890.  
  891. end
  892.  
  893.  
  894.  
  895. ---@class WriteHandle
  896.  
  897. ---@field write fun(text: string)
  898.  
  899. ---@field writeLine fun(text: string)
  900.  
  901. ---@field flush fun()
  902.  
  903. ---@field close fun()
  904.  
  905.  
  906.  
  907. ---@class LogOp
  908.  
  909. ---@field symbol string
  910.  
  911. ---@field defaultArg any
  912.  
  913. ---@field combine fun(args: any, arg: any): any
  914.  
  915.  
  916.  
  917. ---@type table<string, LogOp>
  918.  
  919. LogOps = {
  920.  
  921.     MoveForward = {
  922.  
  923.         symbol = "h",
  924.  
  925.         defaultArg = 0,
  926.  
  927.         ---@param args number
  928.  
  929.         ---@param arg number
  930.  
  931.         ---@return number
  932.  
  933.         combine = function(args, arg)
  934.  
  935.             return args + arg
  936.  
  937.         end
  938.  
  939.     },
  940.  
  941.     MoveVertical = {
  942.  
  943.         symbol = "v",
  944.  
  945.         defaultArg = 0,
  946.  
  947.         ---@param args number
  948.  
  949.         ---@param arg number
  950.  
  951.         ---@return number
  952.  
  953.         combine = function(args, arg)
  954.  
  955.             return args + arg
  956.  
  957.         end
  958.  
  959.     },
  960.  
  961.     Turn = {
  962.  
  963.         symbol = "t",
  964.  
  965.         defaultArg = 0,
  966.  
  967.         ---@param args number
  968.  
  969.         ---@param arg Rotation
  970.  
  971.         ---@return number
  972.  
  973.         combine = function(args, arg)
  974.  
  975.             return math.fmod(math.fmod(args + arg.index, 4) + 4, 4)
  976.  
  977.         end
  978.  
  979.     }
  980.  
  981. }
  982.  
  983.  
  984.  
  985. ---@class LogWriter
  986.  
  987. ---@field op LogOp?
  988.  
  989. ---@field args any
  990.  
  991. ---@field file WriteHandle?
  992.  
  993. LogWriter = {
  994.  
  995.     op = nil,
  996.  
  997.     args = nil,
  998.  
  999.     file = nil,
  1000.  
  1001. }
  1002.  
  1003.  
  1004.  
  1005. ---
  1006.  
  1007. ---@param filename string
  1008.  
  1009. ---@return LogWriter
  1010.  
  1011. function LogWriter:new(filename)
  1012.  
  1013.     local o = {}
  1014.  
  1015.     setmetatable(o, self)
  1016.  
  1017.     self.__index = self
  1018.  
  1019.     o.op         = ""
  1020.  
  1021.     o.args       = ""
  1022.  
  1023.     fs.delete(filename)
  1024.  
  1025.     o.file = fs.open(filename, "a")
  1026.  
  1027.     return o
  1028.  
  1029. end
  1030.  
  1031.  
  1032.  
  1033. ---
  1034.  
  1035. ---@param op LogOp
  1036.  
  1037. ---@param arg any
  1038.  
  1039. function LogWriter:append(op, arg)
  1040.  
  1041.     if self.op ~= op then
  1042.  
  1043.         self:flush()
  1044.  
  1045.         self.op = op
  1046.  
  1047.         self.args = self.op.combine(op.defaultArg, arg)
  1048.  
  1049.     else
  1050.  
  1051.         self.args = self.op.combine(self.args, arg)
  1052.  
  1053.     end
  1054.  
  1055. end
  1056.  
  1057.  
  1058.  
  1059. function LogWriter:flush()
  1060.  
  1061.     if self.op == nil then
  1062.  
  1063.         return
  1064.  
  1065.     end
  1066.  
  1067.     if self.op.defaultArg == self.args then
  1068.  
  1069.         return
  1070.  
  1071.     end
  1072.  
  1073.     self.file.write(self.op.symbol)
  1074.  
  1075.     self.file.writeLine(tostring(self.args))
  1076.  
  1077.     self.file.flush()
  1078.  
  1079. end
  1080.  
  1081.  
  1082.  
  1083. ---@alias BlockName string
  1084.  
  1085.  
  1086.  
  1087. ---@class Program
  1088.  
  1089. ---@field pos Vector
  1090.  
  1091. ---@field targetPos Vector
  1092.  
  1093. ---@field direction Direction
  1094.  
  1095. ---@field map TargetMap
  1096.  
  1097. ---@field targetPatterns string[]
  1098.  
  1099. ---@field junkPatterns string[]
  1100.  
  1101. ---@field tui TUI?
  1102.  
  1103. ---@field stop boolean
  1104.  
  1105. ---@field log LogWriter?
  1106.  
  1107. ---@field safeZone AABB
  1108.  
  1109. Program = {}
  1110.  
  1111.  
  1112.  
  1113. ---
  1114.  
  1115. ---@return Program
  1116.  
  1117. function Program:new()
  1118.  
  1119.     local o = {}
  1120.  
  1121.     setmetatable(o, self)
  1122.  
  1123.     self.__index     = self
  1124.  
  1125.     o.pos            = Vector:new()
  1126.  
  1127.     o.targetPos      = Vector:new()
  1128.  
  1129.     o.direction      = Directions.Forward
  1130.  
  1131.     o.map            = TargetMap:new()
  1132.  
  1133.     o.targetPatterns = {}
  1134.  
  1135.     o.junkPatterns   = {}
  1136.  
  1137.     o.tui            = TUI:new(o)
  1138.  
  1139.     o.log            = LogWriter:new("veinm.log")
  1140.  
  1141.     o.safeZone       = AABB:new(
  1142.  
  1143.         Vector:new(-Vector.minx, -Vector.miny, -Vector.minz),
  1144.  
  1145.         Vector:new(-1, Vector.maxy, Vector.maxz)
  1146.  
  1147.     )
  1148.  
  1149.     return o
  1150.  
  1151. end
  1152.  
  1153.  
  1154.  
  1155. ---
  1156.  
  1157. ---@return Vector
  1158.  
  1159. function Program:posInFront()
  1160.  
  1161.     return self.pos:add(self.direction)
  1162.  
  1163. end
  1164.  
  1165.  
  1166.  
  1167. ---
  1168.  
  1169. ---@param rotation Rotation
  1170.  
  1171. ---@return Vector
  1172.  
  1173. function Program:posRelative(rotation)
  1174.  
  1175.     return self.pos:add(self.direction:add(rotation))
  1176.  
  1177. end
  1178.  
  1179.  
  1180.  
  1181. ---
  1182.  
  1183. ---@return Vector
  1184.  
  1185. function Program:posAbove()
  1186.  
  1187.     return self.pos:add(Directions.Up)
  1188.  
  1189. end
  1190.  
  1191.  
  1192.  
  1193. ---
  1194.  
  1195. ---@return Vector
  1196.  
  1197. function Program:posBelow()
  1198.  
  1199.     return self.pos:add(Directions.Down)
  1200.  
  1201. end
  1202.  
  1203.  
  1204.  
  1205. function Program:turnLeft()
  1206.  
  1207.     turtle.turnLeft()
  1208.  
  1209.     self.direction = self.direction:add(Rotations.Left)
  1210.  
  1211.     self.tui:draw()
  1212.  
  1213.     self.log:append(LogOps.Turn, Rotations.Left)
  1214.  
  1215. end
  1216.  
  1217.  
  1218.  
  1219. function Program:turnRight()
  1220.  
  1221.     turtle.turnRight()
  1222.  
  1223.     self.direction = self.direction:add(Rotations.Right)
  1224.  
  1225.     self.tui:draw()
  1226.  
  1227.     self.log:append(LogOps.Turn, Rotations.Right)
  1228.  
  1229. end
  1230.  
  1231.  
  1232.  
  1233. function Program:move()
  1234.  
  1235.     turtle.dig()
  1236.  
  1237.     while not turtle.forward() do
  1238.  
  1239.         sleep(0.25)
  1240.  
  1241.         turtle.dig()
  1242.  
  1243.     end
  1244.  
  1245.  
  1246.  
  1247.     self:finishMove(self.direction)
  1248.  
  1249.     self.log:append(LogOps.MoveForward, 1)
  1250.  
  1251. end
  1252.  
  1253.  
  1254.  
  1255. function Program:moveUp()
  1256.  
  1257.     turtle.digUp()
  1258.  
  1259.     while not turtle.up() do
  1260.  
  1261.         sleep(0.25)
  1262.  
  1263.         turtle.digUp()
  1264.  
  1265.     end
  1266.  
  1267.  
  1268.  
  1269.     self:finishMove(Directions.Up)
  1270.  
  1271.     self.log:append(LogOps.MoveVertical, 1)
  1272.  
  1273. end
  1274.  
  1275.  
  1276.  
  1277. function Program:moveDown()
  1278.  
  1279.     turtle.digDown()
  1280.  
  1281.     while not turtle.down() do
  1282.  
  1283.         sleep(0.25)
  1284.  
  1285.         turtle.digDown()
  1286.  
  1287.     end
  1288.  
  1289.  
  1290.  
  1291.     self:finishMove(Directions.Down)
  1292.  
  1293.     self.log:append(LogOps.MoveVertical, -1)
  1294.  
  1295. end
  1296.  
  1297.  
  1298.  
  1299. ---
  1300.  
  1301. ---@param dir Direction
  1302.  
  1303. function Program:finishMove(dir)
  1304.  
  1305.     self.pos = self.pos:add(dir)
  1306.  
  1307.     self:markPosVisited()
  1308.  
  1309.     self:processBlocks(dir)
  1310.  
  1311.     self.tui:draw()
  1312.  
  1313. end
  1314.  
  1315.  
  1316.  
  1317. function Program:markPosVisited()
  1318.  
  1319.     self.map:set(self.pos, "visited")
  1320.  
  1321. end
  1322.  
  1323.  
  1324.  
  1325. ---
  1326.  
  1327. ---@return BlockName
  1328.  
  1329. function Program:blockInFront()
  1330.  
  1331.     local succsess, data = turtle.inspect()
  1332.  
  1333.     if succsess then
  1334.  
  1335.         return data.name
  1336.  
  1337.     else
  1338.  
  1339.         return ""
  1340.  
  1341.     end
  1342.  
  1343. end
  1344.  
  1345.  
  1346.  
  1347. ---
  1348.  
  1349. ---@return BlockName
  1350.  
  1351. function Program:blockAbove()
  1352.  
  1353.     local succsess, data = turtle.inspectUp()
  1354.  
  1355.     if succsess then
  1356.  
  1357.         return data.name
  1358.  
  1359.     else
  1360.  
  1361.         return ""
  1362.  
  1363.     end
  1364.  
  1365. end
  1366.  
  1367.  
  1368.  
  1369. ---
  1370.  
  1371. ---@return BlockName
  1372.  
  1373. function Program:blockBelow()
  1374.  
  1375.     local succsess, data = turtle.inspectDown()
  1376.  
  1377.     if succsess then
  1378.  
  1379.         return data.name
  1380.  
  1381.     else
  1382.  
  1383.         return ""
  1384.  
  1385.     end
  1386.  
  1387. end
  1388.  
  1389.  
  1390.  
  1391. ---
  1392.  
  1393. ---@param pos Vector
  1394.  
  1395. ---@return boolean
  1396.  
  1397. function Program:mayVisit(pos)
  1398.  
  1399.     return not self.safeZone:inside(pos)
  1400.  
  1401. end
  1402.  
  1403.  
  1404.  
  1405. ---
  1406.  
  1407. ---@param name BlockName
  1408.  
  1409. ---@return boolean
  1410.  
  1411. function Program:isTarget(name)
  1412.  
  1413.     for i, pattern in ipairs(self.targetPatterns) do
  1414.  
  1415.         if string.find(name, pattern) then
  1416.  
  1417.             return true
  1418.  
  1419.         end
  1420.  
  1421.     end
  1422.  
  1423.     return false
  1424.  
  1425. end
  1426.  
  1427.  
  1428.  
  1429. ---
  1430.  
  1431. ---@param pos Vector
  1432.  
  1433. ---@param name BlockName
  1434.  
  1435. function Program:processBlock(pos, name)
  1436.  
  1437.     if not self:mayVisit(pos) then
  1438.  
  1439.         self.map:set(pos, "avoid")
  1440.  
  1441.     elseif self:isTarget(name) then
  1442.  
  1443.         self.map:set(pos, "queued")
  1444.  
  1445.     else
  1446.  
  1447.         self.map:set(pos, "irrelevant")
  1448.  
  1449.     end
  1450.  
  1451. end
  1452.  
  1453.  
  1454.  
  1455. function Program:processBlockInFront()
  1456.  
  1457.     self:processBlock(self:posInFront(), self:blockInFront())
  1458.  
  1459. end
  1460.  
  1461.  
  1462.  
  1463. ---
  1464.  
  1465. ---@param pos Vector
  1466.  
  1467. ---@return boolean
  1468.  
  1469. function Program:hasProcessed(pos)
  1470.  
  1471.     return self.map:get(pos) ~= "unknown"
  1472.  
  1473. end
  1474.  
  1475.  
  1476.  
  1477. ---
  1478.  
  1479. ---@param lastMove Direction
  1480.  
  1481. function Program:processBlocks(lastMove)
  1482.  
  1483.     -- up
  1484.  
  1485.     if not self:hasProcessed(self:posAbove()) then
  1486.  
  1487.         self:processBlock(self:posAbove(), self:blockAbove())
  1488.  
  1489.     end
  1490.  
  1491.     -- down
  1492.  
  1493.     if not self:hasProcessed(self:posBelow()) then
  1494.  
  1495.         self:processBlock(self:posBelow(), self:blockBelow())
  1496.  
  1497.     end
  1498.  
  1499.  
  1500.  
  1501.     local lastMoveVertical = lastMove.dy ~= 0
  1502.  
  1503.     local checkBack = lastMoveVertical and not self:hasProcessed(self:posRelative(Rotations.Reverse))
  1504.  
  1505.  
  1506.  
  1507.  
  1508.  
  1509.     -- front
  1510.  
  1511.     if not self:hasProcessed(self:posInFront()) then
  1512.  
  1513.         self:processBlockInFront()
  1514.  
  1515.     end
  1516.  
  1517.  
  1518.  
  1519.     local forward = self.direction
  1520.  
  1521.     local right = forward:add(Rotations.Right)
  1522.  
  1523.     local left = forward:add(Rotations.Left)
  1524.  
  1525.     local back = forward:add(Rotations.Reverse)
  1526.  
  1527.  
  1528.  
  1529.     -- right
  1530.  
  1531.     if not self:hasProcessed(self.pos:add(right)) then
  1532.  
  1533.         self:turnTo(right)
  1534.  
  1535.         self:processBlockInFront()
  1536.  
  1537.  
  1538.  
  1539.         if checkBack then
  1540.  
  1541.             self:turnTo(back)
  1542.  
  1543.             self:processBlockInFront()
  1544.  
  1545.             checkBack = false
  1546.  
  1547.         end
  1548.  
  1549.     end
  1550.  
  1551.  
  1552.  
  1553.     -- left
  1554.  
  1555.     if not self:hasProcessed(self.pos:add(left)) then
  1556.  
  1557.         self:turnTo(left)
  1558.  
  1559.         self:processBlockInFront()
  1560.  
  1561.  
  1562.  
  1563.         if checkBack then
  1564.  
  1565.             self:turnTo(back)
  1566.  
  1567.             self:processBlockInFront()
  1568.  
  1569.             checkBack = false
  1570.  
  1571.         end
  1572.  
  1573.     end
  1574.  
  1575.  
  1576.  
  1577.     self.tui:draw()
  1578.  
  1579. end
  1580.  
  1581.  
  1582.  
  1583. ---
  1584.  
  1585. ---@param direction Direction
  1586.  
  1587. function Program:turnTo(direction)
  1588.  
  1589.     local rotation = self.direction:sub(direction)
  1590.  
  1591.     if rotation == Rotations.None then
  1592.  
  1593.         return
  1594.  
  1595.     elseif rotation == Rotations.Right then
  1596.  
  1597.         self:turnRight()
  1598.  
  1599.     elseif rotation == Rotations.Reverse then
  1600.  
  1601.         self:turnLeft()
  1602.  
  1603.         self:turnLeft()
  1604.  
  1605.     elseif rotation == Rotations.Left then
  1606.  
  1607.         self:turnLeft()
  1608.  
  1609.     end
  1610.  
  1611. end
  1612.  
  1613.  
  1614.  
  1615. ---
  1616.  
  1617. ---@param pos Vector
  1618.  
  1619. function Program:moveTo(pos)
  1620.  
  1621.     self.targetPos = pos
  1622.  
  1623.     local delta = pos:sub(self.pos)
  1624.  
  1625.  
  1626.  
  1627.     if not self:mayVisit(pos) then
  1628.  
  1629.         error(string.format("Impossible move to %s, inside safe zone", pos:toString()))
  1630.  
  1631.     end
  1632.  
  1633.  
  1634.  
  1635.     for i = 1, delta.y, 1 do
  1636.  
  1637.         if self.map:get(self:posAbove()) == "avoid" then
  1638.  
  1639.             self:moveTo(self.safeZone:closestAvoidancePoint(self.pos))
  1640.  
  1641.         end
  1642.  
  1643.         self:moveUp()
  1644.  
  1645.     end
  1646.  
  1647.     for i = 1, -delta.y, 1 do
  1648.  
  1649.         if self.map:get(self:posBelow()) == "avoid" then
  1650.  
  1651.             self:moveTo(self.safeZone:closestAvoidancePoint(self.pos))
  1652.  
  1653.         end
  1654.  
  1655.         self:moveDown()
  1656.  
  1657.     end
  1658.  
  1659.  
  1660.  
  1661.     local dir = Directions.None
  1662.  
  1663.     if delta.x > 0 then
  1664.  
  1665.         dir = Directions.Forward
  1666.  
  1667.     elseif delta.x < 0 then
  1668.  
  1669.         dir = Directions.Backward
  1670.  
  1671.     end
  1672.  
  1673.  
  1674.  
  1675.     for i = 1, math.abs(delta.x), 1 do
  1676.  
  1677.         self:turnTo(dir)
  1678.  
  1679.         if not self:mayVisit(self:posInFront()) then
  1680.  
  1681.             self:moveTo(self.safeZone:closestAvoidancePoint(self.pos))
  1682.  
  1683.             self:turnTo(dir)
  1684.  
  1685.         end
  1686.  
  1687.         self:move()
  1688.  
  1689.     end
  1690.  
  1691.  
  1692.  
  1693.     dir = Directions.None
  1694.  
  1695.     if delta.z > 0 then
  1696.  
  1697.         dir = Directions.Right
  1698.  
  1699.     elseif delta.z < 0 then
  1700.  
  1701.         dir = Directions.Left
  1702.  
  1703.     end
  1704.  
  1705.  
  1706.  
  1707.     for i = 1, math.abs(delta.z), 1 do
  1708.  
  1709.         self:turnTo(dir)
  1710.  
  1711.         if not self:mayVisit(self:posInFront()) then
  1712.  
  1713.             self:moveTo(self.safeZone:closestAvoidancePoint(self.pos))
  1714.  
  1715.             self:turnTo(dir)
  1716.  
  1717.         end
  1718.  
  1719.         self:move()
  1720.  
  1721.     end
  1722.  
  1723. end
  1724.  
  1725.  
  1726.  
  1727. function Program:isInventoryFull()
  1728.  
  1729.     local emptySlots = 0
  1730.  
  1731.     for i = 1, 16, 1 do
  1732.  
  1733.         local count = turtle.getItemCount(i)
  1734.  
  1735.         if count == 0 then
  1736.  
  1737.             emptySlots = emptySlots + 1
  1738.  
  1739.         end
  1740.  
  1741.     end
  1742.  
  1743.     return emptySlots <= 1
  1744.  
  1745. end
  1746.  
  1747.  
  1748.  
  1749. function Program:moveToStart()
  1750.  
  1751.     if not self.pos:equals(Zero) then
  1752.  
  1753.         self:moveTo(Zero)
  1754.  
  1755.     end
  1756.  
  1757.     self:turnTo(Directions.Forward)
  1758.  
  1759. end
  1760.  
  1761.  
  1762.  
  1763. function Program:dropJunk()
  1764.  
  1765.     self.tui:setStatus("Dropping junk")
  1766.  
  1767.     for i = 1, 16, 1 do
  1768.  
  1769.         turtle.select(i)
  1770.  
  1771.         local type = turtle.getItemDetail(i)
  1772.  
  1773.         if type then
  1774.  
  1775.             local keep = true
  1776.  
  1777.             for _, pattern in ipairs(self.junkPatterns) do
  1778.  
  1779.                 if string.find(type.name, pattern) then
  1780.  
  1781.                     keep = false
  1782.  
  1783.                     break
  1784.  
  1785.                 end
  1786.  
  1787.             end
  1788.  
  1789.  
  1790.  
  1791.             if not keep then
  1792.  
  1793.                 turtle.dropDown()
  1794.  
  1795.             end
  1796.  
  1797.         end
  1798.  
  1799.     end
  1800.  
  1801.     turtle.select(1)
  1802.  
  1803.     self.tui:setStatus("")
  1804.  
  1805. end
  1806.  
  1807.  
  1808.  
  1809. function Program:unload()
  1810.  
  1811.     self:turnTo(Directions.Backward)
  1812.  
  1813.     self.tui:setStatus("Unloading inventory")
  1814.  
  1815.  
  1816.  
  1817.     for i = 1, 16, 1 do
  1818.  
  1819.         turtle.select(i)
  1820.  
  1821.         local type = turtle.getItemDetail(i)
  1822.  
  1823.         local isFuel = false
  1824.  
  1825.         if type and string.find(type.name, FuelItem) then
  1826.  
  1827.             isFuel = true
  1828.  
  1829.             turtle.transferTo(1)
  1830.  
  1831.         end
  1832.  
  1833.         if not (isFuel and i == 1) then
  1834.  
  1835.             turtle.drop()
  1836.  
  1837.         end
  1838.  
  1839.     end
  1840.  
  1841.     turtle.select(1)
  1842.  
  1843.  
  1844.  
  1845.     self:turnTo(Directions.Forward)
  1846.  
  1847.     self.tui:setStatus("")
  1848.  
  1849. end
  1850.  
  1851.  
  1852.  
  1853. function Program:consumeFuel()
  1854.  
  1855.     if turtle.getFuelLevel() >= turtle.getFuelLimit() - 1000 then
  1856.  
  1857.         return
  1858.  
  1859.     end
  1860.  
  1861.     self.tui:setStatus("Refueling")
  1862.  
  1863.     for slot = 1, 16, 1 do
  1864.  
  1865.         turtle.select(slot)
  1866.  
  1867.         while turtle.getFuelLevel() < turtle.getFuelLimit() - 1000 do
  1868.  
  1869.             if not turtle.refuel(1) then
  1870.  
  1871.                 break
  1872.  
  1873.             end
  1874.  
  1875.         end
  1876.  
  1877.     end
  1878.  
  1879.     turtle.select(1)
  1880.  
  1881.     self.tui:setStatus("")
  1882.  
  1883. end
  1884.  
  1885.  
  1886.  
  1887. function Program:unloadAndRefuel()
  1888.  
  1889.     self:unload()
  1890.  
  1891.     local minFuel = turtle.getFuelLimit() / 2
  1892.  
  1893.     while turtle.getFuelLevel() < minFuel do
  1894.  
  1895.         self.tui:setStatus(string.format("Insert %d fuel to continue", minFuel - turtle.getFuelLevel()))
  1896.  
  1897.         sleep(1)
  1898.  
  1899.         self.tui:draw()
  1900.  
  1901.         self:consumeFuel()
  1902.  
  1903.     end
  1904.  
  1905. end
  1906.  
  1907.  
  1908.  
  1909. ---@class TUI
  1910.  
  1911. ---@field statusMessage string
  1912.  
  1913. ---@field program Program?
  1914.  
  1915. ---@field tick number
  1916.  
  1917. TUI = {}
  1918.  
  1919.  
  1920.  
  1921. ---
  1922.  
  1923. ---@param program Program
  1924.  
  1925. ---@return TUI
  1926.  
  1927. function TUI:new(program)
  1928.  
  1929.     local o = {}
  1930.  
  1931.     setmetatable(o, self)
  1932.  
  1933.     self.__index = self
  1934.  
  1935.     o.statusMessage = ""
  1936.  
  1937.     o.program = program
  1938.  
  1939.     o.tick = 0
  1940.  
  1941.     o.lastTick = os.clock()
  1942.  
  1943.     return o
  1944.  
  1945. end
  1946.  
  1947.  
  1948.  
  1949. ---
  1950.  
  1951. ---@param message string
  1952.  
  1953. function TUI:setStatus(message)
  1954.  
  1955.     self.statusMessage = message
  1956.  
  1957.     self:draw()
  1958.  
  1959. end
  1960.  
  1961.  
  1962.  
  1963. function TUI:draw()
  1964.  
  1965.     local w, h = term.getSize()
  1966.  
  1967.     term.setCursorPos(1, 1)
  1968.  
  1969.     term.clearLine()
  1970.  
  1971.     term.write(TUI.ellipseString(self.statusMessage, w))
  1972.  
  1973.  
  1974.  
  1975.     term.setCursorPos(1, 3)
  1976.  
  1977.     TUI:table(w, { 2, 1 }, 2,
  1978.  
  1979.         { { "Position", self.program.pos:toString() }, { "Direction", self.program.direction.name } })
  1980.  
  1981.  
  1982.  
  1983.     local map = self.program.map
  1984.  
  1985.     local efficiency = math.floor(100 * (map.minedCount + 1) / map.visitedCount)
  1986.  
  1987.     term.setCursorPos(1, 6)
  1988.  
  1989.     TUI:table(w, { 1, 1, 1 }, 2,
  1990.  
  1991.         {
  1992.  
  1993.             { "Discovered", map.discoveredCount },
  1994.  
  1995.             { "Visited",    map.visitedCount },
  1996.  
  1997.             { "Mined",      map.minedCount }
  1998.  
  1999.         })
  2000.  
  2001.     term.setCursorPos(1, 9)
  2002.  
  2003.     TUI:table(w, { 1, 1, 1 }, 2,
  2004.  
  2005.         {
  2006.  
  2007.             { "Queued",     map.queuedCount },
  2008.  
  2009.             { "Efficiency", efficiency .. "%" },
  2010.  
  2011.             { "Fuel",       turtle.getFuelLevel() }
  2012.  
  2013.         })
  2014.  
  2015.  
  2016.  
  2017.     term.setCursorPos(1, h)
  2018.  
  2019.     term.clearLine()
  2020.  
  2021.     if self.lastTick ~= math.floor(os.clock()) then
  2022.  
  2023.         self.tick = self.tick + 1
  2024.  
  2025.         self.lastTick = math.floor(os.clock())
  2026.  
  2027.     end
  2028.  
  2029.     term.write(string.rep(".", math.fmod(self.tick, 4) + 1))
  2030.  
  2031. end
  2032.  
  2033.  
  2034.  
  2035. ---
  2036.  
  2037. ---@param s string
  2038.  
  2039. ---@param len number
  2040.  
  2041. ---@return string
  2042.  
  2043. function TUI.ellipseString(s, len)
  2044.  
  2045.     if string.len(s) > len then
  2046.  
  2047.         return string.sub(s, 1, len - 3) .. "..."
  2048.  
  2049.     end
  2050.  
  2051.     return s
  2052.  
  2053. end
  2054.  
  2055.  
  2056.  
  2057. ---
  2058.  
  2059. ---@param width number
  2060.  
  2061. ---@param columns number[]
  2062.  
  2063. ---@param rows number
  2064.  
  2065. ---@param data any[][]
  2066.  
  2067. function TUI:table(width, columns, rows, data, options)
  2068.  
  2069.     local fractions = 0
  2070.  
  2071.     for _, fract in ipairs(columns) do
  2072.  
  2073.         fractions = fractions + fract
  2074.  
  2075.     end
  2076.  
  2077.     local availWidth = width
  2078.  
  2079.     local fractTotal = 0
  2080.  
  2081.     local colFmt = ""
  2082.  
  2083.     for col, fract in ipairs(columns) do
  2084.  
  2085.         local colStart = availWidth * (fractTotal / fractions)
  2086.  
  2087.         fractTotal = fractTotal + fract
  2088.  
  2089.         local colEnd = availWidth * (fractTotal / fractions)
  2090.  
  2091.         local size = math.floor(colEnd - colStart + 0.5)
  2092.  
  2093.         colFmt = colFmt .. "%-" .. tostring(size - 2) .. "s"
  2094.  
  2095.         if col < #columns then
  2096.  
  2097.             colFmt = colFmt .. "| "
  2098.  
  2099.         end
  2100.  
  2101.     end
  2102.  
  2103.     local cx, cy = term.getCursorPos()
  2104.  
  2105.     for row = 1, rows, 1 do
  2106.  
  2107.         local rowData = {}
  2108.  
  2109.         for col = 1, #data, 1 do
  2110.  
  2111.             local value = data[col][row] or ""
  2112.  
  2113.             table.insert(rowData, value)
  2114.  
  2115.         end
  2116.  
  2117.         term.clearLine()
  2118.  
  2119.         term.write(string.format(colFmt, table.unpack(rowData)))
  2120.  
  2121.         if row < rows then
  2122.  
  2123.             term.setCursorPos(cx, cy + row)
  2124.  
  2125.         end
  2126.  
  2127.     end
  2128.  
  2129. end
  2130.  
  2131.  
  2132.  
  2133. function Program:run()
  2134.  
  2135.     term.clear()
  2136.  
  2137.  
  2138.  
  2139.     self.tui:setStatus("Starting...")
  2140.  
  2141.  
  2142.  
  2143.     self:unloadAndRefuel()
  2144.  
  2145.  
  2146.  
  2147.     self:processBlocks(Directions.Forward)
  2148.  
  2149.     self:markPosVisited()
  2150.  
  2151.  
  2152.  
  2153.     while not self.stop do
  2154.  
  2155.         self:consumeFuel()
  2156.  
  2157.         if turtle.getFuelLevel() < self.pos:distanceManhattan(Zero) * 1.5 then
  2158.  
  2159.             self.tui:setStatus("Fuel low, returning to start")
  2160.  
  2161.             self:moveToStart()
  2162.  
  2163.             self:unloadAndRefuel()
  2164.  
  2165.         end
  2166.  
  2167.  
  2168.  
  2169.         if self:isInventoryFull() then
  2170.  
  2171.             self:dropJunk()
  2172.  
  2173.         end
  2174.  
  2175.         if self:isInventoryFull() then
  2176.  
  2177.             self.tui:setStatus("Going to unload inventory")
  2178.  
  2179.             self:moveToStart()
  2180.  
  2181.             self:unload()
  2182.  
  2183.         end
  2184.  
  2185.  
  2186.  
  2187.         if self.stop then
  2188.  
  2189.             break
  2190.  
  2191.         end
  2192.  
  2193.  
  2194.  
  2195.         local target = self.map:findNextTarget(self.pos, self.direction)
  2196.  
  2197.  
  2198.  
  2199.         if target ~= nil then
  2200.  
  2201.             self.tui:setStatus("Target: " .. target:toString())
  2202.  
  2203.         else
  2204.  
  2205.             self.tui:setStatus("No target, returning to start")
  2206.  
  2207.             self:moveToStart()
  2208.  
  2209.             self:unload()
  2210.  
  2211.             self.tui:setStatus("Finished")
  2212.  
  2213.             break
  2214.  
  2215.         end
  2216.  
  2217.  
  2218.  
  2219.         self:moveTo(target)
  2220.  
  2221.     end
  2222.  
  2223.  
  2224.  
  2225.     self.log:flush()
  2226.  
  2227.  
  2228.  
  2229.     while true do
  2230.  
  2231.         sleep(1)
  2232.  
  2233.         self.tui:draw()
  2234.  
  2235.     end
  2236.  
  2237. end
  2238.  
  2239.  
  2240.  
  2241. Debug = {
  2242.  
  2243.     enabled = false,
  2244.  
  2245.     filters = {},
  2246.  
  2247.     skip = 0,
  2248.  
  2249.     history = {}
  2250.  
  2251. }
  2252.  
  2253. ---
  2254.  
  2255. ---@param id string
  2256.  
  2257. ---@param msg? string
  2258.  
  2259. function DebugStep(id, msg)
  2260.  
  2261.     if not Debug.enabled then
  2262.  
  2263.         return
  2264.  
  2265.     end
  2266.  
  2267.  
  2268.  
  2269.     if #Debug.filters > 0 then
  2270.  
  2271.         local match = false
  2272.  
  2273.         for _, pattern in ipairs(Debug.filters) do
  2274.  
  2275.             if string.find(id, pattern) then
  2276.  
  2277.                 match = true
  2278.  
  2279.                 break
  2280.  
  2281.             end
  2282.  
  2283.         end
  2284.  
  2285.         if not match then
  2286.  
  2287.             return
  2288.  
  2289.         end
  2290.  
  2291.     end
  2292.  
  2293.  
  2294.  
  2295.     if Debug.skip > 0 then
  2296.  
  2297.         Debug.skip = Debug.skip - 1
  2298.  
  2299.         return
  2300.  
  2301.     end
  2302.  
  2303.  
  2304.  
  2305.     local w, h = term.getSize()
  2306.  
  2307.  
  2308.  
  2309.     for i = 0, 4, 1 do
  2310.  
  2311.         term.setCursorPos(1, h - i)
  2312.  
  2313.         term.clearLine()
  2314.  
  2315.     end
  2316.  
  2317.  
  2318.  
  2319.     if msg ~= nil then
  2320.  
  2321.         write(msg)
  2322.  
  2323.     end
  2324.  
  2325.  
  2326.  
  2327.     while true do
  2328.  
  2329.         term.setCursorPos(1, h)
  2330.  
  2331.         term.clearLine()
  2332.  
  2333.         term.write(string.format("@ %s. [space] [ctrl]", id))
  2334.  
  2335.         ---@diagnostic disable-next-line: undefined-field
  2336.  
  2337.         local ev = { os.pullEvent("key") }
  2338.  
  2339.         if ev[2] == 32 then
  2340.  
  2341.             -- space
  2342.  
  2343.             break
  2344.  
  2345.         elseif ev[2] == 341 then
  2346.  
  2347.             -- left control
  2348.  
  2349.             term.setCursorPos(1, h)
  2350.  
  2351.             term.clearLine()
  2352.  
  2353.             local cmd = read(nil, Debug.history)
  2354.  
  2355.             ---@diagnostic disable-next-line: deprecated
  2356.  
  2357.             local fn, err = loadstring(cmd)
  2358.  
  2359.             if fn then
  2360.  
  2361.                 RunDebugCmd(fn)
  2362.  
  2363.             else
  2364.  
  2365.                 write("Error: ", err)
  2366.  
  2367.             end
  2368.  
  2369.             table.insert(Debug.history, cmd)
  2370.  
  2371.         end
  2372.  
  2373.     end
  2374.  
  2375.  
  2376.  
  2377.     for i = 0, 4, 1 do
  2378.  
  2379.         term.setCursorPos(1, h - i)
  2380.  
  2381.         term.clearLine()
  2382.  
  2383.     end
  2384.  
  2385. end
  2386.  
  2387.  
  2388.  
  2389. function RunDebugCmd(__func)
  2390.  
  2391.     _G.echo = function(...)
  2392.  
  2393.         local w, h = term.getSize()
  2394.  
  2395.         for i = 0, 4, 1 do
  2396.  
  2397.             term.setCursorPos(1, h - i)
  2398.  
  2399.             term.clearLine()
  2400.  
  2401.         end
  2402.  
  2403.         local args = { n = select("#", ...), ... }
  2404.  
  2405.         for i = 1, args.n do
  2406.  
  2407.             io.stdout:write(tostring(args[i]))
  2408.  
  2409.         end
  2410.  
  2411.     end
  2412.  
  2413.     ---@diagnostic disable-next-line: deprecated
  2414.  
  2415.     setfenv(__func, getfenv())
  2416.  
  2417.     local status, err = pcall(__func)
  2418.  
  2419.     if not status then
  2420.  
  2421.         echo(err)
  2422.  
  2423.     end
  2424.  
  2425.     _G.echo = nil
  2426.  
  2427. end
  2428.  
  2429.  
  2430.  
  2431. ---@class AABB
  2432.  
  2433. ---@field min Vector
  2434.  
  2435. ---@field max Vector
  2436.  
  2437. AABB = {}
  2438.  
  2439.  
  2440.  
  2441. ---
  2442.  
  2443. ---@param min Vector
  2444.  
  2445. ---@param max Vector
  2446.  
  2447. ---@return AABB
  2448.  
  2449. function AABB:new(min, max)
  2450.  
  2451.     local o = {}
  2452.  
  2453.     setmetatable(o, self)
  2454.  
  2455.     self.__index = self
  2456.  
  2457.     o.min = min
  2458.  
  2459.     o.max = max
  2460.  
  2461.     return o
  2462.  
  2463. end
  2464.  
  2465.  
  2466.  
  2467. ---
  2468.  
  2469. ---@param pos Vector
  2470.  
  2471. ---@return boolean
  2472.  
  2473. function AABB:inside(pos)
  2474.  
  2475.     local min = self.min
  2476.  
  2477.     local max = self.max
  2478.  
  2479.     return pos.x >= min.x and pos.x <= max.x and
  2480.  
  2481.         pos.y >= min.y and pos.y <= max.y and
  2482.  
  2483.         pos.z >= min.z and pos.z <= max.z
  2484.  
  2485. end
  2486.  
  2487.  
  2488.  
  2489. ---
  2490.  
  2491. ---NOTE: I haven't fully tested this algorith
  2492.  
  2493. ---@param pos Vector
  2494.  
  2495. ---@return Vector
  2496.  
  2497. function AABB:closestAvoidancePoint(pos)
  2498.  
  2499.     local min = self.min
  2500.  
  2501.     local max = self.max
  2502.  
  2503.  
  2504.  
  2505.     local options = {
  2506.  
  2507.         { coord = 'x', value = min.x - 1, dist = math.abs(min.x - 1 - pos.x) },
  2508.  
  2509.         { coord = 'y', value = min.y - 1, dist = math.abs(min.y - 1 - pos.y) },
  2510.  
  2511.         { coord = 'z', value = min.z - 1, dist = math.abs(min.z - 1 - pos.z) },
  2512.  
  2513.         { coord = 'x', value = max.x + 1, dist = math.abs(max.x + 1 - pos.x) },
  2514.  
  2515.         { coord = 'y', value = max.y + 1, dist = math.abs(max.y + 1 - pos.y) },
  2516.  
  2517.         { coord = 'z', value = max.z + 1, dist = math.abs(max.z + 1 - pos.z) },
  2518.  
  2519.     }
  2520.  
  2521.  
  2522.  
  2523.     table.sort(options, function(a, b)
  2524.  
  2525.         return a.dist < b.dist
  2526.  
  2527.     end)
  2528.  
  2529.  
  2530.  
  2531.     local result = Vector:new(pos.x, pos.y, pos.z)
  2532.  
  2533.     result[options[2].coord] = options[2].value
  2534.  
  2535.  
  2536.  
  2537.     return result
  2538.  
  2539. end
  2540.  
  2541.  
  2542.  
  2543. ---
  2544.  
  2545. ---@param width (number | string)?
  2546.  
  2547. ---@param height (number | string)?
  2548.  
  2549. ---@param depth (number | string)?
  2550.  
  2551. function Program:parseSafeZone(width, height, depth)
  2552.  
  2553.     self.safeZone = AABB:new(Vector:new(), Vector:new())
  2554.  
  2555.  
  2556.  
  2557.     width = width or "inf"
  2558.  
  2559.     height = height or "inf"
  2560.  
  2561.     depth = depth or "inf"
  2562.  
  2563.  
  2564.  
  2565.     width = tonumber(width) or Vector.maxx * 2;
  2566.  
  2567.     height = tonumber(height) or Vector.maxy * 2
  2568.  
  2569.     depth = tonumber(depth) or Vector.maxz * 2
  2570.  
  2571.  
  2572.  
  2573.     self.safeZone.min.x = -math.ceil(depth)
  2574.  
  2575.     self.safeZone.max.x = -1
  2576.  
  2577.     self.safeZone.min.y = -math.ceil(height / 2 - 0.5)
  2578.  
  2579.     self.safeZone.max.y = math.ceil(height / 2 - 0.5)
  2580.  
  2581.     self.safeZone.min.z = -math.ceil(width / 2 - 0.5)
  2582.  
  2583.     self.safeZone.max.z = math.ceil(width / 2 - 0.5)
  2584.  
  2585. end
  2586.  
  2587.  
  2588.  
  2589. function Start(args)
  2590.  
  2591.     local junkPatterns = {}
  2592.  
  2593.     local targetPatterns = {}
  2594.  
  2595.     local safeZoneArgs = {}
  2596.  
  2597.     local activeList = targetPatterns
  2598.  
  2599.     local help = false
  2600.  
  2601.     for _, v in ipairs(args) do
  2602.  
  2603.         if string.find(v, "^-") then
  2604.  
  2605.             if v == "-junk" then
  2606.  
  2607.                 activeList = junkPatterns
  2608.  
  2609.             elseif v == "-safe" then
  2610.  
  2611.                 activeList = safeZoneArgs
  2612.  
  2613.             elseif v == "-help" then
  2614.  
  2615.                 help = true
  2616.  
  2617.                 break
  2618.  
  2619.             elseif v == "-debug" then
  2620.  
  2621.                 Debug.enabled = true
  2622.  
  2623.                 activeList = Debug.filters
  2624.  
  2625.             else
  2626.  
  2627.                 help = true
  2628.  
  2629.                 return
  2630.  
  2631.             end
  2632.  
  2633.         else
  2634.  
  2635.             table.insert(activeList, v)
  2636.  
  2637.         end
  2638.  
  2639.     end
  2640.  
  2641.     if #targetPatterns == 0 or help or #safeZoneArgs > 3 then
  2642.  
  2643.         print("Usage: veinm <pattern>... [options...]")
  2644.  
  2645.         print("Options:")
  2646.  
  2647.         print(" -help")
  2648.  
  2649.         print(" -junk <junk_pattern>...")
  2650.  
  2651.         print(" -safe <width> <height> <depth>")
  2652.  
  2653.         print(" -debug <filters>...")
  2654.  
  2655.         return
  2656.  
  2657.     end
  2658.  
  2659.  
  2660.  
  2661.     local app = Program:new()
  2662.  
  2663.     app.targetPatterns = targetPatterns
  2664.  
  2665.     app.junkPatterns = junkPatterns
  2666.  
  2667.     app:parseSafeZone(safeZoneArgs[1], safeZoneArgs[2], safeZoneArgs[3])
  2668.  
  2669.     app:run()
  2670.  
  2671. end
  2672.  
  2673.  
  2674.  
  2675. Start({ ... })
  2676.  
  2677.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement