forked from Bananymous/banan-os
				
			AOC2023: Implement day25 part1
7 starts missing + partly broken day22. I might finish these soon...
This commit is contained in:
		
							parent
							
								
									3939da4fb0
								
							
						
					
					
						commit
						8b5e437936
					
				|  | @ -26,6 +26,7 @@ set(AOC2023_PROJECTS | ||||||
| 	day21 | 	day21 | ||||||
| 	day23 | 	day23 | ||||||
| 	day24 | 	day24 | ||||||
|  | 	day25 | ||||||
| 	full | 	full | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | cmake_minimum_required(VERSION 3.26) | ||||||
|  | 
 | ||||||
|  | project(aoc2023_day25 CXX) | ||||||
|  | 
 | ||||||
|  | set(SOURCES | ||||||
|  | 	main.cpp | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | add_executable(aoc2023_day25 ${SOURCES}) | ||||||
|  | target_compile_options(aoc2023_day25 PUBLIC -O2 -g) | ||||||
|  | target_link_libraries(aoc2023_day25 PUBLIC libc ban) | ||||||
|  | 
 | ||||||
|  | add_dependencies(aoc2023_day25 libc-install ban-install) | ||||||
|  | 
 | ||||||
|  | add_custom_target(aoc2023_day25-install | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day25 ${BANAN_AOC2023_BIN}/day25 | ||||||
|  | 	DEPENDS aoc2023_day25 | ||||||
|  | 	DEPENDS aoc2023_always | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | add_dependencies(aoc2023 aoc2023_day25) | ||||||
|  | add_dependencies(aoc2023-install aoc2023_day25-install) | ||||||
|  | @ -0,0 +1,249 @@ | ||||||
|  | #include <BAN/HashMap.h> | ||||||
|  | #include <BAN/HashSet.h> | ||||||
|  | #include <BAN/Sort.h> | ||||||
|  | #include <BAN/String.h> | ||||||
|  | #include <BAN/Vector.h> | ||||||
|  | 
 | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <inttypes.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.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 Component | ||||||
|  | { | ||||||
|  | 	BAN::String name; | ||||||
|  | 	BAN::Vector<BAN::String> connections; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | BAN::HashMap<BAN::String, Component> parse_components(FILE* fp) | ||||||
|  | { | ||||||
|  | 	BAN::HashMap<BAN::String, Component> components; | ||||||
|  | 
 | ||||||
|  | 	char buffer[128]; | ||||||
|  | 	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 parts = MUST(line.split(' ')); | ||||||
|  | 		ASSERT(parts.size() >= 2); | ||||||
|  | 
 | ||||||
|  | 		ASSERT(parts.front().back() == ':'); | ||||||
|  | 		parts.front() = parts.front().substring(0, parts.front().size() - 1); | ||||||
|  | 
 | ||||||
|  | 		if (!components.contains(parts.front())) | ||||||
|  | 			MUST(components.emplace(parts.front(), parts.front())); | ||||||
|  | 		 | ||||||
|  | 		for (size_t i = 1; i < parts.size(); i++) | ||||||
|  | 		{ | ||||||
|  | 			MUST(components[parts.front()].connections.emplace_back(parts[i])); | ||||||
|  | 
 | ||||||
|  | 			if (!components.contains(parts[i])) | ||||||
|  | 				MUST(components.emplace(parts[i], parts[i])); | ||||||
|  | 			MUST(components[parts[i]].connections.emplace_back(parts.front())); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return components; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BAN::String connection_key(const BAN::String& a, const BAN::String& b) | ||||||
|  | { | ||||||
|  | 	auto comp = | ||||||
|  | 		[](const auto& a, const auto& b) | ||||||
|  | 		{ | ||||||
|  | 			ASSERT(a.size() == b.size()); | ||||||
|  | 			for (size_t i = 0; i < a.size(); i++) | ||||||
|  | 				if (a[i] != b[i]) | ||||||
|  | 					return a[i] < b[i]; | ||||||
|  | 			ASSERT_NOT_REACHED(); | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 	const auto& s1 = comp(a, b) ? a : b; | ||||||
|  | 	const auto& s2 = comp(a, b) ? b : a; | ||||||
|  | 
 | ||||||
|  | 	auto key = s1; | ||||||
|  | 	MUST(key.append(s2)); | ||||||
|  | 	return key; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t graph_size(const BAN::HashMap<BAN::String, Component>& graph, const BAN::String& start, const BAN::HashSet<BAN::String>& removed) | ||||||
|  | { | ||||||
|  | 	BAN::HashSet<BAN::String> visited; | ||||||
|  | 	BAN::HashSet<BAN::String> pending; | ||||||
|  | 	MUST(pending.insert(start)); | ||||||
|  | 
 | ||||||
|  | 	while (!pending.empty()) | ||||||
|  | 	{ | ||||||
|  | 		auto current = *pending.begin(); | ||||||
|  | 		pending.remove(current); | ||||||
|  | 
 | ||||||
|  | 		MUST(visited.insert(current)); | ||||||
|  | 
 | ||||||
|  | 		const auto& targets = graph[current].connections; | ||||||
|  | 		for (const auto& target : targets) | ||||||
|  | 		{ | ||||||
|  | 			if (removed.contains(connection_key(current, target)))	 | ||||||
|  | 				continue; | ||||||
|  | 			if (visited.contains(target)) | ||||||
|  | 				continue; | ||||||
|  | 			MUST(pending.insert(target)); | ||||||
|  | 			MUST(visited.insert(target)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return visited.size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BAN::Vector<BAN::String> find_shortest_path(const BAN::String& start, const BAN::String& end, const BAN::HashMap<BAN::String, Component>& graph) | ||||||
|  | { | ||||||
|  | 	BAN::HashMap<BAN::String, BAN::Vector<BAN::String>> paths; | ||||||
|  | 	MUST(paths.insert(start, {})); | ||||||
|  | 	MUST(paths[start].push_back(start)); | ||||||
|  | 
 | ||||||
|  | 	BAN::HashSet<BAN::String> visited; | ||||||
|  | 	BAN::HashSet<BAN::String> pending; | ||||||
|  | 	MUST(pending.insert(start)); | ||||||
|  | 
 | ||||||
|  | 	while (!pending.empty()) | ||||||
|  | 	{ | ||||||
|  | 		BAN::HashSet<BAN::String> next_pending; | ||||||
|  | 
 | ||||||
|  | 		while (!pending.empty()) | ||||||
|  | 		{ | ||||||
|  | 			auto current = *pending.begin(); | ||||||
|  | 			pending.remove(current); | ||||||
|  | 			MUST(visited.insert(current)); | ||||||
|  | 
 | ||||||
|  | 			if (current == end) | ||||||
|  | 				return paths[current]; | ||||||
|  | 
 | ||||||
|  | 			const auto& targets = graph[current].connections; | ||||||
|  | 			for (const auto& target : targets) | ||||||
|  | 			{ | ||||||
|  | 				if (visited.contains(target)) | ||||||
|  | 					continue; | ||||||
|  | 				if (pending.contains(target)) | ||||||
|  | 					continue; | ||||||
|  | 				if (next_pending.contains(target)) | ||||||
|  | 					continue; | ||||||
|  | 				if (paths.contains(target)) | ||||||
|  | 					continue; | ||||||
|  | 				MUST(next_pending.insert(target)); | ||||||
|  | 				MUST(visited.insert(target)); | ||||||
|  | 
 | ||||||
|  | 				MUST(paths.insert(target, paths[current])); | ||||||
|  | 				MUST(paths[target].push_back(target)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		pending = BAN::move(next_pending); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ASSERT_NOT_REACHED(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | i64 puzzle1(FILE* fp) | ||||||
|  | { | ||||||
|  | 	auto components = parse_components(fp); | ||||||
|  | 
 | ||||||
|  | 	BAN::HashMap<BAN::String, i64> connection_count; | ||||||
|  | 
 | ||||||
|  | 	srand(time(nullptr)); | ||||||
|  | 	for (size_t i = 0; i < 100; i++) | ||||||
|  | 	{ | ||||||
|  | 		size_t idx1 = rand() % components.size(); | ||||||
|  | 		size_t idx2 = rand() % components.size(); | ||||||
|  | 
 | ||||||
|  | 		auto path = find_shortest_path(next(components.begin(), idx1)->key, next(components.begin(), idx2)->key, components); | ||||||
|  | 		for (size_t j = 1; j < path.size(); j++) | ||||||
|  | 		{ | ||||||
|  | 			const auto& connection = connection_key(path[j - 1], path[j]); | ||||||
|  | 			if (!connection_count.contains(connection)) | ||||||
|  | 				MUST(connection_count.insert(connection, 0)); | ||||||
|  | 			connection_count[connection]++; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct Connection | ||||||
|  | 	{ | ||||||
|  | 		BAN::String connection; | ||||||
|  | 		i64 count; | ||||||
|  | 	}; | ||||||
|  | 	BAN::Vector<Connection> hot_connections; | ||||||
|  | 	for (const auto& [connection, count] : connection_count) | ||||||
|  | 		MUST(hot_connections.emplace_back(connection, count)); | ||||||
|  | 	BAN::sort::sort(hot_connections.begin(), hot_connections.end(), [](const auto& a, const auto& b) { return a.count > b.count; }); | ||||||
|  | 
 | ||||||
|  | 	for (size_t depth = 0; depth < hot_connections.size(); depth++) | ||||||
|  | 	{ | ||||||
|  | 		for (size_t i = 0; i < depth; i++) | ||||||
|  | 		{ | ||||||
|  | 			for (size_t j = i + 1; j < depth; j++) | ||||||
|  | 			{ | ||||||
|  | 				for (size_t k = j + 1; k < depth; k++) | ||||||
|  | 				{ | ||||||
|  | 					BAN::HashSet<BAN::String> removed; | ||||||
|  | 					MUST(removed.insert(hot_connections[i].connection)); | ||||||
|  | 					MUST(removed.insert(hot_connections[j].connection)); | ||||||
|  | 					MUST(removed.insert(hot_connections[k].connection)); | ||||||
|  | 
 | ||||||
|  | 					size_t lhs_size = graph_size(components, hot_connections[i].connection.sv().substring(0, 3), removed); | ||||||
|  | 					if (lhs_size == components.size()) | ||||||
|  | 						continue; | ||||||
|  | 
 | ||||||
|  | 					size_t rhs_size = graph_size(components, hot_connections[i].connection.sv().substring(3, 3), removed); | ||||||
|  | 					if (rhs_size == components.size()) | ||||||
|  | 						continue; | ||||||
|  | 
 | ||||||
|  | 					if (lhs_size + rhs_size == components.size()) | ||||||
|  | 						return lhs_size * rhs_size; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | i64 puzzle2(FILE* fp) | ||||||
|  | { | ||||||
|  | 	(void)fp; | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char** argv) | ||||||
|  | { | ||||||
|  | 	const char* file_path = "/usr/share/aoc2023/day25_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); | ||||||
|  | } | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| int main() | int main() | ||||||
| { | { | ||||||
| 	for (int i = 1; i <= 24; i++) | 	for (int i = 1; i <= 25; i++) | ||||||
| 	{ | 	{ | ||||||
| 		if (i == 22) | 		if (i == 22) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue