aboutsummaryrefslogtreecommitdiffstats
path: root/src/24
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2020-12-08 21:53:34 +0100
committerLouis Burda <quent.burda@gmail.com>2020-12-08 21:53:34 +0100
commit61c19fd58a9c32919b9b06d38dd9ba202cf43bfb (patch)
tree716d58ee8979d3df123acfedcd5cb8472722d422 /src/24
downloadaoc2018-python-61c19fd58a9c32919b9b06d38dd9ba202cf43bfb.tar.gz
aoc2018-python-61c19fd58a9c32919b9b06d38dd9ba202cf43bfb.zip
Solve 25 days
Diffstat (limited to 'src/24')
-rw-r--r--src/24/input.txt23
-rw-r--r--src/24/notes.txt26
-rw-r--r--src/24/solve.py129
3 files changed, 178 insertions, 0 deletions
diff --git a/src/24/input.txt b/src/24/input.txt
new file mode 100644
index 0000000..969be90
--- /dev/null
+++ b/src/24/input.txt
@@ -0,0 +1,23 @@
+Immune System:
+2728 units each with 5703 hit points (weak to fire) with an attack that does 18 cold damage at initiative 12
+916 units each with 5535 hit points (weak to bludgeoning) with an attack that does 55 slashing damage at initiative 20
+2255 units each with 7442 hit points (weak to radiation) with an attack that does 31 bludgeoning damage at initiative 8
+112 units each with 4951 hit points (immune to cold) with an attack that does 360 fire damage at initiative 9
+7376 units each with 6574 hit points (immune to cold, slashing, fire) with an attack that does 7 bludgeoning damage at initiative 4
+77 units each with 5884 hit points (weak to slashing) with an attack that does 738 radiation damage at initiative 6
+6601 units each with 8652 hit points (weak to fire, cold) with an attack that does 11 fire damage at initiative 19
+3259 units each with 10067 hit points (weak to bludgeoning) with an attack that does 29 cold damage at initiative 13
+2033 units each with 4054 hit points (immune to cold; weak to fire, slashing) with an attack that does 18 slashing damage at initiative 3
+3109 units each with 3593 hit points with an attack that does 9 bludgeoning damage at initiative 11
+
+Infection:
+1466 units each with 57281 hit points (weak to slashing, fire) with an attack that does 58 slashing damage at initiative 7
+247 units each with 13627 hit points with an attack that does 108 fire damage at initiative 15
+1298 units each with 41570 hit points (immune to fire, bludgeoning) with an attack that does 63 fire damage at initiative 14
+2161 units each with 40187 hit points (weak to fire) with an attack that does 33 slashing damage at initiative 5
+57 units each with 55432 hit points (weak to cold) with an attack that does 1687 radiation damage at initiative 17
+3537 units each with 24220 hit points (weak to cold) with an attack that does 11 fire damage at initiative 10
+339 units each with 44733 hit points (immune to cold, bludgeoning; weak to radiation, fire) with an attack that does 258 cold damage at initiative 18
+1140 units each with 17741 hit points (weak to bludgeoning; immune to fire, slashing) with an attack that does 25 fire damage at initiative 2
+112 units each with 44488 hit points (weak to bludgeoning, radiation; immune to cold) with an attack that does 749 radiation damage at initiative 16
+2918 units each with 36170 hit points (immune to bludgeoning; weak to slashing, cold) with an attack that does 24 radiation damage at initiative 1
diff --git a/src/24/notes.txt b/src/24/notes.txt
new file mode 100644
index 0000000..c81d665
--- /dev/null
+++ b/src/24/notes.txt
@@ -0,0 +1,26 @@
+infection and immune sys are armies
+ - made of groups of identical units
+ - group:
+ - same hitpoint
+ - same attack damage
+ - same initiative (who attacks first has highest)
+ - same weaknesses / immunities
+ - effective power = n of members * attack damage
+
+target selection:
+ - highest effective power chooses first sorted
+ if tie => higher initiative chooses first
+ - choose group it would damage most
+ if tie => largest effective power
+ if tie => largest intiative
+ if no group => no attack
+
+attack:
+ - attack in descending order of initiative
+ - regardless of army
+ - immune: 0 dmg
+ - weak: 2 x dmg
+ - only full death, units dont have dynamic health
+
+
+(army, # of units, # of hp, initiative, dict of weaknesses / immunity, damage, type)
diff --git a/src/24/solve.py b/src/24/solve.py
new file mode 100644
index 0000000..020af79
--- /dev/null
+++ b/src/24/solve.py
@@ -0,0 +1,129 @@
+from sys import argv as args
+
+sinput = open("input.txt").read()
+
+immunesys = 0
+infection = 1
+ctype = 0
+
+# parse input from file
+def parseInput():
+ groups = list()
+ for l in sinput.split("\n"):
+ pl = parseLine(l)
+ if pl != None:
+ groups.append(pl)
+ return groups
+
+# parse line of input
+def parseLine(line):
+ global ctype
+ group = None
+ if "Immune" in line:
+ ctype = immunesys
+ elif "Infection" in line:
+ ctype = infection
+ elif line != "":
+ ls = line.split()
+ group = dict()
+ group["type"] = ctype
+ group["units"] = int(ls[0])
+ group["unithp"] = int(ls[4])
+ group["initiative"] = int(ls[-1])
+ group["weak"] = list()
+ group["immune"] = list()
+ if "(" in line:
+ parenthstr = line.split("(")[1].split(")")[0]
+ traits = parenthstr.split(";")
+ for traitstr in traits:
+ info = traitstr.split()
+ group[info[0]] = [v.replace(",","") for v in info[2:]]
+ dmginfo = line.split("does")[1].split("damage")[0].split()
+ group["dmg"] = int(dmginfo[0])
+ group["dmgtype"] = dmginfo[1]
+ return group
+
+def getEffectivePower(g):
+ return g["units"] * g["dmg"]
+
+def getDamage(attacker, defender):
+ dmg = getEffectivePower(attacker)
+ dmg *= (0 if attacker["dmgtype"] in defender["immune"] else 1)
+ dmg *= (2 if attacker["dmgtype"] in defender["weak"] else 1)
+ return dmg
+
+groups = parseInput()
+
+def fight():
+ global groups
+
+ lunits = 0
+
+ immalive = len([g for g in groups if g["type"] == immunesys])
+ infalive = len([g for g in groups if g["type"] == infection])
+
+ while immalive > 0 and infalive > 0:
+ # target selection
+ attacked = list()
+ attackpairs = list()
+ groups = sorted(groups, key = lambda x : (getEffectivePower(x), x["initiative"]), reverse = True)
+ for group in groups:
+ # choose group of other army, which is not already being attacked and sort appropriately
+ enemies = [g for g in groups if g["type"] != group["type"] and g not in attacked]
+ if len(enemies) == 0:
+ continue
+ target = max(enemies, key = lambda x : (getDamage(group, x), getEffectivePower(x), x["initiative"]))
+ if getDamage(group, target) != 0: # enemies which arent immune
+ attacked.append(target)
+ attackpairs.append((groups.index(group), groups.index(target)))
+
+ # attacking phase
+ attackpairs = sorted(attackpairs, key = lambda x : groups[x[0]]["initiative"], reverse = True)
+
+ for ap in attackpairs:
+ attacker = groups[ap[0]]
+ attacked = groups[ap[1]]
+ if attacker["units"] > 0 and attacked["units"] > 0: # if attacker or defender is dead, no need to attack
+ dmg = getDamage(attacker, attacked)
+ attacked["units"] = max(0, attacked["units"] - dmg // attacked["unithp"]) # remove whole numbers of units
+
+ groups = [g for g in groups if g["units"] > 0]
+ immalive = sum([g["units"] for g in groups if g["type"] == immunesys])
+ infalive = sum([g["units"] for g in groups if g["type"] == infection])
+ units = immalive + infalive
+ if units == lunits:
+ return units
+ lunits = units
+ return units
+
+def solve1():
+ print(fight())
+
+def solve2():
+ global groups
+
+ immunewin = False
+ boost = 1
+ while not immunewin:
+ groups = parseInput()
+ for g in groups:
+ if g["type"] == immunesys:
+ g["dmg"] += boost
+
+ fight()
+
+ immunewin = (sum([0 if g["type"] == immunesys else 1 for g in groups]) == 0)
+
+ boost += 1
+
+ #print("boost:", boost)
+ print("units:", sum([v["units"] for v in groups]))
+
+def main():
+ if len(args) > 1:
+ if args[1] == "1":
+ solve1()
+ elif args[1] == "2":
+ solve2()
+
+main()