¿Hay alguna manera de generar procesalmente la historia de un mundo?

28

Estoy algo intrigado por el diagrama que se encuentra aquí, que representa 1800 años de historia cultural en un mundo imaginario creado por un tipo.

ingrese la descripción de la imagen aquí

Este tipo de cosas parece tener fuertes aplicaciones para el desarrollo de juegos, en la medida en que el diseño mundial.

Parece que hizo este diagrama a mano. Lo que me interesa es ver si hay una manera de crear este tipo de diagrama mediante programación.

Si tuviera la tarea de generar diagramas en el estilo de lo anterior a partir de valores aleatorios, ¿cómo lo haría? ¿Hay alguna estructura de datos o algoritmo en particular que considere?

pdusen
fuente
55
Considera echar un vistazo a la Fortaleza Enana . La fuente no está disponible y el proceso de generación mundial no está documentado (por lo que no estoy respondiendo esto), pero puede examinar la historia mundial generada sin tener que aprender a jugar y podría darle una idea del tipo de cosas que puedes hacer.
Josh
Puede encontrar otro recurso, y no una respuesta en: www-cs-students.stanford.edu/~amitp/game-programming/... Este es un artículo para generar un entorno, pero continúa tocando cómo puede ser el entorno se usa para definir límites regionales para reinos basados ​​en recursos (como agua, tierra habitable, etc.) que podrían incluirse en la mezcla para cuando la gente vaya a la guerra sobre qué y dónde o como ... Nuevamente, solo un recurso, no una respuesta.
James
1
Este diagrama se parece mucho al gráfico de poder de Civilization 3. Es posible que desee consultar esa serie para obtener algunas ideas.
WildWeazel

Respuestas:

15

¿Qué tan preciso quieres ser? Una opción buena pero compleja sería simular toda esa historia:

  1. Genere una lista de regiones aleatorias y adyacencias entre estas regiones.
  2. Genera civilizaciones aleatorias con características como población, beligerancia, tecnología ... y puebla las regiones.
  3. Simule tantos años de historia como desee, determinando resultados basados ​​en las características de la civilización.

Por ejemplo: dos civilizaciones beligerantes adyacentes tienen una mayor probabilidad de comenzar una guerra entre sí, lo que conduce a una menor población con el tiempo. Las civilizaciones mercantes tienen mayores recursos, pero son un gran objetivo para las invasiones. Los altamente poblados crecerán más rápido pero también tienen más posibilidades de tener hambre. Las civilizaciones culturalmente heterogéneas tienen menos posibilidades de guerras internas (lo que podría conducir a la ruptura). Y así sucesivamente ... Los resultados también modificarían las características de la civilización: una tecnología más alta conduce a un mejor comercio, armas más fuertes, etc.

Esto también permite contar historias procesales: puede generar no solo un diagrama de territorio, sino también descripciones textuales de la historia a lo largo del tiempo. Puede hacer que este sistema sea tan complejo como desee.


EDITAR: el desafío aquí no es técnico, sino ajustar las heurísticas para una generación de historia realista e interesante. Eche un vistazo más de cerca y piense en los 3 puntos antes mencionados ... ¡esa es prácticamente su explicación técnica! Conviértalo a un bucle (cada iteración puede representar el tiempo que desee, 1 año, medio año, 1 mes ...) y listo. Tendrá que trabajar los elementos internos (estructuras de datos, heurística) y adaptarlo a su problema y necesidades específicas. Esa es la parte difícil aquí y nadie puede ayudarte, ya que se trata de imaginación, prueba y error.

No hay estructuras de datos comunes para este problema además de las que usará para casi cualquier problema: listas, colas, árboles ... y estos estarán vinculados para su implementación específica (¿necesito un árbol genealógico? Una lista de civilizaciones en guerra? una cola de tareas para cada civ?) Por supuesto, usted también necesita una lista de civilizaciones. Las opciones son obvias y tienen sentido común.

La simulación es una cuestión de azar / probabilidad y puedes hacerlo de mil maneras diferentes con números aleatorios. Piensa en cualquier otro juego donde la simulación esté involucrada, como los gerentes de fútbol, ​​los juegos de rol (después de todo, los puntos de vida / estadísticas son solo simulaciones de combate ), los juegos de estrategia ... Son solo características (por lo que necesitarás una forma de almacenar las características y datos de la civilización) y resultados aleatorios estadísticamente basados ​​en ellos (por lo que tendrá que cambiar aleatoriamente el estado de simulación en función de estas características).

Esa es la esencia de su algoritmo: la heurística difícil de ajustar: cómo distribuir las características al comienzo de la simulación para cada civilización y cómo cambiar estadísticamente el estado de simulación en función de ellas.

En resumen: su algoritmo es solo un bucle que varía el tiempo simulado con cualquier incremento deseado. Los incrementos más cortos conducen a una simulación histórica más fina, pero obviamente tomarán más tiempo. Dentro de su bucle habrá un montón de heurísticas como (aproximadamente):

for each civilization
  if civ.isAtWar
    civ.population -= civ.population * 0.05;
    civ.wealth -= 1000.0;
    civ.belligerence += 1.0;
  if civ.population < 100
    civ.negotiatePeace()

Después de todo este trabajo (o si no desea almacenar los datos), debe interpretar todo el estado de la simulación en un formato legible para humanos, como texto, imágenes o lo que desee. Esto también es prueba y error y muy específico para su implementación.

