Based on the one-page RPG "Battle of the Brontës", by Oliver at Henry Sotheran Ltd. https://twitter.com/sotherans/status/1510361300945321994
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

brontes.py 8.2KB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import argparse
  2. from random import randint, shuffle
  3. class Sibling:
  4. def __init__(self, brother_count=None, state=None, game=None):
  5. self.tragedy = 0
  6. self.masterpiece = 0
  7. self.brother = 0
  8. self.brother_count = 0
  9. self.brother_max = int(brother_count) if brother_count else 3
  10. self.brother_ok = True
  11. self.alive = True
  12. self.rounds = 0
  13. self.success = False
  14. self.state = state
  15. self.game = game
  16. def round_check(self):
  17. if self.tragedy >= 10:
  18. self.alive = False
  19. return False
  20. self.rounds += 1
  21. if self.tragedy < 0:
  22. self.tragedy = 0
  23. if self.masterpiece < 0:
  24. self.masterpiece = 0
  25. if self.brother < 0:
  26. self.brother = 0
  27. if self.masterpiece >= 5:
  28. success = self.publication_attempt()
  29. if success:
  30. self.success = True
  31. return True
  32. else:
  33. self.masterpiece = 0
  34. if self.brother_ok and self.brother >= 5:
  35. if self.game.common_brother:
  36. self.game.brother_interferes()
  37. else:
  38. self.brother_count += 1
  39. if self.brother_count >= self.brother_max:
  40. self.brother_ok = False
  41. self.brother = 0
  42. self.masterpiece = 0
  43. def publication_attempt(self):
  44. if randint(1,6) == 6:
  45. return True
  46. self.state.failed_manuscripts += 1
  47. return False
  48. def __str__(self):
  49. out_str = f"This sibling lasted {self.rounds} rounds. Their final scores were: Tragedy: {self.tragedy}, Masterpiece: {self.masterpiece}, Brother: {self.brother} (with {self.brother_count} resets)."
  50. if self.success:
  51. out_str += " They were successful in publishing their manuscript!"
  52. return out_str
  53. class Game:
  54. def __init__(self, siblings=None, common_brother=None, brother_count=None, verbose=None, state=None):
  55. self.num_siblings = int(siblings) if siblings is not None else 3
  56. self.common_brother = bool(common_brother) if common_brother is not None else False
  57. self.brother_count = int(brother_count) if brother_count is not None else self.num_siblings
  58. self.verbose = bool(verbose) if verbose is not None else True
  59. self.state = state
  60. self.siblings = []
  61. for _ in range(self.num_siblings):
  62. if self.common_brother:
  63. self.siblings.append(Sibling(state=self.state, game=self))
  64. else:
  65. self.siblings.append(Sibling(brother_count=self.brother_count, state=self.state, game=self))
  66. if self.common_brother:
  67. self.brother_ok = True
  68. self.per_sibling_brother = []
  69. for _ in range(self.num_siblings):
  70. self.per_sibling_brother.append(True)
  71. self.success = False
  72. def is_brother_ok(self, sibling):
  73. if self.common_brother and self.brother_ok:
  74. return True
  75. elif sibling.brother_ok and not self.common_brother:
  76. return True
  77. return False
  78. def bronte_event(self, sibling):
  79. event_type = randint(1,6)
  80. if event_type <= 2:
  81. self.quiet_day(sibling)
  82. elif event_type <= 5:
  83. self.walk_moors(sibling)
  84. else:
  85. # This is remaining instead of being relegated to git commit history
  86. # because it's being actively investigated as a way to manipulate
  87. # success rates.
  88. # inc_chance = randint(1,6)
  89. # if inc_chance == 6:
  90. # sibling.masterpiece += 1
  91. sibling.masterpiece += 1
  92. def quiet_day(self, sibling):
  93. day_type = randint(1,6)
  94. if day_type == 1:
  95. sibling.tragedy += 1
  96. if self.is_brother_ok(sibling):
  97. sibling.brother += 1
  98. elif day_type == 2:
  99. sibling.masterpiece -= 1
  100. elif day_type == 3:
  101. if self.is_brother_ok(sibling):
  102. sibling.brother += 3
  103. elif day_type == 4:
  104. sibling.masterpiece -= 3
  105. elif day_type == 5:
  106. sibling.masterpiece += 2
  107. elif day_type == 6:
  108. sibling.tragedy += 1
  109. if self.is_brother_ok(sibling):
  110. sibling.brother += 2
  111. def walk_moors(self, sibling):
  112. walk_type = randint(1,6)
  113. if walk_type == 1:
  114. sibling.tragedy += 1
  115. elif walk_type == 2:
  116. if self.is_brother_ok(sibling):
  117. sibling.brother += 2
  118. elif walk_type == 3:
  119. sibling.tragedy += 2
  120. elif walk_type == 4:
  121. sibling.masterpiece += 1
  122. elif walk_type == 5:
  123. if self.is_brother_ok(sibling):
  124. sibling.brother += 2
  125. elif walk_type == 6:
  126. sibling.tragedy += 1
  127. def turn(self, sibling):
  128. self.bronte_event(sibling)
  129. check = sibling.round_check()
  130. if check:
  131. return True
  132. return False
  133. def round(self):
  134. for sibling in self.siblings:
  135. if sibling.alive:
  136. break
  137. else:
  138. self.vprint("Everyone has died.")
  139. for sibling in self.siblings:
  140. self.vprint(sibling)
  141. self.success = False
  142. return False
  143. temp_sibs = self.siblings[:]
  144. shuffle(temp_sibs)
  145. for current_sibling in temp_sibs:
  146. cur_sib_num = self.siblings.index(current_sibling)
  147. if current_sibling.alive:
  148. success = self.turn(current_sibling)
  149. if success:
  150. self.vprint(f"Sibling #{cur_sib_num+1} has published their masterpiece!")
  151. for sibling in self.siblings:
  152. self.vprint(sibling)
  153. self.success = True
  154. return False
  155. return True
  156. def brother_interferes(self):
  157. self.brother_count -= 1
  158. if self.brother_count <= 0:
  159. self.brother_ok = False
  160. def vprint(self, text):
  161. if self.verbose:
  162. print(text)
  163. def play_game(self):
  164. continue_playing = True
  165. while continue_playing:
  166. continue_playing = self.round()
  167. return self.success
  168. class GameState:
  169. def __init__(self, rounds=None, siblings=None, common_brother=None, brother_count=None, verbose=None):
  170. self.rounds = int(rounds) if rounds else 10000
  171. self.successes = 0
  172. self.failures = 0
  173. self.failed_manuscripts = 0
  174. for _ in range(self.rounds):
  175. game = Game(siblings=siblings, common_brother=common_brother, brother_count=brother_count, verbose=verbose, state=self)
  176. success = game.play_game()
  177. if success:
  178. self.successes += 1
  179. else:
  180. self.failures += 1
  181. out_str = f"Across {self.rounds} families of {siblings} sisters and "
  182. if common_brother:
  183. out_str += "one brother"
  184. else:
  185. out_str += "as many brothers"
  186. out_str += f", there were {self.successes} successfully-published masterpieces, and {self.failures} tragic deaths for everybody involved."
  187. out_str += f" In addition, there were {self.failed_manuscripts} manuscripts submitted that were refused by all publishers."
  188. print(out_str)
  189. if __name__ == "__main__":
  190. parser = argparse.ArgumentParser()
  191. parser.add_argument("-v", "--verbose", help="Each round prints its results", action="store_true")
  192. parser.add_argument("-r", "--rounds", help="Number of game rounds to simulate", type=int, nargs="?", default=10000, const=10000)
  193. parser.add_argument("-s", "--siblings", help="Number of players", type=int, nargs="?", default=3, const=3)
  194. parser.add_argument("-c", "--common_brother", help="The siblings share a brother, instead of each having their own", action="store_true")
  195. parser.add_argument("-b", "--brother_count", help="How many times can a brother overwhelm a sibling?\nIf common_brother is set, this is total; otherwise, it is per sibling.", type=int, nargs="?", default=3, const=3)
  196. args = parser.parse_args()
  197. gs = GameState(rounds=args.rounds, siblings=args.siblings, common_brother=args.common_brother, brother_count=args.brother_count, verbose=args.verbose)