def unit_roll(attacker_weak, defender_weak):
    if attacker_weak:
        return -2
    elif defender_weak:
        return 2
    else:
        return roll_random_between(0, 1)
def ai_best_attack(player_units, player_units_strengths, baddies,
                   baddies_strengths, active_consumables):
    first_random = roll_random_float()

    players_tuple = zip(player_units, player_units_strengths,
                        range(len(player_units)))
    best_units = [
        ai_best_unit(baddies, baddies_strengths, player_unit, first_random,
                     active_consumables, "enemy") + (i, )
        for player_unit, strength, i in players_tuple if strength > 0
    ]
    max_grade = max(
        [grade for baddie_index, grade, player_index in best_units])
    best_pairings = [(baddie_index, grade, player_index)
                     for baddie_index, grade, player_index in best_units
                     if grade == max_grade and grade >= 0]
    if best_pairings:
        best_pairing = best_pairings[round(
            roll_random_between(0,
                                len(best_pairings) -
                                1)) if len(best_pairings) > 1 else
                                     0]  #optional roll
        best_pairing = (
            best_pairings[0][0], ) + best_pairing[1:]  # bugged pairings
        print("best AI pairing method 1 (baddie, grade, friendly)",
              repr(best_pairing))
    else:
        best_pairing = None, 0, None

    baddies_tuple = zip(baddies, baddies_strengths, range(len(baddies)))
    best_units_2 = [
        ai_best_unit(player_units, player_units_strengths, baddie,
                     first_random, active_consumables, "ally") + (i, )
        for baddie, strength, i in baddies_tuple if strength > 0
    ]
    max_grade_2 = max(
        [grade for player_index, grade, baddie_index in best_units_2])
    best_players = [
        player_index for player_index, grade, baddie_index in best_units_2
        if grade == max_grade_2
    ]
    if best_players:
        best_player = best_players[0]
        second_random = roll_random_float()
        best_pairing_2 = ai_best_unit(
            baddies, baddies_strengths, player_units[best_player],
            second_random, active_consumables, "enemy") + (best_player, )
        print("best AI pairing method 2 (baddie, grade, friendly)",
              repr(best_pairing_2))
    else:
        best_pairing_2 = None, 0, None

    #the poorer the better? if all units are poor against the best defender then select that one?
    ratio = best_pairing_2[1] / max_grade if max_grade > 0 else 0
    third_random = roll_random_float()
    opposite_day = third_random < 0.2
    if (ratio < 0.5
        ) ^ opposite_day:  # basically only if either method 1 or 2 is poor
        print("Using basic method 1", "opposite day", opposite_day)
        used_pairing = best_pairing
    else:
        print("Using method 2", "opposite day", opposite_day)
        used_pairing = best_pairing_2

    return used_pairing
