class ArmyGroup { public string Source; public int UnitCount; public int HitPoints; public List Weakness = new List(); public List Immunity = new List(); public string DamageType; public int DamageValue; public int Initiative; public int EffectivePower => UnitCount * DamageValue; public bool Alive => UnitCount>0; public ArmyGroup NextTarget = null; public bool IsTargeted = false; } void Main() => Enumerable.Range(0, int.MaxValue).Select(Calc).First(b=>b>0).Dump(); int Calc(int boost) { var groups = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"24_input.txt"))); foreach(var g in groups.Where(p => p.Source=="ImmuneSystem")) g.DamageValue += boost; for (int gen = 0; ; gen++) { if (!(groups.GroupBy(g => g.Source).All(g => g.Any(a => a.Alive)))) break; bool dmg = Fight(groups); if (!dmg) return 0; } return groups.Where(p => p.Source=="ImmuneSystem").Sum(p => p.UnitCount) - groups.Where(p => p.Source=="Infection").Sum(p => p.UnitCount); } List Load(string[] lines) { return new[] { lines.Skip(1).TakeWhile(l => !string.IsNullOrWhiteSpace(l)).Select(l => LoadSingle("ImmuneSystem", l)), lines.Skip(1).SkipWhile(l => !string.IsNullOrWhiteSpace(l)).Skip(2).Select(l => LoadSingle("Infection", l)) } .SelectMany(p=>p) .ToList(); } ArmyGroup LoadSingle(string src, string line) { var ag = new ArmyGroup { Source=src }; var i1 = line.IndexOf(' '); ag.UnitCount = int.Parse(line.Substring(0, i1).Trim()); line = line.Substring(i1).Trim().Substring(15).Trim(); var i6 = line.IndexOf(' '); ag.HitPoints = int.Parse(line.Substring(0, i6).Trim()); line = line.Substring(i6).Trim().Substring(11).Trim(); var i2 = line.IndexOf(')'); if (i2 != -1) { var spec0 = line.Substring(1, i2).TrimEnd(')'); line = line.Substring(1).Substring(i2).Substring(1).Trim(); foreach (var spc in spec0.Split(';').Select(p => p.Trim())) { if (spc.StartsWith("weak to")) ag.Weakness.AddRange(spc.Substring("weak to".Length).Trim().Split(',').Select(p => p.Trim())); if (spc.StartsWith("immune to")) ag.Immunity.AddRange(spc.Substring("immune to".Length).Trim().Split(',').Select(p => p.Trim())); } } line = line.Substring(25).Trim(); var i3 = line.IndexOf(' '); ag.DamageValue = int.Parse(line.Substring(0, i3).Trim()); line = line.Substring(i3).Trim().Trim(); var i4 = line.IndexOf(' '); ag.DamageType = line.Substring(0, i4).Trim(); line = line.Substring(i4).Trim().Trim(); var i5 = line.LastIndexOf(' '); ag.Initiative = int.Parse(line.Substring(i5).Trim()); return ag; } bool Fight(List groups) { // PHASE 1 foreach (var g in groups) { g.IsTargeted=false; g.NextTarget=null; } foreach (var g in groups.Where(g => g.Alive).OrderByDescending(g => g.EffectivePower).ThenByDescending(g => g.Initiative)) { SelectTarget(g, groups); } // PHASE 2 bool d = false; foreach (var g in groups.Where(g => g.Alive).OrderByDescending(g => g.Initiative).Where(g => g.NextTarget!=null)) { var a = Attack(g, g.NextTarget); if (a)d=true; } return d; } void SelectTarget(ArmyGroup att, List groups) { att.NextTarget = groups .Where(g => g.Alive) .Where(g => g.Source != att.Source) .Where(g => !g.IsTargeted) .Where(g => CalcFullDamage(att, g)>0) .OrderByDescending(g => CalcFullDamage(att, g)) .ThenByDescending(g => g.EffectivePower) .ThenByDescending(g => g.Initiative) .FirstOrDefault(); if (att.NextTarget != null) att.NextTarget.IsTargeted = true; } int CalcFullDamage(ArmyGroup att, ArmyGroup def) { if (att.UnitCount==0) return 0; var dmg = att.EffectivePower; if (def.Immunity.Contains(att.DamageType)) return 0; if (def.Weakness.Contains(att.DamageType)) return dmg*2; return dmg; } bool Attack(ArmyGroup att, ArmyGroup def) { var dmg = CalcFullDamage(att, def); var kill = (int)(dmg / def.HitPoints); def.UnitCount -= kill; if (def.UnitCount<0) def.UnitCount = 0; return kill>0; }