Как должна быть реализована схема или система питания (например, Redstone в Minecraft)

8

Я хочу внедрить систему питания, такую ​​как система Редстоун, в Minecraft.

У меня есть n источников питания и m кабелей. Если я отключу источник питания или кабель, цепь должна отключиться. модель кабельной системы

Как мне избежать кругов? Если каждый кабель со статусом «включен» питает близлежащие кабели, я могу создать бесконечные круги, где нет источника питания (см. Изображение). Плюс сайт в том что он работает в Т = м

Я мог посылать импульс питания через график, начиная с каждого источника питания, и при каждом обновлении я отключал каждый кабель. Проблема в том, что он работает в T = n * m.

Есть ли лучшая практика? В Minecraft система Redstone была очень медленной, так что, я думаю, я что-то упустил.

РЕДАКТИРОВАТЬ: система должна работать без затухания на основе расстояния.

Бенедикт С. Фоглер
источник
Это зависит от того, какую модель вы пытаетесь реализовать. Например, источник питания может обеспечить х единиц энергии, которые потребляются при использовании. Другая модель заключается в том, что ваш источник питания имеет потенциал, который ограничивает нагрузку, которую вы можете поместить в цепь, которая функционирует до тех пор, пока вы подаете питание на ваш источник питания.
user3730788

Ответы:

7

Рекурсивное распространение. Например, у вас есть лампа, подключенная с помощью N кабельных объектов к аккумулятору. Лампа спрашивает, подключен ли N-й кабель (это кабель, подключенный непосредственно к лампе). Затем N-й кабель запрашивает кабель N-1, подключен ли он и т. Д. Каждый раз, когда объект спрашивается, включен он или нет, он устанавливает lastEvaluatedпеременную для текущего времени кадра. Рекурсия заканчивается на конечном узле, например на батарее, или когда он достигает объекта, который уже был оценен в этом кадре (это позволяет избежать бесконечной рекурсии). Эти распространения происходят только при изменении системы. Изменения включают добавление / удаление частей или переключателей.

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

MichaelHouse
источник
Эта. Плюс я бы добавил ссылку на источник питания. Если элемент изменился, скажите указанному источнику обновить сеть, к которой он подключен. Таким образом, вы не только должны обновляться, когда что-то меняется, вы также знаете, какие элементы затронуты. Изменения в вашей сети также могут быть легко идентифицированы: потребует ли изменение переоценки или нет и т. Д.
Felsir
@ Feelsir Вы могли бы сделать это, но я бы не рекомендовал это. Это увеличивает сложность без особой выгоды. Может быть несколько источников питания и несколько приемников на источник. Было бы проще просто доверять оценке, и это действительно не так много ресурсов.
MichaelHouse
Я думаю, что в решении не хватает одной вещи: если у вас есть кабель посередине, и у одного есть питание, а у другого конца есть не только один, то получит замену. Вы в основном создали дерево с переменной lastEvauluated, удалив круги. Теперь изменения распространяются вверх по дереву, но не вниз. Я попробую это в моей реализации, нажав на изменения вниз после их подтягивания.
Бенедикт С. Фоглер
Я добавил решение к вашему ответу в одном редакторе, потому что мое дополнение к алгоритму работает.
Бенедикт С. Воглер
1
@Benedikt Вы описываете ожидаемое поведение. Это потому, что моя система использует входы и выходы. Ворота И и ИЛИ не являются двунаправленными (они используют диодную реализацию). Итак, если бы я хотел, чтобы мощность переместилась на что-то за пределами узла B, я бы направил вывод таким образом. Я бы предложил вам создать новый ответ для описания вашей двунаправленной системы.
MichaelHouse
2

В Minecraft существует затухание на основе расстояния с очень коротким расстоянием затухания (диапазон 16 блоков).

Что вам нужно, это тест на связность между графиками .

Один из способов сделать это - многократно брать каждое ребро и объединять связанные узлы в один узел. После того, как все ребра исчезли, вы получите узел для каждой сети. Тогда посылка власти тривиальна.

чокнутый урод
источник
Сохранение подграфа в узле кажется очень умным, однако мне нужно немного подумать о конкретной реализации, потому что в этом ответе немного расплывчато упоминание только о «тесте связности между графами», который представляется довольно важной частью этого решения.
Бенедикт С. Фоглер,
на самом деле это описание является вариантом алгоритма Каргера для поиска минимальных срезов. Но вместо этого вы продолжаете, пока не будет больше краев для контракта.
чокнутый урод
1

Блок питания имеет несколько входных / выходных соединений, но в начальной точке мы не знаем, когда он вводится или выводится.

Каждый блок имеет «Напряжение», которое представляет собой энергию, которая поступает к нему за вычетом потерянного / использованного.

Блок с питанием будет обеспечивать питание для всех окружающих блоков, и каждый блок принимает в качестве входных данных более высокое напряжение от окружающих блоков. Вы также можете усложнить систему, определив интенсивность, но я останусь с Voltage только для простоты.

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

Я бы посоветовал вам разработать интерфейс для любого работающего объекта (куб в MC):

class PowerInterface
{
protected:
    std::vector<shared_ptr<PowerInterface>> sibling;

    double energy=0;
    bool   isActive = false;

    virtual void propagate(double inEnergy) = 0;

    virtual void addSibling(shared_ptr<PowerInterface> newSibling) = 0;
    virtual void removeSibling( shared_ptr<PowerInterface> remSibling) =0;
};

Итак, предположим, что вы реализуете addSibling и removeSibling, наиболее важной частью является функция распространения:

void PoweredCube::propagate( double inEnergy ) 
{
    // Define the behaviour
    energy = inEnergy-1.0; // Normal device
    energy = inEnergy-0.1; // Normal cable
    energy = 10.0;         // Normal source of power.

    if (energy<0.0)
    { 
        energy = 0.0;
        isActive = false;
        // No energy, so do not propagate anymore
        return;
    }
    isActive = true;

    // Propagate
    for (auto &s: sibling)
    {
        // Only propagate to sibling with less energy. 
        if (energy > s->energy) s->propagate( energy);
    }
}

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

Адриан Мэйр
источник
Но с этим решением каждый цикл нуждается в вызовах «n = 1 / уменьшение», прежде чем он будет остановлен. Разве это не дорого?
Бенедикт С. Фоглер
Нет, вы обновляете только изменения: когда вы создаете / удаляете / редактируете блок или когда блок производит активное изменение. Если вы посмотрите код, большинство изменений будет распространяться только на несколько блоков. Конечно, вы могли бы упростить график, как говорит @ BenediktS.Vogler, но ИМХО, это будет довольно быстро. Давайте разместим 1000 активных блоков в активной зоне, что уже является огромным механизмом. Даже в худшем случае полного обновления это всего лишь несколько операций * 1000, что мало. Обычно обновляются только несколько блоков
Adrian Maire