Compare commits
	
		
			2 Commits
		
	
	
		
			d035068982
			...
			55c8a15983
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						55c8a15983 | |
| 
							
							
								
								 | 
						3607e2e759 | 
| 
						 | 
				
			
			@ -22,6 +22,8 @@ set(AOC2024_PROJECTS
 | 
			
		|||
	day21
 | 
			
		||||
	day22
 | 
			
		||||
	day23
 | 
			
		||||
	day24
 | 
			
		||||
	day25
 | 
			
		||||
	full
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
set(SOURCES
 | 
			
		||||
	main.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(aoc2024_day24 ${SOURCES})
 | 
			
		||||
banan_link_library(aoc2024_day24 ban)
 | 
			
		||||
banan_link_library(aoc2024_day24 libc)
 | 
			
		||||
 | 
			
		||||
install(TARGETS aoc2024_day24 OPTIONAL)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,296 @@
 | 
			
		|||
#include <BAN/HashMap.h>
 | 
			
		||||
#include <BAN/HashSet.h>
 | 
			
		||||
#include <BAN/Sort.h>
 | 
			
		||||
#include <BAN/String.h>
 | 
			
		||||
#include <BAN/StringView.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
using i8  = int8_t;
 | 
			
		||||
using i16 = int16_t;
 | 
			
		||||
using i32 = int32_t;
 | 
			
		||||
using i64 = int64_t;
 | 
			
		||||
using isize = ssize_t;
 | 
			
		||||
 | 
			
		||||
using u8  = uint8_t;
 | 
			
		||||
using u16 = uint16_t;
 | 
			
		||||
using u32 = uint32_t;
 | 
			
		||||
using u64 = uint64_t;
 | 
			
		||||
using usize = size_t;
 | 
			
		||||
 | 
			
		||||
static u32 name_to_u32(BAN::StringView name)
 | 
			
		||||
{
 | 
			
		||||
	ASSERT(name.size() == 3);
 | 
			
		||||
	return ((u32)name[0] << 16) | ((u32)name[1] << 8) | ((u32)name[2] << 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BAN::String u32_to_name(u32 val)
 | 
			
		||||
{
 | 
			
		||||
	BAN::String result;
 | 
			
		||||
	MUST(result.push_back(val >> 16));
 | 
			
		||||
	MUST(result.push_back(val >>  8));
 | 
			
		||||
	MUST(result.push_back(val >>  0));
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Op
 | 
			
		||||
{
 | 
			
		||||
	enum { OR, AND, XOR } type { OR };
 | 
			
		||||
	u32 src1 { 0 };
 | 
			
		||||
	u32 src2 { 0 };
 | 
			
		||||
	u32 dst  { 0 };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ParseInputResult
 | 
			
		||||
{
 | 
			
		||||
	BAN::HashMap<u32, u8> wires;
 | 
			
		||||
	BAN::Vector<Op> ops;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ParseInputResult parse_input(FILE* fp)
 | 
			
		||||
{
 | 
			
		||||
	char buffer[128];
 | 
			
		||||
 | 
			
		||||
	BAN::HashMap<u32, u8> wires;
 | 
			
		||||
	while (fgets(buffer, sizeof(buffer), fp))
 | 
			
		||||
	{
 | 
			
		||||
		if (buffer[0] == '\n')
 | 
			
		||||
			break;
 | 
			
		||||
		MUST(wires.insert(name_to_u32(BAN::StringView(buffer).substring(0, 3)), buffer[5] - '0'));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::Vector<Op> ops;
 | 
			
		||||
	while (fgets(buffer, sizeof(buffer), fp))
 | 
			
		||||
	{
 | 
			
		||||
		auto parts = MUST(BAN::StringView(buffer).split([](char c) -> bool { return isspace(c); }));
 | 
			
		||||
		MUST(ops.emplace_back(
 | 
			
		||||
			(parts[1] == "OR"_sv) ? Op::OR : (parts[1] == "AND"_sv) ? Op::AND : Op::XOR,
 | 
			
		||||
			name_to_u32(parts[0]),
 | 
			
		||||
			name_to_u32(parts[2]),
 | 
			
		||||
			name_to_u32(parts[4])
 | 
			
		||||
		));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		.wires = BAN::move(wires),
 | 
			
		||||
		.ops = BAN::move(ops),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
i64 part1(FILE* fp)
 | 
			
		||||
{
 | 
			
		||||
	auto [wires, ops] = parse_input(fp);
 | 
			
		||||
 | 
			
		||||
	BAN::HashSet<u32> defined_wires;
 | 
			
		||||
	for (const auto [wire, _] : wires)
 | 
			
		||||
		MUST(defined_wires.insert(wire));
 | 
			
		||||
 | 
			
		||||
	usize ops_done_count = 0;
 | 
			
		||||
	BAN::Vector<u8> ops_done_map;
 | 
			
		||||
	MUST(ops_done_map.resize((ops.size() + 7) / 8, 0));
 | 
			
		||||
 | 
			
		||||
	while (ops_done_count < ops.size())
 | 
			
		||||
	{
 | 
			
		||||
		for (usize i = 0; i < ops.size(); i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (ops_done_map[i / 8] & (1 << (i % 8)))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (!defined_wires.contains(ops[i].src1) || !defined_wires.contains(ops[i].src2))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			const u8 src1 = wires[ops[i].src1];
 | 
			
		||||
			const u8 src2 = wires[ops[i].src2];
 | 
			
		||||
 | 
			
		||||
			u8 val = 0;
 | 
			
		||||
			switch (ops[i].type)
 | 
			
		||||
			{
 | 
			
		||||
				case Op::OR:  val = src1 | src2; break;
 | 
			
		||||
				case Op::AND: val = src1 & src2; break;
 | 
			
		||||
				case Op::XOR: val = src1 ^ src2; break;
 | 
			
		||||
			}
 | 
			
		||||
			MUST(wires.insert_or_assign(ops[i].dst, val));
 | 
			
		||||
 | 
			
		||||
			MUST(defined_wires.insert(ops[i].dst));
 | 
			
		||||
			ops_done_count++;
 | 
			
		||||
			ops_done_map[i / 8] |= (1 << (i % 8));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 result = 0;
 | 
			
		||||
	for (usize bit = 0; bit < 64; bit++)
 | 
			
		||||
	{
 | 
			
		||||
		char name[4];
 | 
			
		||||
		name[0] = 'z';
 | 
			
		||||
		name[1] = (bit / 10) + '0';
 | 
			
		||||
		name[2] = (bit % 10) + '0';
 | 
			
		||||
		name[3] = '\0';
 | 
			
		||||
 | 
			
		||||
		auto it = wires.find(name_to_u32(name));
 | 
			
		||||
		if (it == wires.end())
 | 
			
		||||
			break;
 | 
			
		||||
		result |= (u64)it->value << bit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool has_sources(Op op, u32 src1, u32 src2)
 | 
			
		||||
{
 | 
			
		||||
	if (op.src1 == src1 && op.src2 == src2)
 | 
			
		||||
		return true;
 | 
			
		||||
	if (op.src1 == src2 && op.src2 == src1)
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct u32Triplet
 | 
			
		||||
{
 | 
			
		||||
	u32 op_and, op_xor, op_or;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static u32Triplet find_ops(const BAN::Vector<Op>& ops, u32 src1, u32 src2)
 | 
			
		||||
{
 | 
			
		||||
	u32 op_and = 0, op_xor = 0, op_or = 0;
 | 
			
		||||
	for (auto op : ops)
 | 
			
		||||
	{
 | 
			
		||||
		if (!has_sources(op, src1, src2))
 | 
			
		||||
			continue;
 | 
			
		||||
		switch (op.type)
 | 
			
		||||
		{
 | 
			
		||||
			case Op::AND: op_and = op.dst; break;
 | 
			
		||||
			case Op::XOR: op_xor = op.dst; break;
 | 
			
		||||
			case Op::OR:  op_or  = op.dst; break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return { op_and, op_xor, op_or };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BAN::String part2(FILE* fp)
 | 
			
		||||
{
 | 
			
		||||
	auto [wires, ops] = parse_input(fp);
 | 
			
		||||
 | 
			
		||||
	BAN::Vector<u32> swapped;
 | 
			
		||||
 | 
			
		||||
	u32 carry = 0;
 | 
			
		||||
	for (usize bit = 0; bit < 64; bit++)
 | 
			
		||||
	{
 | 
			
		||||
		dprintln("bit {}", bit);
 | 
			
		||||
 | 
			
		||||
		char name[4];
 | 
			
		||||
		name[1] = (bit / 10) + '0';
 | 
			
		||||
		name[2] = (bit % 10) + '0';
 | 
			
		||||
		name[3] = '\0';
 | 
			
		||||
 | 
			
		||||
		name[0] = 'x';
 | 
			
		||||
		const u32 src1 = name_to_u32(name);
 | 
			
		||||
 | 
			
		||||
		name[0] = 'y';
 | 
			
		||||
		const u32 src2 = name_to_u32(name);
 | 
			
		||||
 | 
			
		||||
		name[0] = 'z';
 | 
			
		||||
		const u32 dst = name_to_u32(name);
 | 
			
		||||
 | 
			
		||||
		auto [src_and, src_xor, _1] = find_ops(ops, src1, src2);
 | 
			
		||||
		if (!src_and)
 | 
			
		||||
			break;
 | 
			
		||||
		ASSERT(src_xor);
 | 
			
		||||
 | 
			
		||||
		if (bit == 0)
 | 
			
		||||
		{
 | 
			
		||||
			carry = src_and;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto [dst_and, dst_xor, _2] = find_ops(ops, carry, src_xor);
 | 
			
		||||
		if (dst_xor == 0 && dst_and == 0)
 | 
			
		||||
		{
 | 
			
		||||
			dwarnln("swapped src xor, src and");
 | 
			
		||||
			MUST(swapped.push_back(src_xor));
 | 
			
		||||
			MUST(swapped.push_back(src_and));
 | 
			
		||||
			BAN::swap(src_xor, src_and);
 | 
			
		||||
 | 
			
		||||
			auto [tmp_and, tmp_xor, _] = find_ops(ops, carry, src_xor);
 | 
			
		||||
			dst_and = tmp_and;
 | 
			
		||||
			dst_xor = tmp_xor;
 | 
			
		||||
		}
 | 
			
		||||
		else if (dst_xor != dst)
 | 
			
		||||
		{
 | 
			
		||||
			if (dst_and == dst)
 | 
			
		||||
			{
 | 
			
		||||
				dwarnln("swapped dst xor, dst and");
 | 
			
		||||
				MUST(swapped.push_back(dst_xor));
 | 
			
		||||
				MUST(swapped.push_back(dst_and));
 | 
			
		||||
				BAN::swap(dst_xor, dst_and);
 | 
			
		||||
			}
 | 
			
		||||
			else if (src_and == dst)
 | 
			
		||||
			{
 | 
			
		||||
				dwarnln("swapped src and, dst xor");
 | 
			
		||||
				MUST(swapped.push_back(src_and));
 | 
			
		||||
				MUST(swapped.push_back(dst_xor));
 | 
			
		||||
				BAN::swap(src_and, dst_xor);
 | 
			
		||||
			}
 | 
			
		||||
			else if (src_xor == dst)
 | 
			
		||||
			{
 | 
			
		||||
				dwarnln("swapped src xor, dst xor");
 | 
			
		||||
				MUST(swapped.push_back(src_xor));
 | 
			
		||||
				MUST(swapped.push_back(dst_xor));
 | 
			
		||||
				BAN::swap(src_and, dst_xor);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				auto [_1, _2, tmp_or] = find_ops(ops, src_and, dst_and);
 | 
			
		||||
				if (tmp_or == dst)
 | 
			
		||||
				{
 | 
			
		||||
					dwarnln("swapped carry, dst xor");
 | 
			
		||||
					MUST(swapped.push_back(dst_xor));
 | 
			
		||||
					MUST(swapped.push_back(tmp_or));
 | 
			
		||||
					carry = dst_xor;
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					dwarnln("invalid ({}, {}, {})", u32_to_name(dst_xor), u32_to_name(dst_and), u32_to_name(dst));
 | 
			
		||||
					ASSERT_NOT_REACHED();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto [_3, _4, tmp_or] = find_ops(ops, src_and, dst_and);
 | 
			
		||||
		carry = tmp_or;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ASSERT(swapped.size() == 8);
 | 
			
		||||
	BAN::sort::sort(swapped.begin(), swapped.end());
 | 
			
		||||
 | 
			
		||||
	BAN::String result;
 | 
			
		||||
	for (const auto& swap : swapped)
 | 
			
		||||
		MUST(result.append(MUST(BAN::String::formatted("{},", u32_to_name(swap)))));
 | 
			
		||||
	result.pop_back();
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
	const char* file_path = "/usr/share/aoc2024/day24_input.txt";
 | 
			
		||||
 | 
			
		||||
	if (argc >= 2)
 | 
			
		||||
		file_path = argv[1];
 | 
			
		||||
 | 
			
		||||
	FILE* fp = fopen(file_path, "r");
 | 
			
		||||
	if (fp == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		perror("fopen");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("part1: %" PRId64 "\n", part1(fp));
 | 
			
		||||
 | 
			
		||||
	fseek(fp, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
	printf("part2: %s\n", part2(fp).data());
 | 
			
		||||
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
set(SOURCES
 | 
			
		||||
	main.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(aoc2024_day25 ${SOURCES})
 | 
			
		||||
banan_include_headers(aoc2024_day25 ban)
 | 
			
		||||
banan_link_library(aoc2024_day25 libc)
 | 
			
		||||
 | 
			
		||||
install(TARGETS aoc2024_day25 OPTIONAL)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
#include <BAN/Array.h>
 | 
			
		||||
#include <BAN/Vector.h>
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
using i8  = int8_t;
 | 
			
		||||
using i16 = int16_t;
 | 
			
		||||
using i32 = int32_t;
 | 
			
		||||
using i64 = int64_t;
 | 
			
		||||
using isize = ssize_t;
 | 
			
		||||
 | 
			
		||||
using u8  = uint8_t;
 | 
			
		||||
using u16 = uint16_t;
 | 
			
		||||
using u32 = uint32_t;
 | 
			
		||||
using u64 = uint64_t;
 | 
			
		||||
using usize = size_t;
 | 
			
		||||
 | 
			
		||||
i64 part1(FILE* fp)
 | 
			
		||||
{
 | 
			
		||||
	BAN::Vector<BAN::Array<u8, 5>> locks;
 | 
			
		||||
	BAN::Vector<BAN::Array<u8, 5>> keys;
 | 
			
		||||
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		char buffer[6*7 + 1];
 | 
			
		||||
		if (fread(buffer, 1, sizeof(buffer), fp) < sizeof(buffer) - 1)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		BAN::Array<u8, 5> obj;
 | 
			
		||||
		for (usize j = 1; j <= 5; j++)
 | 
			
		||||
			for (usize i = 0; i < 5; i++)
 | 
			
		||||
				obj[i] += (buffer[6 * j + i] == '#');
 | 
			
		||||
 | 
			
		||||
		MUST((buffer[0] == '#' ? locks : keys).push_back(obj));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u32 result = 0;
 | 
			
		||||
	for (auto lock : locks)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto key : keys)
 | 
			
		||||
		{
 | 
			
		||||
			bool valid = true;
 | 
			
		||||
			for (usize i = 0; i < 5 && valid; i++)
 | 
			
		||||
				valid = (key[i] + lock[i] <= 5);
 | 
			
		||||
			result += valid;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
i64 part2(FILE* fp)
 | 
			
		||||
{
 | 
			
		||||
	(void)fp;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
	const char* file_path = "/usr/share/aoc2024/day25_input.txt";
 | 
			
		||||
 | 
			
		||||
	if (argc >= 2)
 | 
			
		||||
		file_path = argv[1];
 | 
			
		||||
 | 
			
		||||
	FILE* fp = fopen(file_path, "r");
 | 
			
		||||
	if (fp == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		perror("fopen");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("part1: %" PRId64 "\n", part1(fp));
 | 
			
		||||
 | 
			
		||||
	fseek(fp, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
	printf("part2: %" PRId64 "\n", part2(fp));
 | 
			
		||||
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue