¿Cómo se debe implementar un circuito o sistema de alimentación (como Redstone en Minecraft)?

8

Quiero implementar un sistema de energía como el sistema Redstone en Minecraft.

Tengo n fuentes de alimentación ym cables. Si desconecto la fuente de alimentación o un cable, el circuito debería apagarse. modelo de sistema de cable

¿Cómo evito los círculos? Si cada cable con el estado "encendido" alimenta los cables cercanos, puedo crear círculos infinitos donde no hay una fuente de energía involucrada (ver imagen). El sitio más es que se ejecuta en T = m

Podría enviar una ráfaga de energía a través del gráfico comenzando en cada fuente de energía y en cada llamada de actualización apago todos los cables. El problema es que se ejecuta en T = n * m.

¿Hay una mejor práctica? En Minecraft, el sistema de redstone era muy lento, así que creo que pasé por alto algo.

EDITAR: El sistema debería funcionar sin una desintegración basada en la distancia.

Benedikt S. Vogler
fuente
Esto depende del modelo que intente implementar. Por ejemplo, una fuente de energía podría proporcionar x unidades de energía que se consumen con el uso. Otro modelo es que su fuente de energía tiene un potencial que limita la carga que puede colocar en el circuito que funciona siempre que suministre energía a su fuente de energía.
user3730788

Respuestas:

7

Propagación recursiva. Por ejemplo, tiene una lámpara conectada por N objetos de cable a una batería. La lámpara le pregunta al enésimo cable si está alimentado (este es el cable conectado directamente a la lámpara). El enésimo cable le pregunta al cable N-1 si está encendido y así sucesivamente. Cada vez que se le pregunta a un objeto si está alimentado o no, establece una lastEvaluatedvariable al tiempo de cuadro actual. La recursión toca fondo en un nodo final, como una batería, o cuando alcanza un objeto que ya ha sido evaluado ese marco (esto evita una recursión infinita). Estas propagaciones solo ocurren cuando el sistema cambia. Los cambios incluyen agregar / quitar partes o interruptores que se alternan.

No hay disminución de distancia o restricciones similares con este sistema. Lo utilicé para crear un simulador de puerta lógica y funciona para varios ejemplos lógicos como un flip-flop.

MichaelHouse
fuente
Esta. Además, agregaría una referencia a la fuente de poder. Si se cambia un elemento, dígale a la fuente referenciada que actualice la red que está alimentando. De esta manera, no solo tiene que actualizar cuando algo cambia, también sabe qué elementos se ven afectados. Los cambios en su red también se pueden identificar fácilmente: un cambio requeriría una reevaluación o no, etc.
Felsir
@Felsir Podrías hacer eso, pero no lo recomendaría. Aumenta la complejidad sin mucha ganancia. Puede haber múltiples fuentes de energía y múltiples sumideros por fuente. Sería más fácil confiar en la evaluación, y realmente no es tan intensivo en recursos.
MichaelHouse
Creo que la solución carece de una cosa: si tiene un cable en el medio y uno tiene alimentación y el otro extremo no solo uno obtendrá el cambio. Básicamente, ha creado un árbol con la variable "lastEvauluated" quitando círculos. Ahora el cambio se propaga hacia arriba en el árbol pero no hacia abajo. Lo intentaré en mi implementación presionando los cambios hacia abajo después de levantarlos.
Benedikt S. Vogler
Agregué la solución a su respuesta en una edición porque mi adición al algoritmo funciona.
Benedikt S. Vogler
1
@Benedikt Usted describe el comportamiento esperado. Eso es porque mi sistema usa entradas y salidas. Las compuertas AND y OR no son bidireccionales (utilizan la implementación de diodos). Entonces, si quisiera que el poder viajara a algo más allá del nodo B, dirigiría una salida de esa manera. Le sugiero que cree una nueva respuesta para describir su sistema bidireccional.
MichaelHouse
2

En Minecraft hay una desintegración basada en la distancia con una distancia de desintegración muy corta (rango de 16 bloques).

Lo que necesitas es una prueba de conectividad entre gráficos .

Una forma de hacerlo sería tomar repetidamente cada borde y combinar los nodos conectados en un solo nodo. Después de que todos los bordes se hayan ido, terminará con un nodo para cada red. Entonces enviar poder es trivial.

monstruo de trinquete
fuente
Guardar un subgrafo en un nodo parece muy inteligente, sin embargo, tengo que pensar un poco en una implementación concreta porque esta respuesta se siente un poco vaga solo al mencionar la "prueba de conectividad entre gráficos" que parece ser una parte bastante importante de esta solución.
Benedikt S. Vogler
en realidad esa descripción es una variante del algoritmo de Karger para encontrar cortes mínimos. Pero, en cambio, continúa hasta que no haya más bordes para contraer.
monstruo de trinquete
1

Un bloque alimentado tiene varias conexiones de entrada / salida, pero en un punto de partida, no sabemos cuándo es entrada o salida.

Cada bloque tiene un "Voltaje", que es la energía que llega a él menos la pérdida / uso.

Un bloque alimentado proporcionará energía a todos los bloques circundantes, y cada bloque toma como entrada el voltaje más alto de los bloques circundantes. También podría complicar el sistema definiendo una Intensidad, pero me quedaré con Voltaje solo por simplicidad.

Cada vez que se realiza un cambio en el circuito, agregando / quitando bloques, o por el circuito en sí, el cambio debe propagarse a todo el circuito hasta la estabilidad.

Te sugiero que diseñes una interfaz para cualquier objeto alimentado (cubo en 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;
};

Entonces, suponiendo que implemente addSibling y removeSibling, la parte más importante es la función de propagación:

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);
    }
}

Como solución recursiva, cada bloque debe reducir un poco la energía, nunca aumentarla. La fuente de energía puede establecer un valor fijo, pero nunca aumentar según las entradas. Eso no debería ser un problema ya que todos los sistemas "reales" funcionan de esta manera.

Adrian Maire
fuente
Pero con esta solución cada bucle necesita llamadas "n = 1 / disminución" antes de que se cierre. ¿No es esto un poco costoso?
Benedikt S. Vogler
No, solo actualiza los cambios: cuando crea / elimina / edita un bloque, o cuando un bloque produce un cambio activo. Si observa el código, la mayoría de los cambios se propagarán solo unos pocos bloques. Por supuesto, podría simplificar el gráfico como dice @ BenediktS.Vogler, pero en mi humilde opinión, ya será bastante rápido. Supongamos 1000 bloques activos en la zona activa, que ya es un mecanismo enorme. Incluso en el peor de los casos de una actualización completa, son solo unas pocas operaciones * 1000, que es breve. Por lo general, solo se actualizan algunos bloques
Adrian Maire