Есть ли способ, чтобы процедурно генерировать историю мира?


28

Меня несколько заинтриговала диаграмма, изображенная здесь, представляющая 1800 лет истории культуры в воображаемом мире, который создал какой-то парень.

введите описание изображения здесь

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

Похоже, он сделал эту диаграмму вручную. Меня интересует, есть ли способ создать диаграмму такого рода программно.

Если бы вам было поручено создавать диаграммы в стиле вышеупомянутого из случайных значений, как бы вы поступили? Существуют ли какие-либо конкретные структуры данных или алгоритмы, которые вы бы рассмотрели?


5
Рассмотрим взглянуть на Dwarf Fortress . Источник недоступен, а процесс генерации мира не задокументирован (поэтому я не даю этому ответа), но вы можете изучить сгенерированную мировую историю, фактически не научившись играть в игру, и она может дать вам представление о вещей, которые вы можете сделать.
Джош

Другой ресурс, а не ответ, можно найти по адресу: www-cs-students.stanford.edu/~amitp/game-programming/… Это статья для создания среды, но далее она затрагивает вопрос о том, как среда может быть используется для определения региональных границ королевств, основанных на ресурсах (таких как вода, пригодная для жизни земля и т. д.), которые могут быть добавлены в смесь, когда люди начинают войну из-за того, что и где, или как ... Опять же, просто ресурс, а не ответ.
Джеймс

1
Эта диаграмма очень похожа на график силы в Civilization 3. Вы можете проверить эту серию для некоторых идей.
WildWeazel

Ответы:


15

Насколько точным ты хочешь быть? Хороший, но сложный выбор будет моделировать всю эту историю:

  1. Создайте случайный список регионов и смежности между этими регионами.
  2. Создайте случайные цивилизации с такими характеристиками, как население, воинственность, технологии ... и заселите регионы.
  3. Смоделируйте столько лет истории, сколько хотите, определяя результаты на основе характеристик цивилизации.

Например: две соседние воюющие цивилизации имеют более высокую вероятность начала войны друг с другом, что приводит к снижению численности населения с течением времени. Торговые цивилизации имеют более высокие ресурсы, но являются отличной целью для вторжений. Густонаселенные будут расти быстрее, но также имеют больше шансов голода. Культурно-неоднородные цивилизации имеют меньшую вероятность внутренних войн (что может привести к распадам). И т. Д. Результаты также изменят характеристики цивилизации: более высокие технологии ведут к лучшей торговле, более сильному оружию и т. Д.

Это позволяет также использовать некоторые процедурные повествования: вы можете выводить не только диаграмму территории, но и текстовые описания истории на протяжении всего времени. Вы можете сделать эту систему настолько сложной, насколько захотите.


РЕДАКТИРОВАТЬ: задача здесь не техническая, а корректировка эвристики для реалистичной и интересной генерации истории. Присмотритесь поближе и подумайте о трех вышеупомянутых моментах ... это в значительной степени ваше техническое объяснение! Переведите это в цикл (каждая итерация может представлять столько времени, сколько вы хотите, 1 год, полгода, 1 месяц ...) и все. Вам придется работать с внутренностями (структура данных, эвристика) и адаптировать его к вашей конкретной проблеме и потребностям. Это самая сложная часть, и никто не может вам помочь, так как речь идет о воображении, пробах и ошибках.

Для этой проблемы нет общих структур данных, кроме тех, которые вы будете использовать практически для любой задачи: списки, очереди, деревья ... и они будут привязаны к вашей конкретной реализации (нужно ли мне генеалогическое дерево? Список цивилизаций) на войне - очередь заданий для каждой цивилизации ?) Конечно, вам нужен и список цивилизаций. Выбор очевиден и в значительной степени основан на здравом смысле.

Симуляция - это случайность / вероятность, и вы можете сделать это тысячами разных способов со случайными числами. Подумайте о любой другой игре, в которой задействованы симуляторы, такие как футбольные менеджеры, RPG (в конце концов, хитпоинты / статистика - это просто боевой симулятор ), стратегические игры ... Это всего лишь характеристики (так что вам понадобится способ хранения характеристик и данных цивилизации) и случайные результаты статистически основаны на них (так что вам придется случайным образом изменять состояние моделирования на основе этих характеристик).

В этом суть вашего алгоритма: сложная настройка эвристики: как распределить характеристики в начале симуляции для каждой цивилизации и как статистически изменить состояние симуляции на их основе.

Вкратце: ваш алгоритм - это просто цикл, в котором моделируется время с любым желаемым приращением. Более короткие приращения приводят к более точному историческому моделированию, но, очевидно, займут больше времени. Внутри вашего цикла будет несколько эвристик вроде (примерно):

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()

После всей этой работы (или во время, если вы не хотите сохранять данные) вы должны интерпретировать все состояния симуляции в удобочитаемый для человека формат, такой как текст, изображения или что угодно. Это тоже метод проб и ошибок, очень специфичный для вашей реализации.

Конкретно для вашего вопроса: чтобы сгенерировать диаграмму наподобие той, что в вашем вопросе, вам нужно отслеживать регионы мира (верхняя часть диаграммы, ось x, это точка 1: создать список регионов в моем ответе) и их цивилизации (цвета в диаграмма, точка 2 ) через время (ось у, цикл моделирования в точке 3 )

Государственные машиныдовольно хорошо имитируют широкие темы (пример кода выше является приближением к жестко запрограммированному конечному автомату), поэтому вы можете начать с реализации простой структуры конечного автомата, которую в целом легко настроить. Каждая цивилизация начиналась с одного из этих конечных автоматов, и симуляция запускала каждый конечный автомат для каждого хода. Каждый конечный автомат должен был бы иметь возможность взаимодействовать с другим конечным автоматом: например, начало войны повлияет на конечный автомат другой цивилизации, возможно, с различными результатами, основанными на их внутреннем состоянии - например, если они находятся в состоянии «голода», они, вероятно, будут Я хочу договориться о мире, но цивилизация, «ищущая проблемы», скорее всего, отомстит. Каждое состояние в машине будет иметь существенное влияние на цивилизацию » s метрики, обозначенные выше в каждом «кадре» (богатство, агрессивность, население и т. д.). Самое главное, что вам не нужно переходить в каждом кадре - именно тогда, когда появляется возможность и / или случайный шанс: это позволяет происходить длительным событиям (таким как война).


Спасибо за очень хороший ответ, хотя он не затрагивает технические аспекты, которые меня беспокоят
pdusen

@pdusen комментарий получился довольно длинным, поэтому я обновил свой ответ с пометкой «РЕДАКТИРОВАТЬ».
kaoD

2
Я собираюсь добавить к этому ответу, если вы не возражаете?
Джонатан Дикинсон

@JonathanDickinson конечно, продолжай :)
kaoD

@pdusen Я добавил еще несколько деталей, связанных с реализацией.
Джонатан Дикинсон

8

Да, есть. Вот простой в использовании генератор истории:

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

Который производит вывод как это:

введите описание изображения здесь

Настройте эвристику для создания разных графиков.

Самый простой способ сделать это - изменить func = random.choice([merge, split, revolt, conquest])строку, чтобы иметь более одной функции с одинаковым именем. Например, страны func = random.choice([merge, split, revolt, conquest, merge, merge])будут объединяться чаще.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.