예제 #1
0
def handover() -> dict:
    target = int(request.args.get('target'))
    my_role = Role.query.get(current_user.uid)
    if not my_role.alive:
        current_app.logger.info('my_role is not alive')
        return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
    with Game.query.with_for_update().get(current_user.gid) as game:
        now = StepProcessor.current_step(game)
        if now is not GameEnum.TURN_STEP_LAST_WORDS:
            current_app.logger.info(f'wrong now step:{now.label}')
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if str(my_role.position) not in game.history['dying']:
            current_app.logger.info(
                f'not in dying: my position={my_role.position},dying={game.history["dying"]}'
            )
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if game.captain_pos != my_role.position:
            current_app.logger.info(
                f'I am not captain, my position={my_role.position},captain pos={game.captain_pos}'
            )
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if target > 0:
            target_role = _get_role_by_pos(game, target)
            if not target_role.alive:
                current_app.logger.info(f'target not alive, target={target}')
                return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        game.captain_pos = target
        publish_history(game.gid, f'{my_role.position}号玩家将警徽移交给了{target}号玩家')
        return GameEnum.OK.digest()
예제 #2
0
def suicide():
    my_role = Role.query.get(current_user.uid)
    if not my_role.alive:
        return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()

    with Game.query.with_for_update().get(current_user.gid) as game:
        if game.status is not GameEnum.GAME_STATUS_DAY:
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        game.steps = []
        publish_history(game.gid, f'{my_role.position}号玩家自爆了')
        StepProcessor.kill(game, my_role.position, GameEnum.SKILL_SUICIDE)
        return StepProcessor.move_on(game)
예제 #3
0
def elect() -> dict:
    choice = request.args.get('choice')
    my_role = Role.query.get(current_user.uid)
    if not my_role.alive:
        return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
    with Game.query.with_for_update().get(current_user.gid) as game:
        now = StepProcessor.current_step(game)
        if choice in ['yes', 'no'] and now is not GameEnum.TURN_STEP_ELECT:
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if choice == 'quit' and now is not GameEnum.TURN_STEP_ELECT_TALK:
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if choice in ['yes', 'no'
                      ] and (GameEnum.TAG_ELECT in my_role.tags
                             or GameEnum.TAG_NOT_ELECT in my_role.tags):
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if choice == 'quit' and (GameEnum.TAG_ELECT not in my_role.tags or
                                 GameEnum.TAG_GIVE_UP_ELECT in my_role.tags):
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()

        if choice == 'yes':
            my_role.tags.append(GameEnum.TAG_ELECT)
        elif choice == 'no':
            my_role.tags.append(GameEnum.TAG_NOT_ELECT)
        elif choice == 'quit':
            publish_history(game.gid, f'{my_role.position}号玩家退水')
            my_role.tags.remove(GameEnum.TAG_ELECT)
            my_role.tags.append(GameEnum.TAG_GIVE_UP_ELECT)
            votee = game.history['voter_votee'][1]
            votee.remove(my_role.position)
            if len(votee) == 1:
                while game.now_index + 1 < len(
                        game.steps) and game.steps[game.now_index + 1] in {
                            GameEnum.TURN_STEP_ELECT_TALK,
                            GameEnum.TURN_STEP_ELECT_VOTE
                        }:
                    game.steps.pop(game.now_index + 1)
                captain_pos = votee[0]
                game.captain_pos = captain_pos
                publish_history(game.gid, f'仅剩一位警上玩家,{captain_pos}号玩家自动当选警长')
                return StepProcessor.move_on(game)
        else:
            raise ValueError(f'Unknown choice: {choice}')
        return GameEnum.OK.digest()
