Чи існує спосіб процедурного генерування історії світу?


28

Мене дещо заінтригує діаграма, знайдена тут, що представляє 1800 років культурної історії у уявному світі, який створив хлопець.

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

Здається, подібні речі мають сильне застосування для розробки ігор, настільки, як світовий дизайн.

Схоже, він робив цю схему вручну. Мені цікаво бачити, чи є спосіб створити подібну діаграму програмно.

Якби вам доручили генерувати діаграми у стилі вищезазначених із випадкових значень, як би ви це зробили? Чи є якісь конкретні структури даних або алгоритми, які ви б розглядали?


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

Інший ресурс, а не відповідь, можна знайти на веб-сайті: www-cs-students.stanford.edu/~amitp/game-programming/… Ця стаття для створення середовища, але вона стосується того, яким може бути середовище. використовується для визначення регіональних кордонів для королівств на основі ресурсів (наприклад, води, живої землі тощо), які можуть бути кинуті в суміш, коли люди йдуть на війну за те, що і де чи як. Знову просто ресурс, а не відповідь.
Джеймс

1
Ця діаграма дуже схожа на графік потужності в цивілізації 3. Можливо, ви хочете ознайомитись із цією серією щодо деяких ідей.
WildWeazel

Відповіді:


15

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

  1. Створіть випадковий список регіонів та суміжності між цими регіонами.
  2. Породжуйте випадкові цивілізації з такими характеристиками, як населення, войовничість, технології ... та заселяйте регіони.
  3. Моделюйте стільки років історії, скільки хочете, визначаючи результати, виходячи з цивілізаційних особливостей.

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

Це дозволяє і деякі процедурні розповіді: ви можете виводити не лише територіальну діаграму, а й текстові описи історії, що минули. Ви можете зробити цю систему такою складною, як вам захочеться.


EDIT: виклик тут не технічний, а коригування евристики для створення реалістичної та цікавої історії. Погляньте уважніше і подумайте про 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 ) через час (вісь y, цикл моделювання в точці 3 ).

Державні машинидуже добре моделюють широкі теми (приклад коду вище - це наближення жорстко закодованої машини) - тому можна почати з впровадження простої рамки державної машини, яку загалом легко налаштувати. Кожна цивілізація починалася б з однієї з цих державних машин, і симуляція запускала б кожну державну машину на кожен крок. Кожна державна машина повинна мати можливість взаємодіяти з іншою державною машиною: наприклад, ініціювання війни вплине на іншу цивілізаційну державну машину, можливо, з різними результатами залежно від їх внутрішнього стану - наприклад, якщо вони перебувають у стані "голоду", вони, ймовірно, хочу домовитись про мир, але цивілізація, яка "шукає неприємностей", швидше за все, помститься. Кожна держава в машині мала б значущий вплив на цивілізацію " s викладені вище показники під час кожного "кадру" (багатство, войовничість, кількість населення тощо). Найголовніше, що вам не потрібно перехідних станів на кожному кадрі - саме тоді, коли виникає можливість та / або випадковий шанс: це дозволяє тривати подовжені події (як війна).


Дякую за дуже приємну відповідь, хоча це не стосується технічних аспектів, які мене хвилюють
pdusen

@pdusen коментар отримав досить довго, тому я оновив свою відповідь на нього під позначкою "РЕДАКТУВАННЯ".
якDD

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.