Example #1
0
def get_player_multistatements(
    player_objs: tuple[Player, ...]
) -> tuple[Statement, ...]:
    """
    Returns array of each player's statements.
    TODO players should choose with what priority they want to speak
    """
    knowledge_base = KnowledgeBase()
    heap = [(i, i) for i in range(const.NUM_PLAYERS)]  # (priority, player_index) tuples
    heapq.heapify(heap)
    while heap:
        _, curr_ind = heapq.heappop(heap)
        if knowledge_base.final_claims[curr_ind].priority < StatementLevel.PRIMARY:
            player_objs[curr_ind].analyze(knowledge_base)
            statement = player_objs[curr_ind].get_statement(knowledge_base)
            player_objs[curr_ind].prev_priority = statement.priority
            knowledge_base.add(statement, curr_ind)
            logger.info(f"Player {curr_ind}: {statement.sentence}")

            if statement.priority < StatementLevel.PRIMARY:
                # New statements have priority at least NUM_PLAYERS
                # to ensure everyone spoke once
                new_priority = const.NUM_PLAYERS + random.randrange(len(heap) + 1)
                heapq.heappush(heap, (new_priority, curr_ind))

    return tuple(knowledge_base.final_claims)
Example #2
0
 def print_statements(all_statements: tuple[Statement, ...]) -> None:
     """Prints all statements that have been said so far."""
     if DISABLE_USER_INPUT:
         return
     logger.info("\n-- GAME BEGINS --\n", cache=True)
     for j, statement in enumerate(all_statements):
         logger.info(f"Player {j}: {statement.sentence}", cache=True)
     UserState.print_cache()
Example #3
0
 def awake_init(cls, player_index: int, game_roles: list[Role]) -> Minion:
     """Initializes Minion - gets Wolf indices."""
     is_user = const.IS_USER[player_index]
     wolf_indices = find_all_player_indices(game_roles, Role.WOLF)
     logger.debug(f"[Hidden] Wolves are at indices: {list(wolf_indices)}")
     if is_user:
         logger.info(f"Wolves are at indices: {list(wolf_indices)}", cache=True)
     return cls(player_index, wolf_indices)
Example #4
0
 def awake_init(cls, player_index: int, game_roles: list[Role]) -> Insomniac:
     """Initializes Insomniac - learns new role."""
     is_user = const.IS_USER[player_index]
     insomniac_new_role = game_roles[player_index]
     logger.debug(f"[Hidden] Insomniac wakes up as a {insomniac_new_role}.")
     if is_user:
         logger.info(f"You woke up as a {insomniac_new_role}!", cache=True)
     return cls(player_index, insomniac_new_role)
Example #5
0
 def awake_init(cls, player_index: int, game_roles: list[Role]) -> Drunk:
     """Initializes Drunk - switches with a card in the center."""
     is_user = const.IS_USER[player_index]
     choice_ind = get_center(is_user)
     logger.debug(
         f"[Hidden] Drunk switches with Center Card {choice_ind - const.NUM_PLAYERS}"
         f" and unknowingly becomes a {game_roles[choice_ind]}.")
     if is_user:
         logger.info("You do not know your new role.", cache=True)
     swap_characters(game_roles, player_index, choice_ind)
     return cls(player_index, choice_ind)
Example #6
0
def get_player_statements(player_objs: tuple[Player, ...]) -> tuple[Statement, ...]:
    """Returns array of each player's statements."""
    knowledge_base = KnowledgeBase()
    curr_ind = 0
    while curr_ind < const.NUM_PLAYERS:
        player_objs[curr_ind].analyze(knowledge_base)
        statement = player_objs[curr_ind].get_statement(knowledge_base)
        knowledge_base.add(statement, curr_ind)
        logger.info(f"Player {curr_ind}: {statement.sentence}")
        curr_ind += 1
    return tuple(knowledge_base.final_claims)
Example #7
0
    def intro(original_roles: Sequence[Role]) -> None:
        """Tells the player what their assigned role is."""
        if DISABLE_USER_INPUT:
            return
        input("Press Enter to continue...")
        logger.info(CLEAR_TERMINAL)
        logger.clear()

        user_index = const.IS_USER.index(True)
        logger.info(
            f"Player {user_index}, you are a {original_roles[user_index]}!", cache=True
        )
        UserState.print_cache()
Example #8
0
def get_player(is_user: bool, exclude: tuple[int, ...] = ()) -> int:
    """Gets a random player index (not in the center) or prompts the user."""
    if is_user:
        choice_ind = -1
        while choice_ind < 0 or choice_ind >= const.NUM_PLAYERS:
            user_input = ""
            while not user_input.isdigit():
                user_input = input(
                    f"Which player index (0 - {const.NUM_PLAYERS - 1})? ")
            choice_ind = int(user_input)

            if choice_ind in exclude:
                logger.info("You cannot choose yourself or an index twice.")
                choice_ind = -1
        return choice_ind

    return random.choice(
        [i for i in range(const.NUM_PLAYERS) if i not in exclude])
