Skip to content

Commit

Permalink
2024 day 16: solve part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
yut23 committed Dec 16, 2024
1 parent 186754c commit 096e645
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 0 deletions.
2 changes: 2 additions & 0 deletions 2024/answer_tests/day16/example1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Day 16:
7036
2 changes: 2 additions & 0 deletions 2024/answer_tests/day16/example2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Day 16:
11048
2 changes: 2 additions & 0 deletions 2024/answer_tests/day16/example3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Day 16:
12
15 changes: 15 additions & 0 deletions 2024/input/day16/example1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############
17 changes: 17 additions & 0 deletions 2024/input/day16/example2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################
3 changes: 3 additions & 0 deletions 2024/input/day16/example3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
###############
#S...........E#
###############
25 changes: 25 additions & 0 deletions 2024/src/day16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/******************************************************************************
* File: day16.cpp
*
* Author: Eric T. Johnson (yut23)
* Created: 2024-12-16
*****************************************************************************/

#include "day16.hpp"
#include "lib.hpp"
#include <fstream> // for ifstream
#include <iostream> // for cout

int main(int argc, char **argv) {
auto args = aoc::parse_args(argc, argv);

auto maze = aoc::day16::Maze::read(args.infile);

int distance = maze.find_shortest_path();
std::cout << distance << "\n";
if (args.input_type == aoc::InputType::MAIN) {
assert(distance < 76404);
}

return 0;
}
170 changes: 170 additions & 0 deletions 2024/src/day16.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/******************************************************************************
* File: day16.hpp
*
* Author: Eric T. Johnson (yut23)
* Created: 2024-12-16
*****************************************************************************/

// based on 2023 day 17 (../../2023/src/day17.hpp)

#ifndef DAY16_HPP_WM3C4GBV
#define DAY16_HPP_WM3C4GBV

#include "ds/grid.hpp" // for Grid
#include "graph_traversal.hpp" // for dijkstra
#include "lib.hpp" // for Pos, Delta, AbsDirection, RelDirection, DEBUG, read_lines
#include "util/hash.hpp" // for make_hash

#include <cassert> // for assert
#include <compare> // for strong_ordering
#include <cstddef> // for size_t
#include <functional> // for bind_front, hash
#include <iostream> // for istream, ostream, cerr
#include <string> // for string
#include <unordered_map> // for unordered_map
#include <utility> // for move
#include <vector> // for vector

namespace aoc::day16 {

class Maze {
public:
struct Key {
Pos pos;
AbsDirection dir;

std::strong_ordering operator<=>(const Key &) const = default;
};

private:
aoc::ds::Grid<char> grid;
Pos start_pos;
Pos end_pos;

void process_neighbors(const Key &key, auto &&visit) const;
int get_distance(const Key &from, const Key &to) const;

public:
int find_shortest_path() const;
void print(std::ostream &, const std::vector<Key> &path = {}) const;

explicit Maze(std::vector<std::string> &&grid_);
static Maze read(std::istream &is) { return Maze{aoc::read_lines(is)}; }
};

std::ostream &operator<<(std::ostream &os, const Maze::Key &key) {
os << "{" << key.pos << ", " << key.dir << "}";
return os;
}

void Maze::process_neighbors(const Key &key, auto &&visit) const {
// try moving straight
Key neighbor{key.pos + Delta(key.dir, true), key.dir};
if (grid.in_bounds(neighbor.pos) && grid[neighbor.pos] != '#') {
visit(neighbor);
}
// try turning
visit(Key{key.pos, directions::turn(key.dir, RelDirection::left)});
visit(Key{key.pos, directions::turn(key.dir, RelDirection::right)});
}

int Maze::get_distance(const Key &from, const Key &to) const {
if (from.dir == to.dir) {
// moved forward
return 1;
} else {
// turned 90 degrees
return 1000;
}
}

int Maze::find_shortest_path() const {
const auto is_target = [&end_pos = end_pos](const Key &key) -> bool {
return key.pos == end_pos;
};

const auto &[distance, path] = aoc::graph::dijkstra(
Key{start_pos, AbsDirection::east},
[this](const Key &key, auto &&visit_neighbor) {
this->process_neighbors(key, visit_neighbor);
},
std::bind_front(&Maze::get_distance, this), is_target);

if constexpr (aoc::DEBUG) {
if (distance >= 0) {
std::cerr << "found path with distance " << distance << ", length "
<< path.size() << ":\n";
} else {
std::cerr << "no path found from " << start_pos << " to " << end_pos
<< "\n";
}
for (const Key &key : path) {
std::cerr << " " << key << "\n";
}
std::cerr << "\n";
print(std::cerr, path);
std::cerr << "\n";
}

return distance;
}

void Maze::print(std::ostream &os, const std::vector<Key> &path) const {
std::unordered_map<Pos, const Key *> path_lookup;
for (const Key &key : path) {
path_lookup[key.pos] = &key;
}

Pos pos;
for (pos.y = 0; pos.y < grid.height; ++pos.y) {
for (pos.x = 0; pos.x < grid.width; ++pos.x) {
auto it = path_lookup.find(pos);
if (it != path_lookup.end()) {
switch (it->second->dir) {
case AbsDirection::north:
os << '^';
break;
case AbsDirection::east:
os << '>';
break;
case AbsDirection::south:
os << 'v';
break;
case AbsDirection::west:
os << '<';
break;
}
} else {
os << grid[pos];
}
}
os << '\n';
}
}

Maze::Maze(std::vector<std::string> &&grid_)
: grid(std::move(grid_)), start_pos(-1, -1), end_pos(-1, -1) {
grid.for_each_with_pos([this](const Pos &pos, char value) {
if (value == 'S') {
this->start_pos = pos;
} else if (value == 'E') {
this->end_pos = pos;
}
});
assert(start_pos.x != -1 && start_pos.y != -1);
assert(end_pos.x != -1 && end_pos.y != -1);
}

} // namespace aoc::day16

template <>
struct std::hash<aoc::day16::Maze::Key> {
std::size_t operator()(const aoc::day16::Maze::Key &key) const noexcept {
// random number (hexdump -n8 -e '"0x" 8/1 "%02x" "ull\n"'</dev/urandom)
std::size_t seed = 0x2b139d1412006c1dull;
util::make_hash(seed, key.pos.x, key.pos.y, key.dir);
return seed;
}
};

#endif /* end of include guard: DAY16_HPP_WM3C4GBV */

0 comments on commit 096e645

Please sign in to comment.