//#define PRINT using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace D24._1 { public class Group { public int Id { get; set; } public bool Healthy = true; public string Team { get; set; } public int Number { get; set; } public int OrigNumber { get; set; } public int Initiative { get; set; } public int HitPoints { get; set; } public int Atk { get; set; } public int AtkBonus { get; set; } public string AtkType { get; set; } public HashSet Weaknesses = new HashSet(); public HashSet Immunities = new HashSet(); public int EffectivePower => Number * (Atk + AtkBonus); public Group Target { get; set; } public bool Targeted { get; set; } private int _computeDamage(Group target) { if (target.Immunities.Contains(AtkType)) return 0; int damage = EffectivePower; if (target.Weaknesses.Contains(AtkType)) damage *= 2; return damage; } public int ComputeDamage(Group target) { int damage = _computeDamage(target); #if PRINT Console.WriteLine($"{Team} group {Id} would deal defending group {target.Id} {damage} damage"); #endif return damage; } public int DealDamage(Group target) { var kill = target.ReceiveDamage(this); #if PRINT Console.WriteLine($"{Team} group {Id} attacks defending group {target.Id}, killing {kill} units"); #endif return kill; } int ReceiveDamage(Group source) { var damage = source._computeDamage(this); int kill = damage / HitPoints; kill = Math.Min(kill, Number); Number -= kill; if (Number <= 0) Healthy = false; return kill; } } public class Program { static void Main(string[] args) { if (args.Length < 1) return; if (File.Exists(args[0]) == false) return; List all; Dictionary teams; ParseFile(args, out all, out teams); while (teams.All(t => t.Value > 0)) { #if PRINT PrintGroups(all, teams); Console.WriteLine(""); #endif TargetSelectionPhase(all); #if PRINT Console.WriteLine(""); #endif AttackPhase(all, teams); #if PRINT Console.WriteLine(""); #endif } int answer = all.Sum(a => a.Number); Console.WriteLine($"The answer is : {answer}"); } private static void PrintGroups(List all, Dictionary teams) { foreach (var team in teams) { int printed = 0; Console.WriteLine($"{team.Key}:"); foreach (var group in all.Where(a => a.Team == team.Key)) { if (group.Healthy == false) continue; Console.WriteLine($"Group {group.Id} contains {group.Number} units"); printed++; } if (printed == 0) Console.WriteLine($"No groups remain."); } } public static void ParseFile(string[] args, out List all, out Dictionary teams) { var file = File.OpenText(args[0]); var reg = new Regex(@"(?\d+) units each with (?\d+) hit points( \(((?immune|weak) to (?[\w\s,]+);? ?)((?immune|weak) to (?[\w\s,]+))?\))? with an attack that does (?\d+) (?\w+) damage at initiative (?\d+)"); string rgroup = string.Empty; all = new List(); teams = new Dictionary(); do { var line = file.ReadLine(); if (line == null) break; if (line == string.Empty) { rgroup = string.Empty; continue; } if (rgroup == string.Empty) { rgroup = line.Substring(0, line.Length - 1); teams.Add(rgroup, 0); continue; } var res = reg.Match(line); var g = new Group(); g.Team = rgroup; teams[rgroup] = teams[rgroup] + 1; g.Id = teams[rgroup]; g.Number = int.Parse(res.Groups["n"].Value); g.OrigNumber = g.Number; g.HitPoints = int.Parse(res.Groups["hp"].Value); g.Atk = int.Parse(res.Groups["atk"].Value); g.AtkType = res.Groups["atkt"].Value; g.Initiative = int.Parse(res.Groups["ini"].Value); for (var i = 1; i <= 2; ++i) { if (res.Groups[$"att{i}"].Success == false) break; var type = res.Groups[$"att{i}"].Value; foreach (var att in res.Groups[$"att{i}s"].Value.Split(", ")) { if (type.Equals("weak")) g.Weaknesses.Add(att); else if (type.Equals("immune")) g.Immunities.Add(att); } } all.Add(g); } while (true); file.Close(); } public static bool AttackPhase(List all, Dictionary teams) { var attackingOrder = all .OrderByDescending(s => s.Initiative); int attacks = 0; foreach (var group in attackingOrder) { if (group.Target == null) continue; if (group.Healthy == false) continue; var kill = AttackTarget(teams, group); if (kill > 0) attacks++; } return attacks > 0; } private static int AttackTarget(Dictionary teams, Group group) { var target = group.Target; group.Target = null; target.Targeted = false; var kill = group.DealDamage(target); if (target.Healthy == false) { if (target.Target != null) { target.Target.Targeted = false; target.Target = null; } teams[target.Team] = teams[target.Team] - 1; } return kill; } public static void TargetSelectionPhase(List all) { var targetingOrder = all .OrderByDescending(a => a.EffectivePower) .ThenByDescending(a => a.Initiative); foreach (var group in targetingOrder) { if (group.Healthy == false) continue; Group max = PickTarget(all, group); if (max == null) continue; group.Target = max; max.Targeted = true; } } private static Group PickTarget(List all, Group group) { Group max = null; int maxDamage = 0; foreach (var target in all) { if (target.Team == group.Team) continue; if (target.Targeted == true) continue; if (target.Healthy == false) continue; int damage = group.ComputeDamage(target); if (damage == 0) continue; if (damage > maxDamage) { maxDamage = damage; max = target; } else if (damage == maxDamage) { if (target.EffectivePower > max.EffectivePower) max = target; else if (target.EffectivePower == max.EffectivePower) { if (target.Initiative > max.Initiative) max = target; } } } return max; } } }