我发现这里的图表代表了一个人在一个虚构的世界中1800年的文化历史,对此我颇有兴趣。
就世界设计而言,这种东西似乎在游戏开发中具有强大的应用。
看起来他是手工绘制的。我感兴趣的是查看是否有一种以编程方式创建这种图表的方法。
如果您负责从随机值生成上述样式的图表,您将如何处理?您是否会考虑任何特定的数据结构或算法?
我发现这里的图表代表了一个人在一个虚构的世界中1800年的文化历史,对此我颇有兴趣。
就世界设计而言,这种东西似乎在游戏开发中具有强大的应用。
看起来他是手工绘制的。我感兴趣的是查看是否有一种以编程方式创建这种图表的方法。
如果您负责从随机值生成上述样式的图表,您将如何处理?您是否会考虑任何特定的数据结构或算法?
Answers:
您想要多精确?一个好的但复杂的选择将是模拟所有历史:
例如:两个相邻的交战文明相互之间发动战争的可能性更高,随着时间的推移,导致人口减少。商人文明拥有更高的资源,但是入侵的主要目标。人口稠密的国家将增长更快,但也有更多的饥饿机会。具有不同文化背景的公民发生内战的可能性较低(可能导致分裂)。依此类推……结果也将改变文明的特征:更高的技术会导致更好的交易,更强大的武器等。
这也允许进行一些过程性的故事讲述:您不仅可以输出区域图,还可以输出历史上的文字描述。您可以根据需要使此系统复杂。
编辑:这里的挑战不是技术性的挑战,而是调整启发法以实现现实和有趣的历史。仔细研究一下上述3点...这几乎是您的技术解释!将其转换为循环(每个迭代可以代表您想要的时间,一年,半年,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的仿真循环)。
状态机非常擅长模拟广泛的主题(上面的代码示例是硬编码状态机的近似)-因此,您可能首先要实现一个总体上易于调整的简单状态机框架。每个文明都将从这些状态机之一开始,而仿真将在每个回合中运行每个状态机。每个状态机都需要能够与其他状态机进行交互:例如,发动战争将影响另一个文明的状态机,并可能基于其内部状态而产生不同的结果-例如,如果它们处于“饥荒”状态,则可能希望谈判和平,但“寻找麻烦”的文明可能会进行报复。机器中的每个状态都会对文明产生重大影响。在每个“框架”(财富,好战,平民等)中概述的指标。最重要的是,您不需要在每个帧上都转移状态-只是在机会和/或随机机会出现时就可以了:这允许长时间的事件(例如战争)发生。
就在这里。这是一个简单的历史记录生成器:
#!/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])
将导致国家更频繁地合并。