Skip to content

Commit 997b2a4

Browse files
day 17 finished (#35)
1 parent 5da85e8 commit 997b2a4

25 files changed

+1894
-1
lines changed

LICENSE

+674
Large diffs are not rendered by default.

day_16/benchmark/CMakeLists.txt

-1
This file was deleted.

day_17/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
aoc_day(17)

day_17/lib/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
aoc_add_library()

day_17/lib/chunk.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "chunk.hpp"
2+
3+
namespace aoc::day_17 {
4+
5+
auto to_combo_operand_literal(int64_t & operand) -> std::string {
6+
switch (operand) {
7+
case 4:
8+
return "a";
9+
case 5:
10+
return "b";
11+
case 6:
12+
return "c";
13+
case 7:
14+
throw std::runtime_error("Invalid operand");
15+
default:
16+
return std::to_string(operand);
17+
}
18+
}
19+
20+
auto chunk::to_string() const -> std::string {
21+
size_t memory_address = 0;
22+
std::string result;
23+
for (size_t i = 0; i < m_data.size(); i += 2) {
24+
auto opcode = m_data[i];
25+
switch (opcode) {
26+
case opcode::ADV:
27+
case opcode::BST:
28+
case opcode::BDV:
29+
case opcode::CDV:
30+
{
31+
auto operand = static_cast<int64_t>(m_data[i + 1]);
32+
result += std::format("{:03}: {} {}\n", memory_address, ::aoc::day_17::to_string(opcode),
33+
to_combo_operand_literal(operand));
34+
break;
35+
}
36+
}
37+
}
38+
return result;
39+
}
40+
41+
auto chunk::operator[](size_t index) const -> opcode const & {
42+
return m_data[index];
43+
}
44+
45+
auto chunk::size() const -> size_t {
46+
return m_data.size();
47+
}
48+
49+
} // namespace aoc::day_17

day_17/lib/chunk.hpp

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
#include <format>
4+
#include <string>
5+
#include <vector>
6+
7+
#include "opcode.hpp"
8+
9+
namespace aoc::day_17 {
10+
11+
/// @brief Converts an operand to a literal string representation
12+
/// @param operand The operand to convert
13+
/// @return String representation of the operand
14+
[[nodiscard]] auto to_combo_operand_literal(int64_t & operand) -> std::string;
15+
16+
/// @brief Represents a sequence of opcodes forming a program chunk
17+
struct chunk {
18+
std::vector<opcode> m_data; ///< The sequence of opcodes
19+
20+
/// @brief Converts the chunk to a string representation
21+
/// @return String representation of the chunk
22+
auto to_string() const -> std::string;
23+
24+
/// @brief Access operator for opcodes in the chunk
25+
/// @param index The index of the opcode to access
26+
/// @return Reference to the opcode at the given index
27+
[[nodiscard]] auto operator[](size_t index) const -> opcode const &;
28+
29+
/// @brief Gets the size of the chunk
30+
/// @return Number of opcodes in the chunk
31+
[[nodiscard]] auto size() const -> size_t;
32+
};
33+
34+
} // namespace aoc::day_17

day_17/lib/generated.hpp

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#pragma once
2+
3+
/**
4+
* @file generated.hpp
5+
* @brief Code generated by the transpiler
6+
*/
7+
8+
#include <cmath>
9+
#include <cstdint>
10+
#include <iostream>
11+
#include <vector>
12+
13+
namespace aoc::day_17 {
14+
15+
void run_using_best_lang() {
16+
bool first_out = true;
17+
int64_t a = 62769524;
18+
int64_t b = 0;
19+
int64_t c = 0;
20+
_0:
21+
b = a % 8;
22+
_10:
23+
b ^= 7;
24+
_20:
25+
c = a / std::pow(2, b);
26+
_30:
27+
a = a / 8;
28+
_40:
29+
b ^= c;
30+
_50:
31+
b ^= 7;
32+
_60:
33+
if (first_out) {
34+
std::cout << b % 8;
35+
first_out = false;
36+
} else {
37+
std::cout << "," << b % 8;
38+
}
39+
_70:
40+
if (a != 0) {
41+
goto _0;
42+
}
43+
if (!first_out) {
44+
std::cout << std::endl;
45+
}
46+
return;
47+
}
48+
49+
[[nodiscard]] auto calculate_byte(int64_t a) -> int64_t {
50+
auto b = a % 8;
51+
b ^= 7;
52+
auto c = a / static_cast<int64_t>(std::pow(2, b));
53+
b ^= c;
54+
b ^= 7;
55+
return b % 8;
56+
}
57+
58+
[[nodiscard]] auto find_register_value(int64_t desired_register_value, std::vector<int64_t> const & goals,
59+
int currentIndex) -> int64_t {
60+
for (int i = 0; i < 8; i++) {
61+
if (calculate_byte(desired_register_value + i) == goals[currentIndex]) {
62+
if (currentIndex + 1 == goals.size()) {
63+
return desired_register_value + 3;
64+
}
65+
int64_t result = find_register_value((desired_register_value + i) << 3, goals, currentIndex + 1);
66+
if (result != -1) {
67+
return result;
68+
}
69+
}
70+
}
71+
return -1;
72+
}
73+
74+
} // namespace aoc::day_17

day_17/lib/opcode.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include "opcode.hpp"
2+
3+
namespace aoc::day_17 {
4+
5+
auto to_string(opcode const & op) -> std::string {
6+
switch (op) {
7+
case opcode::ADV:
8+
return "ADV";
9+
case opcode::BXL:
10+
return "BXL";
11+
case opcode::BST:
12+
return "BST";
13+
case opcode::JNZ:
14+
return "JNZ";
15+
case opcode::BXC:
16+
return "BXC";
17+
case opcode::OUT:
18+
return "OUT";
19+
case opcode::BDV:
20+
return "BDV";
21+
case opcode::CDV:
22+
return "CDV";
23+
}
24+
return "UNKNOWN";
25+
}
26+
27+
} // namespace aoc::day_17

day_17/lib/opcode.hpp

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
namespace aoc::day_17 {
6+
7+
/// @brief Operation codes for the virtual machine instructions
8+
enum class opcode {
9+
ADV = 0, ///< Advance register A by division
10+
BXL = 1, ///< XOR operation on register B with literal
11+
BST = 2, ///< Store value in register B
12+
JNZ = 3, ///< Jump if not zero
13+
BXC = 4, ///< XOR operation between registers B and C
14+
OUT = 5, ///< Output operation
15+
BDV = 6, ///< Divide A and store in B
16+
CDV = 7, ///< Divide A and store in C
17+
};
18+
19+
/// @brief Convert opcode to string representation
20+
/// @param op The opcode to convert
21+
/// @return String representation of the opcode
22+
auto to_string(opcode const & op) -> std::string;
23+
24+
} // namespace aoc::day_17

day_17/lib/parser.cpp

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include "parser.hpp"
2+
3+
namespace aoc::day_17 {
4+
5+
[[nodiscard]] static auto parse_opcode(std::string_view input) -> std::expected<std::vector<opcode>, std::error_code>;
6+
7+
[[nodiscard]] static auto parse_registers(std::string_view input)
8+
-> std::expected<std::array<int64_t, 3>, std::error_code>;
9+
10+
[[nodiscard]] auto parse_program(std::string_view input) -> std::expected<program, std::error_code> {
11+
constexpr auto delimiters = std::array{"\r\n\r\n", "\n\n"};
12+
auto split_pos =
13+
std::ranges::find_if(delimiters, [&](auto delim) { return input.find(delim) != std::string_view::npos; });
14+
15+
if (split_pos == delimiters.end()) {
16+
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
17+
}
18+
19+
auto const split_at = input.find(*split_pos);
20+
auto registerInit = input.substr(0, split_at);
21+
auto bytecode = input.substr(split_at);
22+
23+
auto registers = parse_registers(registerInit);
24+
if (!registers) {
25+
return std::unexpected(registers.error());
26+
}
27+
28+
auto opcodes = parse_opcode(bytecode);
29+
if (!opcodes) {
30+
return std::unexpected(opcodes.error());
31+
}
32+
33+
return program{.m_chunk = opcodes.value(),
34+
.register_a = registers.value()[0],
35+
.register_b = registers.value()[1],
36+
.register_c = registers.value()[2]};
37+
}
38+
39+
[[nodiscard]] static auto parse_opcode(std::string_view input) -> std::expected<std::vector<opcode>, std::error_code> {
40+
auto opcode = input.substr(0, input.find(':'));
41+
auto numbers = input.substr(input.find(':') + 1);
42+
43+
return numbers | std::views::split(',') | std::views::transform([](auto const & number) {
44+
auto result = aoc::parser::rules::parse_number<int64_t>(std::string_view{number});
45+
if (!result) {
46+
throw std::invalid_argument("Failed to parse number");
47+
}
48+
return static_cast<aoc::day_17::opcode>(*result);
49+
}) |
50+
aoc::ranges::to<std::vector<aoc::day_17::opcode>>;
51+
}
52+
53+
[[nodiscard]] static auto parse_registers(std::string_view input)
54+
-> std::expected<std::array<int64_t, 3>, std::error_code> {
55+
std::array<int64_t, 3> registers;
56+
57+
auto lines = input | std::views::split('\n') | std::views::transform([](auto && line) {
58+
auto line_str = std::string_view{line};
59+
if (line_str.back() == '\r') {
60+
line_str = line_str.substr(0, line_str.size() - 1);
61+
}
62+
return line_str;
63+
}) |
64+
aoc::ranges::to<std::vector<std::string_view>>;
65+
66+
if (lines.size() != 3) {
67+
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
68+
}
69+
70+
auto parsed_RegisterA = aoc::parser::rules::parse_number<int64_t>(lines[0].substr(lines[0].find(':') + 1));
71+
if (!parsed_RegisterA) {
72+
return std::unexpected(parsed_RegisterA.error());
73+
}
74+
auto parsed_RegisterB = aoc::parser::rules::parse_number<int64_t>(lines[1].substr(lines[1].find(':') + 1));
75+
if (!parsed_RegisterB) {
76+
return std::unexpected(parsed_RegisterB.error());
77+
}
78+
79+
auto parsed_RegisterC = aoc::parser::rules::parse_number<int64_t>(lines[2].substr(lines[2].find(':') + 1));
80+
if (!parsed_RegisterC) {
81+
return std::unexpected(parsed_RegisterC.error());
82+
}
83+
84+
registers[0] = parsed_RegisterA.value();
85+
registers[1] = parsed_RegisterB.value();
86+
registers[2] = parsed_RegisterC.value();
87+
88+
return registers;
89+
}
90+
91+
} // namespace aoc::day_17

day_17/lib/parser.hpp

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
3+
#include <expected>
4+
#include <ranges>
5+
#include <stdexcept>
6+
#include <string>
7+
#include <string_view>
8+
#include <system_error>
9+
#include <vector>
10+
11+
#include "../../shared/src/parsing_rules.hpp"
12+
#include "../../shared/src/ranges_compatibility_layer.hpp"
13+
#include "program.hpp"
14+
15+
16+
namespace aoc::day_17 {
17+
18+
/// @brief Parses input text into a program structure
19+
/// @param input String view containing the program text
20+
/// @return Expected containing either the parsed program or an error code
21+
/// @throws std::invalid_argument If the opcode format is invalid
22+
/// @details The input format should be:
23+
/// RegisterA: <value>
24+
/// RegisterB: <value>
25+
/// RegisterC: <value>
26+
///
27+
/// Opcode:<value1>,<value2>,...
28+
[[nodiscard]] auto parse_program(std::string_view input) -> std::expected<program, std::error_code>;
29+
30+
} // namespace aoc::day_17

day_17/lib/program.hpp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include <expected>
4+
#include <string>
5+
#include <system_error>
6+
#include <vector>
7+
8+
#include "chunk.hpp"
9+
#include "opcode.hpp"
10+
11+
namespace aoc::day_17 {
12+
13+
/// @brief Represents a complete program for the virtual machine
14+
/// @details Contains the program bytecode and initial register values
15+
struct program {
16+
chunk m_chunk; ///< The program's bytecode
17+
int64_t register_a; ///< Initial value for register A
18+
int64_t register_b; ///< Initial value for register B
19+
int64_t register_c; ///< Initial value for register C
20+
};
21+
22+
} // namespace aoc::day_17

0 commit comments

Comments
 (0)