Original Code


import random

class Character: 
    def __init__(self, name, role):
        self.name = name
        self.role = role
        self.alive = True
        self.player_controlled = False
        self.protected = False
        self.witch_cure = False
        self.witch_poison = False
        self.guard_prev = -1

class KillerGame:
    def __init__(self, num_players=12):
        print("Welcome to the Killer Games!")
        self.players = [Character(str(i), 'civilian') for i in range(num_players)]
        global the_one
        the_one = random.choice([p for p in self.players])
        the_one.player_controlled = True
        for i in range(4):
            random.choice([p for p in self.players if p.role == 'civilian']).role = 'killer'
        for i in range(1):
            guard = random.choice([p for p in self.players if p.role == 'civilian'])
            guard.role = "guard"
        for i in range(1):
            witch = random.choice([p for p in self.players if p.role == 'civilian'])
            witch.role = "witch"
            witch.witch_cure = True
            witch.witch_poison = True
        print("There are 12 player, labled number 0-11.")
        print(f"You are player {the_one.name}")
        if the_one.role == "killer":
            print("You are a killer. Try to murder all civilians and don't get voted out. Good luck.")
        elif the_one.role == "witch":
            print("You are a witch. Try to use your poison and cure to eliminate all killers. Good luck.")
        elif the_one.role == "guard":
            print("You are a guard. Try to use your ability to protect other players to eliminate all killers. Good luck.")
        else:
            print("You are a civilian. Try to elimiate the killers using your power to vote. Good luck.")
        self.turn = 0

    def night_phase_guard(self):
        self.dead = []
        print("Night "+str(self.turn+1))
        for player in self.players:
            if player.role == 'guard' and player.alive:
                if player.player_controlled:
                    valid = 0
                    while valid == 0:
                        target = input("Choose a player to protect (enter their number): ")
                        for p in self.players:
                            if p.name == target:
                                if not p.alive or p in self.dead:
                                    print("This player is dead. Please choose someone else.")
                                    break
                                elif p.protected:
                                    print("You cannot protect this player since you already protected them last turn. Please choose someone else.")
                                    break
                                else:
                                    valid = 1
                                    for pl in self.players:
                                        if pl.protected:
                                            pl.protected = False
                                    p.protected = True
                                    break
                else:
                    if any(not p.protected and p.alive and not p in self.dead for p in self.players):
                        for pl in self.players:
                            if pl.protected:
                                pl.protected = False
                                flag = pl
                        if self.turn == 0:
                            target = player
                        else:
                            target = random.choice([p for p in self.players if not p.protected and p.alive and not p in self.dead and p == flag])
                        target.protected = True

    def night_phase_killer(self):
        if the_one.role == "killer":
            print("Current other alive killers:")
            for k in self.players:
                if k.role == "killer" and k.alive and k.name != the_one.name:
                    print(f"Killer {k.name} is alive.")
        for player in self.players:
            if player.role == 'killer' and player.alive:
                if player.player_controlled:
                    valid = 0
                    while valid == 0:
                        target = input("Choose a civilian to kill (enter their number): ")
                        for p in self.players:
                            if p.name == target:
                                if not p.alive or p in self.dead:
                                    print("This player is dead. Please choose someone else.")
                                    break
                                elif p.name == player.name:
                                    print("You cannot kill yourself. Please choose someone else.")
                                    break
                                elif p.role == "killer":
                                    print("You cannot kill a killer. Please choose someone else.")
                                    break
                                else:
                                    valid = 1
                                    target = p
                                    break
                else:
                    if any(p.role == 'civilian' and p.alive and not p in self.dead for p in self.players):
                        target = random.choice([p for p in self.players if p.role == 'civilian' and p.alive and not p in self.dead])
                if the_one.role == "killer":
                    print(f"Killer {player.name} killed player {target.name}")
                self.dead.append(target)

    def night_phase_witch(self):
        for player in self.players:
            if player.role == 'witch' and player.alive:
                used_cure = False
                if player.player_controlled:
                    if any(p.alive and p in self.dead for p in self.players) and player.witch_cure:
                        valid1 = 0
                        while valid1 == 0:
                            reply1 = input("At least one person was killed tonight. Do you want to use your cure? (Yes or No) ")
                            if reply1 == "Yes":
                                valid1 = 1
                                target = random.choice([p for p in self.players if p.alive and p in self.dead])
                                self.dead.remove(target)
                                player.witch_cure = False
                                used_cure = True
                            elif reply1 == "No":
                                valid1 = 1
                    valid2 = 0
                    if player.witch_poison and not used_cure:
                        while valid2 == 0:
                            reply2 = input("Do you want to use your poison? (Yes or No) ")
                            if reply2 == "Yes":
                                valid2 = 1
                                valid3 = 0
                                while valid3 == 0:
                                    target = input("Choose a player to poison (enter their number): ")
                                    for p in self.players:
                                        if p.name == target:
                                            if not p.alive or p in self.dead:
                                                print("This player is dead. Please choose someone else.")
                                                break
                                            elif p.name == player.name:
                                                print("You cannot poison yourself. Please choose someone else.")
                                                break
                                            else:
                                                valid3 = 1
                                                target = p
                                                self.dead.append(target)
                                                player.witch_poison = False
                                                break
                            elif reply2 == "No":
                                valid2 = 1
                else:
                    if any(p.alive and p in self.dead for p in self.players) and player.witch_cure:
                        pool = [0,1,2,3,4,5,6,7,8,9]
                        flag = random.choice(pool)
                        if flag < 7:
                            target = random.choice([p for p in self.players if p.alive and p in self.dead])
                            self.dead.remove(target)
                            player.witch_cure = False
                            used_cure = True
                    elif player.witch_poison and not used_cure:
                        pool = []
                        for p in self.players:
                            if p.alive and p.name != player.name:
                                pool.append(p)
                                if p.role == "killer":
                                    for i in range(self.turn*8+2):
                                        pool.append(p)
                        target = random.choice([p for p in pool])
                        self.dead.append(target)
                        player.witch_poison = False

    def day_phase_anounce(self):
        for d in self.dead:
            if not d.protected:
                d.alive = False
        print("Day "+str(self.turn+1))
        print("Morning comes. The deaths are revealed.")
        for player in self.players:
            if not player.alive:
                print(f"Player {player.name} is dead.")
        if not the_one.alive:
            print("You are dead. You can continue observing the game.")
            
    def day_phase_vote(self):
        votes = {}
        for player in self.players:
            if player.alive:
                if player.player_controlled:
                    valid = 0
                    while valid == 0:
                        vote = input(f"Player {player.name}, choose someone to vote for (enter their number): ")
                        for p in self.players:
                            if p.name == vote:
                                if not p.alive:
                                    print("This player is dead. Please choose someone else.")
                                    break
                                elif p.name == player.name:
                                    print("You cannot choose yourself. Please choose someone else.")
                                    break
                                else:
                                    valid = 1
                                    vote = p
                                    break
                else:
                    pool = []
                    for p in self.players:
                        if p.alive and p.name != player.name:
                            pool.append(p)
                            if p.role == "killer" and player.role == "civilian":
                                for i in range(self.turn*8+2):
                                    pool.append(p)
                            elif p.role == "civilian" and player.role == "killer":
                                for i in range(self.turn*3+6):
                                    pool.append(p)

                    vote = random.choice([p for p in pool])
                print(f"Player {player.name} votes to exile player {vote.name}")
                if vote.name not in votes:
                    votes[vote.name] = 1
                else:
                    votes[vote.name] += 1

        exile_target = max(votes, key=votes.get)
        print(f"The player with the most votes, {exile_target}, is exiled.")
        for player in self.players:
            if player.name == exile_target:
                player.alive = False
        self.turn += 1

    def check_game_over(self):
        killers_alive = any(player.alive and player.role == 'killer' for player in self.players)
        civilians_alive = any(player.alive and player.role == 'civilian' for player in self.players)

        end = False
        
        if not killers_alive:
            print("Civilians win! All killers are eliminated.")
            end = True
        elif not civilians_alive:
            print("Killers win! All civilians are eliminated.")
            end = True
        if end:
            print("The roles of each player is revealed.")
            for p in self.players:
                print(f"Player {p.name} is a {p.role}.")
        return end

