back home

Day - 21

Part 1 - Code

      
# template
import os
import math
import itertools
import functools

YEAR = 2015
DAY = 21

def stat(cost, damage, armor, name="Nothing"):
    return {"cost": cost, "damage": damage, "armor": armor, "name": name}

weapons = [stat(8,4,0, "Dagger"),stat(10,5,0, "Shortsword"),stat(25,6,0, "Warhammer"),stat(40,7,0, "Longsword"),stat(74,8,0, "Greataxe")]
armor =   [stat(0,0,0, "Leather"), stat(13,0,1, "chain"),stat(31,0,2, "splint"),stat(53,0,3, "Banded"),stat(75,0,4, "Plate"),stat(102,0,5)]
rings =   [stat(25,1,0, "+1 Atk"),stat(50,2,0, "+2 Atk"),stat(100,3,0, "+3 Atk"),stat(20,0,1, "+1 Def"),stat(40,0,2, "+2 Def"),stat(80,0,3, "+3 Def")]    

weapons_combos = weapons
armor_combos = armor
rings_combos = list(itertools.chain( [stat(0,0,0)], rings, itertools.combinations(rings,2)))

# for i in rings_combos:
#     if type(i) == tuple:
#         to_print = ",".join((d["name"] for d in i))
#         print(to_print)
#     else:
#         print(i)

def combine_rings(ring1, ring2):
    c_cost = ring1["cost"]+ring2["cost"]
    c_damage = ring1["damage"]+ring2["damage"]
    c_armor = ring1["armor"]+ring2["armor"]
    return stat(c_cost, c_damage, c_armor, ",".join([ring1["name"], ring2["name"]]) )



def all_combinations():
    for w in weapons_combos:
        # print(f"""with weapon={w["name"]}""")
        for a in armor_combos:
            for r in rings_combos:
                ring_contributions = r if type(r) != tuple else functools.reduce(combine_rings, r, stat(0,0,0))
                name = ",".join([w["name"], a["name"], ring_contributions["name"]])
                dmg = w["damage"] + ring_contributions["damage"]
                shd = a["armor"] + ring_contributions["armor"]
                cost = w["cost"] + a["cost"] + ring_contributions["cost"]
                yield  {"hp": 100, "damage": dmg, "armor": shd, "cost": cost, "name": name}

enemy = {"hp": 104, "damage": 8, "armor": 1}
elf  = {"hp": 100, "damage": 0, "armor": 0}

# returns true if person one can defeat person two
def duel(player, boss): 

    
    player_dmg_dealt = player["damage"] - boss["armor"]
    player_dmg_dealt = 1 if player_dmg_dealt <= 0 else player_dmg_dealt
    
    boss_dmg_dealt = boss["damage"] - player["armor"]
    boss_dmg_dealt = 1 if boss_dmg_dealt <= 0 else boss_dmg_dealt
    

    player_turns = math.ceil(boss["hp"] / player_dmg_dealt)
    boss_turns = math.ceil(player["hp"] / boss_dmg_dealt)

#     print(f"""Player has {player["name"]} items""")
#     print(f"""
#     items={player["name"]}
#     attack={player["damage"]}
#     shield={player["armor"]}
#     cost={player["cost"]}
#     player dmg dealt = {player_dmg_dealt}
#     boss dmg dealt = {boss_dmg_dealt}
#     player_turns = {player_turns}
#     boss_turns = {boss_turns}
# ----------------------------------
# """)

    return player_turns <= (boss_turns)



def main():

    min_gold = 1000000000
    max_gold = -1
    for elf in all_combinations():
        player_win = duel(elf, enemy)
        gold = elf["cost"]
        items = elf["name"]
        if player_win:
            print(f"Elf won with {items} for {gold} gold")
            min_gold = min(min_gold, gold)
        else:
            max_gold = max(max_gold, gold)
    print(f"Player won using {min_gold} gold")
    print(f"Player lost using {max_gold} gold")


if __name__ == "__main__":
    main()