Advertisement
eltea

Advent of Code 2020 Day 13 Parts 1 and 2

Dec 14th, 2020
600
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.39 KB | None | 0 0
  1. //Advent of Code 2020 Day 13 Parts 1 and 2 solution by Mike LeSauvage
  2. public class BusScheduleAnalyzer : MonoBehaviour
  3. {
  4.     [SerializeField] TextAsset busScheduleTextfile = null;  //Hooked up in the input text in the Unity editor.
  5.     [SerializeField] long startTime = 100000000000000;      //Chosen as the test guide says it will surely be a larger number than this. SURELY!
  6.                                                             //In practice, effect on calculation time in negligible. Probably included as a
  7.                                                             //warning of the need to use long vs int.
  8.  
  9.     void Start()
  10.     {
  11.         string[] busScheduleStrings = busScheduleTextfile.text.Split('\n');
  12.         SolvePartOne(busScheduleStrings);
  13.         SolvePartTwo(busScheduleStrings);
  14.     }
  15.  
  16.     void SolvePartTwo(string[] busScheduleStrings)
  17.     {
  18.         List<(int busNum, int requiredDepartureOffset)> busDepartureInfo = new List<(int, int)>();
  19.         string[] allBusNumbersAsStrings = busScheduleStrings[1].Split(',');
  20.  
  21.         //Fill out the list of bus numbers and their required arrival offsets. In format (busNumber, offset)
  22.         for (int i = 0; i < allBusNumbersAsStrings.Length; i++)
  23.         {
  24.             try
  25.             {
  26.                 busDepartureInfo.Add((int.Parse(allBusNumbersAsStrings[i]), i));
  27.             }
  28.             catch (FormatException)
  29.             {
  30.                 //Nothing to do here!
  31.             }
  32.         }
  33.  
  34.         //Choose the initial start time by finding the nearest or greater departure time of the first bus as we did in part 1.
  35.         startTime = GetEqualOrGreaterDepartureTime(startTime, busDepartureInfo[0].busNum);
  36.  
  37.         long incrementAmount = busDepartureInfo[0].busNum; //As each periodicity pairing is found, increase the increment.
  38.         int lockedIn=0;                                    //Keeps track of which bags have been matched to a periodicity with the group.
  39.  
  40.        
  41.         // Solution approach
  42.         //-------------------
  43.         // For any two elements a and b that each have their own periodicty, when treated as a group they have periodicity a*b.
  44.         // EG: one planet does a loop in 50 days, another in 70 days, if they line up at day=0 they'll line up again at 50*70 = 3500 days.
  45.         // So, this approach is to find the match with the next bus, then change the periodicity by multiplying by that bus's loop time
  46.         //   Then, move on to add another bus to the match and repeat.
  47.         for (long testTime=startTime; true; testTime+=incrementAmount)
  48.         {
  49.             int nextBusToLookFor = lockedIn + 1;
  50.             long requiredDepartureTime = testTime + busDepartureInfo[nextBusToLookFor].requiredDepartureOffset;
  51.             long nearestDepartureTime = GetEqualOrGreaterDepartureTime(requiredDepartureTime, busDepartureInfo[nextBusToLookFor].busNum);
  52.            
  53.             if(requiredDepartureTime == nearestDepartureTime)
  54.             {
  55.                 incrementAmount *= busDepartureInfo[nextBusToLookFor].busNum;
  56.                 lockedIn = nextBusToLookFor;
  57.                    
  58.                 if (lockedIn == busDepartureInfo.Count - 1) //They're all lined up!
  59.                 {
  60.                     Debug.Log($"Part Two: Earliest departure is Bus ID {busDepartureInfo[0].busNum} on departure {testTime}");
  61.                     break;
  62.                 }
  63.             }
  64.         }
  65.     }
  66.  
  67.  
  68.     void SolvePartOne(string[] busScheduleStrings)
  69.     {
  70.         int targetDepartureTime = int.Parse(busScheduleStrings[0]);
  71.         string[] allBusNumbersAsStrings = busScheduleStrings[1].Split(',');
  72.  
  73.         Dictionary<int, int> busDeparturesDict = new Dictionary<int, int>();
  74.  
  75.         //Fill the dictionary with the bus numbers.
  76.         //Departure time is -1 until analyzed.
  77.         foreach (string busNumString in allBusNumbersAsStrings)
  78.         {
  79.             try
  80.             {
  81.                 int busNum = int.Parse(busNumString);
  82.                 busDeparturesDict.Add(busNum, -1);
  83.             }
  84.             catch (FormatException)
  85.             {
  86.                 //Nothing to do here.
  87.             }
  88.         }
  89.  
  90.         List<int> busNumbers = new List<int>(busDeparturesDict.Keys);  //Get a list of the dictionary keys as dictionaries cannot be modified in a foreach loop.
  91.  
  92.         //Fill out the dictionary of bus IDs with their departure times equal to or greater than the target (From line 0 of the file).
  93.         foreach (int busNum in busNumbers)
  94.         {
  95.             busDeparturesDict[busNum] = (int)GetEqualOrGreaterDepartureTime(targetDepartureTime, busNum);
  96.         }
  97.  
  98.         //Choose one bus departure time arbitrarily for seed of shortest time.
  99.         int bestBus = busNumbers[0];
  100.  
  101.         foreach (int busNum in busNumbers)
  102.         {
  103.             if (busDeparturesDict[busNum] < busDeparturesDict[bestBus])
  104.             {
  105.                 bestBus = busNum;
  106.             }
  107.         }
  108.  
  109.         int waitTime = busDeparturesDict[bestBus] - targetDepartureTime;
  110.         int partOneSolution = waitTime * bestBus;
  111.         Debug.Log($"Part One Best bus is {bestBus} departing at {busDeparturesDict[bestBus]}");
  112.         Debug.Log($"Part One Time to wait is: {waitTime}");
  113.         Debug.Log($"Part One Solution: {partOneSolution}");
  114.     }
  115.  
  116.     long GetEqualOrGreaterDepartureTime(long targetDepartureTime, int busNum)
  117.     {
  118.         //Busses get around the loop in the time equal to their bus number.
  119.         //All busses depart at t=0.
  120.         //Find the time the bus is at the station greater than or equal to the target departure.
  121.         //To find it:
  122.         //  1) Find the number of cycles the bus runs in the target departure time.
  123.         //  2) If there's a remainder, round up to the next highest number of cycles.
  124.         //  3) Multiply the number of cycles by the bus' loop time to get the arrival that is >= the desired departure time.
  125.         //
  126.         // An earlier solution used Mathf.CeilToInt() but that approach failed when using long values (it would wrap to -ve numbers)
  127.         // So this more manual solution was needed.
  128.  
  129.         long quotient = targetDepartureTime / busNum;    // (1)
  130.         long remainder = targetDepartureTime % busNum;
  131.         if(remainder > 0)
  132.         {
  133.             quotient++;                                  // (2)
  134.         }
  135.         long earliestDepartureTime = quotient * busNum;  // (3)
  136.        
  137.         return earliestDepartureTime;
  138.     }
  139.  
  140. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement