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;
}
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] };
}
}
}
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");
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);
array.push(^tiles, Tile.{
id = id,
- orientation = TileOrientation.NORMAL,
+ orientation = TO.N,
data = tile_data,
edges = edges,
});
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;
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);
}