¿La forma más rápida de agrupar unidades que se puedan ver?

12

En el juego 2D con el que estoy trabajando, el motor del juego me puede dar, para cada unidad, la lista de otras unidades que están en su rango de visión.

Me gustaría saber si existe un algoritmo establecido para clasificar las unidades en grupos , donde cada grupo estaría definido por todas esas unidades que están "conectadas" entre sí (incluso a través de otras).

Un ejemplo podría ayudar a comprender mejor la pregunta (E = enemigo, O = propia unidad). Primero los datos que obtendría del motor del juego:

E1 can see E2, E3, O5
E2 can see E1
E3 can see E1
E4 can see O5
E5 can see O2
E6 can see E7, O9, O1
E7 can see E6
O1 can see E6
O2 can see O5, E5
O5 can see E1, E4, O2
O9 can see E6

Entonces debería calcular los grupos de la siguiente manera:

G1 = E1, E2, E3, E4, E5, O2, O5
G2 = O1, O9, E6, E7

Se puede suponer con seguridad que existe una propiedad conmutativa para el campo de visión: [si A ve B, entonces B ve A].

Solo para aclarar: ya escribí una implementación ingenua que se repite en cada fila de la información del motor del juego, pero por lo que parece, parece un problema lo suficientemente general como para que se haya estudiado en profundidad y tenga varios algoritmos establecidos (tal vez pasando a través de alguna estructura en forma de árbol?). Mi problema es que no pude encontrar una manera de describir mi problema que arrojó resultados de Google útiles.

¡Gracias de antemano por su ayuda!

Mac
fuente
1
Creo que esta pregunta es lo suficientemente general como para obtener mejores respuestas en stackoverflow, o tal vez incluso matemáticas (¿teoría de conjuntos?). No es el desarrollo del juego específico, es mi punto.
Tor Valamo
1
@Tor: probablemente sea cierto, pero el hecho de que sabemos que es para un juego podría permitir a las personas elaborar respuestas que sean más específicas para el problema.
Robert Fraser
Creo que podría hacer algunas cosas inteligentes con un giro en el hashing espacial y un mapa de visibilidad, solo necesito pensarlo.
Jonathan Dickinson el

Respuestas:

7

Si su relación "puede ver" es simétrica, de modo que "A puede ver B" implica que "B puede ver A", entonces los grupos que desea calcular son los componentes conectados del gráfico definidos por la relación "puede ver". Como otros han señalado, existen algoritmos simples para calcularlos, tales como:

while ungrouped units remain:
    let u = arbitrary ungrouped unit
    let g = new group
    let s = temporary stack
    assign u to g
    push u onto s
    while s is not empty:
        let v = topmost unit in s
        remove v from s
        for each unit w that v can see:
            if w is ungrouped:
                assign w to g
                push w onto s
            end if
        end for
    end while
 end while

(Se puede usar una cola o cualquier otra colección que implemente eficientemente las operaciones "agregar nuevo elemento" y "eliminar y devolver algún elemento" en lugar de la pila santerior).

Si su relación "puede ver" no es simétrica, debe decidir si desea que sus grupos sean los componentes fuertemente o débilmente conectados. Para componentes débilmente conectados, el algoritmo anterior funcionará tal cual, excepto que la línea for each unit w that v can seedebe reemplazarse por for each unit w that can see v, or that v can see. Para componentes fuertemente conectados , puede usar uno de los algoritmos (de Kosaraju , Tarjan o Gabow ) mencionados en la página de Wikipedia vinculada.

Para las relaciones no simétricas, es posible que también desee calcular el cierre transitivo de la relación, o de sus componentes fuertemente conectados. Para esto, puede usar el algoritmo Floyd – Warshall ; vea esta respuesta en SO para obtener (un poco) más información.


PD. Como el artículo de Wikipedia que he vinculado a las notas anteriores, puede ser más eficiente actualizar dinámicamente los grupos a medida que cambia la relación de visibilidad. No estoy familiarizado con los algoritmos avanzados (?) Mencionados en Wikipedia, pero no debería ser difícil improvisar algo que al menos sea mejor que volver a calcular los grupos desde cero cada vez.