예제 #4
0
def shoot() -> dict:
    target = int(request.args.get('target'))
    my_role = Role.query.get(current_user.uid)
    if not my_role.alive:
        return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
    with Game.query.with_for_update().get(current_user.gid) as game:
        history = game.history
        now = StepProcessor.current_step(game)
        if now is not GameEnum.TURN_STEP_LAST_WORDS:
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if not my_role.args['shootable'] or str(
                my_role.position) not in game.history['dying']:
            return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        if target > 0:
            target_role = _get_role_by_pos(game, target)
            if not target_role.alive:
                return GameEnum.GAME_MESSAGE_CANNOT_ACT.digest()
        publish_history(game.gid,
                        f'{my_role.position}号玩家发动技能“枪击”,带走了{target}号玩家')
        StepProcessor.kill(game, target, GameEnum.SKILL_SHOOT)
        return GameEnum.OK.digest()
예제 #5
0
    def check_win(game):
        # groups = Role.query.with_entities(Role.group_type, func.count(Role.group_type)).filter(Role.gid == game.gid, Role.alive == int(True)).group_by(Role.group_type).all()
        # groups = {g: cnt for g, cnt in groups}

        players = Role.query.with_entities(Role.position,
                                           Role.group_type).filter(
                                               Role.gid == game.gid,
                                               Role.alive == int(True)).all()
        groups = collections.defaultdict(int)
        for p, g in players:
            if p not in game.history['dying']:
                groups[g] += 1

        if GameEnum.GROUP_TYPE_WOLVES not in groups:
            publish_history(game.gid, '游戏结束,好人阵营胜利')
            # game.status = GameEnum.GAME_STATUS_FINISHED
            original_players = game.players
            StepProcessor.init_game(game)
            game.players = original_players
            all_players = Role.query.filter(Role.gid == game.gid).all()
            for p in all_players:
                p.reset()
            raise GameFinished()

        if game.victory_mode is GameEnum.VICTORY_MODE_KILL_GROUP and (
                GameEnum.GROUP_TYPE_GODS not in groups
                or GameEnum.GROUP_TYPE_VILLAGERS not in groups):
            publish_history(game.gid, '游戏结束,狼人阵营胜利')
            # game.status = GameEnum.GAME_STATUS_FINISHED
            original_players = game.players
            StepProcessor.init_game(game)
            game.players = original_players
            all_players = Role.query.filter(Role.gid == game.gid).all()
            for p in all_players:
                p.reset()
            raise GameFinished()

        if GameEnum.GROUP_TYPE_GODS not in groups and GameEnum.GROUP_TYPE_VILLAGERS not in groups:
            publish_history(game.gid, '游戏结束,狼人阵营胜利')
            # game.status = GameEnum.GAME_STATUS_FINISHED
            original_players = game.players
            StepProcessor.init_game(game)
            game.players = original_players
            all_players = Role.query.filter(Role.gid == game.gid).all()
            for p in all_players:
                p.reset()
            raise GameFinished()