def assign_consumable_response(params):
    friendlies, friendly_strengths, baddies, baddie_strengths, active_consumables = init_battle(
        params)
    meta = {"newPVE": 0}
    targeted = False
    enemy_turn = False
    casting_ai = False

    consumables = lookup_items_by_type_and_subtype("consumable", "consumable")
    if params["code"] == "A0A":  # Ally / merc
        damaged = any([
            strength < get_unit_max_strength(unit, True)
            for unit, strength in zip(friendlies, friendly_strengths)
        ])
        if params.get('name') == "-1":
            level = session["user_object"]["userInfo"]["player"][
                "level"] + 5  #steele = player level + 5
        else:
            level = 6
            if params.get('name', '0')[0].isalpha():
                merc = lookup_item_by_code(params["name"])
                level = int(merc["level"])
            else:
                for neighbor in session['user_object']["neighbors"]:
                    if neighbor["uid"] == int(params.get('name', '0')):
                        level = neighbor["level"]
        valid_consumables = [c for c in consumables if "-secondary" not in c and \
                             int(c.get("requiredLevel", "0")) <= level and \
                             (damaged or c["consumable"].get("-target") == 'enemy' or c["consumable"].get("-target") == 'enemy' or int(c["consumable"].get("-di","0")) >= 0) and \
                             'requiredDate' not in c and \
                             c["consumable"].get("-allypower", "true") != "false"]

        if session['user_object']["userInfo"]["player"][
                "tutorialProgress"] == 'tut_step_krunsch1AllyUsed':
            print("During tut_step_krunsch1AllyUsed: fixed N04 Air Strike")
            # only one occurrence of fixed allyConsumable uses an N04
            valid_consumables = [lookup_item_by_code("N04")]

        selected_random_consumable_roll = roll_random_between(
            0,
            len(valid_consumables) - 1)

        selected_random_consumable = round(
            selected_random_consumable_roll
        )  # required roll fixed allyconsumable in tutorialstep
        selected_consumable = valid_consumables[selected_random_consumable]
        handle_quest_progress(meta,
                              progress_useAOA_consumable(selected_consumable))
    elif params.get("name") == "AI":
        secondaries = [
            get_unit_secondary(b) for b, i in zip(baddies, range(len(baddies)))
            if get_unit_secondary(b) is not None and not is_stunned(
                ("enemy", i), active_consumables)
        ]
        if len(secondaries) > 1:
            print("WARN: more that one secondary", repr(secondaries))
        if not secondaries:
            print("ERROR: no secondary", repr(secondaries))
            raise Exception("ERROR: no secondary", repr(secondaries))

        selected_consumable = lookup_item_by_code(secondaries[0])
        enemy_turn = True

        cast_chance = roll_random_float()
        cast_percent = float(selected_consumable["consumable"]["-castpercent"])

        print(("Not c" if cast_chance >= cast_percent else "C") +
              "asting secondary power", cast_chance, ">=", cast_percent)
        selected_consumable = None  #second targeted call will be made

        casting_ai = cast_chance < cast_percent

        #selected_consumable = None
    else:
        selected_consumable = lookup_item_by_code(params["code"])
        targeted = True
        enemy_turn = is_affected_by_consumable(
            ("AI", None), {"consumable": {}}, active_consumables)
        handle_quest_progress(
            meta,
            progress_useGeneral_consumable(selected_consumable, enemy_turn))

    # TODO: AI secondary abily Z-units
    if selected_consumable is not None:
        if selected_consumable["consumable"].get("-type") != "all":
            if (selected_consumable["consumable"].get("-target")
                    == 'enemy') ^ enemy_turn:
                live_baddies_index = get_alive_unit_index(baddie_strengths)
                if targeted:
                    targeted_baddie = int(params["id"])
                #TODO enemy support heals, accuracy,...
                else:
                    targeted_baddie = live_baddies_index[round(
                        roll_random_between(
                            0,
                            round(len(live_baddies_index) -
                                  1)))] if len(live_baddies_index
                                               ) > 1 else live_baddies_index[0]
                apply_consumable_direct_impact(meta, selected_consumable,
                                               targeted_baddie, baddies,
                                               baddie_strengths, params, False,
                                               active_consumables)
                # session["battle"] = None`
                # handle_win(baddie_strengths, meta, {})  #TODO next map?
                # handle_loss()

                target = ('enemy', targeted_baddie)
            else:
                live_friendly_index = get_alive_unit_index(friendly_strengths)
                if targeted:
                    targeted_friendly = int(params["id"])
                elif enemy_turn:
                    targeted_friendly = next(
                        i for s, i in zip(friendly_strengths,
                                          range(len(friendly_strengths)))
                        if s > 0 and not is_affected_by_consumable(
                            ("ally",
                             i), selected_consumable, active_consumables))
                else:
                    targeted_friendly = live_friendly_index[round(
                        roll_random_between(
                            0, round(len(live_friendly_index) - 1))
                    )] if len(
                        live_friendly_index) > 1 else live_friendly_index[0]
                apply_consumable_direct_impact(meta, selected_consumable,
                                               targeted_friendly, friendlies,
                                               friendly_strengths, params,
                                               True, active_consumables)
                target = ('ally', targeted_friendly)
        else:

            # TODO: more consumables
            # if consumable["consumable"].get("-type") == "all":
            print("Consumable", selected_consumable["-code"],
                  selected_consumable["consumable"].get("-diweapon",
                                                        ""), "affects all")

            if (selected_consumable["consumable"].get("-target")
                    == 'enemy') ^ enemy_turn:
                for i in range(len(baddies)):
                    apply_consumable_direct_impact(meta, selected_consumable,
                                                   i, baddies,
                                                   baddie_strengths, params,
                                                   False, active_consumables)
                if not targeted and not enemy_turn and len(
                        get_alive_unit_index(baddie_strengths)) > 1:
                    roll_random_float()  #required roll
                target = ('enemy', None)
            else:
                print("target allies")
                for i in range(len(friendlies)):
                    apply_consumable_direct_impact(meta, selected_consumable,
                                                   i, friendlies,
                                                   friendly_strengths, params,
                                                   True, active_consumables)
                if not targeted and not enemy_turn and len(
                        get_alive_unit_index(friendly_strengths)) > 1:
                    roll_random_float()
                target = ('ally', None)

            # for i in range(len(baddie_strengths)):
            # baddie_strengths[i] -= 15
            # print("Baddie", i, "strength", baddie_strengths[i])
            # if baddie_strengths[i] <= 0:
            #     baddie_strengths[i] = 0
            #     print("Baddie", i, "down by consumable")

        if int(selected_consumable["consumable"].get("-duration", "0")) > 0:
            active_consumables.append(
                (selected_consumable, target,
                 int(selected_consumable["consumable"].get("-duration", "0"))))

        if selected_consumable["-name"] == "consumable75":
            print(selected_consumable)
            defenseshield_activate(friendlies, active_consumables,
                                   selected_consumable)

        if targeted and enemy_turn:
            print("Consumable use ends enemy turn")
            process_consumable_end_turn(active_consumables, baddie_strengths,
                                        friendly_strengths, False)
        elif not enemy_turn:
            process_consumable_end_turn(active_consumables, baddie_strengths,
                                        friendly_strengths, True)

        handle_win(baddie_strengths, meta, params)
        handle_loss(friendly_strengths)

        report_battle_log(friendly_strengths, baddie_strengths, not enemy_turn,
                          None, None, active_consumables)

        if targeted and enemy_turn:
            consume_consumables(active_consumables)
    if casting_ai:
        active_consumables.append(({"consumable": {}}, ("AI", None), -1))

    assign_consumable_response = {
        "errorType": 0,
        "userId": 1,
        "metadata": meta,
        "data": []
    }
    return assign_consumable_response