|
| 1 | +import java.util.Arrays; |
| 2 | + |
| 3 | +/** |
| 4 | + * --- Day 3: Spiral Memory --- |
| 5 | + * |
| 6 | + * You come across an experimental new kind of memory stored on an infinite |
| 7 | + * two-dimensional grid. |
| 8 | + * |
| 9 | + * Each square on the grid is allocated in a spiral pattern starting at a |
| 10 | + * location marked 1 and then counting up while spiraling outward. For example, |
| 11 | + * the first few squares are allocated like this: |
| 12 | + * |
| 13 | + * 17 16 15 14 13 |
| 14 | + * 18 5 4 3 12 |
| 15 | + * 19 6 1 2 11 |
| 16 | + * 20 7 8 9 10 |
| 17 | + * 21 22 23---> ... |
| 18 | + * |
| 19 | + * While this is very space-efficient (no squares are skipped), requested data must be |
| 20 | + * carried back to square 1 (the location of the only access port for this |
| 21 | + * memory system) by programs that can only move up, down, left, or right. They |
| 22 | + * always take the shortest path: the Manhattan Distance between the location of |
| 23 | + * the data and square 1. |
| 24 | + * |
| 25 | + * For example: |
| 26 | + * |
| 27 | + * Data from square 1 is carried 0 steps, since it's at the access port. |
| 28 | + * Data from square 12 is carried 3 steps, such as: down, left, left. |
| 29 | + * Data from square 23 is carried only 2 steps: up twice. |
| 30 | + * Data from square 1024 must be carried 31 steps. |
| 31 | + */ |
| 32 | +public class Day3 { |
| 33 | + |
| 34 | + public static int part1(int n) { |
| 35 | + // closes AxA location to n in grid |
| 36 | + int r = (int) Math.ceil(Math.sqrt(n)); |
| 37 | + int nextFull = r * r; |
| 38 | + int distToOne = r - 1; |
| 39 | + |
| 40 | + if (nextFull == n) { |
| 41 | + return distToOne; |
| 42 | + } |
| 43 | + |
| 44 | + // Will be distToOne or distToOne + 1 |
| 45 | + int halfway = nextFull - distToOne; |
| 46 | + int halfwayDistToOne = distToOne + (r + 1) % 2; |
| 47 | + |
| 48 | + if (halfway == n) { |
| 49 | + return halfwayDistToOne; |
| 50 | + } |
| 51 | + |
| 52 | + if (halfway > n) { |
| 53 | + // bump to same row as square |
| 54 | + int diff = halfway - n; |
| 55 | + n = halfway + diff; |
| 56 | + } |
| 57 | + |
| 58 | + // Same column as square |
| 59 | + int i = 0; |
| 60 | + while (halfway + i != n && nextFull - i != n) { |
| 61 | + i++; |
| 62 | + } |
| 63 | + return halfwayDistToOne - i; |
| 64 | + } |
| 65 | + |
| 66 | + /** |
| 67 | + * --- Part Two --- |
| 68 | + * |
| 69 | + * As a stress test on the system, the programs here clear the grid and then |
| 70 | + * store the value 1 in square 1. Then, in the same allocation order as shown |
| 71 | + * above, they store the sum of the values in all adjacent squares, including |
| 72 | + * diagonals. <br> |
| 73 | + * So, the first few squares' values are chosen as follows: <br> |
| 74 | + * Square 1 starts with the value 1. <br> |
| 75 | + * Square 2 has only one adjacent filled square (with value 1), so it also stores 1. <br> |
| 76 | + * Square 3 has both of the above squares as neighbors and stores the sum of their values, 2. <br> |
| 77 | + * Square 4 has all three of the aforementioned squares as neighbors and stores the sum of their values, 4. <br> |
| 78 | + * Square 5 only has the first and fourth squares as neighbors, so it gets the value 5. <br> |
| 79 | + * |
| 80 | + * Once a square is written, its value does not change. Therefore, the first |
| 81 | + * few squares would receive the following values: <br> |
| 82 | + * |
| 83 | + * 147 142 133 122 59 |
| 84 | + * 304 5 4 2 57 |
| 85 | + * 330 10 1 1 54 |
| 86 | + * 351 11 23 25 26 |
| 87 | + * 362 747 806---> ... |
| 88 | + * |
| 89 | + */ |
| 90 | + public static long part2(int n) { |
| 91 | + // base case |
| 92 | + if(n < 3) { |
| 93 | + return 1; |
| 94 | + } |
| 95 | + |
| 96 | + long[] grid = new long[n]; |
| 97 | + Arrays.fill(grid, -1); |
| 98 | + |
| 99 | + // base case |
| 100 | + grid[0] = 1; |
| 101 | + grid[1] = 1; |
| 102 | + |
| 103 | + long sum = gridSum(n, grid); |
| 104 | + System.out.println(n + " : " + sum); |
| 105 | + return sum; |
| 106 | + } |
| 107 | + |
| 108 | + |
| 109 | + private static long gridSum(int n, long[] grid) { |
| 110 | + if (grid[n-1] != -1) { |
| 111 | + return grid[n-1]; |
| 112 | + } |
| 113 | + |
| 114 | + // Always need this |
| 115 | + grid[n-1] = gridSum(n - 1, grid); |
| 116 | + |
| 117 | + // closes AxA location to n in grid |
| 118 | + int r = (int) Math.ceil(Math.sqrt(n)); |
| 119 | + int closestSquareToN = r * r; |
| 120 | + |
| 121 | + // Corner |
| 122 | + if (n == closestSquareToN) { |
| 123 | + int diagPos = n == 4 ? 1 : (r - 2) * (r - 2); |
| 124 | + grid[n-1] += gridSum(diagPos, grid); |
| 125 | + grid[n-1] += gridSum(diagPos + 1, grid); |
| 126 | + return grid[n-1]; |
| 127 | + } |
| 128 | + |
| 129 | + int halfwayDiagDistance = (r * r - (r - 2) * (r - 2) - 2); |
| 130 | + |
| 131 | + // Adj square corner |
| 132 | + int prevSquare = (r - 1) * (r - 1); |
| 133 | + if(n == prevSquare + 1) { |
| 134 | + grid[n-1] += gridSum(n - halfwayDiagDistance + 2, grid); |
| 135 | + return grid[n-1]; |
| 136 | + } |
| 137 | + |
| 138 | + // Adj square corner |
| 139 | + if(n == prevSquare + 2) { |
| 140 | + int adjPos = n - halfwayDiagDistance + 1; |
| 141 | + grid[n-1] += gridSum(prevSquare, grid); |
| 142 | + grid[n-1] += gridSum(adjPos, grid); |
| 143 | + if(n > 6) { |
| 144 | + grid[n-1] += gridSum(adjPos + 1, grid); |
| 145 | + } |
| 146 | + return grid[n-1]; |
| 147 | + } |
| 148 | + |
| 149 | + // Corner |
| 150 | + int halfway = closestSquareToN - (r - 1); |
| 151 | + int diagPos = halfway - halfwayDiagDistance; |
| 152 | + if (n == halfway) { |
| 153 | + grid[n-1] += gridSum(diagPos, grid); |
| 154 | + return grid[n-1]; |
| 155 | + } |
| 156 | + |
| 157 | + // Adj halfway corner |
| 158 | + if(n == halfway + 1) { |
| 159 | + grid[n-1] += gridSum(diagPos, grid); |
| 160 | + grid[n-1] += gridSum(diagPos + 1, grid); |
| 161 | + grid[n-1] += gridSum(n - 2, grid); |
| 162 | + return grid[n-1]; |
| 163 | + } |
| 164 | + |
| 165 | + // Adj halfway corner |
| 166 | + if(n == halfway - 1) { |
| 167 | + grid[n-1] += gridSum(diagPos, grid); |
| 168 | + grid[n-1] += gridSum(diagPos - 1, grid); |
| 169 | + return grid[n-1]; |
| 170 | + } |
| 171 | + |
| 172 | + // Middle of an edge |
| 173 | + int adj = halfwayDiagDistance; |
| 174 | + if (n < halfway) { |
| 175 | + adj--; |
| 176 | + } else { |
| 177 | + adj++; |
| 178 | + } |
| 179 | + int adjN = n - adj; |
| 180 | + grid[n - 1] += gridSum(adjN, grid); |
| 181 | + grid[n - 1] += gridSum(adjN + 1, grid); |
| 182 | + grid[n - 1] += gridSum(adjN - 1, grid); |
| 183 | + |
| 184 | + return grid[n-1]; |
| 185 | + } |
| 186 | +} |
0 commit comments