Existe uma maneira de gerar processualmente a história de um mundo?

28

Estou um pouco intrigado com o diagrama encontrado aqui, que representa 1800 anos de história cultural em um mundo imaginário que um cara criou.

insira a descrição da imagem aqui

Esse tipo de coisa parece ter fortes aplicações para o desenvolvimento de jogos, na medida em que o design mundial.

Parece que ele fez esse diagrama manualmente. O que me interessa é ver se existe uma maneira de criar esse tipo de diagrama programaticamente.

Se você recebesse a tarefa de gerar diagramas no estilo acima, a partir de valores aleatórios, como você o faria? Existem estruturas de dados ou algoritmos específicos que você consideraria?

pdusen
fonte
5
Considere dar uma olhada no Dwarf Fortress . A fonte não está disponível e o processo de geração mundial não está documentado (e é por isso que não estou respondendo a isso), mas você pode examinar a história mundial gerada sem precisar aprender a jogar o jogo e isso pode lhe dar uma ideia do tipo de coisas que você pode fazer.
10282 Josh
Outro recurso, e não uma resposta, pode ser encontrado em: www-cs-students.stanford.edu/~amitp/game-programming/… Este é um artigo para gerar um ambiente, mas continua abordando como o ambiente pode ser usado para definir limites regionais para reinos com base em recursos (como água, terra habitável etc.) que podem ser misturados quando as pessoas entram em guerra sobre o que e onde ou como. Novamente, apenas um recurso, não uma resposta.
James
1
Este diagrama se parece muito com o gráfico de potência do Civilization 3. Você pode conferir essa série para algumas idéias.
WildWeazel

Respostas:

15

Quão preciso você quer ser? Uma escolha boa, porém complexa, seria simular toda essa história:

  1. Gere uma lista de regiões aleatórias e adjacências entre essas regiões.
  2. Gere civilizações aleatórias com características como população, beligerância, tecnologia ... e preencha as regiões.
  3. Simule quantos anos de história você desejar, determinando resultados com base nas características da civilização.

Por exemplo: duas civilizações beligerantes adjacentes têm maior probabilidade de iniciar uma guerra entre si, o que leva a uma população menor ao longo do tempo. As civilizações mercantes têm recursos mais altos, mas são um ótimo alvo para invasões. Os altamente populosos crescerão mais rapidamente, mas também terão mais chances de fome. Civs culturalmente heterogêneos têm uma chance menor de guerras internas (o que pode levar a rompimentos.) E assim por diante ... Os resultados também modificam as características da civilização: a alta tecnologia leva a um melhor comércio, armas mais fortes etc.

Isso também permite algumas narrativas processuais: você pode gerar não apenas um diagrama de território, mas também descrições textuais da história ao longo do tempo. Você pode tornar esse sistema tão complexo quanto desejar.


EDIT: o desafio aqui não é técnico, mas é ajustar as heurísticas para gerar uma história realista e interessante. Dê uma olhada e pense nos três pontos mencionados acima ... essa é praticamente a sua explicação técnica! Traduzir para um loop (cada iteração pode representar o tempo que você quiser, 1 ano, meio ano, 1 mês ...) e é isso. Você terá que trabalhar com os internos (estruturas de dados, heurísticas) e adaptá-los ao seu problema e necessidades específicos. Essa é a parte difícil aqui e ninguém pode ajudá-lo, já que se trata de imaginação, tentativa e erro.

Não há estruturas de dados comuns para esse problema além daquelas que você usará para praticamente qualquer problema: listas, filas, árvores ... e elas serão vinculadas à sua implementação específica (preciso de uma árvore genealógica? Uma lista de civilizações em guerra - uma fila de tarefas para cada civil?) É claro que você também precisa de uma lista de civilizações. As escolhas são óbvias e praticamente senso comum.

A simulação é uma questão de chance / probabilidade e você pode fazer isso de mil maneiras diferentes com números aleatórios. Pense em qualquer outro jogo em que a simulação esteja envolvida, como gerentes de futebol, RPGs (afinal, hitpoints / estatísticas são apenas simulações de combate ), jogos de estratégia ... São apenas características (então você precisará de uma maneira de armazenar características e dados da civilização) e resultados aleatórios estatisticamente baseados neles (então você terá que alterar aleatoriamente o estado da simulação com base nessas características).

Essa é a essência do seu algoritmo: as heurísticas difíceis de ajustar: como distribuir características no início da simulação para cada civilização e como alterar estatisticamente o estado da simulação com base nelas.

Em resumo: seu algoritmo é apenas um loop que varia o tempo simulado com qualquer incremento desejado. Incrementos mais curtos levam a uma simulação histórica mais refinada, mas obviamente levarão mais tempo. Dentro do seu loop, haverá várias 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()

Depois de todo esse trabalho (ou durante, se você não quiser armazenar os dados), você deve interpretar todo o estado da simulação em um formato legível por humanos, como texto, imagens ou o que você desejar. Isso também é tentativa e erro e é muito específico para sua implementação.

Específico à sua pergunta: para gerar um diagrama como o da sua pergunta, você terá que rastrear regiões do mundo (parte superior do diagrama, eixo x, esse é o ponto 1: gerar lista de regiões na minha resposta) e suas civilizações (cores no diagrama, ponto 2 ) ao longo do tempo (eixo y, o loop de simulação no ponto 3 ).

Máquinas de estadosão muito bons em simular tópicos amplos (o exemplo de código acima é uma aproximação de uma máquina de estado codificada) - então você pode começar implementando uma estrutura simples de máquina de estado que é fácil de ajustar. Cada civilização começaria com uma dessas máquinas de estado e a simulação executaria cada máquina de estado a cada turno. Cada máquina de estado precisaria ser capaz de interagir com outra máquina de estado: por exemplo, iniciar uma guerra afetaria a máquina de estado de outra civilização, possivelmente com resultados diferentes com base em seu estado interno - por exemplo, se eles estiverem no estado de "fome", provavelmente deseja negociar a paz, mas uma civilização que "esteja em busca de problemas" provavelmente retaliará. Cada estado na máquina teria efeitos significativos na civilização ' métricas descritas acima durante cada 'quadro' (riqueza, beligerância, população etc.). Mais importante ainda, você não precisa fazer a transição de estados em todos os quadros - exatamente quando surgem oportunidades e / ou chances aleatórias: isso permite que eventos prolongados (como a guerra) ocorram.

kaoD
fonte
Obrigado por uma resposta muito agradável, embora ele não toque sobre os aspectos técnicos Estou preocupado
pdusen
@pdusen o comentário ficou muito longo, então atualizei minha resposta com a marca "EDIT".
14132 kaoD
2
Vou acrescentar a esta resposta, se você não se importa?
31712 Jonathan Jonathaninson
@JonathanDickinson com certeza, vá em frente :)
kaoD 14/02
@pdusen Adicionei mais alguns detalhes específicos da implementação.
11136 Jonathan Dickinson
8

Sim existe. Aqui está um gerador de história simples e simples:

#!/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])))

O que produz resultados como este:

insira a descrição da imagem aqui

Ajuste as heurísticas para criar gráficos diferentes.

A maneira mais simples de fazer isso seria alterar a func = random.choice([merge, split, revolt, conquest])linha para ter mais de uma função com o mesmo nome. Por exemplo func = random.choice([merge, split, revolt, conquest, merge, merge]), levará as nações a se fundirem com mais frequência.

brice
fonte