added day 20 part 2
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 22 Dec 2020 04:19:21 +0000 (22:19 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 22 Dec 2020 04:19:21 +0000 (22:19 -0600)
day20.onyx

index 53cc8342f24500c90f7a1de52ae2101758f3397d..8c466e9ad84fac3ac22bf109f1cd329cf00585cd 100644 (file)
@@ -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);
 }