Spaces:
Runtime error
Runtime error
yingqianjiang-lingoace
commited on
Commit
•
0a0d866
1
Parent(s):
4e30523
update
Browse files- Character.py +45 -14
- CharacterStatistics.py +23 -7
- WorldSimulation.py +24 -5
- app.py +74 -17
- attribute_distribution.png +0 -0
- clan_size_distribution.png +0 -0
- config.py +3 -3
- plugins/BattlePlugin.py +2 -1
- plugins/BirthPlugin.py +3 -7
- plugins/CultivationPlugin.py +5 -0
- plugins/MarriagePlugin.py +6 -3
- plugins/ResourceDepletionPlugin.py +4 -2
- spiritual_roots_distribution.png +0 -0
- sum_spiritual_roots_distribution.png +0 -0
Character.py
CHANGED
@@ -18,6 +18,7 @@ class Character:
|
|
18 |
self.combat_power = self.calculate_combat_power()
|
19 |
self.partner = partner
|
20 |
self.is_alive = True
|
|
|
21 |
self.buff = False
|
22 |
self.history = []
|
23 |
self.special_history = []
|
@@ -27,7 +28,7 @@ class Character:
|
|
27 |
self.clan = clan # 宗族
|
28 |
|
29 |
def die(self):
|
30 |
-
if self.is_alive and self.
|
31 |
self.history.append(f"{self.real_age}岁,死亡")
|
32 |
self.special_history.append(f"{self.real_age}岁,死亡")
|
33 |
self.is_alive = False
|
@@ -37,7 +38,7 @@ class Character:
|
|
37 |
print("角色已经死亡,无法进行修炼。")
|
38 |
return
|
39 |
# 成仙者不再修炼
|
40 |
-
if self.
|
41 |
return
|
42 |
|
43 |
self.experience_points += experience_points
|
@@ -56,12 +57,17 @@ class Character:
|
|
56 |
self.experience_points = 0
|
57 |
self.history.append(f"{self.real_age}岁,突破成功,在{cultivation_level}级, 到达{self.view_rank()}")
|
58 |
self.special_history.append(f"{self.real_age}岁,突破成功,在{cultivation_level}级, 到达{self.view_rank()}")
|
|
|
|
|
|
|
|
|
|
|
59 |
else:
|
60 |
self.history.append(f"{self.real_age}岁,突破失败,在{self.cultivation_level}级")
|
61 |
|
62 |
def marry(self, partner):
|
63 |
# 成仙者不再结婚
|
64 |
-
if self.
|
65 |
return
|
66 |
if not self.is_alive:
|
67 |
print("角色已经死亡,无法结婚。")
|
@@ -86,7 +92,7 @@ class Character:
|
|
86 |
|
87 |
def give_birth(self):
|
88 |
# 成仙者不会生育
|
89 |
-
if self.
|
90 |
return
|
91 |
if not self.is_alive:
|
92 |
print("角色已经死亡,无法生育。")
|
@@ -116,7 +122,7 @@ class Character:
|
|
116 |
|
117 |
def grow(self):
|
118 |
# 成仙者不会衰老
|
119 |
-
if self.
|
120 |
return
|
121 |
self.real_age += 1
|
122 |
|
@@ -142,7 +148,7 @@ class Character:
|
|
142 |
attack_power = (self.cultivation_rank + 1) * 30 + 1 * (1 + self.cultivation_level)
|
143 |
defense_power = (self.cultivation_rank + 1) * 30 + 1 * (1 + self.cultivation_level)
|
144 |
attack_speed = (self.cultivation_rank + 1) * 30 + 1 * (1 + self.cultivation_level)
|
145 |
-
health_points = (self.cultivation_rank + 1) *
|
146 |
|
147 |
# 根据修为层次计算加成比例,初始加成为10%
|
148 |
bonus = 0.1 * (self.cultivation_rank * 0 + 1)
|
@@ -161,18 +167,18 @@ class Character:
|
|
161 |
defense_power *= 1 + bonus
|
162 |
|
163 |
return {
|
164 |
-
'attack_power': attack_power,
|
165 |
-
'defense_power': defense_power,
|
166 |
-
'attack_speed': attack_speed,
|
167 |
-
'health_points': health_points
|
168 |
}
|
169 |
|
170 |
def before_battle(self):
|
171 |
self.combat_power = self.calculate_combat_power()
|
172 |
|
173 |
-
def attack(self, opponent):
|
174 |
# 成仙者不会打架
|
175 |
-
if self.
|
176 |
return
|
177 |
if not self.is_alive:
|
178 |
print("角色已经死亡,无法攻击。")
|
@@ -182,8 +188,33 @@ class Character:
|
|
182 |
if self.special_constitution[0] == 1:
|
183 |
self.buff = True
|
184 |
|
185 |
-
#
|
186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
if damage > 0:
|
188 |
opponent.combat_power['health_points'] -= damage
|
189 |
|
|
|
18 |
self.combat_power = self.calculate_combat_power()
|
19 |
self.partner = partner
|
20 |
self.is_alive = True
|
21 |
+
self.is_immortal = False
|
22 |
self.buff = False
|
23 |
self.history = []
|
24 |
self.special_history = []
|
|
|
28 |
self.clan = clan # 宗族
|
29 |
|
30 |
def die(self):
|
31 |
+
if self.is_alive and not self.is_immortal:
|
32 |
self.history.append(f"{self.real_age}岁,死亡")
|
33 |
self.special_history.append(f"{self.real_age}岁,死亡")
|
34 |
self.is_alive = False
|
|
|
38 |
print("角色已经死亡,无法进行修炼。")
|
39 |
return
|
40 |
# 成仙者不再修炼
|
41 |
+
if self.is_immortal:
|
42 |
return
|
43 |
|
44 |
self.experience_points += experience_points
|
|
|
57 |
self.experience_points = 0
|
58 |
self.history.append(f"{self.real_age}岁,突破成功,在{cultivation_level}级, 到达{self.view_rank()}")
|
59 |
self.special_history.append(f"{self.real_age}岁,突破成功,在{cultivation_level}级, 到达{self.view_rank()}")
|
60 |
+
# 判断是否达到成仙的条件
|
61 |
+
if self.cultivation_rank >= IMMORTAL_RANK:
|
62 |
+
self.is_immortal = True
|
63 |
+
self.history.append(f"{self.real_age}岁,成仙了")
|
64 |
+
self.special_history.append(f"{self.real_age}岁,成仙了")
|
65 |
else:
|
66 |
self.history.append(f"{self.real_age}岁,突破失败,在{self.cultivation_level}级")
|
67 |
|
68 |
def marry(self, partner):
|
69 |
# 成仙者不再结婚
|
70 |
+
if self.is_immortal:
|
71 |
return
|
72 |
if not self.is_alive:
|
73 |
print("角色已经死亡,无法结婚。")
|
|
|
92 |
|
93 |
def give_birth(self):
|
94 |
# 成仙者不会生育
|
95 |
+
if self.is_immortal:
|
96 |
return
|
97 |
if not self.is_alive:
|
98 |
print("角色已经死亡,无法生育。")
|
|
|
122 |
|
123 |
def grow(self):
|
124 |
# 成仙者不会衰老
|
125 |
+
if self.is_immortal:
|
126 |
return
|
127 |
self.real_age += 1
|
128 |
|
|
|
148 |
attack_power = (self.cultivation_rank + 1) * 30 + 1 * (1 + self.cultivation_level)
|
149 |
defense_power = (self.cultivation_rank + 1) * 30 + 1 * (1 + self.cultivation_level)
|
150 |
attack_speed = (self.cultivation_rank + 1) * 30 + 1 * (1 + self.cultivation_level)
|
151 |
+
health_points = (self.cultivation_rank + 1) * 90 + 3 * (1 + self.cultivation_level)
|
152 |
|
153 |
# 根据修为层次计算加成比例,初始加成为10%
|
154 |
bonus = 0.1 * (self.cultivation_rank * 0 + 1)
|
|
|
167 |
defense_power *= 1 + bonus
|
168 |
|
169 |
return {
|
170 |
+
'attack_power': math.ceil(attack_power),
|
171 |
+
'defense_power': math.ceil(defense_power),
|
172 |
+
'attack_speed': math.ceil(attack_speed),
|
173 |
+
'health_points': math.ceil(health_points),
|
174 |
}
|
175 |
|
176 |
def before_battle(self):
|
177 |
self.combat_power = self.calculate_combat_power()
|
178 |
|
179 |
+
def attack(self, opponent, params={"P_CRIT": 0.2, "P_DODGE": 0.05, "P_BLOCK": 0.1, "P_COUNTER": 0.05}):
|
180 |
# 成仙者不会打架
|
181 |
+
if self.is_immortal:
|
182 |
return
|
183 |
if not self.is_alive:
|
184 |
print("角色已经死亡,无法攻击。")
|
|
|
188 |
if self.special_constitution[0] == 1:
|
189 |
self.buff = True
|
190 |
|
191 |
+
# 根据攻击速度计算攻击次数
|
192 |
+
attack_times = math.ceil(self.combat_power['attack_speed'] / 10)
|
193 |
+
# 根据攻击力计算伤害
|
194 |
+
damage = self.combat_power['attack_power'] * attack_times
|
195 |
+
# 根据防御力计算伤害减免
|
196 |
+
damage -= opponent.combat_power['defense_power']
|
197 |
+
# 伤害最小为1
|
198 |
+
damage = max(1, damage)
|
199 |
+
# 根据概率计算暴击
|
200 |
+
if random.random() < params['P_CRIT']:
|
201 |
+
damage *= 2
|
202 |
+
print("暴击!")
|
203 |
+
# 根据概率计算闪避
|
204 |
+
if random.random() < params['P_DODGE']:
|
205 |
+
print("闪避!")
|
206 |
+
return
|
207 |
+
# 根据概率计算格挡
|
208 |
+
if random.random() < params['P_BLOCK']:
|
209 |
+
print("格挡!")
|
210 |
+
damage = max(1, damage // 2)
|
211 |
+
# 根据概率计算反击
|
212 |
+
if random.random() < params['P_COUNTER']:
|
213 |
+
print("反击!")
|
214 |
+
damage = max(1, damage // 2)
|
215 |
+
opponent.combat_power['health_points'] -= damage
|
216 |
+
return
|
217 |
+
|
218 |
if damage > 0:
|
219 |
opponent.combat_power['health_points'] -= damage
|
220 |
|
CharacterStatistics.py
CHANGED
@@ -32,7 +32,7 @@ class CharacterStatistics:
|
|
32 |
ax.set_xlabel(attributes[i])
|
33 |
ax.set_ylabel('Frequency')
|
34 |
plt.tight_layout()
|
35 |
-
|
36 |
|
37 |
# real_age、apparent_age、cultivation_level、cultivation_rank、孩子数量 统计平均数
|
38 |
def calculate_average_attributes(self):
|
@@ -62,7 +62,7 @@ class CharacterStatistics:
|
|
62 |
axs[i].set_ylabel('Value')
|
63 |
|
64 |
plt.tight_layout()
|
65 |
-
|
66 |
|
67 |
def calculate_average_special_constitution(self):
|
68 |
special_constitution_mean = np.mean(self.characters_array[:, 9:13], axis=0)
|
@@ -81,12 +81,13 @@ class CharacterStatistics:
|
|
81 |
def plot_sum_spiritual_root_distribution(self):
|
82 |
spiritual_roots_sum = np.sum(self.characters_array[:, 13:18], axis=1)
|
83 |
|
|
|
84 |
plt.hist(spiritual_roots_sum, bins=5, range=(0,5))
|
85 |
plt.xlabel('Number of Spiritual Roots')
|
86 |
plt.ylabel('Number of Characters')
|
87 |
plt.title('Distribution of Spiritual Roots')
|
88 |
|
89 |
-
|
90 |
|
91 |
# 各灵根人口分布(x轴:5种灵根,y轴:人数)
|
92 |
def plot_spiritual_roots_distribution(self):
|
@@ -96,12 +97,13 @@ class CharacterStatistics:
|
|
96 |
means = np.mean(spiritual_roots, axis=0)
|
97 |
roots = ['Metal', 'Wood', 'Water', 'Fire', 'Earth']
|
98 |
|
|
|
99 |
plt.bar(roots, means)
|
100 |
plt.xlabel('Spiritual Roots')
|
101 |
plt.ylabel('Percentage of Characters')
|
102 |
plt.title('Distribution of Spiritual Roots in Population')
|
103 |
|
104 |
-
|
105 |
|
106 |
# 宗族人数分布图
|
107 |
def plot_clan_size_distribution(self):
|
@@ -110,10 +112,11 @@ class CharacterStatistics:
|
|
110 |
clan_size[c.clan] = clan_size.get(c.clan, 0) + 1
|
111 |
|
112 |
sizes = list(clan_size.values())
|
|
|
113 |
plt.hist(sizes, bins=20)
|
114 |
plt.xlabel('Clan Size')
|
115 |
plt.ylabel('Number of Clans')
|
116 |
-
|
117 |
|
118 |
def summarize(self):
|
119 |
print("===== Character Statistics Summary =====")
|
@@ -177,14 +180,27 @@ class CharacterStatistics:
|
|
177 |
md += f"{root_names[i]}: {v:.2%}\n\n"
|
178 |
|
179 |
# md += "### Plotting graphs...\n\n"
|
|
|
|
|
|
|
180 |
# md += "#### Attribute Distribution\n\n"
|
181 |
# md += "![Attribute Distribution](./attribute_distribution.png)\n\n"
|
|
|
|
|
|
|
182 |
# md += "#### Spiritual Roots Distribution\n\n"
|
183 |
# md += "![Spiritual Roots Distribution](./spiritual_roots_distribution.png)\n\n"
|
|
|
|
|
|
|
184 |
# md += "#### Sum Spiritual Roots Distribution\n\n"
|
185 |
# md += "![Sum Spiritual Roots Distribution](./sum_spiritual_roots_distribution.png)\n\n"
|
|
|
|
|
|
|
186 |
# md += "#### Clan Size Distribution\n\n"
|
187 |
-
# md += "![Clan Size Distribution](
|
|
|
188 |
md += "#### Top Cultivators\n\n"
|
189 |
md += self.print_top_cultivators_markdown()
|
190 |
md += "#### Attack Power Ranking\n\n"
|
@@ -194,7 +210,7 @@ class CharacterStatistics:
|
|
194 |
md += "#### Top Clans\n\n"
|
195 |
md += self.print_top_clans_markdown()
|
196 |
|
197 |
-
return md
|
198 |
|
199 |
def print_top_cultivators(self, top_n=10):
|
200 |
|
|
|
32 |
ax.set_xlabel(attributes[i])
|
33 |
ax.set_ylabel('Frequency')
|
34 |
plt.tight_layout()
|
35 |
+
return fig
|
36 |
|
37 |
# real_age、apparent_age、cultivation_level、cultivation_rank、孩子数量 统计平均数
|
38 |
def calculate_average_attributes(self):
|
|
|
62 |
axs[i].set_ylabel('Value')
|
63 |
|
64 |
plt.tight_layout()
|
65 |
+
return fig
|
66 |
|
67 |
def calculate_average_special_constitution(self):
|
68 |
special_constitution_mean = np.mean(self.characters_array[:, 9:13], axis=0)
|
|
|
81 |
def plot_sum_spiritual_root_distribution(self):
|
82 |
spiritual_roots_sum = np.sum(self.characters_array[:, 13:18], axis=1)
|
83 |
|
84 |
+
fig = plt.figure()
|
85 |
plt.hist(spiritual_roots_sum, bins=5, range=(0,5))
|
86 |
plt.xlabel('Number of Spiritual Roots')
|
87 |
plt.ylabel('Number of Characters')
|
88 |
plt.title('Distribution of Spiritual Roots')
|
89 |
|
90 |
+
return fig
|
91 |
|
92 |
# 各灵根人口分布(x轴:5种灵根,y轴:人数)
|
93 |
def plot_spiritual_roots_distribution(self):
|
|
|
97 |
means = np.mean(spiritual_roots, axis=0)
|
98 |
roots = ['Metal', 'Wood', 'Water', 'Fire', 'Earth']
|
99 |
|
100 |
+
fig = plt.figure()
|
101 |
plt.bar(roots, means)
|
102 |
plt.xlabel('Spiritual Roots')
|
103 |
plt.ylabel('Percentage of Characters')
|
104 |
plt.title('Distribution of Spiritual Roots in Population')
|
105 |
|
106 |
+
return fig
|
107 |
|
108 |
# 宗族人数分布图
|
109 |
def plot_clan_size_distribution(self):
|
|
|
112 |
clan_size[c.clan] = clan_size.get(c.clan, 0) + 1
|
113 |
|
114 |
sizes = list(clan_size.values())
|
115 |
+
fig = plt.figure()
|
116 |
plt.hist(sizes, bins=20)
|
117 |
plt.xlabel('Clan Size')
|
118 |
plt.ylabel('Number of Clans')
|
119 |
+
return fig
|
120 |
|
121 |
def summarize(self):
|
122 |
print("===== Character Statistics Summary =====")
|
|
|
180 |
md += f"{root_names[i]}: {v:.2%}\n\n"
|
181 |
|
182 |
# md += "### Plotting graphs...\n\n"
|
183 |
+
# 画图并保存
|
184 |
+
fig1 = self.plot_attribute_distribution()
|
185 |
+
# fig1.savefig('./attribute_distribution.png')
|
186 |
# md += "#### Attribute Distribution\n\n"
|
187 |
# md += "![Attribute Distribution](./attribute_distribution.png)\n\n"
|
188 |
+
|
189 |
+
fig2 = self.plot_spiritual_roots_distribution()
|
190 |
+
# fig2.savefig('./spiritual_roots_distribution.png')
|
191 |
# md += "#### Spiritual Roots Distribution\n\n"
|
192 |
# md += "![Spiritual Roots Distribution](./spiritual_roots_distribution.png)\n\n"
|
193 |
+
|
194 |
+
fig3 = self.plot_sum_spiritual_root_distribution()
|
195 |
+
# fig3.savefig('./sum_spiritual_roots_distribution.png')
|
196 |
# md += "#### Sum Spiritual Roots Distribution\n\n"
|
197 |
# md += "![Sum Spiritual Roots Distribution](./sum_spiritual_roots_distribution.png)\n\n"
|
198 |
+
|
199 |
+
fig4 = self.plot_clan_size_distribution()
|
200 |
+
# fig4.savefig('clan_size_distribution.png')
|
201 |
# md += "#### Clan Size Distribution\n\n"
|
202 |
+
# md += "![Clan Size Distribution](clan_size_distribution.png)\n\n"
|
203 |
+
|
204 |
md += "#### Top Cultivators\n\n"
|
205 |
md += self.print_top_cultivators_markdown()
|
206 |
md += "#### Attack Power Ranking\n\n"
|
|
|
210 |
md += "#### Top Clans\n\n"
|
211 |
md += self.print_top_clans_markdown()
|
212 |
|
213 |
+
return md, fig1, fig2, fig3, fig4
|
214 |
|
215 |
def print_top_cultivators(self, top_n=10):
|
216 |
|
WorldSimulation.py
CHANGED
@@ -30,6 +30,10 @@ class WorldSimulation:
|
|
30 |
self.dead_characters = []
|
31 |
self.nth_round = 0
|
32 |
self.world_log = []
|
|
|
|
|
|
|
|
|
33 |
|
34 |
def log(self, msg):
|
35 |
self.world_log.append(msg)
|
@@ -40,7 +44,9 @@ class WorldSimulation:
|
|
40 |
|
41 |
# 每人可用资源
|
42 |
per_capita_resources = self.resources / len(self.characters)
|
43 |
-
|
|
|
|
|
44 |
self.birth_plugin.set_birth_rate(LOW_BIRTH_RATE)
|
45 |
self.battle_plugin.set_battle_rate(HIGH_BATTLE_RATE)
|
46 |
else:
|
@@ -50,7 +56,7 @@ class WorldSimulation:
|
|
50 |
self.resource_depletion_plugin.execute(per_capita_resources, self.characters, self.character_die)
|
51 |
|
52 |
self.battle_plugin.execute(self.characters, self.character_die)
|
53 |
-
self.birth_plugin.execute(self.characters)
|
54 |
self.marriage_plugin.execute(self.characters)
|
55 |
self.cultivation_plugin.execute(self.characters, self.world_spiritual_energy, self.init_world_spiritual_energy, self.consume_spiritual_energy)
|
56 |
|
@@ -58,22 +64,30 @@ class WorldSimulation:
|
|
58 |
md = ""
|
59 |
for _ in range(num_rounds):
|
60 |
self.nth_round += 1
|
|
|
|
|
61 |
print(f"第{self.nth_round}轮模拟:")
|
62 |
md += f"第{self.nth_round}轮模拟:\n"
|
63 |
self.simulate_round()
|
|
|
|
|
64 |
print(f"当前世界灵气总量:{self.world_spiritual_energy}")
|
65 |
print(f"当前存活角色数量:{len(self.characters)}")
|
66 |
print("")
|
|
|
|
|
67 |
md += f"当前世界灵气总量:{self.world_spiritual_energy}\n"
|
68 |
md += f"当前存活角色数量:{len(self.characters)}\n\n"
|
|
|
|
|
69 |
if len(self.characters) == 0:
|
70 |
print("所有角色死亡,模拟结束")
|
71 |
md += "所有角色死亡,模拟结束\n"
|
72 |
break
|
73 |
return md
|
74 |
|
75 |
-
def add_custom_character(self, name, gender, special_constitution, spiritual_roots):
|
76 |
-
character = Character(name, gender, special_constitution, spiritual_roots)
|
77 |
self.characters.append(character)
|
78 |
|
79 |
def create_population(self, initial_population, special_constitution_ratio, spiritual_roots_ratio):
|
@@ -85,7 +99,7 @@ class WorldSimulation:
|
|
85 |
|
86 |
def character_die(self, character):
|
87 |
# 成仙者不会死亡
|
88 |
-
if character.
|
89 |
return
|
90 |
character.die()
|
91 |
self.world_spiritual_energy += character.consume_spiritual_energy # 灵气回归
|
@@ -94,6 +108,11 @@ class WorldSimulation:
|
|
94 |
character.partner.partner = None # 解除配偶关系
|
95 |
self.characters.remove(character)
|
96 |
self.dead_characters.append(character)
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
def consume_spiritual_energy(self, amount):
|
99 |
self.world_spiritual_energy -= amount
|
|
|
30 |
self.dead_characters = []
|
31 |
self.nth_round = 0
|
32 |
self.world_log = []
|
33 |
+
self.history_population = []
|
34 |
+
self.history_spi_energy = []
|
35 |
+
self.history_new_birth_count = []
|
36 |
+
self.history_dead_count = []
|
37 |
|
38 |
def log(self, msg):
|
39 |
self.world_log.append(msg)
|
|
|
44 |
|
45 |
# 每人可用资源
|
46 |
per_capita_resources = self.resources / len(self.characters)
|
47 |
+
|
48 |
+
# 资源较少时会降低生育率
|
49 |
+
if per_capita_resources < self.resources_threshold * 1.8:
|
50 |
self.birth_plugin.set_birth_rate(LOW_BIRTH_RATE)
|
51 |
self.battle_plugin.set_battle_rate(HIGH_BATTLE_RATE)
|
52 |
else:
|
|
|
56 |
self.resource_depletion_plugin.execute(per_capita_resources, self.characters, self.character_die)
|
57 |
|
58 |
self.battle_plugin.execute(self.characters, self.character_die)
|
59 |
+
self.birth_plugin.execute(self.characters, self.register_character)
|
60 |
self.marriage_plugin.execute(self.characters)
|
61 |
self.cultivation_plugin.execute(self.characters, self.world_spiritual_energy, self.init_world_spiritual_energy, self.consume_spiritual_energy)
|
62 |
|
|
|
64 |
md = ""
|
65 |
for _ in range(num_rounds):
|
66 |
self.nth_round += 1
|
67 |
+
self.history_dead_count.append(0)
|
68 |
+
self.history_new_birth_count.append(0)
|
69 |
print(f"第{self.nth_round}轮模拟:")
|
70 |
md += f"第{self.nth_round}轮模拟:\n"
|
71 |
self.simulate_round()
|
72 |
+
print(f"共死亡{self.history_dead_count[-1]}人!")
|
73 |
+
print(f"共{self.history_new_birth_count[-1]}对夫妻生了孩子!")
|
74 |
print(f"当前世界灵气总量:{self.world_spiritual_energy}")
|
75 |
print(f"当前存活角色数量:{len(self.characters)}")
|
76 |
print("")
|
77 |
+
md += f"共死亡{self.history_dead_count[-1]}人!\n"
|
78 |
+
md += f"共{self.history_new_birth_count[-1]}对夫妻生了孩子!\n"
|
79 |
md += f"当前世界灵气总量:{self.world_spiritual_energy}\n"
|
80 |
md += f"当前存活角色数量:{len(self.characters)}\n\n"
|
81 |
+
self.history_spi_energy.append(self.world_spiritual_energy)
|
82 |
+
self.history_population.append(len(self.characters))
|
83 |
if len(self.characters) == 0:
|
84 |
print("所有角色死亡,模拟结束")
|
85 |
md += "所有角色死亡,模拟结束\n"
|
86 |
break
|
87 |
return md
|
88 |
|
89 |
+
def add_custom_character(self, name, gender, special_constitution, spiritual_roots, clan=None):
|
90 |
+
character = Character(name, gender, special_constitution, spiritual_roots, clan)
|
91 |
self.characters.append(character)
|
92 |
|
93 |
def create_population(self, initial_population, special_constitution_ratio, spiritual_roots_ratio):
|
|
|
99 |
|
100 |
def character_die(self, character):
|
101 |
# 成仙者不会死亡
|
102 |
+
if character.is_immortal:
|
103 |
return
|
104 |
character.die()
|
105 |
self.world_spiritual_energy += character.consume_spiritual_energy # 灵气回归
|
|
|
108 |
character.partner.partner = None # 解除配偶关系
|
109 |
self.characters.remove(character)
|
110 |
self.dead_characters.append(character)
|
111 |
+
self.history_dead_count[-1] += 1
|
112 |
+
|
113 |
+
def register_character(self, character):
|
114 |
+
self.characters.append(character)
|
115 |
+
self.history_new_birth_count[-1] += 1
|
116 |
|
117 |
def consume_spiritual_energy(self, amount):
|
118 |
self.world_spiritual_energy -= amount
|
app.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
import gradio as gr
|
|
|
|
|
2 |
from CharacterStatistics import CharacterStatistics
|
3 |
from WorldSimulation import WorldSimulation
|
4 |
|
@@ -6,17 +8,16 @@ from WorldSimulation import WorldSimulation
|
|
6 |
simulation = None
|
7 |
num_rounds = 0
|
8 |
|
9 |
-
def initialize_world(world_spiritual_energy=1000000):
|
10 |
global simulation
|
11 |
-
simulation = WorldSimulation(world_spiritual_energy=world_spiritual_energy)
|
12 |
|
13 |
return f"世界初始化成功:\n\n世界灵气能量:{world_spiritual_energy}"
|
14 |
|
15 |
-
|
16 |
-
def add_custom_character(name, gender, special_constitution, spiritual_roots):
|
17 |
special_constitution = [1 if i in special_constitution else 0 for i in range(4)]
|
18 |
spiritual_roots = [1 if i in spiritual_roots else 0 for i in range(5)]
|
19 |
-
simulation.add_custom_character(name, gender, special_constitution, spiritual_roots)
|
20 |
return str(simulation.characters[-1])
|
21 |
|
22 |
def start_simulation(c1, c2, c3, c4, r1, r2, r3, r4, r5, initial_population=3000):
|
@@ -33,6 +34,13 @@ def run_simulation(rounds):
|
|
33 |
return "请先初始化世界"
|
34 |
return simulation.run_simulation(num_rounds)
|
35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
def get_character_info(name):
|
37 |
characters = simulation.find_characters_by_name(name)
|
38 |
if len(characters) == 0:
|
@@ -50,6 +58,11 @@ def get_world_stats():
|
|
50 |
stats = CharacterStatistics(simulation.characters)
|
51 |
return stats.summarize_markdown()
|
52 |
|
|
|
|
|
|
|
|
|
|
|
53 |
with gr.Blocks() as demo:
|
54 |
# 初始化世界
|
55 |
gr.Markdown("## 初始化世界")
|
@@ -64,13 +77,14 @@ with gr.Blocks() as demo:
|
|
64 |
gr.Markdown("每次添加一个,添加完可以继续添加。")
|
65 |
name_input = gr.Textbox(label="姓名")
|
66 |
gender_input = gr.Radio(["男", "女"], label="性别")
|
|
|
67 |
special_constitution_input = gr.CheckboxGroup(["战斗体质", "合欢体质", "灵龟体质", "蜉蝣体质"], label="特殊体质", type="index")
|
68 |
spiritual_roots_input = gr.CheckboxGroup(["金", "木", "水", "火", "土"], label="灵根", type="index")
|
69 |
add_character_button = gr.Button("添加角色")
|
70 |
new_character_info_output = gr.Textbox(label="新角色信息")
|
71 |
add_character_button.click(fn=add_custom_character, inputs=[name_input,
|
72 |
gender_input,
|
73 |
-
special_constitution_input, spiritual_roots_input], outputs=new_character_info_output)
|
74 |
|
75 |
# 生成初始人口
|
76 |
gr.Markdown("## 生成初始人口")
|
@@ -105,23 +119,66 @@ with gr.Blocks() as demo:
|
|
105 |
run_simulation_button.click(fn=run_simulation, inputs=[rounds_input], outputs=[simulation_result_output])
|
106 |
|
107 |
# 查看统计信息
|
108 |
-
|
109 |
-
|
110 |
with gr.Box():
|
111 |
world_stats_output = gr.Markdown("")
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
# 查看角色信息
|
115 |
-
with gr.
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
119 |
get_character_info_button.click(fn=get_character_info, inputs=[character_info_output], outputs=[character_info_output_display])
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
get_dead_character_info_button.click(fn=get_dead_character_info, inputs=[dead_character_info_output], outputs=[dead_character_info_output_display])
|
126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
demo.queue().launch(debug=True)
|
|
|
1 |
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
from CharacterStatistics import CharacterStatistics
|
5 |
from WorldSimulation import WorldSimulation
|
6 |
|
|
|
8 |
simulation = None
|
9 |
num_rounds = 0
|
10 |
|
11 |
+
def initialize_world(world_spiritual_energy=1000000, resources = 100000, resources_threshold = 10):
|
12 |
global simulation
|
13 |
+
simulation = WorldSimulation(world_spiritual_energy=world_spiritual_energy, resources=resources, resources_threshold=resources_threshold)
|
14 |
|
15 |
return f"世界初始化成功:\n\n世界灵气能量:{world_spiritual_energy}"
|
16 |
|
17 |
+
def add_custom_character(name, gender, special_constitution, spiritual_roots, clan=None):
|
|
|
18 |
special_constitution = [1 if i in special_constitution else 0 for i in range(4)]
|
19 |
spiritual_roots = [1 if i in spiritual_roots else 0 for i in range(5)]
|
20 |
+
simulation.add_custom_character(name, gender, special_constitution, spiritual_roots, clan)
|
21 |
return str(simulation.characters[-1])
|
22 |
|
23 |
def start_simulation(c1, c2, c3, c4, r1, r2, r3, r4, r5, initial_population=3000):
|
|
|
34 |
return "请先初始化世界"
|
35 |
return simulation.run_simulation(num_rounds)
|
36 |
|
37 |
+
def find_character_by_clan(name):
|
38 |
+
characters = simulation.find_characters_by_clan(name)
|
39 |
+
if len(characters) == 0:
|
40 |
+
return f"没有找到名字为{name}的宗族"
|
41 |
+
else:
|
42 |
+
return "\n\n".join([str(c) for c in characters])
|
43 |
+
|
44 |
def get_character_info(name):
|
45 |
characters = simulation.find_characters_by_name(name)
|
46 |
if len(characters) == 0:
|
|
|
58 |
stats = CharacterStatistics(simulation.characters)
|
59 |
return stats.summarize_markdown()
|
60 |
|
61 |
+
def plot():
|
62 |
+
y = simulation.history_population
|
63 |
+
df = pd.DataFrame({"x": np.arange(len(y)), "y": y, "spi_count": simulation.history_spi_energy})
|
64 |
+
return df
|
65 |
+
|
66 |
with gr.Blocks() as demo:
|
67 |
# 初始化世界
|
68 |
gr.Markdown("## 初始化世界")
|
|
|
77 |
gr.Markdown("每次添加一个,添加完可以继续添加。")
|
78 |
name_input = gr.Textbox(label="姓名")
|
79 |
gender_input = gr.Radio(["男", "女"], label="性别")
|
80 |
+
clan_input = gr.Textbox(label="宗族")
|
81 |
special_constitution_input = gr.CheckboxGroup(["战斗体质", "合欢体质", "灵龟体质", "蜉蝣体质"], label="特殊体质", type="index")
|
82 |
spiritual_roots_input = gr.CheckboxGroup(["金", "木", "水", "火", "土"], label="灵根", type="index")
|
83 |
add_character_button = gr.Button("添加角色")
|
84 |
new_character_info_output = gr.Textbox(label="新角色信息")
|
85 |
add_character_button.click(fn=add_custom_character, inputs=[name_input,
|
86 |
gender_input,
|
87 |
+
special_constitution_input, spiritual_roots_input, clan_input], outputs=new_character_info_output)
|
88 |
|
89 |
# 生成初始人口
|
90 |
gr.Markdown("## 生成初始人口")
|
|
|
119 |
run_simulation_button.click(fn=run_simulation, inputs=[rounds_input], outputs=[simulation_result_output])
|
120 |
|
121 |
# 查看统计信息
|
122 |
+
get_stats_button = gr.Button("查看统计信息")
|
123 |
+
with gr.Accordion("详细文字统计信息", open=False):
|
124 |
with gr.Box():
|
125 |
world_stats_output = gr.Markdown("")
|
126 |
+
plot1 = gr.Plot()
|
127 |
+
with gr.Row():
|
128 |
+
plot2 = gr.Plot()
|
129 |
+
plot3 = gr.Plot()
|
130 |
+
plot4 = gr.Plot()
|
131 |
+
get_stats_button.click(fn=get_world_stats, inputs=[], outputs=[world_stats_output, plot1, plot2, plot3, plot4])
|
132 |
|
133 |
# 查看角色信息
|
134 |
+
with gr.Accordion("查看角色信息", open=False):
|
135 |
+
with gr.Row():
|
136 |
+
character_info_output = gr.Textbox(label="角色信息", info="输入角色名字")
|
137 |
+
get_character_info_button = gr.Button("查看角色信息")
|
138 |
+
character_info_output_display = gr.Textbox(label="角色信息")
|
139 |
get_character_info_button.click(fn=get_character_info, inputs=[character_info_output], outputs=[character_info_output_display])
|
140 |
|
141 |
+
# 查看宗族信息
|
142 |
+
with gr.Accordion("查看宗族信息", open=False):
|
143 |
+
with gr.Row():
|
144 |
+
clan_name_input = gr.Textbox(label="宗族名字", info="输入宗族名字")
|
145 |
+
get_clan_characters_button = gr.Button("查看宗族角色")
|
146 |
+
characters_output_display = gr.Textbox(label="角色信息")
|
147 |
+
get_clan_characters_button.click(fn=find_character_by_clan, inputs=[clan_name_input], outputs=[characters_output_display])
|
148 |
+
|
149 |
+
with gr.Accordion("查看死亡角色", open=False):
|
150 |
+
with gr.Row():
|
151 |
+
dead_character_info_output = gr.Number(label="死亡角色信息", info="输入死亡角色序号(按死亡顺序排列)", precision=0)
|
152 |
+
get_dead_character_info_button = gr.Button("查看死亡角色信息")
|
153 |
+
dead_character_info_output_display = gr.Textbox(label="死亡角色信息")
|
154 |
get_dead_character_info_button.click(fn=get_dead_character_info, inputs=[dead_character_info_output], outputs=[dead_character_info_output_display])
|
155 |
|
156 |
+
# 可视化
|
157 |
+
btn = gr.Button(value="查看人口图表")
|
158 |
+
plot_output = gr.LinePlot(
|
159 |
+
x="x",
|
160 |
+
y="y",
|
161 |
+
title="人口随时间变化图",
|
162 |
+
x_title="年份",
|
163 |
+
y_title="人口数量",
|
164 |
+
overlay_point=True,
|
165 |
+
tooltip=["x", "y"],
|
166 |
+
width=350,
|
167 |
+
height=300,
|
168 |
+
)
|
169 |
+
btn2 = gr.Button(value="查看灵气图表")
|
170 |
+
plot_output2 = gr.LinePlot(
|
171 |
+
x="x",
|
172 |
+
y="spi_count",
|
173 |
+
title="灵气随时间变化图",
|
174 |
+
x_title="年份",
|
175 |
+
y_title="灵气数量",
|
176 |
+
overlay_point=True,
|
177 |
+
tooltip=["x", "spi_count"],
|
178 |
+
width=350,
|
179 |
+
height=300,
|
180 |
+
)
|
181 |
+
btn.click(plot, outputs=plot_output)
|
182 |
+
btn2.click(plot, outputs=plot_output2)
|
183 |
+
|
184 |
demo.queue().launch(debug=True)
|
attribute_distribution.png
ADDED
clan_size_distribution.png
ADDED
config.py
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
P_DIE_WHEN_LOSE = 0.
|
2 |
MAX_BATTLE_ROUND = 100 # 战斗最大回合数
|
3 |
MARRIAGE_RATE = 0.3 # 每名适龄青年每次模拟找对象的概率
|
4 |
|
5 |
# 当每人资源大于阈值时,生育率正常,战斗率正常
|
6 |
-
NORMAL_BIRTH_RATE = 0.
|
7 |
NORMAL_BATTLE_RATE = 0.3
|
8 |
|
9 |
# 当每人资源低于阈值时,生育率线性下降,战斗率线性上升
|
10 |
LOW_BIRTH_RATE = 0.05
|
11 |
-
HIGH_BATTLE_RATE = 0.
|
12 |
|
13 |
# 每10轮有5%概率发生灾难
|
14 |
DISASTER_PROB = 0.05
|
|
|
1 |
+
P_DIE_WHEN_LOSE = 0.7 # 战败死亡的概率
|
2 |
MAX_BATTLE_ROUND = 100 # 战斗最大回合数
|
3 |
MARRIAGE_RATE = 0.3 # 每名适龄青年每次模拟找对象的概率
|
4 |
|
5 |
# 当每人资源大于阈值时,生育率正常,战斗率正常
|
6 |
+
NORMAL_BIRTH_RATE = 0.15
|
7 |
NORMAL_BATTLE_RATE = 0.3
|
8 |
|
9 |
# 当每人资源低于阈值时,生育率线性下降,战斗率线性上升
|
10 |
LOW_BIRTH_RATE = 0.05
|
11 |
+
HIGH_BATTLE_RATE = 0.9
|
12 |
|
13 |
# 每10轮有5%概率发生灾难
|
14 |
DISASTER_PROB = 0.05
|
plugins/BattlePlugin.py
CHANGED
@@ -36,7 +36,8 @@ class BattlePlugin:
|
|
36 |
return (opponent, character)
|
37 |
|
38 |
def perform_battles(self, characters, character_die):
|
39 |
-
|
|
|
40 |
for _ in range(int(len(eligible_characters) * self.probability)):
|
41 |
character = random.choice(eligible_characters)
|
42 |
opponent = random.choice(eligible_characters)
|
|
|
36 |
return (opponent, character)
|
37 |
|
38 |
def perform_battles(self, characters, character_die):
|
39 |
+
# 按照年龄筛选出参战人员,且成仙者不参与战斗
|
40 |
+
eligible_characters = [character for character in characters if character.apparent_age > self.min_battle_age and character.check_is_alive() and not character.is_immortal]
|
41 |
for _ in range(int(len(eligible_characters) * self.probability)):
|
42 |
character = random.choice(eligible_characters)
|
43 |
opponent = random.choice(eligible_characters)
|
plugins/BirthPlugin.py
CHANGED
@@ -6,17 +6,13 @@ class BirthPlugin:
|
|
6 |
self.birth_age = min_birth_age
|
7 |
self.birth_age_range = birth_age_range
|
8 |
|
9 |
-
def perform_births(self, characters):
|
10 |
-
new_birth_count = 0
|
11 |
for character in characters:
|
12 |
if character.partner and character.apparent_age > self.birth_age and character.apparent_age < self.birth_age + self.birth_age_range and character.partner.apparent_age > self.birth_age and character.partner.apparent_age < self.birth_age + self.birth_age_range:
|
13 |
-
if random.random() < self.birth_rate
|
14 |
child = character.give_birth()
|
15 |
if child:
|
16 |
-
|
17 |
-
new_birth_count += 1
|
18 |
-
|
19 |
-
print(f"共{new_birth_count}对夫妻生了孩子!")
|
20 |
|
21 |
def set_birth_rate(self, birth_rate):
|
22 |
self.birth_rate = birth_rate
|
|
|
6 |
self.birth_age = min_birth_age
|
7 |
self.birth_age_range = birth_age_range
|
8 |
|
9 |
+
def perform_births(self, characters, register_character):
|
|
|
10 |
for character in characters:
|
11 |
if character.partner and character.apparent_age > self.birth_age and character.apparent_age < self.birth_age + self.birth_age_range and character.partner.apparent_age > self.birth_age and character.partner.apparent_age < self.birth_age + self.birth_age_range:
|
12 |
+
if random.random() < self.birth_rate:
|
13 |
child = character.give_birth()
|
14 |
if child:
|
15 |
+
register_character(child)
|
|
|
|
|
|
|
16 |
|
17 |
def set_birth_rate(self, birth_rate):
|
18 |
self.birth_rate = birth_rate
|
plugins/CultivationPlugin.py
CHANGED
@@ -14,6 +14,11 @@ class CultivationPlugin:
|
|
14 |
elif character.special_constitution[3] == 1: # 蜉蝣体质
|
15 |
cultivation_speed *= 2
|
16 |
|
|
|
|
|
|
|
|
|
|
|
17 |
if world_spiritual_energy > 0:
|
18 |
cultivation_speed *= world_spiritual_energy / init_world_spiritual_energy
|
19 |
success_rate = 1 - 0.2 * random.random()
|
|
|
14 |
elif character.special_constitution[3] == 1: # 蜉蝣体质
|
15 |
cultivation_speed *= 2
|
16 |
|
17 |
+
# 消耗buff
|
18 |
+
if character.buff:
|
19 |
+
cultivation_speed *= 1.5
|
20 |
+
character.buff = False
|
21 |
+
|
22 |
if world_spiritual_energy > 0:
|
23 |
cultivation_speed *= world_spiritual_energy / init_world_spiritual_energy
|
24 |
success_rate = 1 - 0.2 * random.random()
|
plugins/MarriagePlugin.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1 |
import random
|
2 |
class MarriagePlugin:
|
3 |
-
def __init__(self, marriage_rate):
|
4 |
self.marriage_rate = marriage_rate
|
|
|
5 |
|
6 |
-
def perform_marriages(self, characters):
|
7 |
# 随机一些表观年龄>20的角色结婚
|
8 |
-
eligible_characters = [character for character in characters if character.apparent_age >
|
9 |
new_couple_count = 0
|
10 |
for _ in range(int(len(eligible_characters) * self.marriage_rate)):
|
11 |
character = random.choice(eligible_characters)
|
@@ -14,6 +15,8 @@ class MarriagePlugin:
|
|
14 |
character.marry(partner)
|
15 |
new_couple_count += 1
|
16 |
print(f"{new_couple_count}对新人结婚了")
|
|
|
|
|
17 |
|
18 |
def set_marriage_rate(self, marriage_rate):
|
19 |
self.marriage_rate = marriage_rate
|
|
|
1 |
import random
|
2 |
class MarriagePlugin:
|
3 |
+
def __init__(self, marriage_rate, min_marriage_age=20):
|
4 |
self.marriage_rate = marriage_rate
|
5 |
+
self.min_marriage_age = min_marriage_age # 最小结婚年龄
|
6 |
|
7 |
+
def perform_marriages(self, characters, callback=None):
|
8 |
# 随机一些表观年龄>20的角色结婚
|
9 |
+
eligible_characters = [character for character in characters if character.apparent_age > self.min_marriage_age and character.partner is None and character.check_is_alive() and not character.is_immortal]
|
10 |
new_couple_count = 0
|
11 |
for _ in range(int(len(eligible_characters) * self.marriage_rate)):
|
12 |
character = random.choice(eligible_characters)
|
|
|
15 |
character.marry(partner)
|
16 |
new_couple_count += 1
|
17 |
print(f"{new_couple_count}对新人结婚了")
|
18 |
+
if callback is not None:
|
19 |
+
callback(new_couple_count)
|
20 |
|
21 |
def set_marriage_rate(self, marriage_rate):
|
22 |
self.marriage_rate = marriage_rate
|
plugins/ResourceDepletionPlugin.py
CHANGED
@@ -30,6 +30,8 @@ class ResourceDepletionPlugin:
|
|
30 |
character_die_callback(c)
|
31 |
|
32 |
def execute(self, resources, characters, character_die_callback):
|
33 |
-
#
|
34 |
if resources < self.depletion_threshold:
|
35 |
-
|
|
|
|
|
|
30 |
character_die_callback(c)
|
31 |
|
32 |
def execute(self, resources, characters, character_die_callback):
|
33 |
+
# 检查资源是否耗尽,如果耗尽则有概率触发灾难
|
34 |
if resources < self.depletion_threshold:
|
35 |
+
probability = random.random()
|
36 |
+
if probability < 0.3:
|
37 |
+
self.trigger_disaster(characters, character_die_callback)
|
spiritual_roots_distribution.png
ADDED
sum_spiritual_roots_distribution.png
ADDED