Solution to problem 6 in Python

This commit is contained in:
David Doblas Jiménez 2023-08-27 16:19:21 +02:00
parent b8b0f026f1
commit 78c98449ec

196
src/Year_2018/P6.py Normal file
View File

@ -0,0 +1,196 @@
# --- Day 6: Chronal Coordinates ---
# The device on your wrist beeps several times, and once again you feel like
# you're falling.
# "Situation critical," the device announces. "Destination indeterminate.
# Chronal interference detected. Please specify new target coordinates."
# The device then produces a list of coordinates (your puzzle input). Are they
# places it thinks are safe or dangerous? It recommends you check manual page
# 729. The Elves did not give you a manual.
# If they're dangerous, maybe you can minimize the danger by finding the
# coordinate that gives the largest distance from the other points.
# Using only the Manhattan distance, determine the area around each coordinate
# by counting the number of integer X,Y locations that are closest to that
# coordinate (and aren't tied in distance to any other coordinate).
# Your goal is to find the size of the largest area that isn't infinite. For
# example, consider the following list of coordinates:
# 1, 1
# 1, 6
# 8, 3
# 3, 4
# 5, 5
# 8, 9
# If we name these coordinates A through F, we can draw them on a grid, putting
# 0,0 at the top left:
# ..........
# .A........
# ..........
# ........C.
# ...D......
# .....E....
# .B........
# ..........
# ..........
# ........F.
# This view is partial - the actual grid extends infinitely in all directions.
# Using the Manhattan distance, each location's closest coordinate can be
# determined, shown here in lowercase:
# aaaaa.cccc
# aAaaa.cccc
# aaaddecccc
# aadddeccCc
# ..dDdeeccc
# bb.deEeecc
# bBb.eeee..
# bbb.eeefff
# bbb.eeffff
# bbb.ffffFf
# Locations shown as . are equally far from two or more coordinates, and so
# they don't count as being closest to any.
# In this example, the areas of coordinates A, B, C, and F are infinite - while
# not shown here, their areas extend forever outside the visible grid. However,
# the areas of coordinates D and E are finite: D is closest to 9 locations, and
# E is closest to 17 (both including the coordinate's location itself).
# Therefore, in this example, the size of the largest area is 17.
# What is the size of the largest area that isn't infinite?
from collections import defaultdict
from copy import copy
with open("files/P6.txt") as f:
puzzle = [
tuple(map(int, line.split(", ")))
for line in f.read().strip().split("\n")
]
def manhattan_dist(coordinate, x, y):
return abs(coordinate[0] - x) + abs(coordinate[1] - y)
def part_1():
coordinates = copy(puzzle)
# Max/min coordinates
max_first = max(coordinates, key=lambda e: (e[0], e[0]))[0]
max_second = max(coordinates, key=lambda e: (e[1], e[1]))[1]
min_first = min(coordinates, key=lambda e: (e[0], e[0]))[0]
min_second = min(coordinates, key=lambda e: (e[1], e[1]))[1]
# Holds total area per coordinate region
areas = defaultdict(int)
# Holds disqualified (infinite) regions
infiniteCoordinates = set()
for x in range(min_first, max_first + 1):
for y in range(min_second, max_second + 1):
# Holds distances mapped to coordinates
distances = defaultdict(list)
for coordinate in coordinates:
distances[manhattan_dist(coordinate, x, y)].append(coordinate)
shortestDistance = min(distances.keys())
if len(distances[shortestDistance]) == 1:
areas[distances[shortestDistance][0]] += 1
# Borders also indicate infinite regions
if (
y == min_second
or y == max_second
or x == min_first
or x == max_first
):
infiniteCoordinates |= set(distances[shortestDistance])
# Remove disqualified (infinite) regions
for coordinate in infiniteCoordinates:
del areas[coordinate]
print(f"The size of the largest area is {max(areas.values())}")
# --- Part Two ---
# On the other hand, if the coordinates are safe, maybe the best you can do is
# try to find a region near as many coordinates as possible.
# For example, suppose you want the sum of the Manhattan distance to all of the
# coordinates to be less than 32. For each location, add up the distances to all
# of the given coordinates; if the total of those distances is less than 32,
# that location is within the desired region. Using the same coordinates as
# above, the resulting region looks like this:
# ..........
# .A........
# ..........
# ...###..C.
# ..#D###...
# ..###E#...
# .B.###....
# ..........
# ..........
# ........F.
# In particular, consider the highlighted location 4,3 located at the top middle
# of the region. Its calculation is as follows, where abs() is the absolute
# value function:
# Distance to coordinate A: abs(4-1) + abs(3-1) = 5
# Distance to coordinate B: abs(4-1) + abs(3-6) = 6
# Distance to coordinate C: abs(4-8) + abs(3-3) = 4
# Distance to coordinate D: abs(4-3) + abs(3-4) = 2
# Distance to coordinate E: abs(4-5) + abs(3-5) = 3
# Distance to coordinate F: abs(4-8) + abs(3-9) = 10
# Total distance: 5 + 6 + 4 + 2 + 3 + 10 = 30
# Because the total distance to all coordinates (30) is less than 32, the
# location is within the region.
# This region, which also includes coordinates D and E, has a total size of 16.
# Your actual region will need to be much larger than this example, though,
# instead including all locations with a total distance of less than 10000.
# What is the size of the region containing all locations which have a total
# distance to all given coordinates of less than 10000?
def part_2():
coordinates = copy(puzzle)
# Max coordinates
max_x = max(coordinates, key=lambda e: (e[0], e[0]))[0]
max_y = max(coordinates, key=lambda e: (e[1], e[1]))[1]
size = 0
for x in range(max_x + 1):
for y in range(max_y + 1):
total_dist = 0
for coordinate in coordinates:
distance = manhattan_dist(coordinate, x, y)
total_dist += distance
if total_dist < 10_000:
size += 1
print(f"The size of the region is {size}")
if __name__ == "__main__":
part_1()
part_2()