aoc-2018-python

Advent of Code 2018 Solutions in Python
git clone https://git.sinitax.com/sinitax/aoc-2018-python
Log | Files | Refs | README | sfeed.txt

solve.py (4032B)


      1import sys
      2sys.path.append("../common")
      3import aoc
      4
      5immunesys = 0
      6infection = 1
      7ctype = 0
      8
      9# parse input from file
     10def parse_input():
     11    groups = list()
     12    for l in aoc.data.split("\n"):
     13        pl = parse_entry(l)
     14        if pl != None:
     15            groups.append(pl)
     16    return groups
     17
     18# parse line of input
     19def parse_entry(line):
     20    global ctype
     21    group = None
     22    if "Immune" in line:
     23        ctype = immunesys
     24    elif "Infection" in line:
     25        ctype = infection
     26    elif line != "":
     27        ls = line.split()
     28        group = dict()
     29        group["type"] = ctype
     30        group["units"] = int(ls[0])
     31        group["unithp"] = int(ls[4])
     32        group["initiative"] = int(ls[-1])
     33        group["weak"] = list()
     34        group["immune"] = list()
     35        if "(" in line:
     36            parenthstr = line.split("(")[1].split(")")[0]
     37            traits = parenthstr.split(";")
     38            for traitstr in traits:
     39                info = traitstr.split()
     40                group[info[0]] = [v.replace(",","") for v in info[2:]]
     41        dmginfo = line.split("does")[1].split("damage")[0].split()
     42        group["dmg"] = int(dmginfo[0])
     43        group["dmgtype"] = dmginfo[1]
     44    return group
     45
     46def effective_power(g):
     47    return g["units"] * g["dmg"]
     48
     49def damage(attacker, defender):
     50    dmg = effective_power(attacker)
     51    dmg *= (0 if attacker["dmgtype"] in defender["immune"] else 1)
     52    dmg *= (2 if attacker["dmgtype"] in defender["weak"] else 1)
     53    return dmg
     54
     55groups = parse_input()
     56
     57def fight():
     58    global groups
     59
     60    lunits = 0
     61
     62    immalive = len([g for g in groups if g["type"] == immunesys])
     63    infalive = len([g for g in groups if g["type"] == infection])
     64
     65    while immalive > 0 and infalive > 0:
     66        # target selection
     67        attacked = list()
     68        attackpairs = list()
     69        groups = sorted(groups, key = lambda x : (effective_power(x), x["initiative"]), reverse = True)
     70        for group in groups:
     71            # choose group of other army, which is not already being attacked and sort appropriately
     72            enemies = [g for g in groups if g["type"] != group["type"] and g not in attacked]
     73            if len(enemies) == 0:
     74                continue
     75            target = max(enemies, key = lambda x : \
     76                (damage(group, x), effective_power(x), x["initiative"]))
     77            if damage(group, target) != 0: # enemies which arent immune
     78                attacked.append(target)
     79                attackpairs.append((groups.index(group), groups.index(target)))
     80
     81        # attacking phase
     82        attackpairs = sorted(attackpairs, key = lambda x : groups[x[0]]["initiative"], reverse = True)
     83
     84        for ap in attackpairs:
     85            attacker = groups[ap[0]]
     86            attacked = groups[ap[1]]
     87             # if attacker or defender is dead, no need to attack
     88            if attacker["units"] > 0 and attacked["units"] > 0:
     89                dmg = damage(attacker, attacked)
     90                 # remove whole numbers of units
     91                attacked["units"] = max(0, attacked["units"] - dmg // attacked["unithp"])
     92
     93        groups = [g for g in groups if g["units"] > 0]
     94        immalive = sum([g["units"] for g in groups if g["type"] == immunesys])
     95        infalive = sum([g["units"] for g in groups if g["type"] == infection])
     96        units = immalive + infalive
     97        if units == lunits:
     98            return units
     99        lunits = units
    100
    101    return units
    102
    103def solve1(args):
    104    return fight()
    105
    106def solve2(args):
    107    global groups
    108
    109    immunewin = False
    110    boost = 1
    111    while not immunewin:
    112        groups = parse_input()
    113        for g in groups:
    114            if g["type"] == immunesys:
    115                g["dmg"] += boost
    116
    117        fight()
    118
    119        immunewin = (sum([0 if g["type"] == immunesys else 1 for g in groups]) == 0)
    120
    121        boost += 1
    122
    123    aoc.debug("boost:", boost)
    124
    125    return sum([v["units"] for v in groups])
    126
    127aoc.run(solve1, solve2, sols=[10538, 9174])