Program.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. //#define PRINT
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. namespace D24._1
  8. {
  9. public class Group
  10. {
  11. public int Id { get; set; }
  12. public bool Healthy = true;
  13. public string Team { get; set; }
  14. public int Number { get; set; }
  15. public int OrigNumber { get; set; }
  16. public int Initiative { get; set; }
  17. public int HitPoints { get; set; }
  18. public int Atk { get; set; }
  19. public int AtkBonus { get; set; }
  20. public string AtkType { get; set; }
  21. public HashSet<string> Weaknesses = new HashSet<string>();
  22. public HashSet<string> Immunities = new HashSet<string>();
  23. public int EffectivePower => Number * (Atk + AtkBonus);
  24. public Group Target { get; set; }
  25. public bool Targeted { get; set; }
  26. private int _computeDamage(Group target)
  27. {
  28. if (target.Immunities.Contains(AtkType)) return 0;
  29. int damage = EffectivePower;
  30. if (target.Weaknesses.Contains(AtkType)) damage *= 2;
  31. return damage;
  32. }
  33. public int ComputeDamage(Group target)
  34. {
  35. int damage = _computeDamage(target);
  36. #if PRINT
  37. Console.WriteLine($"{Team} group {Id} would deal defending group {target.Id} {damage} damage");
  38. #endif
  39. return damage;
  40. }
  41. public int DealDamage(Group target)
  42. {
  43. var kill = target.ReceiveDamage(this);
  44. #if PRINT
  45. Console.WriteLine($"{Team} group {Id} attacks defending group {target.Id}, killing {kill} units");
  46. #endif
  47. return kill;
  48. }
  49. int ReceiveDamage(Group source)
  50. {
  51. var damage = source._computeDamage(this);
  52. int kill = damage / HitPoints;
  53. kill = Math.Min(kill, Number);
  54. Number -= kill;
  55. if (Number <= 0) Healthy = false;
  56. return kill;
  57. }
  58. }
  59. public class Program
  60. {
  61. static void Main(string[] args)
  62. {
  63. if (args.Length < 1) return;
  64. if (File.Exists(args[0]) == false) return;
  65. List<Group> all;
  66. Dictionary<string, int> teams;
  67. ParseFile(args, out all, out teams);
  68. while (teams.All(t => t.Value > 0))
  69. {
  70. #if PRINT
  71. PrintGroups(all, teams);
  72. Console.WriteLine("");
  73. #endif
  74. TargetSelectionPhase(all);
  75. #if PRINT
  76. Console.WriteLine("");
  77. #endif
  78. AttackPhase(all, teams);
  79. #if PRINT
  80. Console.WriteLine("");
  81. #endif
  82. }
  83. int answer = all.Sum(a => a.Number);
  84. Console.WriteLine($"The answer is : {answer}");
  85. }
  86. private static void PrintGroups(List<Group> all, Dictionary<string, int> teams)
  87. {
  88. foreach (var team in teams)
  89. {
  90. int printed = 0;
  91. Console.WriteLine($"{team.Key}:");
  92. foreach (var group in all.Where(a => a.Team == team.Key))
  93. {
  94. if (group.Healthy == false) continue;
  95. Console.WriteLine($"Group {group.Id} contains {group.Number} units");
  96. printed++;
  97. }
  98. if (printed == 0) Console.WriteLine($"No groups remain.");
  99. }
  100. }
  101. public static void ParseFile(string[] args, out List<Group> all, out Dictionary<string, int> teams)
  102. {
  103. var file = File.OpenText(args[0]);
  104. 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+)");
  105. string rgroup = string.Empty;
  106. all = new List<Group>();
  107. teams = new Dictionary<string, int>();
  108. do
  109. {
  110. var line = file.ReadLine();
  111. if (line == null) break;
  112. if (line == string.Empty)
  113. {
  114. rgroup = string.Empty;
  115. continue;
  116. }
  117. if (rgroup == string.Empty)
  118. {
  119. rgroup = line.Substring(0, line.Length - 1);
  120. teams.Add(rgroup, 0);
  121. continue;
  122. }
  123. var res = reg.Match(line);
  124. var g = new Group();
  125. g.Team = rgroup;
  126. teams[rgroup] = teams[rgroup] + 1;
  127. g.Id = teams[rgroup];
  128. g.Number = int.Parse(res.Groups["n"].Value);
  129. g.OrigNumber = g.Number;
  130. g.HitPoints = int.Parse(res.Groups["hp"].Value);
  131. g.Atk = int.Parse(res.Groups["atk"].Value);
  132. g.AtkType = res.Groups["atkt"].Value;
  133. g.Initiative = int.Parse(res.Groups["ini"].Value);
  134. for (var i = 1; i <= 2; ++i)
  135. {
  136. if (res.Groups[$"att{i}"].Success == false) break;
  137. var type = res.Groups[$"att{i}"].Value;
  138. foreach (var att in res.Groups[$"att{i}s"].Value.Split(", "))
  139. {
  140. if (type.Equals("weak")) g.Weaknesses.Add(att);
  141. else if (type.Equals("immune")) g.Immunities.Add(att);
  142. }
  143. }
  144. all.Add(g);
  145. } while (true);
  146. file.Close();
  147. }
  148. public static bool AttackPhase(List<Group> all, Dictionary<string, int> teams)
  149. {
  150. var attackingOrder = all
  151. .OrderByDescending(s => s.Initiative);
  152. int attacks = 0;
  153. foreach (var group in attackingOrder)
  154. {
  155. if (group.Target == null) continue;
  156. if (group.Healthy == false) continue;
  157. var kill = AttackTarget(teams, group);
  158. if (kill > 0) attacks++;
  159. }
  160. return attacks > 0;
  161. }
  162. private static int AttackTarget(Dictionary<string, int> teams, Group group)
  163. {
  164. var target = group.Target;
  165. group.Target = null;
  166. target.Targeted = false;
  167. var kill = group.DealDamage(target);
  168. if (target.Healthy == false)
  169. {
  170. if (target.Target != null)
  171. {
  172. target.Target.Targeted = false;
  173. target.Target = null;
  174. }
  175. teams[target.Team] = teams[target.Team] - 1;
  176. }
  177. return kill;
  178. }
  179. public static void TargetSelectionPhase(List<Group> all)
  180. {
  181. var targetingOrder = all
  182. .OrderByDescending(a => a.EffectivePower)
  183. .ThenByDescending(a => a.Initiative);
  184. foreach (var group in targetingOrder)
  185. {
  186. if (group.Healthy == false) continue;
  187. Group max = PickTarget(all, group);
  188. if (max == null) continue;
  189. group.Target = max;
  190. max.Targeted = true;
  191. }
  192. }
  193. private static Group PickTarget(List<Group> all, Group group)
  194. {
  195. Group max = null;
  196. int maxDamage = 0;
  197. foreach (var target in all)
  198. {
  199. if (target.Team == group.Team) continue;
  200. if (target.Targeted == true) continue;
  201. if (target.Healthy == false) continue;
  202. int damage = group.ComputeDamage(target);
  203. if (damage == 0) continue;
  204. if (damage > maxDamage)
  205. {
  206. maxDamage = damage;
  207. max = target;
  208. }
  209. else if (damage == maxDamage)
  210. {
  211. if (target.EffectivePower > max.EffectivePower)
  212. max = target;
  213. else if (target.EffectivePower == max.EffectivePower)
  214. {
  215. if (target.Initiative > max.Initiative)
  216. max = target;
  217. }
  218. }
  219. }
  220. return max;
  221. }
  222. }
  223. }