Ejemplo n.º 1
0
def start(var, wrapper, *, forced=False, restart=""):
    if (not forced and LAST_START and wrapper.source in LAST_START and
            LAST_START[wrapper.source][0] + timedelta(seconds=var.START_RATE_LIMIT) >
            datetime.now() and not restart):
        LAST_START[wrapper.source][1] += 1
        wrapper.source.send(messages["command_ratelimited"])
        return

    if restart:
        global RESTART_TRIES
        RESTART_TRIES += 1
    if RESTART_TRIES > MAX_RETRIES:
        from src.wolfgame import stop_game
        stop_game(var, abort=True)
        return

    if not restart:
        LAST_START[wrapper.source] = [datetime.now(), 1]

    villagers = get_players()
    vils = set(get_players())

    if not restart:
        if var.PHASE == "none":
            wrapper.source.send(messages["no_game_running"].format(botconfig.CMD_CHAR))
            return
        if var.PHASE != "join":
            wrapper.source.send(messages["werewolf_already_running"])
            return
        if wrapper.source not in villagers and not forced:
            return

        now = datetime.now()
        var.GAME_START_TIME = now  # Only used for the idler checker
        dur = int((var.CAN_START_TIME - now).total_seconds())
        if dur > 0 and not forced:
            plural = "" if dur == 1 else "s"
            wrapper.send(messages["please_wait"].format(dur, plural))
            return

        if len(villagers) < var.MIN_PLAYERS:
            wrapper.send(messages["not_enough_players"].format(wrapper.source, var.MIN_PLAYERS))
            return

        if len(villagers) > var.MAX_PLAYERS:
            wrapper.send.send(messages["max_players"].format(wrapper.source, var.MAX_PLAYERS))
            return

        with var.WARNING_LOCK:
            if not forced and wrapper.source in START_VOTES:
                wrapper.pm(messages["start_already_voted"])
                return

            start_votes_required = min(math.ceil(len(villagers) * var.START_VOTES_SCALE), var.START_VOTES_MAX)
            if not forced and len(START_VOTES) < start_votes_required:
                # If there's only one more vote required, start the game immediately.
                # Checked here to make sure that a player that has already voted can't
                # vote again for the final start.
                if len(START_VOTES) < start_votes_required - 1:
                    START_VOTES.add(wrapper.source)
                    remaining_votes = start_votes_required - len(START_VOTES)
                    word = "vote" if remaining_votes == 1 else "votes"
                    wrapper.send(messages["start_voted"].format(wrapper.source, remaining_votes, word))

                    # If this was the first vote
                    if len(START_VOTES) == 1:
                        t = threading.Timer(60, expire_start_votes, (var, wrapper.target))
                        var.TIMERS["start_votes"] = (t, time.time(), 60)
                        t.daemon = True
                        t.start()
                    return

        if not var.FGAMED:
            votes = {} #key = gamemode, not hostmask
            for gamemode in var.GAMEMODE_VOTES.values():
                if len(villagers) >= var.GAME_MODES[gamemode][1] and len(villagers) <= var.GAME_MODES[gamemode][2]:
                    votes[gamemode] = votes.get(gamemode, 0) + 1
            voted = [gamemode for gamemode in votes if votes[gamemode] == max(votes.values()) and votes[gamemode] >= len(villagers)/2]
            if voted:
                from src.wolfgame import cgamemode
                cgamemode(random.choice(voted))
            else:
                possiblegamemodes = []
                numvotes = 0
                for gamemode, num in votes.items():
                    if len(villagers) < var.GAME_MODES[gamemode][1] or len(villagers) > var.GAME_MODES[gamemode][2] or var.GAME_MODES[gamemode][3] == 0:
                        continue
                    possiblegamemodes += [gamemode] * num
                    numvotes += num
                if len(villagers) - numvotes > 0:
                    possiblegamemodes += [None] * ((len(villagers) - numvotes) // 2)
                # check if we go with a voted mode or a random mode
                gamemode = random.choice(possiblegamemodes)
                if gamemode is None:
                    possiblegamemodes = []
                    for gamemode in var.GAME_MODES.keys() - var.DISABLED_GAMEMODES:
                        if len(villagers) >= var.GAME_MODES[gamemode][1] and len(villagers) <= var.GAME_MODES[gamemode][2] and var.GAME_MODES[gamemode][3] > 0:
                            possiblegamemodes += [gamemode] * var.GAME_MODES[gamemode][3]
                    gamemode = random.choice(possiblegamemodes)
                from src.wolfgame import cgamemode
                cgamemode(gamemode)

    else:
        from src.wolfgame import cgamemode
        cgamemode(restart)
        var.GAME_ID = time.time() # restart reaper timer

    from src.wolfgame import chk_win_conditions # TODO: Move that into its own postgame module
    event = Event("role_attribution", {"addroles": Counter()})
    if event.dispatch(var, chk_win_conditions, villagers):
        addroles = event.data["addroles"]
        strip = lambda x: re.sub("\(.*\)", "", x)
        lv = len(villagers)
        roles = []
        for num, rolelist in var.CURRENT_GAMEMODE.ROLE_GUIDE.items():
            if num <= lv:
                roles.extend(rolelist)
        defroles = Counter(strip(x) for x in roles)
        for role, count in list(defroles.items()):
            if role[0] == "-":
                srole = role[1:]
                defroles[srole] -= count
                del defroles[role]
                if defroles[srole] == 0:
                    del defroles[srole]
        if not defroles:
            wrapper.send(messages["no_settings_defined"].format(wrapper.source, lv))
            return
        for role, num in defroles.items():
            addroles[role] = max(addroles.get(role, num), len(var.FORCE_ROLES.get(role, ())))
        if sum([addroles[r] for r in addroles if r not in var.CURRENT_GAMEMODE.SECONDARY_ROLES]) > lv:
            wrapper.send(messages["too_many_roles"])
            return
        for role in All:
            addroles.setdefault(role, 0)
    else:
        addroles = event.data["addroles"]

    # convert roleset aliases into the appropriate roles
    possible_rolesets = [Counter()]
    roleset_roles = defaultdict(int)
    for role, amt in list(addroles.items()):
        # not a roleset? add a fixed amount of them
        if role not in var.CURRENT_GAMEMODE.ROLE_SETS:
            for pr in possible_rolesets:
                pr[role] += amt
            continue
        # if a roleset, ensure we don't try to expose the roleset name in !stats or future attribution
        del addroles[role]
        # init !stats with all 0s so that it can number things properly; the keys need to exist in the Counter
        # across every possible roleset so that !stats works right
        rs = Counter(var.CURRENT_GAMEMODE.ROLE_SETS[role])
        for r in rs:
            for pr in possible_rolesets:
                pr[r] += 0
        toadd = random.sample(list(rs.elements()), amt)
        for r in toadd:
            addroles[r] += 1
            roleset_roles[r] += 1
        add_rolesets = []
        temp_rolesets = []
        for c in itertools.combinations(rs.elements(), amt):
            add_rolesets.append(Counter(c))
        for pr in possible_rolesets:
            for ar in add_rolesets:
                temp = Counter(pr)
                temp.update(ar)
                temp_rolesets.append(temp)
        possible_rolesets = temp_rolesets

    if var.ORIGINAL_SETTINGS and not restart:  # Custom settings
        need_reset = True
        wvs = sum(addroles[r] for r in Wolfchat)
        if len(villagers) < (sum(addroles.values()) - sum(addroles[r] for r in var.CURRENT_GAMEMODE.SECONDARY_ROLES)):
            wrapper.send(messages["too_few_players_custom"])
        elif not wvs and var.CURRENT_GAMEMODE.name != "villagergame":
            wrapper.send(messages["need_one_wolf"])
        elif wvs > (len(villagers) / 2):
            wrapper.send(messages["too_many_wolves"])
        else:
            need_reset = False

        if need_reset:
            from src.wolfgame import reset_settings
            reset_settings()
            wrapper.send(messages["default_reset"].format(botconfig.CMD_CHAR))
            var.PHASE = "join"
            return

    if var.ADMIN_TO_PING is not None and not restart:
        for decor in (COMMANDS["join"] + COMMANDS["start"]):
            decor(_command_disabled)

    var.ROLES.clear()
    var.MAIN_ROLES.clear()
    var.NIGHT_COUNT = 0
    var.DAY_COUNT = 0
    var.TRAITOR_TURNED = False
    var.FINAL_ROLES = {}
    var.EXTRA_WOLVES = 0

    var.DEADCHAT_PLAYERS.clear()
    var.SPECTATING_WOLFCHAT.clear()
    var.SPECTATING_DEADCHAT.clear()

    for role in All:
        var.ROLES[role] = UserSet()
    var.ROLES[var.DEFAULT_ROLE] = UserSet()
    for role, ps in var.FORCE_ROLES.items():
        if role not in var.CURRENT_GAMEMODE.SECONDARY_ROLES.keys():
            vils.difference_update(ps)

    for role, count in addroles.items():
        if role in var.CURRENT_GAMEMODE.SECONDARY_ROLES:
            var.ROLES[role] = (None,) * count
            continue # We deal with those later, see below

        to_add = set()

        if role in var.FORCE_ROLES:
            if len(var.FORCE_ROLES[role]) > count:
                channels.Main.send(messages["error_frole_too_many"].format(role))
                return
            for user in var.FORCE_ROLES[role]:
                # If multiple main roles were forced, only first one is put in MAIN_ROLES
                if not user in var.MAIN_ROLES:
                    var.MAIN_ROLES[user] = role
                var.ORIGINAL_MAIN_ROLES[user] = role
                to_add.add(user)
                count -= 1

        selected = random.sample(vils, count)
        for x in selected:
            var.MAIN_ROLES[x] = role
            var.ORIGINAL_MAIN_ROLES[x] = role
            vils.remove(x)
        var.ROLES[role].update(selected)
        var.ROLES[role].update(to_add)
    var.ROLES[var.DEFAULT_ROLE].update(vils)
    for x in vils:
        var.MAIN_ROLES[x] = var.DEFAULT_ROLE
        var.ORIGINAL_MAIN_ROLES[x] = var.DEFAULT_ROLE
    if vils:
        for pr in possible_rolesets:
            pr[var.DEFAULT_ROLE] += len(vils)

    # Collapse possible_rolesets into var.ROLE_STATS
    # which is a FrozenSet[FrozenSet[Tuple[str, int]]]
    possible_rolesets_set = set()
    event = Event("reconfigure_stats", {"new": []})
    for pr in possible_rolesets:
        event.data["new"] = [pr]
        event.dispatch(var, pr, "start")
        for v in event.data["new"]:
            if min(v.values()) >= 0:
                possible_rolesets_set.add(frozenset(v.items()))
    var.ROLE_STATS = frozenset(possible_rolesets_set)

    # Now for the secondary roles
    for role, dfn in var.CURRENT_GAMEMODE.SECONDARY_ROLES.items():
        count = len(var.ROLES[role])
        var.ROLES[role] = UserSet()
        if role in var.FORCE_ROLES:
            ps = var.FORCE_ROLES[role]
            var.ROLES[role].update(ps)
            count -= len(ps)
        # Don't do anything further if this secondary role was forced on enough players already
        if count <= 0:
            continue
        possible = get_players(dfn)
        if len(possible) < count:
            wrapper.send(messages["not_enough_targets"].format(role))
            if var.ORIGINAL_SETTINGS:
                from src.wolfgame import reset_settings
                var.ROLES.clear()
                var.ROLES["person"] = UserSet(var.ALL_PLAYERS)
                reset_settings()
                wrapper.send(messages["default_reset"].format(botconfig.CMD_CHAR))
                var.PHASE = "join"
                return
            else:
                wrapper.send(messages["role_skipped"])
                continue
        var.ROLES[role].update(x for x in random.sample(possible, count))

    with var.WARNING_LOCK: # cancel timers
        for name in ("join", "join_pinger", "start_votes"):
            if name in var.TIMERS:
                var.TIMERS[name][0].cancel()
                del var.TIMERS[name]

    var.LAST_STATS = None
    var.LAST_TIME = None

    for role, players in var.ROLES.items():
        for player in players:
            evt = Event("new_role", {"messages": [], "role": role, "in_wolfchat": False}, inherit_from=None)
            evt.dispatch(var, player, None)

    if not restart:
        gamemode = var.CURRENT_GAMEMODE.name
        if gamemode == "villagergame":
            gamemode = "default"

        # Alert the players to option changes they may not be aware of
        options = []
        if var.ORIGINAL_SETTINGS.get("ROLE_REVEAL") is not None:
            if var.ROLE_REVEAL == "on":
                options.append("role reveal")
            elif var.ROLE_REVEAL == "team":
                options.append("team reveal")
            elif var.ROLE_REVEAL == "off":
                options.append("no role reveal")
        if var.ORIGINAL_SETTINGS.get("STATS_TYPE") is not None:
            if var.STATS_TYPE == "disabled":
                options.append("no stats")
            else:
                options.append("{0} stats".format(var.STATS_TYPE))
        if var.ORIGINAL_SETTINGS.get("ABSTAIN_ENABLED") is not None or var.ORIGINAL_SETTINGS.get("LIMIT_ABSTAIN") is not None:
            if var.ABSTAIN_ENABLED and var.LIMIT_ABSTAIN:
                options.append("restricted abstaining")
            elif var.ABSTAIN_ENABLED:
                options.append("unrestricted abstaining")
            else:
                options.append("no abstaining")

        if len(options) > 2:
            options = " with {0}, and {1}".format(", ".join(options[:-1]), options[-1])
        elif len(options) == 2:
            options = " with {0} and {1}".format(options[0], options[1])
        elif len(options) == 1:
            options = " with {0}".format(options[0])
        else:
            options = ""

        wrapper.send(messages["welcome"].format(", ".join(x.nick for x in villagers), gamemode, options))
        wrapper.target.mode("+m")

    var.ORIGINAL_ROLES.clear()
    for role, players in var.ROLES.items():
        var.ORIGINAL_ROLES[role] = players.copy()

    var.DAY_TIMEDELTA = timedelta(0)
    var.NIGHT_TIMEDELTA = timedelta(0)
    var.DAY_START_TIME = datetime.now()
    var.NIGHT_START_TIME = datetime.now()
    var.LAST_PING = None

    var.PLAYERS = {plr:dict(var.USERS[plr.nick]) for plr in villagers if plr.nick in var.USERS} # FIXME: Please kill this

    if restart:
        var.PHASE = "join" # allow transition_* to run properly if game was restarted on first night
    if not var.START_WITH_DAY:
        from src.wolfgame import transition_night
        var.GAMEPHASE = "night"
        transition_night()
    else:
        from src.wolfgame import transition_day
        var.FIRST_DAY = True
        var.GAMEPHASE = "day"
        transition_day()

    decrement_stasis()

    if not (botconfig.DEBUG_MODE and var.DISABLE_DEBUG_MODE_REAPER):
        # DEATH TO IDLERS!
        from src.wolfgame import reaper
        reapertimer = threading.Thread(None, reaper, args=(wrapper.client, var.GAME_ID))
        reapertimer.daemon = True
        reapertimer.start()
Ejemplo n.º 2
0
def start(var, wrapper, *, forced=False, restart=""):
    if (not forced and LAST_START and wrapper.source in LAST_START
            and LAST_START[wrapper.source][0] +
            timedelta(seconds=var.START_RATE_LIMIT) > datetime.now()
            and not restart):
        LAST_START[wrapper.source][1] += 1
        wrapper.source.send(messages["command_ratelimited"])
        return

    if restart:
        global RESTART_TRIES
        RESTART_TRIES += 1
    if RESTART_TRIES > MAX_RETRIES:
        from src.wolfgame import stop_game
        stop_game(var, abort=True)
        return

    if not restart:
        LAST_START[wrapper.source] = [datetime.now(), 1]

    villagers = get_players()
    vils = set(get_players())

    if not restart:
        if var.PHASE == "none":
            wrapper.source.send(messages["no_game_running"].format(
                botconfig.CMD_CHAR))
            return
        if var.PHASE != "join":
            wrapper.source.send(messages["werewolf_already_running"])
            return
        if wrapper.source not in villagers and not forced:
            return

        now = datetime.now()
        var.GAME_START_TIME = now  # Only used for the idler checker
        dur = int((var.CAN_START_TIME - now).total_seconds())
        if dur > 0 and not forced:
            plural = "" if dur == 1 else "s"
            wrapper.send(messages["please_wait"].format(dur, plural))
            return

        if len(villagers) < var.MIN_PLAYERS:
            wrapper.send(messages["not_enough_players"].format(
                wrapper.source, var.MIN_PLAYERS))
            return

        if len(villagers) > var.MAX_PLAYERS:
            wrapper.send.send(messages["max_players"].format(
                wrapper.source, var.MAX_PLAYERS))
            return

        with var.WARNING_LOCK:
            if not forced and wrapper.source in START_VOTES:
                wrapper.pm(messages["start_already_voted"])
                return

            start_votes_required = min(
                math.ceil(len(villagers) * var.START_VOTES_SCALE),
                var.START_VOTES_MAX)
            if not forced and len(START_VOTES) < start_votes_required:
                # If there's only one more vote required, start the game immediately.
                # Checked here to make sure that a player that has already voted can't
                # vote again for the final start.
                if len(START_VOTES) < start_votes_required - 1:
                    START_VOTES.add(wrapper.source)
                    remaining_votes = start_votes_required - len(START_VOTES)
                    word = "vote" if remaining_votes == 1 else "votes"
                    wrapper.send(messages["start_voted"].format(
                        wrapper.source, remaining_votes, word))

                    # If this was the first vote
                    if len(START_VOTES) == 1:
                        t = threading.Timer(60, expire_start_votes,
                                            (var, wrapper.target))
                        var.TIMERS["start_votes"] = (t, time.time(), 60)
                        t.daemon = True
                        t.start()
                    return

        if not var.FGAMED:
            votes = {}  #key = gamemode, not hostmask
            for gamemode in var.GAMEMODE_VOTES.values():
                if len(villagers) >= var.GAME_MODES[gamemode][1] and len(
                        villagers) <= var.GAME_MODES[gamemode][2]:
                    votes[gamemode] = votes.get(gamemode, 0) + 1
            voted = [
                gamemode for gamemode in votes
                if votes[gamemode] == max(votes.values())
                and votes[gamemode] >= len(villagers) / 2
            ]
            if voted:
                from src.wolfgame import cgamemode
                cgamemode(random.choice(voted))
            else:
                possiblegamemodes = []
                numvotes = 0
                for gamemode, num in votes.items():
                    if len(villagers) < var.GAME_MODES[gamemode][1] or len(
                            villagers) > var.GAME_MODES[gamemode][
                                2] or var.GAME_MODES[gamemode][3] == 0:
                        continue
                    possiblegamemodes += [gamemode] * num
                    numvotes += num
                if len(villagers) - numvotes > 0:
                    possiblegamemodes += [None] * (
                        (len(villagers) - numvotes) // 2)
                # check if we go with a voted mode or a random mode
                gamemode = random.choice(possiblegamemodes)
                if gamemode is None:
                    possiblegamemodes = []
                    for gamemode in var.GAME_MODES.keys(
                    ) - var.DISABLED_GAMEMODES:
                        if len(villagers
                               ) >= var.GAME_MODES[gamemode][1] and len(
                                   villagers) <= var.GAME_MODES[gamemode][
                                       2] and var.GAME_MODES[gamemode][3] > 0:
                            possiblegamemodes += [
                                gamemode
                            ] * var.GAME_MODES[gamemode][3]
                    gamemode = random.choice(possiblegamemodes)
                from src.wolfgame import cgamemode
                cgamemode(gamemode)

    else:
        from src.wolfgame import cgamemode
        cgamemode(restart)
        var.GAME_ID = time.time()  # restart reaper timer

    from src.wolfgame import chk_win_conditions  # TODO: Move that into its own postgame module
    event = Event("role_attribution", {"addroles": Counter()})
    if event.dispatch(var, chk_win_conditions, villagers):
        addroles = event.data["addroles"]
        strip = lambda x: re.sub("\(.*\)", "", x)
        lv = len(villagers)
        roles = []
        for num, rolelist in var.CURRENT_GAMEMODE.ROLE_GUIDE.items():
            if num <= lv:
                roles.extend(rolelist)
        defroles = Counter(strip(x) for x in roles)
        for role, count in list(defroles.items()):
            if role[0] == "-":
                srole = role[1:]
                defroles[srole] -= count
                del defroles[role]
                if defroles[srole] == 0:
                    del defroles[srole]
        if not defroles:
            wrapper.send(messages["no_settings_defined"].format(
                wrapper.source, lv))
            return
        for role, num in defroles.items():
            addroles[role] = max(addroles.get(role, num),
                                 len(var.FORCE_ROLES.get(role, ())))
        if sum([
                addroles[r] for r in addroles
                if r not in var.CURRENT_GAMEMODE.SECONDARY_ROLES
        ]) > lv:
            wrapper.send(messages["too_many_roles"])
            return
        for role in All:
            addroles.setdefault(role, 0)
    else:
        addroles = event.data["addroles"]

    # convert roleset aliases into the appropriate roles
    possible_rolesets = [Counter()]
    roleset_roles = defaultdict(int)
    for role, amt in list(addroles.items()):
        # not a roleset? add a fixed amount of them
        if role not in var.CURRENT_GAMEMODE.ROLE_SETS:
            for pr in possible_rolesets:
                pr[role] += amt
            continue
        # if a roleset, ensure we don't try to expose the roleset name in !stats or future attribution
        del addroles[role]
        # init !stats with all 0s so that it can number things properly; the keys need to exist in the Counter
        # across every possible roleset so that !stats works right
        rs = Counter(var.CURRENT_GAMEMODE.ROLE_SETS[role])
        for r in rs:
            for pr in possible_rolesets:
                pr[r] += 0
        toadd = random.sample(list(rs.elements()), amt)
        for r in toadd:
            addroles[r] += 1
            roleset_roles[r] += 1
        add_rolesets = []
        temp_rolesets = []
        for c in itertools.combinations(rs.elements(), amt):
            add_rolesets.append(Counter(c))
        for pr in possible_rolesets:
            for ar in add_rolesets:
                temp = Counter(pr)
                temp.update(ar)
                temp_rolesets.append(temp)
        possible_rolesets = temp_rolesets

    if var.ORIGINAL_SETTINGS and not restart:  # Custom settings
        need_reset = True
        wvs = sum(addroles[r] for r in Wolfchat)
        if len(villagers) < (sum(addroles.values()) - sum(
                addroles[r] for r in var.CURRENT_GAMEMODE.SECONDARY_ROLES)):
            wrapper.send(messages["too_few_players_custom"])
        elif not wvs and var.CURRENT_GAMEMODE.name != "villagergame":
            wrapper.send(messages["need_one_wolf"])
        elif wvs > (len(villagers) / 2):
            wrapper.send(messages["too_many_wolves"])
        else:
            need_reset = False

        if need_reset:
            from src.wolfgame import reset_settings
            reset_settings()
            wrapper.send(messages["default_reset"].format(botconfig.CMD_CHAR))
            var.PHASE = "join"
            return

    if var.ADMIN_TO_PING is not None and not restart:
        for decor in (COMMANDS["join"] + COMMANDS["start"]):
            decor(_command_disabled)

    var.ROLES.clear()
    var.MAIN_ROLES.clear()
    var.NIGHT_COUNT = 0
    var.DAY_COUNT = 0
    var.TRAITOR_TURNED = False
    var.FINAL_ROLES = {}
    var.EXTRA_WOLVES = 0

    var.DEADCHAT_PLAYERS.clear()
    var.SPECTATING_WOLFCHAT.clear()
    var.SPECTATING_DEADCHAT.clear()

    for role in All:
        var.ROLES[role] = UserSet()
    var.ROLES[var.DEFAULT_ROLE] = UserSet()
    for role, ps in var.FORCE_ROLES.items():
        if role not in var.CURRENT_GAMEMODE.SECONDARY_ROLES.keys():
            vils.difference_update(ps)

    for role, count in addroles.items():
        if role in var.CURRENT_GAMEMODE.SECONDARY_ROLES:
            var.ROLES[role] = (None, ) * count
            continue  # We deal with those later, see below

        to_add = set()

        if role in var.FORCE_ROLES:
            if len(var.FORCE_ROLES[role]) > count:
                channels.Main.send(
                    messages["error_frole_too_many"].format(role))
                return
            for user in var.FORCE_ROLES[role]:
                # If multiple main roles were forced, only first one is put in MAIN_ROLES
                if not user in var.MAIN_ROLES:
                    var.MAIN_ROLES[user] = role
                var.ORIGINAL_MAIN_ROLES[user] = role
                to_add.add(user)
                count -= 1

        selected = random.sample(vils, count)
        for x in selected:
            var.MAIN_ROLES[x] = role
            var.ORIGINAL_MAIN_ROLES[x] = role
            vils.remove(x)
        var.ROLES[role].update(selected)
        var.ROLES[role].update(to_add)
    var.ROLES[var.DEFAULT_ROLE].update(vils)
    for x in vils:
        var.MAIN_ROLES[x] = var.DEFAULT_ROLE
        var.ORIGINAL_MAIN_ROLES[x] = var.DEFAULT_ROLE
    if vils:
        for pr in possible_rolesets:
            pr[var.DEFAULT_ROLE] += len(vils)

    # Collapse possible_rolesets into var.ROLE_STATS
    # which is a FrozenSet[FrozenSet[Tuple[str, int]]]
    possible_rolesets_set = set()
    event = Event("reconfigure_stats", {"new": []})
    for pr in possible_rolesets:
        event.data["new"] = [pr]
        event.dispatch(var, pr, "start")
        for v in event.data["new"]:
            if min(v.values()) >= 0:
                possible_rolesets_set.add(frozenset(v.items()))
    var.ROLE_STATS = frozenset(possible_rolesets_set)

    # Now for the secondary roles
    for role, dfn in var.CURRENT_GAMEMODE.SECONDARY_ROLES.items():
        count = len(var.ROLES[role])
        var.ROLES[role] = UserSet()
        if role in var.FORCE_ROLES:
            ps = var.FORCE_ROLES[role]
            var.ROLES[role].update(ps)
            count -= len(ps)
        # Don't do anything further if this secondary role was forced on enough players already
        if count <= 0:
            continue
        possible = get_players(dfn)
        if len(possible) < count:
            wrapper.send(messages["not_enough_targets"].format(role))
            if var.ORIGINAL_SETTINGS:
                from src.wolfgame import reset_settings
                var.ROLES.clear()
                var.ROLES["person"] = UserSet(var.ALL_PLAYERS)
                reset_settings()
                wrapper.send(messages["default_reset"].format(
                    botconfig.CMD_CHAR))
                var.PHASE = "join"
                return
            else:
                wrapper.send(messages["role_skipped"])
                continue
        var.ROLES[role].update(x for x in random.sample(possible, count))

    with var.WARNING_LOCK:  # cancel timers
        for name in ("join", "join_pinger", "start_votes"):
            if name in var.TIMERS:
                var.TIMERS[name][0].cancel()
                del var.TIMERS[name]

    var.LAST_STATS = None
    var.LAST_TIME = None

    for role, players in var.ROLES.items():
        for player in players:
            evt = Event("new_role", {
                "messages": [],
                "role": role,
                "in_wolfchat": False
            },
                        inherit_from=None)
            evt.dispatch(var, player, None)

    if not restart:
        gamemode = var.CURRENT_GAMEMODE.name
        if gamemode == "villagergame":
            gamemode = "default"

        # Alert the players to option changes they may not be aware of
        options = []
        if var.ORIGINAL_SETTINGS.get("ROLE_REVEAL") is not None:
            if var.ROLE_REVEAL == "on":
                options.append("role reveal")
            elif var.ROLE_REVEAL == "team":
                options.append("team reveal")
            elif var.ROLE_REVEAL == "off":
                options.append("no role reveal")
        if var.ORIGINAL_SETTINGS.get("STATS_TYPE") is not None:
            if var.STATS_TYPE == "disabled":
                options.append("no stats")
            else:
                options.append("{0} stats".format(var.STATS_TYPE))
        if var.ORIGINAL_SETTINGS.get(
                "ABSTAIN_ENABLED") is not None or var.ORIGINAL_SETTINGS.get(
                    "LIMIT_ABSTAIN") is not None:
            if var.ABSTAIN_ENABLED and var.LIMIT_ABSTAIN:
                options.append("restricted abstaining")
            elif var.ABSTAIN_ENABLED:
                options.append("unrestricted abstaining")
            else:
                options.append("no abstaining")

        if len(options) > 2:
            options = " with {0}, and {1}".format(", ".join(options[:-1]),
                                                  options[-1])
        elif len(options) == 2:
            options = " with {0} and {1}".format(options[0], options[1])
        elif len(options) == 1:
            options = " with {0}".format(options[0])
        else:
            options = ""

        wrapper.send(messages["welcome"].format(
            ", ".join(x.nick for x in villagers), gamemode, options))
        wrapper.target.mode("+m")

    var.ORIGINAL_ROLES.clear()
    for role, players in var.ROLES.items():
        var.ORIGINAL_ROLES[role] = players.copy()

    var.DAY_TIMEDELTA = timedelta(0)
    var.NIGHT_TIMEDELTA = timedelta(0)
    var.DAY_START_TIME = datetime.now()
    var.NIGHT_START_TIME = datetime.now()
    var.LAST_PING = None

    var.PLAYERS = {
        plr: dict(var.USERS[plr.nick])
        for plr in villagers if plr.nick in var.USERS
    }  # FIXME: Please kill this

    if restart:
        var.PHASE = "join"  # allow transition_* to run properly if game was restarted on first night
    if not var.START_WITH_DAY:
        from src.wolfgame import transition_night
        var.GAMEPHASE = "night"
        transition_night()
    else:
        from src.wolfgame import transition_day
        var.FIRST_DAY = True
        var.GAMEPHASE = "day"
        transition_day()

    decrement_stasis()

    if not (botconfig.DEBUG_MODE and var.DISABLE_DEBUG_MODE_REAPER):
        # DEATH TO IDLERS!
        from src.wolfgame import reaper
        reapertimer = threading.Thread(None,
                                       reaper,
                                       args=(wrapper.client, var.GAME_ID))
        reapertimer.daemon = True
        reapertimer.start()
Ejemplo n.º 3
0
def chk_decision(var, *, timeout=False):
    with var.GRAVEYARD_LOCK:
        players = set(get_players()) - get_absent(var)
        avail = len(players)
        needed = avail // 2 + 1

        to_vote = []

        for votee, voters in VOTES.items():
            votes = (set(voters)
                     | get_forced_votes(var, votee)) - get_forced_abstains(var)
            if sum(get_vote_weight(var, x) for x in votes) >= needed:
                to_vote.append(votee)
                break

        behaviour_evt = Event("lynch_behaviour", {
            "num_lynches": 1,
            "kill_ties": False,
            "force": timeout
        },
                              votes=VOTES,
                              players=avail)
        behaviour_evt.dispatch(var)

        num_lynches = behaviour_evt.data["num_lynches"]
        kill_ties = behaviour_evt.data["kill_ties"]
        force = behaviour_evt.data["force"]

        abstaining = False
        if not to_vote:
            if len(ABSTAINS | get_forced_abstains(var)) >= avail / 2:
                abstaining = True
            elif force:
                voting = []
                if VOTES:
                    plurality = [(x, len(y)) for x, y in VOTES.items()]
                    plurality.sort(key=lambda x: x[1])
                    votee, value = plurality.pop()
                    max_value = value
                    # Fetch all of the highest ties, exit out if we find someone lower
                    # If everyone is tied, then at some point plurality will be empty,
                    # but the values will still be at the max. Everything's fine, just break
                    while value == max_value:
                        voting.append(votee)
                        if not plurality:
                            break
                        votee, value = plurality.pop()

                if len(voting) == 1:
                    to_vote.append(voting[0])
                elif voting and kill_ties:
                    if set(voting) == set(
                            get_players()
                    ):  # killing everyone off? have you considered not doing that
                        abstaining = True
                    else:
                        to_vote.extend(voting)
                else:
                    abstaining = True

        if abstaining:
            for forced_abstainer in get_forced_abstains(var):
                if forced_abstainer not in ABSTAINS:  # did not explicitly abstain
                    channels.Main.send(messages["player_meek_abstain"].format(
                        forced_abstainer))

            abstain_evt = Event("abstain", {})
            abstain_evt.dispatch(var, ABSTAINS | get_forced_abstains(var))

            global ABSTAINED
            ABSTAINED = True
            channels.Main.send(messages["village_abstain"])

            from src.wolfgame import transition_night
            transition_night()

        if to_vote:
            global LYNCHED
            LYNCHED += len(
                to_vote)  # track how many people we've lynched today

            if timeout:
                channels.Main.send(messages["sunset_lynch"])

            for votee in to_vote:
                voters = list(VOTES[votee])
                for forced_voter in get_forced_votes(var, votee):
                    if forced_voter not in voters:  # did not explicitly vote
                        channels.Main.send(messages["impatient_vote"].format(
                            forced_voter, votee))
                        voters.append(
                            forced_voter
                        )  # they need to be counted as voting for them still

                if not try_lynch_immunity(var, votee):
                    lynch_evt = Event("lynch", {}, players=avail)
                    if lynch_evt.dispatch(var, votee, voters):
                        if var.ROLE_REVEAL in ("on", "team"):
                            rrole = get_reveal_role(votee)
                            an = "n" if rrole.startswith(
                                ("a", "e", "i", "o", "u")) else ""
                            lmsg = random.choice(
                                messages["lynch_reveal"]).format(
                                    votee, an, rrole)
                        else:
                            lmsg = random.choice(
                                messages["lynch_no_reveal"]).format(votee)
                        channels.Main.send(lmsg)
                        add_dying(var, votee, "villager", "lynch")

            kill_players(var, end_game=False)  # FIXME

        elif timeout:
            channels.Main.send(messages["sunset"])

        from src.wolfgame import chk_win
        if chk_win():
            return  # game ended, just exit out

        if timeout or LYNCHED >= num_lynches:
            from src.wolfgame import transition_night
            transition_night()
Ejemplo n.º 4
0
def chk_decision(var, *, timeout=False):
    with var.GRAVEYARD_LOCK:
        players = set(get_players()) - get_absent(var)
        avail = len(players)
        needed = avail // 2 + 1

        to_vote = []

        for votee, voters in VOTES.items():
            votes = (set(voters) | get_forced_votes(var, votee)) - get_forced_abstains(var)
            if sum(get_vote_weight(var, x) for x in votes) >= needed:
                to_vote.append(votee)
                break

        behaviour_evt = Event("lynch_behaviour", {"num_lynches": 1, "kill_ties": False, "force": timeout}, votes=VOTES, players=avail)
        behaviour_evt.dispatch(var)

        num_lynches = behaviour_evt.data["num_lynches"]
        kill_ties = behaviour_evt.data["kill_ties"]
        force = behaviour_evt.data["force"]

        abstaining = False
        if not to_vote:
            if len(ABSTAINS | get_forced_abstains(var)) >= avail / 2:
                abstaining = True
            elif force:
                voting = []
                if VOTES:
                    plurality = [(x, len(y)) for x, y in VOTES.items()]
                    plurality.sort(key=lambda x: x[1])
                    votee, value = plurality.pop()
                    max_value = value
                    # Fetch all of the highest ties, exit out if we find someone lower
                    # If everyone is tied, then at some point plurality will be empty,
                    # but the values will still be at the max. Everything's fine, just break
                    while value == max_value:
                        voting.append(votee)
                        if not plurality:
                            break
                        votee, value = plurality.pop()

                if len(voting) == 1:
                    to_vote.append(voting[0])
                elif voting and kill_ties:
                    if set(voting) == set(get_players()): # killing everyone off? have you considered not doing that
                        abstaining = True
                    else:
                        to_vote.extend(voting)
                else:
                    abstaining = True

        if abstaining:
            for forced_abstainer in get_forced_abstains(var):
                if forced_abstainer not in ABSTAINS: # did not explicitly abstain
                    channels.Main.send(messages["player_meek_abstain"].format(forced_abstainer))

            abstain_evt = Event("abstain", {})
            abstain_evt.dispatch(var, ABSTAINS | get_forced_abstains(var))

            global ABSTAINED
            ABSTAINED = True
            channels.Main.send(messages["village_abstain"])

            from src.wolfgame import transition_night
            transition_night()

        if to_vote:
            global LYNCHED
            LYNCHED += len(to_vote) # track how many people we've lynched today

            if timeout:
                channels.Main.send(messages["sunset_lynch"])

            for votee in to_vote:
                voters = list(VOTES[votee])
                for forced_voter in get_forced_votes(var, votee):
                    if forced_voter not in voters: # did not explicitly vote
                        channels.Main.send(messages["impatient_vote"].format(forced_voter, votee))
                        voters.append(forced_voter) # they need to be counted as voting for them still

                if not try_lynch_immunity(var, votee):
                    lynch_evt = Event("lynch", {}, players=avail)
                    if lynch_evt.dispatch(var, votee, voters):
                        if var.ROLE_REVEAL in ("on", "team"):
                            rrole = get_reveal_role(votee)
                            an = "n" if rrole.startswith(("a", "e", "i", "o", "u")) else ""
                            lmsg = random.choice(messages["lynch_reveal"]).format(votee, an, rrole)
                        else:
                            lmsg = random.choice(messages["lynch_no_reveal"]).format(votee)
                        channels.Main.send(lmsg)
                        add_dying(var, votee, "villager", "lynch")

            kill_players(var, end_game=False) # FIXME

        elif timeout:
            channels.Main.send(messages["sunset"])

        from src.wolfgame import chk_win
        if chk_win():
            return # game ended, just exit out

        if timeout or LYNCHED >= num_lynches:
            from src.wolfgame import transition_night
            transition_night()