| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- //#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<string> Weaknesses = new HashSet<string>();
- public HashSet<string> Immunities = new HashSet<string>();
- 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<Group> all;
- Dictionary<string, int> 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<Group> all, Dictionary<string, int> 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<Group> all, out Dictionary<string, int> teams)
- {
- var file = File.OpenText(args[0]);
- var reg = new Regex(@"(?<n>\d+) units each with (?<hp>\d+) hit points( \(((?<att1>immune|weak) to (?<att1s>[\w\s,]+);? ?)((?<att2>immune|weak) to (?<att2s>[\w\s,]+))?\))? with an attack that does (?<atk>\d+) (?<atkt>\w+) damage at initiative (?<ini>\d+)");
- string rgroup = string.Empty;
- all = new List<Group>();
- teams = new Dictionary<string, int>();
- 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<Group> all, Dictionary<string, int> 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<string, int> 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<Group> 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<Group> 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;
- }
- }
- }
|