AOC2023: Implement day10
This commit is contained in:
parent
2f8759d2d3
commit
f077e17b2a
|
@ -12,6 +12,7 @@ set(AOC2023_PROJECTS
|
||||||
day7
|
day7
|
||||||
day8
|
day8
|
||||||
day9
|
day9
|
||||||
|
day10
|
||||||
)
|
)
|
||||||
|
|
||||||
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)
|
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
project(aoc2023_day10 CXX)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(aoc2023_day10 ${SOURCES})
|
||||||
|
target_compile_options(aoc2023_day10 PUBLIC -O2 -g)
|
||||||
|
target_link_libraries(aoc2023_day10 PUBLIC libc ban)
|
||||||
|
|
||||||
|
add_dependencies(aoc2023_day10 libc-install ban-install)
|
||||||
|
|
||||||
|
add_custom_target(aoc2023_day10-install
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day10 ${BANAN_AOC2023_BIN}/day10
|
||||||
|
DEPENDS aoc2023_day10
|
||||||
|
DEPENDS aoc2023_always
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(aoc2023 aoc2023_day10)
|
||||||
|
add_dependencies(aoc2023-install aoc2023_day10-install)
|
|
@ -0,0 +1,278 @@
|
||||||
|
#include <BAN/Array.h>
|
||||||
|
#include <BAN/String.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
using i32 = int32_t;
|
||||||
|
using i64 = int64_t;
|
||||||
|
|
||||||
|
using u32 = uint32_t;
|
||||||
|
using u64 = uint64_t;
|
||||||
|
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
North,
|
||||||
|
East,
|
||||||
|
South,
|
||||||
|
West
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Position
|
||||||
|
{
|
||||||
|
size_t x;
|
||||||
|
size_t y;
|
||||||
|
Direction from;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Grid = BAN::Vector<BAN::Vector<u32>>;
|
||||||
|
|
||||||
|
Grid parse_grid(FILE* fp)
|
||||||
|
{
|
||||||
|
Grid grid;
|
||||||
|
char buffer[256];
|
||||||
|
while (fgets(buffer, sizeof(buffer), fp))
|
||||||
|
{
|
||||||
|
if (strlen(buffer) < 2)
|
||||||
|
continue;
|
||||||
|
MUST(grid.emplace_back(strlen(buffer) - 1));
|
||||||
|
for (size_t i = 0; buffer[i + 1]; i++)
|
||||||
|
grid.back()[i] = buffer[i];
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_enter_tile_from(char tile, Direction from)
|
||||||
|
{
|
||||||
|
switch (from)
|
||||||
|
{
|
||||||
|
case Direction::North:
|
||||||
|
return tile == '|' || tile == 'L' || tile == 'J';
|
||||||
|
case Direction::South:
|
||||||
|
return tile == '|' || tile == '7' || tile == 'F';
|
||||||
|
case Direction::West:
|
||||||
|
return tile == '-' || tile == 'J' || tile == '7';
|
||||||
|
case Direction::East:
|
||||||
|
return tile == '-' || tile == 'L' || tile == 'F';
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Direction tile_exit_direction(char tile, Direction enter)
|
||||||
|
{
|
||||||
|
switch (tile)
|
||||||
|
{
|
||||||
|
case '|': return (enter == Direction::North) ? Direction::South : Direction::North;
|
||||||
|
case '-': return (enter == Direction::East) ? Direction::West : Direction::East;
|
||||||
|
case 'L': return (enter == Direction::North) ? Direction::East : Direction::North;
|
||||||
|
case 'J': return (enter == Direction::North) ? Direction::West : Direction::North;
|
||||||
|
case '7': return (enter == Direction::South) ? Direction::West : Direction::South;
|
||||||
|
case 'F': return (enter == Direction::South) ? Direction::East : Direction::South;
|
||||||
|
}
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::Array<Position, 2> find_grid_first_moves(const Grid& grid)
|
||||||
|
{
|
||||||
|
BAN::Array<Position, 2> positions;
|
||||||
|
for (size_t y = 0; y < grid.size(); y++)
|
||||||
|
{
|
||||||
|
for (size_t x = 0; x < grid.size(); x++)
|
||||||
|
{
|
||||||
|
if (grid[y][x] == 'S')
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
if (can_enter_tile_from(grid[y - 1][x], Direction::South))
|
||||||
|
positions[index++] = { x, y - 1, Direction::South };
|
||||||
|
if (can_enter_tile_from(grid[y + 1][x], Direction::North))
|
||||||
|
positions[index++] = { x, y + 1, Direction::North };
|
||||||
|
if (can_enter_tile_from(grid[y][x - 1], Direction::East))
|
||||||
|
positions[index++] = { x - 1, y, Direction::East };
|
||||||
|
if (can_enter_tile_from(grid[y][x + 1], Direction::West))
|
||||||
|
positions[index++] = { x + 1, y, Direction::West };
|
||||||
|
ASSERT(index == 2);
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 puzzle1(FILE* fp)
|
||||||
|
{
|
||||||
|
auto grid = parse_grid(fp);
|
||||||
|
auto positions = find_grid_first_moves(grid);
|
||||||
|
|
||||||
|
for (i64 distance = 1;; distance++)
|
||||||
|
{
|
||||||
|
if (positions[0].x == positions[1].x && positions[0].y == positions[1].y)
|
||||||
|
return distance;
|
||||||
|
|
||||||
|
for (auto& position : positions)
|
||||||
|
{
|
||||||
|
Direction direction = tile_exit_direction(grid[position.y][position.x], position.from);
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction::North: position.y--; position.from = Direction::South; break;
|
||||||
|
case Direction::South: position.y++; position.from = Direction::North; break;
|
||||||
|
case Direction::West: position.x--; position.from = Direction::East; break;
|
||||||
|
case Direction::East: position.x++; position.from = Direction::West; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 puzzle2(FILE* fp)
|
||||||
|
{
|
||||||
|
enum Flag : u32
|
||||||
|
{
|
||||||
|
Path = 1 << 8,
|
||||||
|
Left = 1 << 9,
|
||||||
|
Right = 1 << 10,
|
||||||
|
Mask = Path | Left | Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto grid = parse_grid(fp);
|
||||||
|
auto position = find_grid_first_moves(grid)[0];
|
||||||
|
|
||||||
|
while ((grid[position.y][position.x] & ~Flag::Mask) != 'S')
|
||||||
|
{
|
||||||
|
Direction direction = tile_exit_direction(grid[position.y][position.x] & ~Flag::Mask, position.from);
|
||||||
|
|
||||||
|
switch (grid[position.y][position.x] & ~Flag::Mask)
|
||||||
|
{
|
||||||
|
case '|':
|
||||||
|
if (position.x > 0)
|
||||||
|
grid[position.y][position.x - 1] |= (direction == Direction::North) ? Flag::Left : Flag::Right;
|
||||||
|
if (position.x < grid[position.y].size() - 1)
|
||||||
|
grid[position.y][position.x + 1] |= (direction == Direction::North) ? Flag::Right : Flag::Left;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
if (position.y > 0)
|
||||||
|
grid[position.y - 1][position.x] |= (direction == Direction::East) ? Flag::Left : Flag::Right;
|
||||||
|
if (position.y < grid.size() - 1)
|
||||||
|
grid[position.y + 1][position.x] |= (direction == Direction::East) ? Flag::Right : Flag::Left;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
if (position.x > 0)
|
||||||
|
grid[position.y][position.x - 1] |= (direction == Direction::North) ? Flag::Left : Flag::Right;
|
||||||
|
if (position.y < grid.size() - 1)
|
||||||
|
grid[position.y + 1][position.x] |= (direction == Direction::North) ? Flag::Left : Flag::Right;
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
if (position.x < grid[position.y].size() - 1)
|
||||||
|
grid[position.y][position.x + 1] |= (direction == Direction::West) ? Flag::Left : Flag::Right;
|
||||||
|
if (position.y < grid.size() - 1)
|
||||||
|
grid[position.y + 1][position.x] |= (direction == Direction::West) ? Flag::Left : Flag::Right;
|
||||||
|
break;
|
||||||
|
case '7':
|
||||||
|
if (position.y > 0)
|
||||||
|
grid[position.y - 1][position.x] |= (direction == Direction::South) ? Flag::Left : Flag::Right;
|
||||||
|
if (position.x < grid[position.y].size() - 1)
|
||||||
|
grid[position.y][position.x + 1] |= (direction == Direction::South) ? Flag::Left : Flag::Right;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
if (position.y > 0)
|
||||||
|
grid[position.y - 1][position.x] |= (direction == Direction::East) ? Flag::Left : Flag::Right;
|
||||||
|
if (position.x > 0)
|
||||||
|
grid[position.y][position.x - 1] |= (direction == Direction::East) ? Flag::Left : Flag::Right;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
grid[position.y][position.x] |= Flag::Path;
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction::North: position.y--; position.from = Direction::South; break;
|
||||||
|
case Direction::South: position.y++; position.from = Direction::North; break;
|
||||||
|
case Direction::West: position.x--; position.from = Direction::East; break;
|
||||||
|
case Direction::East: position.x++; position.from = Direction::West; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark start tile as part of the path
|
||||||
|
grid[position.y][position.x] |= Flag::Path;
|
||||||
|
|
||||||
|
// Clean up flags
|
||||||
|
for (auto& row : grid)
|
||||||
|
{
|
||||||
|
for (u32& tile : row)
|
||||||
|
{
|
||||||
|
// Remove left and right from path
|
||||||
|
if (tile & Flag::Path)
|
||||||
|
tile &= ~(Flag::Left | Flag::Right);
|
||||||
|
// Tile should never be both left and right
|
||||||
|
ASSERT(!((tile & Flag::Left) && (tile & Flag::Right)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine whether left or right is enclosed by loop
|
||||||
|
Flag enclosed = Flag::Path;
|
||||||
|
for (const auto& row : grid)
|
||||||
|
{
|
||||||
|
for (u32 tile : row)
|
||||||
|
{
|
||||||
|
if ((tile & (Flag::Right | Flag::Left)))
|
||||||
|
{
|
||||||
|
enclosed = (tile & Flag::Right) ? Flag::Left : Flag::Right;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enclosed != Flag::Path)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ASSERT(enclosed != Flag::Path);
|
||||||
|
|
||||||
|
// Expand all enclosed areas
|
||||||
|
bool modified = true;
|
||||||
|
while (modified)
|
||||||
|
{
|
||||||
|
modified = false;
|
||||||
|
for (size_t y = 1; y < grid.size(); y++)
|
||||||
|
{
|
||||||
|
for (size_t x = 1; x < grid[y].size(); x++)
|
||||||
|
{
|
||||||
|
if (grid[y][x] & Flag::Mask)
|
||||||
|
continue;
|
||||||
|
if ((grid[y - 1][x] & enclosed) || (grid[y][x - 1] & enclosed))
|
||||||
|
{
|
||||||
|
grid[y][x] |= enclosed;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate number of enclosed tiles
|
||||||
|
i64 result = 0;
|
||||||
|
for (const auto& row : grid)
|
||||||
|
for (u32 c : row)
|
||||||
|
result += !!(c & enclosed);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const char* file_path = "/usr/share/aoc2023/day10_input.txt";
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
file_path = argv[1];
|
||||||
|
|
||||||
|
FILE* fp = fopen(file_path, "r");
|
||||||
|
if (fp == nullptr)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("puzzle1: %lld\n", puzzle1(fp));
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
printf("puzzle2: %lld\n", puzzle2(fp));
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
Loading…
Reference in New Issue