Перейти к публикации

В ближайшее время постараюсь разобраться с картой сервера/ЛК/бб кодами

Внимание, с 14 февраля до 20 февраля могут проходить работы на сервере, где также находится лаунчсервер. В связи с этим авторизация в лаунчере может не работать

Doob

Пользователи
  • Публикации

    716
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    62

Последний раз Doob выиграл 27 марта 2018

Публикации Doob были самыми популярными!

Репутация

1 440 Очень хороший

8 подписчиков

Информация

  • Пол
    Не определился

Посетители профиля

937 просмотров профиля
  1. Ядро копателя готово, теперь можно и пощупать. Напишем пробную функцию сканирования и добычи одного слоя. Сначала откалибруем компас и зададим таблицу с координатами сканируемых квадратов. Затем, отсканируем квадрат 16 на 16 блоков, выведем количество обнаруженных блоков. И в цикле обойдем все метки. Вроде бы все просто. Ах, да... будем искать ближайший блок к текущей позиции, чтобы быстрее закончить работу. Есть много подходов к определению расстояний. Например квадрат гипотенузы равен сумме квадратов катетов, формула для нашего случая будет math.sqrt((X-x)^2+(Z-z)^2), где X,Z - координаты робота, x,z - координаты метки, можно выкинуть квадратный корень, в нашем случае бесполезный и даже вредный. Но тут есть одно "но", мы получили гипотенузу, а это наименьшее расстояние между точками, а роботы по диагонали не ходят. Я буду вычислять дельту между точками, суммируя реальное расстояние, которое пройдет робот по формуле math.abs(X-x)+math.abs(Z-z) Эта операция в сферическом вакууме потребляет на 5% больше процессорного времени, чем предыдущая, но с лихвой окупается сэкономленными шагами. В цикле будем обходить таблицу с метками, до каждой вычисляя расстояние, самый лучший результат с индексом будем хранить в отдельных переменных. По окончании работы цикла, будем посылать робота в ближайшую точку. Код всей тестовой программы под спойлером. А вот и видео с демонстрацией. Можно добавить штрафы на повороты, тогда он будет меньше крутиться и собирать кучи линейкой, а не змейкой.
  2. Робот может двигаться, пора добавить функцию сканирования породы и калибровки компаса. (Пока тестировал, обнаружил баг работы с зачарованными инструментами, пришлось немного переделать функцию step() - теперь после неудачного свинга, робот дополнительно проверяет наличие блока. Можно будет оставить, даже когда разрабы это исправят) Чтобы отфильтровать блоки по плотности, надо получить плотность нужных блоков с учетом шумов. На расстоянии x8 z8 y1 от геосканера, максимальная плотность бедрока равна -0.317, внесем в фильтр -0.31. Для руды 3.683, но это ванильная руда, в модах бывает и больше. Минимальная плотность обсидиана 49.312, значит, eсли он не нужен, установим для полезных блоков максимальную плотность 40. C минимальной плотностью не все так гладко. Свинцовая руда из индастриала имеет плотность 2.5 это как у деревянных предметов, разброс с учетом шума от 1.3 до 2.7, это пересекается с камнем, у которого 0.8 - 2.2. Вот таблица некоторых блоков с минимальной и максимальной плотностью: Руда 2.312 - 3.683 Стекло -0.388 - 0.983 Камень 0.812 - 2.183 Грязь -0.188 - 1.183 Сундук 1.312 - 2.683 Обсидиан 49.312 - 50.683 Видно, что плотность стекла пересекается с плотностью коренной породы, но у бедрока приоритет выше, поэтому лучше лишний раз обойти. Исходя из этих данных, полезные блоки будут отмечаться с минимальной плотностью 2.3 и максимальной 40 Теперь опишем функцию сканирования. Заглянем в подсказку. Чтобы получить сырые данные, зададим координаты и размеры квадрата, относительно сканера. geolyzer.scan(позиция_х, позиция_z, позиция_y, ширина, длина, высота) Так как один раз можно отканировать только 64 блока, будем делать 4 подхода, получая координаты квадрата по горизонтали из вызывающей функции. Преобразовываем данные в координаты, попутно анализируя плотность условным оператором и устанавливаем метки. При обнаружении бедрока устанавливаем соответсвующий флаг во внешней для всех функций переменной. Получаем функцию scan(), выглядеть она будет примерно так: 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 Раз уже взялись за геосканер, напишем и компас. Чтобы определить стороны света, надо сломать блок перед носом, просканировать его, затем установить обратно и, если есть разница - выдать результат. Для большей надежности добавим вращение вокруг своей оси, т. к. блока перед носом может и не быть или быть, но не тот. Координаты блоков задаем в таблице, сбрасываем текущее направление, определяем заново, вот и вся функция. Назовем ее compass() 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 Самые важные функции готовы, можно приступить к тестированию.
  3. Чтобы программа могла контролировать движения робота, добавим систему координат и функционал связанный с ней. Так как робот будет шахтером, то все движения должны сопровождаться разрушением блоков, он будет ползать сквозь породу, попутно захватывая руду. Описание основной двигательной деятельности занимает всего четыре функции (можно и три, но в прошлой версии, в процессе борьбы за место, пришлось одну разделить) Приведу базовый код, затем опишу, что он делает. local component = require('component') -- подгрузить обертку из OpenOS local X, Y, Z, D = 0, 0, 0, 0 local WORLD = {x = {}, y = {}, z = {}} local function add_component(name) -- получение прокси компонента name = component.list(name)() -- получить адрес по имени if name then -- если есть адрес return component.proxy(name) -- вернуть прокси end end local robot = add_component('robot') -- загрузка компонента local function step(side) -- функция движения на 1 блок local state, type = robot.swing(side) -- тестовый свинг if not state and type == 'block' 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[i] and (Y-1 <= WORLD.y[i] and Y+1 >= WORLD.y[i]) and Z == WORLD.z[i] then if WORLD.y[i] == Y+1 then -- добыть блок сверху, если есть robot.swing(1) elseif WORLD.y[i] == 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) 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 Сначала создаются переменные для локальных координат робота. X, Y, Z - собственно, позиция робота, относительно стартовой точки. D - направление, куда смотрит мордочка робота. при старте программы она относительная. Поэтому, чтобы привязать ее к сторонам света, надо будет произвести некоторое шаманство при помощи геосканера. Таблица WORLD - это метки, которые будут устанавливаться в процессе сканирования. Таблица разделена на три, это смежные хранилища переменных для каждой координаты, например, сканер обнаружил блок с подходящей плотностью по координатам x15, y-10, z3, в таблицу они будут добавлены по одному индексу. Допустим, таблица была пустая, после добавления будет иметь вид WORLD.x[1] = 15, WORLD.y[1] = -10, WORLD.z[1] = 3 или WORLD = {x = {15}, y = {-10}, z = {3}} Далее следует функция, упрощающая добавление компонентов. На вход получает имя нужного компонента и, если он есть, выдает прокси к нему. Функция step() - основное движение робота. Учитывая, что программа используется исключительно для копания, копание будет в каждом шаге. Робот не тыкается носом в породу и не спрашивает какой блок перед ним. Махнул инструментом и смотрит результат. Если есть блок, но добыть его не получилось - следовательно, дальше делать нечего, там бедрок или еще чего похуже, потом добавим правильную обработку и эвакуацию по хлебным крошкам, а пока пусть будет заглушка. Если махнул удачно - пробуем еще раз и еще, до посинения. Это своеобразная защита от лагающего гравия/песка и назойливых сущностей (в виде гномиков). Далее, если функция движения была совершена удачно, то обновляем локальные координаты, учитывая направление движения. Ну и в конце функции сканируем таблицу меток, ищем метки с текущей позицией и удаляем, т. к. по нашим данным робот находится в блоке руды, следовательно, он его добыл. Еще есть дополнительная проверка по вертикали - если есть руда сверху или снизу, то захватываем, это позволит сократить общую сумму переходов между метками и ускорить добычу. Функция turn() - основной поворотник (аналог robot.turn(), но с обновлением переменной направления) Робот поворачивается, записывая результат в переменную, добавляя/отнимая единицу по модулю 4 при каждом повороте. Функция smart_turn() - поворот на желаемую сторону света, с минимумом действий. Вычисляет разницу между текущим и целевым направлением, запуская результат по модулю 4 через turn() Функции поворота можно будет объединить, но пока оставлю так. Функция go() - великий ход конем до нужных координат. Принимает координаты целевого блока, двигается по вертикали, поворачивает на цель X, двигается до цели, поворачивает на цель Z, двигается до цели. Для поворота использует smart_turn(), т. к. оси x и z глобальные, это стороны света.
  4. Просто в прошлой версии был свитч переключения режимов - визуальный с красивостями и только текст, в режиме текста все теги добавлялись нормально руками.
  5. Doob

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

    Никак, стандартный уровень шума не изменился. Первая версия сканировала 8x8 блоков, я жестко задал плотности, которые получил при сканировании дальних блоков (самая мягкая руда и бедрок) В этой версии изменять ничего не придется, т. к. так же сканируются 4 квадрата x8, только теперь робот не будет смещаться по горизонтали, пока не дойдет до бедрока. С таким подходом уровень шумов по краям будет постоянным, ускорится добыча руды и общий профит.
  6. И так, наконец-то возвращаюсь к роботу-копателю. Да будут новые баги и новые фичи! Краткий план внедряемых фич: Улучшенное сканирование руд. Робот сканирует под собой квадрат 16x16 блоков, опускаясь блок за блоком. При обнаружении бедрока запускается функция добычи. При добыче робот поднимается и в цикле ищет ближайшие по горизонтали блоки руды, захватывая три слоя - Y+1, Y, Y-1 Определение энергопотребления сборки при запуске. На старте, робот запоминает количество потребленной энергии на один шаг + прочность инструмента. Это будет служить константой при проверке статуса, чтобы была возможность гарантированно вернуться на точку старта. Умная упаковка добычи. Перед обработкой рассыпухи, теперь будет точнее анализироваться свободное место, упаковка не будет происходить механическим перебором, из-за которого бывали внезапные сбои. При наличии генератора, робот всегда будет с собой таскать уголь, при разгрузке на точке старта будет забирать стак угля или угольных блоков. Текущие константа энергопотребления и координаты будут записываться в EEPROM. Следовательно, при наличии сетевой/связанной карты, робота можно будет будить и не бояться выгрузки чанков, лагов, космических лучей. Скорее всего, добавлю функцию аварийного крафта кирок из булыги, в случае работы с ванильными инструментами. Планируется утилита, собирающая программу по параметрам, заданным пользователем. ...Или не планируется, скорее всего все возможности копалки не получится впихнуть на EEPROM, поэтому на EEPROM будет загрузчик с main функцией, а дополнительные модули придется записывать на жесткий диск. Планируется поддержка модов, например, возможность возить с собой и разворачивать заправочную станцию в виде генератора и зарядника. Или скидывать предметы в межпространственные сундуки. Тут надо будет смотреть, как будут развиваться моды. В блоге буду описывать каждую функцию, чтобы отследить создание программы шаг за шагом, надеюсь, кому-то это поможет.
  7. Doob

    Гайд по геосканеру

    Недавнo завезли нескoлькo нoвых функций. canSeeSky():boolean - вoзвращает true, если виднo небo. Прoзрачные блoки, стекла, крoвати не мешают, а ступеньки и пoлублoки считаются непрoзрачными. detect(стoрoна:number):boolean, string - кoпия функции component.robot, прoверяет наличие блoка с указаннoй стoрoны. Вoзмoжные варианты выдачи: (false, 'air') - пустo, блoк вoздуха. (false, 'replaceable') - есть блoк, кoтoрый мoжнo сдвинуть этo цветы, трава, лианы (пoка неизвестнo, как будет пoсле апдейта MC1.14). В приватах мoжнo считать за true, т. к. рoбoтoм лoмаются тoлькo кактусы. (false, 'liquid') - блoк жидкoсти, текущая жидкoсть (true, 'solid') - есть твердый блoк. (true, 'entity') - какая-тo сущнoсть. (true, 'passable') - блoк, через кoтoрый мoжет хoдить сущнoсть - флаг, кувшинка, паутина, пoртал и т. п. Некoтoрые блoки имеют нескoлькo сoстoяний, например, дверь и люк в oбoих solid, а вoрoта тoлькo закрытые. store(стoрoна:number, адрес_БД:string, слoт_БД:number):boolean - сoхранить блoк с указаннoй стoрoны в базу данных. isSunVisible():boolean - вoзвращает true, если пoгoда ясная, вo время дoждя или грoзы - false
  8. Их есть у меня. Год назад придумал годный квест, даже с админами договорился об организации, но в связи с переездом так ничего и не запустил. Если наберётся хотя бы 10 игроков, то могу допилить до ума.
  9. Doob

    Обновление OpenComputers до версии 1.7.3

    Баги починили, эт хорошо. А вот дюпы жалко.
  10. Все это занимает с десяток строк на питоне, при использовании либы MoviePy. Хотя, я не разбирался в формате стримов, но обычные видосы можно адаптировать и воспроизводить на опенкомпах моментально. Единственная проблема, решение которой мне никто так и не подсказал это как синхронизировать звуковую дорожку.
  11. Doob

    Бот

    Не годится, слишком топорно. Любой современный чат-бот разбирает выражения и опознает ошибки. Для фильтров или ответов можно взять пару гигов логов игровых чатов, прогнать через какой-нибудь тензорфлоу, сделать дамп сети и через него читать. Хотя, опенкомпы довольно медлительны, и лучше использовать внешний сервис, тогда будет легко прикрутить полноценного бота.
  12. Это работает везде, где есть опенкомповские события. Даже пиная монитор можно загадить пул и будут пропускаться ивенты, тут ничего не поделать, это вина мода и игры, а не программ.
  13. Doob

    Кэшированный инвентарь робота

    Так и есть: нет верстака - нет упаковки, но это устарело. Я хочу, чтобы программа собиралась под конкретного робота, из конструктора или напрямую из интернета.
  14. Doob

    Кэшированный инвентарь робота

    Я для транспозеров делал немного похожее, но проще, т. к. там все операции под контролем. Для робота перепроверка по кэшу не так сильно нужна, да и не поможет она, если робот наедет на воронку. У меня есть интересная задачка по управлению инвентарем. Для геокопалки я написал упаковщик предметов в блоки (алмы, уголь, редстоун и т. д.), изначально все сканировал, предвычислял оптимальное заполнение, но алгоритм вышел очень громоздкий и занимал большую часть прошивки. Немного подумав, я упростил - сделал сортировку в конец инвентаря, оттуда по одному предмету кидал в область крафта. Вышло компактно, но сортировка и крафт занимают очень много времени, защита от дурака - полный перебор, а после какой-то обновы изменилась механика инвентаря и алгоритм перестал нормально завершаться. Интересно бы собрать идеи оптимального и компактного упаковщика. Хотя, компактность уже не важна, т. к. прошивку можно сделать лончером кода с диска или из интернета.
×