def find_valid_combination(builder, start_regions, world, player, drop_keys=True):
    logger = logging.getLogger('')
    logger.info('Shuffling Key doors for %s', builder.name)
    if (len(builder.candidates) < builder.key_doors_num):
        if (not drop_keys):
            logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num)
            return False
        builder.key_doors_num = len(builder.candidates)
        logger.info('Lowering key door count because not enough candidates: %s', builder.name)
    combinations = ncr(len(builder.candidates), builder.key_doors_num)
    itr = 0
    start = time.process_time()
    sample_list = list(range(0, int(combinations)))
    random.shuffle(sample_list)
    proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
    key_layout = build_key_layout(builder, start_regions, proposal, world, player)
    while (not validate_key_layout(key_layout, world, player)):
        itr += 1
        stop_early = False
        if ((itr % 1000) == 0):
            mark = (time.process_time() - start)
            if (((mark > 10) and (((itr * 100) / combinations) > 50)) or ((mark > 20) and (((itr * 100) / combinations) > 25)) or (mark > 30)):
                stop_early = True
        if ((itr >= combinations) or stop_early):
            if (not drop_keys):
                logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num)
                return False
            logger.info('Lowering key door count because no valid layouts: %s', builder.name)
            builder.key_doors_num -= 1
            if (builder.key_doors_num < 0):
                raise Exception(('Bad dungeon %s - 0 key doors not valid' % builder.name))
            combinations = ncr(len(builder.candidates), builder.key_doors_num)
            sample_list = list(range(0, int(combinations)))
            random.shuffle(sample_list)
            itr = 0
            start = time.process_time()
        proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
        key_layout.reset(proposal, builder, world, player)
        if (((itr + 1) % 1000) == 0):
            mark = (time.process_time() - start)
            logger.info('%s time elapsed. %s iterations/s', mark, (itr / mark))
    if (player not in world.key_logic.keys()):
        world.key_logic[player] = {}
    analyze_dungeon(key_layout, world, player)
    builder.key_door_proposal = proposal
    world.key_logic[player][builder.name] = key_layout.key_logic
    world.key_layout[player][builder.name] = key_layout
    return True
def find_valid_combination(builder, start_regions, world, player, drop_keys=True):
    logger = logging.getLogger()
    logger.info('Shuffling Key doors for %s', builder.name)
    if (len(builder.candidates) < builder.key_doors_num):
        if (not drop_keys):
            logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num)
            return False
        builder.key_doors_num = len(builder.candidates)
        logger.info('Lowering key door count because not enough candidates: %s', builder.name)
    combinations = ncr(len(builder.candidates), builder.key_doors_num)
    itr = 0
    start = time.process_time()
    sample_list = list(range(0, int(combinations)))
    random.shuffle(sample_list)
    proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
    key_layout = build_key_layout(builder, start_regions, proposal, world, player)
    while (not validate_key_layout(key_layout, world, player)):
        itr += 1
        stop_early = False
        if ((itr % 1000) == 0):
            mark = (time.process_time() - start)
            if (((mark > 10) and (((itr * 100) / combinations) > 50)) or ((mark > 20) and (((itr * 100) / combinations) > 25)) or (mark > 30)):
                stop_early = True
        if ((itr >= combinations) or stop_early):
            if (not drop_keys):
                logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num)
                return False
            logger.info('Lowering key door count because no valid layouts: %s', builder.name)
            builder.key_doors_num -= 1
            if (builder.key_doors_num < 0):
                raise Exception(('Bad dungeon %s - 0 key doors not valid' % builder.name))
            combinations = ncr(len(builder.candidates), builder.key_doors_num)
            sample_list = list(range(0, int(combinations)))
            random.shuffle(sample_list)
            itr = 0
            start = time.process_time()
        proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
        key_layout.reset(proposal, builder, world, player)
        if (((itr + 1) % 1000) == 0):
            mark = (time.process_time() - start)
            logger.info('%s time elapsed. %s iterations/s', mark, (itr / mark))
    if (player not in world.key_logic.keys()):
        world.key_logic[player] = {}
    analyze_dungeon(key_layout, world, player)
    builder.key_door_proposal = proposal
    world.key_logic[player][builder.name] = key_layout.key_logic
    world.key_layout[player][builder.name] = key_layout
    return True