예제 #6
0
    def _leave_step(game: Game) -> dict:
        now = StepProcessor.current_step(game)
        if now is None:
            return GameEnum.OK.digest()
        if now is GameEnum.TURN_STEP_ELECT:
            roles = Role.query.filter(Role.gid == game.gid).limit(
                len(game.players)).all()
            for r in roles:
                if GameEnum.TAG_ELECT not in r.tags and GameEnum.TAG_NOT_ELECT not in r.tags:
                    r.tags.append(GameEnum.TAG_NOT_ELECT)

            voters = []
            votees = []
            for r in roles:
                if not r.alive:
                    continue
                if GameEnum.TAG_ELECT in r.tags:
                    votees.append(r.position)
                else:
                    voters.append(r.position)
            voters.sort()
            votees.sort()

            if not voters or not votees:
                # no captain
                while game.now_index + 1 < len(
                        game.steps) and game.steps[game.now_index + 1] in {
                            GameEnum.TURN_STEP_ELECT_TALK,
                            GameEnum.TURN_STEP_ELECT_VOTE
                        }:
                    game.steps.pop(game.now_index + 1)
                if not voters:
                    publish_history(game.gid, '所有人都竞选警长,本局游戏无警长')
                else:
                    publish_history(game.gid, '没有人竞选警长,本局游戏无警长')
            elif len(votees) == 1:
                # auto win captain
                while game.now_index + 1 < len(
                        game.steps) and game.steps[game.now_index + 1] in {
                            GameEnum.TURN_STEP_ELECT_TALK,
                            GameEnum.TURN_STEP_ELECT_VOTE
                        }:
                    game.steps.pop(game.now_index + 1)
                captain_pos = votees[0]
                game.captain_pos = captain_pos
                publish_history(game.gid, f'只有{captain_pos}号玩家竞选警长,自动当选')
            else:
                publish_history(
                    game.gid,
                    f"竞选警长的玩家为:{','.join(map(str,votees))}\n未竞选警长的玩家为:{','.join(map(str,voters))}"
                )
                game.history['voter_votee'] = [voters, votees]
            return GameEnum.OK.digest()
        elif now in [
                GameEnum.TURN_STEP_VOTE, GameEnum.TURN_STEP_ELECT_VOTE,
                GameEnum.TURN_STEP_PK_VOTE, GameEnum.TURN_STEP_ELECT_PK_VOTE
        ]:
            msg = ""
            announce_result = collections.defaultdict(list)
            ticket_cnt = collections.defaultdict(int)
            forfeit = []
            most_voted = []
            max_ticket = 0
            for voter_pos, votee_pos in game.history['vote_result'].items():
                voter_pos = int(voter_pos)
                votee_pos = int(votee_pos)
                if votee_pos in [
                        GameEnum.TARGET_NOT_ACTED.value,
                        GameEnum.TARGET_NO_ONE.value
                ]:
                    forfeit.append(voter_pos)
                    continue
                announce_result[votee_pos].append(voter_pos)
                ticket_cnt[votee_pos] += 1
                if voter_pos == game.captain_pos:
                    ticket_cnt[votee_pos] += 0.5
            for voter in game.history['voter_votee'][0]:
                if str(voter) not in game.history['vote_result']:
                    forfeit.append(voter)
            if forfeit and now in [
                    GameEnum.TURN_STEP_PK_VOTE,
                    GameEnum.TURN_STEP_ELECT_PK_VOTE
            ]:
                return GameEnum.GAME_MESSAGE_NOT_VOTED_YET.digest(*forfeit)
            for votee, voters in sorted(announce_result.items()):
                msg += '{} <= {}\n'.format(votee, ','.join(map(str, voters)))
            if forfeit:
                msg += '弃票:{}\n'.format(','.join(map(str, forfeit)))

            if not ticket_cnt:
                most_voted = game.history['voter_votee'][1]
            else:
                ticket_cnt = sorted(ticket_cnt.items(),
                                    key=lambda x: x[1],
                                    reverse=True)
                most_voted.append(ticket_cnt[0][0])
                max_ticket = ticket_cnt[0][1]
                for votee, ticket in ticket_cnt[1:]:
                    if ticket == max_ticket:
                        most_voted.append(votee)
                    else:
                        break
            most_voted.sort()

            if len(most_voted) == 1:
                if now in [
                        GameEnum.TURN_STEP_VOTE, GameEnum.TURN_STEP_PK_VOTE
                ]:
                    msg += f'{most_voted[0]}号玩家以{max_ticket}票被公投出局'
                    publish_history(game.gid, msg)
                    StepProcessor.kill(game, most_voted[0],
                                       GameEnum.SKILL_VOTE)
                else:
                    game.captain_pos = most_voted[0]
                    msg += f'{most_voted[0]}号玩家以{max_ticket}票当选警长'
                    publish_history(game.gid, msg)
                return GameEnum.OK.digest()
            else:
                # 平票
                # todo 无人投票
                if now in [
                        GameEnum.TURN_STEP_VOTE, GameEnum.TURN_STEP_ELECT_VOTE
                ]:
                    if now is GameEnum.TURN_STEP_VOTE:
                        game.steps.insert(game.now_index + 1,
                                          GameEnum.TURN_STEP_PK_TALK)
                        game.steps.insert(game.now_index + 2,
                                          GameEnum.TURN_STEP_PK_VOTE)
                    else:
                        game.steps.insert(game.now_index + 1,
                                          GameEnum.TURN_STEP_ELECT_PK_TALK)
                        game.steps.insert(game.now_index + 2,
                                          GameEnum.TURN_STEP_ELECT_PK_VOTE)
                    votees = most_voted
                    voters = []
                    roles = Role.query.filter(Role.gid == game.gid).limit(
                        len(game.players)).all()
                    for r in roles:
                        if r.alive and r.voteable and r.position not in votees:
                            voters.append(r.position)
                    game.history['voter_votee'] = [voters, votees]
                    msg += '以下玩家以{}票平票进入PK:{}'.format(
                        max_ticket, ','.join(map(str, votees)))
                    publish_history(game.gid, msg)
                    return GameEnum.OK.digest()
                else:
                    msg += '以下玩家以{}票再次平票:{}\n'.format(
                        max_ticket, ','.join(map(str, most_voted)))
                    if now is GameEnum.TURN_STEP_PK_VOTE:
                        msg += '今天是平安日,无人被公投出局'
                    else:
                        msg += '警徽流失,本局游戏无警长'
                    publish_history(game.gid, msg)
                    return GameEnum.OK.digest()
        elif now is GameEnum.TAG_ATTACKABLE_WOLF:
            publish_music(game.gid, 'wolf_end_voice', None, False)
            return GameEnum.OK.digest()
        elif now is GameEnum.ROLE_TYPE_SEER:
            publish_music(game.gid, 'seer_end_voice', None, False)
            return GameEnum.OK.digest()
        elif now is GameEnum.ROLE_TYPE_WITCH:
            publish_music(game.gid, 'witch_end_voice', None, False)
            return GameEnum.OK.digest()
        elif now is GameEnum.ROLE_TYPE_SAVIOR:
            publish_music(game.gid, 'savior_end_voice', None, False)
            return GameEnum.OK.digest()
        else:
            return GameEnum.OK.digest()
