= """
example 2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8
""".strip().split()
day4
Day 4: Camp Cleanup
The list of camp cleanup assignments has unfortunate overlaps. For example:
2-4,6-8 First elf has sections 2,3,4; Second has 6,7,8.
2-3,4-5 First has 2,3; Second has 4,6
5-7,7-9 First has 5,6,7; Second has 7,8,9.
Actual numbers may be higher.
Some assignments fully contain the other.
Find them.
Example:
Consider this list of assignments.
We could do this by comparing min & max. Or we could generate sets from ranges and check intersection is as large as the smaller range. But the second could struggle with very large ranges.
Code
def get_assignments(data: list[str] # List of assignment pairs "2-4,6-8"
-> list[list[int]]: # List of [min,max,min,max]
) """Split each assignment by commas and dashes."""
= (row.replace("-",",").split(",") for row in data)
_ return [[int(x) for x in row] for row in _]
get_assignments
get_assignments (data:list[str])
Split each assignment by commas and dashes.
Type | Details | |
---|---|---|
data | list | List of assignment pairs “2-4,6-8” |
Returns | list | List of [min,max,min,max] |
= get_assignments(example)
assignments assignments
[[2, 4, 6, 8],
[2, 3, 4, 5],
[5, 7, 7, 9],
[2, 8, 3, 7],
[6, 6, 4, 6],
[2, 6, 4, 8]]
Code
def right_contains_left(row: list[int] # [min,max,min,max]
-> bool: # Right pair contains left
)return (row[2] <= row[0]) and (row[3] >= row[1])
def left_contains_right(row: list[int] # [min,max,min,max]
-> bool: # Left pair contains right
)return (row[0] <= row[2]) and (row[1] >= row[3])
def contains(row: list[int] # [min,max,min,max]
-> bool: # One pair contains the other
)return right_contains_left(row) or left_contains_right(row)
contains
contains (row:list[int])
Type | Details | |
---|---|---|
row | list | [min,max,min,max] |
Returns | bool | One pair contains the other |
left_contains_right
left_contains_right (row:list[int])
Type | Details | |
---|---|---|
row | list | [min,max,min,max] |
Returns | bool | Left pair contains right |
right_contains_left
right_contains_left (row:list[int])
Type | Details | |
---|---|---|
row | list | [min,max,min,max] |
Returns | bool | Right pair contains left |
Test that
assert [contains(row) for row in assignments] == [False, False, False, True, True, False]
Part 1
Get the data
with open("data/day4_input.txt") as f:
= [x.strip() for x in f.readlines()]
data 3] data[:
['14-28,13-28', '72-81,82-91', '4-4,6-95']
Run
= get_assignments(data)
assignments = [contains(row) for row in assignments]
contains f"{sum(contains):,} assignment pairs fully overlap"
'305 assignment pairs fully overlap'
Part 2
OK, how many overlap at all?
Tempting to reuse day2.intersect
but in case the ranges are billions long, let’s just do some min/max checking again. For some reason it’s easier for me to reason about disjoint
first and then define overlap
as not disjoint
.
Code
def disjoint(row: list[int] # [min,max,min,max]
-> bool: # The ranges are disjoint
)# min1 > max2 or max1 < min2
return (row[0] > row[3]) or (row[1] < row[2])
def overlap(row: list[int] # [min,max,min,max]
-> bool: # The ranges overlap
)return not disjoint(row)
overlap
overlap (row:list[int])
Type | Details | |
---|---|---|
row | list | [min,max,min,max] |
Returns | bool | The ranges overlap |
disjoint
disjoint (row:list[int])
Type | Details | |
---|---|---|
row | list | [min,max,min,max] |
Returns | bool | The ranges are disjoint |
Run
= [overlap(x) for x in get_assignments(data)]
overlaps f"{sum(overlaps):,} assignments overlap at all."
'811 assignments overlap at all.'