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

Таблица лидеров


Популярные публикации

Отображаются публикации с наибольшей репутацией начиная с 21.02.2019 во всех областях

  1. 4 балла
    В процессе подгонки всех функций программа очень быстро разрослась. Дам краткий обзор всех новинок. Минимальная и максимальная плотность были вынесены в начало программы к остальным переменным. + переменная port, для модема. + переменные steps и turns, вначале для отладки. steps пригодилась для подсчета шагов, чтобы каждые 32 шага проверять состояние инструмента и батареи. + функция arr2a_arr() - преобразование списков в ассоциативный массив для быстрого доступа к элементам. Массивы tails, fragments преобразуются этой функцией, функции работающие с ними, адаптированны соответствующим образом. + функция report(), позволяющая передавать пользователю статусные сообщения посредством модема или связанной карты. В данный момент еще пикает и выводит сообщения на экран. Также завершает работу программы, при получении соответствующего параметра. remove_point() отделилась от функций, удаляющих метки из таблицы. Функция check() отделилась от функции step(). Каждые 32 шага или по принуждению вычисляет расстояние до стартовой точки, сохраняет текущие координаты. Если уровень энергии или инструмента не хватает на дорогу до дома + 64 шага, совершается переход домой, потом возврат к работе. Если есть генератор, происходит попытка заправки. Каждый шаг проверяются точки вокруг робота и удаляются, если робот может их достать. Функция calibration() объединилась с калибровкой компаса. Происходит проверка компонентов: геосканер, контроллер инвентаря, инструмент и наличие блока под роботом, в случае не обнаружения - программа завершается. Настраиваются модем или связанная карта. sorter() упростилась и ускорилась. + функция home() перемещает робота на точку старта, запускает сортировку и упаковку предметов, ищет сундук (требует, в случае не обнаружения). Складывает добычу в сундук, в случае переполнения ждет, когда освободится место. Достает из сундука неупакованные предметы и переупаковывает. Забирает из сундука стак с углем, при наличии генератора. Кроме перехода к точке старта, ищет в инвентаре эндерсундук из EnderStorage и сгружает ресурсы в него. Пытается найти более новый инструмент в сундуке, либо ищет зарядник и пробует засунуть в него и зарядить. На точке старта ждет, пока уровень энергии не достигнет 99% потом возвращается к работе. + функция main() - основной цикл сканирования и добычи. Робот сканирует весь чанк, в цикле перебирает все метки, выбирает ближайшую и перемещается к ней, пока не пройдет по всем. Ну и цикл перехода по спирали от чанка к чанку. Запускает функцию main(), по завершении вычисляет координаты следующего чанка, перемещает к нему, запускает main() и т. д., пока не дойдет до последнего, потом вызывает функуию home() и завершает работу. Осталось добавить возвращение по хлебным крошкам при аварии. И правильную работу с инструментами, имеющими нестандартную механику износа - энергетические и магические кирки/буры из модов. Еще можно заставить робота таскать с собой генератор из какого-нибудь мода и зарядник, тогда ему не нужно будет возвращаться на точку старта для подзарядки (а с эндерсундуком вообще на надо будет возвращаться) Бета-версию можно посмотреть и скачать тут - https://pastebin.com/hXWLDKre
  2. 3 балла
    Эвристические функции: Давление - штраф на ход по вертикали. Нужен из-за того, что начало сканирования либо сверху, либо снизу, следовательно, заканчивать добычу надо ближе туда, где начнется следующее сканирование. Недоход - робот не заходит на позицию метки, чтобы сэкономить лишний шаг (не всегда оптимально, но суммарный эффект - положительный) Штраф на повороты - добавление шага, при оценке расстояния, если целевой блок находится дальше, чем в одном блоке от текущей оси. Изначально идея была в том, чтобы собрать такое сочетание эвристик, при котором робот будет быстрее заканчивать добычу и выходить к точке старта. Важно, чтобы робот финишировал наверху, для быстрого перехода к следующему чанку. Сначала добавил ограничение хода по вертикали. Если робот может достать блок, не доходя до него, то добывает и ищет следующий. Потом добавил такую же функцию для всех осей. Прибавил еще мягкое давление по вертикали, чтобы нижние блоки имели приоритет над верхними. Но когда запустил пачку роботов, перепутал знак и получил давление вверх. Пришлось потом запускать еше, с правильной формулой. Из всех решений выбрал лучший результат - штраф на повороты, недоход по всем осям и давление вниз. В тесте робот довольно много скакал по вертикали, я решил это исправить. Увеличил давление в два раза, но не заметил, что половина формулы поменяла знак. Робот стал настолько мало скакать по вертикали, что процесс превратился в послойную добычу, со слоем 2 блока. Перевернул формулу, чтобы добыча опять шла снизу вверх. Поменял обратно, получил не очень хороший результат - робот получает меньше свободы, делает больше лишних движений. По факту, сейчас штрафы на повороты конкурируют с недоходом, т. к. при добыче больших залежей, у робота вокруг много блоков и ему без разницы, сделать шаг к следующему или повернуться к соседнему. Самый оптимальный вариант для таких случаев - змейкой, но у этих залежей бывают всякие аппендиксы. Это вызывает некоторую нагрузку на дальнейшие ходы - роботу приходится больше вертеться и ходить. К тому же, недоход надо расширить: из-за того, что счет идет шагами, без точного учета поворотов - робот может не повернуться к соседнему блоку. Или зайти в блок, если он находится в соседнем блоке к текущей оси, хотя мог бы достать без последнего шага. Осталось придумать, как это починить без лапши (хотя там лапша из одного бряка по флагу, но все-равно не приятно) и подобрать такие уровни штрафов/давления, чтобы робот красиво закольцовывал добычу с большим выигрышем по времени, чем сейчас. Параметры: чанк со стандартной генерацией (IC2+AE2), без каверн, алмазная кирка без всяких заклятий и примочек, робот без бустеров. Время Шаги Повороты Сумма 1361 1094 430 1524 послойный 1189 982 358 1340 свободный без улучшений 1185 972 358 1330 недоход по y 1156 932 368 1300 недоход по y, давление вниз 1248 1076 360 1436 недоход по y, давление вверх 1254 1036 380 1416 недоход по xyz 1186 948 368 1316 недоход по xyz, давление вниз 1267 1064 374 1438 недоход по xyz, давление вверх 1210 996 302 1298 штраф на повороты, недоход по y 1141 928 326 1254 штраф на повороты, недоход по xyz, давление вниз 1291 1122 358 1480 штраф на повороты, недоход по xyz, давление вверх x2 1244 1032 368 1400 штраф на повороты, недоход по xyz, давление вниз x2 Итого, самый оптимальный получился - 1254 действия за 19 минут 1 секунду. А вот прогон на тестовом стенде ничего не показал - слишком маленькое расстояние. Но все-таки, на 10 секунд раньше взял последний блок, чем в прошлом тесте.
  3. 2 балла
    В сотрудничестве с @Zer0Galaxy мы доработали целочисленную библиотеку metaint. Итак, встречайте: RSA Криптосистема с открытым ключом Теперь на "отечественной" библиотеке metaint Доработаны алгоритмы поиска простых чисел - засчет уменьшения скорости шанс прохождения составного числа как простого уменьшен (на самом деле я просто сделал 8 тестов ферма на число) Поддерживаются ключи с кастомным количеством бит А так же полная оптимизация генерации ключей. Осталось лишь оптимизировать поиск простых чисел и ключи в 2048 бит в ваших руках. Установка pastebin run 1xudmTa7 - выберите RSA и установите. С hpm проблемы( Использование Библиотека возвращает класс. Для получения инстанса - просто require("RSA")(<params>): RSA_instance Аргументом (он один) конструктора класса может быть: строка - путь к файлу собственной структуры. В нем обязательно должен быть публичный ключ. число (битовая длина ключа, не менее 16 - иначе будет недоступно шифрование текста. Да и не выйдет меньше 16) таблица. В ней нужно 2 поля - private_key и public_key, структура как у файла ключа библиотеки. Так же должен быть публичный ключ. Методы инстанса RSA RSA:save(filepath: string) - сохранить ключ в файл RSA:encrypt(number:number) - зашифровать число RSA:decrypt(cryptedNum: number) - расшифровать число. Кинет ошибку, если нет приватного ключа RSA:sign(number: number) - подписать число. Кинет ошибку, если нет приватного ключа. RSA:verify(number:number, signedNumber: number): boolean - проверить подпись. Вернет true, если подпись верна. Работа с текстом. Очень медленно, битовая длина ключа - минимум 16 бит (StrToInt возвращает число в 32 бит, 16 бит*16 бит = 32 бит. Ограничение из-за использования остатка от деления). RSA:textEncrypt(text: string[,salt: string]):table[metaint] - шифрует текст поблочно, перемешивая блоки - защита от DPI. Блок равен 32 бита. Соль - строка, которая будет добавлена к тексту ради сокрытия первого блока текста (с ним не происходит ничего, он просто шифруется). RSA:textDecrypt(text: string[, saltLen: number]): string - расшифровывает текст с учетом длины соли, если она указана. Применяет обратное преобразование текста для расшифровки - защита от DPI, все дела. RSA:textSign(text:string): table[metaint] - поблочно подписывает текст, перемешивая блоки. RSA:textVerify(text:string, signedBlocks: table[metaint]): boolean - проверяет подпись текста. Работа с текстом проверялась на 32 битном ключе и юникоде (достаточно длинном). Ошибок алгоритма быть не должно. Более полная документация с описанием алгоритмов.
  4. 2 балла
    Для настройки работы программы, надо узнать плотность добываемых блоков и плотность мусора, чтобы сделать предварительный фильтр на этапе сканирования. Плотность одной руды в разных модах отличается, но она близка к ванильной т. к. механика работы инструментов одна. Сделал шпаргалку с информацией о плотностях из разных модов. Плотность, уровень инструмента, название. Minecraft 3 2 Алмазная руда 3 2 Изумрудная руда 3 2 Золотая руда 3 2 Красная руда 3 1 Железная руда 3 1 Лазуритовая руда 3 0 Угольная руда 3 0 Кварцевая руда 0.3 -1 Светокамень -1 -1 Бедрок 50 3 Обсидиан 3 -1 Эндерняк 2.5 0 Сундук 2 0 Булыжник 2 -1 Адский кирпич 2 0 Доски 1.5 0 Камень 1.5 -1 Каменный кирпич 1.25 -1 Терракота 0.8 0 Песчаник 0.6 0 Трава 0.6 0 Гравий 0.6 0 Глина 0.5 0 Земля 0.5 0 Песок 0.4 0 Адский камень 0.5 0 Песок душ -------------------------------------------- IC2 4 2 Урановая руда 3 1 Медная руда 3 1 Оловянная руда 2 1 Свинцовая руда -------------------------------------------- AE2 50 3 Небесный камень 50 0 Сундук из небесного камня 3 0 Кварцевая руда -------------------------------------------- Mekanism 3 -1 Осмиевая руда 3 -1 Медная руда 3 -1 Оловянная руда -------------------------------------------- Forestry 3 1 Апатитовая руда 3 1 Медная руда 3 1 Оловянная руда -------------------------------------------- TConstruct 10 4 Кобальтовая руда 10 4 Ардитовая руда -------------------------------------------- ThermalFoundation 3 1 Медная руда 3 1 Оловянная руда 3 2 Серебряная руда 3 2 Свинцовая руда 3 1 Алюминиевая руда 3 2 Никелевая руда 3 3 Платиновая руда 3 3 Иридиевая руда 3 3 Мифриловая руда -------------------------------------------- Galacticraft 6 1 Алюминиевая руда 5 1 Оловянная руда 5 1 Медная руда 3 2 Кремниевая руда Moon 5 1 Медная руда 5 1 Оловянная руда 5 -1 Сапфировая руда 3 1 Сырная руда 1.5 0 Лунный камень 0.5 0 Лунный грунт 0.5 -1 Лунный дерн Mars 2.2 3 Деш руда 2.2 1 Железная руда 2.2 1 Оловянная руда 2.2 1 Медная руда 2.2 0 Булыжник 2.2 0 Реголит Asteroids 3 2 Алюминиевая руда 3 3 Ильменитовая руда 3 2 Железная руда 3 0 Камень Venus 5 -1 Алюминиевая руда 5 -1 Медная руда 5 -1 Свинцовая руда 5 -1 Кварцевая руда 5 -1 Кремниевая руда 5 -1 Оловянная руда 5 -1 Солнечная пыль 2.2 1 Магма 2.2 1 Пемза 1.5 1 Твердый камень 0.9 1 Мягкий камень 0.9 -1 Выжженный камень
  5. 2 балла
    Добытые ресурсы надобно рассортировать. Для этого дела задействуем контроллер инвентаря. Надобно пройти по всем слотам, получить информацию о содержимом и сравнить название со списком ненужных предметов (который предварительно составим), при совпадении опустошать. Но это не вся функция. У нас есть еще верстак, который может помочь, очень сильно ужать, некоторые ресурсы (уголь, редстоун, алмазы, изумруды, лазурит). Верстак занимает в инвентаре 9 слотов, еще 1 слот добавим на результат, если не все влезет в блок. Поэтому, пока робот ищет мусор, пусть считает пустые слоты, для верстака. В начале уберем блоки сверху и снизу, чтобы случайно не перемешать мусор с излишками добра. для удобоваримости сократил некоторые имена: inventory - размер инвентаря, получим в начале работы программы, controller - контроллер инвентаря, tails - список названий лишних предметов (без префикса "minecraft:", можно добавлять названия из любого мода) robot.swing(0) -- освободить место для мусора robot.swing(1) -- освободить место для буфера ------- сброс мусора ------- local empty = 0 -- создать счетчик пустых слотов for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for name = 1, #tails do -- пройти по таблице хвостов if item.name:gsub('%g+:', '') == tails[name] then -- проверить на совпадение robot.select(slot) -- выбрать слот robot.drop(0) -- выбросить к отходам empty = empty + 1 -- обновить счетчик break -- прервать цикл сравнения end end else empty = empty + 1 -- обновить счетчик end end Далее следует проверить и выкинуть наверх предметы, которые будут мешать при крафте. Подсчитанные пустые слоты отнимем от требуемого количества для крафта, пройдем по инвентарю уберем их. -- упаковка предметов в блоки -- if crafting then -- если есть верстак -- перенос лишних предметов в буфер -- if empty < 10 then -- если пустых слотов меньше 10 empty = 10-empty -- увеличить количество пустых слотов для обратного отсчета for slot = 1, inventory do -- просканировать инвентарь if robot.count(slot) > 0 then -- если слот не пуст robot.select(slot) -- выбрать слот robot.drop(1) -- выбросить в буфер empty = empty - 1 -- обновить счетчик end if empty == 0 then -- если место освободилось break -- прервать цикл end end end Предварительно создадим таблицу fragments, в которой будут храниться названия предметов, которые можно сложить в блоки. Теперь создадим таблицу, в которой будут счетчики для каждого типа фрагментов. Пройдем по инвентарю, получим информацию о слоте, сравним, прибавим - все как в первом цикле, можно было бы даже их объединить, но на предыдущем шаге мы выкинули какие-то предметы. Чтобы узнать какие именно - придется городить еще один цикл, оставим как есть. -- подсчет предметов доступных для упаковки -- local available = {} -- создать таблицу счетчиков for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for n = 1, #fragments do -- пройти по списку названий фрагментов if item.name:gsub('%g+:', '') == fragments[n] then -- сравнить по имени if available[n] then -- если есть подобные фрагменты available[n] = available[n] + item.size -- обновить else -- иначе available[n] = item.size -- создать end break end end end end Наконец-то можно крафтить. Хотя, нет. Надо расчистить слоты верстака, чтобы в него сложить рецепт. Будем перебирать слоты от 1 до 9, но в роботе верстак занимает слоты с другими номерами, а именно 1 2 3 5 6 7 9 10 11, можно было бы составить условие от 1 до 11, с исключением целых по модулю 4. Сделаем проще - номера слотов занесем в таблицу "workbench", которая будет служить списком ссылок с системы 1-9 на 1-11. Вынесем ее подальше, чтобы она не создавалась при каждом запуске. В цикле проверяем количество предметов в слоте, если оно не нулевое - ищем в инвентаре пустой слот, исключая слоты верстака. Переносим предметы в найденный слот. Если перенести не удалось - что-то попало в верстак после крафта. Забираем предметы из буфера и завершаем функцию, возвращая true, что будет сообщать о перегрузе и времени выдвигаться домой. for c_slot = 1, 9 do -- цикл чистки зоны верстака if robot.count(workbench[c_slot]) > 0 then -- если слот не пуст robot.select(workbench[c_slot]) -- выбрать слот верстака for slot = 4, inventory do -- обойти весь инвентарь, кроме рабочей зоны if robot.count(slot) == 0 and (slot == 4 or slot == 8 or slot > 11) then -- если есть свободный robot.transferTo(slot) -- освободить слот break -- выйти из цикла end end if robot.count() > 0 then -- проверить на перегрузку robot.suck(1) -- забрать из буфера return true -- остановить упаковку end end end Верстак расчищен, пора заняться упаковкой. Перебираем слоты инвентаря, исключая верстак, сравниваем названия со списком. При совпадении, делим содержимое на 9, заполняем верстак И крафтим блок. ------- основной цикл крафта ------- for slot = 4, inventory do -- цикл поиска фрагментов local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item and (slot == 4 or slot == 8 or slot > 11) then -- если есть предмет вне рабочей зоны if item.name:gsub('%g+:', '') == fragments[i] then -- сравнить по названию фрагмента robot.select(slot) -- при совпадении выбрать слот for n = 1, 9 do -- цикл заполнения рабочей зоны robot.transferTo(workbench[n], item.size/9) -- разделить текущий стак на 9 частей и перенести в верстак end if robot.count(1) == 64 then -- сброс при заполнении верстака break end end end end crafting.craft() -- создание блока Можно заметить fragments, откуда i? Об этом позже. После крафта могли остаться какие-то остатки, если не все слоты поделились на 9. Проверяем содержимое в слотах, если предметов меньше 64 - перебираем слоты после текущего и сравниваем, при совпадении содержимого пробуем перенести. При опустошении текущего слота - прерываем перебор со сравнением. -- цикл сортировки остатков for A = 1, inventory do -- основной проход local size = robot.count(A) -- получить количество предметов if size > 0 and size < 64 then -- если слот не пуст и не полон robot.select(A) -- выбрать слот for B = A+1, inventory do -- проход сравнения if robot.compareTo(B) then -- если предметы одинаковые robot.transferTo(B, 64-robot.count(B)) -- перенести до заполнения end if robot.count() == 0 then -- если слот освободился break -- прервать сравнение end end end end Последние три цикла заворачиваем в такую конструкцию: for i = 1, #fragments do -- перебор всех названий if available[i] then -- если в инвентаре такой есть for j = 1, math.ceil(available[i]/576) do -- разделить результат на стаки ... end end end Первый цикл перебирает названия фрагментов. Условный оператор проверяет наличие такого типа в инвентаре. Внутренний цикл повторяет чистку, крафт и сортировку, если в результате будет больше стака блоков. Новые используемые переменные: local tails = {'cobblestone','dirt','gravel','sand','stained_hardened_clay','sandstone','stone','grass','end_stone','hardened_clay','mossy_cobblestone','planks','fence','torch','nether_brick','nether_brick_fence','nether_brick_stairs','netherrack','soul_sand'} local workbench = {1,2,3,5,6,7,9,10,11} local fragments = {'redstone','coal','dye','diamond','emerald'} local controller = add_component('inventory_controller') local crafting = add_component('crafting') local inventory = robot.inventorySize() Полный текст функции, с более рациональным вызовом robot.count(): local function sorter() -- сортировка лута robot.swing(0) -- освободить место для мусора robot.swing(1) -- освободить место для буфера ------- сброс мусора ------- local empty = 0 -- создать счетчик пустых слотов for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for name = 1, #tails do -- пройти по таблице хвостов if item.name:gsub('%g+:', '') == tails[name] then -- проверить на совпадение robot.select(slot) -- выбрать слот robot.drop(0) -- выбросить к отходам empty = empty + 1 -- обновить счетчик break -- прервать цикл сравнения end end else empty = empty + 1 -- обновить счетчик end end -- упаковка предметов в блоки -- if crafting and empty < 12 then -- если есть верстак и переполнение -- перенос лишних предметов в буфер -- if empty < 10 then -- если пустых слотов меньше 10 empty = 10-empty -- увеличить количество пустых слотов для обратного отсчета for slot = 1, inventory do -- просканировать инвентарь if robot.count(slot) > 0 then -- если слот не пуст robot.select(slot) -- выбрать слот robot.drop(1) -- выбросить в буфер empty = empty - 1 -- обновить счетчик end if empty == 0 then -- если место освободилось break -- прервать цикл end end end -- подсчет предметов доступных для упаковки -- local available = {} -- создать таблицу счетчиков for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for n = 1, #fragments do -- пройти по списку названий фрагментов if item.name:gsub('%g+:', '') == fragments[n] then -- сравнить по имени if available[n] then -- если есть подобные фрагменты available[n] = available[n] + item.size -- обновить else -- иначе available[n] = item.size -- создать end break end end end end ------- основной цикл крафта ------- for i = 1, #fragments do -- перебор всех названий if available[i] then -- если в инвентаре такой есть for j = 1, math.ceil(available[i]/576) do -- разделить результат на стаки for c_slot = 1, 9 do -- цикл чистки зоны верстака if robot.count(workbench[c_slot]) > 0 then -- если слот не пуст for slot = 4, inventory do -- обойти весь инвентарь, кроме рабочей зоны if robot.count(slot) == 0 and (slot == 4 or slot == 8 or slot > 11) then -- если есть свободный robot.select(workbench[c_slot]) -- выбрать слот верстака robot.transferTo(slot) -- освободить слот break -- выйти из цикла end end if robot.count() > 0 then -- проверить на перегрузку robot.suck(1) -- забрать из буфера return true -- остановить упаковку end end end ------- основной цикл крафта ------- for slot = 4, inventory do -- цикл поиска фрагментов local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item and (slot == 4 or slot == 8 or slot > 11) then -- если есть предмет вне рабочей зоны if item.name:gsub('%g+:', '') == fragments[i] then -- сравнить по названию фрагмента robot.select(slot) -- при совпадении выбрать слот for n = 1, 9 do -- цикл заполнения рабочей зоны robot.transferTo(workbench[n], item.size/9) -- разделить текущий стак на 9 частей и перенести в верстак end if robot.count(1) == 64 then -- сброс при заполнении верстака break end end end end crafting.craft() -- создание блока -- цикл сортировки остатков for A = 1, inventory do -- основной проход local size = robot.count(A) -- получить количество предметов if size > 0 and size < 64 then -- если слот не пуст и не полон for B = A+1, inventory do -- проход сравнения if robot.compareTo(B) then -- если предметы одинаковые robot.select(A) -- выбрать слот robot.transferTo(B, 64-robot.count(B)) -- перенести до заполнения end if robot.count() == 0 then -- если слот освободился break -- прервать сравнение end end end end end end end end robot.suck(1) --- забрать предметы из буфера end
  6. 2 балла
    Развлекайся =D В качестве компенсации, вот тебе фотка моего кота:
  7. 1 балл
    Все-таки добывать руду слоями дольше, чем свободным обходом всех доступных блоков. На тестовом стенде свободный - 2 минуты 50 секунд, послойный - 3 минуты 9 секунд. Тут очень сильно напрашивается дополнительная эвристика на сокращение поворотов и прямых одноблочных ходов. И упаковку запускать только при нужде.
  8. 1 балл
    Захотелось написать очередную прошивку для EEPROM с интересным функционалом. Чтобы запихать этого монстра (в масштабах прошивок, разумеется) в 4-килобайтный лимит, пришлось пройти семь кругов ада, сожрать пуд соли и пролить ведро крови. Из фич отмечу следующие: • Поддержка всех уровней видеокарт и мониторов • Отображение подробной информации об имеющихся дисках • Форматирование / переименование / выбор файловой системы в качестве загрузочного тома • Аналог Internet Recovery, позволяющий запустить скрипт по указанной URL в качестве нативного кода для EEPROM Исходник без минификации: https://github.com/IgorTimofeev/MineOS/blob/master/EFI/Full.lua Команды для прошивки: wget https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/EFI/Minified.lua /EFI.lua -f flash /EFI.lua
  9. 1 балл
    Без доработки мода вряд ли возможно. Впервые узнав о стороне, задаваемой аргументом, я сразу попробовал копнуть во всех направлениях и получил ошибку везде кроме сторон 0, 1, 3.
  10. 1 балл
    Именно так. robot.swing() в библиотеке копает только вперёд, а для копки вверх и вниз имеются robot.swingUp() и robot.swingDown(). Компонент же располагает единственной функцией, направление задаётся аргументом.
  11. 1 балл
    Наиболее интересной мне показалась эта идея: Хорошая эвристика. В идеале хорошо было бы найти такие штрафы на перемещения во вертикали (а также на повороты), чтобы при плотном расположении руд робот перемещался по кластеру как в классической копалке "слой через два". Мои идеи в этом направлении пока что сводятся к увеличению глубины поиска ближайших целей.
  12. 1 балл
    Тут на днях обновленная софтина Multiscreen в маркет затесалась - добавлена группировка по цветам для гораздо более быстрой отрисовки жирных пикч. Правда, чем жирнее пикча, тем дольше будет обрабатываться каждый моник, но и это уже заметный прогресс:
  13. 1 балл
    Чтобы программа могла контролировать движения робота, добавим систему координат и функционал связанный с ней. Так как робот будет шахтером, то все движения должны сопровождаться разрушением блоков, он будет ползать сквозь породу, попутно захватывая руду. Описание основной двигательной деятельности занимает всего четыре функции (можно и три, но в прошлой версии, в процессе борьбы за место, пришлось одну разделить) Приведу базовый код, затем опишу, что он делает. 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 глобальные, это стороны света.
  14. 1 балл
    При запуске программы надо оценить возможности робота, чтобы в дальнейшем, можно было точно знать количество энергии для перехода на точку старта. Робот должен измерить уровень энергии, сделать шаг, измерить еще раз и вычислить разницу. Эту разницу будет учитывать при измерении расстояния и принимать решение - идти домой или не идти. Функция robot.durability() не показывает правильный износ для зачарованных инструментов. Придется несколько раз ставить и разрушать блок, пока не обнаружится износ. Вынесем пока это все в отдельную функцию calibration() local function calibration() -- калибровка при запуске local energy = computer.energy() -- получить уровень энергии step(0) -- сделать шаг E_C = math.ceil(energy-computer.energy()) -- записать уровень потребления energy = robot.durability() -- получить уровень износа/разряда инструмента while energy == robot.durability() do -- пока не обнаружена разница robot.place(1) -- установить блок robot.swing(1) -- разрушить блок end W_R = energy-robot.durability() -- записать результат step(1) -- вернуться на место end Переменные E_C и W_R выносим в обую область видимости. Во время работы, например, после четырех сканов, робот будет оценивать количество доступных шагов по этой формуле: math.min(robot.durability()/W_R, computer.energy()/E_C) и сравнивать с расстоянием до точки старта. Из-за магической механики, зачарованные инструменты (Unbreaking) изнашиваются неравномерно, но роботу это нисколько не мешает.
  15. 1 балл
    Геосканер потребляет много энергии, а функция compass() делает по 4 скана, пока не установит направление. Надо это исправить. Пусть робот сначала проверит наличие блока перед носом, сделает скан всех блоков вокруг себя, затем сломает блок и проверит разницу в полученных данных. Таким образом, будет производиться всего два сканирования, в прошлой версии их могло быть бесконечно много - если рядом нет блоков, робот бы крутился и молотил инструментом по воздуху, попутно делая по 4 сканирования. Улучшенная функция будет выглядеть так: local function compass() local sides = {2, 1, 3, 0} -- линки сторон света, для сырых данных D = nil -- обнуление направления while not D do -- пока не найдено направление if robot.detect(3) then -- проверить наличие блока перед носом local A = geolyzer.scan(-1, -1, 0, 3, 3, 1) -- сделать первый скан robot.swing(3) -- сломать блок local B = geolyzer.scan(-1, -1, 0, 3, 3, 1) -- сделать второй скан for n = 2, 8, 2 do -- обойти смежные блоки в таблице if math.ceil(B[n])-math.ceil(A[n])<0 then -- если блок исчез D = sides[n/2] -- установить новое направление break -- выйти из цикла end end else turn() -- задействовать простой поворот end end end
  16. 1 балл
    Робот может двигаться, пора добавить функцию сканирования породы и калибровки компаса. (Пока тестировал, обнаружил баг работы с зачарованными инструментами, пришлось немного переделать функцию 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 Самые важные функции готовы, можно приступить к тестированию.
  17. 1 балл
    Ядро копателя готово, теперь можно и пощупать. Напишем пробную функцию сканирования и добычи одного слоя. Сначала откалибруем компас и зададим таблицу с координатами сканируемых квадратов. Затем, отсканируем квадрат 16 на 16 блоков, выведем количество обнаруженных блоков. И в цикле обойдем все метки. Вроде бы все просто. Ах, да... будем искать ближайший блок к текущей позиции, чтобы быстрее закончить работу. Есть много подходов к определению расстояний. Например квадрат гипотенузы равен сумме квадратов катетов, формула для нашего случая будет math.sqrt((X-x)^2+(Z-z)^2), где X,Z - координаты робота, x,z - координаты метки, можно выкинуть квадратный корень, в нашем случае бесполезный и даже вредный. Но тут есть одно "но", мы получили гипотенузу, а это наименьшее расстояние между точками, а роботы по диагонали не ходят. Я буду вычислять дельту между точками, суммируя реальное расстояние, которое пройдет робот по формуле math.abs(X-x)+math.abs(Z-z) Эта операция в сферическом вакууме потребляет на 5% больше процессорного времени, чем предыдущая, но с лихвой окупается сэкономленными шагами. В цикле будем обходить таблицу с метками, до каждой вычисляя расстояние, самый лучший результат с индексом будем хранить в отдельных переменных. По окончании работы цикла, будем посылать робота в ближайшую точку. Код всей тестовой программы под спойлером. А вот и видео с демонстрацией. Можно добавить штрафы на повороты, тогда он будет меньше крутиться и собирать кучи линейкой, а не змейкой.
  18. 1 балл
    И так, наконец-то возвращаюсь к роботу-копателю. Да будут новые баги и новые фичи! Краткий план внедряемых фич: Улучшенное сканирование руд. Робот сканирует под собой квадрат 16x16 блоков, опускаясь блок за блоком. При обнаружении бедрока запускается функция добычи. При добыче робот поднимается и в цикле ищет ближайшие по горизонтали блоки руды, захватывая три слоя - Y+1, Y, Y-1 Определение энергопотребления сборки при запуске. На старте, робот запоминает количество потребленной энергии на один шаг + прочность инструмента. Это будет служить константой при проверке статуса, чтобы была возможность гарантированно вернуться на точку старта. Умная упаковка добычи. Перед обработкой рассыпухи, теперь будет точнее анализироваться свободное место, упаковка не будет происходить механическим перебором, из-за которого бывали внезапные сбои. При наличии генератора, робот всегда будет с собой таскать уголь, при разгрузке на точке старта будет забирать стак угля или угольных блоков. Текущие константа энергопотребления и координаты будут записываться в EEPROM. Следовательно, при наличии сетевой/связанной карты, робота можно будет будить и не бояться выгрузки чанков, лагов, космических лучей. Скорее всего, добавлю функцию аварийного крафта кирок из булыги, в случае работы с ванильными инструментами. Планируется утилита, собирающая программу по параметрам, заданным пользователем. ...Или не планируется, скорее всего все возможности копалки не получится впихнуть на EEPROM, поэтому на EEPROM будет загрузчик с main функцией, а дополнительные модули придется записывать на жесткий диск. Планируется поддержка модов, например, возможность возить с собой и разворачивать заправочную станцию в виде генератора и зарядника. Или скидывать предметы в межпространственные сундуки. Тут надо будет смотреть, как будут развиваться моды. В блоге буду описывать каждую функцию, чтобы отследить создание программы шаг за шагом, надеюсь, кому-то это поможет.
  19. 1 балл
    Да, название уже придумано 😃
  20. 1 балл
    За полный сбор:
  21. 1 балл
    Кстати о майноси: тут несколько занятных софтин подъехало. Например, YobaКалькулатрон9000, как его окрестил товарищ-шестиклассник. Умеет почти все: от работы с различными системами счисления и побитового редактирования чисел до базовых бинарно-тригонометрических операций с отображением символа по его коду. Во-вторых, слегка обновился местный проводник, заимев удобную панель навигации по текущему пути с кликабельными подпунктами а-ля Винда. Ну, и всякие кнопочки для ручного ввода пути появились, а также фича ресайза тулбара: Ну, и наконец не шибко сложная, однако довольно полезная в быту возможность интерфейсного привата компа по аналогии с useradd:
  22. 1 балл
    Это для того, что бы в случае грифа пойти с этим сканом кредит оформлять на грифера?
  23. 1 балл
    Я всегда думал, что шрифт OpenComputers моноширинный. При просмотре этой таблицы полной неожиданностью для меня стало то, что символы в дипазоне FF00-FF5F и еще некоторые имеют двойную ширину.
  24. 1 балл
    UPD #1 - Добавлена смена инструментов (инструкция ниже!) UPD #2 - Прикручена система координат (пока ни как не используется) Робот который копает карьер, как бы не было банально, но он копает его максимально экономно! Характеристики робота: корпус 1 ур. 1шт, процессор 1 ур. 1шт, оперативная память 1ур. 1шт, улучшение инвентаря минимум 1шт (чем больше тем лучше) Для авто-смены инструментов нужно добавить контроллер инвентаря 1шт Для работы дать роботу кирку как минимум железную, а лучше всего электро-бур В программе есть настройки: Настройки задаются в исходном коде программы! trashSlots - количество слотов инвентаря под фильтры, и мусор который робот будет выкидывать по мере продвижения (в эти слоты желательно класть: камень, землю, гравий, песок, ... , булыжник (он ложится в последний слот, что-бы не тормозить функцию проверки блоков) предметы в слотах располагаются по мере их количества в предполагаемом месте работы (самые часто встречаемые, должны лежать в начале, что-бы не тормозить работу) !!! Все слоты-фильтры должны быть заполнены !!! lengh - Длинна карьера width - Ширина карьера (вводите четные числа, если введенное число не будет четным, от него отнимется 1) deepM - Высота карьера (вводите числа кратные 3, если число не будет кратное 3, от него отнимутся значения пока оно не будет кратное 3) Для авто-смены инструментов: -------------------------------------------------------------------------------------- toolSwich - смена инструментов роботом true - да / false - нет (если нет контроллера инвентаря оставить выключенным) tool1 - 1 слот для инструмента tool2 - последний слот для инструмента !!! Все отмеченные слоты должны быть заняты !!! -------------------------------------------------------------------------------------- Примечание!!! ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- Устанавливайте высоту карьера на 10 блоков меньше чем высота установки робота, во избежание застревания оного ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- Принцип работы: Робот опускается под землю, и идет змейкой в заданных параметрах проверяя блок под собой и над собой на совпадение в слотах-фильтрах. Если робот не нашел совпадение, то добывает блок, если совпадение найдено, движется дальше. После выкапывания всего, робот возвращается на поверхность и на место дыры под собой ставит блок из последнего слота-фильтра. Пример работы робота: https://www.dropbox.com/s/be9l5rnday5s7ej/2016-08-25_01.40.42.png?dl=0 https://www.dropbox.com/s/vxm0kmklyhpngud/2016-08-25_01.40.51.png?dl=0 Комплектация робота: https://www.dropbox.com/s/vqpn5uei5sjjmes/2016-08-25_02.19.47.png?dl=0 Настройки программы: https://www.dropbox.com/s/nww84qssk4vqa8z/2016-08-25_02.21.21.png?dl=0 Загрузка робота: https://www.dropbox.com/s/jbeuoudovdg63rf/2016-08-25_02.24.45.png?dl=0 Накопанные ресурсы за 1ч:10м: https://www.dropbox.com/s/vfg78en1hw1alx8/2016-08-25_03.41.41.png?dl=0 Робот прошел вниз 36 блоков. У него сломалась кирка и почти сел аккумулятор, так что ставьте глубину не больше 30 - 33 Исходный код: http://pastebin.com/fEa4Qxef https://gist.github.com/L3rok1/17e9265bc33032a9230d5feefe29165a
  25. 1 балл
    А OpenNet между тем медленно но уверенно развивается. Начал свою работу чат-сервер "chatroom". Для подключения к серверу Вам понадобится компьютер или планшет, подключенный к ON, библиотечка thread и конечно же программа чат-клиент. В качестве параметра клиенту нужно указать ip или dns-имя чат-сервера Пример запуска клиента: chat chatroom А вот результат работы:
Таблица лидеров находится в часовом поясе Москва/GMT+03:00
  • Рассылка

    Хотите узнавать о наших последних новостях и информации?

    Подписаться
×