Example #9
0
    def awake_init(cls, player_index: int, game_roles: list[Role]) -> Troublemaker:
        """Initializes Troublemaker - switches one player with another player."""
        is_user = const.IS_USER[player_index]
        if is_user:
            logger.info("Choose two players to switch places:")
        choice_1 = get_player(is_user, (player_index,))
        choice_2 = get_player(is_user, (player_index, choice_1))

        swap_characters(game_roles, choice_1, choice_2)
        logger.debug(
            f"[Hidden] Troublemaker switches Player {choice_1} and Player {choice_2}."
        )
        if is_user:
            logger.info(
                f"You switch Player {choice_1} with Player {choice_2}.", cache=True
            )

        return cls(player_index, choice_1, choice_2)
Example #10
0
    def print_game_result(game_roles: tuple[Role, ...], winning_team: Team) -> None:
        if DISABLE_USER_INPUT:
            return

        user_index = const.IS_USER.index(True)
        player_role = game_roles[user_index]
        if (
            (winning_team == Team.VILLAGE and player_role in const.VILLAGE_ROLES)
            or (winning_team == Team.WEREWOLF and player_role in const.EVIL_ROLES)
            or (winning_team == Team.TANNER and player_role == Role.TANNER)
        ):
            outcome = "won"
        else:
            outcome = "lost"

        logger.info(
            f"You were a {player_role} at the end of the game, " f"so you {outcome}!"
        )
Example #11
0
def get_center(is_user: bool, exclude: tuple[int, ...] = ()) -> int:
    """Gets a random index of a center card or prompts the user."""
    if is_user:
        choice_ind = -1
        while choice_ind < 0 or choice_ind >= const.NUM_CENTER:
            user_input = ""
            while not user_input.isdigit():
                user_input = input(
                    f"Which center card (0 - {const.NUM_CENTER - 1})? ")
            choice_ind = int(user_input)

            if choice_ind + const.NUM_PLAYERS in exclude:
                logger.info("You cannot choose an index twice.")
                choice_ind = -1
        return choice_ind + const.NUM_PLAYERS

    return random.choice([
        const.NUM_PLAYERS + i for i in range(const.NUM_CENTER)
        if const.NUM_PLAYERS + i not in exclude
    ])
Example #12
0
def get_statement_rl(player_obj: Any, knowledge_base: KnowledgeBase,
                     default_answer: Statement) -> Statement:
    """Gets Reinforcement Learning Wolf statement."""
    statements = get_wolf_statements(player_obj, knowledge_base)

    # Necessary to put this here to avoid circular import
    from wolfbot.encoder import WolfBotDecoder

    exp_dict: dict[str, dict[Statement, int]] = {}
    with open(const.EXPERIENCE_PATH, encoding="utf-8") as exp_file:
        exp_dict = json.load(exp_file, cls=WolfBotDecoder)
    experience = defaultdict(lambda: defaultdict(int), exp_dict)
    if not experience:
        logger.info("Experience dict did not load properly.")

    state = (tuple(player_obj.wolf_indices),
             tuple(knowledge_base.all_statements))
    scores = experience[str(state)]
    logger.info(f"Experience dict loaded.\n    {len(scores)} matching states.")

    best_choice = (default_answer, -100)
    for potential_statement, score in scores.items():
        if score > best_choice[1]:
            best_choice = (potential_statement, score)
    if best_choice[0] is None:
        logger.info("Using default statement...")
        return default_answer
    for statement in statements:
        if best_choice[0] == statement:
            return statement

    raise RuntimeError("Reached end of rl_wolf code.")
