|
|
@@ -0,0 +1,264 @@
|
|
|
+//#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 Initiative { get; set; }
|
|
|
+ public int HitPoints { get; set; }
|
|
|
+ public int Atk { 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;
|
|
|
+ public Group Target { get; set; }
|
|
|
+ public bool Targeted { get; set; }
|
|
|
+
|
|
|
+ private int _computeDamage(Group target)
|
|
|
+ {
|
|
|
+ int damage = 0;
|
|
|
+ if (target.Weaknesses.Contains(AtkType))
|
|
|
+ damage = 2 * EffectivePower;
|
|
|
+ else if (target.Immunities.Contains(AtkType) == false)
|
|
|
+ damage = EffectivePower;
|
|
|
+ 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 void DealDamage(Group target)
|
|
|
+ {
|
|
|
+ var kill = target.ReceiveDamage(this);
|
|
|
+#if PRINT
|
|
|
+ Console.WriteLine($"{Team} group {Id} attacks defending group {target.Id}, killing {kill} units");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private 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.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();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void AttackPhase(List<Group> all, Dictionary<string, int> teams)
|
|
|
+ {
|
|
|
+ var attackingOrder = all
|
|
|
+ .OrderByDescending(s => s.Initiative);
|
|
|
+
|
|
|
+ foreach (var group in attackingOrder)
|
|
|
+ {
|
|
|
+ if (group.Target == null) continue;
|
|
|
+ if (group.Healthy == false) continue;
|
|
|
+
|
|
|
+ AttackTarget(teams, group);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void AttackTarget(Dictionary<string, int> teams, Group group)
|
|
|
+ {
|
|
|
+ var target = group.Target;
|
|
|
+ group.Target = null;
|
|
|
+ target.Targeted = false;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void TargetSelectionPhase(List<Group> all)
|
|
|
+ {
|
|
|
+ var targetingOrder = all
|
|
|
+ .OrderByDescending(a => a.EffectivePower)
|
|
|
+ .ThenBy(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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|