advent-of-code

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

commit 95413b9368ecddfb2760c1f7a653063e6e8ef180
parent 92c6da10e190dcb5a176ac67991df6d4ea78e555
Author: amin <dev@aminmesbah.com>
Date:   Tue, 25 Dec 2018 08:30:28 +0000

WIP: Add solution for 12-1

FossilOrigin-Name: 608eacd903d36c066bacde940b96e4e2041d610c3f97f4d9dd2cce682f5fdf3d
Diffstat:
Aday_12.zig | 463+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 463 insertions(+), 0 deletions(-)

diff --git a/day_12.zig b/day_12.zig @@ -0,0 +1,463 @@ +const std = @import("std"); +const debug = std.debug; +const math = std.math; +const mem = std.mem; +const TypeId = @import("builtin").TypeId; + +pub fn main() void { + debug.warn("state {}, rules {}\n", input_initial_state.len, input_rules.len); + debug.warn("state {}, rules {}\n", test_initial_state.len, test_rules.len); +} + +fn Generation(comptime T: type) type { + return struct { + const Self = @This(); + + gen_num: usize, + first_plant: isize, + last_plant: isize, + pots: []const T, + + fn simNextGeneration(allocator: *std.mem.Allocator, current: Self, rules: u32) Self { + var next_pots = std.ArrayList(T).init(allocator); + defer next_pots.deinit(); + + comptime const pot_int_highest_bit = @truncate(math.Log2Int(T), @typeInfo(T).Int.bits - 1); + comptime debug.assert(pot_int_highest_bit == @typeInfo(T).Int.bits - 1); + + var pot_context: u5 = 0; + var next_pots_pot_int: T = 0; + var next_pot_bit = pot_int_highest_bit; + + for (current.pots) |pot_int, i| { + if (i == 0) { + // Check the 3 leading pots + // 00000 + // ^ + // assign corresponding bit from rules + { + const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); + debug.warn("Rule result {b}\n", new_pot_state); + next_pots_pot_int |= T(new_pot_state) << next_pot_bit; + debug.warn("{x}\n", next_pots_pot_int); + if (next_pot_bit == 0) { + _ = next_pots.append(next_pots_pot_int); + next_pots_pot_int = 0; + next_pot_bit = pot_int_highest_bit; + } + } + + // 0000X + // ^ + { + var shifted: u5 = 0; + var overflowHappened = @shlWithOverflow(u5, pot_context, 1, &shifted); + debug.warn("Overflow? {}\n", overflowHappened); + + pot_context = shifted; + debug.warn("Shifted {b}\n", pot_context); + + const new_bit: u1 = @boolToInt((pot_int & (T(1) << pot_int_highest_bit)) != 0); + pot_context |= new_bit; + debug.warn("With new bit {b}\n", pot_context); + + // assign corresponding bit from rules + const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); + debug.warn("Rule result {b}\n", new_pot_state); + next_pots_pot_int |= T(new_pot_state) << next_pot_bit; + debug.warn("{x}\n", next_pots_pot_int); + if (next_pot_bit == 0) { + _ = next_pots.append(next_pots_pot_int); + next_pots_pot_int = 0; + next_pot_bit = pot_int_highest_bit; + } + } + + // 000XX + // ^ + { + var shifted: u5 = 0; + var overflowHappened = @shlWithOverflow(u5, pot_context, 1, &shifted); + debug.warn("Overflow? {}\n", overflowHappened); + + pot_context = shifted; + debug.warn("Shifted {b}\n", pot_context); + + const new_bit: u1 = @boolToInt((pot_int & (T(1) << pot_int_highest_bit - 1)) != 0); + pot_context |= new_bit; + debug.warn("With new bit {b}\n", pot_context); + + // assign corresponding bit from rules + const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); + debug.warn("Rule result {b}\n", new_pot_state); + next_pots_pot_int |= T(new_pot_state) << next_pot_bit; + debug.warn("{x}\n", next_pots_pot_int); + } + } + + var pot_bit = pot_int_highest_bit - 2; + while (pot_bit >= 0) : ({ pot_bit -= 1; next_pot_bit -= 1; }) { + var shifted: u5 = 0; + var overflowHappened = @shlWithOverflow(u5, pot_context, 1, &shifted); + debug.warn("Overflow? {}\n", overflowHappened); + + pot_context = shifted; + debug.warn("Shifted {b}\n", pot_context); + + const new_bit: u1 = @boolToInt((pot_int & (T(1) << pot_bit)) != 0); + pot_context |= new_bit; + debug.warn("With new bit {b}\n", pot_context); + + // assign corresponding bit from rules + const new_pot_state = @boolToInt((rules & (u32(1) << pot_context)) != 0); + debug.warn("Rule result {b}\n", new_pot_state); + next_pots_pot_int |= T(new_pot_state) << next_pot_bit; + debug.warn("{x}\n", next_pots_pot_int); + if (next_pot_bit == 0) { + _ = next_pots.append(next_pots_pot_int); + next_pots_pot_int = 0; + next_pot_bit = pot_int_highest_bit; + } + + if (pot_bit == 0) { + break; + } + } + _ = next_pots.append(next_pots_pot_int); + } + + // TODO: check the 3 following pots!! + + const result = Self { + .gen_num = current.gen_num + 1, + .first_plant = current.first_plant, + .last_plant = current.last_plant, + .pots = next_pots.toSlice(), + }; + return result; + } + }; +} + +test "sim next gen" { + const TestPotInt = u128; + const TestGeneration = Generation(TestPotInt); + var allocator = debug.global_allocator; + + const rules = rulesToInt(u32, test_rules); + + var g = TestGeneration { + .gen_num = 0, + .first_plant = 0, + .last_plant = 24, + .pots = potsToIntArray(TestPotInt, test_initial_state), + }; + + var expected_first_plant: isize = undefined; + var expected_last_plant: isize = undefined; + var expected_pots: []const TestPotInt = potsToIntArray(TestPotInt, "........................."); + + while (g.gen_num <= 20) { + debug.warn("{}\n", g.gen_num); + // TODO: replace this with an array of expected `Generation`s + switch (g.gen_num) { + 0 => { + expected_first_plant = 0; + expected_last_plant = 24; + expected_pots = potsToIntArray(TestPotInt, "#..#.#..##......###...###"); + }, + 1 => { + expected_first_plant = 0; + expected_last_plant = 24; + // TODO: Why is the assertion on this passing!? + expected_pots = potsToIntArray(TestPotInt, "#...#....#.....#..#..#..#"); + }, + 2 => { + expected_first_plant = 0; + expected_last_plant = 25; + expected_pots = potsToIntArray(TestPotInt, "##..##...##....#..#..#..##"); + }, + 3 => { + expected_first_plant = -1; + expected_last_plant = 25; + expected_pots = potsToIntArray(TestPotInt, "#.#...#..#.#....#..#..#...#"); + }, + 4 => { + expected_first_plant = 0; + expected_last_plant = 26; + expected_pots = potsToIntArray(TestPotInt, "#.#..#...#.#...#..#..##..##"); + }, + 5 => { + expected_first_plant = 1; + expected_last_plant = 26; + expected_pots = potsToIntArray(TestPotInt, "#...##...#.#..#..#...#...#"); + }, + 6 => { + expected_first_plant = 1; + expected_last_plant = 27; + expected_pots = potsToIntArray(TestPotInt, "##.#.#....#...#..##..##..##"); + }, + 7 => { + expected_first_plant = 0; + expected_last_plant = 27; + expected_pots = potsToIntArray(TestPotInt, "#..###.#...##..#...#...#...#"); + }, + 8 => { + expected_first_plant = 0; + expected_last_plant = 28; + expected_pots = potsToIntArray(TestPotInt, "#....##.#.#.#..##..##..##..##"); + }, + 9 => { + expected_first_plant = 0; + expected_last_plant = 28; + expected_pots = potsToIntArray(TestPotInt, "##..#..#####....#...#...#...#"); + }, + 10 => { + expected_first_plant = -1; + expected_last_plant = 29; + expected_pots = potsToIntArray(TestPotInt, "#.#..#...#.##....##..##..##..##"); + }, + 11 => { + expected_first_plant = 0; + expected_last_plant = 29; + expected_pots = potsToIntArray(TestPotInt, "#...##...#.#...#.#...#...#...#"); + }, + 12 => { + expected_first_plant = 0; + expected_last_plant = 30; + expected_pots = potsToIntArray(TestPotInt, "##.#.#....#.#...#.#..##..##..##"); + }, + 13 => { + expected_first_plant = -1; + expected_last_plant = 30; + expected_pots = potsToIntArray(TestPotInt, "#..###.#....#.#...#....#...#...#"); + }, + 14 => { + expected_first_plant = -1; + expected_last_plant = 31; + expected_pots = potsToIntArray(TestPotInt, "#....##.#....#.#..##...##..##..##"); + }, + 15 => { + expected_first_plant = -1; + expected_last_plant = 31; + expected_pots = potsToIntArray(TestPotInt, "##..#..#.#....#....#..#.#...#...#"); + }, + 16 => { + expected_first_plant = -2; + expected_last_plant = 32; + expected_pots = potsToIntArray(TestPotInt, "#.#..#...#.#...##...#...#.#..##..##"); + }, + 17 => { + expected_first_plant = -1; + expected_last_plant = 32; + expected_pots = potsToIntArray(TestPotInt, "#...##...#.#.#.#...##...#....#...#"); + }, + 18 => { + expected_first_plant = -1; + expected_last_plant = 33; + expected_pots = potsToIntArray(TestPotInt, "##.#.#....#####.#.#.#...##...##..##"); + }, + 19 => { + expected_first_plant = -2; + expected_last_plant = 33; + expected_pots = potsToIntArray(TestPotInt, "#..###.#..#.#.#######.#.#.#..#.#...#"); + }, + 20 => { + expected_first_plant = -2; + expected_last_plant = 34; + expected_pots = potsToIntArray(TestPotInt, "#....##....#####...#######....#.#..##"); + }, + else => unreachable, + } + debug.warn("{} == {}\n", expected_pots[0], g.pots[0]); + debug.assert(expected_first_plant == g.first_plant); + debug.assert(expected_last_plant == g.last_plant); + debug.assert(g.pots.len == 1); + debug.assert(expected_pots.len == 1); + //debug.assert(mem.eql(TestPotInt, expected_pots, g.pots)); + debug.assert(expected_pots[0] == g.pots[0]); + g = TestGeneration.simNextGeneration(allocator, g, rules); + } +} + +fn rulesToInt(comptime T: type, rules: []const []const u8) T { + comptime { + debug.assert(@typeId(T) == TypeId.Int); + debug.assert(!@typeInfo(T).Int.is_signed); + } + debug.assert(rules.len <= @typeInfo(T).Int.bits); + var num: T = 0; + + for (rules) |r| { + var it = mem.split(r, " =>"); + const pattern = potsToInt(u8, it.next().?); + const result = it.next().?; + debug.assert(it.next() == null); + + const bit_value = switch (result[0]) { + '.' => T(0), + '#' => T(1), + else => unreachable, + }; + + comptime const ShiftInt = math.Log2Int(T); + num |= bit_value << @intCast(ShiftInt, pattern); + } + return num; +} + +test "rules to int" { + debug.assert(rulesToInt(u32, test_rules) == 0x7CA09D18); +} + +fn potsToIntArray(comptime T: type, comptime pots: []const u8) []const T { + comptime { + debug.assert(!@typeInfo(T).Int.is_signed); + } + comptime const int_bits = @typeInfo(T).Int.bits; + + // round up to account for remainder pots + comptime const result_array_len: usize = (pots.len + int_bits - 1) / int_bits; + + var result = []T{0} ** result_array_len; + + for (result) |*elem, i| { + const pot_slice_index = i * int_bits; + + var pot_slice: []const u8 = undefined; + + if (i == result.len - 1) { + // we need right-padding + var padded_pots = "." ** int_bits; + for (pots[pot_slice_index..]) |p, p_i| { + padded_pots[p_i] = p; + } + pot_slice = padded_pots; + } else { + const next_slice_index = pot_slice_index + int_bits; + pot_slice = pots[pot_slice_index..next_slice_index]; + } + + elem.* = potsToInt(T, pot_slice); + } + + return result; +} + +test "pots to int array" { + { + const potArray = potsToIntArray(u8, test_initial_state); + debug.assert(potArray.len == 4); + debug.assert(potArray[0] == 0x94); // #..#.#.. + debug.assert(potArray[1] == 0xC0); // ##...... + debug.assert(potArray[2] == 0xE3); // ###...## + debug.assert(potArray[3] == 0x80); // #....... + } + + { + const potArray = potsToIntArray(u32, test_initial_state); + debug.assert(potArray.len == 1); + debug.assert(potArray[0] == 0x94C0E380); + } +} + +fn potsToInt(comptime T: type, pots: []const u8) T { + comptime { + debug.assert(@typeId(T) == TypeId.Int); + debug.assert(!@typeInfo(T).Int.is_signed); + } + comptime const int_bits = @typeInfo(T).Int.bits; + debug.assert(pots.len <= int_bits); + + var num: T = 0; + var i: u8 = 0; + + for (pots) |p| { + if (p == '#') { + comptime const ShiftInt = math.Log2Int(T); + num |= T(1) << @intCast(ShiftInt, pots.len - 1 - i); + } + i += 1; + } + return num; +} + +test "pots to int" { + debug.assert(potsToInt(u8, ".....") == 0); + debug.assert(potsToInt(u8, "....#") == 1); + debug.assert(potsToInt(u8, "...##") == 3); + debug.assert(potsToInt(u8, "..###") == 7); + debug.assert(potsToInt(u8, ".####") == 15); + debug.assert(potsToInt(u8, "#####") == 31); + debug.assert(potsToInt(u8, "####.") == 30); + debug.assert(potsToInt(u8, "###..") == 28); + debug.assert(potsToInt(u8, "##...") == 24); + debug.assert(potsToInt(u8, "#....") == 16); + debug.assert(potsToInt(u8, "#") == 1); + debug.assert(potsToInt(u5, "#####") == 31); + debug.assert(potsToInt(u128, ".#.#.") == 10); + debug.assert(potsToInt(u128, "#.#.#") == 21); + debug.assert(potsToInt(u32, test_initial_state) == 0x12981C7); + debug.assert(potsToInt(u128, input_initial_state) == 0xC9A1A56AFCD5E918842DC9C44); +} + +const test_initial_state = "#..#.#..##......###...###"; + +const test_rules = []const []const u8 { + "...## => #", + "..#.. => #", + ".#... => #", + ".#.#. => #", + ".#.## => #", + ".##.. => #", + ".#### => #", + "#.#.# => #", + "#.### => #", + "##.#. => #", + "##.## => #", + "###.. => #", + "###.# => #", + "####. => #", +}; + +const input_initial_state = "##..#..##.#....##.#..#.#.##.#.#.######..##.#.#.####.#..#...##...#....#....#.##.###..#..###...#...#.."; + +// Hey, look at that: +// - there are exactly 32 rules +// - the patterns, if interpreted as bits, correspond to u8 0 through 31 +// - the results, by themselves, could fit in a single u32 +// That's... interesting... +const input_rules = []const []const u8 { + "#..#. => .", + ".#..# => #", + "..#.# => .", + "..... => .", + ".#... => #", + "#..## => #", + "..##. => #", + "#.##. => #", + "#.#.# => .", + "###.# => #", + ".#### => .", + "..### => .", + ".###. => .", + "#.#.. => #", + "###.. => .", + "##.#. => .", + "##..# => .", + "##.## => .", + "#.### => .", + "...## => #", + "##... => #", + "####. => .", + ".#.## => .", + "#...# => #", + ".#.#. => #", + "....# => .", + ".##.. => .", + "...#. => .", + "..#.. => .", + "#.... => .", + ".##.# => #", + "##### => #", +};