Example #13
0
def get_voting_result(
    player_objs: tuple[Player, ...], all_predictions: tuple[tuple[Role, ...], ...]
) -> tuple[tuple[Role, ...], tuple[int, ...], tuple[int, ...]]:
    """
    Creates confidence levels for each prediction and takes most common role guess
    array as the final guess for that index.

    - guess_histogram stores counts of prediction arrays.
    - wolf_votes stores individual votes for Wolves.
    """
    wolf_votes = [0] * const.NUM_PLAYERS
    if const.INTERACTIVE_MODE and const.INFLUENCE_PROB == 1:
        # Convince other players to vote with you.
        logger.info(
            "\nAll players trust you. Who should everyone vote for? "
            "(If you think there are no Wolves, vote for yourself.)"
        )
        vote_ind = get_player(is_user=True)
        if vote_ind == const.IS_USER.index(True):
            wolf_votes = [1] * const.NUM_PLAYERS
            player_votes = [
                (i + 1) % const.NUM_PLAYERS for i in range(const.NUM_PLAYERS)
            ]
        else:
            wolf_votes[vote_ind] += const.NUM_PLAYERS
            player_votes = [vote_ind] * const.NUM_PLAYERS
    else:
        player_votes = []
        for i, prediction in enumerate(all_predictions):
            vote_ind = player_objs[i].vote(prediction)
            wolf_votes[vote_ind] += 1
            player_votes.append(vote_ind)

    assert len(player_votes) == const.NUM_PLAYERS
    logger.info(f"\nVote Array: {wolf_votes}\n")
    [(avg_role_guesses, _)] = Counter(all_predictions).most_common(1)
    max_votes = max(wolf_votes)
    guessed_wolf_inds = [i for i, count in enumerate(wolf_votes) if count == max_votes]
    return avg_role_guesses, tuple(guessed_wolf_inds), tuple(player_votes)
Example #14
0
    def awake_init(cls, player_index: int,
                   game_roles: list[Role]) -> Doppelganger:
        """Initializes Doppelganger - learns new role."""
        from wolfbot.roles import get_role_obj

        is_user = const.IS_USER[player_index]
        choice_ind = get_player(is_user, (player_index, ))
        choice_char = game_roles[choice_ind]
        logger.debug(f"[Hidden] Doppelganger copies Player {choice_ind} "
                     f"and becomes a {choice_char}.")
        if is_user:
            logger.info(
                f"You copied Player {choice_ind} and are now a {choice_char}!",
                cache=True,
            )
        # Temporarily set Doppelganger in game_roles to the new role
        # so she wakes up with the other characters.
        game_roles[player_index] = choice_char
        if choice_char in (Role.WOLF, Role.MASON):
            get_role_obj(choice_char).awake_init(player_index, game_roles)

        # else do switches later
        return cls(player_index, choice_ind, choice_char)
Example #15
0
    def awake_init(cls, player_index: int, game_roles: list[Role]) -> Wolf:
        """
        Constructor: original_roles defaults to [] when a player becomes
        a Wolf and realizes it.
        Initializes Wolf - gets Wolf indices and a random center card, if applicable.
        """
        is_user = const.IS_USER[player_index]
        center_index, center_role = None, None
        wolf_indices = find_all_player_indices(game_roles, Role.WOLF)
        if len(wolf_indices) == 1 and const.NUM_CENTER > 0:
            center_index = get_center(is_user)
            center_role = game_roles[center_index]
            if is_user:
                logger.info(
                    f"You see Center {center_index - const.NUM_PLAYERS} "
                    f"is a {center_role}.",
                    cache=True,
                )
        logger.debug(f"[Hidden] Wolves are at indices: {list(wolf_indices)}")
        if is_user:
            logger.info(f"Wolves are at indices: {list(wolf_indices)}",
                        cache=True)

        return cls(player_index, wolf_indices, center_index, center_role)
Example #16
0
def eval_winning_team(
    game_roles: tuple[Role, ...],
    guessed_wolf_inds: list[int],
    player_votes: tuple[int, ...],
) -> Team:
    """Decide which team won based on the final vote."""
    killed_wolf, killed_tanner, villager_win = False, False, False
    if len(guessed_wolf_inds) == const.NUM_PLAYERS:
        logger.info("No wolves were found.")
        if final_wolf_inds := find_all_player_indices(game_roles, Role.WOLF):
            logger.info(f"But Player(s) {list(final_wolf_inds)} was a Wolf!\n")
        else:
            logger.info("That was correct!\n")
            villager_win = True
Example #17
0
    def awake_init(cls, player_index: int, game_roles: list[Role]) -> Seer:
        """Initializes Seer - either sees 2 center cards or 1 player card."""
        is_user = const.IS_USER[player_index]
        if const.NUM_CENTER > 1:
            if is_user:
                logger.info("Do you want to see 1 player card or 2 center cards?")
                choose_center = bool(get_numeric_input(1, 3) - 1)
            else:
                # Pick two center cards more often, because
                # that generally yields higher win rates.
                choose_center = weighted_coin_flip(const.CENTER_SEER_PROB)

            if choose_center:
                peek_ind1 = get_center(is_user)
                peek_ind2 = get_center(is_user, (peek_ind1,))
                peek_char1 = game_roles[peek_ind1]
                peek_char2 = game_roles[peek_ind2]
                logger.debug(
                    f"[Hidden] Seer sees that Center {peek_ind1 - const.NUM_PLAYERS} "
                    f"is a {peek_char1}, Center {peek_ind2 - const.NUM_PLAYERS} "
                    f"is a {peek_char2}."
                )
                if is_user:
                    logger.info(
                        f"You see that Center {peek_ind1 - const.NUM_PLAYERS} "
                        f"is a {peek_char1}, and "
                        f"Center {peek_ind2 - const.NUM_PLAYERS} is a {peek_char2}.",
                        cache=True,
                    )
                return cls(
                    player_index, (peek_ind1, peek_char1), (peek_ind2, peek_char2)
                )

        peek_ind = get_player(is_user, (player_index,))
        peek_char = game_roles[peek_ind]
        logger.debug(f"[Hidden] Seer sees that Player {peek_ind} is a {peek_char}.")
        if is_user:
            logger.info(f"You see that Player {peek_ind} is a {peek_char}.", cache=True)
        return cls(player_index, (peek_ind, peek_char))
