Compare commits
6 Commits
5ee2dd1812
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e64eef9a2 | |||
| 4681a782b5 | |||
| d78e9d6c63 | |||
| f4aa799bac | |||
| 95cabafd30 | |||
| 2ed5dc8b97 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
src/Year_2015/files/
|
||||||
|
|
||||||
.coverage
|
.coverage
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.5.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 23.10.0
|
rev: v0.15.16
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
|
||||||
# Ruff version.
|
|
||||||
rev: "v0.1.1"
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
args: [--fix]
|
||||||
|
- id: ruff-format
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
system
|
3.14
|
||||||
|
|||||||
@@ -1,59 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.ruff]
|
||||||
name = "template"
|
target-version = "py314"
|
||||||
version = "0.1.0"
|
|
||||||
description = ""
|
|
||||||
authors = ["daviddoji <daviddoji@pm.me>"]
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = ">=3.9,<3.12"
|
|
||||||
numpy = "^1.21.4"
|
|
||||||
sympy = "^1.9"
|
|
||||||
scipy = "^1.8.0"
|
|
||||||
networkx = "^2.7.1"
|
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
|
||||||
pytest = "^5.2"
|
|
||||||
pytest-cov = "^3.0.0"
|
|
||||||
pre-commit = "^2.15.0"
|
|
||||||
flake8 = "^4.0.1"
|
|
||||||
mypy = "^0.910"
|
|
||||||
isort = "^5.9.3"
|
|
||||||
black = {version = "^21.10b0", allow-prereleases = true}
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core>=1.0.0"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
||||||
|
|
||||||
[tool.isort]
|
|
||||||
multi_line_output = 3
|
|
||||||
include_trailing_comma = true
|
|
||||||
force_grid_wrap = 0
|
|
||||||
use_parentheses = true
|
|
||||||
line_length = 79
|
|
||||||
|
|
||||||
[tool.black]
|
|
||||||
line-length = 79
|
|
||||||
target-version = ['py38']
|
|
||||||
include = '\.pyi?$'
|
|
||||||
exclude = '''
|
|
||||||
|
|
||||||
(
|
|
||||||
/(
|
|
||||||
\.eggs # exclude a few common directories in the
|
|
||||||
| \.git # root of the project
|
|
||||||
| \.hg
|
|
||||||
| \.mypy_cache
|
|
||||||
| \.tox
|
|
||||||
| \.venv
|
|
||||||
| _build
|
|
||||||
| buck-out
|
|
||||||
| build
|
|
||||||
| dist
|
|
||||||
)/
|
|
||||||
| foo.py # also separately exclude a file named foo.py in
|
|
||||||
# the root of the project
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
ignore = ["E402"]
|
ignore = ["E402"]
|
||||||
|
extend-select = ["I"] # enables import sorting (isort-equivalent)
|
||||||
|
|||||||
@@ -32,12 +32,12 @@
|
|||||||
# To what floor do the instructions take Santa?
|
# To what floor do the instructions take Santa?
|
||||||
|
|
||||||
with open("files/P1.txt") as f:
|
with open("files/P1.txt") as f:
|
||||||
directions = [line for line in f.read().strip().split()]
|
directions = f.read().strip()
|
||||||
|
|
||||||
|
|
||||||
def part_1() -> None:
|
def part_1() -> None:
|
||||||
up = int(directions[0].count("("))
|
up = directions.count("(")
|
||||||
down = int(directions[0].count(")"))
|
down = directions.count(")")
|
||||||
print(f"The floor is {up - down}")
|
print(f"The floor is {up - down}")
|
||||||
|
|
||||||
|
|
||||||
@@ -58,14 +58,15 @@ def part_1() -> None:
|
|||||||
|
|
||||||
def part_2() -> None:
|
def part_2() -> None:
|
||||||
floor = 0
|
floor = 0
|
||||||
for idx, char in enumerate(directions[0], start=1):
|
for idx, char in enumerate(directions, start=1):
|
||||||
if char == "(":
|
if char == "(":
|
||||||
floor += 1
|
floor += 1
|
||||||
elif char == ")":
|
elif char == ")":
|
||||||
floor -= 1
|
floor -= 1
|
||||||
if floor == -1:
|
if floor == -1:
|
||||||
print(f"The position is {idx}")
|
print(f"The position is {idx}")
|
||||||
break
|
return
|
||||||
|
print("Basement was never reached!")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -21,41 +21,29 @@
|
|||||||
# ^v^v^v^v^v delivers a bunch of presents to some very lucky children at
|
# ^v^v^v^v^v delivers a bunch of presents to some very lucky children at
|
||||||
# only 2 houses.
|
# only 2 houses.
|
||||||
|
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
with open("files/P3.txt") as f:
|
with open("files/P3.txt") as f:
|
||||||
moves = [line for line in f.read().strip().split()][0]
|
moves = [line for line in f.read().strip()]
|
||||||
|
|
||||||
|
|
||||||
def move_up(pos: Tuple[int, int]) -> Tuple[int, int]:
|
Position = tuple[int, int]
|
||||||
return pos[0], pos[1] + 1
|
DIRECTIONS: dict[str, Position] = {
|
||||||
|
"^": (0, 1),
|
||||||
|
"v": (0, -1),
|
||||||
def move_down(pos: Tuple[int, int]) -> Tuple[int, int]:
|
"<": (-1, 0),
|
||||||
return pos[0], pos[1] - 1
|
">": (1, 0),
|
||||||
|
}
|
||||||
|
|
||||||
def move_left(pos: Tuple[int, int]) -> Tuple[int, int]:
|
|
||||||
return pos[0] - 1, pos[1]
|
|
||||||
|
|
||||||
|
|
||||||
def move_right(pos: Tuple[int, int]) -> Tuple[int, int]:
|
|
||||||
return pos[0] + 1, pos[1]
|
|
||||||
|
|
||||||
|
|
||||||
def part_1() -> None:
|
def part_1() -> None:
|
||||||
pos = [(0, 0)]
|
x, y = 0, 0
|
||||||
|
visited: set[Position] = {(x, y)}
|
||||||
for move in moves:
|
for move in moves:
|
||||||
if move == "^":
|
delta_x, delta_y = DIRECTIONS[move]
|
||||||
pos.append(move_up(pos[-1]))
|
x += delta_x
|
||||||
elif move == "v":
|
y += delta_y
|
||||||
pos.append(move_down(pos[-1]))
|
visited.add((x, y))
|
||||||
elif move == "<":
|
|
||||||
pos.append(move_left(pos[-1]))
|
|
||||||
elif move == ">":
|
|
||||||
pos.append(move_right(pos[-1]))
|
|
||||||
|
|
||||||
print(f"{len(set(pos))} houses receive at least one present")
|
print(f"{len(visited)} houses receive at least one present")
|
||||||
|
|
||||||
|
|
||||||
# --- Part Two ---
|
# --- Part Two ---
|
||||||
@@ -81,33 +69,23 @@ def part_1() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def part_2():
|
def part_2():
|
||||||
santa = [(0, 0)]
|
x_santa, y_santa = 0, 0
|
||||||
robo_santa = [(0, 0)]
|
x_robo, y_robo = 0, 0
|
||||||
for idx, move in enumerate(moves):
|
visited: set[Position] = {(0, 0)}
|
||||||
if move == "^":
|
|
||||||
if idx % 2 == 0:
|
|
||||||
santa.append(move_up(santa[-1]))
|
|
||||||
else:
|
|
||||||
robo_santa.append(move_up(robo_santa[-1]))
|
|
||||||
elif move == "v":
|
|
||||||
if idx % 2 == 0:
|
|
||||||
santa.append(move_down(santa[-1]))
|
|
||||||
else:
|
|
||||||
robo_santa.append(move_down(robo_santa[-1]))
|
|
||||||
elif move == "<":
|
|
||||||
if idx % 2 == 0:
|
|
||||||
santa.append(move_left(santa[-1]))
|
|
||||||
else:
|
|
||||||
robo_santa.append(move_left(robo_santa[-1]))
|
|
||||||
elif move == ">":
|
|
||||||
if idx % 2 == 0:
|
|
||||||
santa.append(move_right(santa[-1]))
|
|
||||||
else:
|
|
||||||
robo_santa.append(move_right(robo_santa[-1]))
|
|
||||||
|
|
||||||
print(
|
for idx, move in enumerate(moves):
|
||||||
f"{len(set(santa + robo_santa))} houses receive at least one present"
|
delta_x, delta_y = DIRECTIONS[move]
|
||||||
)
|
# santa's turn
|
||||||
|
if idx % 2:
|
||||||
|
x_santa += delta_x
|
||||||
|
y_santa += delta_y
|
||||||
|
visited.add((x_santa, y_santa))
|
||||||
|
else:
|
||||||
|
x_robo += delta_x
|
||||||
|
y_robo += delta_y
|
||||||
|
visited.add((x_robo, y_robo))
|
||||||
|
|
||||||
|
print(f"{len(visited)} houses receive at least one present")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -18,21 +18,30 @@
|
|||||||
# an MD5 hash starting with five zeroes is 1048970; that is, the MD5 hash of
|
# an MD5 hash starting with five zeroes is 1048970; that is, the MD5 hash of
|
||||||
# pqrstuv1048970 looks like 000006136ef....
|
# pqrstuv1048970 looks like 000006136ef....
|
||||||
|
|
||||||
# Your puzzle input is yzbqklnj.
|
|
||||||
|
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
_input = "yzbqklnj"
|
with open("files/P4.txt") as f:
|
||||||
|
secret_key = f.read().strip()
|
||||||
|
|
||||||
|
key_bytes = secret_key.encode()
|
||||||
|
|
||||||
|
|
||||||
def part_1() -> None:
|
def part_1() -> None:
|
||||||
end = "9" * len(_input)
|
padding = 5
|
||||||
for num in range(int(end)):
|
prefix = "0" * padding
|
||||||
testing = _input + str(num)
|
base_hash = md5(key_bytes)
|
||||||
result = md5(testing.encode()).hexdigest()
|
|
||||||
if result.startswith("0" * 5):
|
for num in count(1):
|
||||||
print(f"The lowest positive number is {num}")
|
digest_ctx = base_hash.copy()
|
||||||
break
|
digest_ctx.update(str(num).encode())
|
||||||
|
digest = digest_ctx.hexdigest()
|
||||||
|
if digest.startswith(prefix):
|
||||||
|
print(f"The lowest positive number starting with {padding} zeroes is {num}")
|
||||||
|
return
|
||||||
|
|
||||||
|
raise RuntimeError("No valid number found")
|
||||||
|
|
||||||
|
|
||||||
# --- Part Two ---
|
# --- Part Two ---
|
||||||
@@ -41,13 +50,19 @@ def part_1() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def part_2() -> None:
|
def part_2() -> None:
|
||||||
end = "9" * len(_input)
|
padding = 6
|
||||||
for num in range(int(end)):
|
prefix = "0" * padding
|
||||||
testing = _input + str(num)
|
base_hash = md5(key_bytes)
|
||||||
result = md5(testing.encode()).hexdigest()
|
|
||||||
if result.startswith("0" * 6):
|
for num in count(1):
|
||||||
print(f"The lowest positive number is {num}")
|
digest_ctx = base_hash.copy()
|
||||||
break
|
digest_ctx.update(str(num).encode())
|
||||||
|
digest = digest_ctx.hexdigest()
|
||||||
|
if digest.startswith(prefix):
|
||||||
|
print(f"The lowest positive number starting with {padding} zeroes is {num}")
|
||||||
|
return
|
||||||
|
|
||||||
|
raise RuntimeError("No valid number found")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -25,44 +25,32 @@
|
|||||||
|
|
||||||
# How many strings are nice?
|
# How many strings are nice?
|
||||||
|
|
||||||
from collections import Counter
|
|
||||||
|
|
||||||
with open("files/P5.txt") as f:
|
with open("files/P5.txt") as f:
|
||||||
strings = [line for line in f.read().strip().split()]
|
strings = [line for line in f.read().strip().split()]
|
||||||
|
|
||||||
|
VOWELS = set("aeiou")
|
||||||
|
FORBIDDEN = ("ab", "cd", "pq", "xy")
|
||||||
|
|
||||||
|
|
||||||
def has_enough_vowels(s: str) -> bool:
|
def has_enough_vowels(s: str) -> bool:
|
||||||
num_vowels = sum(s.lower().count(v) for v in "aeiou")
|
return sum(char in VOWELS for char in s) >= 3
|
||||||
if num_vowels >= 3:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def has_double_letter(s: str) -> bool:
|
def has_double_letter(s: str) -> bool:
|
||||||
double_letters = sum(1 for i, j in zip(s, s[1:]) if i == j)
|
return any(left == right for left, right in zip(s, s[1:]))
|
||||||
if double_letters >= 1:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def has_substring(s: str) -> bool:
|
def has_substring(s: str) -> bool:
|
||||||
substrings = ["ab", "cd", "pq", "xy"]
|
return any(substring in s for substring in FORBIDDEN)
|
||||||
for substring in substrings:
|
|
||||||
if substring in s:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def part_1() -> None:
|
def part_1() -> None:
|
||||||
nice = 0
|
nice = sum(
|
||||||
for string in strings:
|
has_enough_vowels(string)
|
||||||
if (
|
and has_double_letter(string)
|
||||||
has_enough_vowels(string)
|
and not has_substring(string)
|
||||||
and has_double_letter(string)
|
for string in strings
|
||||||
and not has_substring(string)
|
)
|
||||||
):
|
|
||||||
nice += 1
|
|
||||||
|
|
||||||
print(f"There are {nice} nice strings")
|
print(f"There are {nice} nice strings")
|
||||||
|
|
||||||
|
|
||||||
@@ -103,18 +91,11 @@ def has_pairs(s: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def has_letter_between(s: str) -> bool:
|
def has_letter_between(s: str) -> bool:
|
||||||
_my_str = zip(s, s[1:], s[2:])
|
return any(a == c for a, _, c in zip(s, s[1:], s[2:]))
|
||||||
for triple in _my_str:
|
|
||||||
if triple[0] == triple[-1]:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def part_2() -> None:
|
def part_2() -> None:
|
||||||
nice = 0
|
nice = sum(has_pairs(string) and has_letter_between(string) for string in strings)
|
||||||
for string in strings:
|
|
||||||
if has_pairs(string) and has_letter_between(string):
|
|
||||||
nice += 1
|
|
||||||
print(f"There are {nice} nicer strings")
|
print(f"There are {nice} nicer strings")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user