-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge conflict on advent of code because of COURSE
- Loading branch information
Showing
4 changed files
with
232 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
__pycache__/ | ||
.vscode/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import aoc | ||
import typing | ||
import pprint | ||
|
||
Coord = typing.Tuple[int,int] | ||
|
||
def main(): | ||
aoc.header("Crossed Wires") | ||
test() | ||
|
||
inp = aoc.get_input() | ||
path_a = list(inp.readline().split(",")) | ||
path_b = list(inp.readline().split(",")) | ||
aoc.output(1, part1, args=[path_a, path_b]) | ||
aoc.output(2, part2, args=[path_a, path_b]) | ||
|
||
def manhattan(c : Coord): return abs(c[0]) + abs(c[1]) | ||
|
||
def test(): | ||
# part 1 | ||
def assert_manhattan(str_a, str_b, dist): | ||
closest = closest_intersection(str_a.split(","), str_b.split(",")) | ||
assert manhattan(closest) == dist, f"Expected {dist}, got {manhattan(closest)}" | ||
|
||
assert_manhattan("R8,U5,L5,D3", "U7,R6,D4,L4", 6) | ||
assert_manhattan("U7,R6,D4,L4", "R8,U5,L5,D3", 6) | ||
assert_manhattan("R75,D30,R83,U83,L12,D49,R71,U7,L72","U62,R66,U55,R34,D71,R55,D58,R83", 159) | ||
assert_manhattan("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51", "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7", 135) | ||
|
||
# part 2 | ||
def assert_steps(str_a, str_b, steps): | ||
res = shortest_intersection(str_a.split(","), str_b.split(",")) | ||
assert res == steps, f"Expected {steps}, got {res}" | ||
|
||
assert_steps("R8,U5,L5,D3", "U7,R6,D4,L4", 30) | ||
assert_steps("R75,D30,R83,U83,L12,D49,R71,U7,L72","U62,R66,U55,R34,D71,R55,D58,R83", 610) | ||
assert_steps("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51", "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7", 410) | ||
|
||
print("✓ All tests passed!") | ||
|
||
def part1(path_a : typing.Iterable[str], path_b : typing.Iterable[str]) -> int: | ||
closest = closest_intersection(path_a, path_b) | ||
return manhattan(closest) | ||
|
||
def part2(path_a : typing.Iterable[str], path_b : typing.Iterable[str]) -> int: | ||
return shortest_intersection(path_a, path_b) | ||
|
||
directions = { | ||
'R': ( 1, 0), | ||
'U': ( 0, 1), | ||
'L': (-1, 0), | ||
'D': ( 0,-1) | ||
} | ||
|
||
def path_to_coords(path : typing.Iterable[str]) -> typing.Iterator[Coord]: | ||
# start at origin | ||
position = (0,0) | ||
|
||
for direction in path: | ||
offset = directions[direction[0]] | ||
count = int(direction[1:]) | ||
|
||
for i in range(count): | ||
yield ( | ||
position[0] + (i + 1) * offset[0], | ||
position[1] + (i + 1) * offset[1] | ||
) | ||
|
||
position = ( | ||
position[0] + count * offset[0], | ||
position[1] + count * offset[1] | ||
) | ||
|
||
def closest_intersection(path_a : typing.Iterable[str], path_b : typing.Iterable[str]) -> Coord: | ||
intersections = set(path_to_coords(path_a)).intersection(set(path_to_coords(path_b))) | ||
return sorted(intersections, key=manhattan)[0] | ||
|
||
def shortest_intersection(path_a : typing.Iterable[str], path_b : typing.Iterable[str]): | ||
seq_a = list(path_to_coords(path_a)) | ||
seq_b = list(path_to_coords(path_b)) | ||
intersections = set(seq_a).intersection(set(seq_b)) # we could reuse this from p1 | ||
lengths = map(lambda c:seq_a.index(c) + seq_b.index(c) + 2, intersections) | ||
return min(lengths) | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import aoc | ||
import re | ||
from enum import Enum | ||
|
||
def main(): | ||
aoc.header("Secure Container") | ||
test() | ||
|
||
(lower, upper) = aoc.get_input().readline().split("-") | ||
aoc.output(1, part, args=[int(lower),int(upper),validate_p1]) | ||
aoc.output(2, part, args=[int(lower),int(upper),validate_p2]) | ||
# aoc.output(2, part2) | ||
|
||
def test(): | ||
assert rule2("111111") | ||
assert not rule2("123789") | ||
|
||
assert rule4("112233") | ||
assert not rule4("123444") | ||
assert rule4("111122") | ||
|
||
print("✓ All tests passed!") | ||
|
||
def part(lower : int, upper : int, validate): | ||
i = lower | ||
passwords = [] | ||
while i < upper: | ||
i = succ(i) # not all outputs from succ pass rule3 :thinking: | ||
if validate(i): passwords.append(i) | ||
if passwords[-1] >= upper: passwords.pop() | ||
|
||
for i in passwords: | ||
if not validate(i): print(i) | ||
return len(passwords) | ||
|
||
def rule2(s : str): | ||
for o in range(5): | ||
if s[o] == s[o+1]: | ||
return True | ||
return False | ||
|
||
def rule3(s : str): | ||
for o in range(5): | ||
if int(s[o]) > int(s[o+1]): | ||
return False | ||
return True | ||
|
||
class R4State(Enum): | ||
SINGLE = 1 | ||
DOUBLE = 2 | ||
TRAP = 3 | ||
|
||
def rule4(s : str): | ||
state = R4State.SINGLE | ||
|
||
matching = s[0] | ||
for current in s[1:]: | ||
if current == matching: | ||
if state == R4State.SINGLE: | ||
state = R4State.DOUBLE | ||
elif state == R4State.DOUBLE: | ||
state = R4State.TRAP | ||
else: | ||
if state == R4State.DOUBLE: | ||
return True | ||
state = R4State.SINGLE | ||
matching = current | ||
|
||
return state == R4State.DOUBLE | ||
|
||
r3_re = re.compile(r"^(\d*)([1-9])(0+)$") | ||
def succ(i : int) -> int: | ||
# Would like to make this implicitly pass rule3 | ||
i += 1 | ||
m = r3_re.match(str(i)) | ||
if m is not None: | ||
return int(f"{m.group(1)}{m.group(2)}{m.group(2) * len(m.group(3))}") | ||
else: return i | ||
|
||
def validate_p1(i : int) -> bool: | ||
s = str(i) | ||
return rule2(s) and rule3(s) | ||
|
||
def validate_p2(i : int) -> bool: | ||
s = str(i) | ||
return rule4(s) and rule3(s) | ||
|
||
if __name__ == "__main__": | ||
main() |