import argparse from random import randint, shuffle class Sibling: def __init__(self, brother_count=None, state=None, game=None): self.tragedy = 0 self.masterpiece = 0 self.brother = 0 self.brother_count = 0 self.brother_max = int(brother_count) if brother_count else 3 self.brother_ok = True self.alive = True self.rounds = 0 self.success = False self.state = state self.game = game def round_check(self): if self.tragedy >= 10: self.alive = False return False self.rounds += 1 if self.tragedy < 0: self.tragedy = 0 if self.masterpiece < 0: self.masterpiece = 0 if self.brother < 0: self.brother = 0 if self.masterpiece >= 5: success = self.publication_attempt() if success: self.success = True return True else: self.masterpiece = 0 if self.brother_ok and self.brother >= 5: if self.game.common_brother: self.game.brother_interferes() else: self.brother_count += 1 if self.brother_count >= self.brother_max: self.brother_ok = False self.brother = 0 self.masterpiece = 0 def publication_attempt(self): if randint(1,6) == 6: return True self.state.failed_manuscripts += 1 return False def __str__(self): 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)." if self.success: out_str += " They were successful in publishing their manuscript!" return out_str class Game: def __init__(self, siblings=None, common_brother=None, brother_count=None, verbose=None, state=None): self.num_siblings = int(siblings) if siblings is not None else 3 self.common_brother = bool(common_brother) if common_brother is not None else False self.brother_count = int(brother_count) if brother_count is not None else self.num_siblings self.verbose = bool(verbose) if verbose is not None else True self.state = state self.siblings = [] for _ in range(self.num_siblings): if self.common_brother: self.siblings.append(Sibling(state=self.state, game=self)) else: self.siblings.append(Sibling(brother_count=self.brother_count, state=self.state, game=self)) if self.common_brother: self.brother_ok = True self.per_sibling_brother = [] for _ in range(self.num_siblings): self.per_sibling_brother.append(True) self.success = False def is_brother_ok(self, sibling): if self.common_brother and self.brother_ok: return True elif sibling.brother_ok and not self.common_brother: return True return False def bronte_event(self, sibling): event_type = randint(1,6) if event_type <= 2: self.quiet_day(sibling) elif event_type <= 5: self.walk_moors(sibling) else: # This is remaining instead of being relegated to git commit history # because it's being actively investigated as a way to manipulate # success rates. # inc_chance = randint(1,6) # if inc_chance == 6: # sibling.masterpiece += 1 sibling.masterpiece += 1 def quiet_day(self, sibling): day_type = randint(1,6) if day_type == 1: sibling.tragedy += 1 if self.is_brother_ok(sibling): sibling.brother += 1 elif day_type == 2: sibling.masterpiece -= 1 elif day_type == 3: if self.is_brother_ok(sibling): sibling.brother += 3 elif day_type == 4: sibling.masterpiece -= 3 elif day_type == 5: sibling.masterpiece += 2 elif day_type == 6: sibling.tragedy += 1 if self.is_brother_ok(sibling): sibling.brother += 2 def walk_moors(self, sibling): walk_type = randint(1,6) if walk_type == 1: sibling.tragedy += 1 elif walk_type == 2: if self.is_brother_ok(sibling): sibling.brother += 2 elif walk_type == 3: sibling.tragedy += 2 elif walk_type == 4: sibling.masterpiece += 1 elif walk_type == 5: if self.is_brother_ok(sibling): sibling.brother += 2 elif walk_type == 6: sibling.tragedy += 1 def turn(self, sibling): self.bronte_event(sibling) check = sibling.round_check() if check: return True return False def round(self): for sibling in self.siblings: if sibling.alive: break else: self.vprint("Everyone has died.") for sibling in self.siblings: self.vprint(sibling) self.success = False return False temp_sibs = self.siblings[:] shuffle(temp_sibs) for current_sibling in temp_sibs: cur_sib_num = self.siblings.index(current_sibling) if current_sibling.alive: success = self.turn(current_sibling) if success: self.vprint(f"Sibling #{cur_sib_num+1} has published their masterpiece!") for sibling in self.siblings: self.vprint(sibling) self.success = True return False return True def brother_interferes(self): self.brother_count -= 1 if self.brother_count <= 0: self.brother_ok = False def vprint(self, text): if self.verbose: print(text) def play_game(self): continue_playing = True while continue_playing: continue_playing = self.round() return self.success class GameState: def __init__(self, rounds=None, siblings=None, common_brother=None, brother_count=None, verbose=None): self.rounds = int(rounds) if rounds else 10000 self.successes = 0 self.failures = 0 self.failed_manuscripts = 0 for _ in range(self.rounds): game = Game(siblings=siblings, common_brother=common_brother, brother_count=brother_count, verbose=verbose, state=self) success = game.play_game() if success: self.successes += 1 else: self.failures += 1 out_str = f"Across {self.rounds} families of {siblings} sisters and " if common_brother: out_str += "one brother" else: out_str += "as many brothers" out_str += f", there were {self.successes} successfully-published masterpieces, and {self.failures} tragic deaths for everybody involved." out_str += f" In addition, there were {self.failed_manuscripts} manuscripts submitted that were refused by all publishers." print(out_str) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", help="Each round prints its results", action="store_true") parser.add_argument("-r", "--rounds", help="Number of game rounds to simulate", type=int, nargs="?", default=10000, const=10000) parser.add_argument("-s", "--siblings", help="Number of players", type=int, nargs="?", default=3, const=3) parser.add_argument("-c", "--common_brother", help="The siblings share a brother, instead of each having their own", action="store_true") 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) args = parser.parse_args() gs = GameState(rounds=args.rounds, siblings=args.siblings, common_brother=args.common_brother, brother_count=args.brother_count, verbose=args.verbose)