Example #18
0
    player_votes: tuple[int, ...],
) -> Team:
    """Decide which team won based on the final vote."""
    killed_wolf, killed_tanner, villager_win = False, False, False
    if len(guessed_wolf_inds) == const.NUM_PLAYERS:
        logger.info("No wolves were found.")
        if final_wolf_inds := find_all_player_indices(game_roles, Role.WOLF):
            logger.info(f"But Player(s) {list(final_wolf_inds)} was a Wolf!\n")
        else:
            logger.info("That was correct!\n")
            villager_win = True
    else:
        # Hunter kills the player he voted for if he dies.
        for i in guessed_wolf_inds:
            logger.info(
                f"Player {i} was chosen as a Wolf.\nPlayer {i} was a {game_roles[i]}!\n"
            )
            if game_roles[i] is Role.HUNTER:
                if player_votes[i] not in guessed_wolf_inds:
                    guessed_wolf_inds.append(player_votes[i])
                logger.info(
                    f"(Player {i}) Hunter died and killed "
                    f"Player {player_votes[i]} too!\n"
                )
            elif game_roles[i] is Role.WOLF:
                killed_wolf = True
            elif game_roles[i] is Role.TANNER:
                killed_tanner = True

    if villager_win or killed_wolf:
        logger.info("Village Team wins!")
Example #19
0
 def print_cache() -> None:
     """Clears console and then output all lines stored in the logging cache."""
     logger.info(CLEAR_TERMINAL)
     for log_level, line in logger.output_cache:
         logger.log(log_level, line)
Example #20
0
def night_falls(
    original_roles: tuple[Role, ...]
) -> tuple[tuple[Player, ...], tuple[Role, ...]]:
    """
    Initialize role object list and perform all switching and peeking actions.

    Roles that find other roles go first in AWAKE_ORDER (e.g. Masons), which
    means we don't need to pass in original_roles to these constructors.
    The Doppelganger can take actions after these role types complete.
    """
    logger.info("\n-- NIGHT FALLS --\n")
    print_roles(original_roles, "Hidden")

    # Awaken each player in order and initialize the Player object.
    game_roles = list(original_roles)
    player_objs = cast(list[Player], [None] * const.NUM_PLAYERS)

    for awaken_role in const.AWAKE_ORDER:
        if awaken_role is Role.NONE and Role.DOPPELGANGER in const.ROLE_SET:
            logger.info("Doppelganger, wake up again!")
            # Revert Doppelganger roles back to normal, and perform any actions now.
            for i in range(const.NUM_PLAYERS):
                if original_roles[i] is Role.DOPPELGANGER:
                    game_roles[i] = Role.DOPPELGANGER
                    if player_objs[i].new_role in const.AWAKE_ORDER[5:-1]:
                        role_obj = get_role_obj(player_objs[i].new_role)
                        _ = role_obj.awake_init(i, game_roles)  # TODO: save this info
            logger.info("Doppelganger, go back to sleep.\n")

        if awaken_role in const.ROLE_SET:
            logger.info(f"{awaken_role}, wake up.")
            role_obj = get_role_obj(awaken_role)
            for i in range(const.NUM_PLAYERS):
                if original_roles[i] is awaken_role:
                    player_objs[i] = role_obj.awake_init(i, game_roles)
            logger.info(f"{awaken_role}, go to sleep.\n")

    # All other players wake up at the same time.
    logger.info("Everyone, wake up!\n")
    for i, role_name in enumerate(original_roles[: const.NUM_PLAYERS]):
        if role_name in const.ROLE_SET - set(const.AWAKE_ORDER):
            role_obj = get_role_obj(role_name)
            player_objs[i] = role_obj.awake_init(i, game_roles)

    UserState.night_falls()
    logger.info("\n-- GAME BEGINS --\n")
    return tuple(player_objs[: const.NUM_PLAYERS]), tuple(game_roles)