From: Brendan Hansen Date: Tue, 22 Dec 2020 04:19:21 +0000 (-0600) Subject: added day 20 part 2 X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fcbd9d7188553b3e7c8c0392319c1aab2cf1981e;p=onyx-aoc-2020.git added day 20 part 2 --- diff --git a/day20.onyx b/day20.onyx index 53cc834..8c466e9 100644 --- a/day20.onyx +++ b/day20.onyx @@ -6,34 +6,52 @@ use package core.string.reader as reader TILE_DATA_WIDTH :: 10 TILE_DATA_HEIGHT :: 10 +// Thought about making this dynamic and really fancy... +// and then I remembered this is advent of code so that +// is not necessary. +TileData :: #type [TILE_DATA_WIDTH * TILE_DATA_HEIGHT] bool; + Tile :: struct { id : u32; - orientation : TileOrientation; + orientation : TO; data : [] bool; edges : [] u32; pos_x : u32 = 0; pos_y : u32 = 0; - edges_with_match : Sides = Sides.{}; + edges_match : Sides = Sides.{}; } Sides :: struct { - top : bool = false; - right : bool = false; - bottom : bool = false; - left : bool = false; + top : SideRelation = SideRelation.{}; + right : SideRelation = SideRelation.{}; + bottom : SideRelation = SideRelation.{}; + left : SideRelation = SideRelation.{}; } -TileOrientation :: enum { - NORMAL; ROTATE_90; ROTATE_180; ROTATE_270; - FLIPPED_NORMAL; FLIPPED_ROTATE_90; FLIPPED_ROTATE_180; FLIPPED_ROTATE_270; +SideRelation :: struct { + tile : u32 = 0; + ori : TO = TO.N; } -// Thought about making this dynamic and really fancy... -// and then I remembered this is advent of code so that -// is not necessary. -TileData :: #type [TILE_DATA_WIDTH * TILE_DATA_HEIGHT] bool; +TO :: enum (u32) { + N; R90; R180; R270; + F; FR90; FR180; FR270; +} + +// TOT[t0][t1] = t1 * t0; t1 after t0 +tile_orientation_table := (#type [8] TO).[ + // N R90 R180 R270 F FR90 FR180 FR270 + /* N */ TO.[ TO.N, TO.R90, TO.R180, TO.R270, TO.F, TO.FR90, TO.FR180, TO.FR270 ], + /* R90 */ TO.[ TO.R90, TO.R180, TO.R270, TO.N, TO.FR270, TO.F, TO.FR90, TO.FR180,], + /* R180 */ TO.[ TO.R180, TO.R270, TO.N, TO.R90, TO.FR180, TO.FR270, TO.F, TO.FR90, ], + /* R270 */ TO.[ TO.R270, TO.N, TO.R90, TO.R180, TO.FR90, TO.FR180, TO.FR270, TO.F, ], + /* F */ TO.[ TO.F, TO.FR90, TO.FR180, TO.FR270, TO.N, TO.R90, TO.R180, TO.R270, ], + /* FR90 */ TO.[ TO.FR90, TO.FR180, TO.FR270, TO.F, TO.R270, TO.N, TO.R90, TO.R180, ], + /* FR180 */ TO.[ TO.FR180, TO.FR270, TO.F, TO.FR90, TO.R180, TO.R270, TO.N, TO.R90 ], + /* FR270 */ TO.[ TO.FR270, TO.F, TO.FR90, TO.FR180, TO.R90, TO.R180, TO.R270, TO.N ], +]; reverse_binary :: proc (n_: u32, digits := TILE_DATA_WIDTH) -> u32 { res := 0; @@ -48,57 +66,62 @@ reverse_binary :: proc (n_: u32, digits := TILE_DATA_WIDTH) -> u32 { } build_edges :: proc (tile: [] bool, a := context.allocator) -> [] u32 { - edges : [] u32; + edges : [..] u32; #context_scope { context.allocator = a; - alloc.alloc_slice(^edges, 8); + array.init(^edges, 8); } - next_edge := 0; for y: u32.[0, 9] { edge := 0; - for x: 0 .. 10 { edge <<= 1; if tile[x + y * TILE_DATA_WIDTH] do edge |= 1; } - edges[next_edge] = edge; - next_edge += 1; + array.push(^edges, edge); } for x: u32.[0, 9] { edge := 0; - for y: 0 .. 10 { edge <<= 1; if tile[x + y * TILE_DATA_WIDTH] do edge |= 1; } - edges[next_edge] = edge; - next_edge += 1; + array.push(^edges, edge); } - for i: 0 .. 4 { - edges[next_edge] = reverse_binary(edges[i]); - next_edge += 1; - } + for i: 0 .. 4 do array.push(^edges, reverse_binary(edges[i])); - return edges; + return edges.data[0 .. 8]; } +// These were not fun to think about by hand... But they make the +// program ridiculously faster so I'm okay with it. +side_relations := (#type [4] TO).[ + TO.[ TO.F, TO.N, TO.R90, TO.FR90, ], + TO.[ TO.N, TO.F, TO.FR90, TO.R90, ], + TO.[ TO.R270, TO.FR90, TO.FR180, TO.N, ], + TO.[ TO.FR90, TO.R270, TO.N, TO.FR180, ], + TO.[ TO.R180, TO.FR180, TO.FR270, TO.R270, ], + TO.[ TO.FR180, TO.R180, TO.R270, TO.FR270, ], + TO.[ TO.FR270, TO.R90, TO.R180, TO.F, ], + TO.[ TO.R90, TO.FR270, TO.F, TO.R180, ], +]; + has_matching_edges :: proc (t1: ^Tile, t2: ^Tile) -> bool { match := false; - for e_idx: 0 .. t1.edges.count / 2 do for e2: t2.edges { - if t1.edges[e_idx] == e2 { + for e_idx: 0 .. t1.edges.count / 2 do for e2_idx: 0 .. t2.edges.count { + if t1.edges[e_idx] == t2.edges[e2_idx] { match = true; switch e_idx { - case 0 do t1.edges_with_match.top = true; - case 1 do t1.edges_with_match.right = true; - case 2 do t1.edges_with_match.bottom = true; - case 3 do t1.edges_with_match.left = true; + case 0 do t1.edges_match.top = SideRelation.{ t2.id, side_relations[e2_idx][0] }; + case 1 do t1.edges_match.bottom = SideRelation.{ t2.id, side_relations[e2_idx][1] }; + case 2 do t1.edges_match.left = SideRelation.{ t2.id, side_relations[e2_idx][2] }; + case 3 do t1.edges_match.right = SideRelation.{ t2.id, side_relations[e2_idx][3] }; } } } @@ -106,6 +129,132 @@ has_matching_edges :: proc (t1: ^Tile, t2: ^Tile) -> bool { return match; } +// This assumes the `t` was in NORMAL orientation to begin with. +apply_orientation :: proc (t: ^Tile, ori: TO) { + new_sides := t.edges_match; + + switch ori { + case TO.R90 { + new_sides.top = t.edges_match.left; + new_sides.right = t.edges_match.top; + new_sides.bottom = t.edges_match.right; + new_sides.left = t.edges_match.bottom; + } + case TO.R180 { + new_sides.top = t.edges_match.bottom; + new_sides.right = t.edges_match.left; + new_sides.bottom = t.edges_match.top; + new_sides.left = t.edges_match.right; + } + case TO.R270 { + new_sides.top = t.edges_match.right; + new_sides.right = t.edges_match.bottom; + new_sides.bottom = t.edges_match.left; + new_sides.left = t.edges_match.top; + } + case TO.F { + new_sides.top = t.edges_match.bottom; + new_sides.bottom = t.edges_match.top; + } + case TO.FR90 { + new_sides.top = t.edges_match.left; + new_sides.right = t.edges_match.bottom; + new_sides.bottom = t.edges_match.right; + new_sides.left = t.edges_match.top; + } + case TO.FR180 { + new_sides.right = t.edges_match.left; + new_sides.left = t.edges_match.right; + } + case TO.FR270 { + new_sides.top = t.edges_match.right; + new_sides.right = t.edges_match.top; + new_sides.bottom = t.edges_match.left; + new_sides.left = t.edges_match.bottom; + } + } + + if new_sides.top.tile != 0 do new_sides.top.ori = tile_orientation_table[cast(u32) new_sides.top.ori][cast(u32) ori]; + if new_sides.bottom.tile != 0 do new_sides.bottom.ori = tile_orientation_table[cast(u32) new_sides.bottom.ori][cast(u32) ori]; + if new_sides.left.tile != 0 do new_sides.left.ori = tile_orientation_table[cast(u32) new_sides.left.ori][cast(u32) ori]; + if new_sides.right.tile != 0 do new_sides.right.ori = tile_orientation_table[cast(u32) new_sides.right.ori][cast(u32) ori]; + + t.edges_match = new_sides; + t.orientation = ori; +} + +dummy_data := u32.[ + 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, +]; + +index_square_with_orientation :: proc (data: ^$T, ori: TO, size: i32, x: i32, y: i32) -> ^T { + switch ori { + case TO.N do return ^data[x + y * size]; + case TO.R90 do return ^data[y + (size - 1 - x) * size]; + case TO.R180 do return ^data[(size - 1 - x) + (size - 1 - y) * size]; + case TO.R270 do return ^data[(size - 1 - y) + x * size]; + case TO.F do return ^data[x + (size - 1 - y) * size]; + case TO.FR90 do return ^data[y + x * size]; + case TO.FR180 do return ^data[(size - 1 - x) + y * size]; + case TO.FR270 do return ^data[(size - 1 - y) + (size - 1 - x) * size]; + } + + return null; +} + +sea_monster_width := 20; +sea_monster_height := 3; +sea_monster := u8.[ + #char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char" ",#char"#",#char" ", + #char"#",#char" ",#char" ",#char" ",#char" ",#char"#",#char"#",#char" ",#char" ",#char" ",#char" ",#char"#",#char"#",#char" ",#char" ",#char" ",#char" ",#char"#",#char"#",#char"#", + #char" ",#char"#",#char" ",#char" ",#char"#",#char" ",#char" ",#char"#",#char" ",#char" ",#char"#",#char" ",#char" ",#char"#",#char" ",#char" ",#char"#",#char" ",#char" ",#char" ", +]; + +scan_for_monsters :: proc (forest: ^u8, ori: TO, width: u32, height: u32) -> bool { + found_monsters := false; + + for y: 0 .. height - sea_monster_height - 1 { + for x: 0 .. width - sea_monster_width - 1 { + is_monster := true; + + for my: 0 .. sea_monster_height { + for mx: 0 .. sea_monster_width { + if sea_monster[mx + my * sea_monster_width] != #char "#" do continue; + if *index_square_with_orientation(forest, ori, width, x + mx, y + my) != #char "." do continue; + + is_monster = false; + break break; + } + } + + if is_monster { + for my: 0 .. sea_monster_height { + for mx: 0 .. sea_monster_width { + if sea_monster[mx + my * sea_monster_width] != #char "#" do continue; + if *index_square_with_orientation(forest, ori, width, x + mx, y + my) != #char "#" do continue; + + *index_square_with_orientation(forest, ori, width, x + mx, y + my) = #char "o"; + } + } + + found_monsters = true; + } + } + } + + return found_monsters; +} + +tile_pos_state :: struct { + match : SideRelation; + pos_x : i32; + pos_y : i32; +} + main :: proc (args: [] cstr) { contents := file.get_contents("input/day20.txt"); @@ -115,6 +264,10 @@ main :: proc (args: [] cstr) { array.init(^tiles); defer array.free(^tiles); + tile_map : map.Map(u32, ^Tile); + map.init(^tile_map, 67); + defer map.free(^tile_map); + tile_data := calloc(200 * sizeof TileData); defer cfree(tile_data); @@ -142,7 +295,7 @@ main :: proc (args: [] cstr) { array.push(^tiles, Tile.{ id = id, - orientation = TileOrientation.NORMAL, + orientation = TO.N, data = tile_data, edges = edges, }); @@ -150,7 +303,10 @@ main :: proc (args: [] cstr) { reader.advance_line(^file); } + for ^t: tiles do map.put(^tile_map, t.id, t); + prod: u64 = 1; + top_left_id := 0; for i: 0 .. tiles.count - 1 { matching_count := 0; @@ -164,8 +320,67 @@ main :: proc (args: [] cstr) { if matching_count == 2 { prod *= ~~tiles[i].id; + + // HACK!!! + if tiles[i].edges_match.top.tile != 0 && tiles[i].edges_match.right.tile != 0 { + if top_left_id == 0 do top_left_id = tiles[i].id; + } } } printf("Corner product: %l\n", prod); + + grid : [12 * 12] u32; + memory.set(^grid, sizeof [12 * 12] u32, 0); + + to_process : [..] tile_pos_state; + array.init(^to_process); + defer array.free(^to_process); + + array.push(^to_process, tile_pos_state.{ SideRelation.{ top_left_id, TO.F }, 0, 0 }); + while to_process.count > 0 { + tid := to_process[0]; + array.delete(^to_process, 0); + + tile_ptr := map.get(^tile_map, tid.match.tile); + + if tid.pos_x < 0 || tid.pos_y < 0 || tid.pos_x >= 12 || tid.pos_y >= 12 do continue; + if grid[tid.pos_x + 12 * tid.pos_y] != 0 do continue; + + tile_ptr.pos_x = tid.pos_x; + tile_ptr.pos_y = tid.pos_y; + grid[tid.pos_x + 12 * tid.pos_y] = tid.match.tile; + + apply_orientation(tile_ptr, tid.match.ori); + + if tile_ptr.edges_match.top.tile != 0 && tid.pos_y >= 1 do array.push(^to_process, tile_pos_state.{ tile_ptr.edges_match.top, tid.pos_x, tid.pos_y - 1 }); + if tile_ptr.edges_match.bottom.tile != 0 && tid.pos_y <= 10 do array.push(^to_process, tile_pos_state.{ tile_ptr.edges_match.bottom, tid.pos_x, tid.pos_y + 1 }); + if tile_ptr.edges_match.left.tile != 0 && tid.pos_x >= 1 do array.push(^to_process, tile_pos_state.{ tile_ptr.edges_match.left, tid.pos_x - 1, tid.pos_y }); + if tile_ptr.edges_match.right.tile != 0 && tid.pos_x <= 10 do array.push(^to_process, tile_pos_state.{ tile_ptr.edges_match.right, tid.pos_x + 1, tid.pos_y }); + } + + forest : [12 * 8 * 12 * 8] u8; + for y: 0 .. 12 { + for x: 0 .. 12 { + tile := map.get(^tile_map, grid[y * 12 + x]); + + for fy: 0 .. 8 { + for fx: 0 .. 8 { + res := *index_square_with_orientation(cast(^bool) tile.data.data, tile.orientation, 10, fx + 1, fy + 1); + loc := (y * 12 * 8 * 8) + (fy * 12 * 8) + (x * 8) + fx; + if res do forest[loc] = #char "#"; + else do forest[loc] = #char "."; + } + } + } + } + + for ori: TO.[ TO.N, TO.R90, TO.R180, TO.R270, TO.F, TO.FR90, TO.FR180, TO.FR270 ] { + if scan_for_monsters(cast(^u8) forest, ori, 12 * 8, 12 * 8) do break; + } + + safe_count := 0; + for c: forest do if c == #char "#" do safe_count += 1; + + printf("Safe count: %i\n", safe_count); }