ここにある図は、ある人が作成した想像上の世界の1800年の文化史を表しています。
この種のものは、世界のデザインに関する限り、ゲーム開発に強力な用途があるように思われます。
彼はこの図を手でやったようです。私が興味を持っているのは、この種のダイアグラムをプログラムで作成する方法があるかどうかを見ることです。
ランダムな値から上記のスタイルのダイアグラムを生成するタスクを課された場合、どのように対処しますか?検討する特定のデータ構造またはアルゴリズムはありますか?
ここにある図は、ある人が作成した想像上の世界の1800年の文化史を表しています。
この種のものは、世界のデザインに関する限り、ゲーム開発に強力な用途があるように思われます。
彼はこの図を手でやったようです。私が興味を持っているのは、この種のダイアグラムをプログラムで作成する方法があるかどうかを見ることです。
ランダムな値から上記のスタイルのダイアグラムを生成するタスクを課された場合、どのように対処しますか?検討する特定のデータ構造またはアルゴリズムはありますか?
回答:
どのくらい正確になりたいですか?良いが複雑な選択は、そのすべての履歴をシミュレートすることです
たとえば、2つの隣接する好戦的な文明は、互いに戦争を開始する可能性が高く、時間の経過とともに人口が減少します。商人文明はより高い資源を持っていますが、侵略の大きな標的です。人口の多い人はより速く成長しますが、空腹の可能性も高くなります。文化的に異質な都市は、内部戦争の可能性が低くなります(解散につながる可能性があります)など...結果は文明の特性も変更します。技術の向上は、より良い取引、より強力な武器などにつながります。
これにより、いくつかの手続き的なストーリーテリングも可能になります。テリトリーダイアグラムだけでなく、時間全体の履歴のテキスト記述も出力できます。このシステムは、必要に応じて複雑にすることができます。
編集:ここでの課題は技術的なものではなく、現実的で興味深い履歴生成のためにヒューリスティックを調整します。よく見て、前述の3つのポイントについて考えてみてください...それはほとんど技術的な説明です!それをループに変換します(各反復は、1年、半年、1か月など、必要なだけの時間を表すことができます)。内部(データ構造、ヒューリスティック)を処理し、特定の問題やニーズに合わせて調整する必要があります。それはここでは難しい部分であり、想像力、試行錯誤に関するものなので、誰もあなたを助けることができません。
ほとんどすべての問題に使用するもの以外に、この問題に共通のデータ構造はありません:リスト、キュー、ツリー...これらは特定の実装に結び付けられます(系図ツリーが必要ですか?文明のリスト戦争中ですか?各civのタスクのキュー?)もちろん、文明のリストも必要です。選択は明白であり、ほとんど常識です。
シミュレーションは偶然/確率の問題であり、乱数を使って何千もの異なる方法を作ることができます。フットボールマネージャー、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のシミュレーションループ)
ステートマシン幅広いトピックのシミュレーションが得意です(上記のコードサンプルはハードコードされた状態マシンの近似です)。したがって、全体的に微調整しやすい単純な状態マシンフレームワークを実装することから始めることができます。各文明はこれらのステートマシンの1つから始まり、各ターンでシミュレーションが各ステートマシンを実行します。各ステートマシンは他のステートマシンと対話できる必要があります。たとえば、戦争の開始は別の文明のステートマシンに影響を与え、内部状態に基づいて結果が異なる場合があります。たとえば、「飢f」状態の場合平和を交渉したいが、「トラブルを探している」文明は報復するでしょう。機械の各状態は、文明に意味のある影響を与えます。s各「フレーム」中の上記の指標(富、好戦性、人口など)。最も重要なことは、すべてのフレームで状態を移行する必要がないことです-機会および/またはランダムな機会が発生した場合のみ:これにより、長期にわたるイベント(戦争など)が発生します。
はいあります。これは簡単な履歴ジェネレーターです。
#!/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])
、国がより頻繁に合併することになります。