forked from Bananymous/banan-os
				
			
		
			
				
	
	
		
			252 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include <BAN/Queue.h>
 | |
| #include <BAN/Sort.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;
 | |
| 
 | |
| struct Position
 | |
| {
 | |
| 	u32 x, y;
 | |
| 
 | |
| 	constexpr bool operator==(Position other) const
 | |
| 	{
 | |
| 		return x == other.x && y == other.y;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| struct Grid2D
 | |
| {
 | |
| 	usize width { 0 };
 | |
| 	usize height { 0 };
 | |
| 	BAN::Vector<char> data;
 | |
| 
 | |
| 	inline char get(usize x, usize y) const
 | |
| 	{
 | |
| 		ASSERT(x < width && y < height);
 | |
| 		return data[y * width + x];
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static Grid2D read_grid2d(FILE* fp)
 | |
| {
 | |
| 	usize width { 0 };
 | |
| 	usize height { 0 };
 | |
| 	BAN::Vector<char> data;
 | |
| 
 | |
| 	char buffer[1024] {};
 | |
| 	while (fgets(buffer, sizeof(buffer), fp))
 | |
| 	{
 | |
| 		const usize len = strlen(buffer);
 | |
| 		if (len == 0 || buffer[0] == '\n')
 | |
| 			break;
 | |
| 		if (data.empty())
 | |
| 			width = len - 1;
 | |
| 		height++;
 | |
| 
 | |
| 		ASSERT(buffer[width] == '\n');
 | |
| 
 | |
| 		if (data.capacity() < height * width)
 | |
| 			MUST(data.reserve(2 * height * width));
 | |
| 
 | |
| 		MUST(data.resize(height * width));
 | |
| 		memcpy(&data[(height - 1) * width], buffer, width);
 | |
| 	}
 | |
| 
 | |
| 	(void)data.shrink_to_fit();
 | |
| 
 | |
| 	return Grid2D {
 | |
| 		.width = width,
 | |
| 		.height = height,
 | |
| 		.data = BAN::move(data),
 | |
| 	};
 | |
| }
 | |
| 
 | |
| i64 part1(FILE* fp)
 | |
| {
 | |
| 	auto map = read_grid2d(fp);
 | |
| 
 | |
| 	BAN::Vector<bool> checked;
 | |
| 	MUST(checked.resize(map.width * map.height, false));
 | |
| 
 | |
| 	i64 result = 0;
 | |
| 
 | |
| 	for (u32 y = 0; y < map.height; y++)
 | |
| 	{
 | |
| 		for (u32 x = 0; x < map.width; x++)
 | |
| 		{
 | |
| 			const auto pos = Position { .x = x, .y = y };
 | |
| 			if (checked[map.width * pos.y + pos.x])
 | |
| 				continue;
 | |
| 
 | |
| 			const char target = map.get(pos.x, pos.y);
 | |
| 
 | |
| 			u32 area = 0;
 | |
| 			u32 perimiter = 0;
 | |
| 
 | |
| 			BAN::Queue<Position> checking;
 | |
| 			MUST(checking.push(pos));
 | |
| 
 | |
| 			while (!checking.empty())
 | |
| 			{
 | |
| 				const auto pos = checking.front();
 | |
| 				checking.pop();
 | |
| 
 | |
| 				if (pos.x >= map.width || pos.y >= map.height || map.get(pos.x, pos.y) != target)
 | |
| 				{
 | |
| 					perimiter++;
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (checked[map.width * pos.y + pos.x])
 | |
| 					continue;
 | |
| 				checked[map.width * pos.y + pos.x] = true;
 | |
| 
 | |
| 				area++;
 | |
| 
 | |
| 				MUST(checking.push({ .x = pos.x + 1, .y = pos.y + 0 }));
 | |
| 				MUST(checking.push({ .x = pos.x - 1, .y = pos.y + 0 }));
 | |
| 				MUST(checking.push({ .x = pos.x + 0, .y = pos.y + 1 }));
 | |
| 				MUST(checking.push({ .x = pos.x + 0, .y = pos.y - 1 }));
 | |
| 			}
 | |
| 
 | |
| 			result += area * perimiter;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| i64 part2(FILE* fp)
 | |
| {
 | |
| 	auto map = read_grid2d(fp);
 | |
| 
 | |
| 	BAN::Vector<bool> checked;
 | |
| 	MUST(checked.resize(map.width * map.height, false));
 | |
| 
 | |
| 	i64 result = 0;
 | |
| 
 | |
| 	for (u32 y = 0; y < map.height; y++)
 | |
| 	{
 | |
| 		for (u32 x = 0; x < map.width; x++)
 | |
| 		{
 | |
| 			const auto pos = Position { .x = x, .y = y };
 | |
| 			if (checked[map.width * pos.y + pos.x])
 | |
| 				continue;
 | |
| 
 | |
| 			const char target = map.get(pos.x, pos.y);
 | |
| 
 | |
| 			struct PerimeterEntry
 | |
| 			{
 | |
| 				Position pos;
 | |
| 				bool counted;
 | |
| 			};
 | |
| 			BAN::Vector<PerimeterEntry> perimiters[4];
 | |
| 
 | |
| 			struct CheckEntry
 | |
| 			{
 | |
| 				Position pos;
 | |
| 				uint8_t dir;
 | |
| 			};
 | |
| 			BAN::Queue<CheckEntry> checking;
 | |
| 			MUST(checking.push({ .pos = pos, .dir = 0 }));
 | |
| 
 | |
| 			u32 area = 0;
 | |
| 
 | |
| 			while (!checking.empty())
 | |
| 			{
 | |
| 				const auto [pos, dir] = checking.front();
 | |
| 				checking.pop();
 | |
| 
 | |
| 				if (pos.x >= map.width || pos.y >= map.height || map.get(pos.x, pos.y) != target)
 | |
| 				{
 | |
| 					MUST(perimiters[dir].emplace_back(pos, true));
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (checked[map.width * pos.y + pos.x])
 | |
| 					continue;
 | |
| 				checked[map.width * pos.y + pos.x] = true;
 | |
| 
 | |
| 				area++;
 | |
| 
 | |
| 				MUST(checking.push({ .pos { .x = pos.x + 1, .y = pos.y + 0 }, .dir = 0 }));
 | |
| 				MUST(checking.push({ .pos { .x = pos.x - 1, .y = pos.y + 0 }, .dir = 1 }));
 | |
| 				MUST(checking.push({ .pos { .x = pos.x + 0, .y = pos.y + 1 }, .dir = 2 }));
 | |
| 				MUST(checking.push({ .pos { .x = pos.x + 0, .y = pos.y - 1 }, .dir = 3 }));
 | |
| 			}
 | |
| 
 | |
| 			for (auto& perimiter : perimiters)
 | |
| 			{
 | |
| 				BAN::sort::sort(perimiter.begin(), perimiter.end(),
 | |
| 					[](const auto& a, const auto& b) -> bool
 | |
| 					{
 | |
| 						if (a.pos.x != b.pos.x)
 | |
| 							return a.pos.x < b.pos.x;
 | |
| 						return a.pos.y < b.pos.y;
 | |
| 					}
 | |
| 				);
 | |
| 
 | |
| 				for (auto it1 = perimiter.begin(); it1 != perimiter.end(); it1++)
 | |
| 				{
 | |
| 					for (auto it2 = it1 + 1; it2 != perimiter.end(); it2++)
 | |
| 					{
 | |
| 						if (it1->pos.x != it2->pos.x && it1->pos.y != it2->pos.y)
 | |
| 							continue;
 | |
| 						if (!it2->counted)
 | |
| 							continue;
 | |
| 						const u32 diff_x = it2->pos.x - it1->pos.x;
 | |
| 						const u32 diff_y = it2->pos.y - it1->pos.y;
 | |
| 						if ((diff_x == 0 && diff_y == 1) || (diff_x == 1 && diff_y == 0))
 | |
| 							it2->counted = false;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			u32 sides = 0;
 | |
| 			for (const auto& perimiter : perimiters)
 | |
| 				for (auto [_, counted] : perimiter)
 | |
| 					sides += counted;
 | |
| 
 | |
| 			result += area * sides;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| int main(int argc, char** argv)
 | |
| {
 | |
| 	const char* file_path = "/usr/share/aoc2024/day12_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);
 | |
| }
 |