Skip to content

Commit

Permalink
Indent AoC 2024 with 2 spaces
Browse files Browse the repository at this point in the history
  • Loading branch information
loociano committed Dec 4, 2024
1 parent 2778ab2 commit 4ef4b65
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 144 deletions.
5 changes: 4 additions & 1 deletion aoc2024/src/day01/python/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

_LocationIds: TypeAlias = Sequence[int]


def _parse_input(input: Sequence[str]) -> tuple[_LocationIds, _LocationIds]:
"""Converts puzzle input into 2 sequences of location IDs.
Expand All @@ -34,6 +35,7 @@ def _parse_input(input: Sequence[str]) -> tuple[_LocationIds, _LocationIds]:
location_ids2.append(id2)
return tuple(location_ids1), tuple(location_ids2)


def calculate_distance(input: Sequence[str]) -> int:
"""
Calculates total distance between 2 sequences of location IDs.
Expand All @@ -54,6 +56,7 @@ def calculate_distance(input: Sequence[str]) -> int:
abs(id1 - id2)
for id1, id2 in zip(sorted_ids1, sorted_ids2)) # t:O(n)


def calculate_similarity_score(input: Sequence[str]) -> int:
"""
Calculates similarity score between 2 sequences of location IDs.
Expand All @@ -70,4 +73,4 @@ def calculate_similarity_score(input: Sequence[str]) -> int:
location_ids1, location_ids2 = _parse_input(input) # t:O(n)
counter = Counter(location_ids2)
# Calculate similarity score:
return sum(id * counter.get(id, 0) for id in location_ids1) # t:O(n)
return sum(id * counter.get(id, 0) for id in location_ids1) # t:O(n)
107 changes: 56 additions & 51 deletions aoc2024/src/day02/python/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,67 +16,72 @@
# A report has a variable number of levels, represented by integers.
type Report = tuple[int, ...]


def _parse_input(input: Sequence[str]) -> Sequence[Report]:
"""Converts puzzle input into a sequence of reports.
"""Converts puzzle input into a sequence of reports.
n=len(input), k=len(report)
Time complexity: O(kn)
Space complexity: O(kn)
"""
return tuple(tuple(int(level) for level in line.split()) for line in input)

n=len(input), k=len(report)
Time complexity: O(kn)
Space complexity: O(kn)
"""
return tuple(tuple(int(level) for level in line.split()) for line in input)

def _is_inc_or_dec(report: Report) -> bool:
last = None
last_sign = None
for curr in report:
if last is not None:
if last == curr:
return False
curr_sign = curr - last > 0
if last_sign is not None:
if curr_sign != last_sign:
return False
last_sign = curr_sign
last = curr
return True

def _are_adj_diff_safe(report: Report, safety_range = (1, 3)) -> bool:
last = None
for curr in report:
if last is not None:
diff = abs(last - curr)
if diff < safety_range[0] or diff > safety_range[1]:
return False
last = curr
return True
last = None
last_sign = None
for curr in report:
if last is not None:
if last == curr:
return False
curr_sign = curr - last > 0
if last_sign is not None:
if curr_sign != last_sign:
return False
last_sign = curr_sign
last = curr
return True


def _are_adj_diff_safe(report: Report, safety_range=(1, 3)) -> bool:
last = None
for curr in report:
if last is not None:
diff = abs(last - curr)
if diff < safety_range[0] or diff > safety_range[1]:
return False
last = curr
return True


def _could_be_safe(report: Report) -> bool:
"""Returns true if a report could be safe by removing one level."""
for i in range(len(report)):
# Drop element and test report safety.
test_report = report[:i] + report[i+1:]
if _is_inc_or_dec(test_report) and _are_adj_diff_safe(test_report):
return True
return False
"""Returns true if a report could be safe by removing one level."""
for i in range(len(report)):
# Drop element and test report safety.
test_report = report[:i] + report[i + 1:]
if _is_inc_or_dec(test_report) and _are_adj_diff_safe(test_report):
return True
return False


def _is_safe(report: Report, tolerate_bad_level) -> bool:
"""Returns true if a report is safe.
"""Returns true if a report is safe.
A report is safe the two conditions are true:
a) The levels are either all increasing or all decreasing.
b) Any two adjacent levels differ by at least one and at most three.
"""
is_safe = _is_inc_or_dec(report) and _are_adj_diff_safe(report)
if not is_safe and tolerate_bad_level:
return _could_be_safe(report)
return is_safe

A report is safe the two conditions are true:
a) The levels are either all increasing or all decreasing.
b) Any two adjacent levels differ by at least one and at most three.
"""
is_safe = _is_inc_or_dec(report) and _are_adj_diff_safe(report)
if not is_safe and tolerate_bad_level:
return _could_be_safe(report)
return is_safe

def num_safe_reports(input: Sequence[str], tolerate_bad_level=False) -> int:
"""Returns the number of safe reports.
"""Returns the number of safe reports.
n=len(input), k=len(report)
Time complexity: O(3k^2n) = O(k^2n)
Space complexity: O(n+k)
"""
return sum(_is_safe(report, tolerate_bad_level) for report in _parse_input(input))
n=len(input), k=len(report)
Time complexity: O(3k^2n) = O(k^2n)
Space complexity: O(n+k)
"""
return sum(_is_safe(report, tolerate_bad_level) for report in _parse_input(input))
69 changes: 37 additions & 32 deletions aoc2024/src/day03/python/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,49 @@
from functools import reduce
from typing import Sequence


def _to_muls(matches: Sequence[str]) -> Sequence[tuple[int, ...]]:
# Ignore match[0] which contains the raw pair ('1','2') if mul(1,2).
# match[1] and match[2] capture the individual digits in pair (as string).
return tuple(tuple(map(int, (match[1], match[2]))) for match in matches) # t:O(n)
# Ignore match[0] which contains the raw pair ('1','2') if mul(1,2).
# match[1] and match[2] capture the individual digits in pair (as string).
return tuple(tuple(map(int, (match[1], match[2]))) for match in matches) # t:O(n)


def _calculate(muls: Sequence[tuple[int, ...]]) -> int:
return sum(reduce(lambda a,b: a*b, mul) for mul in muls) # t:O(n)
return sum(reduce(lambda a, b: a * b, mul) for mul in muls) # t:O(n)


def _find_muls(string: str) -> Sequence[str]:
return re.findall(r'mul\(((\d+),(\d+))\)', string) # t:O(n)
return re.findall(r'mul\(((\d+),(\d+))\)', string) # t:O(n)


def calculate(input: str) -> int:
"""
Sums all the valid multiplications.
Example valid multiplications: 'mul(1,2)', 'mul(123,456)'
"""
matches = _find_muls(input)
if matches is None:
raise ValueError('No multiplications were found.')
return _calculate(_to_muls(matches))
"""
Sums all the valid multiplications.
Example valid multiplications: 'mul(1,2)', 'mul(123,456)'
"""
matches = _find_muls(input)
if matches is None:
raise ValueError('No multiplications were found.')
return _calculate(_to_muls(matches))


def calculate2(input: str) -> int:
"""
Sums all valid multiplications between do() and don't() operations.
At the beginning of the program, mul instructions are enabled.
Example: mul(1,1)do()mul(2,2)don't() -> 1*1+2*2=5
"""
# Assumption: do() does not appear before don't(). Verified on my puzzle input.
result = 0
first_dont_pos = input.find('don\'t()')
result += calculate(input[:first_dont_pos])

sub_input = input[first_dont_pos+len('don\'t()'):] # Trim beginning.
while sub_input.find('do()') != -1:
sub_input = sub_input[sub_input.find('do()'):] # Trim anything before first do().
dont_pos = sub_input.find('don\'t()') # Find the next don't().
result += calculate(sub_input[:dont_pos]) # Calculate muls between do() and don't()
sub_input = sub_input[dont_pos+len('don\'t()'):] # Trim anything before don't()
# Assumption: last operation is don't(). Verified on my puzzle input.
return result
"""
Sums all valid multiplications between do() and don't() operations.
At the beginning of the program, mul instructions are enabled.
Example: mul(1,1)do()mul(2,2)don't() -> 1*1+2*2=5
"""
# Assumption: do() does not appear before don't(). Verified on my puzzle input.
result = 0
first_dont_pos = input.find('don\'t()')
result += calculate(input[:first_dont_pos])

sub_input = input[first_dont_pos + len('don\'t()'):] # Trim beginning.
while sub_input.find('do()') != -1:
sub_input = sub_input[sub_input.find('do()'):] # Trim anything before first do().
dont_pos = sub_input.find('don\'t()') # Find the next don't().
result += calculate(sub_input[:dont_pos]) # Calculate muls between do() and don't()
sub_input = sub_input[dont_pos + len('don\'t()'):] # Trim anything before don't()
# Assumption: last operation is don't(). Verified on my puzzle input.
return result
123 changes: 63 additions & 60 deletions aoc2024/src/day04/python/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,70 +13,73 @@
# limitations under the License.
from typing import Sequence


def _extract_all_lines(input: Sequence[str]) -> Sequence[str]:
"""Breaks down the input grid into lines to facilitate word search.
Time complexity: O(n) + O(nm) + O(2m*n) + O(2m*n) = O(nm)
Space complexity: O(n) + O(m) + O(m+n) + O(m+n) = O(3m+3n) = O(m+n)
"""
lines = []
width = len(input[0])
height = len(input)
# Horizontals
for line in input:
lines.append(line)
# Verticals
for x in range(width):
vertical = []
for y in range(height):
vertical.append(input[y][x])
lines.append(''.join(vertical))
# Decreasing diagonals
for delta in range(-width+2, width):
diagonal = []
for y in range(height):
if 0 <= y+delta < width:
diagonal.append(input[y][y+delta])
lines.append(''.join(diagonal))
# Increasing diagonals
for delta in range(-width, width-2):
diagonal = []
for y in range(height-1, -1, -1):
if 0 <= width-y+delta < width:
diagonal.append(input[y][width-y+delta])
lines.append(''.join(diagonal))
return lines
"""Breaks down the input grid into lines to facilitate word search.
Time complexity: O(n) + O(nm) + O(2m*n) + O(2m*n) = O(nm)
Space complexity: O(n) + O(m) + O(m+n) + O(m+n) = O(3m+3n) = O(m+n)
"""
lines = []
width = len(input[0])
height = len(input)
# Horizontals
for line in input:
lines.append(line)
# Verticals
for x in range(width):
vertical = []
for y in range(height):
vertical.append(input[y][x])
lines.append(''.join(vertical))
# Decreasing diagonals
for delta in range(-width + 2, width):
diagonal = []
for y in range(height):
if 0 <= y + delta < width:
diagonal.append(input[y][y + delta])
lines.append(''.join(diagonal))
# Increasing diagonals
for delta in range(-width, width - 2):
diagonal = []
for y in range(height - 1, -1, -1):
if 0 <= width - y + delta < width:
diagonal.append(input[y][width - y + delta])
lines.append(''.join(diagonal))
return lines


def count_xmas_words(input: Sequence[str]) -> int:
"""Counts occurrences of the word XMAS in a grid.
The word XMAS may appear horizontally, vertically and diagonally.
It may appear reversed SMAX too.
"""Counts occurrences of the word XMAS in a grid.
The word XMAS may appear horizontally, vertically and diagonally.
It may appear reversed SMAX too.
Time complexity: O(n*nm) + O(m+n) = O(n^2m)
Space complexity: O(n+m)
"""
lines = _extract_all_lines(input)
return sum(line.count('XMAS') for line in lines) + sum(line.count('SAMX') for line in lines)