Específico para su pregunta: para generar un diagrama como el de su pregunta, tendrá que rastrear las regiones del mundo (parte superior del diagrama, eje x, ese es el punto 1: generar la lista de regiones en mi respuesta) y sus civilizaciones (colores en el diagrama, punto 2 ) a través del tiempo (eje y, el bucle de simulación en el punto 3 ).

Máquinas de estadoson bastante buenos para simular temas amplios (el ejemplo de código anterior es una aproximación de una máquina de estado codificada), por lo que puede comenzar implementando un marco de máquina de estado simple que en general sea fácil de ajustar. Cada civilización comenzaría con una de estas máquinas de estado y la simulación ejecutaría cada máquina de estado para cada turno. Cada máquina de estado necesitaría poder interactuar con otra máquina de estado: por ejemplo, iniciar una guerra afectaría a la máquina de estado de otra civilización, posiblemente con diferentes resultados basados ​​en su estado interno; por ejemplo, si están en el estado de 'hambruna' probablemente quiere negociar la paz, pero una civilización "buscando problemas" probablemente tomaría represalias. Cada estado en la máquina tendría efectos significativos en la civilización ' s métricas descritas anteriormente durante cada 'marco' (riqueza, beligerancia, población, etc.) Lo más importante es que no es necesario realizar una transición de estados en cada cuadro, justo cuando surge la oportunidad y / o el azar: esto permite que ocurran eventos prolongados (como la guerra).

kaoD
fuente
Gracias por una muy buena respuesta, a pesar de que no toca los aspectos técnicos que me preocupan
pdusen
@pdusen el comentario se hizo bastante largo, así que actualicé mi respuesta con la marca "EDITAR".
kaoD
2
Voy a agregar a esta respuesta, si no te importa?
Jonathan Dickinson
@JonathanDickinson seguro, adelante :)
kaoD
@pdusen Agregué algunos detalles más específicos de la implementación.
Jonathan Dickinson
8

Sí hay. Aquí hay un generador de historial muy simple:

#!/usr/bin/env python
# to create a visualisation, run like this:
#    ./timeline.py --dot | dot -Tpng > filename.png
import sys
import random
from pprint import pprint
# Names is a newline separated list of nation names.
file = "names.txt"
names = open(file, "r").read().split("\n") 
history = []
dot = False
if len(sys.argv) > 1 and sys.argv[1] == "--dot":
  dot = True

def wrap(str, wrap='"'):
  return wrap+str+wrap

def merge(states, names):
  number = random.randint(2,3)
  mergers = [] 
  if number < len(states):
    mergers = random.sample(states, number)
    new_name = random.choice(names)
    states = list(set(states).difference(set(mergers)))
    states.append(new_name)
    names.remove(new_name)
    if dot:
      for state in mergers:
        print '"%s" -> "%s"'%(state, new_name)
      print '{rank=same; %s }'%wrap(new_name)
    else:
      print "MERGE %s ==> '%s'"%( ", ".join(map(wrap,mergers)), new_name)
  return states, names 


def split(states, names):
  number = random.randint(2,3)
  if number < len(names):
    splitter = random.choice(states)
    states.remove(splitter)
    new_states = random.sample(names, number)
    names = list(set(names).difference(set(new_states)))
    states = list(set(states).union(set(new_states)))
    if dot:
      for state in new_states:
        print '"%s" -> "%s"'%(splitter, state)
      print '{rank=same; %s }'%("; ".join(map(wrap, new_states)))
    else:
      print "SPLIT '%s' ==> %s"%(splitter, ", ".join(map(wrap,new_states)))
  return states, names

def revolt(states, names):
  old = random.choice(states)
  new = random.choice(names)
  names.remove(new)
  states.remove(old)
  states.append(new)
  if dot:
    print '"%s" -> "%s"'%(old, new)
    print '{rank=same; "%s"}'%new
  else:
    print "REVOLT '%s' ==> '%s'"%(old, new)
  return states, names

def conquest(states, names):
  if len(states) > 1:
    loser = random.choice(states)
    states.remove(loser)
    winner = random.choice(states)
    if dot:
      print '"%s" -> "%s" [label="conquered by"]'%(loser, winner)
    else:
      print "CONQUEST '%s' conquered '%s'"%(winner, loser)
  return states, names


#ignore empty names
names = [name for name in names if name] #yes, really.

origin = random.sample(names, random.randint(1,3))
names = list(set(names).difference(set(origin)))
history.append(origin) #random starting states

if dot:
  print "digraph g {"
  print "{rank=same; %s}"%("; ".join(map(wrap,origin)))
else:
  print("BEGIN %s"%(", ".join(map(wrap,history[0]))))

while names:
  func = random.choice([merge, split, revolt, conquest])
  states, names = func(history[-1], names)
  history.append(states)

if dot:
  print '{rank=same; %s}'%("; ".join(map(wrap,history[-1])))
  print "}"
else:
  print "END %s"%(", ".join(map(wrap,history[-1])))

Lo que produce una salida como esta:

ingrese la descripción de la imagen aquí

Ajuste la heurística para crear diferentes gráficos.

La forma más sencilla de hacer esto sería cambiar la func = random.choice([merge, split, revolt, conquest])línea para que tenga más de una función con el mismo nombre. Por ejemplo func = random.choice([merge, split, revolt, conquest, merge, merge]), las naciones se fusionarán con más frecuencia.

brice
fuente