Advertisement
eltea

Advent of Code 2020 Day 14 Parts 1 and 2

Dec 14th, 2020
262
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.42 KB | None | 0 0
  1. //Advent of Code 2020 Day 14 Parts 1 and 2 solution by Mike LeSauvage
  2. public class DockingDataAnalyzer : MonoBehaviour
  3. {
  4.     [SerializeField] TextAsset dockingDataTextfile = null;   //Hooked up in the input text in the Unity editor.
  5.     string maskRegex = @"mask = ([10X]{36})$";                        //Gets the 36-digit mask
  6.     string memWriteRegex = @"mem\[(?<address>\d+)\] = (?<data>\d+)";  //Gets the memory address (labelled address) and the data to be written (labelled data)
  7.  
  8.  
  9.     // Start is called before the first frame update
  10.     void Start()
  11.     {
  12.         string[] stringSeparator = { "\r\n" }; //Find two new lines for breaks. Windows encoding has both carriage return and line feed.
  13.         string[] dockingMemoryStrings = dockingDataTextfile.text.Split(stringSeparator, System.StringSplitOptions.RemoveEmptyEntries);
  14.  
  15.         SolvePartOne(dockingMemoryStrings);
  16.         SolvePartTwo(dockingMemoryStrings);
  17.     }
  18.  
  19.     void SolvePartTwo(string[] dockingMemoryStrings)
  20.     {
  21.         //floatingBitPositions stores which bits are Xs in the provided mask, in order from rightmost to leftmost bit.
  22.         //EG: floatingBitPositions[0]=4 tells you that the rightmost X was in bit position 4 such as: X0100
  23.         //    floatingBitPositions[1]=7 tells you the next X was in position 7 so mask could now be: X11X0100
  24.         List<int> floatingBitPositions = new List<int>();  
  25.        
  26.         Hashtable memory = new Hashtable();
  27.         long startingORMask = 0;
  28.         long cleanAddressANDMask = 0;
  29.  
  30.        
  31.         // Overall approach
  32.         // -----------------
  33.         // (1) Loop across the file. When a mask is found:
  34.         //     (2) Get a clean address AND mask that can clear the address' bits where Xs are present (so they can be iterated through for all combinations.)
  35.         //     (3) Get a starting OR mask that will be used to set all the 1s where there were 1s in the original mask.
  36.         //     (4) Record the bit positions of all the floating bits, from right to left, in the floatingBitPositions list.
  37.         //
  38.         // (5) Loop across each of the memory writes.
  39.         //   (6) Get an address where the X-position bits are cleared to zero using the clean address AND mask.
  40.         //   (7) Loop for a count of the number of bit combinations for the floating Xs. EG: 4 Xs = 2^4=16 loops (from 0000 to 1111).
  41.         //       (8) Check each bit in the count and map every '1' to the corresponding bit's position in the floatingBitPositions, ORing it in to the starting OR mask.
  42.         // (9) Apply the OR mask to the memory address.
  43.         // (10)Write the data out, using a hash so that duplicate locations are overwritten.
  44.        
  45.         foreach (string s in dockingMemoryStrings)
  46.         {
  47.             MatchCollection allMatches = Regex.Matches(s, maskRegex);
  48.             if (allMatches.Count == 1)                                                                                 //(1)
  49.             {
  50.                 floatingBitPositions.Clear();
  51.  
  52.                 //Prepare the address AND mask. This is used for clearing all the bits in the address where the        //(2)
  53.                 //floating bits are located for setting when iterating through all floating bit combinations.
  54.                 string cleanAddressANDMaskString = allMatches[0].Groups[1].Value.Replace('0', '1');
  55.                 cleanAddressANDMaskString = cleanAddressANDMaskString.Replace('X', '0');
  56.                 cleanAddressANDMask = Convert.ToInt64(cleanAddressANDMaskString, 2);
  57.  
  58.                 //Prepare the starting OR mask. This mask will later be manipulated to add 1s in the position of the   //(3)
  59.                 //floating bits to give every combination of of the mask (ie: 4 Xs is 16 combinations)
  60.                 char[] maskChars = allMatches[0].Groups[1].Value.ToCharArray();
  61.                 for (int i = maskChars.Length - 1; i >= 0; i--)
  62.                 {
  63.                     if (maskChars[i] == 'X')
  64.                     {
  65.                         maskChars[i] = '0';
  66.                         floatingBitPositions.Add(maskChars.Length - i - 1);                                            //(4)
  67.                     }
  68.                 }
  69.  
  70.                 string maskString = new string(maskChars);
  71.                 startingORMask = Convert.ToInt64(maskString, 2);
  72.             }
  73.  
  74.             allMatches = Regex.Matches(s, memWriteRegex);
  75.             if (allMatches.Count == 1)                                                                                 //(5)
  76.             {
  77.                 //Get address and clear floating bit locations.
  78.                 long address = long.Parse(allMatches[0].Groups["address"].Value);
  79.                 address &= cleanAddressANDMask;                                                                        //(6)
  80.  
  81.                 long data = long.Parse(allMatches[0].Groups["data"].Value);
  82.  
  83.                 long bitRange = (long)Mathf.Pow(2, floatingBitPositions.Count);  //Eg, 3 bits is 2^3=8 loop from 0->7 which will cover 000 to 111
  84.  
  85.                 //Loop for all combinations given by the number of bits.
  86.                 for (long i = 0; i < bitRange; i++)                                                                    //(7)
  87.                 {
  88.                     long orMask = startingORMask;
  89.  
  90.                     //Set up the OR Mask to include bits set on this combination
  91.                     for (int bitNum = 0; bitNum < floatingBitPositions.Count; bitNum++)
  92.                     {
  93.                         //Maps the bit combination of i to the floating bits in the original mask.
  94.                         //So if i is 6 its bits are 1010. If the mask was 0X100XX01X0 it will be mapped to 0 '1' 1 0 0 '0' '1' 0 1 '0' 0 (in quotes are injected)
  95.                         //This requires floatingBitPositions to be set up with indexes matching bits from right to left.
  96.                         //  Meaning: floatingBitPositions[0] is the rightmost X bit in the mask
  97.                         //           floatingBitPositions[1] is the X bit to the left of that one, etc..
  98.                         if (IsBitSet(i, bitNum))                                                                       //(8)
  99.                         {
  100.                             int equivalentBitPositionInFullMask = floatingBitPositions[bitNum];
  101.                             long setOneBitMask = (long)1 << equivalentBitPositionInFullMask;
  102.                             orMask |= setOneBitMask;
  103.                         }
  104.                     }
  105.                     //OR mask is now set up with the Xs filled with 0s or 1s. Apply to address.
  106.                     long maskedAddress = address | orMask;                                                             //(9)
  107.  
  108.                     memory[maskedAddress] = data;                                                                      //(10)
  109.                 }
  110.             }
  111.         }
  112.         PrintMemorySum(memory);
  113.     }
  114.  
  115.     bool IsBitSet(long fromNumber, int bitPosition)
  116.     {
  117.         long mask = (long)1 << bitPosition;
  118.         return (fromNumber & mask) > 0;
  119.     }
  120.  
  121.     void SolvePartOne(string[] dockingMemoryStrings)
  122.     {
  123.         Hashtable memory = new Hashtable();
  124.         long andMask = 0;
  125.         long orMask = 0;
  126.  
  127.         foreach (string s in dockingMemoryStrings)
  128.         {
  129.             MatchCollection allMatches = Regex.Matches(s, maskRegex);
  130.             if (allMatches.Count == 1)
  131.             {
  132.                 string maskString = allMatches[0].Groups[1].Value;
  133.                 string andMaskString = maskString.Replace('X', '1');
  134.                 string orMaskString = maskString.Replace('X', '0');
  135.  
  136.                 andMask = Convert.ToInt64(andMaskString, 2);
  137.                 orMask = Convert.ToInt64(orMaskString, 2);
  138.             }
  139.  
  140.             allMatches = Regex.Matches(s, memWriteRegex);
  141.             if (allMatches.Count == 1)
  142.             {
  143.                 long address = long.Parse(allMatches[0].Groups["address"].Value);
  144.                 long data = long.Parse(allMatches[0].Groups["data"].Value);
  145.  
  146.                 data = data & andMask;
  147.                 data = data | orMask;
  148.  
  149.                 memory[address] = data;
  150.             }
  151.         }
  152.         PrintMemorySum(memory);
  153.     }
  154.  
  155.     void PrintMemorySum(Hashtable memory)
  156.     {
  157.         long memorySum = 0;
  158.         foreach (DictionaryEntry memEntry in memory)
  159.         {
  160.             memorySum += (long)memEntry.Value;
  161.         }
  162.         Debug.Log($"Sum of all data in memory: {memorySum}");
  163.     }
  164.  
  165. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement