formatting and cosmetic updates to 2020.
This commit is contained in:
parent
84c4cf9991
commit
cd75e58f77
28 changed files with 320 additions and 287 deletions
|
|
@ -2,41 +2,43 @@
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
def parse(puzzle_input):
|
def parse(puzzle_input):
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
return [int(string) for string in puzzle_input.splitlines()]
|
return [int(string) for string in puzzle_input.splitlines()]
|
||||||
|
|
||||||
def part1(data:list[int]):
|
|
||||||
|
def part1(data: list[int]):
|
||||||
"""Solve part 1"""
|
"""Solve part 1"""
|
||||||
data.sort()
|
data.sort()
|
||||||
while True:
|
while True:
|
||||||
if len(data)<2:
|
if len(data) < 2:
|
||||||
raise ValueError('no match found')
|
raise ValueError("no match found")
|
||||||
s = data[0]+data[-1]
|
s = data[0] + data[-1]
|
||||||
if s>2020:
|
if s > 2020:
|
||||||
data.pop(-1)
|
data.pop(-1)
|
||||||
elif s<2020:
|
elif s < 2020:
|
||||||
data.pop(0)
|
data.pop(0)
|
||||||
else:
|
else:
|
||||||
return data[0]*data[-1]
|
return data[0] * data[-1]
|
||||||
|
|
||||||
|
|
||||||
def part2(data):
|
def part2(data):
|
||||||
"""Solve part 2"""
|
"""Solve part 2"""
|
||||||
data.sort()
|
data.sort()
|
||||||
while True:
|
while True:
|
||||||
if len(data)<3:
|
if len(data) < 3:
|
||||||
raise ValueError('no match found')
|
raise ValueError("no match found")
|
||||||
if data[0]+data[1]+data[-1]>2020:
|
if data[0] + data[1] + data[-1] > 2020:
|
||||||
data.pop(-1)
|
data.pop(-1)
|
||||||
elif data[0]+data[-1]+data[-2]<2020:
|
elif data[0] + data[-1] + data[-2] < 2020:
|
||||||
data.pop(0)
|
data.pop(0)
|
||||||
elif data[0]+data[1]+data[-1]==2020:
|
elif data[0] + data[1] + data[-1] == 2020:
|
||||||
return data[0]*data[1]*data[-1]
|
return data[0] * data[1] * data[-1]
|
||||||
else:
|
else:
|
||||||
return data[0]*data[-1]*data[-2]
|
return data[0] * data[-1] * data[-2]
|
||||||
|
|
||||||
|
|
||||||
def solve(puzzle_input):
|
def solve(puzzle_input):
|
||||||
"""Solve the puzzle for the given input"""
|
"""Solve the puzzle for the given input"""
|
||||||
|
|
@ -46,6 +48,7 @@ def solve(puzzle_input):
|
||||||
|
|
||||||
return solution1, solution2
|
return solution1, solution2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for path in sys.argv[1:]:
|
for path in sys.argv[1:]:
|
||||||
print(f"{path}:")
|
print(f"{path}:")
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,24 @@ import day1 as aoc
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
|
||||||
|
# these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "example2").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
def test_parse(example):
|
||||||
def test_parse_example1(example1):
|
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == [1721, 979, 366, 299, 675, 1456]
|
assert example == [1721, 979, 366, 299, 675, 1456]
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
|
||||||
def test_part1_example1(example1):
|
def test_part1(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == 514579
|
assert aoc.part1(example) == 514579
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
|
||||||
def test_part2_example2(example1):
|
def test_part2(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example1) == 241861950
|
assert aoc.part2(example) == 241861950
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,52 @@
|
||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PasswordSpec:
|
class PasswordSpec:
|
||||||
first:int
|
first: int
|
||||||
second:int
|
second: int
|
||||||
letter:str
|
letter: str
|
||||||
password:str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
def parse(puzzle_input: str):
|
def parse(puzzle_input: str):
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
regex = re.compile(r'^(\d+)-(\d+) (\w): (\w+)$')
|
regex = re.compile(r"^(\d+)-(\d+) (\w): (\w+)$")
|
||||||
toInt = lambda x: PasswordSpec(int(x[0]), int(x[1]), x[2], x[3])
|
|
||||||
|
def toInt(x):
|
||||||
|
return PasswordSpec(int(x[0]), int(x[1]), x[2], x[3])
|
||||||
|
|
||||||
return [toInt(regex.match(i).groups()) for i in puzzle_input.splitlines()]
|
return [toInt(regex.match(i).groups()) for i in puzzle_input.splitlines()]
|
||||||
|
|
||||||
|
|
||||||
def part1(data):
|
def part1(data):
|
||||||
"""Solve part 1"""
|
"""Solve part 1"""
|
||||||
test = lambda x: x.first<=x.password.count(x.letter)<=x.second
|
|
||||||
|
def test(x):
|
||||||
|
return x.first <= x.password.count(x.letter) <= x.second
|
||||||
|
|
||||||
# these two lines are equivilant.
|
# these two lines are equivilant.
|
||||||
# return sum(1 for p in data if test(p))
|
# return sum(1 for p in data if test(p))
|
||||||
return len([1 for p in data if test(p)])
|
return len([1 for p in data if test(p)])
|
||||||
|
|
||||||
|
|
||||||
def test_password(passwordSpec: PasswordSpec):
|
def test_password(passwordSpec: PasswordSpec):
|
||||||
if passwordSpec.password[passwordSpec.first-1]==passwordSpec.letter:
|
if passwordSpec.password[passwordSpec.first - 1] == passwordSpec.letter:
|
||||||
return passwordSpec.password[passwordSpec.second-1]!=passwordSpec.letter
|
return passwordSpec.password[passwordSpec.second - 1] != passwordSpec.letter
|
||||||
else:
|
else:
|
||||||
return passwordSpec.password[passwordSpec.second-1]==passwordSpec.letter
|
return passwordSpec.password[passwordSpec.second - 1] == passwordSpec.letter
|
||||||
|
|
||||||
|
|
||||||
def part2(data):
|
def part2(data):
|
||||||
"""Solve part 2"""
|
"""Solve part 2"""
|
||||||
return sum(1 for p in data if test_password(p))
|
return sum(1 for p in data if test_password(p))
|
||||||
|
|
||||||
|
|
||||||
def solve(puzzle_input):
|
def solve(puzzle_input):
|
||||||
"""Solve the puzzle for the given input"""
|
"""Solve the puzzle for the given input"""
|
||||||
data = parse(puzzle_input)
|
data = parse(puzzle_input)
|
||||||
|
|
@ -43,6 +55,7 @@ def solve(puzzle_input):
|
||||||
|
|
||||||
return solution1, solution2
|
return solution1, solution2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for path in sys.argv[1:]:
|
for path in sys.argv[1:]:
|
||||||
print(f"{path}:")
|
print(f"{path}:")
|
||||||
|
|
|
||||||
|
|
@ -6,32 +6,28 @@ import day2 as aoc
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
|
||||||
|
# these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "example2").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
def test_parse(example):
|
||||||
def test_parse_example1(example1):
|
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == [
|
assert example == [
|
||||||
aoc.PasswordSpec(first=1, second=3, letter='a', password='abcde'),
|
aoc.PasswordSpec(first=1, second=3, letter="a", password="abcde"),
|
||||||
aoc.PasswordSpec(first=1, second=3, letter='b', password='cdefg'),
|
aoc.PasswordSpec(first=1, second=3, letter="b", password="cdefg"),
|
||||||
aoc.PasswordSpec(first=2, second=9, letter='c', password='ccccccccc')
|
aoc.PasswordSpec(first=2, second=9, letter="c", password="ccccccccc"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
|
||||||
def test_part1_example1(example1):
|
def test_part1(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == 2
|
assert aoc.part1(example) == 2
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
|
||||||
def test_part2_example2(example1):
|
def test_part2(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example1) == 1
|
assert aoc.part2(example) == 1
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,35 @@
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
def parse(puzzle_input: str):
|
def parse(puzzle_input: str):
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
# returns a 2 dimentional array where true means there is a tree there.
|
# returns a 2 dimentional array where true means there is a tree there.
|
||||||
return [[i=='#' for i in j] for j in puzzle_input.splitlines()]
|
return [[i == "#" for i in j] for j in puzzle_input.splitlines()]
|
||||||
|
|
||||||
|
|
||||||
def part1(data):
|
def part1(data):
|
||||||
"""Solve part 1"""
|
"""Solve part 1"""
|
||||||
return solveForSlope(data, 3, 1)
|
return solveForSlope(data, 3, 1)
|
||||||
|
|
||||||
|
|
||||||
def solveForSlope(data, right: int, down: int):
|
def solveForSlope(data, right: int, down: int):
|
||||||
return sum(1 for index, row in enumerate(data[::down]) if row[(right*index)%len(row)])
|
return sum(
|
||||||
|
1 for index, row in enumerate(data[::down]) if row[(right * index) % len(row)]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def part2(data):
|
def part2(data):
|
||||||
"""Solve part 2"""
|
"""Solve part 2"""
|
||||||
return solveForSlope(data,1,1)*solveForSlope(data,3,1)*solveForSlope(data,5,1)*solveForSlope(data,7,1)*solveForSlope(data,1,2)
|
return (
|
||||||
|
solveForSlope(data, 1, 1)
|
||||||
|
* solveForSlope(data, 3, 1)
|
||||||
|
* solveForSlope(data, 5, 1)
|
||||||
|
* solveForSlope(data, 7, 1)
|
||||||
|
* solveForSlope(data, 1, 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def solve(puzzle_input):
|
def solve(puzzle_input):
|
||||||
"""Solve the puzzle for the given input"""
|
"""Solve the puzzle for the given input"""
|
||||||
|
|
@ -30,6 +40,7 @@ def solve(puzzle_input):
|
||||||
|
|
||||||
return solution1, solution2
|
return solution1, solution2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for path in sys.argv[1:]:
|
for path in sys.argv[1:]:
|
||||||
print(f"{path}:")
|
print(f"{path}:")
|
||||||
|
|
|
||||||
|
|
@ -6,38 +6,38 @@ import day3 as aoc
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
|
||||||
|
# these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example.txt").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
def test_parse(example):
|
||||||
def test_parse_example1(example1):
|
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == [[False, False, True, True, False, False, False, False, False, False, False],
|
assert example == [
|
||||||
[True, False, False, False, True, False, False, False, True, False, False],
|
[False, False, True, True, False, False, False, False, False, False, False],
|
||||||
[False, True, False, False, False, False, True, False, False, True, False],
|
[True, False, False, False, True, False, False, False, True, False, False],
|
||||||
[False, False, True, False, True, False, False, False, True, False, True],
|
[False, True, False, False, False, False, True, False, False, True, False],
|
||||||
[False, True, False, False, False, True, True, False, False, True, False],
|
[False, False, True, False, True, False, False, False, True, False, True],
|
||||||
[False, False, True, False, True, True, False, False, False, False, False],
|
[False, True, False, False, False, True, True, False, False, True, False],
|
||||||
[False, True, False, True, False, True, False, False, False, False, True],
|
[False, False, True, False, True, True, False, False, False, False, False],
|
||||||
[False, True, False, False, False, False, False, False, False, False, True],
|
[False, True, False, True, False, True, False, False, False, False, True],
|
||||||
[True, False, True, True, False, False, False, True, False, False, False],
|
[False, True, False, False, False, False, False, False, False, False, True],
|
||||||
[True, False, False, False, True, True, False, False, False, False, True],
|
[True, False, True, True, False, False, False, True, False, False, False],
|
||||||
[False, True, False, False, True, False, False, False, True, False, True]]
|
[True, False, False, False, True, True, False, False, False, False, True],
|
||||||
|
[False, True, False, False, True, False, False, False, True, False, True],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part1_example1(example1):
|
def test_part1(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == 7
|
assert aoc.part1(example) == 7
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part2_example2(example2):
|
def test_part2(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example2) == 336
|
assert aoc.part2(example) == 336
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Passport:
|
class Passport:
|
||||||
|
|
@ -17,105 +17,131 @@ class Passport:
|
||||||
passportId: str | None
|
passportId: str | None
|
||||||
countryId: str | None
|
countryId: str | None
|
||||||
|
|
||||||
def regexUnwrap(match, group:int)->str | None:
|
|
||||||
|
def regexUnwrap(match, group: int) -> str | None:
|
||||||
return match[group] if match is not None else None
|
return match[group] if match is not None else None
|
||||||
|
|
||||||
def parse(puzzle_input: str)->list[Passport]:
|
|
||||||
|
def parse(puzzle_input: str) -> list[Passport]:
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
rawRecords = [i.replace('\n', ' ') for i in puzzle_input.split('\n\n')]
|
rawRecords = [i.replace("\n", " ") for i in puzzle_input.split("\n\n")]
|
||||||
records = []
|
records = []
|
||||||
for rawRecord in rawRecords:
|
for rawRecord in rawRecords:
|
||||||
birthYear=re.search(r'byr:(\S+)', rawRecord)
|
birthYear = re.search(r"byr:(\S+)", rawRecord)
|
||||||
issueYear=re.search(r'iyr:(\S+)', rawRecord)
|
issueYear = re.search(r"iyr:(\S+)", rawRecord)
|
||||||
expirationYear=re.search(r'eyr:(\S+)', rawRecord)
|
expirationYear = re.search(r"eyr:(\S+)", rawRecord)
|
||||||
height=re.search(r'hgt:(\S+)', rawRecord)
|
height = re.search(r"hgt:(\S+)", rawRecord)
|
||||||
hairColor=re.search(r'hcl:(\S+)', rawRecord)
|
hairColor = re.search(r"hcl:(\S+)", rawRecord)
|
||||||
eyeColor=re.search(r'ecl:(\S+)', rawRecord)
|
eyeColor = re.search(r"ecl:(\S+)", rawRecord)
|
||||||
passportId=re.search(r'pid:(\S+)', rawRecord)
|
passportId = re.search(r"pid:(\S+)", rawRecord)
|
||||||
countryId=re.search(r'cid:(\S+)', rawRecord)
|
countryId = re.search(r"cid:(\S+)", rawRecord)
|
||||||
records.append(Passport(
|
records.append(
|
||||||
int(birthYear[1]) if birthYear is not None else None,
|
Passport(
|
||||||
int(issueYear[1]) if issueYear is not None else None,
|
int(birthYear[1]) if birthYear is not None else None,
|
||||||
int(expirationYear[1]) if expirationYear is not None else None,
|
int(issueYear[1]) if issueYear is not None else None,
|
||||||
regexUnwrap(height,1),
|
int(expirationYear[1]) if expirationYear is not None else None,
|
||||||
regexUnwrap(hairColor,1),
|
regexUnwrap(height, 1),
|
||||||
regexUnwrap(eyeColor,1),
|
regexUnwrap(hairColor, 1),
|
||||||
regexUnwrap(passportId,1),
|
regexUnwrap(eyeColor, 1),
|
||||||
regexUnwrap(countryId,1),
|
regexUnwrap(passportId, 1),
|
||||||
))
|
regexUnwrap(countryId, 1),
|
||||||
|
)
|
||||||
|
)
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def lazyCheckPassport(passport: Passport)->bool:
|
|
||||||
|
def lazyCheckPassport(passport: Passport) -> bool:
|
||||||
return (
|
return (
|
||||||
passport.birthYear is not None
|
passport.birthYear is not None
|
||||||
and passport.issueYear is not None
|
and passport.issueYear is not None
|
||||||
and passport.expirationYear is not None
|
and passport.expirationYear is not None
|
||||||
and passport.height is not None
|
and passport.height is not None
|
||||||
and passport.hairColor is not None
|
and passport.hairColor is not None
|
||||||
and passport.eyeColor is not None
|
and passport.eyeColor is not None
|
||||||
and passport.passportId is not None
|
and passport.passportId is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def part1(data):
|
def part1(data):
|
||||||
"""Solve part 1"""
|
"""Solve part 1"""
|
||||||
return sum(1 for record in data if lazyCheckPassport(record))
|
return sum(1 for record in data if lazyCheckPassport(record))
|
||||||
|
|
||||||
def checkBirthYear(passport: Passport)->bool:
|
|
||||||
if passport.birthYear is None: return False
|
|
||||||
return (1920<=passport.birthYear<=2002)
|
|
||||||
|
|
||||||
def checkIssueYear(passport: Passport)->bool:
|
def checkBirthYear(passport: Passport) -> bool:
|
||||||
if passport.issueYear is None: return False
|
if passport.birthYear is None:
|
||||||
return (2010<=passport.issueYear<=2020)
|
return False
|
||||||
|
return 1920 <= passport.birthYear <= 2002
|
||||||
|
|
||||||
def checkExpirationYear(passport: Passport)->bool:
|
|
||||||
if passport.expirationYear is None: return False
|
|
||||||
return (2020<=passport.expirationYear<=2030)
|
|
||||||
|
|
||||||
def checkHeight(passport: Passport)->bool:
|
def checkIssueYear(passport: Passport) -> bool:
|
||||||
if passport.height is None: return False
|
if passport.issueYear is None:
|
||||||
rematch = re.match(r'(\d+)((?:in)|(?:cm))', passport.height)
|
return False
|
||||||
if rematch is None: return False
|
return 2010 <= passport.issueYear <= 2020
|
||||||
|
|
||||||
|
|
||||||
|
def checkExpirationYear(passport: Passport) -> bool:
|
||||||
|
if passport.expirationYear is None:
|
||||||
|
return False
|
||||||
|
return 2020 <= passport.expirationYear <= 2030
|
||||||
|
|
||||||
|
|
||||||
|
def checkHeight(passport: Passport) -> bool:
|
||||||
|
if passport.height is None:
|
||||||
|
return False
|
||||||
|
rematch = re.match(r"(\d+)((?:in)|(?:cm))", passport.height)
|
||||||
|
if rematch is None:
|
||||||
|
return False
|
||||||
number = int(rematch.group(1))
|
number = int(rematch.group(1))
|
||||||
unit = rematch.group(2)
|
unit = rematch.group(2)
|
||||||
if unit == 'in':
|
if unit == "in":
|
||||||
return (59<=number<=76)
|
return 59 <= number <= 76
|
||||||
else:
|
else:
|
||||||
return (150<=number<=193)
|
return 150 <= number <= 193
|
||||||
|
|
||||||
def checkHairColour(passport: Passport)->bool:
|
|
||||||
if passport.hairColor is None: return False
|
|
||||||
return (re.match(r'#[0123456789abcdef]{6}$', passport.hairColor) is not None)
|
|
||||||
|
|
||||||
def checkEyeColour(passport: Passport)->bool:
|
def checkHairColour(passport: Passport) -> bool:
|
||||||
if passport.eyeColor is None: return False
|
if passport.hairColor is None:
|
||||||
return (passport.eyeColor == 'amb'
|
return False
|
||||||
or passport.eyeColor == 'blu'
|
return re.match(r"#[0123456789abcdef]{6}$", passport.hairColor) is not None
|
||||||
or passport.eyeColor == 'brn'
|
|
||||||
or passport.eyeColor == 'gry'
|
|
||||||
or passport.eyeColor == 'grn'
|
|
||||||
or passport.eyeColor == 'hzl'
|
|
||||||
or passport.eyeColor == 'oth'
|
|
||||||
)
|
|
||||||
|
|
||||||
def checkPassportId(passport: Passport)->bool:
|
|
||||||
if passport.passportId is None: return False
|
|
||||||
return (re.match(r'[0-9]{9}$', passport.passportId) is not None)
|
|
||||||
|
|
||||||
def checkPassport(passport: Passport)->bool:
|
def checkEyeColour(passport: Passport) -> bool:
|
||||||
return (checkBirthYear(passport)
|
if passport.eyeColor is None:
|
||||||
and checkIssueYear(passport)
|
return False
|
||||||
and checkExpirationYear(passport)
|
return (
|
||||||
and checkHeight(passport)
|
passport.eyeColor == "amb"
|
||||||
and checkHairColour(passport)
|
or passport.eyeColor == "blu"
|
||||||
and checkEyeColour(passport)
|
or passport.eyeColor == "brn"
|
||||||
and checkPassportId(passport)
|
or passport.eyeColor == "gry"
|
||||||
)
|
or passport.eyeColor == "grn"
|
||||||
|
or passport.eyeColor == "hzl"
|
||||||
|
or passport.eyeColor == "oth"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def checkPassportId(passport: Passport) -> bool:
|
||||||
|
if passport.passportId is None:
|
||||||
|
return False
|
||||||
|
return re.match(r"[0-9]{9}$", passport.passportId) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def checkPassport(passport: Passport) -> bool:
|
||||||
|
return (
|
||||||
|
checkBirthYear(passport)
|
||||||
|
and checkIssueYear(passport)
|
||||||
|
and checkExpirationYear(passport)
|
||||||
|
and checkHeight(passport)
|
||||||
|
and checkHairColour(passport)
|
||||||
|
and checkEyeColour(passport)
|
||||||
|
and checkPassportId(passport)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def part2(data):
|
def part2(data):
|
||||||
"""Solve part 2"""
|
"""Solve part 2"""
|
||||||
return sum(1 for record in data if checkPassport(record))
|
return sum(1 for record in data if checkPassport(record))
|
||||||
|
|
||||||
|
|
||||||
def solve(puzzle_input):
|
def solve(puzzle_input):
|
||||||
"""Solve the puzzle for the given input"""
|
"""Solve the puzzle for the given input"""
|
||||||
data = parse(puzzle_input)
|
data = parse(puzzle_input)
|
||||||
|
|
@ -124,6 +150,7 @@ def solve(puzzle_input):
|
||||||
|
|
||||||
return solution1, solution2
|
return solution1, solution2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for path in sys.argv[1:]:
|
for path in sys.argv[1:]:
|
||||||
print(f"{path}:")
|
print(f"{path}:")
|
||||||
|
|
|
||||||
|
|
@ -7,59 +7,67 @@ from day4 import Passport
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
|
||||||
|
# these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example.txt").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "example2").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
def test_parse(example):
|
||||||
def test_parse_example1(example1):
|
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == [Passport(birthYear=1937,
|
assert example == [
|
||||||
issueYear=2017,
|
Passport(
|
||||||
expirationYear=2020,
|
birthYear=1937,
|
||||||
height='183cm',
|
issueYear=2017,
|
||||||
hairColor='#fffffd',
|
expirationYear=2020,
|
||||||
eyeColor='gry',
|
height="183cm",
|
||||||
passportId='860033327',
|
hairColor="#fffffd",
|
||||||
countryId='147'),
|
eyeColor="gry",
|
||||||
Passport(birthYear=1929,
|
passportId="860033327",
|
||||||
|
countryId="147",
|
||||||
|
),
|
||||||
|
Passport(
|
||||||
|
birthYear=1929,
|
||||||
issueYear=2013,
|
issueYear=2013,
|
||||||
expirationYear=2023,
|
expirationYear=2023,
|
||||||
height=None,
|
height=None,
|
||||||
hairColor='#cfa07d',
|
hairColor="#cfa07d",
|
||||||
eyeColor='amb',
|
eyeColor="amb",
|
||||||
passportId='028048884',
|
passportId="028048884",
|
||||||
countryId='350'),
|
countryId="350",
|
||||||
Passport(birthYear=1931,
|
),
|
||||||
|
Passport(
|
||||||
|
birthYear=1931,
|
||||||
issueYear=2013,
|
issueYear=2013,
|
||||||
expirationYear=2024,
|
expirationYear=2024,
|
||||||
height='179cm',
|
height="179cm",
|
||||||
hairColor='#ae17e1',
|
hairColor="#ae17e1",
|
||||||
eyeColor='brn',
|
eyeColor="brn",
|
||||||
passportId='760753108',
|
passportId="760753108",
|
||||||
countryId=None),
|
countryId=None,
|
||||||
Passport(birthYear=None,
|
),
|
||||||
|
Passport(
|
||||||
|
birthYear=None,
|
||||||
issueYear=2011,
|
issueYear=2011,
|
||||||
expirationYear=2025,
|
expirationYear=2025,
|
||||||
height='59in',
|
height="59in",
|
||||||
hairColor='#cfa07d',
|
hairColor="#cfa07d",
|
||||||
eyeColor='brn',
|
eyeColor="brn",
|
||||||
passportId='166559648',
|
passportId="166559648",
|
||||||
countryId=None)]
|
countryId=None,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part1_example1(example1):
|
def test_part1(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == 2
|
assert aoc.part1(example) == 2
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part2_example2(example1):
|
def test_part2(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example1) == 2
|
assert aoc.part2(example) == 2
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,39 @@
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
def row_to_int(string: str)->int:
|
|
||||||
return int(string.replace('F','0').replace('B','1'),2)
|
|
||||||
|
|
||||||
def col_to_int(string: str)->int:
|
def row_to_int(string: str) -> int:
|
||||||
return int(string.replace('L','0').replace('R','1'),2)
|
return int(string.replace("F", "0").replace("B", "1"), 2)
|
||||||
|
|
||||||
|
|
||||||
|
def col_to_int(string: str) -> int:
|
||||||
|
return int(string.replace("L", "0").replace("R", "1"), 2)
|
||||||
|
|
||||||
|
|
||||||
def parse(puzzle_input: str):
|
def parse(puzzle_input: str):
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
rows_and_cols = [(row_to_int(x[:7]),col_to_int(x[7:])) for x in puzzle_input.splitlines()]
|
rows_and_cols = [
|
||||||
return set([x[0]*8+x[1] for x in rows_and_cols])
|
(row_to_int(x[:7]), col_to_int(x[7:])) for x in puzzle_input.splitlines()
|
||||||
|
]
|
||||||
|
return set([x[0] * 8 + x[1] for x in rows_and_cols])
|
||||||
|
|
||||||
|
|
||||||
def part1(data):
|
def part1(data):
|
||||||
"""Solve part 1"""
|
"""Solve part 1"""
|
||||||
return max(data)
|
return max(data)
|
||||||
|
|
||||||
|
|
||||||
def part2(data):
|
def part2(data):
|
||||||
"""Solve part 2"""
|
"""Solve part 2"""
|
||||||
for row in range(128):
|
for row in range(128):
|
||||||
for col in range(8):
|
for col in range(8):
|
||||||
potentialID=row*8+col
|
potentialID = row * 8 + col
|
||||||
if potentialID not in data:
|
if potentialID not in data:
|
||||||
if (potentialID+1 in data) and (potentialID-1 in data):
|
if (potentialID + 1 in data) and (potentialID - 1 in data):
|
||||||
return potentialID
|
return potentialID
|
||||||
|
|
||||||
|
|
||||||
def solve(puzzle_input):
|
def solve(puzzle_input):
|
||||||
"""Solve the puzzle for the given input"""
|
"""Solve the puzzle for the given input"""
|
||||||
data = parse(puzzle_input)
|
data = parse(puzzle_input)
|
||||||
|
|
@ -38,6 +43,7 @@ def solve(puzzle_input):
|
||||||
|
|
||||||
return solution1, solution2
|
return solution1, solution2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for path in sys.argv[1:]:
|
for path in sys.argv[1:]:
|
||||||
print(f"{path}:")
|
print(f"{path}:")
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,24 @@ import day5 as aoc
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
|
||||||
|
# these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example.txt").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "input").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
def test_parse(example):
|
||||||
def test_parse_example1(example1):
|
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == {119, 357, 567, 820}
|
assert example == {119, 357, 567, 820}
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
|
||||||
def test_part1_example1(example1):
|
def test_part1(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == 820
|
assert aoc.part1(example) == 820
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
|
||||||
def test_part2_example2(example2):
|
def test_part2(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example2) == 640
|
assert aoc.part2(example) == 640
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
def parse(puzzle_input: str):
|
def parse(puzzle_input: str):
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
records = [[list(y) for y in x.splitlines()] for x in puzzle_input.split('\n\n')]
|
records = [[list(y) for y in x.splitlines()] for x in puzzle_input.split("\n\n")]
|
||||||
return records
|
return records
|
||||||
|
|
||||||
|
|
||||||
def part1(data):
|
def part1(data):
|
||||||
"""Solve part 1"""
|
"""Solve part 1"""
|
||||||
results = []
|
results = []
|
||||||
|
|
@ -21,6 +20,7 @@ def part1(data):
|
||||||
results.append(len(result))
|
results.append(len(result))
|
||||||
return sum(results)
|
return sum(results)
|
||||||
|
|
||||||
|
|
||||||
def part2(data):
|
def part2(data):
|
||||||
"""Solve part 2"""
|
"""Solve part 2"""
|
||||||
results = []
|
results = []
|
||||||
|
|
@ -31,6 +31,7 @@ def part2(data):
|
||||||
results.append(len(result))
|
results.append(len(result))
|
||||||
return sum(results)
|
return sum(results)
|
||||||
|
|
||||||
|
|
||||||
def solve(puzzle_input):
|
def solve(puzzle_input):
|
||||||
"""Solve the puzzle for the given input"""
|
"""Solve the puzzle for the given input"""
|
||||||
data = parse(puzzle_input)
|
data = parse(puzzle_input)
|
||||||
|
|
@ -39,6 +40,7 @@ def solve(puzzle_input):
|
||||||
|
|
||||||
return solution1, solution2
|
return solution1, solution2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for path in sys.argv[1:]:
|
for path in sys.argv[1:]:
|
||||||
print(f"{path}:")
|
print(f"{path}:")
|
||||||
|
|
|
||||||
|
|
@ -6,50 +6,33 @@ import day6 as aoc
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
|
||||||
|
# these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example.txt").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "example2").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_parse_example1(example1):
|
def test_parse(example):
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == [
|
assert example == [
|
||||||
[
|
[["a", "b", "c"]],
|
||||||
['a', 'b', 'c']
|
[["a"], ["b"], ["c"]],
|
||||||
],
|
[["a", "b"], ["a", "c"]],
|
||||||
[
|
[["a"], ["a"], ["a"], ["a"]],
|
||||||
['a'],
|
[["b"]],
|
||||||
['b'],
|
]
|
||||||
['c']
|
|
||||||
],
|
|
||||||
[
|
|
||||||
['a', 'b'],
|
|
||||||
['a', 'c']
|
|
||||||
],
|
|
||||||
[
|
|
||||||
['a'],
|
|
||||||
['a'],
|
|
||||||
['a'],
|
|
||||||
['a']
|
|
||||||
],
|
|
||||||
[
|
|
||||||
['b']
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part1_example1(example1):
|
def test_part1(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == 11
|
assert aoc.part1(example) == 11
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.skip(reason="Not implemented")
|
# @pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part2_example2(example1):
|
def test_part2(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example1) == 6
|
assert aoc.part2(example) == 6
|
||||||
|
|
|
||||||
0
2020/template/example.txt
Normal file
0
2020/template/example.txt
Normal file
0
2020/template/input.txt
Normal file
0
2020/template/input.txt
Normal file
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
def parse(puzzle_input: str):
|
def parse(puzzle_input: str):
|
||||||
"""Parse input"""
|
"""Parse input"""
|
||||||
|
|
@ -2,32 +2,27 @@
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import pytest
|
import pytest
|
||||||
import template as aoc
|
import temp as aoc
|
||||||
|
|
||||||
PUZZLE_DIR = pathlib.Path(__file__).parent
|
PUZZLE_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
#these test fixtures setup the test, mainly by reading the filename into a string in this simple case.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example1():
|
def example():
|
||||||
puzzle_input = (PUZZLE_DIR / "example1").read_text().strip()
|
puzzle_input = (PUZZLE_DIR / "example.txt").read_text().strip()
|
||||||
return aoc.parse(puzzle_input)
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def example2():
|
|
||||||
puzzle_input = (PUZZLE_DIR / "example2").read_text().strip()
|
|
||||||
return aoc.parse(puzzle_input)
|
return aoc.parse(puzzle_input)
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Not implemented")
|
@pytest.mark.skip(reason="Not implemented")
|
||||||
def test_parse_example1(example1):
|
def test_parse_example(example):
|
||||||
"""Test that input is parsed properly"""
|
"""Test that input is parsed properly"""
|
||||||
assert example1 == ...
|
assert example == ...
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Not implemented")
|
@pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part1_example1(example1):
|
def test_part1_example(example):
|
||||||
"""Test part 1 on example input"""
|
"""Test part 1 on example input"""
|
||||||
assert aoc.part1(example1) == ...
|
assert aoc.part1(example) == ...
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Not implemented")
|
@pytest.mark.skip(reason="Not implemented")
|
||||||
def test_part2_example2(example2):
|
def test_part2_example(example):
|
||||||
"""Test part 2 on example input"""
|
"""Test part 2 on example input"""
|
||||||
assert aoc.part2(example2) == ...
|
assert aoc.part2(example) == ...
|
||||||
Loading…
Add table
Add a link
Reference in a new issue