예제 #7
0
 def _enter_step(game: Game) -> GameEnum:
     now = StepProcessor.current_step(game)
     if now is GameEnum.TURN_STEP_TURN_NIGHT:
         game.status = GameEnum.GAME_STATUS_NIGHT
         for d in game.history['dying']:
             role = Role.query.filter(Role.gid == game.gid,
                                      Role.position == d).limit(1).first()
             role.alive = False
         StepProcessor._reset_history(game)
         publish_music(game.gid, 'night_start_voice', 'night_bgm', True)
         publish_info(
             game.gid,
             json.dumps({
                 'days': game.days,
                 'game_status': game.status.label
             }))
         publish_history(game.gid,
                         ('***************************\n'
                          '<pre>         第{}天           </pre>\n'
                          '***************************').format(game.days),
                         show=False)
         return GameEnum.STEP_FLAG_AUTO_MOVE_ON
     elif now is GameEnum.TAG_ATTACKABLE_WOLF:
         publish_music(game.gid, 'wolf_start_voice', 'wolf_bgm', True)
         all_players = Role.query.filter(Role.gid == game.gid,
                                         Role.alive == int(True)).limit(
                                             len(game.players)).all()
         for p in all_players:
             if GameEnum.TAG_ATTACKABLE_WOLF in p.tags:
                 break
         else:
             scheduler.add_job(id=f'{game.gid}_WOLF_KILL_{game.step_cnt}',
                               func=timeout_move_on,
                               args=(game.gid, game.step_cnt),
                               next_run_time=datetime.now() +
                               timedelta(seconds=random.randint(
                                   GameEnum.GAME_TIMEOUT_RANDOM_FROM.label,
                                   GameEnum.GAME_TIMEOUT_RANDOM_TO.label)))
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now in [GameEnum.TURN_STEP_TALK, GameEnum.TURN_STEP_ELECT_TALK]:
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.TURN_STEP_ELECT:
         publish_music(game.gid, 'elect', None, False)
         publish_history(game.gid, '###上警阶段###', False)
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.TURN_STEP_VOTE:
         game.history['vote_result'] = {}
         voters = []
         votees = []
         roles = Role.query.filter(Role.gid == game.gid).limit(
             len(game.players)).all()
         for r in roles:
             if not r.alive:
                 continue
             votees.append(r.position)
             if r.voteable:
                 voters.append(r.position)
         game.history['voter_votee'] = [voters, votees]
         publish_history(game.gid, '###投票阶段###', False)
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.TURN_STEP_ELECT_VOTE:
         game.history['vote_result'] = {}
         publish_history(game.gid, '###警长投票阶段###', False)
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.TURN_STEP_PK_VOTE:
         game.history['vote_result'] = {}
         publish_history(game.gid, '###PK投票阶段###', False)
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.TURN_STEP_ELECT_PK_VOTE:
         game.history['vote_result'] = {}
         publish_history(game.gid, '###警长PK投票阶段###', False)
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.TURN_STEP_ANNOUNCE:
         if game.history['dying']:
             publish_history(
                 game.gid, '昨晚,以下位置的玩家倒下了,不分先后:{}'.format(','.join(
                     [str(d) for d in sorted(game.history['dying'])])))
         else:
             publish_history(game.gid, "昨晚是平安夜")
         return GameEnum.STEP_FLAG_AUTO_MOVE_ON
     elif now is GameEnum.TURN_STEP_TURN_DAY:
         game.status = GameEnum.GAME_STATUS_DAY
         publish_music(game.gid, 'day_start_voice', 'day_bgm', False)
         StepProcessor.calculate_die_in_night(game)
         publish_info(
             game.gid,
             json.dumps({
                 'days': game.days,
                 'game_status': game.status.label
             }))
         return GameEnum.STEP_FLAG_AUTO_MOVE_ON
     elif now is GameEnum.ROLE_TYPE_SEER:
         publish_music(game.gid, 'seer_start_voice', 'seer_bgm', True)
         seer_cnt = db.session.query(db.func.count(Role.uid)).filter(
             Role.gid == game.gid, Role.alive == int(True),
             Role.role_type == GameEnum.ROLE_TYPE_SEER).scalar()
         if seer_cnt == 0:
             scheduler.add_job(id=f'{game.gid}_SEER_{game.step_cnt}',
                               func=timeout_move_on,
                               args=(game.gid, game.step_cnt),
                               next_run_time=datetime.now() +
                               timedelta(seconds=random.randint(
                                   GameEnum.GAME_TIMEOUT_RANDOM_FROM.label,
                                   GameEnum.GAME_TIMEOUT_RANDOM_TO.label)))
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.ROLE_TYPE_WITCH:
         publish_music(game.gid, 'witch_start_voice', 'witch_bgm', True)
         witch_cnt = db.session.query(db.func.count(Role.uid)).filter(
             Role.gid == game.gid, Role.alive == int(True),
             Role.role_type == GameEnum.ROLE_TYPE_WITCH).scalar()
         if witch_cnt == 0:
             scheduler.add_job(id=f'{game.gid}_WITCH_{game.step_cnt}',
                               func=timeout_move_on,
                               args=(game.gid, game.step_cnt),
                               next_run_time=datetime.now() +
                               timedelta(seconds=random.randint(
                                   GameEnum.GAME_TIMEOUT_RANDOM_FROM.label,
                                   GameEnum.GAME_TIMEOUT_RANDOM_TO.label)))
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION
     elif now is GameEnum.ROLE_TYPE_SAVIOR:
         publish_music(game.gid, 'savior_start_voice', 'savior_bgm', True)
         savior_cnt = db.session.query(db.func.count(Role.uid)).filter(
             Role.gid == game.gid, Role.alive == int(True),
             Role.role_type == GameEnum.ROLE_TYPE_SAVIOR).scalar()
         if savior_cnt == 0:
             scheduler.add_job(id=f'{game.gid}_SAVIOR',
                               func=timeout_move_on,
                               args=(game.gid, game.step_cnt),
                               next_run_time=datetime.now() +
                               timedelta(seconds=random.randint(
                                   GameEnum.GAME_TIMEOUT_RANDOM_FROM.label,
                                   GameEnum.GAME_TIMEOUT_RANDOM_TO.label)))
         return GameEnum.STEP_FLAG_WAIT_FOR_ACTION