Перейти к содержимому

some blog name

  • записей
    15
  • комментарий
    41
  • просмотров
    17 515

Робот с геосканером. Часть #3 [первый тест]

Doob

689 просмотров

Ядро копателя готово, теперь можно и пощупать.

Напишем пробную функцию сканирования и добычи одного слоя.

Сначала откалибруем компас и зададим таблицу с координатами сканируемых квадратов.

Затем, отсканируем квадрат 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 комментариев


Рекомендуемые комментарии

Нет комментариев для отображения

Гость
Добавить комментарий...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...