Робот с геосканером. Часть #3 [первый тест]
Ядро копателя готово, теперь можно и пощупать.
Напишем пробную функцию сканирования и добычи одного слоя.
Сначала откалибруем компас и зададим таблицу с координатами сканируемых квадратов.
Затем, отсканируем квадрат 16 на 16 блоков, выведем количество обнаруженных блоков.
И в цикле обойдем все метки. Вроде бы все просто.
Ах, да... будем искать ближайший блок к текущей позиции, чтобы быстрее закончить работу.
Есть много подходов к определению расстояний.
Например квадрат гипотенузы равен сумме квадратов катетов, формула для нашего случая будет math.sqrt((X-x)^2+(Z-z)^2), где X,Z - координаты робота, x,z - координаты метки, можно выкинуть квадратный корень, в нашем случае бесполезный и даже вредный. Но тут есть одно "но", мы получили гипотенузу, а это наименьшее расстояние между точками, а роботы по диагонали не ходят.
Я буду вычислять дельту между точками, суммируя реальное расстояние, которое пройдет робот по формуле math.abs(X-x)+math.abs(Z-z)
Эта операция в сферическом вакууме потребляет на 5% больше процессорного времени, чем предыдущая, но с лихвой окупается сэкономленными шагами.
В цикле будем обходить таблицу с метками, до каждой вычисляя расстояние, самый лучший результат с индексом будем хранить в отдельных переменных. По окончании работы цикла, будем посылать робота в ближайшую точку.
Код всей тестовой программы под спойлером.
local component = require('component') -- подгрузить обертку из OpenOS
local X, Y, Z, D = 0, 0, 0, 0 -- переменные локальной системы координат
local WORLD = {x = {}, y = {}, z = {}}
local border = false -- указатель статуса обнаружения бедрока
local function add_component(name) -- получение прокси компонента
name = component.list(name)() -- получить адрес по имени
if name then -- если есть адрес
return component.proxy(name) -- вернуть прокси
end
end
local robot = add_component('robot') -- загрузка компонента
local geolyzer = add_component('geolyzer')
local function step(side) -- функция движения на 1 блок
if not robot.swing(side) and robot.detect(side) then -- если блок нельзя разрушить
print('bedrock')
os.exit() -- временная заглушка
else
while robot.swing(side) do end -- копать пока возможно
end
if robot.move(side) then -- если робот сдвинулся, обновить координаты
if side == 0 then
Y = Y-1
elseif side == 1 then
Y = Y+1
elseif side == 3 then
if D == 0 then
Z = Z+1
elseif D == 1 then
X = X-1
elseif D == 2 then
Z = Z-1
else
X = X+1
end
end
end
if #WORLD.x ~= 0 then -- если таблица меток не пуста
for i = 1, #WORLD.x do -- пройти по всем позициям
if X == WORLD.x and (Y-1 <= WORLD.y and Y+1 >= WORLD.y) and Z == WORLD.z then
if WORLD.y == Y+1 then -- добыть блок сверху, если есть
robot.swing(1)
elseif WORLD.y == Y-1 then -- добыть блок снизу
robot.swing(0)
end
table.remove(WORLD.x, i) -- удалить метку из таблицы
table.remove(WORLD.y, i)
table.remove(WORLD.z, i)
end
end
end
end
local function turn(side) -- поворот в сторону
side = side or false
if robot.turn(side) and D then -- если робот повернулся, обновить переменную направления
if side then
D = (D+1)%4
else
D = (D-1)%4
end
end
end
local function smart_turn(side) -- поворот в определенную сторону света
while D ~= side do
turn((side-D)%4==1)
end
end
local function go(x, y, z) -- переход по указанным координатам
while Y ~= y do
if Y < y then
step(1)
elseif Y > y then
step(0)
end
end
if X < x then
smart_turn(3)
elseif X > x then
smart_turn(1)
end
while X ~= x do
step(3)
end
if Z < z then
smart_turn(0)
elseif Z > z then
smart_turn(2)
end
while Z ~= z do
step(3)
end
end
local function scan(xx, zz) -- сканирование квадрата x8 относительно робота
local raw, index = geolyzer.scan(xx, zz, -1, 8, 8, 1), 1 -- получить сырые данные, установить индекс в начало таблицы
for z = zz, zz+7 do -- развертка данных по z
for x = xx, xx+7 do -- развертка данных по х
if raw[index] >= 2.3 and raw[index] <= 40 then -- если обнаружен блок с плотностью от 2.3 до 40
table.insert(WORLD.x, X+x) --| записать метку в список
table.insert(WORLD.y, Y-1) --| с коррекцией локальных
table.insert(WORLD.z, Z+z) --| координат геосканера
elseif raw[index] < -0.31 then -- если обнаружен блок с отрицательной плотностью
border = true -- сделать отметку
end
index = index + 1 -- переход к следующему индексу сырых даннх
end
end
end
local function compass() -- определение сторон света
local sides = {{-1,0}, {0,-1}, {1,0}, [0]={0,1}} -- привязка значений сторон света к смежным блокам
D = nil -- обнуление текущего направления
while not D do -- пока направление не найдено
for n = 0, 3 do -- перебор сторон света
robot.swing(3) -- разрушение блока
if geolyzer.scan(sides[n][1], sides[n][2], 0, 1, 1, 1)[1] == 0 and robot.place(3) then -- тестовое сканирование и установка блока
if geolyzer.scan(sides[n][1], sides[n][2], 0, 1, 1, 1)[1] > 0 then -- если обнаружена разница в сканах
D = n -- установить новое направление
break -- выйти из цикла
end
end
end
turn() -- задействовать простой поворот
end
end
local function test_mine()
local quads = {{-7, -7}, {-7, 1}, {1, -7}, {1, 1}} -- координаты сканируемых квадратов
compass() -- калибровка компаса
for q = 1, 4 do -- сканирование квадрата x16 в 4 подхода
scan(table.unpack(quads[q]))
end
print(#WORLD.x) -- вывод количества обнаруженных блоков
while #WORLD.x ~= 0 do -- выполнять, пока есть метки
local n_delta, c_delta, current = math.huge, math.huge -- переменные для хранения временного и минимального расстояния
for index = 1, #WORLD.x do -- обойти все метки
n_delta = math.abs(X-WORLD.x[index])+math.abs(Z-WORLD.z[index]) -- рассчитать дельту между текущей позицией и меткой
if n_delta < c_delta then -- если новая дельта меньше текущей
c_delta, current = n_delta, index -- обновить текущую дельту и индекс метки
end
end
go(WORLD.x[current], WORLD.y[current], WORLD.z[current]) -- перейти по координатам полученной метки
end
go(0, 0, 0)
end
test_mine()
А вот и видео с демонстрацией.
Можно добавить штрафы на повороты, тогда он будет меньше крутиться и собирать кучи линейкой, а не змейкой.
- 5
0 комментариев
Рекомендуемые комментарии
Нет комментариев для отображения