절차 적으로 세계의 역사를 생성 할 수있는 방법이 있습니까?


28

나는 다소 발견 그림에 흥미를하고 여기에 어떤 사람이 만든 가상의 세계 문화 역사 1800 년 대표.

여기에 이미지 설명을 입력하십시오

이런 종류의 것은 세계 디자인과 마찬가지로 게임 개발을위한 강력한 응용 프로그램을 가지고있는 것 같습니다.

그가이 도표를 손으로 한 것 같습니다. 내가 관심이있는 것은 프로그래밍 방식으로 이런 종류의 다이어그램을 만드는 방법이 있는지 보는 것입니다.

임의의 값으로 위의 스타일로 다이어그램을 생성하는 작업을 수행했다면 어떻게 할 것입니까? 고려할 특정 데이터 구조 나 알고리즘이 있습니까?


5
Dwarf Fortress를 살펴보십시오 . 소스를 사용할 수 없으며 세계 생성 프로세스가 문서화되어 있지 않기 때문에 (나는 이것을 대답하지 않는 이유) 실제로 게임을 배우지 않고도 생성 된 세계 역사를 조사 할 수 있으며, 이에 대한 아이디어를 얻을 수 있습니다 당신이 할 수있는 일들.
Josh

답변이없는 다른 자료는 다음 사이트 에서 찾을 수 있습니다. www-cs-students.stanford.edu/~amitp/game-programming/… 이것은 환경을 생성하기위한 기사이지만 환경이 어떻게 이루어지는 지에 대해 계속 논의합니다. 사람들이 무엇을 어디에서 또는 어디에서 전쟁을 할 때 섞일 수있는 자원 (물, 살기 좋은 땅 등)을 기반으로 왕국에 대한 지역 경계를 정의하는 데 사용됩니다. 다시 말하지만 답이 아닌 자원 일뿐입니다.
제임스

1
이 다이어그램은 Civilization 3의 파워 그래프와 매우 유사합니다.이 시리즈에서 몇 가지 아이디어를 확인하십시오.
WildWeazel

답변:


15

얼마나 정확하고 싶습니까? 훌륭하지만 복잡한 선택은 모든 역사를 시뮬레이션하는 것입니다.

  1. 이 지역들 사이에 임의의 지역 목록과 인접성을 생성하십시오.
  2. 인구, 호전, 기술 등의 특성으로 무작위 문명을 생성하고 지역을 채우십시오.
  3. 문명 특성에 따라 결과를 결정하면서 원하는만큼의 역사를 시뮬레이션하십시오.

예 : 인접한 두 호전적인 문명은 서로 전쟁을 시작할 확률이 높으므로 시간이 지남에 따라 인구가 줄어 듭니다. 상인 문명은 자원이 많지만 침략의 큰 목표입니다. 인구가 많은 사람은 더 빨리 자라지 만 기아 가능성이 더 큽니다. 문화적으로 이질적인 문명은 내부 전쟁의 가능성이 낮습니다 (이로 인해 이탈이 발생할 수 있습니다.) 등등 .. 결과도 문명 특성을 수정할 것입니다. 높은 기술은 더 나은 거래, 더 강한 무기 등으로 이어집니다.

이를 통해 절차 적 스토리 텔링도 가능합니다. 영토 다이어그램뿐만 아니라 시간의 역사에 대한 텍스트 설명도 출력 할 수 있습니다. 이 시스템을 원하는만큼 복잡하게 만들 수 있습니다.


편집 : 여기의 도전은 기술적 인 것이 아니라 사실적이고 흥미로운 역사 생성을 위해 휴리스틱을 조정합니다. 자세히 살펴보고 앞서 언급 한 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 의 시뮬레이션 루프 )

상태 머신광범위한 주제를 시뮬레이션하는 데 능숙합니다 (위의 코드 샘플은 하드 코딩 된 상태 머신의 근사치입니다). 따라서 전체적으로 조정하기 쉬운 간단한 상태 머신 프레임 워크를 구현하여 시작할 수 있습니다. 각 문명은 이러한 상태 머신 중 하나에서 시작하여 시뮬레이션은 각 턴마다 각 상태 머신을 실행합니다. 각 국가 기계는 다른 국가 기계와 상호 작용할 수 있어야합니다. 예를 들어, 전쟁을 시작하면 다른 문명의 국가 기계에 영향을 줄 수 있습니다. 예를 들어 내부 상태에 따라 다른 결과를 낼 수 있습니다. 평화 협상을 원하지만 '문제를 찾는'문명은 보복 할 것입니다. 기계의 각 상태는 문명에 의미있는 영향을 미칩니다. 각 '프레임'(부, 호전, 인구 등) 동안 위에 요약 된 지표. 가장 중요한 것은 기회 및 / 또는 임의의 기회가 발생할 때마다 모든 프레임에서 상태를 전환 할 필요가 없다는 것입니다. 이렇게하면 전쟁과 같은 장기적인 이벤트가 발생할 수 있습니다.


내가 걱정하는 기술적 측면을 다루지 않더라도 아주 좋은 답변에 감사드립니다
pdusen

@ pdusen 댓글이 너무 길어 "편집"표시로 답변을 업데이트했습니다.
kaoD February

2
마음에 들지 않으면이 답변에 추가 할 것입니까?
Jonathan Dickinson


@ 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])국가들이 더 자주 합병하게 될 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.