diff --git a/2016/01.cpp b/2016/01.cpp new file mode 100644 index 0000000..93f4773 --- /dev/null +++ b/2016/01.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace { + +namespace phx = boost::phoenix; +namespace qi = boost::spirit::qi; + +enum class Dir { L, R }; + +struct Command { + Dir dir; + std::size_t n; +}; + +using Input = std::vector; + +// Input file grammar +class Grammar : public qi::grammar { + qi::rule dir; + qi::rule command; + qi::rule commands; + +public: + Grammar() : Grammar::base_type{commands} { + using namespace qi::labels; + + dir = + qi::string("L") [ _val = Dir::L ] | + qi::string("R") [ _val = Dir::R ]; + command = qi::lexeme[ + dir [ phx::bind(&Command::dir, _val) = _1] >> + qi::ulong_long [ phx::bind(&Command::n, _val) = _1] + ]; + commands = command % ","; + } +}; + +auto Part1(Input const& input) -> std::int64_t +{ + auto here = aocpp::Coord{}; + auto face = aocpp::Coord{0,1}; + + for (auto const& command : input) { + switch (command.dir) { + case Dir::R: face = aocpp::CW(face); break; + case Dir::L: face = aocpp::CCW(face); break; + } + here += command.n * face; + } + + return aocpp::Norm1(here); +} + +auto Part2(Input const& input) -> std::int64_t +{ + auto seen = std::unordered_set{aocpp::Coord{0,0}}; + auto here = aocpp::Coord{0,0}; + auto face = aocpp::Coord{0,1}; + + for (auto const& command : input) { + switch (command.dir) { + case Dir::R: face = aocpp::CW(face); break; + case Dir::L: face = aocpp::CCW(face); break; + } + for (auto const _ : boost::irange(command.n)) { + here += face; + if (!seen.insert(here).second) { + return aocpp::Norm1(here); + } + } + } + + throw std::runtime_error{"no solution to part 2"}; +} + +} // namespace + +TEST_SUITE("2016-01 examples") { + TEST_CASE("example 1") { + auto in = std::istringstream{"R2, L3"}; + auto const commands = aocpp::ParseGrammar_(Grammar{}, in); + CHECK(5 == Part1(commands)); + } + TEST_CASE("example 2") { + auto in = std::istringstream{"R2, R2, R2"}; + auto const commands = aocpp::ParseGrammar_(Grammar{}, in); + CHECK(2 == Part1(commands)); + } + TEST_CASE("example 3") { + auto in = std::istringstream{"R5, L5, R5, R3"}; + auto const commands = aocpp::ParseGrammar_(Grammar{}, in); + CHECK(12 == Part1(commands)); + } + TEST_CASE("example 4") { + auto in = std::istringstream{"R8, R4, R4, R8"}; + auto const commands = aocpp::ParseGrammar_(Grammar{}, in); + CHECK(4 == Part2(commands)); + } +} + +auto Main(std::istream & in, std::ostream & out) -> void +{ + auto const input = aocpp::ParseGrammar_(Grammar{}, in); + out << "Part 1: " << Part1(input) << std::endl; + out << "Part 2: " << Part2(input) << std::endl; +} diff --git a/2016/CMakeLists.txt b/2016/CMakeLists.txt new file mode 100644 index 0000000..a0c635a --- /dev/null +++ b/2016/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(2016_01 01.cpp) +target_link_libraries(2016_01 aocpp Boost::headers) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6212a40..a86abe5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory(dlx) add_subdirectory(knothash) add_subdirectory(zmod) add_subdirectory(intcode) +add_subdirectory(2016) add_subdirectory(2017) add_subdirectory(2018) add_subdirectory(2019) diff --git a/lib/include/aocpp/Coord.hpp b/lib/include/aocpp/Coord.hpp index 8782f3d..415668e 100644 --- a/lib/include/aocpp/Coord.hpp +++ b/lib/include/aocpp/Coord.hpp @@ -66,6 +66,15 @@ auto operator-=(Coord &, Coord) -> Coord &; /// Write a coordinate to a string "(x,y)" auto operator<<(std::ostream &, Coord) -> std::ostream &; +/// Scale a coordinate +auto operator*(std::int64_t, Coord) -> Coord; + +/// Scale a coordinate +auto operator*(Coord, std::int64_t) -> Coord; + +/// Scale a coordinate +auto operator*=(Coord &, std::int64_t) -> Coord &; + } // namespace template<> diff --git a/lib/include/aocpp/Parsing.hpp b/lib/include/aocpp/Parsing.hpp index 80d3188..223665a 100644 --- a/lib/include/aocpp/Parsing.hpp +++ b/lib/include/aocpp/Parsing.hpp @@ -30,6 +30,22 @@ auto ParseGrammar(G const& grammar, std::istream & in) -> typename G::start_type return result; } +template +auto ParseGrammar_(G const& grammar, std::istream & in) -> typename G::start_type::attr_type +{ + namespace qi = boost::spirit::qi; + auto result = typename G::start_type::attr_type {}; + auto const content = std::string{std::istreambuf_iterator{in}, {}}; + auto b = content.begin(); // updated on successful parse + auto const e = content.end(); + + if (!qi::phrase_parse(b, e, grammar, qi::ascii::space, result) || b != e) { + throw std::runtime_error{"Bad input file: " + content}; + } + + return result; +} + } #endif \ No newline at end of file diff --git a/lib/src/Coord.cpp b/lib/src/Coord.cpp index 33a4027..0b3a863 100644 --- a/lib/src/Coord.cpp +++ b/lib/src/Coord.cpp @@ -95,6 +95,22 @@ auto operator-=(Coord & a, Coord b) -> Coord & { return a; } +auto operator*(Coord a, std::int64_t b) -> Coord { + return a *= b; +} + +auto operator*(std::int64_t a, Coord b) -> Coord { + b.x *= a; + b.y *= a; + return b; +} + +auto operator*=(Coord & a, std::int64_t b) -> Coord & { + a.x *= b; + a.y *= b; + return a; +} + auto operator<<(std::ostream & out, Coord c) -> std::ostream & { return out << "(" << c.x << "," << c.y << ")"; }