Files
aoc/sol/24/day17.cpp
2025-12-02 09:13:23 +01:00

248 lines
6.8 KiB
C++

#include <vector>
#include <ranges>
#include <numeric>
#include <memory>
#include <iostream>
#include "aoc.hpp"
#include "aoc/utils.hpp"
#include "fmt/format.h"
#include "stb_image_write.h"
#include "ctre.hpp"
using namespace aoc::types;
using namespace std::string_view_literals;
namespace uec {
enum class opcode : u16 {
adv = 0,
bxl = 1,
bst = 2,
jnz = 3,
bxc = 4,
out = 5,
bdv = 6,
cdv = 7,
nop = 8,
};
auto opcode_to_str(opcode op) -> char const* {
switch (op) {
case opcode::adv: return "adv";
case opcode::bxl: return "bxl";
case opcode::bst: return "bst";
case opcode::jnz: return "jnz";
case opcode::bxc: return "bxc";
case opcode::out: return "out";
case opcode::bdv: return "bdv";
case opcode::cdv: return "cdv";
default: return "nop";
}
}
struct regs {
u64 a;
u64 b;
u64 c;
};
class cpu {
public:
cpu() {}
auto load(uec::regs const& regs, std::vector<opcode> const& program) -> void {
m_regs = regs;
m_program = program;
}
auto registers() const -> uec::regs const& { return m_regs; }
auto program() const -> std::vector<opcode> const& { return m_program; }
auto clock() -> void {
auto const op = read();
if (peek() == opcode::nop) return;
auto const x = combo();
switch (op) {
case opcode::adv: adv(x); break;
case opcode::bxl: bxl(); break;
case opcode::bst: bst(x); break;
case opcode::jnz: jnz(); break;
case opcode::bxc: bxc(); break;
case opcode::out: out(x); break;
case opcode::bdv: bdv(x); break;
case opcode::cdv: cdv(x); break;
default: nop(); break;
}
m_pc += 2;
}
auto halt() const -> bool {
return peek() == opcode::nop;
}
auto str() const -> std::string {
using namespace std::string_literals;
std::string str{};
str += "PC: " + fmt::format("{:#04x}", m_pc) + "\n";
str += "\n";
str += "A: " + fmt::format("{:#010x}", m_regs.a) + "\n";
str += "B: " + fmt::format("{:#010x}", m_regs.b) + "\n";
str += "C: " + fmt::format("{:#010x}", m_regs.c) + "\n";
str += "\n";
for (usize i = 0; i < m_program.size(); ++i) {
str += fmt::format("{:02x}: ", i);
str += fmt::format("{}{}\n", opcode_to_str(m_program[i]), m_pc == i ? " <" : "");
}
str += "\n";
str += " ";
for (usize i = 0; i < 16; ++i) {
str += fmt::format("{:02X}", i);
if (i == 7) str += " ";
else str += " ";
}
str += "\n\n";
for (usize i = 0; i < m_output.size() * sizeof(u64); ++i) {
if (i % 16 == 0) str += fmt::format("{:04X}: ", i);
str += fmt::format("{:02X}", *(reinterpret_cast<u8*>(const_cast<u64*>(m_output.data())) + i));
if ((i + 1) % 16 == 0) str += "\n";
else if ((i + 1) % 8 == 0) str += " ";
else str += " ";
}
if (m_output.size() != 0)
str += "\n";
str += "-------------------------------------------------------\n";
return str;
}
auto output_str() const -> std::string {
std::string str{};
for (usize i = 0; i < m_output.size(); ++i) {
str += fmt::format("{}", m_output.at(i));
if (i < m_output.size() - 1) str += ",";
}
return str;
}
auto output() const -> std::vector<u64> const& { return m_output; }
private:
auto read() const -> opcode {
if (m_pc < m_program.size())
return m_program[m_pc];
return opcode::nop;
}
auto peek() const -> opcode {
if (m_pc + 1 < m_program.size())
return m_program[m_pc + 1];
return opcode::nop;
}
auto combo() const -> u64 {
auto const op = u64(peek());
if (op < 4) return op;
else if (op == 4) return m_regs.a;
else if (op == 5) return m_regs.b;
else if (op == 6) return m_regs.c;
else return u64(opcode::nop);
}
private:
auto adv(u64 x) -> void {
m_regs.a >>= x;
}
auto bxl() -> void {
m_regs.b = m_regs.b ^ u64(peek());
}
auto bst(u64 x) -> void {
m_regs.b = x % 8;
}
auto jnz() -> void {
if (m_regs.a == 0)
return;
m_pc = u64(peek()) - 2;
}
auto bxc() -> void {
m_regs.b = m_regs.b ^ m_regs.c;
}
auto out(u64 x) -> void {
m_output.emplace_back(x % 8);
}
auto bdv(u64 x) -> void {
m_regs.b = m_regs.a >> x;
}
auto cdv(u64 x) -> void {
m_regs.c = m_regs.a >> x;
}
auto nop() -> void {}
private:
u64 m_pc{};
uec::regs m_regs{};
std::vector<opcode> m_program{};
std::vector<u64> m_output{};
};
}
auto part_a(uec::regs const& regs, std::vector<uec::opcode> const& program) -> void {
uec::cpu cpu{};
cpu.load(regs, program);
while (!cpu.halt()) {
// fmt::print("{}", cpu.str());
// std::cin.get();
cpu.clock();
}
fmt::print("{}", cpu.str());
fmt::print("{}\n", cpu.output_str());
}
auto aoc24::day17([[maybe_unused]]std::span<char const*> const& args) -> std::expected<void, aoc::error> {
// auto res = aoc::read_text("./dat/24/ex/17a.txt");
auto res = aoc::read_text("./dat/24/ex/17b.txt");
// auto res = aoc::read_text("./dat/24/re/17.txt");
if (!res) return std::unexpected(res.error());
auto const txt = *res;
auto const lines = txt | aoc::split("\n"sv)
| aoc::map_to_sv
| aoc::filter_non_empty
| std::ranges::to<std::vector>();
uec::regs regs{};
std::vector<uec::opcode> program{};
for (auto const& line : lines) {
if (auto reg = ctre::match<"Register ([A-Z]): ([0-9]+)">(line)) {
if (reg.get<1>().view() == "A"sv) {
regs.a = std::stoul(reg.get<2>().to_string());
} else if (reg.get<1>().view() == "A"sv) {
regs.b = std::stoul(reg.get<2>().to_string());
} else if (reg.get<1>().view() == "A"sv) {
regs.c = std::stoul(reg.get<2>().to_string());
} else {
// fmt::print("unknown register\n");
}
continue;
}
if (auto prog = ctre::match<"Program: ([0-9,]+)">(line)) {
program = prog.get<1>().to_string() | aoc::split(","sv)
| aoc::map_to_str
| aoc::filter_non_empty
| std::views::transform([](auto const& str) {
return uec::opcode(std::stoul(str));
}) | std::ranges::to<std::vector>();
}
}
// part_a(regs, program);
uec::cpu cpu{};
cpu.load({}, program);
return {};
}