advent-of-code

Solutions for Advent of Code.
git clone git://git.amin.space/advent-of-code.git
Log | Files | Refs | LICENSE

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 };