Time complexity: O(n*nm) + O(m+n) = O(n^2m)
Space complexity: O(n+m)
"""
lines = _extract_all_lines(input)
return sum(line.count('XMAS') for line in lines) + sum(line.count('SAMX') for line in lines)

def count_xmas_shapes(input: Sequence[str]) -> int:
"""Counts occurrences of the X-MAS shape in a grid.
There are 4 possible X-MAS shapes:
M S S S M M S M
A A A A
M S M M S S S M
"""Counts occurrences of the X-MAS shape in a grid.
There are 4 possible X-MAS shapes:
M S S S M M S M
A A A A
M S M M S S S M
Time complexity: O(nm)
Space complexity: O(1)
"""
width = len(input[0])
height = len(input)
counter = 0
for y in range(height):
for x in range(width):
# X-MAS shape must fit within bounds.
if 0 < y < height-1 and 0 < x < width-1 and input[y][x] == 'A':
found_shape = (((input[y-1][x-1] == 'M' and input[y+1][x+1] == 'S')
or (input[y-1][x-1] == 'S' and input[y+1][x+1] == 'M'))
and ((input[y+1][x-1] == 'M' and input[y-1][x+1] == 'S')
or (input[y+1][x-1] == 'S' and input[y-1][x+1] == 'M')))
counter += 1 if found_shape else 0
return counter
Time complexity: O(nm)
Space complexity: O(1)
"""
width = len(input[0])
height = len(input)
counter = 0
for y in range(height):
for x in range(width):
# X-MAS shape must fit within bounds.
if 0 < y < height - 1 and 0 < x < width - 1 and input[y][x] == 'A':
found_shape = (((input[y - 1][x - 1] == 'M' and input[y + 1][x + 1] == 'S')
or (input[y - 1][x - 1] == 'S' and input[y + 1][x + 1] == 'M'))
and ((input[y + 1][x - 1] == 'M' and input[y - 1][x + 1] == 'S')
or (input[y + 1][x - 1] == 'S' and input[y - 1][x + 1] == 'M')))
counter += 1 if found_shape else 0
return counter

0 comments on commit 4ef4b65

Please sign in to comment.