La mitad de esto es fácil: si dos unidades en diferentes grupos adquieren una línea de visión entre ellas, fusiona los grupos. Tratar con unidades que se pierden de vista es un poco más complicado; Una solución simple pero quizás no óptima es volver a ejecutar el algoritmo de agrupación para las unidades en el grupo afectado siempre que eso suceda. Hay algunas optimizaciones que puede hacer para eso, si los cambios de visibilidad ocurren un par de unidades a la vez:

  • Si una unidad solo puede ver otra unidad y la pierde de vista, simplemente retírela de su grupo anterior y asígnela a un grupo nuevo.
  • De lo contrario, podría comenzar en una de las unidades afectadas y ejecutar una búsqueda A * en el gráfico de visibilidad (utilizando, por ejemplo, la distancia en línea recta como heurística) para la otra unidad. Si lo encuentra, el grupo no se separó; si no lo hace, el conjunto de unidades que visitó la búsqueda forma el nuevo grupo.
  • Podría intentar adivinar cuál de las dos unidades es más probable que pertenezca a la mitad más pequeña del grupo, si se divide, y comenzar la búsqueda desde esa unidad. Una posibilidad sería comenzar siempre desde la unidad que puede ver directamente menos unidades.
Ilmari Karonen
fuente
4

Lo que tienes es un gráfico de conectividad. Y, en general, la mejor manera de agrupar los nodos conectados (es decir, los caracteres) juntos es con un algoritmo de búsqueda de gráficos. Profundidad primero, ancho primero, lo que sea. Todo lo que está haciendo es crear una lista de qué nodos son accesibles desde todos los demás. Mientras su gráfico no esté dirigido (si A es visible para B, entonces B es visible para A), esto funciona bien.

Puede haber algunos algoritmos para mejorar esto para casos específicos. Por ejemplo, si a veces los personajes no se mueven (y el terreno tampoco se mueve, por lo que los personajes inmóviles permanecen visibles), puede optar por no probarlos nuevamente para actualizar sus gráficos de conectividad.

Pero, en general, tendrá que volver a probar la visibilidad en cada fotograma. Las probabilidades son que va a ser más lento que el recorrido del gráfico para encontrar los grupos de visibilidad.

Nicol Bolas
fuente
3
Solo para agregar el término técnico: lo que está tratando de encontrar son los componentes conectados del gráfico, y el algoritmo estándar es: (1) poner todos los nodos en una lista, (2) elegir un nodo, (3) encontrar todos los nodos conectados que usan BFS / DFS, (4) elimine todos los nodos que encontró de la lista, (5) repita hasta que no queden más nodos.
Nathan Reed el
3

Parece un problema de conectividad gráfica estándar. Puede haber algún tipo de algoritmo para esto, y podría verse así:

remaining units = all units
for each unit in remaining units:
    current group = create a new group
    add this unit to current group
    for each unit visible to this unit:
        if unit is in a group already:
            merge current group into that existing group
            set current group as that existing group
        else:
            remove that unit from remaining units
            add that unit to current group

Espero que sea posible implementar esto a través de un árbol, como el agrupamiento jerárquico, pero dudo que eso funcione más rápido: los árboles tienden a ser O (log N), mientras que la mayoría de las comprobaciones que doy anteriormente se pueden implementar como O (1) .

Kylotan
fuente
Fuera de interés, el enfoque de agrupamiento jerárquico es un poco así: para cada unidad, cree un grupo. Luego, por cada par de unidades que pueden verse entre sí, si están en grupos diferentes, combine los grupos en uno y descarte el otro.
Kylotan
Esto es lo que denominé implementación ingenua en mi OP. ¡Es bueno saber que podría no ser tan malo como pensaba entonces! :)
mac
La forma de hacerlo como árbol es usar un conjunto de unión con compresión de ruta . Eso no es muy ingenuo, y de hecho es óptimo.
Peter Taylor
2

Estoy de acuerdo con todos los demás que respondieron en términos de que se trata de un problema de conectividad gráfica, sin embargo, permítanme señalar que lo que necesita aquí es el gráfico de triangulación de Delaunay generado a partir de todas sus unidades relevantes. Lo que esto hace es garantizar que solo las unidades más cercanas entre sí se conectarán en el gráfico que genere. Le resultará muy difícil hacer esto de cualquier otra manera, ya que los cruces de gráficos (sin planaridad) harán que las unidades demasiado alejadas entre sí estén conectadas incorrectamente dentro del gráfico.

Lo anterior solo se aplica si está utilizando un espacio continuo (como en la mayoría de los FPS de movimiento libre); sin embargo, si ya tiene una cuadrícula subyacente (un gráfico plano) en el que se mueven sus unidades, puede usarla para evaluar la conectividad.

Ingeniero
fuente