Initial commit
							
								
								
									
										232
									
								
								PriorityQueue.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,232 @@
 | 
			
		||||
--[[
 | 
			
		||||
 | 
			
		||||
 PriorityQueue - v1.0.1 - public domain Lua priority queue
 | 
			
		||||
 implemented with indirect binary heap
 | 
			
		||||
 no warranty implied; use at your own risk
 | 
			
		||||
 | 
			
		||||
 based on binaryheap library (github.com/iskolbin/binaryheap)
 | 
			
		||||
 | 
			
		||||
 author: Ilya Kolbin (iskolbin@gmail.com)
 | 
			
		||||
 url: github.com/iskolbin/priorityqueue
 | 
			
		||||
 | 
			
		||||
 See documentation in README file.
 | 
			
		||||
 | 
			
		||||
 COMPATIBILITY
 | 
			
		||||
 | 
			
		||||
 Lua 5.1, 5.2, 5.3, LuaJIT 1, 2
 | 
			
		||||
 | 
			
		||||
 LICENSE
 | 
			
		||||
 | 
			
		||||
 This software is dual-licensed to the public domain and under the following
 | 
			
		||||
 license: you are granted a perpetual, irrevocable license to copy, modify,
 | 
			
		||||
 publish, and distribute this file as you see fit.
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local floor, setmetatable = math.floor, setmetatable
 | 
			
		||||
 | 
			
		||||
local function siftup( self, from )
 | 
			
		||||
	local items, priorities, indices, higherpriority = self, self._priorities, self._indices, self._higherpriority
 | 
			
		||||
	local index = from
 | 
			
		||||
	local parent = floor( index / 2 )
 | 
			
		||||
	while index > 1 and higherpriority( priorities[index], priorities[parent] ) do
 | 
			
		||||
		priorities[index], priorities[parent] = priorities[parent], priorities[index]
 | 
			
		||||
		items[index], items[parent] = items[parent], items[index]
 | 
			
		||||
		indices[items[index]], indices[items[parent]] = index, parent
 | 
			
		||||
		index = parent
 | 
			
		||||
		parent = floor( index / 2 )
 | 
			
		||||
	end
 | 
			
		||||
	return index
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function siftdown( self, limit )
 | 
			
		||||
	local items, priorities, indices, higherpriority, size = self, self._priorities, self._indices, self._higherpriority, self._size
 | 
			
		||||
	for index = limit, 1, -1 do
 | 
			
		||||
		local left = index + index
 | 
			
		||||
		local right = left + 1
 | 
			
		||||
		while left <= size do
 | 
			
		||||
			local smaller = left
 | 
			
		||||
			if right <= size and higherpriority( priorities[right], priorities[left] ) then
 | 
			
		||||
				smaller = right
 | 
			
		||||
			end
 | 
			
		||||
			if higherpriority( priorities[smaller], priorities[index] ) then
 | 
			
		||||
				items[index], items[smaller] = items[smaller], items[index]
 | 
			
		||||
				priorities[index], priorities[smaller] = priorities[smaller], priorities[index]
 | 
			
		||||
				indices[items[index]], indices[items[smaller]] = index, smaller
 | 
			
		||||
			else
 | 
			
		||||
				break
 | 
			
		||||
			end
 | 
			
		||||
			index = smaller
 | 
			
		||||
			left = index + index
 | 
			
		||||
			right = left + 1
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local PriorityQueueMt
 | 
			
		||||
 | 
			
		||||
local PriorityQueue = {}
 | 
			
		||||
 | 
			
		||||
local function minishigher( a, b )
 | 
			
		||||
	return a < b
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function maxishigher( a, b )
 | 
			
		||||
	return a > b
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue.new( priority_or_array )
 | 
			
		||||
	local t = type( priority_or_array )
 | 
			
		||||
	local higherpriority = minishigher
 | 
			
		||||
	
 | 
			
		||||
	if t == 'table' then
 | 
			
		||||
		higherpriority = priority_or_array.higherpriority or higherpriority
 | 
			
		||||
	elseif t == 'function' or t == 'string' then
 | 
			
		||||
		higherpriority = priority_or_array
 | 
			
		||||
	elseif t ~= 'nil' then
 | 
			
		||||
		local msg = 'Wrong argument type to PriorityQueue.new, it must be table or function or string, has: %q'
 | 
			
		||||
		error( msg:format( t ))
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if type( higherpriority ) == 'string' then
 | 
			
		||||
		if higherpriority == 'min' then
 | 
			
		||||
			higherpriority = minishigher
 | 
			
		||||
		elseif higherpriority == 'max' then
 | 
			
		||||
			higherpriority = maxishigher
 | 
			
		||||
		else
 | 
			
		||||
			local msg = 'Wrong string argument to PriorityQueue.new, it must be "min" or "max", has: %q'
 | 
			
		||||
			error( msg:format( tostring( higherpriority )))
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local self = setmetatable( {
 | 
			
		||||
		_priorities = {},
 | 
			
		||||
		_indices = {},
 | 
			
		||||
		_size = 0,
 | 
			
		||||
		_higherpriority = higherpriority or minishigher
 | 
			
		||||
	}, PriorityQueueMt )
 | 
			
		||||
 | 
			
		||||
	if t == 'table' then
 | 
			
		||||
		self:batchenq( priority_or_array )
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return self
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:enqueue( item, priority )
 | 
			
		||||
	local items, priorities, indices = self, self._priorities, self._indices
 | 
			
		||||
	if indices[item] ~= nil then
 | 
			
		||||
		error( 'Item ' .. tostring(indices[item]) .. ' is already in the heap' )
 | 
			
		||||
	end
 | 
			
		||||
	local size = self._size + 1
 | 
			
		||||
	self._size = size	
 | 
			
		||||
	items[size], priorities[size], indices[item] = item, priority, size
 | 
			
		||||
	siftup( self, size ) 
 | 
			
		||||
	return self
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:remove( item )
 | 
			
		||||
	local index = self._indices[item]
 | 
			
		||||
	if index ~= nil then
 | 
			
		||||
		local size = self._size
 | 
			
		||||
		local items, priorities, indices = self, self._priorities, self._indices
 | 
			
		||||
		indices[item] = nil
 | 
			
		||||
		if size == index then
 | 
			
		||||
			items[size], priorities[size] = nil, nil
 | 
			
		||||
			self._size = size - 1
 | 
			
		||||
		else
 | 
			
		||||
			local lastitem = items[size]
 | 
			
		||||
			items[index], priorities[index] = items[size], priorities[size]
 | 
			
		||||
			items[size], priorities[size] = nil, nil
 | 
			
		||||
			indices[lastitem] = index
 | 
			
		||||
			size = size - 1
 | 
			
		||||
			self._size = size
 | 
			
		||||
			if size > 1 then
 | 
			
		||||
				siftdown( self, siftup( self, index )) 
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		return true
 | 
			
		||||
	else
 | 
			
		||||
		return false
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:contains( item )
 | 
			
		||||
	return self._indices[item] ~= nil
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:update( item, priority )
 | 
			
		||||
	local ok = self:remove( item )
 | 
			
		||||
	if ok then
 | 
			
		||||
		self:enqueue( item, priority )
 | 
			
		||||
		return true
 | 
			
		||||
	else
 | 
			
		||||
		return false
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:dequeue()
 | 
			
		||||
	local size = self._size
 | 
			
		||||
	
 | 
			
		||||
	assert( size > 0, 'Heap is empty' )
 | 
			
		||||
	
 | 
			
		||||
	local items, priorities, indices = self, self._priorities, self._indices
 | 
			
		||||
	local item, priority = items[1], priorities[1]
 | 
			
		||||
	indices[item] = nil
 | 
			
		||||
 | 
			
		||||
	if size > 1 then
 | 
			
		||||
		local newitem = items[size]
 | 
			
		||||
		items[1], priorities[1] = newitem, priorities[size]
 | 
			
		||||
		items[size], priorities[size] = nil, nil
 | 
			
		||||
		indices[newitem] = 1
 | 
			
		||||
		size = size - 1
 | 
			
		||||
		self._size = size
 | 
			
		||||
		siftdown( self, 1 )
 | 
			
		||||
	else
 | 
			
		||||
		items[1], priorities[1] = nil, nil
 | 
			
		||||
		self._size = 0
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return item, priority
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:peek()
 | 
			
		||||
	return self[1], self._priorities[1]
 | 
			
		||||
end
 | 
			
		||||
	
 | 
			
		||||
function PriorityQueue:len()
 | 
			
		||||
	return self._size
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:empty()
 | 
			
		||||
	return self._size <= 0
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function PriorityQueue:batchenq( iparray )
 | 
			
		||||
	local items, priorities, indices = self, self._priorities, self._indices
 | 
			
		||||
	local size = self._size
 | 
			
		||||
	for i = 1, #iparray, 2 do
 | 
			
		||||
		local item, priority = iparray[i], iparray[i+1]
 | 
			
		||||
		if indices[item] ~= nil then
 | 
			
		||||
			error( 'Item ' .. tostring(indices[item]) .. ' is already in the heap' )
 | 
			
		||||
		end
 | 
			
		||||
		size = size + 1
 | 
			
		||||
		items[size], priorities[size] = item, priority
 | 
			
		||||
		indices[item] = size
 | 
			
		||||
	end
 | 
			
		||||
	self._size = size
 | 
			
		||||
	if size > 1 then
 | 
			
		||||
		siftdown( self, floor( size / 2 ))
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
PriorityQueueMt = {
 | 
			
		||||
	__index = PriorityQueue,
 | 
			
		||||
	__len = PriorityQueue.len,
 | 
			
		||||
}
 | 
			
		||||
      
 | 
			
		||||
return setmetatable( PriorityQueue, {
 | 
			
		||||
	__call = function( _, ... )
 | 
			
		||||
		return PriorityQueue.new( ... )
 | 
			
		||||
	end
 | 
			
		||||
} )
 | 
			
		||||
							
								
								
									
										61
									
								
								alarm.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,61 @@
 | 
			
		||||
local dfpwm = require("cc.audio.dfpwm")
 | 
			
		||||
 | 
			
		||||
local speaker = peripheral.find("speaker")
 | 
			
		||||
local monitor = peripheral.find("monitor")
 | 
			
		||||
local chat = peripheral.find("chatBox")
 | 
			
		||||
local detector = peripheral.find("playerDetector")
 | 
			
		||||
 | 
			
		||||
local distance = 20
 | 
			
		||||
local target = "OROURKEIRE"
 | 
			
		||||
--local target = "devplayer0"
 | 
			
		||||
 | 
			
		||||
function klaxon()
 | 
			
		||||
  local decoder = dfpwm.make_decoder()
 | 
			
		||||
  for chunk in io.lines("data/danger.dfpwm", 16 * 1024) do
 | 
			
		||||
    local buffer = decoder(chunk)
 | 
			
		||||
  
 | 
			
		||||
    while not speaker.playAudio(buffer) do
 | 
			
		||||
      os.pullEvent("speaker_audio_empty")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function showWarning()
 | 
			
		||||
  monitor.clear()
 | 
			
		||||
  monitor.setTextScale(2)
 | 
			
		||||
  --[[local w, h = monitor.getSize()
 | 
			
		||||
  local tw = string.len("WARNING")
 | 
			
		||||
  for i = 1, 3 do
 | 
			
		||||
    monitor.setCursorPos(w/2 - tw/2, (h/3)*i)
 | 
			
		||||
    monitor.write("WARNING")
 | 
			
		||||
  end]]
 | 
			
		||||
  monitor.setCursorPos(8, 3)
 | 
			
		||||
  monitor.write("WARNING")
 | 
			
		||||
  monitor.setCursorPos(8, 5)
 | 
			
		||||
  monitor.write("WARNING")
 | 
			
		||||
  monitor.setCursorPos(8, 8)
 | 
			
		||||
  monitor.write("WARNING")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function broadcastWarning()
 | 
			
		||||
  local msg = {
 | 
			
		||||
    {text = "WARNING", color = "red"},
 | 
			
		||||
    {text = ": Kevin is ", color = "white"},
 | 
			
		||||
    {text = "IN THE BASE", color = "yellow"},
 | 
			
		||||
    {text = "!"},
 | 
			
		||||
  }
 | 
			
		||||
  local msgJSON = textutils.serializeJSON(msg)
 | 
			
		||||
  chat.sendFormattedMessage(msgJSON)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
while true do
 | 
			
		||||
  if detector.isPlayerInRange(25, target) then
 | 
			
		||||
    showWarning()
 | 
			
		||||
    broadcastWarning()
 | 
			
		||||
    klaxon()
 | 
			
		||||
  else
 | 
			
		||||
    monitor.clear()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sleep(1)
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								danger.dfpwm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								datapack/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
*.zip
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "type": "computercraft:tool",
 | 
			
		||||
  "item": "minecraft:diamond_pickaxe",
 | 
			
		||||
  "adjective": "upgrade.minecraft.diamond_pickaxe.adjective",
 | 
			
		||||
  "allowEnchantments": true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								datapack/pack.mcmeta
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
	"pack": {
 | 
			
		||||
		"pack_format": 15,
 | 
			
		||||
		"description": "Enchanted pickaxe for ComputerCraft"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								icon/ore-coal.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 623 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-copper.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 653 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-diamond.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 625 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-coal.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 613 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-copper.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 636 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-diamond.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 641 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-emerald.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 636 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-gold.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 615 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-iron.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 624 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-lapis.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 661 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-ds-redstone.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 715 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-emerald.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 634 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-generic.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-gold.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 633 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-iron.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 615 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-lapis.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 676 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/ore-redstone.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 644 B  | 
							
								
								
									
										
											BIN
										
									
								
								icon/turtle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.5 KiB  | 
							
								
								
									
										727
									
								
								mine.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,727 @@
 | 
			
		||||
package.path = "/mod/?.lua;" .. package.path
 | 
			
		||||
local PriorityQueue = require "PriorityQueue"
 | 
			
		||||
 | 
			
		||||
function Set(list)
 | 
			
		||||
  local set = {}
 | 
			
		||||
  for _, l in ipairs(list) do set[l] = true end
 | 
			
		||||
  return set
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local yieldTime = 0
 | 
			
		||||
function yield()
 | 
			
		||||
  if os.clock() - yieldTime > 2 then
 | 
			
		||||
    sleep(0)
 | 
			
		||||
    yieldTime = os.clock()
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function arr(n, init)
 | 
			
		||||
  local a = {}
 | 
			
		||||
  for _ = 1, n do
 | 
			
		||||
    table.insert(a, init)
 | 
			
		||||
  end
 | 
			
		||||
  return a
 | 
			
		||||
end
 | 
			
		||||
function arr2(x, y, init)
 | 
			
		||||
  local a = {}
 | 
			
		||||
  for _ = 1, y do
 | 
			
		||||
    table.insert(a, arr(x, init))
 | 
			
		||||
  end
 | 
			
		||||
  return a
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local dirMap = {
 | 
			
		||||
  ["0,-1"] = 0,
 | 
			
		||||
  ["0,1"] = 0.5,
 | 
			
		||||
  ["1,0"] = 0.25,
 | 
			
		||||
  ["-1,0"] = -0.25,
 | 
			
		||||
}
 | 
			
		||||
function direction(v)
 | 
			
		||||
  local d = dirMap[v.x..","..v.z]
 | 
			
		||||
  assert(d)
 | 
			
		||||
  return d
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function findDistances(points)
 | 
			
		||||
  local dist = arr2(#points, #points, math.huge)
 | 
			
		||||
  -- local prev = arr2(#points, #points)
 | 
			
		||||
 | 
			
		||||
  for i = 1, #points do
 | 
			
		||||
    for j = 1, #points do
 | 
			
		||||
      if i ~= j then
 | 
			
		||||
        dist[i][j] = (points[i] - points[j]):length()
 | 
			
		||||
        -- prev[i][j] = i
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  for i = 1, #points do
 | 
			
		||||
    dist[i][i] = 0
 | 
			
		||||
    -- prev[i][i] = i
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  return dist
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function groupOres(ores, dist, offset)
 | 
			
		||||
  if not offset then
 | 
			
		||||
    offset = 0
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local groups = {}
 | 
			
		||||
  for i, o in ipairs(ores) do
 | 
			
		||||
    table.insert(groups, {
 | 
			
		||||
      ["type"] = o.name,
 | 
			
		||||
      ["blocks"] = {
 | 
			
		||||
        [i] = vector.new(o.x, o.y, o.z),
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local didJoin = true
 | 
			
		||||
  while didJoin do
 | 
			
		||||
    didJoin = false
 | 
			
		||||
    for i, io in pairs(groups) do
 | 
			
		||||
      for x, a in pairs(io.blocks) do
 | 
			
		||||
        for j, jo in pairs(groups) do
 | 
			
		||||
          if i ~= j and jo.type == io.type then
 | 
			
		||||
            for y, b in pairs(jo.blocks) do
 | 
			
		||||
              -- yield()
 | 
			
		||||
              assert(a ~= b)
 | 
			
		||||
              if dist[offset + x][offset + y] == 1 then
 | 
			
		||||
                for z, p in pairs(jo.blocks) do
 | 
			
		||||
                  io.blocks[z] = p
 | 
			
		||||
                end
 | 
			
		||||
                groups[j] = nil
 | 
			
		||||
                didJoin = true
 | 
			
		||||
                break
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local retGroups = {}
 | 
			
		||||
  for _, g in pairs(groups) do
 | 
			
		||||
    local v = vector.new(0, 0, 0)
 | 
			
		||||
    local retBlocks = {}
 | 
			
		||||
    for _, p in pairs(g.blocks) do
 | 
			
		||||
      v = v + p
 | 
			
		||||
      table.insert(retBlocks, p)
 | 
			
		||||
    end
 | 
			
		||||
    g.blocks = retBlocks
 | 
			
		||||
    g.center = v / #g.blocks
 | 
			
		||||
    assert(g.center)
 | 
			
		||||
    table.insert(retGroups, g)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  return retGroups
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function pathThrough(points, dist, s, e)
 | 
			
		||||
  -- local n = #points
 | 
			
		||||
 | 
			
		||||
  -- dist[s][n] = 0
 | 
			
		||||
  -- dist[n][s] = 0
 | 
			
		||||
  -- dist[e][n] = 0
 | 
			
		||||
  -- dist[n][e] = 0
 | 
			
		||||
 | 
			
		||||
  -- for k = 1, #points do
 | 
			
		||||
  --   for i = 1, #points do
 | 
			
		||||
  --     for j = 1, #points do
 | 
			
		||||
  --       if dist[i][j] > dist[i][k] + dist[k][j] then
 | 
			
		||||
  --         dist[i][j] = dist[i][k] + dist[k][j]
 | 
			
		||||
  --         prev[i][j] = prev[k][j]
 | 
			
		||||
  --       end
 | 
			
		||||
  --     end
 | 
			
		||||
  --     yield()
 | 
			
		||||
  --   end
 | 
			
		||||
  -- end
 | 
			
		||||
 | 
			
		||||
  -- local path = {e}
 | 
			
		||||
  -- while s ~= e do
 | 
			
		||||
  --   e = prev[s][e]
 | 
			
		||||
  --   table.insert(path, 1, e)
 | 
			
		||||
  -- end
 | 
			
		||||
 | 
			
		||||
  -- return path
 | 
			
		||||
 | 
			
		||||
  -- nearest neighbour
 | 
			
		||||
  local unvisited = {}
 | 
			
		||||
  local ui = nil
 | 
			
		||||
  for i = 1, #points do
 | 
			
		||||
    if not e or i ~= e then
 | 
			
		||||
      table.insert(unvisited, i)
 | 
			
		||||
    end
 | 
			
		||||
    if i == s then
 | 
			
		||||
      ui = #unvisited
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  assert(ui)
 | 
			
		||||
 | 
			
		||||
  local visited = {}
 | 
			
		||||
  local route = {}
 | 
			
		||||
  while #unvisited ~= 1 do
 | 
			
		||||
    local u = unvisited[ui]
 | 
			
		||||
    table.remove(unvisited, ui)
 | 
			
		||||
 | 
			
		||||
    local cDist = math.huge
 | 
			
		||||
    local closest = nil
 | 
			
		||||
    for ii = 1, #unvisited do
 | 
			
		||||
      local i = unvisited[ii]
 | 
			
		||||
      assert(i ~= u)
 | 
			
		||||
      if dist[u][i] < cDist then
 | 
			
		||||
        cDist = dist[u][i]
 | 
			
		||||
        closest = ii
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    assert(closest)
 | 
			
		||||
    table.insert(route, unvisited[closest])
 | 
			
		||||
    ui = closest
 | 
			
		||||
  end
 | 
			
		||||
  if e then
 | 
			
		||||
    table.insert(route, e)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  return route
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- A*
 | 
			
		||||
function findPath(s, e)
 | 
			
		||||
  local points = {}
 | 
			
		||||
  function addPoint(p)
 | 
			
		||||
    local k = p:tostring()
 | 
			
		||||
    if not points[k] then
 | 
			
		||||
      points[k] = p
 | 
			
		||||
    end
 | 
			
		||||
    return k
 | 
			
		||||
  end
 | 
			
		||||
  s = addPoint(s)
 | 
			
		||||
  e = addPoint(e)
 | 
			
		||||
 | 
			
		||||
  function h(i)
 | 
			
		||||
    return (points[e] - points[i]):length()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  function neighbors(k)
 | 
			
		||||
    local p = points[k]
 | 
			
		||||
    local ns = {
 | 
			
		||||
      p + vector.new( 1,  0,  0),
 | 
			
		||||
      p + vector.new( 0,  1,  0),
 | 
			
		||||
      p + vector.new( 0,  0,  1),
 | 
			
		||||
      p + vector.new(-1,  0,  0),
 | 
			
		||||
      p + vector.new( 0, -1,  0),
 | 
			
		||||
      p + vector.new( 0,  0, -1),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for i, n in ipairs(ns) do
 | 
			
		||||
      ns[i] = addPoint(n)
 | 
			
		||||
    end
 | 
			
		||||
    return ns
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local gScoreT = {}
 | 
			
		||||
  gScoreT[s] = 0
 | 
			
		||||
  function gScore(i)
 | 
			
		||||
    if gScoreT[i] then
 | 
			
		||||
      return gScoreT[i]
 | 
			
		||||
    else
 | 
			
		||||
      return math.huge
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local fScoreT = {}
 | 
			
		||||
  fScoreT[s] = h(s)
 | 
			
		||||
  function fScore(i)
 | 
			
		||||
    if fScoreT[i] then
 | 
			
		||||
      return fScoreT[i]
 | 
			
		||||
    else
 | 
			
		||||
      return math.huge
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local openSet = PriorityQueue(function(a, b)
 | 
			
		||||
    return fScore(a) < fScore(b)
 | 
			
		||||
  end)
 | 
			
		||||
  openSet:enqueue(s, s)
 | 
			
		||||
 | 
			
		||||
  local prev = {}
 | 
			
		||||
  local found = false
 | 
			
		||||
  while not openSet:empty() do
 | 
			
		||||
    local current = openSet:dequeue()
 | 
			
		||||
    if current == e then
 | 
			
		||||
      found = true
 | 
			
		||||
      break
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    local ns = neighbors(current)
 | 
			
		||||
    for _, n in ipairs(ns) do
 | 
			
		||||
      tentativeG = gScore(current) + 1
 | 
			
		||||
      if tentativeG < gScore(n) then
 | 
			
		||||
        prev[n] = current
 | 
			
		||||
        gScoreT[n] = tentativeG
 | 
			
		||||
        fScoreT[n] = tentativeG + h(n)
 | 
			
		||||
        if not openSet:contains(n) then
 | 
			
		||||
          openSet:enqueue(n, n)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  assert(found)
 | 
			
		||||
 | 
			
		||||
  local path = {points[e]}
 | 
			
		||||
  local current = e
 | 
			
		||||
  while prev[current] do
 | 
			
		||||
    current = prev[current]
 | 
			
		||||
    table.insert(path, 1, points[current])
 | 
			
		||||
  end
 | 
			
		||||
  return path
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local ORES = Set{
 | 
			
		||||
  "minecraft:coal_ore",
 | 
			
		||||
  "minecraft:deepslate_coal_ore",
 | 
			
		||||
  "minecraft:iron_ore",
 | 
			
		||||
  "minecraft:deepslate_iron_ore",
 | 
			
		||||
  "minecraft:copper_ore",
 | 
			
		||||
  "minecraft:deepslate_copper_ore",
 | 
			
		||||
  "minecraft:gold_ore",
 | 
			
		||||
  "minecraft:deepslate_gold_ore",
 | 
			
		||||
  "minecraft:redstone_ore",
 | 
			
		||||
  "minecraft:deepslate_redstone_ore",
 | 
			
		||||
  "minecraft:emerald_ore",
 | 
			
		||||
  "minecraft:deepslate_emerald_ore",
 | 
			
		||||
  "minecraft:lapis_ore",
 | 
			
		||||
  "minecraft:deepslate_lapis_ore",
 | 
			
		||||
  "minecraft:diamond_ore",
 | 
			
		||||
  "minecraft:deepslate_diamond_ore",
 | 
			
		||||
 | 
			
		||||
  "create:ochrum",
 | 
			
		||||
  "create:zinc_ore",
 | 
			
		||||
  "create:deepslate_zinc_ore",
 | 
			
		||||
  "create_new_age:thorium_ore",
 | 
			
		||||
 | 
			
		||||
  "powah:deepslate_uraninite_ore_poor",
 | 
			
		||||
  "powah:deepslate_uraninite_ore",
 | 
			
		||||
  "powah:deepslate_uraninite_ore_dense",
 | 
			
		||||
  "powah:uraninite_ore_poor",
 | 
			
		||||
  "powah:uraninite_ore",
 | 
			
		||||
  "powah:uraninite_ore_dense",
 | 
			
		||||
 | 
			
		||||
  -- "minecraft:diamond_ore",
 | 
			
		||||
  -- "minecraft:netherrack",
 | 
			
		||||
}
 | 
			
		||||
local BLACKLIST = Set{
 | 
			
		||||
  "minecraft:diamond_pickaxe",
 | 
			
		||||
  "computercraft:wireless_modem_advanced",
 | 
			
		||||
  "advancedperipherals:geo_scanner",
 | 
			
		||||
  "enderchests:ender_chest",
 | 
			
		||||
  "functionalstorage:ender_drawer",
 | 
			
		||||
  "advancedperipherals:chunk_controller",
 | 
			
		||||
}
 | 
			
		||||
local FUEL = "minecraft:coal"
 | 
			
		||||
local CHANNEL = 1337
 | 
			
		||||
 | 
			
		||||
settings.define("mine.name", {
 | 
			
		||||
  description = "Miner name",
 | 
			
		||||
  default = "DiggyBoi",
 | 
			
		||||
  type = "string",
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function selectItem(item, nbt)
 | 
			
		||||
  for i = 1, 16 do
 | 
			
		||||
    info = turtle.getItemDetail(i)
 | 
			
		||||
    if (not item and not info) or (info and info.name == item and (not nbt or item.nbt == nbt)) then
 | 
			
		||||
      turtle.select(i)
 | 
			
		||||
      return i
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Miner = {}
 | 
			
		||||
function Miner.new()
 | 
			
		||||
  local self = setmetatable({}, { __index = Miner })
 | 
			
		||||
  self.name = settings.get("mine.name")
 | 
			
		||||
  self.equipped = {
 | 
			
		||||
    ["left"] = {
 | 
			
		||||
      ["last"] = "NONE",
 | 
			
		||||
      ["lastNbt"] = nil,
 | 
			
		||||
      ["fun"] = turtle.equipLeft,
 | 
			
		||||
    },
 | 
			
		||||
    ["right"] = {
 | 
			
		||||
      ["last"] = "NONE",
 | 
			
		||||
      ["lastNbt"] = nil,
 | 
			
		||||
      ["fun"] = turtle.equipRight,
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  -- Get upgrades to a known state by "equipping" nothing
 | 
			
		||||
  self:equip(nil, "left")
 | 
			
		||||
  self:equip(nil, "right")
 | 
			
		||||
 | 
			
		||||
  self:equip("advancedperipherals:geo_scanner", "right")
 | 
			
		||||
  self.scanner = peripheral.wrap("right")
 | 
			
		||||
 | 
			
		||||
  self:equip("computercraft:wireless_modem_advanced", "right")
 | 
			
		||||
  self.modem = peripheral.wrap("right")
 | 
			
		||||
  self.modem.open(CHANNEL)
 | 
			
		||||
 | 
			
		||||
  self.pos = vector.new(0, 0, 0)
 | 
			
		||||
  self:doGPS(true)
 | 
			
		||||
 | 
			
		||||
  self:equip("advancedperipherals:chunk_controller", "left")
 | 
			
		||||
 | 
			
		||||
  self.i = 1
 | 
			
		||||
  return self
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:equip(item, side, nbt)
 | 
			
		||||
  local e = self.equipped[side]
 | 
			
		||||
  if item == e.last and nbt == e.lastNbt then
 | 
			
		||||
    return
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  assert(selectItem(item, nbt))
 | 
			
		||||
  e.fun()
 | 
			
		||||
  e.last = item
 | 
			
		||||
  e.lastNbt = nbt
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:sendMessage(type, msg)
 | 
			
		||||
  self:equip("computercraft:wireless_modem_advanced", "right")
 | 
			
		||||
  msg["type"] = type
 | 
			
		||||
  msg["name"] = self.name
 | 
			
		||||
  msg["pos"] = self.absolutePos
 | 
			
		||||
  self.modem.transmit(CHANNEL, CHANNEL, msg)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:dig(dir)
 | 
			
		||||
  self:equip("minecraft:diamond_pickaxe", "right")
 | 
			
		||||
  if dir == "up" then
 | 
			
		||||
    assert(turtle.digUp())
 | 
			
		||||
  elseif dir == "forward" then
 | 
			
		||||
    assert(turtle.dig())
 | 
			
		||||
  elseif dir == "down" then
 | 
			
		||||
    assert(turtle.digDown())
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:xchgItems()
 | 
			
		||||
  local slots = {}
 | 
			
		||||
  local emptySlots = 0
 | 
			
		||||
  for i = 1, 16 do
 | 
			
		||||
    local info = turtle.getItemDetail(i)
 | 
			
		||||
    if info then
 | 
			
		||||
      assert(info.name)
 | 
			
		||||
      if not BLACKLIST[info.name] then
 | 
			
		||||
        slots[i] = info
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      emptySlots = emptySlots + 1
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if emptySlots >= 2 and turtle.getFuelLevel() >= 1000 then
 | 
			
		||||
    return
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if turtle.detectDown() then
 | 
			
		||||
    self:dig("down")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if emptySlots < 2 then
 | 
			
		||||
    selectItem("enderchests:ender_chest")
 | 
			
		||||
    assert(turtle.placeDown())
 | 
			
		||||
 | 
			
		||||
    for i, info in pairs(slots) do
 | 
			
		||||
      assert(turtle.select(i))
 | 
			
		||||
      assert(turtle.dropDown())
 | 
			
		||||
    end
 | 
			
		||||
    self:dig("down")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  if turtle.getFuelLevel() < 1000 then
 | 
			
		||||
    selectItem("functionalstorage:ender_drawer")
 | 
			
		||||
    assert(turtle.placeDown())
 | 
			
		||||
    assert(turtle.suckDown())
 | 
			
		||||
    assert(turtle.refuel())
 | 
			
		||||
    self:dig("down")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:safeMove(dir)
 | 
			
		||||
  if dir == "up" then
 | 
			
		||||
    while turtle.detectUp() do
 | 
			
		||||
      self:dig("up")
 | 
			
		||||
    end
 | 
			
		||||
    assert(turtle.up())
 | 
			
		||||
  elseif dir == "forward" then
 | 
			
		||||
    while turtle.detect() do
 | 
			
		||||
      self:dig("forward")
 | 
			
		||||
    end
 | 
			
		||||
    assert(turtle.forward())
 | 
			
		||||
  else
 | 
			
		||||
    if turtle.detectDown() then
 | 
			
		||||
      self:dig("down")
 | 
			
		||||
    end
 | 
			
		||||
    assert(turtle.down())
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:doGPS(orientation)
 | 
			
		||||
  local x, y, z = gps.locate()
 | 
			
		||||
  assert(x)
 | 
			
		||||
  self.absolutePos = vector.new(x, y, z)
 | 
			
		||||
 | 
			
		||||
  if orientation then
 | 
			
		||||
    assert(turtle.forward())
 | 
			
		||||
    x, y, z = gps.locate()
 | 
			
		||||
    assert(x)
 | 
			
		||||
    self.dir = vector.new(x, y, z) - self.absolutePos
 | 
			
		||||
    assert(turtle.back())
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:findOres(radius)
 | 
			
		||||
  self:equip("advancedperipherals:geo_scanner", "right")
 | 
			
		||||
  local info, err = self.scanner.scan(radius)
 | 
			
		||||
  assert(info, err)
 | 
			
		||||
 | 
			
		||||
  local found = {}
 | 
			
		||||
  for _, b in ipairs(info) do
 | 
			
		||||
    -- only ores and avoid bedrock
 | 
			
		||||
    if ORES[b.name] and self.absolutePos.y + b.y >= -58 then
 | 
			
		||||
      table.insert(found, b)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return found
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:faceDir(newDir)
 | 
			
		||||
  local deltaDir = direction(newDir) - direction(self.dir)
 | 
			
		||||
  if deltaDir == 0.25 or deltaDir == -0.75 then
 | 
			
		||||
    assert(turtle.turnRight())
 | 
			
		||||
    self.dir = newDir
 | 
			
		||||
  elseif deltaDir == -0.25 or deltaDir == 0.75 then
 | 
			
		||||
    assert(turtle.turnLeft())
 | 
			
		||||
    self.dir = newDir
 | 
			
		||||
  elseif deltaDir == 0.5 or deltaDir == -0.5 then
 | 
			
		||||
    assert(turtle.turnRight())
 | 
			
		||||
    assert(turtle.turnRight())
 | 
			
		||||
    self.dir = newDir
 | 
			
		||||
  elseif deltaDir == 0 then
 | 
			
		||||
    -- nothing to do :)
 | 
			
		||||
  else
 | 
			
		||||
    assert(false, "invalid delta dir "..deltaDir)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:navigateThrough(path)
 | 
			
		||||
  for _, p in ipairs(path) do
 | 
			
		||||
    -- print("move from to", self.pos, p)
 | 
			
		||||
    local delta = p - self.pos
 | 
			
		||||
    assert(delta:length() ~= 0)
 | 
			
		||||
    -- print("doing", delta)
 | 
			
		||||
    if delta:length() ~= 1 then
 | 
			
		||||
      -- print("path finding between points")
 | 
			
		||||
      local path = findPath(self.pos, p)
 | 
			
		||||
      table.remove(path, 1)
 | 
			
		||||
      table.remove(path, #path)
 | 
			
		||||
      assert(#path ~= 0)
 | 
			
		||||
 | 
			
		||||
      self:navigateThrough(path)
 | 
			
		||||
      delta = p - self.pos
 | 
			
		||||
    end
 | 
			
		||||
    assert(delta:length() == 1)
 | 
			
		||||
 | 
			
		||||
    local moveDir = nil
 | 
			
		||||
    if delta.y == 1 then
 | 
			
		||||
      moveDir = "up"
 | 
			
		||||
    elseif delta.y == -1 then
 | 
			
		||||
      moveDir = "down"
 | 
			
		||||
    else
 | 
			
		||||
      self:faceDir(delta)
 | 
			
		||||
      moveDir = "forward"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    self:safeMove(moveDir)
 | 
			
		||||
    self.pos = p
 | 
			
		||||
    self.absolutePos = self.absolutePos + delta
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Miner:mineOres(radius)
 | 
			
		||||
  local startingDir = self.dir
 | 
			
		||||
  self.pos = vector.new(0, 0, 0)
 | 
			
		||||
 | 
			
		||||
  local ores = self:findOres(radius)
 | 
			
		||||
  local orePoints = {}
 | 
			
		||||
  for _, b in ipairs(ores) do
 | 
			
		||||
    table.insert(orePoints, vector.new(b.x, b.y, b.z))
 | 
			
		||||
  end
 | 
			
		||||
  local veins = groupOres(ores, findDistances(orePoints))
 | 
			
		||||
  print("Found "..#ores.." ores ("..#veins.." veins)")
 | 
			
		||||
 | 
			
		||||
  local veinsPoints = {
 | 
			
		||||
    self.pos,
 | 
			
		||||
    self.dir * radius * 2,
 | 
			
		||||
  }
 | 
			
		||||
  for _, v in ipairs(veins) do
 | 
			
		||||
    table.insert(veinsPoints, v.center)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local veinsPath = pathThrough(veinsPoints, findDistances(veinsPoints), 1, 2)
 | 
			
		||||
  -- strip out end point
 | 
			
		||||
  table.remove(veinsPath, #veinsPath)
 | 
			
		||||
 | 
			
		||||
  local veinsSummary = {}
 | 
			
		||||
  for _, i in ipairs(veinsPath) do
 | 
			
		||||
    local v = veins[i - 2]
 | 
			
		||||
    table.insert(veinsSummary, {
 | 
			
		||||
      pos = self.absolutePos + v.center,
 | 
			
		||||
      type = v.type,
 | 
			
		||||
      count = #v.blocks,
 | 
			
		||||
    })
 | 
			
		||||
  end
 | 
			
		||||
  self:sendMessage("iterationStart", {
 | 
			
		||||
    i = self.i,
 | 
			
		||||
    oreCount = #ores,
 | 
			
		||||
    veins = veinsSummary,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  for pi, i in ipairs(veinsPath) do
 | 
			
		||||
    self:xchgItems()
 | 
			
		||||
 | 
			
		||||
    local vein = veins[i - 2]
 | 
			
		||||
    local closest = nil
 | 
			
		||||
    local cDist = math.huge
 | 
			
		||||
    for _, b in ipairs(vein.blocks) do
 | 
			
		||||
      local d = (b - self.pos):length()
 | 
			
		||||
      if d < cDist then
 | 
			
		||||
        closest = b
 | 
			
		||||
        cDist = d
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    assert(closest)
 | 
			
		||||
 | 
			
		||||
    local pathToStart = findPath(self.pos, closest)
 | 
			
		||||
    table.remove(pathToStart, 1)
 | 
			
		||||
    table.remove(pathToStart, #pathToStart)
 | 
			
		||||
 | 
			
		||||
    print("Moving to vein of " .. #vein.blocks .. " " .. vein.type .. " " .. cDist .. " blocks away starting at", closest)
 | 
			
		||||
    self:navigateThrough(pathToStart)
 | 
			
		||||
 | 
			
		||||
    local veinPoints = {self.pos}
 | 
			
		||||
    for _, b in ipairs(vein.blocks) do
 | 
			
		||||
      table.insert(veinPoints, b)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    local veinPathI = pathThrough(veinPoints, findDistances(veinPoints), 1)
 | 
			
		||||
    local veinPath = {}
 | 
			
		||||
    for _, i in ipairs(veinPathI) do
 | 
			
		||||
      table.insert(veinPath, veinPoints[i])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    print("Digging through vein (" .. #vein.blocks .. " blocks)")
 | 
			
		||||
    self:sendMessage("veinStart", {
 | 
			
		||||
      i = pi,
 | 
			
		||||
      total = #veins,
 | 
			
		||||
      oreType = vein.type,
 | 
			
		||||
      oreCount = #vein.blocks,
 | 
			
		||||
    })
 | 
			
		||||
    self:navigateThrough(veinPath)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  self:navigateThrough({veinsPoints[2]})
 | 
			
		||||
  self:faceDir(startingDir)
 | 
			
		||||
  self.i = self.i + 1
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local miner = Miner.new()
 | 
			
		||||
while true do
 | 
			
		||||
  miner:mineOres(16)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- test code, please ignore
 | 
			
		||||
assert(false)
 | 
			
		||||
 | 
			
		||||
-- local f, err = fs.open("points.csv", "w")
 | 
			
		||||
-- assert(f, err)
 | 
			
		||||
 | 
			
		||||
local points = {
 | 
			
		||||
  vector.new(0, 0, 0),
 | 
			
		||||
  vector.new(0, 0, -16),
 | 
			
		||||
}
 | 
			
		||||
for _, b in ipairs(ores) do
 | 
			
		||||
  -- print("found " .. b.name .. " at " .. b.x .. ", " .. b.y .. ", " .. b.z)
 | 
			
		||||
  table.insert(points, vector.new(b.x, b.y, b.z))
 | 
			
		||||
end
 | 
			
		||||
print("found "..#ores.." ores")
 | 
			
		||||
 | 
			
		||||
local dist = findDistances(points)
 | 
			
		||||
local groups = groupOres(ores, dist, 2)
 | 
			
		||||
 | 
			
		||||
for _, g in ipairs(groups) do
 | 
			
		||||
  print("group of "..#g.blocks.." "..g.type.." at", g.center)
 | 
			
		||||
end
 | 
			
		||||
print(#groups, "groups of ore found")
 | 
			
		||||
 | 
			
		||||
local gPoints = {
 | 
			
		||||
  vector.new(0, 0, 0),
 | 
			
		||||
  vector.new(0, 0, -16),
 | 
			
		||||
}
 | 
			
		||||
for _, g in ipairs(groups) do
 | 
			
		||||
  table.insert(gPoints, g.center)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local gDist = findDistances(gPoints)
 | 
			
		||||
local gPath = pathThrough(gPoints, gDist, 1, 2)
 | 
			
		||||
for _, i in ipairs(gPath) do
 | 
			
		||||
  local p = gPoints[i]
 | 
			
		||||
  f.write(p.x..","..p.y..","..p.z.."\n")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
f.write("MARK\n")
 | 
			
		||||
 | 
			
		||||
-- print(points[1], gPoints[gPath[1]])
 | 
			
		||||
-- local pathTo = findPath(points[1], gPoints[gPath[1]])
 | 
			
		||||
-- for _, p in ipairs(pathTo) do
 | 
			
		||||
--   -- local p = pathTo[i]
 | 
			
		||||
--   -- print(p)
 | 
			
		||||
--   f.write(p.x..","..p.y..","..p.z.."\n")
 | 
			
		||||
-- end
 | 
			
		||||
 | 
			
		||||
miner:mineOres(groups)
 | 
			
		||||
 | 
			
		||||
-- table.remove(pathTo, 1)
 | 
			
		||||
-- table.remove(pathTo, #pathTo)
 | 
			
		||||
-- miner:navigateThrough(pathTo)
 | 
			
		||||
 | 
			
		||||
-- f.write("MARK\n")
 | 
			
		||||
 | 
			
		||||
-- local allPath = pathThrough(points, dist, 1, 2)
 | 
			
		||||
-- for _, i in ipairs(allPath) do
 | 
			
		||||
--   local p = points[i]
 | 
			
		||||
--   -- print(points[i])
 | 
			
		||||
--   f.write(p.x..","..p.y..","..p.z.."\n")
 | 
			
		||||
-- end
 | 
			
		||||
 | 
			
		||||
f.close()
 | 
			
		||||
 | 
			
		||||
-- local f = vector.new(1, 0, 0)
 | 
			
		||||
-- local p = vector.new(0, -1, 7)
 | 
			
		||||
-- local t = vector.new(-1, -1, 7)
 | 
			
		||||
 | 
			
		||||
-- print(direction(t - p) - direction(f))
 | 
			
		||||
							
								
								
									
										185
									
								
								mineMonitor.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,185 @@
 | 
			
		||||
local CHANNEL = 1337
 | 
			
		||||
local TARGET = "devplayer0"
 | 
			
		||||
local ICON_BASE = "https://p.nul.ie/cc/icon/"
 | 
			
		||||
local ORE_ICONS = {
 | 
			
		||||
  ["minecraft:coal_ore"] = "coal",
 | 
			
		||||
  ["minecraft:deepslate_coal_ore"] = "ds-coal",
 | 
			
		||||
  ["minecraft:iron_ore"] = "iron",
 | 
			
		||||
  ["minecraft:deepslate_iron_ore"] = "ds-iron",
 | 
			
		||||
  ["minecraft:copper_ore"] = "copper",
 | 
			
		||||
  ["minecraft:deepslate_copper_ore"] = "ds-copper",
 | 
			
		||||
  ["minecraft:gold_ore"] = "gold",
 | 
			
		||||
  ["minecraft:deepslate_gold_ore"] = "ds-gold",
 | 
			
		||||
  ["minecraft:redstone_ore"] = "redstone",
 | 
			
		||||
  ["minecraft:deepslate_redstone_ore"] = "ds-redstone",
 | 
			
		||||
  ["minecraft:emerald_ore"] = "emerald",
 | 
			
		||||
  ["minecraft:deepslate_emerald_ore"] = "ds-emerald",
 | 
			
		||||
  ["minecraft:lapis_ore"] = "lapis",
 | 
			
		||||
  ["minecraft:deepslate_lapis_ore"] = "ds-lapis",
 | 
			
		||||
  ["minecraft:diamond_ore"] = "diamond",
 | 
			
		||||
  ["minecraft:deepslate_diamond_ore"] = "ds-diamond",
 | 
			
		||||
 | 
			
		||||
  -- ["create:ochrum"] =,
 | 
			
		||||
  -- ["create:zinc_ore"] =,
 | 
			
		||||
  -- ["create:deepslate_zinc_ore"] =,
 | 
			
		||||
  -- ["create_new_age:thorium_ore"] =,
 | 
			
		||||
 | 
			
		||||
  -- ["powah:deepslate_uraninite_ore_poor"] =,
 | 
			
		||||
  -- ["powah:deepslate_uraninite_ore"] =,
 | 
			
		||||
  -- ["powah:deepslate_uraninite_ore_dense"] =,
 | 
			
		||||
  -- ["powah:uraninite_ore_poor"] =,
 | 
			
		||||
  -- ["powah:uraninite_ore"] =,
 | 
			
		||||
  -- ["powah:uraninite_ore_dense"] =,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function oreIcon(t)
 | 
			
		||||
  local i = "generic"
 | 
			
		||||
  if ORE_ICONS[t] then
 | 
			
		||||
    i = ORE_ICONS[t]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  return ICON_BASE .. "ore-" .. i .. ".png"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local modem = peripheral.find("modem")
 | 
			
		||||
-- local monitor = peripheral.find("monitor")
 | 
			
		||||
local chat = peripheral.find("chatBox")
 | 
			
		||||
local cartographer = peripheral.find("cartographer")
 | 
			
		||||
 | 
			
		||||
function vecToStr(v)
 | 
			
		||||
  return v.x .. "," .. v.y .. "," .. v.z
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function markerSet(msg)
 | 
			
		||||
  return "autominer_" .. msg.name
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function setPosMarker(msg)
 | 
			
		||||
  local id = markerSet(msg)
 | 
			
		||||
  local mId = id .. "_pos"
 | 
			
		||||
  cartographer.removeMarker(id, mId)
 | 
			
		||||
  cartographer.addPOIMarker(
 | 
			
		||||
    id, mId, msg.name, msg.name .. "'s position",
 | 
			
		||||
    msg.pos.x, msg.pos.y, msg.pos.z,
 | 
			
		||||
    ICON_BASE .. "turtle.png")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function handleStart(msg)
 | 
			
		||||
  local cMsg = {
 | 
			
		||||
    {text = msg.name, color = "green"},
 | 
			
		||||
    {text = " @ ", color = "white"},
 | 
			
		||||
    {text = vecToStr(msg.pos), color = "aqua"},
 | 
			
		||||
    {text = " found ", color = "white"},
 | 
			
		||||
    {text = tostring(msg.oreCount), color = "red"},
 | 
			
		||||
    {text = " ores (", color = "white"},
 | 
			
		||||
    {text = tostring(#msg.veins), color = "red"},
 | 
			
		||||
    {text = " veins) on iteration ", color = "white"},
 | 
			
		||||
    {text = tostring(msg.i), color = "green"},
 | 
			
		||||
    {text = "!", color = "white"},
 | 
			
		||||
  }
 | 
			
		||||
  local msgJSON = textutils.serializeJSON(cMsg)
 | 
			
		||||
  chat.sendFormattedMessage(msgJSON, "AutoMine")
 | 
			
		||||
 | 
			
		||||
  local id = markerSet(msg)
 | 
			
		||||
  cartographer.addMarkerSet(id, "AutoMiner " .. msg.name)
 | 
			
		||||
  cartographer.clearMarkerSet(id)
 | 
			
		||||
 | 
			
		||||
  setPosMarker(msg)
 | 
			
		||||
 | 
			
		||||
  local orePoints = {}
 | 
			
		||||
  for i, v in ipairs(msg.veins) do
 | 
			
		||||
    table.insert(orePoints, v.pos)
 | 
			
		||||
    cartographer.addPOIMarker(
 | 
			
		||||
      id, id .. "_vein_" .. i, v.count .. " " .. v.type, "Vein of " .. v.count .. " " .. v.type,
 | 
			
		||||
      v.pos.x, v.pos.y, v.pos.z,
 | 
			
		||||
      oreIcon(v.type))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  cartographer.addLineMarker(
 | 
			
		||||
    id, id .. "_vein_path", "Mining path #" .. msg.i, "Current mining path",
 | 
			
		||||
    "#FF00FF", 0.7, 3,
 | 
			
		||||
    orePoints)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function handleVein(msg)
 | 
			
		||||
  local cMsg = {
 | 
			
		||||
    {text = msg.name, color = "green"},
 | 
			
		||||
    {text = " @ ", color = "white"},
 | 
			
		||||
    {text = vecToStr(msg.pos), color = "aqua"},
 | 
			
		||||
    {text = " is mining ", color = "white"},
 | 
			
		||||
    {text = tostring(msg.oreCount), color = "red"},
 | 
			
		||||
    {text = " ", color = "white"},
 | 
			
		||||
    {text = msg.oreType, color = "dark_purple"},
 | 
			
		||||
    {text = " (vein ", color = "white"},
 | 
			
		||||
    {text = tostring(msg.i), color = "red"},
 | 
			
		||||
    {text = " / ", color = "white"},
 | 
			
		||||
    {text = tostring(msg.total), color = "red"},
 | 
			
		||||
    {text = ")!", color = "white"},
 | 
			
		||||
  }
 | 
			
		||||
  local msgJSON = textutils.serializeJSON(cMsg)
 | 
			
		||||
  chat.sendFormattedMessageToPlayer(msgJSON, TARGET, "AutoMine")
 | 
			
		||||
 | 
			
		||||
  local id = markerSet(msg)
 | 
			
		||||
  setPosMarker(msg)
 | 
			
		||||
  if msg.i ~= 1 then
 | 
			
		||||
    cartographer.removeMarker(id, id .. "_vein_" .. (msg.i - 1))
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
cartographer.refreshIntegrations()
 | 
			
		||||
 | 
			
		||||
modem.open(CHANNEL)
 | 
			
		||||
 | 
			
		||||
-- handleStart({
 | 
			
		||||
--   i = 69,
 | 
			
		||||
--   oreCount = 1337,
 | 
			
		||||
--   veins = {
 | 
			
		||||
--     {
 | 
			
		||||
--       pos = {x = 297, y = 124, z = 1935},
 | 
			
		||||
--       type = "minecraft:coal_ore",
 | 
			
		||||
--       count = 12,
 | 
			
		||||
--     },
 | 
			
		||||
--     {
 | 
			
		||||
--       pos = {x = 297, y = 134, z = 1935},
 | 
			
		||||
--       type = "minecraft:iron_ore",
 | 
			
		||||
--       count = 7,
 | 
			
		||||
--     },
 | 
			
		||||
--     {
 | 
			
		||||
--       pos = {x = 400, y = 134, z = 1935},
 | 
			
		||||
--       type = "minecraft:diamond_ore",
 | 
			
		||||
--       count = 2,
 | 
			
		||||
--     },
 | 
			
		||||
--     {
 | 
			
		||||
--       pos = {x = 400, y = 134, z = 1975},
 | 
			
		||||
--       type = "minecraft:sussy_ore",
 | 
			
		||||
--       count = 7,
 | 
			
		||||
--     }
 | 
			
		||||
--   },
 | 
			
		||||
--   name = "test",
 | 
			
		||||
--   pos = {x = 297, y = 154, z = 1935},
 | 
			
		||||
-- })
 | 
			
		||||
-- handleVein({
 | 
			
		||||
--   i = 2,
 | 
			
		||||
--   total = 69,
 | 
			
		||||
--   oreCount = 1337,
 | 
			
		||||
--   oreType = "minecraft:yeet",
 | 
			
		||||
--   name = "test",
 | 
			
		||||
--   pos = {x = 1, y = 2, z = 3},
 | 
			
		||||
-- })
 | 
			
		||||
-- assert(false)
 | 
			
		||||
 | 
			
		||||
while true do
 | 
			
		||||
  local event, side, channel, replyChannel, msg, distance = os.pullEvent("modem_message")
 | 
			
		||||
  if channel == CHANNEL then
 | 
			
		||||
    if msg.type == "iterationStart" then
 | 
			
		||||
      handleStart(msg)
 | 
			
		||||
      -- sendChat(msg.name.." found "..msg.oreCount.." ores ("..msg.veinCount.." veins)")
 | 
			
		||||
      -- for _, v in ipairs(msg.path) do
 | 
			
		||||
      --   print("vein path "..v.x..", "..v.y..", "..v.z)
 | 
			
		||||
      -- end
 | 
			
		||||
    elseif msg.type == "veinStart" then
 | 
			
		||||
      handleVein(msg)
 | 
			
		||||
      -- sendChat(msg.name.." is mining "..msg.count.." "..msg.oreType.." (vein "..msg.i..") @ "..msg.pos.x..", "..msg.pos.y..", "..msg.pos.z)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										134
									
								
								tree.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
			
		||||
local dirt = "minecraft:dirt"
 | 
			
		||||
local sapling = "minecraft:oak_sapling"
 | 
			
		||||
local meal = "minecraft:bone_meal"
 | 
			
		||||
local log = "minecraft:oak_log"
 | 
			
		||||
local apple = "minecraft:apple"
 | 
			
		||||
local stick = "minecraft:stick"
 | 
			
		||||
local items = {
 | 
			
		||||
  sapling, meal, log, apple, stick
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local mealCount = 16
 | 
			
		||||
 | 
			
		||||
function selectItem(item)
 | 
			
		||||
  for i = 1, 16 do
 | 
			
		||||
    info = turtle.getItemDetail(i)
 | 
			
		||||
    if info and info.name == item then
 | 
			
		||||
      turtle.select(i)
 | 
			
		||||
      return i
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Farmer = {}
 | 
			
		||||
function Farmer:new(invName)
 | 
			
		||||
  local t = setmetatable({}, { __index = Farmer })
 | 
			
		||||
  t.invName = invName
 | 
			
		||||
  return t
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Farmer:connectInv()
 | 
			
		||||
  local m = peripheral.find("modem")
 | 
			
		||||
  self.mName= m.getNameLocal()
 | 
			
		||||
  self.inv = peripheral.wrap(self.invName)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Farmer:unload(item)
 | 
			
		||||
  for i = 1, 16 do
 | 
			
		||||
    info = turtle.getItemDetail(i)
 | 
			
		||||
    if info and info.name == item then
 | 
			
		||||
      -- print(item, self.mName, i)
 | 
			
		||||
      self.inv.pullItems(self.mName, i)
 | 
			
		||||
      sleep(0.5)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Farmer:yoink(item, count)
 | 
			
		||||
  for i, info in pairs(self.inv.list()) do
 | 
			
		||||
    if info.name == item then
 | 
			
		||||
      if info.count >= count then
 | 
			
		||||
        self.inv.pushItems(self.mName, i, count)
 | 
			
		||||
        sleep(0.5)
 | 
			
		||||
        return true
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  return false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Farmer:farmTree(noMeal)
 | 
			
		||||
  assert(turtle.turnRight())
 | 
			
		||||
  assert(turtle.turnRight())
 | 
			
		||||
  assert(turtle.forward())
 | 
			
		||||
  assert(turtle.forward())
 | 
			
		||||
  assert(turtle.down())
 | 
			
		||||
 | 
			
		||||
  assert(selectItem(dirt))
 | 
			
		||||
  assert(turtle.place())
 | 
			
		||||
  assert(turtle.up())
 | 
			
		||||
  assert(selectItem(sapling))
 | 
			
		||||
  assert(turtle.place())
 | 
			
		||||
 | 
			
		||||
  if noMeal then
 | 
			
		||||
    while true do
 | 
			
		||||
      local present, info = turtle.inspect()
 | 
			
		||||
      assert(present)
 | 
			
		||||
      if info.name == log then
 | 
			
		||||
        break
 | 
			
		||||
      end
 | 
			
		||||
      sleep(20)
 | 
			
		||||
    end
 | 
			
		||||
  else
 | 
			
		||||
    assert(selectItem(meal))
 | 
			
		||||
    local grown = false
 | 
			
		||||
    for i = 1, mealCount do
 | 
			
		||||
      turtle.place()
 | 
			
		||||
      sleep(1)
 | 
			
		||||
      local present, info = turtle.inspect()
 | 
			
		||||
      assert(present)
 | 
			
		||||
      if info.name == log then
 | 
			
		||||
        grown = true
 | 
			
		||||
        break
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    if not grown then
 | 
			
		||||
      assert(false, "failed to grow tree")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  turtle.select(1)
 | 
			
		||||
  assert(turtle.dig())
 | 
			
		||||
  assert(turtle.down())
 | 
			
		||||
  assert(turtle.dig())
 | 
			
		||||
  assert(turtle.up())
 | 
			
		||||
  sleep(5)
 | 
			
		||||
  assert(turtle.down())
 | 
			
		||||
  for i = 1, 8 do
 | 
			
		||||
    sleep(0.5)
 | 
			
		||||
    turtle.suck()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  assert(turtle.up())
 | 
			
		||||
  assert(turtle.turnRight())
 | 
			
		||||
  assert(turtle.turnRight())
 | 
			
		||||
  assert(turtle.forward())
 | 
			
		||||
  assert(turtle.forward())
 | 
			
		||||
  sleep(0.5)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Farmer:xchgItems()
 | 
			
		||||
  self:connectInv()
 | 
			
		||||
  for _, i in pairs(items) do
 | 
			
		||||
    self:unload(i)
 | 
			
		||||
  end
 | 
			
		||||
  assert(self:yoink(sapling, 1))
 | 
			
		||||
  return self:yoink(meal, mealCount)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local f = Farmer:new("ae2:interface_0")
 | 
			
		||||
while true do
 | 
			
		||||
  local noMeal = not f:xchgItems()
 | 
			
		||||
  f:farmTree(noMeal)
 | 
			
		||||
end
 | 
			
		||||