Apakah ada cara untuk secara prosedural menghasilkan sejarah dunia?


28

Saya agak tertarik dengan diagram yang ditemukan di sini yang mewakili 1800 tahun sejarah budaya dalam dunia imajiner yang diciptakan oleh seorang pria.

masukkan deskripsi gambar di sini

Hal semacam ini tampaknya memiliki aplikasi yang kuat untuk pengembangan game, sejauh desain dunia.

Sepertinya dia melakukan diagram ini dengan tangan. Yang saya tertarik adalah melihat apakah ada cara untuk membuat diagram semacam ini secara programatik.

Jika Anda ditugaskan untuk membuat diagram dengan gaya di atas dari nilai acak, bagaimana Anda akan melakukannya? Apakah ada struktur atau algoritma data tertentu yang akan Anda pertimbangkan?


5
Pertimbangkan untuk melihat Benteng Dwarf . Sumber tidak tersedia dan proses generasi dunia tidak berdokumen (yang saya pikir saya tidak membuat jawaban ini) tetapi Anda dapat memeriksa sejarah dunia yang dihasilkan tanpa benar-benar harus belajar bermain permainan dan itu mungkin memberi Anda ide semacam itu hal yang dapat Anda lakukan.
Josh

Sumber lain, dan bukan jawaban yang dapat ditemukan di: www-cs-students.stanford.edu/~amitp/game-programming/… Ini adalah artikel untuk menghasilkan lingkungan tetapi terus menyentuh bagaimana lingkungan dapat digunakan untuk menentukan batas wilayah kerajaan berdasarkan sumber daya (seperti air, tanah layak huni, dll) yang dapat dilemparkan ke dalam campuran ketika orang pergi berperang untuk apa dan di mana atau seperti .. Sekali lagi, hanya sumber daya, bukan jawaban.
James

1
Diagram ini terlihat sangat mirip dengan grafik kekuatan di Civilization 3. Anda mungkin ingin memeriksa seri itu untuk beberapa ide.
WildWeazel

Jawaban:


15

Seberapa akurat yang Anda inginkan? Pilihan yang baik tetapi kompleks akan mensimulasikan semua sejarah itu:

  1. Buat daftar wilayah acak dan kedekatan antara wilayah ini.
  2. Hasilkan peradaban acak dengan karakteristik seperti populasi, pertikaian, teknologi ... dan isi daerah.
  3. Simulasikan sebanyak mungkin sejarah yang Anda inginkan, tentukan hasil berdasarkan karakteristik peradaban.

Misalnya: dua peradaban berperang yang berdekatan memiliki kemungkinan yang lebih tinggi untuk memulai perang satu sama lain, yang mengarah ke populasi yang lebih rendah seiring waktu. Peradaban pedagang memiliki sumber daya yang lebih tinggi, tetapi merupakan target yang bagus untuk invasi. Penduduk yang padat akan tumbuh lebih cepat tetapi juga memiliki lebih banyak peluang kelaparan. Kaum sipil yang heterogen secara budaya memiliki kemungkinan perang internal yang lebih rendah (yang dapat menyebabkan perpecahan.) Dan seterusnya ... Hasil juga akan memodifikasi karakteristik peradaban: teknologi yang lebih tinggi mengarah pada perdagangan yang lebih baik, senjata yang lebih kuat, dll.

Hal ini memungkinkan untuk beberapa penceritaan prosedural juga: Anda dapat menghasilkan tidak hanya diagram wilayah, tetapi juga deskripsi tekstual sejarah selama ini. Anda dapat membuat sistem ini serumit yang Anda inginkan.


EDIT: tantangan di sini bukan tantangan teknis, tetapi menyesuaikan heuristik untuk generasi sejarah yang realistis dan menarik. Lihatlah lebih dekat dan pikirkan 3 poin yang disebutkan di atas ... itulah penjelasan teknis Anda! Terjemahkan ke dalam satu lingkaran (setiap iterasi dapat mewakili waktu sebanyak yang Anda inginkan, 1 tahun, setengah tahun, 1 bulan ...) dan hanya itu. Anda harus mengerjakan inner (struktur data, heuristik) dan menyesuaikannya dengan masalah dan kebutuhan spesifik Anda. Itu bagian yang sulit di sini dan tidak ada yang bisa membantu Anda, karena ini tentang imajinasi, coba-coba.

Tidak ada struktur data umum untuk masalah ini selain yang akan Anda gunakan untuk hampir semua masalah: daftar, antrian, pohon ... dan ini akan terikat untuk implementasi spesifik Anda (apakah saya memerlukan pohon silsilah? Daftar peradaban berperang? antrian tugas untuk setiap warga sipil?) Tentu saja Anda memerlukan daftar peradaban juga. Pilihannya jelas dan cukup masuk akal.

