day_11.zig (8027B)
1 const std = @import("std"); 2 const debug = std.debug; 3 const mem = std.mem; 4 5 const grid_serial_number: u32 = 5791; 6 const grid_side_len = 300; 7 8 pub fn main() void { 9 const result1 = regionOfHighestTotalPower(grid_serial_number); 10 debug.assert(V2.eq(V2.init(20, 68), result1)); 11 debug.warn("11-1: {}\n", result1); 12 13 const result2 = regionOfHighestTotalPowerOfAnySize(grid_serial_number); 14 debug.assert(V2.eq(V2.init(231, 273), result2.region)); 15 debug.assert(16 == result2.square_side_len); 16 debug.warn("11-2: {},{}\n", result2.region, result2.square_side_len); 17 } 18 19 fn regionOfHighestTotalPowerOfAnySize(gsn: u32) RegionInfo { 20 var grid = []i32{0} ** (grid_side_len * grid_side_len); 21 22 for (grid) |*cell, i| { 23 var coord = point_from_index(i, grid_side_len); 24 25 // grid is 1 based, though array is 0 based 26 cell.* = getFuelCellPower(coord.x + 1, coord.y + 1, gsn); 27 } 28 // https://en.wikipedia.org/wiki/Summed-area_table 29 var sat = []i32{0} ** grid.len; 30 for (grid) |cell, i| { 31 const c = point_from_index(i, grid_side_len); 32 33 sat[i] = grid[index_from_point(c.x, c.y, grid_side_len)]; 34 35 if (c.x > 0) { 36 sat[i] += sat[index_from_point(c.x - 1, c.y, grid_side_len)]; 37 } 38 if (c.y > 0) { 39 sat[i] += sat[index_from_point(c.x, c.y - 1, grid_side_len)]; 40 } 41 if (c.x > 0 and c.y > 0) { 42 sat[i] -= sat[index_from_point(c.x - 1, c.y - 1, grid_side_len)]; 43 } 44 } 45 46 var highest_highest_power: i32 = grid[0]; 47 var highest_highest_power_region = V2.init(0, 0); 48 var highest_square_side_len: u32 = 1; 49 50 var square_side_len: u32 = 1; 51 while (square_side_len <= grid_side_len) : (square_side_len += 1) { 52 var highest_power: i32 = @minValue(i32); 53 var highest_power_region = V2.init(1, 1); 54 55 for (grid) |cell, i| { 56 const array_coord = point_from_index(i, grid_side_len); 57 58 // cells start at (1, 1) 59 const cell_coord = V2.init(array_coord.x + 1, array_coord.y + 1); 60 61 const search_threshold = (grid_side_len - square_side_len) + 1; 62 if (cell_coord.x > search_threshold or cell_coord.y > search_threshold) { 63 continue; 64 } 65 66 const ssl = square_side_len - 1; 67 68 // +----------------------+ 69 // | | | | 70 // | A| B| | 71 // |------+-----------+ | 72 // | | | | 73 // | | | | 74 // | | | | 75 // | C| D| | 76 // |------+-----------+ | 77 // | | 78 // +----------------------+ 79 // sum = D - B - C + A 80 81 // D 82 var sum: i32 = sat[index_from_point(array_coord.x + ssl, array_coord.y + ssl, grid_side_len)]; 83 84 if (array_coord.x > 0) { 85 // - C 86 sum -= sat[index_from_point(array_coord.x - 1, array_coord.y + ssl, grid_side_len)]; 87 } 88 if (array_coord.y > 0) { 89 // - B 90 sum -= sat[index_from_point(array_coord.x + ssl, array_coord.y - 1, grid_side_len)]; 91 } 92 if (array_coord.x > 0 and array_coord.y > 0) { 93 // + A 94 sum += sat[index_from_point(array_coord.x - 1, array_coord.y - 1, grid_side_len)]; 95 } 96 97 if (sum > highest_power) { 98 highest_power = sum; 99 highest_power_region = cell_coord; 100 } 101 } 102 103 if (highest_power > highest_highest_power) { 104 highest_highest_power = highest_power; 105 highest_highest_power_region = highest_power_region; 106 highest_square_side_len = square_side_len; 107 } 108 } 109 110 return RegionInfo { 111 .region = highest_highest_power_region, 112 .square_side_len = highest_square_side_len, 113 }; 114 } 115 116 const RegionInfo = struct { 117 region: V2, 118 square_side_len: u32, 119 }; 120 121 test "region of highest total power of any size" { 122 const r18 = regionOfHighestTotalPowerOfAnySize(18); 123 debug.assert(V2.eq(V2.init(90, 269), r18.region)); 124 debug.assert(16 == r18.square_side_len); 125 126 const r42 = regionOfHighestTotalPowerOfAnySize(42); 127 debug.assert(V2.eq(V2.init(232, 251), r42.region)); 128 debug.assert(12 == r42.square_side_len); 129 } 130 131 fn regionOfHighestTotalPower(gsn: u32) V2{ 132 var grid = []i32{0} ** (grid_side_len * grid_side_len); 133 134 for (grid) |*cell, i| { 135 var coord = point_from_index(i, grid_side_len); 136 137 // grid is 1 based, though array is 0 based 138 coord.x += 1; 139 coord.y += 1; 140 141 cell.* = getFuelCellPower(coord.x, coord.y, gsn); 142 } 143 144 var square_side_len: u32 = 3; 145 var highest_power: i32 = 0; 146 var highest_power_region = V2.init(1, 1); 147 148 for (grid) |cell, i| { 149 const array_coord = point_from_index(i, grid_side_len); 150 const cell_coord = V2.init(array_coord.x + 1, array_coord.y + 1); 151 152 const search_threshold = grid_side_len - square_side_len; 153 if (cell_coord.x >= search_threshold or cell_coord.y >= search_threshold) { 154 continue; 155 } 156 157 var sum: i32 = 0; 158 var square_x: u32 = 0; 159 while (square_x < square_side_len) : (square_x += 1) { 160 var square_y: u32 = 0; 161 while (square_y < square_side_len) : (square_y += 1) { 162 sum += grid[index_from_point(array_coord.x + square_x, array_coord.y + square_y, grid_side_len)]; 163 } 164 } 165 166 if (sum > highest_power) { 167 highest_power = sum; 168 highest_power_region = cell_coord; 169 } 170 } 171 return highest_power_region; 172 } 173 174 test "region of highest total power" { 175 debug.assert(V2.eq(V2.init(33, 45), regionOfHighestTotalPower(18))); 176 debug.assert(V2.eq(V2.init(21, 61), regionOfHighestTotalPower(42))); 177 } 178 179 fn getFuelCellPower(cell_x: u32, cell_y: u32, gsn: u32) i32 { 180 const rack_id = cell_x + 10; 181 var power_level: i32 = @intCast(i32, rack_id * cell_y); 182 power_level += @intCast(i32, gsn); 183 power_level *= @intCast(i32, rack_id); 184 power_level = hundredsDigit(power_level); 185 power_level -= 5; 186 return power_level; 187 } 188 189 test "get fuel cell power" { 190 debug.assert(4 == getFuelCellPower(3, 5, 8)); 191 debug.assert(-5 == getFuelCellPower(122, 79, 57)); 192 debug.assert(0 == getFuelCellPower(217, 196, 39)); 193 debug.assert(4 == getFuelCellPower(101, 153, 71)); 194 } 195 196 inline fn hundredsDigit(n: i32) i32 { 197 return @mod(@divTrunc(n, 100), 10); 198 } 199 200 test "hundreds digit" { 201 debug.assert(0 == hundredsDigit(0)); 202 debug.assert(0 == hundredsDigit(10)); 203 debug.assert(1 == hundredsDigit(100)); 204 debug.assert(0 == hundredsDigit(1000)); 205 debug.assert(3 == hundredsDigit(12345)); 206 } 207 208 fn point_from_index(i: usize, stride: usize) V2 { 209 var x: u32 = @intCast(u32, i % stride); 210 var y: u32 = @intCast(u32, @divTrunc(i, stride)); 211 return V2.init(x, y); 212 } 213 214 test "point from index" { 215 debug.assert(V2.eq(V2.init(0, 0), point_from_index(0, 5))); 216 debug.assert(V2.eq(V2.init(1, 1), point_from_index(6, 5))); 217 debug.assert(V2.eq(V2.init(2, 1), point_from_index(7, 5))); 218 debug.assert(V2.eq(V2.init(4, 2), point_from_index(14, 5))); 219 debug.assert(V2.eq(V2.init(4, 4), point_from_index(24, 5))); 220 } 221 222 inline fn index_from_point(x: u32, y: u32, stride: usize) usize { 223 return y * stride + x; 224 } 225 226 test "index from point" { 227 debug.assert(0 == index_from_point(0, 0, 5)); 228 debug.assert(6 == index_from_point(1, 1, 5)); 229 debug.assert(7 == index_from_point(2, 1, 5)); 230 debug.assert(14 == index_from_point(4, 2, 5)); 231 debug.assert(24 == index_from_point(4, 4, 5)); 232 } 233 234 const V2 = struct { 235 x: u32, 236 y: u32, 237 238 pub fn init(x: u32, y: u32) V2 { 239 return V2 { 240 .x = x, 241 .y = y, 242 }; 243 } 244 245 pub fn eq(vec1: V2, vec2: V2) bool { 246 return vec1.x == vec2.x and vec1.y == vec2.y; 247 } 248 };