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

Оккультные практики при поиске руды

Рекомендуемые сообщения

В моде OpenComputers есть интересное устройство, которое позволяет определить плотность блока на расстоянии.

Но вот беда, данные он выдает довольно шумные и чем больше расстояние, тем больше шума.

 

Чтобы определить подлинную плотность блока, можно просканировать его несколько раз, а результат усреднить. Шум, мешающий сканированию, имеет вероятностную природу. И после нескольких сканирований можно статистически найти, какая вероятней всего плотность у блока.

За один тик мы можем просканировать 64 блока. Чтобы проанализировать всю доступную область (65 x 65 x 64) сотней итераций, нам понадобится 422500 тиков, что равно 21125 секунд или 352 минуты, то есть без малого 6 часов.

Но сколько раз надо сканировать? Сто? Тысячу?

 

Нам открыто тайное знание и есть точный ответ.
Один. Всего за одно сканирование мы можем найти руду среди любых других блоков. Если хочется абсолютной уверенности, придется сделать пару магических пассов и просканировать повторно.

 

Начнем с теории.

 

Для начала откроем код мода и найдем функцию geolyzer.scan, она располагается [здесь] src/main/scala/li/cil/oc/integration/vanilla/EventHandlerVanilla.scala и называется onGeolyzerScan()
Просмотрев код, мы можем понять, что функция принимает параметры, по этим параметрам сканирует блоки в мире. Делает разные проверки вроде world.blockExists(x, y, z) && !world.isAirBlock(x, y, z), чтобы убедится, что блок есть. Потом получает информацию о блоке по координатам, делает еще несколько проверок (опять проверить, что блок все-таки есть block != null, проверяет дополнительные параметры: includeReplaceable, isFluid(block), block.isReplaceable(world, blockPos.x, blockPos.y, blockPos.z))
Потом происходит измерение расстояния до блока. И в конце берется плотность, смешивается с шумом и расстоянием. Результат добавляется к таблице блоков и отправляется игроку.

 

Вроде-бы ничего необычного. Шум, расстояние, плотность. Нам и так известна зависимость силы шума от расстояния.

 

И вот тут начинается волшебство.
Рассмотрим поподробнее код вычисления итоговой плотности блока.

e.data(index) = e.data(index) * distance * Settings.get.geolyzerNoise + block.getBlockHardness(world, x, y, z)

Коротко можно это записать в виде формулы: R = G * D * N + H
G - это сгенерированный шум.
D - расстояние до блока.
N - множитель шума из конфига (стандартно - 2).
H - настоящая плотность.
R - результат работы геосканера.

 

Если мы попробуем в качестве эксперимента отнять от результата предполагаемую плотность, то ничего нового не узнаем. Если обратим все операции с известными значениями, то получим только шум.

 

А можем ли мы так же разобрать формулу шума? Давайте попробуем.

 

Несколькими строками выше [ссылка]. Можно наблюдать получение массива случайных байт.

val noise = new Array[Byte](e.data.length)
world.rand.nextBytes(noise)

Далее следует нормализация значений.

noise.map(_ / 128f / 33f).copyToArray(e.data)

Хм. Так-так-так. Если мы это все обьеденим с предыдущей формулой, то получится что-то вроде такого:
R = G(RANDOM_BYTE / 128 / 33) * D * N + H

 

И что это нам дает?
А то, что исходное псевдослучайное число имеет жесткую дискретность. ГПСЧ дает случайные числа типа byte, а это только 256 значений (-128, +127).
Нам известны все значения, кроме H и RANDOM_BYTE, что нам это дает?
Мы можем предположить значение H и обратить всю формулу.
(R - H) / D / N * 128 * 33
Для стандартного конфига можно сократить до:
2112 * (R - H) / D

 

А теперь тайное знание для тех, кто не понял самостоятельно.


Мы взяли желаемую плотность блока (например 3 для руды).
Подставили вместо H.
Получили случайное значение.
Можем легко определить, угадали ли плотность или нет.

 

Из-за дискретности случайных значений генератора, распределение вероятностей для блоков с разной плотностью не одинаковое.

cbdzk30.png

 

 

Перейдем к практике.

 

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

