Skip to content

Commit

Permalink
Merge conflict on advent of code because of COURSE
Browse files Browse the repository at this point in the history
  • Loading branch information
LukeMoll committed Dec 6, 2019
2 parents 8797999 + a599c98 commit 59d30b8
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
__pycache__/
.vscode/
57 changes: 56 additions & 1 deletion aoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,59 @@ def output(part : int, func, post=None, comment=None, args=[], kwargs={}):
else:
print(f" {result}")

print()
print()

def __find_next_day():
days = map(
lambda fn: int(os.path.basename(fn).replace("day","").replace(".py","")),
filter(
lambda fn: fn.startswith("day"),
os.listdir(os.path.dirname(__file__))
)
)
return max(days) + 1


if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(dest="command")

parser_new = subparser.add_parser("new", help="Create a file for a new day")
parser_new.add_argument("day", type=int, nargs="?", default=__find_next_day(), help="Day to create (defaults to next day that doesn't exist)")

args = parser.parse_args()
if args.command == "new":
filename = os.path.join(os.path.dirname(__file__), f"day{args.day:02}.py")
if os.path.exists(filename):
print(f"day{args.day:02}.py exists, overwrite? (y/N)")
if input().lower() != "y":
exit()
with open(filename, 'w') as fd:
fd.write(
"""import aoc
def main():
aoc.header("Your title here")
test()
# aoc.output(1, part1)
# aoc.output(2, part2)
def test():
pass
print("✓ All tests passed!")
def part1():
pass
def part2():
pass
if __name__ == "__main__":
main()
""")
print(f"Created day{args.day:02}.py")



86 changes: 86 additions & 0 deletions day03.py
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()
89 changes: 89 additions & 0 deletions day04.py
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()

0 comments on commit 59d30b8

Please sign in to comment.