Simulasi adalah masalah kebetulan / probabilitas dan Anda dapat membuatnya seribu cara berbeda dengan angka acak. Pikirkan permainan lain yang melibatkan simulasi seperti manajer sepak bola, RPG (lagipula, hitpoint / statistik hanyalah simulasi pertempuran ), permainan strategi ... Itu hanya karakteristik (jadi Anda akan memerlukan cara untuk menyimpan karakteristik dan data peradaban) dan hasil acak berdasarkan statistik mereka (jadi Anda harus mengubah keadaan simulasi secara acak berdasarkan karakteristik ini.)

Itulah inti dari algoritme Anda: heuristik yang sulit disesuaikan: cara mendistribusikan karakteristik pada awal simulasi untuk setiap peradaban dan cara mengubah keadaan simulasi secara statistik berdasarkan pada mereka.

Singkatnya: algoritma Anda hanya satu putaran mulai waktu yang disimulasikan dengan kenaikan yang diinginkan. Peningkatan yang lebih pendek menyebabkan simulasi historis yang lebih baik, tetapi jelas akan memakan waktu lebih lama. Di dalam lingkaran Anda akan ada banyak heuristik seperti (kira-kira):

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

Setelah semua pekerjaan ini (atau selama jika Anda tidak ingin menyimpan data), Anda harus menafsirkan semua keadaan simulasi ke dalam format yang dapat dibaca manusia seperti teks, gambar atau apa pun yang Anda inginkan. Ini juga trial and error dan sangat spesifik untuk implementasi Anda.

Khusus untuk pertanyaan Anda: untuk menghasilkan diagram seperti yang ada di pertanyaan Anda, Anda harus melacak wilayah dunia (di atas diagram, sumbu x, itulah poin 1: menghasilkan daftar wilayah dalam jawaban saya) dan peradaban mereka (warna dalam diagram, titik 2 ) melalui waktu (sumbu y, loop simulasi di titik 3. )

Mesin negaracukup pandai mensimulasikan topik luas (contoh kode di atas adalah perkiraan mesin negara yang dikodekan) - jadi Anda bisa mulai dengan menerapkan kerangka kerja mesin keadaan sederhana yang secara keseluruhan mudah untuk di-tweak. Setiap peradaban akan memulai dengan salah satu mesin negara ini dan simulasi akan menjalankan setiap mesin negara untuk setiap belokan. Setiap mesin negara harus dapat berinteraksi dengan mesin negara lain: misalnya memulai perang akan mempengaruhi mesin negara peradaban lain, mungkin dengan hasil yang berbeda berdasarkan keadaan internal mereka - misalnya jika mereka berada dalam keadaan 'kelaparan' mereka kemungkinan akan ingin menegosiasikan perdamaian, tetapi sebuah peradaban 'mencari masalah' kemungkinan akan membalas. Setiap negara di mesin akan memiliki efek yang berarti pada peradaban ' metrik yang diuraikan di atas selama setiap 'bingkai' (kekayaan, pertikaian, populasi, dll.). Yang paling penting Anda tidak perlu mengubah keadaan pada setiap frame - hanya ketika kesempatan dan / atau kesempatan acak muncul: ini memungkinkan peristiwa yang berkepanjangan (seperti perang) terjadi.


Terima kasih atas jawaban yang sangat bagus, meskipun tidak menyentuh aspek teknis yang saya khawatirkan
pdusen

@ pdusen komentarnya jadi cukup lama jadi saya memperbarui jawaban saya dengan tanda "EDIT".
kaoD

2
Saya akan menambahkan jawaban ini, jika Anda tidak keberatan?
Jonathan Dickinson

@ JonathanDickinson yakin, silakan :)
kaoD

@ pdusen saya menambahkan beberapa detail implementasi yang lebih spesifik.
Jonathan Dickinson

8

Ya ada. Berikut ini adalah penghasil sejarah yang sederhana:

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

Yang menghasilkan output seperti ini:

masukkan deskripsi gambar di sini

Sesuaikan heuristik untuk membuat grafik yang berbeda.

Cara paling sederhana untuk melakukan ini adalah dengan mengubah func = random.choice([merge, split, revolt, conquest])baris untuk memiliki lebih dari satu fungsi dengan nama yang sama. Misalnya func = random.choice([merge, split, revolt, conquest, merge, merge])akan menyebabkan negara lebih sering bergabung.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.