local sqrt = math.sqrt
local component = require('component')
local geolyzer = component.geolyzer
local hologram = component.hologram

local function distance(x, y, z)
  return sqrt(x^2 + y^2 + z^2)
end

local function magic(R, H, D)
  return 2112 * (R - H) / D % 1
end

local function visualize(hardness, elevation, size)
  hologram.clear()
  hologram.setScale(9)
  local blocks, result
  for x = -size, size do
    for z = -size, size do
      blocks = geolyzer.scan(x, z, elevation, 1, 1, 32)
      for i_y = 1, 32 do
        result = magic(blocks[i_y], hardness, distance(x, i_y+elevation-1, z))
        if blocks[i_y] ~= 0 and (result > 0.9998 or result < 0.00005) then
          hologram.set(x+24, i_y, z+24, true)
        end
      end
    end
  end
end

local hrd, ele, siz = table.unpack({...})
hrd = hrd or 3
ele = ele or -32
siz = siz or 16
visualize(hrd, ele, siz)

А вот результат:

 

grNrLdJ.png

fZVZjrO.png
 

 

При сканировании заметны артефакты. Когда разные плотности близки на целочисленных расстояниях, позникают коллизии. 
Это можно частично компенсировать, если есть блок кандидат на ошибку.


На любом расстоянии можно рассчитать абсолютный минимальный и максимальный уровень шума. С расстоянием, у близких плотностей пересечение значений увеличивается, но если плотность блока не в области пересечений, то можно точно определить к какой области он относится.

 

P4v8cL8.png
Пересечение плотностей руды (3) и камня (1.5), точками обозначены три сканирования блока руды.

 

Результаты обратного вычисления для разных плотностей хорошо это демонстрируют.

onqp1Vm.png

 

Для компенсации артефактов надо ввести дополнительное условие: полученный RANDOM_BYTE должен быть в диапазоне -128:127.
Вот финальный скрипт и результат.

local sqrt = math.sqrt
local component = require('component')
local geolyzer = component.geolyzer
local hologram = component.hologram

local function distance(x, y, z)
  return sqrt(x^2 + y^2 + z^2)
end

local function magic(R, H, D)
  return 2112 * (R - H) / D
end

local function visualize(hardness, elevation, size)
  hologram.clear()
  hologram.setScale(9)
  local blocks, result
  for x = -size, size do
    for z = -size, size do
      blocks = geolyzer.scan(x, z, elevation, 1, 1, 32)
      for i_y = 1, 32 do
        result = magic(blocks[i_y], hardness, distance(x, i_y+elevation-1, z))
        if blocks[i_y] ~= 0 and result > -128 and result < 127 and (result%1 > 0.9998 or result%1 < 0.0002) then
          hologram.set(x+24, i_y, z+24, true)
        end
      end
    end
  end
end

local hrd, ele, siz = table.unpack({...})
hrd = hrd or 3
ele = ele or -32
siz = siz or 16
visualize(hrd, ele, siz)
 

l3rb7E1.png

 

Для более точного определения плотности можно сделать два сканирования. Одно сместить относительно другого так, чтобы расстояния с артефактами не совпадали.

 

Чтобы не выполнять тяжелую операцию sqrt, можно создать словарь, где [x^2 + y^2 + z^2] = sqrt(x^2 + y^2 + z^2), всего понадобится 1742 уникальных значений.

 

P.S. Пост является компиляцией знаний из [этой] темы. Собрал, чтобы перевести и опубликовать на официальном форуме.

Автор идеи хакнуть геосканер - @eu_tomat

  • Нравится 5
  • Одобряю 1
  • Спасибо 2

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я думаю, можно ещё пошаманить с функцией magic и не рассчитывать максимальную и минимальную плотность камня.

Если

1 час назад, Doob сказал:

2112 * (R - H) / D

это выражение - это RANDOM_BYTE, то его можно перед взятием дробной части проверить и на попадание в диапазон [-128; 127].

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да, действительно, эффект будет тот же.

Вот распределение результатов для руды и камня.

onqp1Vm.png

Дополнил тему.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

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

Гость
Ответить в тему...

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

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

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

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

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


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