day_12.zig (17545B)
1 const std = @import("std"); 2 const debug = std.debug; 3 const math = std.math; 4 const mem = std.mem; 5 const TypeId = @import("builtin").TypeId; 6 7 pub fn main() void { 8 debug.warn("state {}, rules {}\n", input_initial_state.len, input_rules.len); 9 debug.warn("state {}, rules {}\n", test_initial_state.len, test_rules.len); 10 } 11 12 fn Generation(comptime T: type) type { 13 return struct { 14 const Self = @This(); 15 16 gen_num: usize, 17 first_plant: isize, 18 last_plant: isize, 19 pots: []const T, 20 21 fn simNextGeneration(allocator: *std.mem.Allocator, current: Self, rules: u32) Self { 22 var next_pots = std.ArrayList(T).init(allocator); 23 defer next_pots.deinit(); 24 25 comptime const pot_int_highest_bit = @truncate(math.Log2Int(T), @typeInfo(T).Int.bits - 1); 26 comptime debug.assert(pot_int_highest_bit == @typeInfo(T).Int.bits - 1); 27 28 var pot_context: u5 = 0; 29 var next_pots_pot_int: T = 0; 30 var next_pot_bit = pot_int_highest_bit; 31 32 for (current.pots) |pot_int, i| { 33 if (i == 0) { 34 // Check the 3 leading pots 35 // 00000 36 // ^ 37 // assign corresponding bit from rules 38 { 39 const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); 40 debug.warn("Rule result {b}\n", new_pot_state); 41 next_pots_pot_int |= T(new_pot_state) << next_pot_bit; 42 debug.warn("{x}\n", next_pots_pot_int); 43 if (new_pot_state == 1) { 44 next_pot_bit -= 1; 45 } 46 if (next_pot_bit == 0) { 47 _ = next_pots.append(next_pots_pot_int); 48 next_pots_pot_int = 0; 49 next_pot_bit = pot_int_highest_bit; 50 } 51 } 52 53 // 0000X 54 // ^ 55 { 56 var shifted: u5 = 0; 57 var overflowHappened = @shlWithOverflow(u5, pot_context, 1, &shifted); 58 debug.warn("Overflow? {}\n", overflowHappened); 59 60 pot_context = shifted; 61 debug.warn("Shifted {b}\n", pot_context); 62 63 const new_bit: u1 = @boolToInt((pot_int & (T(1) << pot_int_highest_bit)) != 0); 64 pot_context |= new_bit; 65 debug.warn("With new bit {b}\n", pot_context); 66 67 // assign corresponding bit from rules 68 const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); 69 debug.warn("Rule result {b}\n", new_pot_state); 70 next_pots_pot_int |= T(new_pot_state) << next_pot_bit; 71 debug.warn("{x}\n", next_pots_pot_int); 72 if (new_pot_state == 1) { 73 next_pot_bit -= 1; 74 } 75 if (next_pot_bit == 0) { 76 _ = next_pots.append(next_pots_pot_int); 77 next_pots_pot_int = 0; 78 next_pot_bit = pot_int_highest_bit; 79 } 80 } 81 82 // 000XX 83 // ^ 84 { 85 var shifted: u5 = 0; 86 var overflowHappened = @shlWithOverflow(u5, pot_context, 1, &shifted); 87 debug.warn("Overflow? {}\n", overflowHappened); 88 89 pot_context = shifted; 90 debug.warn("Shifted {b}\n", pot_context); 91 92 const new_bit: u1 = @boolToInt((pot_int & (T(1) << pot_int_highest_bit - 1)) != 0); 93 pot_context |= new_bit; 94 debug.warn("With new bit {b}\n", pot_context); 95 96 // assign corresponding bit from rules 97 const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); 98 debug.warn("Rule result {b}\n", new_pot_state); 99 next_pots_pot_int |= T(new_pot_state) << next_pot_bit; 100 debug.warn("{x}\n", next_pots_pot_int); 101 if (new_pot_state == 1) { 102 next_pot_bit -= 1; 103 } 104 if (next_pot_bit == 0) { 105 _ = next_pots.append(next_pots_pot_int); 106 next_pots_pot_int = 0; 107 next_pot_bit = pot_int_highest_bit; 108 } 109 } 110 } 111 112 var pot_bit = pot_int_highest_bit - 2; 113 while (pot_bit >= 0) : ({ pot_bit -= 1; next_pot_bit -= 1; }) { 114 var shifted: u5 = 0; 115 var overflowHappened = @shlWithOverflow(u5, pot_context, 1, &shifted); 116 debug.warn("Overflow? {}\n", overflowHappened); 117 118 pot_context = shifted; 119 debug.warn("Shifted {b}\n", pot_context); 120 121 const new_bit: u1 = @boolToInt((pot_int & (T(1) << pot_bit)) != 0); 122 pot_context |= new_bit; 123 debug.warn("With new bit {b}\n", pot_context); 124 125 // assign corresponding bit from rules 126 const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); 127 debug.warn("Rule result {b}\n", new_pot_state); 128 next_pots_pot_int |= T(new_pot_state) << next_pot_bit; 129 debug.warn("{x}\n", next_pots_pot_int); 130 if (next_pot_bit == 0) { 131 _ = next_pots.append(next_pots_pot_int); 132 next_pots_pot_int = 0; 133 next_pot_bit = pot_int_highest_bit; 134 } 135 136 if (pot_bit == 0) { 137 break; 138 } 139 } 140 _ = next_pots.append(next_pots_pot_int); 141 } 142 143 // TODO: check the 3 following pots!! 144 145 const result = Self { 146 .gen_num = current.gen_num + 1, 147 .first_plant = current.first_plant, 148 .last_plant = current.last_plant, 149 .pots = next_pots.toSlice(), 150 }; 151 return result; 152 } 153 }; 154 } 155 156 test "sim next gen" { 157 const TestPotInt = u128; 158 const TestGeneration = Generation(TestPotInt); 159 var allocator = debug.global_allocator; 160 161 const rules = rulesToInt(u32, test_rules); 162 163 var g = TestGeneration { 164 .gen_num = 0, 165 .first_plant = 0, 166 .last_plant = 24, 167 .pots = potsToIntArray(TestPotInt, test_initial_state), 168 }; 169 170 var expected_first_plant: isize = undefined; 171 var expected_last_plant: isize = undefined; 172 var expected_pots: []const TestPotInt = potsToIntArray(TestPotInt, "........................."); 173 174 while (g.gen_num <= 20) { 175 debug.warn("{}\n", g.gen_num); 176 // TODO: replace this with an array of expected `Generation`s 177 switch (g.gen_num) { 178 0 => { 179 expected_first_plant = 0; 180 expected_last_plant = 24; 181 expected_pots = potsToIntArray(TestPotInt, "#..#.#..##......###...###"); 182 }, 183 1 => { 184 expected_first_plant = 0; 185 expected_last_plant = 24; 186 // TODO: Why is the assertion on this passing!? 187 expected_pots = potsToIntArray(TestPotInt, "#...#....#.....#..#..#..#"); 188 }, 189 2 => { 190 expected_first_plant = 0; 191 expected_last_plant = 25; 192 expected_pots = potsToIntArray(TestPotInt, "##..##...##....#..#..#..##"); 193 }, 194 3 => { 195 expected_first_plant = -1; 196 expected_last_plant = 25; 197 expected_pots = potsToIntArray(TestPotInt, "#.#...#..#.#....#..#..#...#"); 198 }, 199 4 => { 200 expected_first_plant = 0; 201 expected_last_plant = 26; 202 expected_pots = potsToIntArray(TestPotInt, "#.#..#...#.#...#..#..##..##"); 203 }, 204 5 => { 205 expected_first_plant = 1; 206 expected_last_plant = 26; 207 expected_pots = potsToIntArray(TestPotInt, "#...##...#.#..#..#...#...#"); 208 }, 209 6 => { 210 expected_first_plant = 1; 211 expected_last_plant = 27; 212 expected_pots = potsToIntArray(TestPotInt, "##.#.#....#...#..##..##..##"); 213 }, 214 7 => { 215 expected_first_plant = 0; 216 expected_last_plant = 27; 217 expected_pots = potsToIntArray(TestPotInt, "#..###.#...##..#...#...#...#"); 218 }, 219 8 => { 220 expected_first_plant = 0; 221 expected_last_plant = 28; 222 expected_pots = potsToIntArray(TestPotInt, "#....##.#.#.#..##..##..##..##"); 223 }, 224 9 => { 225 expected_first_plant = 0; 226 expected_last_plant = 28; 227 expected_pots = potsToIntArray(TestPotInt, "##..#..#####....#...#...#...#"); 228 }, 229 10 => { 230 expected_first_plant = -1; 231 expected_last_plant = 29; 232 expected_pots = potsToIntArray(TestPotInt, "#.#..#...#.##....##..##..##..##"); 233 }, 234 11 => { 235 expected_first_plant = 0; 236 expected_last_plant = 29; 237 expected_pots = potsToIntArray(TestPotInt, "#...##...#.#...#.#...#...#...#"); 238 }, 239 12 => { 240 expected_first_plant = 0; 241 expected_last_plant = 30; 242 expected_pots = potsToIntArray(TestPotInt, "##.#.#....#.#...#.#..##..##..##"); 243 }, 244 13 => { 245 expected_first_plant = -1; 246 expected_last_plant = 30; 247 expected_pots = potsToIntArray(TestPotInt, "#..###.#....#.#...#....#...#...#"); 248 }, 249 14 => { 250 expected_first_plant = -1; 251 expected_last_plant = 31; 252 expected_pots = potsToIntArray(TestPotInt, "#....##.#....#.#..##...##..##..##"); 253 }, 254 15 => { 255 expected_first_plant = -1; 256 expected_last_plant = 31; 257 expected_pots = potsToIntArray(TestPotInt, "##..#..#.#....#....#..#.#...#...#"); 258 }, 259 16 => { 260 expected_first_plant = -2; 261 expected_last_plant = 32; 262 expected_pots = potsToIntArray(TestPotInt, "#.#..#...#.#...##...#...#.#..##..##"); 263 }, 264 17 => { 265 expected_first_plant = -1; 266 expected_last_plant = 32; 267 expected_pots = potsToIntArray(TestPotInt, "#...##...#.#.#.#...##...#....#...#"); 268 }, 269 18 => { 270 expected_first_plant = -1; 271 expected_last_plant = 33; 272 expected_pots = potsToIntArray(TestPotInt, "##.#.#....#####.#.#.#...##...##..##"); 273 }, 274 19 => { 275 expected_first_plant = -2; 276 expected_last_plant = 33; 277 expected_pots = potsToIntArray(TestPotInt, "#..###.#..#.#.#######.#.#.#..#.#...#"); 278 }, 279 20 => { 280 expected_first_plant = -2; 281 expected_last_plant = 34; 282 expected_pots = potsToIntArray(TestPotInt, "#....##....#####...#######....#.#..##"); 283 }, 284 else => unreachable, 285 } 286 debug.warn("Gen {}\n", g.gen_num); 287 debug.warn("{} == {}\n", expected_pots[0], g.pots[0]); 288 //debug.assert(expected_first_plant == g.first_plant); 289 //debug.assert(expected_last_plant == g.last_plant); 290 debug.assert(g.pots.len == 1); 291 debug.assert(expected_pots.len == 1); 292 debug.assert(expected_pots[0] == g.pots[0]); 293 debug.assert(mem.eql(TestPotInt, expected_pots, g.pots)); 294 g = TestGeneration.simNextGeneration(allocator, g, rules); 295 } 296 } 297 298 fn rulesToInt(comptime T: type, rules: []const []const u8) T { 299 comptime { 300 debug.assert(@typeId(T) == TypeId.Int); 301 debug.assert(!@typeInfo(T).Int.is_signed); 302 } 303 debug.assert(rules.len <= @typeInfo(T).Int.bits); 304 var num: T = 0; 305 306 for (rules) |r| { 307 var it = mem.split(r, " =>"); 308 const pattern = potsToInt(u8, it.next().?); 309 const result = it.next().?; 310 debug.assert(it.next() == null); 311 312 const bit_value = switch (result[0]) { 313 '.' => T(0), 314 '#' => T(1), 315 else => unreachable, 316 }; 317 318 comptime const ShiftInt = math.Log2Int(T); 319 num |= bit_value << @intCast(ShiftInt, pattern); 320 } 321 return num; 322 } 323 324 test "rules to int" { 325 debug.assert(rulesToInt(u32, test_rules) == 0x7CA09D18); 326 } 327 328 fn potsToIntArray(comptime T: type, comptime pots: []const u8) []const T { 329 comptime { 330 debug.assert(!@typeInfo(T).Int.is_signed); 331 } 332 comptime const int_bits = @typeInfo(T).Int.bits; 333 334 // round up to account for remainder pots 335 comptime const result_array_len: usize = (pots.len + int_bits - 1) / int_bits; 336 337 var result = []T{0} ** result_array_len; 338 339 for (result) |*elem, i| { 340 const pot_slice_index = i * int_bits; 341 342 var pot_slice: []const u8 = undefined; 343 344 if (i == result.len - 1) { 345 // we need right-padding 346 var padded_pots = "." ** int_bits; 347 for (pots[pot_slice_index..]) |p, p_i| { 348 padded_pots[p_i] = p; 349 } 350 pot_slice = padded_pots; 351 } else { 352 const next_slice_index = pot_slice_index + int_bits; 353 pot_slice = pots[pot_slice_index..next_slice_index]; 354 } 355 356 elem.* = potsToInt(T, pot_slice); 357 } 358 359 return result; 360 } 361 362 test "pots to int array" { 363 { 364 const potArray = potsToIntArray(u8, test_initial_state); 365 debug.assert(potArray.len == 4); 366 debug.assert(potArray[0] == 0x94); // #..#.#.. 367 debug.assert(potArray[1] == 0xC0); // ##...... 368 debug.assert(potArray[2] == 0xE3); // ###...## 369 debug.assert(potArray[3] == 0x80); // #....... 370 } 371 372 { 373 const potArray = potsToIntArray(u32, test_initial_state); 374 debug.assert(potArray.len == 1); 375 debug.assert(potArray[0] == 0x94C0E380); 376 } 377 } 378 379 fn potsToInt(comptime T: type, pots: []const u8) T { 380 comptime { 381 debug.assert(@typeId(T) == TypeId.Int); 382 debug.assert(!@typeInfo(T).Int.is_signed); 383 } 384 comptime const int_bits = @typeInfo(T).Int.bits; 385 debug.assert(pots.len <= int_bits); 386 387 var num: T = 0; 388 var i: u8 = 0; 389 390 for (pots) |p| { 391 if (p == '#') { 392 comptime const ShiftInt = math.Log2Int(T); 393 num |= T(1) << @intCast(ShiftInt, pots.len - 1 - i); 394 } 395 i += 1; 396 } 397 return num; 398 } 399 400 test "pots to int" { 401 debug.assert(potsToInt(u8, ".....") == 0); 402 debug.assert(potsToInt(u8, "....#") == 1); 403 debug.assert(potsToInt(u8, "...##") == 3); 404 debug.assert(potsToInt(u8, "..###") == 7); 405 debug.assert(potsToInt(u8, ".####") == 15); 406 debug.assert(potsToInt(u8, "#####") == 31); 407 debug.assert(potsToInt(u8, "####.") == 30); 408 debug.assert(potsToInt(u8, "###..") == 28); 409 debug.assert(potsToInt(u8, "##...") == 24); 410 debug.assert(potsToInt(u8, "#....") == 16); 411 debug.assert(potsToInt(u8, "#") == 1); 412 debug.assert(potsToInt(u5, "#####") == 31); 413 debug.assert(potsToInt(u128, ".#.#.") == 10); 414 debug.assert(potsToInt(u128, "#.#.#") == 21); 415 debug.assert(potsToInt(u32, test_initial_state) == 0x12981C7); 416 debug.assert(potsToInt(u128, input_initial_state) == 0xC9A1A56AFCD5E918842DC9C44); 417 } 418 419 const test_initial_state = "#..#.#..##......###...###"; 420 421 const test_rules = []const []const u8 { 422 "...## => #", 423 "..#.. => #", 424 ".#... => #", 425 ".#.#. => #", 426 ".#.## => #", 427 ".##.. => #", 428 ".#### => #", 429 "#.#.# => #", 430 "#.### => #", 431 "##.#. => #", 432 "##.## => #", 433 "###.. => #", 434 "###.# => #", 435 "####. => #", 436 }; 437 438 const input_initial_state = "##..#..##.#....##.#..#.#.##.#.#.######..##.#.#.####.#..#...##...#....#....#.##.###..#..###...#...#.."; 439 440 // Hey, look at that: 441 // - there are exactly 32 rules 442 // - the patterns, if interpreted as bits, correspond to u8 0 through 31 443 // - the results, by themselves, could fit in a single u32 444 // That's... interesting... 445 const input_rules = []const []const u8 { 446 "#..#. => .", 447 ".#..# => #", 448 "..#.# => .", 449 "..... => .", 450 ".#... => #", 451 "#..## => #", 452 "..##. => #", 453 "#.##. => #", 454 "#.#.# => .", 455 "###.# => #", 456 ".#### => .", 457 "..### => .", 458 ".###. => .", 459 "#.#.. => #", 460 "###.. => .", 461 "##.#. => .", 462 "##..# => .", 463 "##.## => .", 464 "#.### => .", 465 "...## => #", 466 "##... => #", 467 "####. => .", 468 ".#.## => .", 469 "#...# => #", 470 ".#.#. => #", 471 "....# => .", 472 ".##.. => .", 473 "...#. => .", 474 "..#.. => .", 475 "#.... => .", 476 ".##.# => #", 477 "##### => #", 478 };