Advertisement
eltea

Advent of Code 2020 Day 11 Parts 1 and 2

Dec 12th, 2020
235
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.38 KB | None | 0 0
  1. //Advent of Code 2020 Day 11 Parts 1 and 2 solution by Mike LeSauvage
  2. public class SeatAnalysis : MonoBehaviour
  3. {
  4.     public delegate int GetOccupiedSeatsDelegate(char[,] seatMap, int row, int col);
  5.  
  6.     [SerializeField] TextAsset seatMapTextfile = null;   //Hooked up in the input text in the Unity editor.
  7.  
  8.     static char FLOOR_CHAR = '.';
  9.     static char EMPTY_CHAR = 'L';
  10.     static char OCCUPIED_CHAR = '#';
  11.     static int PART_ONE_OCCUPIED_SEAT_THRESHOLD = 4;
  12.     static int PART_TWO_OCCUPIED_SEAT_THRESHOLD = 5;
  13.     static int PART_ONE_ID = 0;
  14.     static int PART_TWO_ID = 1;
  15.  
  16.     void Start()
  17.     {
  18.         string[] seatMapStrings = seatMapTextfile.text.Split('\n');
  19.  
  20.         char[,] startingSeatMap = new char[seatMapStrings.Length, seatMapStrings[0].Length-1];
  21.  
  22.         //Solve part one.
  23.         FillMapFromStrings(seatMapStrings, startingSeatMap);
  24.         SolveDay10(startingSeatMap, PART_ONE_ID);
  25.  
  26.         //Reset map and solve part two.
  27.         FillMapFromStrings(seatMapStrings, startingSeatMap);
  28.         SolveDay10(startingSeatMap, PART_TWO_ID);
  29.  
  30.     }
  31.  
  32.     // Takes the provided initial map as a string and loads it into the two-dimensional character map.
  33.     void FillMapFromStrings(string[] mapAsStrings, char[,] map)
  34.     {
  35.         int numRows = map.GetLength(0);
  36.         int numCols = map.GetLength(1);
  37.  
  38.         for (int row = 0; row < numRows; row++)
  39.         {
  40.             for (int seat = 0; seat < numCols; seat++)
  41.             {
  42.                 map[row, seat] = mapAsStrings[row][seat];
  43.             }
  44.         }
  45.     }
  46.  
  47.     // Repeatedly calls PlayMusicalChairs() to change seating configuration until it stabilizes.
  48.     // Uses different rules for part one or two based on partID.
  49.     // When the seating map is no longer changing, outputs how many people are in seats.
  50.     void SolveDay10(char[,] currentSeatMap, int partID)
  51.     {
  52.         char[,] nextSeatMap = new char[currentSeatMap.GetLength(0), currentSeatMap.GetLength(1)];
  53.         char[,] tempSeatMap;
  54.  
  55.         while (true)
  56.         {
  57.             bool didSeatsChange = false;
  58.  
  59.             if(partID == PART_ONE_ID)
  60.             {
  61.                 didSeatsChange = PlayMusicalChairs(currentSeatMap, nextSeatMap, GetNumOccupiedAdjacentSeatsPartOneRules, PART_ONE_OCCUPIED_SEAT_THRESHOLD);
  62.             }
  63.             else if(partID == PART_TWO_ID)
  64.             {
  65.                 didSeatsChange = PlayMusicalChairs(currentSeatMap, nextSeatMap, GetNumOccupiedAdjacentSeatsPartTwoRules, PART_TWO_OCCUPIED_SEAT_THRESHOLD);
  66.             }
  67.  
  68.             tempSeatMap = currentSeatMap;
  69.             currentSeatMap = nextSeatMap;
  70.             nextSeatMap = tempSeatMap;
  71.  
  72.             if (!didSeatsChange)
  73.                 break;
  74.         }
  75.  
  76.         Debug.Log($"Final occupied count: {GetOccupiedSeatCount(currentSeatMap)}");
  77.     }
  78.  
  79.     // Counts occupied seats in a seat map.
  80.     int GetOccupiedSeatCount(char[,] seatMap)
  81.     {
  82.         int numRows = seatMap.GetLength(0);
  83.         int numCols = seatMap.GetLength(1);
  84.         int occupiedCount = 0;
  85.  
  86.         for (int row = 0; row < numRows; row++)
  87.         {
  88.             for (int col = 0; col < numCols; col++)
  89.             {
  90.                 if (seatMap[row, col] == OCCUPIED_CHAR)
  91.                 {
  92.                     occupiedCount++;
  93.                 }
  94.             }
  95.         }
  96.         return occupiedCount;
  97.     }
  98.  
  99.     // Takes a starting seat map and generates the new map.
  100.     // Irrespective of part one or two:
  101.     //    - people sit in seats when there are no adjacent seats occupied.
  102.     //    - people leave seats when there are too many adjacent seats occupied.
  103.     //
  104.     // The rules for part one and two are different, so:
  105.     //    - the delegate GetNumOccupiedAdjacentSeats is used to determine how many seats are adjacent.
  106.     //    - occupiedSeatThreshold specifies how many adjacent seats are acceptable.
  107.     //
  108.     // Returns true if map changed, false otherwise.
  109.     bool PlayMusicalChairs(char[,] startingSeats, char[,] endingSeats, GetOccupiedSeatsDelegate GetNumOccupiedAdjacentSeats, int occupiedSeatThreshold)
  110.     {
  111.         int numRows = startingSeats.GetLength(0);
  112.         int numCols = startingSeats.GetLength(1);
  113.         bool changed = false;
  114.  
  115.         for (int row = 0; row < numRows; row++)
  116.         {
  117.             for (int col = 0; col < numCols; col++)
  118.             {
  119.                 char startSeat = startingSeats[row, col];
  120.  
  121.                 //Floor. Floor never changes.
  122.                 if (startSeat == FLOOR_CHAR)
  123.                 {
  124.                     endingSeats[row, col] = FLOOR_CHAR;
  125.                     continue;
  126.                 }
  127.  
  128.                 int numAdjacentOccupied = GetNumOccupiedAdjacentSeats(startingSeats, row, col);
  129.  
  130.                 //Handle seats
  131.                 if(startSeat == EMPTY_CHAR && numAdjacentOccupied == 0)
  132.                 {
  133.                     endingSeats[row, col] = OCCUPIED_CHAR;
  134.                     changed = true;
  135.                 }
  136.                 else if(startSeat == OCCUPIED_CHAR && numAdjacentOccupied >= occupiedSeatThreshold)
  137.                 {
  138.                     endingSeats[row, col] = EMPTY_CHAR;
  139.                     changed = true;
  140.                 }
  141.                 else
  142.                 {
  143.                     endingSeats[row, col] = startingSeats[row, col];
  144.                 }
  145.             }
  146.         }
  147.  
  148.         return changed;
  149.     }
  150.  
  151.     // Implements counting rules for Day 11 Part 2.
  152.     // Counts how many occupied seats can be seen from the target [row, col] seat.
  153.     // Looks out at each of the 8 main compass directions (N S W E NW NE SW SE)
  154.     //   - Stops looking when a seat is encountered, occupied or not, or edge of map reached.
  155.     //   - Increments count if occupied.
  156.     //
  157.     // Returns the number of people found in adjacent seats.
  158.     int GetNumOccupiedAdjacentSeatsPartTwoRules(char[,] seatMap, int row, int col)
  159.     {
  160.         int numOccupied = 0;
  161.  
  162.         //Look directions are the 8 compass directions.
  163.         //NOTE: this is NOT (x,y) - (y,x) as we've been working in (row, col)
  164.         (int, int)[] lookDirections = new (int, int)[] {  (-1, -1), (-1,  0), (-1, 1),
  165.                                                           ( 0, -1),           ( 0, 1),
  166.                                                           ( 1, -1), ( 1,  0), ( 1, 1)
  167.                                                        };
  168.  
  169.         //Handle looking in each of the 8 directions from the target seat.
  170.         foreach( (int rowDelta, int colDelta)dir in lookDirections)
  171.         {
  172.             int curRow = row;
  173.             int curCol = col;
  174.  
  175.             while (true)
  176.             {
  177.                 //Move current row/col
  178.                 //Check if valid spot in map.
  179.                 //Check for seat
  180.                 //If occupied, add to count.
  181.                 //If occupied or empty, break loop
  182.                 curRow += dir.rowDelta;
  183.                 curCol += dir.colDelta;
  184.  
  185.                 if(!IsSeatInMap(curRow, curCol, seatMap.GetLength(0), seatMap.GetLength(1)))
  186.                 {
  187.                     break;
  188.                 }
  189.  
  190.                 if(seatMap[curRow, curCol] == OCCUPIED_CHAR)
  191.                 {
  192.                     numOccupied++;
  193.                     break;
  194.                 }
  195.  
  196.                 if(seatMap[curRow, curCol] == EMPTY_CHAR)
  197.                 {
  198.                     break;
  199.                 }
  200.  
  201.             }
  202.         }
  203.         return numOccupied;
  204.     }
  205.  
  206.     // Implements counting rules for Day 11 Part 1
  207.     // Counts how many seats are immediately adjacent to the seat at [row, col] in the given seat map.
  208.     // Adjacent seats are the 8 main compass directions (N S W E NW NE SW SE)
  209.     //
  210.     // Returns the number of people found in adjacent seats.
  211.     int GetNumOccupiedAdjacentSeatsPartOneRules(char[,] seatMap, int row, int col)
  212.     {
  213.         int numOccupied = 0;
  214.  
  215.         //Loop across the three rows and within them the three columns surrounding the target seat.
  216.         for(int testRow = row-1; testRow < row+2; testRow++)
  217.         {
  218.             for(int testCol = col-1; testCol < col+2; testCol++)
  219.             {
  220.                 //Don't test the seat at the center of the test!
  221.                 if(testRow == row && testCol == col)
  222.                 {
  223.                     continue;
  224.                 }
  225.  
  226.                 //Count seat if it's occupied.
  227.                 if(IsSeatInMap(testRow, testCol, seatMap.GetLength(0), seatMap.GetLength(1)))
  228.                 {
  229.                     if(seatMap[testRow, testCol] == '#')
  230.                     {
  231.                         numOccupied++;
  232.                     }
  233.                 }
  234.             }
  235.         }
  236.         return numOccupied;
  237.     }
  238.  
  239.     //Checks if the seat is in the range bounded by the map.
  240.     bool IsSeatInMap(int row, int col, int numRows, int numCols)
  241.     {
  242.         if(row >= 0 && row < numRows && col >= 0 && col < numCols)
  243.         {
  244.             return true;
  245.         }
  246.  
  247.         return false;
  248.     }
  249.  
  250.     //Print out the current seat map. Included here for debugging.
  251.     void PrintSeatMap(char[,] seatMap)
  252.     {
  253.         char[] rowChars = new char[seatMap.GetLength(1)];
  254.  
  255.         for(int row=0; row < seatMap.GetLength(0); row++)
  256.         {
  257.             for (int seat = 0; seat < seatMap.GetLength(1); seat++)
  258.             {
  259.                 rowChars[seat] = seatMap[row, seat];
  260.             }
  261.  
  262.             string rowString = new string(rowChars);
  263.             Debug.Log(rowString);
  264.         }
  265.     }
  266.  
  267. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement