forked from Bananymous/banan-os
				
			
		
			
				
	
	
		
			192 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include <BAN/HashSet.h>
 | |
| #include <BAN/Sort.h>
 | |
| #include <BAN/Vector.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 u8  = uint8_t;
 | |
| using u16 = uint16_t;
 | |
| using u32 = uint32_t;
 | |
| using u64 = uint64_t;
 | |
| 
 | |
| struct Position
 | |
| {
 | |
| 	i64 x, y, z;
 | |
| 
 | |
| 	bool operator==(const Position& other) const
 | |
| 	{
 | |
| 		return x == other.x && y == other.y && z == other.z;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static constexpr bool rectangle_contains(const Position& c1, const Position& c2, const Position& p)
 | |
| {
 | |
| 	ASSERT(c1.x <= c2.x);
 | |
| 	ASSERT(c1.y <= c2.y);
 | |
| 	return (c1.x <= p.x && p.x <= c2.x) && (c1.y <= p.y && p.y <= c2.y);
 | |
| }
 | |
| 
 | |
| struct Brick
 | |
| {
 | |
| 	Position corners[2];
 | |
| 	BAN::HashSet<Brick*> supporting;
 | |
| 	BAN::HashSet<Brick*> supported_by;
 | |
| 
 | |
| 	bool supports(const Brick& other) const
 | |
| 	{
 | |
| 		if (corners[1].z + 1 != other.corners[0].z)
 | |
| 			return false;
 | |
| 
 | |
| 		for (i32 i = 0; i < 4; i++)
 | |
| 			if (rectangle_contains(corners[0], corners[1], { other.corners[i / 2].x, other.corners[i % 2].y, 0 }))
 | |
| 				return true;
 | |
| 
 | |
| 		for (i32 i = 0; i < 4; i++)
 | |
| 			if (rectangle_contains(other.corners[0], other.corners[1], { corners[i / 2].x, corners[i % 2].y, 0 }))
 | |
| 				return true;
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| i64 parse_i64(BAN::StringView str)
 | |
| {
 | |
| 	i64 result = 0;
 | |
| 	for (char c : str)
 | |
| 	{
 | |
| 		ASSERT(isdigit(c));
 | |
| 		result = (result * 10) + (c - '0');
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| BAN::Vector<Brick> parse_bricks(FILE* fp)
 | |
| {
 | |
| 	BAN::Vector<Brick> bricks;
 | |
| 
 | |
| 	char buffer[64];
 | |
| 	while (fgets(buffer, sizeof(buffer), fp))
 | |
| 	{
 | |
| 		BAN::StringView line(buffer);
 | |
| 		ASSERT(line.back() == '\n');
 | |
| 		line = line.substring(0, line.size() - 1);
 | |
| 		if (line.empty())
 | |
| 			break;
 | |
| 
 | |
| 		auto corner_strs = MUST(line.split('~'));
 | |
| 		ASSERT(corner_strs.size() == 2);
 | |
| 
 | |
| 		Brick brick;
 | |
| 		for (i32 i = 0; i < 2; i++)
 | |
| 		{
 | |
| 			auto coords = MUST(corner_strs[i].split(','));
 | |
| 			ASSERT(coords.size() == 3);
 | |
| 			brick.corners[i].x = parse_i64(coords[0]);
 | |
| 			brick.corners[i].y = parse_i64(coords[1]);
 | |
| 			brick.corners[i].z = parse_i64(coords[2]);
 | |
| 		}
 | |
| 		ASSERT(brick.corners[0].x <= brick.corners[1].x);
 | |
| 		ASSERT(brick.corners[0].y <= brick.corners[1].y);
 | |
| 		ASSERT(brick.corners[0].z <= brick.corners[1].z);
 | |
| 		MUST(bricks.push_back(brick));
 | |
| 	}
 | |
| 
 | |
| 	return bricks;
 | |
| }
 | |
| 
 | |
| i64 puzzle1(FILE* fp)
 | |
| {
 | |
| 	auto brick_comp = [](const Brick& b1, const Brick& b2) { return b1.corners[0].z < b2.corners[0].z; };
 | |
| 
 | |
| 	auto bricks = parse_bricks(fp);
 | |
| 	BAN::sort::sort(bricks.begin(), bricks.end(), brick_comp);
 | |
| 
 | |
| 	// Simulate brick falling
 | |
| 	for (size_t i = 0; i < bricks.size();)
 | |
| 	{
 | |
| 		bool can_fall = bricks[i].corners[0].z > 1;
 | |
| 		for (size_t j = 0; j < i && can_fall; j++)
 | |
| 			if (bricks[j].supports(bricks[i]))
 | |
| 				can_fall = false;
 | |
| 
 | |
| 		if (!can_fall)
 | |
| 			i++;
 | |
| 		else
 | |
| 		{
 | |
| 			bricks[i].corners[0].z--;
 | |
| 			bricks[i].corners[1].z--;
 | |
| 			for (; i > 0; i--)
 | |
| 			{
 | |
| 				if (brick_comp(bricks[i - 1], bricks[i]))
 | |
| 					break;
 | |
| 				BAN::swap(bricks[i - 1], bricks[i]);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Store brick supporting structures
 | |
| 	for (size_t i = 0; i < bricks.size(); i++)
 | |
| 	{
 | |
| 		for (size_t j = 0; j < bricks.size(); j++)
 | |
| 		{
 | |
| 			if (i == j)
 | |
| 				continue;
 | |
| 			if (bricks[i].supports(bricks[j]))
 | |
| 			{
 | |
| 				MUST(bricks[i].supporting.insert(&bricks[j]));
 | |
| 				MUST(bricks[j].supported_by.insert(&bricks[i]));
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	i64 result = 0;
 | |
| 	for (const auto& brick : bricks)
 | |
| 	{
 | |
| 		bool disintegratable = true;
 | |
| 		for (const auto* support : brick.supporting)
 | |
| 			if (support->supported_by.size() <= 1)
 | |
| 				disintegratable = false;
 | |
| 		result += disintegratable;
 | |
| 	}
 | |
| 
 | |
| 	// OFF BY 7
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| i64 puzzle2(FILE* fp)
 | |
| {
 | |
| 	(void)fp;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int main(int argc, char** argv)
 | |
| {
 | |
| 	const char* file_path = "/usr/share/aoc2023/day22_input.txt";
 | |
| 
 | |
| 	if (argc >= 2)
 | |
| 		file_path = argv[1];
 | |
| 
 | |
| 	FILE* fp = fopen(file_path, "r");
 | |
| 	if (fp == nullptr)
 | |
| 	{
 | |
| 		perror("fopen");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	printf("puzzle1: %" PRId64 "\n", puzzle1(fp));
 | |
| 
 | |
| 	fseek(fp, 0, SEEK_SET);
 | |
| 
 | |
| 	printf("puzzle2: %" PRId64 "\n", puzzle2(fp));
 | |
| 
 | |
| 	fclose(fp);
 | |
| }
 |