killer_game = KillerGame()

while 1:
    if not killer_game.check_game_over():
        killer_game.night_phase_guard()
        killer_game.night_phase_killer()
        killer_game.night_phase_witch()
        killer_game.day_phase_anounce()
    else:
        print("New Game")
        killer_game = KillerGame()
        continue
    if not killer_game.check_game_over():
        killer_game.day_phase_vote()
    else:
        print("New Game")
        killer_game = KillerGame()
        continue

FEEDBACK SECTION

Improvements to be made


  1. Add Player number validation function to validate each input of the user.
def get_valid_player_number(self, prompt, exclude_self=False, alive_only=True):
    while True:
        try:
            player_num = int(input(prompt))
            if 0 <= player_num < len(self.players):
                player = self.players[player_num]
                if exclude_self and player.player_controlled:
                    print("You cannot choose yourself. Please choose someone else.")
                elif alive_only and not player.alive:
                    print("This player is dead. Please choose someone else.")
                else:
                    return player_num
            else:
                print("Invalid number. Please try again.")
        except ValueError:
            print("Please enter a valid number.")
  1. In the night_phase_guard(self), add the logic of immediately killing the player because testing have shown that there are occasions where we could vote even after death for multiple rounds.
if not target.protected:
                target.alive = False
								print(f"Player {target.name} has been killed.")
else:
                print(f"Player {target.name} was protected and survived.")
  1. In the phase of assigning roles, divide into assigning killers, guard, witch separately. Thus, this will ensure that every time a unique role is assigned.
def __init__(self, num_players=12):
    print("Welcome to the Killer Games!")
    self.players = [Character(str(i), 'civilian') for i in range(num_players)]
    the_one = random.choice(self.players)
    the_one.player_controlled = True

    # Assign killers
    killers = random.sample([p for p in self.players if p.role == 'civilian'], 4)
    for killer in killers:
        killer.role = 'killer'

    # Assign guard
    guard = random.choice([p for p in self.players if p.role == 'civilian'])
    guard.role = "guard"

    # Assign witch
    witch = random.choice([p for p in self.players if p.role == 'civilian'])
    witch.role = "witch"
    witch.witch_cure = True
    witch.witch_poison = True
  1. Call check_game_over at the start of day and night phases. 每日一遍,防止诈尸
def start_day_phase(self):
    if self.check_game_over():
        return

def start_night_phase(self):
    if self.check_game_over():
        return

Code’s strengths


  1. The overall concept and structure of your game are well-thought-out. You've created a complex, interactive game that requires strategic thinking, making it engaging and fun to play.
  2. You've effectively used object-oriented programming principles. Defining a Character class and a KillerGame class helps in organizing the code logically and making it more manageable.