Hoid Slayer He/Him Posted February 9 Author Posted February 9 23 minutes ago, Doc12 said: I really screw myself over whenever I play really well evil because then no one trusts me in a game when I'm village @Hoid Slayerno thoughts about me ? Looking back, there really was an alternate game where I didn't get converted c1. I had found the Shardbearer c1, and we could have lynched him c2, making it all the harder for whoever did get converted to even the score. So again, Mistfallen's one action changed the game (even though I had to put in all the work ) Only that you fixed all of our mechanic mistakes with your plotting A true hero
Jo and the Bush all/any Posted February 9 Posted February 9 15 minutes ago, Mistfallen Soldier said: lol We were going to do Jo, and then it switched to you, so at least one of you would’ve been an Elim, if not both (perhaps Jo would have converted you) But anyways, yeah, I am very proud of myself for doing that I wouldn't have converted anyone until I was in the middle of getting executed, so i would have been a solo act for far longer. Not sure who I would have converted.
DrakeMarshall he/him Posted February 9 Posted February 9 honestly I might've considered protecting Archer but I kinda forgot that day and night weren't seperated that's what I get for not paying attention anyways seems like it was a fun game, gg to all those that played and thanks to the GMs sorry for fibbing about being your spren coco, no hard feelings I hope I mean its not like it rly worked considering how the first day ended, I did feel you seemed villagey though re: game balance, agreed that if things had gone differently, a spren with an elim radiant could have yielded basically a bonus elim with a vote manipulation power, which could've made things quite a bit more elim favouring but you know how that song and dance goes, the life-link between spren and radiant makes it very much all-or-nothing, either you get 2 elims or 0 the best laid plans of mice and spren often go awry :P If the Spren win condition were tweaked a bit, perhaps there could be incentives to remain more neutral and play both sides, making the role less prone to tipping the balance. I could probably spitball a few ideas if you ever want to run this again sometime. Or not. Honestly it's probably ok to leave it as-is. If you do ever do this setup again, I bet you'll have a better idea how to handle it. anyways balance isn't everything 2
Verdance he/him Posted February 9 Posted February 9 5 hours ago, DrakeMarshall said: re: game balance, agreed that if things had gone differently, a spren with an elim radiant could have yielded basically a bonus elim with a vote manipulation power, which could've made things quite a bit more elim favouring but you know how that song and dance goes, the life-link between spren and radiant makes it very much all-or-nothing, either you get 2 elims or 0 the best laid plans of mice and spren often go awry :P If the Spren win condition were tweaked a bit, perhaps there could be incentives to remain more neutral and play both sides, making the role less prone to tipping the balance. I could probably spitball a few ideas if you ever want to run this again sometime. Or not. Honestly it's probably ok to leave it as-is. If you do ever do this setup again, I bet you'll have a better idea how to handle it. anyways balance isn't everything I heard that and in indignation- -am forced to agree since you only play each setup once
Through the Living Hopper He/Him Posted February 9 Posted February 9 7 hours ago, DrakeMarshall said: honestly I might've considered protecting Archer but I kinda forgot that day and night weren't seperated SEE?!
Divergent He/Him Posted February 9 Posted February 9 20 hours ago, Doc12 said: Oh I promise I am not that scary Dive you've been evil with me once (kinda) and we lost bad in the AN XD It's funny that you spectators didn't even see my PMs - I was chatting up almost all the villagers and telling them what they wanted to hear, which is why no one suspected me in the end But then again, I PM everyone regardless of whether I'm evil or good, so always trust me when I'm PMing you because I am friendly and kind Oh I was scared alright when you went after me D2 of that game back when we didn't know yet that we were both elims . I know from experience that you do so well convincing people of your innocence if given the opportunity to do so in PMs haha so I can see why the villagers trusted you until the end
Doc12 Posted February 9 Posted February 9 (edited) 25 minutes ago, Divergent said: I know from experience that you do so well convincing people of your innocence if given the opportunity to do so in PMs haha so I can see why the villagers trusted you until the end But I was innocent in the one long game where you were a pinch hitter Glad I convinced you then even if someone (glares in Mistfallen's direction) got me lynched anyway. Edit: You know. Maybe that's WHY I got killed day 1 of the AN... The GMs nerfed me by not opening PMs day 1 Edited February 9 by Doc12 2
Divergent He/Him Posted February 9 Posted February 9 23 minutes ago, Doc12 said: But I was innocent in the one long game where you were a pinch hitter Glad I convinced you then even if someone (glares in Mistfallen's direction) got me lynched anyway. Edit: You know. Maybe that's WHY I got killed day 1 of the AN... The GMs nerfed me by not opening PMs day 1 When I saw that there wasn't a living Tineye D1, I was like "If Doc's playing this game, he'd be sad there's no PMs" 1
Akimikoisthecutest Posted February 11 Posted February 11 (edited) Uh so I made this little script with this rule set because I thought it would be kind of fun to see it. Uh this kind of works like a master spreadsheet might but it is probably buggy and inefficient. If any more experienced coders (*cough cough* @Usseewa @KaladinsSenseOfHumourSpren) want to give me feedback, that would be appreciated import random import os def clear_screen(): os.system('cls' if os.name == 'nt' else 'clear') class Player: def __init__(self, name, alignment, role=None, radiant_target=None): self.name = name self.alignment = alignment self.role = role self.is_alive = True self.is_roleblocked = False self.protected = False self.role_action_type = None self.role_action_target = None self.faction_action_type = None self.faction_action_target = None self.radiant_target = radiant_target self.spren_prot_used = False def __repr__(self): status = "" if self.is_alive else "[DEAD] " return f"{status}{self.name} ({self.alignment} {self.role if self.role else ''})" # --- ABILITY CONFIGURATION --- # Defines exactly which roles can do which actions ROLE_ABILITIES = { 'Direform': ['block'], 'Stormform': ['redirect'], 'Envoyform': ['decrease'], 'Inspiring': ['increase', 'block_conv'], 'Heroic': ['protect'], 'Spren': ['protect_radiant'], 'Shardbearer': ['kill'], 'Discerning': ['scan'], 'Sneaky': ['sneaky'], 'Willful': [] # Passive role } # --- GLOBAL GAME STATE --- conversion_charges = 2 def display_graveyard(players): dead_players = [f"{p.name} ({p.alignment} {p.role})" for p in players if not p.is_alive] print("\n=== GRAVEYARD (ELIMINATED) ===") if dead_players: for entry in dead_players: print(f"• {entry}") else: print("No one has died yet.") print("==============================") def simulate_cycle(players): global conversion_charges alive_at_start = [p.name for p in players if p.is_alive] for p in players: p.is_roleblocked = False p.protected = False # 1. Stormform Redirect for p in [x for x in players if x.role == 'Stormform' and not x.is_roleblocked and x.is_alive]: if p.role_action_type == 'redirect' and p.role_action_target: storm_target = p.role_action_target actors_to_redirect = [a for a in players if (a.role_action_target == storm_target or a.faction_action_target == storm_target) and a != p] for actor in actors_to_redirect: new_dest = random.choice([x for x in players if x != storm_target and x.is_alive]) if actor.role_action_target == storm_target: actor.role_action_target = new_dest if actor.faction_action_target == storm_target: actor.faction_action_target = new_dest print(f"STORM: {p.name} redirected {actor.name} to {new_dest.name}!") # 2. Direform Roleblock for p in [x for x in players if x.role == 'Direform' and not x.is_roleblocked and x.is_alive]: if p.role_action_type == 'block' and p.role_action_target: p.role_action_target.is_roleblocked = True print(f"BLOCK: {p.name} (Direform) roleblocked {p.role_action_target.name}") # 4. Protections (Heroic & Spren) protected_list = [p.role_action_target for p in players if p.role == 'Heroic' and not p.is_roleblocked and p.is_alive] for p in [x for x in players if x.role == 'Spren' and x.is_alive and not x.is_roleblocked]: if p.role_action_type == 'protect_radiant' and not p.spren_prot_used and p.role_action_target == p.radiant_target: protected_list.append(p.radiant_target) p.spren_prot_used = True print(f"SUCCESS: {p.name} (Spren) protected {p.radiant_target.name}!") # 5. Conversion (Regals) conv_blocked = [p.role_action_target for p in players if p.role == 'Inspiring' and p.role_action_type == 'block_conv' and not p.is_roleblocked and p.is_alive] for p in [x for x in players if x.alignment == 'Regal' and x.faction_action_type == 'convert' and not x.is_roleblocked and x.is_alive]: if conversion_charges <= 0: continue target = p.faction_action_target if target and target.alignment.capitalize() == 'Singer' and target not in conv_blocked and target.role != 'Willful': target.alignment = 'Regal' conversion_charges -= 1 print(f"CONVERSION: {target.name} has joined the Regals!") # 6. Kills targets_to_die = [] for p in [x for x in players if (x.faction_action_type == 'kill' or x.role == 'Shardbearer') and not x.is_roleblocked and x.is_alive]: target = p.faction_action_target if p.alignment == 'Regal' else p.role_action_target if target and target not in protected_list: targets_to_die.append(target) for target in list(set(targets_to_die)): target.is_alive = False # 7. Spren Death Logic for s in [x for x in players if x.role == 'Spren' and x.is_alive]: if s.radiant_target and not s.radiant_target.is_alive: s.is_alive = False print(f"LOSS: {s.name}'s Radiant ({s.radiant_target.name}) died. {s.name} is eliminated.") print("\n--- NIGHT DEATH REPORT ---") deaths = [n for n in alive_at_start if not next(pl for pl in players if pl.name == n).is_alive] if deaths: for d in deaths: print(f"DIED: {d}") else: print("No one died tonight.") display_graveyard(players) return players def simulate_day_phase(players, votes): tally = {p.name: 0 for p in players if p.is_alive} penalized = [p.role_action_target.name for p in players if p.role == 'Envoyform' and p.role_action_type == 'decrease' and not p.is_roleblocked and p.is_alive and p.role_action_target] buffed = [p.role_action_target.name for p in players if p.role == 'Inspiring' and p.role_action_type == 'increase' and not p.is_roleblocked and p.is_alive and p.role_action_target] for v_name, t_name in votes.items(): if t_name in tally: weight = 1 if v_name in penalized: weight = 0 elif v_name in buffed: weight = 2 tally[t_name] += weight if tally: victim_name = max(tally, key=tally.get) if tally[victim_name] > 0: victim = next(p for p in players if p.name == victim_name) victim.is_alive = False print(f"\n--- DAY PHASE: {victim_name} voted out with {tally[victim_name]} votes ---") display_graveyard(players) return players def check_winner(players): living = [p for p in players if p.is_alive] singers = [p for p in living if p.alignment == "Singer"] regals = [p for p in living if p.alignment == "Regal"] shard = [p for p in living if p.role == "Shardbearer"] if shard and len(shard) >= (len(singers) + len(regals)): return "SHARDBEARER" if not regals and not shard: return "SINGERS" if len(regals) > len(singers) and not shard: return "REGALS" return None # --- SETUP --- players = [ Player("Rudy", "Regal", "Direform"), Player("Lucias", "Singer", "Discerning"), Player("Jolene", "Singer", "Heroic"), Player("Mariana", "Singer", "Willful"), Player("Aiden", "Shardbearer", "Shardbearer"), Player("Bailey", "Singer", "Spren", radiant_target="Vivienne"), Player("Kai", "Singer", "Inspiring"), Player("Tadashi", "Regal", "Stormform"), Player("Abraham", "Singer", "Sneaky"), Player("Neil", "Regal", "Envoyform"), ] p = {player.name: player for player in players} p['Bailey'].radiant_target = p['Jolene'] # --- MAIN GAME LOOP --- cycle = 1 while True: clear_screen() print(f"=== CYCLE {cycle} ===") # 1. REGAL TEAM ACTION (Kill/Convert) regals_alive = [r for r in players if r.alignment == 'Regal' and r.is_alive] if regals_alive: print("\n--- REGAL FACTION ACTION (TEAM) ---") while True: t_act = input("Action (kill/convert) or blank: ").strip().lower() if not t_act or t_act in ['kill', 'convert']: break print("Invalid faction action!") if t_act: t_target_name = input("Target Name: ").strip() t_target = p.get(t_target_name) for r in regals_alive: r.faction_action_type, r.faction_action_target = t_act, t_target # 2. INDIVIDUAL ROLE ACTIONS (RESTRICTED) for name, actor in p.items(): if not actor.is_alive: continue allowed = ROLE_ABILITIES.get(actor.role, []) if not allowed: print(f"\n{name} ({actor.role}) has no active abilities.") continue print(f"\n{name} ({actor.role}) Turn:") print(f"Available abilities: {', '.join(allowed)}") while True: act = input("Enter ability or blank to skip: ").strip().lower() if not act or act in allowed: break print(f"Invalid! {name} can only use: {', '.join(allowed)}") if act: target_name = input(f"Target for {name}: ").strip() actor.role_action_type = act actor.role_action_target = p.get(target_name) clear_screen() players = simulate_cycle(players) winner = check_winner(players) if winner: print(f"\n*** {winner} WIN! ***") break input("\nPress Enter to begin Voting Phase...") clear_screen() print("=== VOTING PHASE ===") votes = {} for n, v in p.items(): if v.is_alive: vote = input(f"{n}, vote for someone: ").strip() votes[n] = vote players = simulate_day_phase(players, {k: v for k, v in votes.items() if v in p}) winner = check_winner(players) if winner: print(f"\n*** {winner} WIN! ***") break cycle += 1 for player in players: player.role_action_type = player.role_action_target = None player.faction_action_type = player.faction_action_target = None input("\nPress Enter for next cycle...") Edited February 12 by Akimikoisthecutest 2
Usseewa Posted February 12 Posted February 12 17 minutes ago, Akimikoisthecutest said: Uh so I made this little script with this rule set because I thought it would be kind of fun to see it. Uh this kind of works like a master spreadsheet might but it is probably buggy and inefficient. If any more experienced coders (*cough cough* @Usseewa @KaladinsSenseOfHumourSpren) want to give me feedback, that would be appreciated I'll give some feedback tomorrow probably when I can, tho Ksauce may beat me to it.
KaladinsSenseOfHumourSpren He/Him Posted February 12 Posted February 12 7 hours ago, Akimikoisthecutest said: Uh so I made this little script with this rule set because I thought it would be kind of fun to see it. Uh this kind of works like a master spreadsheet might but it is probably buggy and inefficient. If any more experienced coders (*cough cough* @Usseewa @KaladinsSenseOfHumourSpren) want to give me feedback, that would be appreciated I've only taken a cursory look, but this looks really good! You know you can do this, right? import random import os def clear_screen(): os.system('cls' if os.name == 'nt' else 'clear') class Player: def __init__(self, name, alignment, role=None, radiant_target=None): self.name = name self.alignment = alignment self.role = role self.is_alive = True self.is_roleblocked = False self.protected = False self.role_action_type = None self.role_action_target = None self.faction_action_type = None self.faction_action_target = None self.radiant_target = radiant_target self.spren_prot_used = False def __repr__(self): status = "" if self.is_alive else "[DEAD] " return f"{status}{self.name} ({self.alignment} {self.role if self.role else ''})" # --- ABILITY CONFIGURATION --- # Defines exactly which roles can do which actions ROLE_ABILITIES = { 'Direform': ['block'], 'Stormform': ['redirect'], 'Envoyform': ['decrease'], 'Inspiring': ['increase', 'block_conv'], 'Heroic': ['protect'], 'Spren': ['protect_radiant'], 'Shardbearer': ['kill'], 'Discerning': ['scan'], 'Sneaky': ['sneaky'], 'Willful': [] # Passive role } # --- GLOBAL GAME STATE --- conversion_charges = 2 def display_graveyard(players): dead_players = [f"{p.name} ({p.alignment} {p.role})" for p in players if not p.is_alive] print("\n=== GRAVEYARD (ELIMINATED) ===") if dead_players: for entry in dead_players: print(f"• {entry}") else: print("No one has died yet.") print("==============================") def simulate_cycle(players): global conversion_charges alive_at_start = [p.name for p in players if p.is_alive] for p in players: p.is_roleblocked = False p.protected = False # 1. Stormform Redirect for p in [x for x in players if x.role == 'Stormform' and not x.is_roleblocked and x.is_alive]: if p.role_action_type == 'redirect' and p.role_action_target: storm_target = p.role_action_target actors_to_redirect = [a for a in players if (a.role_action_target == storm_target or a.faction_action_target == storm_target) and a != p] for actor in actors_to_redirect: new_dest = random.choice([x for x in players if x != storm_target and x.is_alive]) if actor.role_action_target == storm_target: actor.role_action_target = new_dest if actor.faction_action_target == storm_target: actor.faction_action_target = new_dest print(f"STORM: {p.name} redirected {actor.name} to {new_dest.name}!") # 2. Direform Roleblock for p in [x for x in players if x.role == 'Direform' and not x.is_roleblocked and x.is_alive]: if p.role_action_type == 'block' and p.role_action_target: p.role_action_target.is_roleblocked = True print(f"BLOCK: {p.name} (Direform) roleblocked {p.role_action_target.name}") # 4. Protections (Heroic & Spren) protected_list = [p.role_action_target for p in players if p.role == 'Heroic' and not p.is_roleblocked and p.is_alive] for p in [x for x in players if x.role == 'Spren' and x.is_alive and not x.is_roleblocked]: if p.role_action_type == 'protect_radiant' and not p.spren_prot_used and p.role_action_target == p.radiant_target: protected_list.append(p.radiant_target) p.spren_prot_used = True print(f"SUCCESS: {p.name} (Spren) protected {p.radiant_target.name}!") # 5. Conversion (Regals) conv_blocked = [p.role_action_target for p in players if p.role == 'Inspiring' and p.role_action_type == 'block_conv' and not p.is_roleblocked and p.is_alive] for p in [x for x in players if x.alignment == 'Regal' and x.faction_action_type == 'convert' and not x.is_roleblocked and x.is_alive]: if conversion_charges <= 0: continue target = p.faction_action_target if target and target.alignment.capitalize() == 'Singer' and target not in conv_blocked and target.role != 'Willful': target.alignment = 'Regal' conversion_charges -= 1 print(f"CONVERSION: {target.name} has joined the Regals!") # 6. Kills targets_to_die = [] for p in [x for x in players if (x.faction_action_type == 'kill' or x.role == 'Shardbearer') and not x.is_roleblocked and x.is_alive]: target = p.faction_action_target if p.alignment == 'Regal' else p.role_action_target if target and target not in protected_list: targets_to_die.append(target) for target in list(set(targets_to_die)): target.is_alive = False # 7. Spren Death Logic for s in [x for x in players if x.role == 'Spren' and x.is_alive]: if s.radiant_target and not s.radiant_target.is_alive: s.is_alive = False print(f"LOSS: {s.name}'s Radiant ({s.radiant_target.name}) died. {s.name} is eliminated.") print("\n--- NIGHT DEATH REPORT ---") deaths = [n for n in alive_at_start if not next(pl for pl in players if pl.name == n).is_alive] if deaths: for d in deaths: print(f"DIED: {d}") else: print("No one died tonight.") display_graveyard(players) return players def simulate_day_phase(players, votes): tally = {p.name: 0 for p in players if p.is_alive} penalized = [p.role_action_target.name for p in players if p.role == 'Envoyform' and p.role_action_type == 'decrease' and not p.is_roleblocked and p.is_alive and p.role_action_target] buffed = [p.role_action_target.name for p in players if p.role == 'Inspiring' and p.role_action_type == 'increase' and not p.is_roleblocked and p.is_alive and p.role_action_target] for v_name, t_name in votes.items(): if t_name in tally: weight = 1 if v_name in penalized: weight = 0 elif v_name in buffed: weight = 2 tally[t_name] += weight if tally: victim_name = max(tally, key=tally.get) if tally[victim_name] > 0: victim = next(p for p in players if p.name == victim_name) victim.is_alive = False print(f"\n--- DAY PHASE: {victim_name} voted out with {tally[victim_name]} votes ---") display_graveyard(players) return players def check_winner(players): living = [p for p in players if p.is_alive] singers = [p for p in living if p.alignment == "Singer"] regals = [p for p in living if p.alignment == "Regal"] shard = [p for p in living if p.role == "Shardbearer"] if shard and len(shard) >= (len(singers) + len(regals)): return "SHARDBEARER" if not regals and not shard: return "SINGERS" if len(regals) > len(singers) and not shard: return "REGALS" return None # --- SETUP --- players = [ Player("Rudy", "Regal", "Direform"), Player("Lucias", "Singer", "Discerning"), Player("Jolene", "Singer", "Heroic"), Player("Mariana", "Singer", "Willful"), Player("Aiden", "Shardbearer", "Shardbearer"), Player("Bailey", "Singer", "Spren", radiant_target="Vivienne"), Player("Kai", "Singer", "Inspiring"), Player("Tadashi", "Regal", "Stormform"), Player("Abraham", "Singer", "Sneaky"), Player("Neil", "Regal", "Envoyform"), ] p = {player.name: player for player in players} p['Bailey'].radiant_target = p['Vivienne'] # --- MAIN GAME LOOP --- cycle = 1 while True: clear_screen() print(f"=== CYCLE {cycle} ===") # 1. REGAL TEAM ACTION (Kill/Convert) regals_alive = [r for r in players if r.alignment == 'Regal' and r.is_alive] if regals_alive: print("\n--- REGAL FACTION ACTION (TEAM) ---") while True: t_act = input("Action (kill/convert) or blank: ").strip().lower() if not t_act or t_act in ['kill', 'convert']: break print("Invalid faction action!") if t_act: t_target_name = input("Target Name: ").strip() t_target = p.get(t_target_name) for r in regals_alive: r.faction_action_type, r.faction_action_target = t_act, t_target # 2. INDIVIDUAL ROLE ACTIONS (RESTRICTED) for name, actor in p.items(): if not actor.is_alive: continue allowed = ROLE_ABILITIES.get(actor.role, []) if not allowed: print(f"\n{name} ({actor.role}) has no active abilities.") continue print(f"\n{name} ({actor.role}) Turn:") print(f"Available abilities: {', '.join(allowed)}") while True: act = input("Enter ability or blank to skip: ").strip().lower() if not act or act in allowed: break print(f"Invalid! {name} can only use: {', '.join(allowed)}") if act: target_name = input(f"Target for {name}: ").strip() actor.role_action_type = act actor.role_action_target = p.get(target_name) clear_screen() players = simulate_cycle(players) winner = check_winner(players) if winner: print(f"\n*** {winner} WIN! ***") break input("\nPress Enter to begin Voting Phase...") clear_screen() print("=== VOTING PHASE ===") votes = {} for n, v in p.items(): if v.is_alive: vote = input(f"{n}, vote for someone: ").strip() votes[n] = vote players = simulate_day_phase(players, {k: v for k, v in votes.items() if v in p}) winner = check_winner(players) if winner: print(f"\n*** {winner} WIN! ***") break cycle += 1 for player in players: player.role_action_type = player.role_action_target = None player.faction_action_type = player.faction_action_target = None input("\nPress Enter for next cycle...") There's a code option next to the spoiler box one. You know, I might do something like this for the Tyrean ruleset Who need spreadsheets when you have code
Amanuensis he/him Posted February 12 Posted February 12 (edited) 12 hours ago, KaladinsSenseOfHumourSpren said: I've only taken a cursory look, but this looks really good! You know you can do this, right? import random import os def clear_screen(): os.system('cls' if os.name == 'nt' else 'clear') class Player: def __init__(self, name, alignment, role=None, radiant_target=None): self.name = name self.alignment = alignment self.role = role self.is_alive = True self.is_roleblocked = False self.protected = False self.role_action_type = None self.role_action_target = None self.faction_action_type = None self.faction_action_target = None self.radiant_target = radiant_target self.spren_prot_used = False def __repr__(self): status = "" if self.is_alive else "[DEAD] " return f"{status}{self.name} ({self.alignment} {self.role if self.role else ''})" # --- ABILITY CONFIGURATION --- # Defines exactly which roles can do which actions ROLE_ABILITIES = { 'Direform': ['block'], 'Stormform': ['redirect'], 'Envoyform': ['decrease'], 'Inspiring': ['increase', 'block_conv'], 'Heroic': ['protect'], 'Spren': ['protect_radiant'], 'Shardbearer': ['kill'], 'Discerning': ['scan'], 'Sneaky': ['sneaky'], 'Willful': [] # Passive role } # --- GLOBAL GAME STATE --- conversion_charges = 2 def display_graveyard(players): dead_players = [f"{p.name} ({p.alignment} {p.role})" for p in players if not p.is_alive] print("\n=== GRAVEYARD (ELIMINATED) ===") if dead_players: for entry in dead_players: print(f"• {entry}") else: print("No one has died yet.") print("==============================") def simulate_cycle(players): global conversion_charges alive_at_start = [p.name for p in players if p.is_alive] for p in players: p.is_roleblocked = False p.protected = False # 1. Stormform Redirect for p in [x for x in players if x.role == 'Stormform' and not x.is_roleblocked and x.is_alive]: if p.role_action_type == 'redirect' and p.role_action_target: storm_target = p.role_action_target actors_to_redirect = [a for a in players if (a.role_action_target == storm_target or a.faction_action_target == storm_target) and a != p] for actor in actors_to_redirect: new_dest = random.choice([x for x in players if x != storm_target and x.is_alive]) if actor.role_action_target == storm_target: actor.role_action_target = new_dest if actor.faction_action_target == storm_target: actor.faction_action_target = new_dest print(f"STORM: {p.name} redirected {actor.name} to {new_dest.name}!") # 2. Direform Roleblock for p in [x for x in players if x.role == 'Direform' and not x.is_roleblocked and x.is_alive]: if p.role_action_type == 'block' and p.role_action_target: p.role_action_target.is_roleblocked = True print(f"BLOCK: {p.name} (Direform) roleblocked {p.role_action_target.name}") # 4. Protections (Heroic & Spren) protected_list = [p.role_action_target for p in players if p.role == 'Heroic' and not p.is_roleblocked and p.is_alive] for p in [x for x in players if x.role == 'Spren' and x.is_alive and not x.is_roleblocked]: if p.role_action_type == 'protect_radiant' and not p.spren_prot_used and p.role_action_target == p.radiant_target: protected_list.append(p.radiant_target) p.spren_prot_used = True print(f"SUCCESS: {p.name} (Spren) protected {p.radiant_target.name}!") # 5. Conversion (Regals) conv_blocked = [p.role_action_target for p in players if p.role == 'Inspiring' and p.role_action_type == 'block_conv' and not p.is_roleblocked and p.is_alive] for p in [x for x in players if x.alignment == 'Regal' and x.faction_action_type == 'convert' and not x.is_roleblocked and x.is_alive]: if conversion_charges <= 0: continue target = p.faction_action_target if target and target.alignment.capitalize() == 'Singer' and target not in conv_blocked and target.role != 'Willful': target.alignment = 'Regal' conversion_charges -= 1 print(f"CONVERSION: {target.name} has joined the Regals!") # 6. Kills targets_to_die = [] for p in [x for x in players if (x.faction_action_type == 'kill' or x.role == 'Shardbearer') and not x.is_roleblocked and x.is_alive]: target = p.faction_action_target if p.alignment == 'Regal' else p.role_action_target if target and target not in protected_list: targets_to_die.append(target) for target in list(set(targets_to_die)): target.is_alive = False # 7. Spren Death Logic for s in [x for x in players if x.role == 'Spren' and x.is_alive]: if s.radiant_target and not s.radiant_target.is_alive: s.is_alive = False print(f"LOSS: {s.name}'s Radiant ({s.radiant_target.name}) died. {s.name} is eliminated.") print("\n--- NIGHT DEATH REPORT ---") deaths = [n for n in alive_at_start if not next(pl for pl in players if pl.name == n).is_alive] if deaths: for d in deaths: print(f"DIED: {d}") else: print("No one died tonight.") display_graveyard(players) return players def simulate_day_phase(players, votes): tally = {p.name: 0 for p in players if p.is_alive} penalized = [p.role_action_target.name for p in players if p.role == 'Envoyform' and p.role_action_type == 'decrease' and not p.is_roleblocked and p.is_alive and p.role_action_target] buffed = [p.role_action_target.name for p in players if p.role == 'Inspiring' and p.role_action_type == 'increase' and not p.is_roleblocked and p.is_alive and p.role_action_target] for v_name, t_name in votes.items(): if t_name in tally: weight = 1 if v_name in penalized: weight = 0 elif v_name in buffed: weight = 2 tally[t_name] += weight if tally: victim_name = max(tally, key=tally.get) if tally[victim_name] > 0: victim = next(p for p in players if p.name == victim_name) victim.is_alive = False print(f"\n--- DAY PHASE: {victim_name} voted out with {tally[victim_name]} votes ---") display_graveyard(players) return players def check_winner(players): living = [p for p in players if p.is_alive] singers = [p for p in living if p.alignment == "Singer"] regals = [p for p in living if p.alignment == "Regal"] shard = [p for p in living if p.role == "Shardbearer"] if shard and len(shard) >= (len(singers) + len(regals)): return "SHARDBEARER" if not regals and not shard: return "SINGERS" if len(regals) > len(singers) and not shard: return "REGALS" return None # --- SETUP --- players = [ Player("Rudy", "Regal", "Direform"), Player("Lucias", "Singer", "Discerning"), Player("Jolene", "Singer", "Heroic"), Player("Mariana", "Singer", "Willful"), Player("Aiden", "Shardbearer", "Shardbearer"), Player("Bailey", "Singer", "Spren", radiant_target="Vivienne"), Player("Kai", "Singer", "Inspiring"), Player("Tadashi", "Regal", "Stormform"), Player("Abraham", "Singer", "Sneaky"), Player("Neil", "Regal", "Envoyform"), ] p = {player.name: player for player in players} p['Bailey'].radiant_target = p['Vivienne'] # --- MAIN GAME LOOP --- cycle = 1 while True: clear_screen() print(f"=== CYCLE {cycle} ===") # 1. REGAL TEAM ACTION (Kill/Convert) regals_alive = [r for r in players if r.alignment == 'Regal' and r.is_alive] if regals_alive: print("\n--- REGAL FACTION ACTION (TEAM) ---") while True: t_act = input("Action (kill/convert) or blank: ").strip().lower() if not t_act or t_act in ['kill', 'convert']: break print("Invalid faction action!") if t_act: t_target_name = input("Target Name: ").strip() t_target = p.get(t_target_name) for r in regals_alive: r.faction_action_type, r.faction_action_target = t_act, t_target # 2. INDIVIDUAL ROLE ACTIONS (RESTRICTED) for name, actor in p.items(): if not actor.is_alive: continue allowed = ROLE_ABILITIES.get(actor.role, []) if not allowed: print(f"\n{name} ({actor.role}) has no active abilities.") continue print(f"\n{name} ({actor.role}) Turn:") print(f"Available abilities: {', '.join(allowed)}") while True: act = input("Enter ability or blank to skip: ").strip().lower() if not act or act in allowed: break print(f"Invalid! {name} can only use: {', '.join(allowed)}") if act: target_name = input(f"Target for {name}: ").strip() actor.role_action_type = act actor.role_action_target = p.get(target_name) clear_screen() players = simulate_cycle(players) winner = check_winner(players) if winner: print(f"\n*** {winner} WIN! ***") break input("\nPress Enter to begin Voting Phase...") clear_screen() print("=== VOTING PHASE ===") votes = {} for n, v in p.items(): if v.is_alive: vote = input(f"{n}, vote for someone: ").strip() votes[n] = vote players = simulate_day_phase(players, {k: v for k, v in votes.items() if v in p}) winner = check_winner(players) if winner: print(f"\n*** {winner} WIN! ***") break cycle += 1 for player in players: player.role_action_type = player.role_action_target = None player.faction_action_type = player.faction_action_target = None input("\nPress Enter for next cycle...") There's a code option next to the spoiler box one. You know, I might do something like this for the Tyrean ruleset Who need spreadsheets when you have code You could maybe look at the SE bot I made with Python, did do the Tyrian rules Edited February 12 by Amanuensis
KaladinsSenseOfHumourSpren He/Him Posted February 12 Posted February 12 35 minutes ago, Amanuensis said: You could maybe look at the SE bot I made with Python, did do the Tyrian rules Ooh Can you send the code?
Amanuensis he/him Posted February 12 Posted February 12 30 minutes ago, KaladinsSenseOfHumourSpren said: Ooh Can you send the code? https://github.com/MahaloWell/SEBOT I might not have pushed the most recent version (I forget if I'd edited it since, been mostly focused on my RPG since that brief Python stint) but I can check when I get home
KaladinsSenseOfHumourSpren He/Him Posted February 13 Posted February 13 8 hours ago, Amanuensis said: https://github.com/MahaloWell/SEBOT I might not have pushed the most recent version (I forget if I'd edited it since, been mostly focused on my RPG since that brief Python stint) but I can check when I get home Wow this is beyond my Python skill Is this fully automatic? Like does it need the GM to say what actions players are taking?
Amanuensis he/him Posted February 13 Posted February 13 (edited) 18 hours ago, KaladinsSenseOfHumourSpren said: Wow this is beyond my Python skill Is this fully automatic? Like does it need the GM to say what actions players are taking? Yeah so for the most part it's a fully automatic discord bot but there's an option for GMs to be more hands on with it for custom rules. Eventually we should be trying to run a game on discord to see how it plays out Edited February 14 by Amanuensis 2
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now