Advertisement
drpanwe

Ghost In the Shell - CodinGame

Oct 29th, 2021
849
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 20.99 KB | None | 0 0
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5.  
  6. // To debug: Console.Error.WriteLine("Debug messages...");
  7. internal class Player
  8. {
  9.     private const int MAX_PRODUCTION = 3;
  10.     private const int UPGRADE_COST = 10;
  11.     private static string[] _inputs;
  12.     private static bool _isBombingAvailable;
  13.     private static bool _isFirstRound;
  14.     private static List<string> _commands;
  15.     private static List<Bomb> _bombStates;
  16.     private static int _factoryCount;
  17.     private static List<Factory> _wantedFactories;
  18.     private static Factory _friendlyHq;
  19.     private static Factory _enemyHq;
  20.     private static List<Factory> _allFactories;
  21.  
  22.     private static void Main(string[] args)
  23.     {
  24.         _isFirstRound = true;
  25.         _factoryCount = int.Parse(Console.ReadLine());
  26.         int linkCount = int.Parse(Console.ReadLine()); // the number of links between factories
  27.         List<Link> map = new List<Link>();
  28.         _isBombingAvailable = true;
  29.         for (int i = 0; i < linkCount; i++)
  30.         {
  31.             _inputs = Console.ReadLine().Split(' ');
  32.             int factory1 = int.Parse(_inputs[0]);
  33.             int factory2 = int.Parse(_inputs[1]);
  34.             int distance = int.Parse(_inputs[2]);
  35.             map.Add(new Link(factory1, factory2, distance));
  36.         }
  37.  
  38.         _commands = new List<string>();
  39.         _bombStates = new List<Bomb>();
  40.  
  41.         while (true) // game loop
  42.         {
  43.             UpdateGame(map);
  44.         }
  45.     }
  46.  
  47.     private static void UpdateGame(List<Link> map)
  48.     {
  49.         _commands.Clear(); // Reset commands each turn.
  50.         List<Entity> entities = new List<Entity>();
  51.         int entityCount = int.Parse(Console.ReadLine()); // the number of entities (e.g. factories and troops)
  52.         for (int i = 0; i < entityCount; i++)
  53.         {
  54.             _inputs = Console.ReadLine().Split(' ');
  55.             ParseEntity(_inputs, entities);
  56.         }
  57.  
  58.         _allFactories = entities.Where(e => e.GetType() == typeof(Factory)).Select(x => (Factory)x).ToList();
  59.         List<Factory> friendlyFactories = _allFactories.Where(e => e.IsFriendly).ToList();
  60.         List<Factory> nonFriendlyFactories = _allFactories.Where(e => !e.IsFriendly).ToList();
  61.         List<Factory> hostileFactories = _allFactories.Where(e => e.IsHostile).ToList();
  62.        
  63.         if (friendlyFactories.Any() && hostileFactories.Any())
  64.         {
  65.             int production = friendlyFactories.Sum(f => f.Production);
  66.             int enemyProduction = hostileFactories.Sum(f => f.Production);
  67.             if (production == enemyProduction)
  68.             {
  69.                 _commands.Add($"MSG Production is equal!");
  70.             }
  71.             else
  72.             {
  73.                 int diffBetweenProduction = production - enemyProduction;
  74.                 if (diffBetweenProduction > 2)
  75.                 {
  76.                     _commands.Add($"MSG Production superior!");
  77.                 }
  78.             }
  79.  
  80.         }
  81.         List<Troop> enemyTroops = entities.Where(e => e.IsHostile && e.GetType() == typeof(Troop))
  82.             .Select(t => (Troop)t).ToList();
  83.         List<Bomb> bombsPresent = entities.Where(e => e.GetType() == typeof(Bomb)).Select(b => (Bomb)b)
  84.             .ToList();
  85.         UpdateBombStates(bombsPresent);
  86.  
  87.         if (_isBombingAvailable)
  88.         {
  89.             SendBomb(map, entities, friendlyFactories, nonFriendlyFactories);
  90.         }
  91.  
  92.         if (_isFirstRound)
  93.         {
  94.             _wantedFactories = _allFactories;
  95.             _friendlyHq = friendlyFactories.First();
  96.             _enemyHq = hostileFactories.First();
  97.             ExecuteFirstRound(friendlyFactories, map, nonFriendlyFactories);
  98.         }
  99.         else
  100.         {
  101.             ExecuteRound(map, friendlyFactories, nonFriendlyFactories, enemyTroops, bombsPresent);
  102.         }
  103.  
  104.         ExecuteWaitCommandAsFallback();
  105.         ExecuteCommands();
  106.         // Any valid action, such as "WAIT" or "MOVE source destination cyborgs"
  107.     }
  108.  
  109.     private static void ExecuteCommands()
  110.     {
  111.         Console.WriteLine(string.Join(';', _commands));
  112.     }
  113.  
  114.     private static void ExecuteWaitCommandAsFallback()
  115.     {
  116.         if (_commands.Count == 0)
  117.         {
  118.             _commands.Add("WAIT");
  119.         }
  120.     }
  121.  
  122.     private static void ExecuteRound(List<Link> map, List<Factory> friendlyFactories, List<Factory> nonFriendlyFactories, List<Troop> enemyTroops, List<Bomb> bombs)
  123.     {
  124.         foreach (Factory factory in friendlyFactories)
  125.         {
  126.             if (ShouldEvacuateFactory(factory, _allFactories, map))
  127.             {
  128.                 _commands.AddRange(Evacuate(friendlyFactories, factory, map));
  129.                 continue;
  130.             }
  131.  
  132.             if (factory.Production == 0)
  133.             {
  134.                 if (factory.Defense >= UPGRADE_COST)
  135.                 {
  136.                     Console.Error.WriteLine($"{factory.Id}: ZERO PROD UPGRADE!");
  137.                     _commands.Add($"INC {factory.Id}");
  138.                 }
  139.             }
  140.             else
  141.             {
  142.                 int availableCyborgs = CalculateDefenses(factory, enemyTroops);
  143.                 availableCyborgs = DefendFactories(factory, availableCyborgs, friendlyFactories, enemyTroops, map);
  144.                 if (ShouldIncreaseProduction(factory, friendlyFactories.Count, availableCyborgs, bombs.Any(b => b.IsHostile)))
  145.                 {
  146.                     Console.Error.WriteLine($"{factory.Id}: UPGRADE!");
  147.                     availableCyborgs -= 10;
  148.                     _commands.Add($"INC {factory.Id}");
  149.                 }
  150.  
  151.                 if(friendlyFactories.Count >= _wantedFactories.Count)
  152.                 {
  153.                     Factory upgradeTarget = friendlyFactories.FirstOrDefault(f => f.Production == 0 && f.Id != factory.Id);
  154.                     if (upgradeTarget != null && upgradeTarget.Defense < UPGRADE_COST)
  155.                     {
  156.                         Console.Error.WriteLine($"{factory.Id}: UPGRADE TARGET {upgradeTarget.Id}!");
  157.                         _commands.Add($"MOVE {factory.Id} {upgradeTarget.Id} {UPGRADE_COST - upgradeTarget.Defense}");
  158.                     }
  159.                 }
  160.  
  161.                 if (factory.Production == MAX_PRODUCTION || friendlyFactories.Count < _wantedFactories.Count)
  162.                 {
  163.                     int target = FindTarget(factory, nonFriendlyFactories, bombs, map);
  164.                     if (target != factory.Id)
  165.                     {
  166.                         Console.Error.WriteLine($"{factory.Id}: RELOCATE TO {target}!");
  167.                         RelocateCyborgs(map, factory, availableCyborgs, target);
  168.                     }
  169.                 }
  170.             }
  171.         }
  172.     }
  173.  
  174.     private static void RelocateCyborgs(List<Link> map, Factory factory, int availableCyborgs, int target)
  175.     {
  176.         int path = FindPath(factory, target, map);
  177.         if (availableCyborgs > 0)
  178.         {
  179.             _commands.Add($"MOVE {factory.Id} {path} {availableCyborgs}");
  180.         }
  181.     }
  182.  
  183.     private static void SendBomb(List<Link> map, List<Entity> entities, List<Factory> friendlyFactories, List<Factory> nonFriendlyFactories)
  184.     {
  185.         Factory enemyHq = (Factory)entities.First(e => e.IsHostile && e.GetType() == typeof(Factory));
  186.         List<int> linkedFactories = GetClosestXLinkedFactories(enemyHq, map, 3);
  187.         int bombTarget = nonFriendlyFactories.Where(t => linkedFactories.Contains(t.Id))
  188.             .OrderByDescending(t => t.Production).First().Id;
  189.         _commands.Add($"BOMB {friendlyFactories.First().Id} {enemyHq.Id}");
  190.         _commands.Add($"BOMB {friendlyFactories.First().Id} {bombTarget}");
  191.         _isBombingAvailable = false;
  192.     }
  193.  
  194.     private static void ExecuteFirstRound(List<Factory> friendlyFactories, List<Link> map, List<Factory> nonFriendlyFactories)
  195.     {
  196.         Factory hq = friendlyFactories.First();
  197.         var requiredExpansions = (_factoryCount / 2);
  198.         List<int> closestExpansions = GetClosestXLinkedFactories(hq, map, requiredExpansions);
  199.         _wantedFactories = _wantedFactories.Where(f => closestExpansions.Contains(f.Id)).ToList();
  200.         _wantedFactories.Add(hq);
  201.         Console.Error.WriteLine("Wanted factories:");
  202.         foreach(var f in _wantedFactories)
  203.         {
  204.             Console.Error.WriteLine(f.Id);
  205.         }
  206.         IOrderedEnumerable<Factory> prioritizedTargets = nonFriendlyFactories.Where(f => closestExpansions.Contains(f.Id))
  207.             .OrderByDescending(f => (f.Production * 10) / (f.Defense + 1));
  208.         int availableCyborgs = hq.Defense;
  209.         foreach (Factory target in prioritizedTargets)
  210.         {
  211.             if (availableCyborgs < target.Defense)
  212.             {
  213.                 continue;
  214.             }
  215.  
  216.             int requiredForces = target.Defense + 1;
  217.             _commands.Add($"MOVE {hq.Id} {target.Id} {requiredForces}");
  218.             availableCyborgs -= requiredForces;
  219.         }
  220.         _isFirstRound = false;
  221.     }
  222.  
  223.     private static void UpdateBombStates(List<Bomb> bombsPresent)
  224.     {
  225.         if (!bombsPresent.Any())
  226.         {
  227.             _bombStates.Clear();
  228.         }
  229.         else
  230.         {
  231.             _bombStates.AddRange(bombsPresent.Where(b => !_bombStates.Select(s => s.Id).Contains(b.Id)));
  232.             _bombStates.RemoveAll(b => b.Age == b.ETA);
  233.             foreach (Bomb b in _bombStates)
  234.             {
  235.                 b.Age++;
  236.             }
  237.         }
  238.     }
  239.  
  240.     private static List<string> Evacuate(List<Factory> friendlyFactories, Factory factory, List<Link> map)
  241.     {
  242.         IEnumerable<Factory> evacuationCandidates = friendlyFactories.Where(f => f.Id != factory.Id);
  243.         int evacuationTarget = 0;
  244.         int closestDistance = int.MaxValue;
  245.         foreach (Factory candidate in evacuationCandidates)
  246.         {
  247.             Link link = GetLinkBetween(factory, candidate, map);
  248.             if (link.Distance < closestDistance)
  249.             {
  250.                 closestDistance = link.Distance;
  251.                 evacuationTarget = candidate.Id;
  252.             }
  253.         }
  254.  
  255.         return new List<string> { $"MSG Evacuating {factory.Id}", $"MOVE {factory.Id} {evacuationTarget} {factory.Defense}" };
  256.     }
  257.  
  258.     private static bool ShouldEvacuateFactory(Factory factory, List<Factory> factories, List<Link> map)
  259.     {
  260.         if (_bombStates.Count == 0 || !factories.Any(f => f.IsHostile)) // check hostiles left due to bomb crashing code if game already won.
  261.         {
  262.             return false;
  263.         }
  264.  
  265.         foreach (Bomb bomb in _bombStates)
  266.         {
  267.             if (bomb.Source == factory.Id)
  268.             { // Target will never be source factory
  269.                 continue;
  270.             }
  271.             else if (IsHostileBomb(bomb))
  272.             {
  273.                 Factory bombSource = factories.First(f => f.Id == bomb.Source);
  274.                 int distance = GetLinkBetween(factory, bombSource, map).Distance;
  275.                 if (distance - bomb.Age == 1)
  276.                 {
  277.                     return true;
  278.                 }
  279.             }
  280.             else
  281.             {
  282.                 if (bomb.Target == factory.Id && bomb.ETA == 1)
  283.                 {
  284.                     return true;
  285.                 }
  286.             }
  287.         }
  288.  
  289.         return false;
  290.     }
  291.  
  292.     private static bool IsHostileBomb(Bomb bomb)
  293.     {
  294.         return bomb.Target == -1;
  295.     }
  296.  
  297.     private static int DefendFactories(Factory source, int availableCyborgs, List<Factory> friendlyFactories, List<Troop> enemyTroops, List<Link> map)
  298.     {
  299.         var defenseCandidates = friendlyFactories.Where(f => f.Id != source.Id).Select(f => GetLinkBetween(source, f, map)).Where(f => f.Distance <= 6).OrderBy(f => f.Distance)
  300.             .Select(f => new { f.Distance, Id = f.Factory1 == source.Id ? f.Factory2 : f.Factory1 }).Take(3);
  301.         foreach (var candidate in defenseCandidates)
  302.         {
  303.             Factory targetFactory = friendlyFactories.First(f => f.Id == candidate.Id);
  304.             List<Troop> attackers = enemyTroops.Where(t => t.Target == targetFactory.Id).ToList();
  305.             if (!attackers.Any())
  306.             {
  307.                 continue;
  308.             }
  309.  
  310.             int attackingTroops = attackers.Where(t => t.ETA <= candidate.Distance).Sum(t => t.Strength);
  311.             int defendingTroops = targetFactory.Defense + (targetFactory.Production * candidate.Distance);
  312.             if (attackingTroops <= defendingTroops)
  313.             {
  314.                 continue;
  315.             }
  316.  
  317.             int backupRequired = attackingTroops - defendingTroops;
  318.             int sentBackup = backupRequired > availableCyborgs ? availableCyborgs : backupRequired;
  319.             _commands.Add($"MOVE {source.Id} {candidate.Id} {sentBackup}");
  320.             return availableCyborgs - sentBackup;
  321.         }
  322.  
  323.         return availableCyborgs;
  324.     }
  325.  
  326.     // Calculate required defenses six turns ahead, considering already sent enemy troops.
  327.     // Does not evaluate enemy troops not sent yet by close enemy factories.
  328.     private static int CalculateDefenses(Factory source, List<Troop> enemyTroops)
  329.     {
  330.         List<Troop> attackers = enemyTroops.Where(t => t.Target == source.Id && t.ETA <= 6).ToList();
  331.         if (attackers.Count == 0)
  332.         {
  333.             return source.Defense;
  334.         }
  335.         int incomingAttackers = attackers.Sum(t => t.Strength);
  336.         if (source.Defense > incomingAttackers)
  337.         {
  338.             return source.Defense - incomingAttackers;
  339.         }
  340.  
  341.         return 0;
  342.     }
  343.  
  344.     private static int FindPath(Factory source, int targetFactoryId, List<Link> map)
  345.     {
  346.         Link directPath = map.First(l =>
  347.             (l.Factory1 == source.Id && l.Factory2 == targetFactoryId) ||
  348.             l.Factory1 == targetFactoryId && l.Factory2 == source.Id);
  349.         int directDistance = directPath.Distance;
  350.         IEnumerable<Link> alternativePaths = map.Where(l => l.Factory1 == source.Id || l.Factory2 == source.Id).Where(l =>
  351.             !(l.Factory1 == source.Id && l.Factory2 == targetFactoryId) &&
  352.             !(l.Factory1 == targetFactoryId && l.Factory2 == source.Id));
  353.         List<Factory> candidates = new List<Factory>();
  354.         foreach (Link alternativePath in alternativePaths)
  355.         {
  356.             int intermediateDistance = alternativePath.Distance;
  357.             int intermediateFactoryId = alternativePath.Factory1 == source.Id
  358.                 ? alternativePath.Factory2
  359.                 : alternativePath.Factory1;
  360.             Link directPathFromIntermediate =
  361.                 map.First(l =>
  362.                     (l.Factory1 == intermediateFactoryId && l.Factory2 == targetFactoryId) ||
  363.                     l.Factory2 == intermediateFactoryId && l.Factory1 == targetFactoryId);
  364.             if (intermediateDistance + directPathFromIntermediate.Distance <= directDistance)
  365.             {
  366.                 Factory intermediateFactory = _allFactories.First(f => f.Id == intermediateFactoryId);
  367.                 intermediateFactory.DistanceTo = intermediateDistance;
  368.                 candidates.Add(intermediateFactory);
  369.             }
  370.         }
  371.  
  372.         if (candidates.Any())
  373.         {
  374.             return candidates.OrderBy(c => c.DistanceTo).ThenByDescending(c => c.Production).First().Id;
  375.         }
  376.         return targetFactoryId;
  377.     }
  378.  
  379.     private static int FindTarget(Factory source, List<Factory> targets, List<Bomb> friendlyBombs, List<Link> map)
  380.     {
  381.         if (_wantedFactories.All(f => _allFactories.First(a => a.Id == f.Id).IsFriendly))
  382.         {
  383.             // If we control the required factories, send troops to front line.
  384.             var candidates = _wantedFactories.Where(f => f.Id != source.Id && !TargetWillBeBombed(friendlyBombs, GetLinkBetween(source, f, map)))
  385.                 .OrderBy(f => GetLinkBetween(f, _enemyHq, map).Distance);
  386.             var selectedTarget = candidates.FirstOrDefault()?.Id ?? source.Id;
  387.             Console.Error.WriteLine($"{source.Id} selected target {selectedTarget}");
  388.             return selectedTarget;
  389.         }
  390.         else
  391.         {
  392.             Console.Error.WriteLine($"HQ: {_friendlyHq.Id}");
  393.             var candidates = _wantedFactories.Where(f => f.Id != source.Id && f.Id != _friendlyHq.Id && !TargetWillBeBombed(friendlyBombs, GetLinkBetween(source, f, map)))
  394.                 .OrderBy(f => _allFactories.Find(a => a.Id == f.Id).Team).ThenBy(f => GetLinkBetween(f, _friendlyHq, map).Distance);
  395.             Console.Error.WriteLine(string.Join(" ", candidates.Select(c => c.Id)));
  396.             Console.Error.WriteLine(string.Join(" ", candidates.Select(c => c.Team)));
  397.             var selectedTarget = candidates.FirstOrDefault()?.Id ?? source.Id;
  398.             Console.Error.WriteLine($"{source.Id} selected target {selectedTarget}");
  399.             return selectedTarget;
  400.         }
  401.     }
  402.  
  403.     private static bool TargetWillBeBombed(List<Bomb> friendlyBombs, Link link)
  404.     {
  405.         return !friendlyBombs.All(b => b.ETA != link.Distance && b.ETA != link.Distance + 1);
  406.     }
  407.  
  408.     private static Link GetLinkBetween(Factory source, Factory target, List<Link> map)
  409.     {
  410.         if (source.Id == target.Id)
  411.         {
  412.             throw new ArgumentException("Source and Target can not be the same factory");
  413.         }
  414.         return map.First(l =>
  415.             (l.Factory1 == source.Id && l.Factory2 == target.Id) ||
  416.             (l.Factory1 == target.Id && l.Factory2 == source.Id));
  417.     }
  418.  
  419.     private static List<int> GetClosestXLinkedFactories(Factory source, List<Link> map, int take)
  420.     {
  421.         IEnumerable<Link> links = map.Where(m => m.Factory1 == source.Id || m.Factory2 == source.Id).OrderBy(l => l.Distance)
  422.             .Take(take);
  423.         return links.Select(link => link.Factory1 == source.Id ? link.Factory2 : link.Factory1).ToList();
  424.     }
  425.  
  426.     private static bool ShouldIncreaseProduction(Factory source, int factoryCount, int availableCyborgs, bool bombPresent)
  427.     {
  428.         return source.Production != MAX_PRODUCTION && factoryCount >= _wantedFactories.Count && availableCyborgs >= 10 && !bombPresent;
  429.     }
  430.  
  431.     private static void ParseEntity(string[] inputs, List<Entity> entities)
  432.     {
  433.         int entityId = int.Parse(inputs[0]);
  434.         string entityType = inputs[1];
  435.         int entityTeam = int.Parse(inputs[2]);
  436.         int arg2 = int.Parse(inputs[3]);
  437.         int arg3 = int.Parse(inputs[4]);
  438.         int arg4 = int.Parse(inputs[5]);
  439.         int arg5 = int.Parse(inputs[6]);
  440.         switch (entityType)
  441.         {
  442.             case "BOMB":
  443.                 entities.Add(new Bomb
  444.                 {
  445.                     Id = entityId,
  446.                     Type = entityType,
  447.                     Team = entityTeam,
  448.                     Source = arg2,
  449.                     Target = arg3,
  450.                     ETA = arg4
  451.                 });
  452.                 break;
  453.             case "TROOP":
  454.                 entities.Add(new Troop
  455.                 {
  456.                     Id = entityId,
  457.                     Type = entityType,
  458.                     Team = entityTeam,
  459.                     Source = arg2,
  460.                     Target = arg3,
  461.                     Strength = arg4,
  462.                     ETA = arg5
  463.                 });
  464.                 break;
  465.             default:
  466.                 entities.Add(new Factory
  467.                 {
  468.                     Id = entityId,
  469.                     Type = entityType,
  470.                     Team = entityTeam,
  471.                     Defense = arg2,
  472.                     Production = arg3,
  473.                     Cooldown = arg4
  474.                 });
  475.                 break;
  476.         }
  477.     }
  478.  
  479.     public class Link
  480.     {
  481.         public int Factory1 { get; }
  482.         public int Factory2 { get; }
  483.         public int Distance { get; }
  484.  
  485.         public Link(int factory1, int factory2, int distance)
  486.         {
  487.             Factory1 = factory1;
  488.             Factory2 = factory2;
  489.             Distance = distance;
  490.         }
  491.     }
  492.  
  493.     public class Target
  494.     {
  495.         public int Id { get; }
  496.         public int Defense { get; }
  497.         public int Distance { get; }
  498.         public int Production { get; }
  499.  
  500.         public Target(int id, int distance, int production, int defense)
  501.         {
  502.             Id = id;
  503.             Distance = distance;
  504.             Production = production;
  505.             Defense = defense;
  506.         }
  507.     }
  508.     public class Entity
  509.     {
  510.         public int Id { get; set; }
  511.         public string Type { get; set; }
  512.         public int Team { get; set; }
  513.         public bool IsFriendly => Team == 1;
  514.         public bool IsNeutral => Team == 0;
  515.         public bool IsHostile => Team == -1;
  516.     }
  517.  
  518.     private class Troop : Entity
  519.     {
  520.         public int Source { get; set; }
  521.         public int Target { get; set; }
  522.         public int Strength { get; set; }
  523.         public int ETA { get; set; }
  524.     }
  525.  
  526.     public class Factory : Entity
  527.     {
  528.         public int Defense { get; set; }
  529.         public int Production { get; set; }
  530.         public int Cooldown { get; set; }
  531.         public int DistanceTo { get; set; }
  532.     }
  533.  
  534.     public class Bomb : Entity
  535.     {
  536.         public int Source { get; set; }
  537.         public int Target { get; set; }
  538.         public int ETA { get; set; }
  539.         public int Age { get; set; }
  540.     }
  541. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement