Program.cs 8.0 KB

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