Last active
December 5, 2023 16:11
-
-
Save luqmansen/94c3b121710f357f77a324e9da4d5e4d to your computer and use it in GitHub Desktop.
Advent of code 2023
This file contains hidden or 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
# https://adventofcode.com/2023/day/1 | |
# get full input at https://adventofcode.com/2023/day/1/input | |
if __name__ == '__main__': | |
per_line = _input.split('\n') | |
total = 0 | |
for line in per_line: | |
print(f'line: {line}') | |
# find first occurence of a number | |
first_num = None | |
first_num_idx = None | |
for i, char in enumerate(line): | |
if char.isdigit(): | |
first_num = char | |
first_num_idx = i | |
break | |
# find last occurence of a number | |
last_num = None | |
last_num_idx = None | |
for i, char in enumerate(line[::-1]): | |
if char.isdigit(): | |
last_num = char | |
last_num_idx = len(line) - i - 1 | |
break | |
# if there are no numbers, skip | |
if first_num is None or last_num is None: | |
continue | |
if first_num_idx == last_num_idx: | |
print(first_num + first_num) | |
total += int(first_num + first_num) | |
continue | |
if first_num_idx != last_num_idx: | |
print(first_num + last_num) | |
total += int(first_num + last_num) | |
print(total) |
This file contains hidden or 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
@dataclass | |
class GameSet: | |
red: int = 0 | |
green: int = 0 | |
blue: int = 0 | |
def match_rule(self, rule: 'GameSet'): | |
if any([ | |
self.blue > rule.blue, | |
self.red > rule.red, | |
self.green > rule.green | |
]): | |
return False | |
return True | |
@dataclass | |
class Game: | |
id: int | |
sets: List[GameSet] | |
def parse_from_raw_sets(raw_sets) -> List[GameSet]: | |
""" | |
raw_sets string format: | |
"1 green, 1 red, 10 blue; 12 blue; 2 red, 9 blue" | |
""" | |
game_set = [] | |
# `sets` format = ["1 green, 1 red, 10 blue", 12 blue, 2 red, 9 blue] | |
for sets in raw_sets.split(';'): | |
# `colors` format = [1 green, 1 red, 10 blue] | |
gameset = GameSet() | |
for colors in sets.split(','): | |
num, color = colors.strip().split(' ') | |
setattr(gameset, color, int(num)) | |
game_set.append(gameset) | |
return game_set | |
def parse_line(line: str) -> 'Game': | |
""" | |
limit string format: | |
"Game 73: 1 green, 1 red, 10 blue; 12 blue; 2 red, 9 blue" | |
""" | |
raw_game, raw_sets = line.split(':') | |
_, game_id = raw_game.split(' ') | |
return Game( | |
id=int(game_id), | |
sets=parse_from_raw_sets(raw_sets), | |
) | |
if __name__ == '__main__': | |
''' | |
https://adventofcode.com/2023/day/2 | |
get full input at https://adventofcode.com/2023/day/2/input | |
''' | |
game_collection = [] | |
for line in _input.split("\n"): | |
game_collection.append(parse_line(line)) | |
# Limit 12 red cubes, 13 green cubes, and 14 blue cubes | |
rule_limit = GameSet( | |
red=12, | |
green=13, | |
blue=14, | |
) | |
matching_rule = set() | |
for game in game_collection: | |
all_match = all( | |
[ | |
sets.match_rule(rule_limit) | |
for sets in game.sets | |
] | |
) | |
if all_match: | |
matching_rule.add(game.id) | |
print(f'{game.id} match') | |
else: | |
print(f'{game.id} not match: {game.sets}') | |
print(sum(matching_rule)) |
This file contains hidden or 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
from dataclasses import dataclass | |
from typing import Dict, Set | |
@dataclass | |
class PartNumber: | |
id: int | |
value: int | |
def __hash__(self): | |
return self.id | |
_part_number_id = 1 | |
def get_part_number_id() -> int: | |
"""Global unique counter for part number id""" | |
global _part_number_id | |
_part_number_id += 1 | |
return _part_number_id - 1 | |
def parse_numbers_per_line(raw_line, y_index) -> Dict[str, PartNumber]: | |
""" | |
Map the numbers to the lookup table where | |
key = x_index, y_index | |
value = PartNumber object | |
multiple keys can map to the same PartNumber object | |
""" | |
pointer = 0 | |
partial_lookup_table = {} | |
def create_key(x_index): | |
return f'{x_index},{y_index}' | |
while pointer < len(raw_line): | |
if raw_line[pointer].isdigit(): | |
# start a new key for each x,y coordinate | |
keys = set() | |
start_pointer = pointer | |
keys.add(create_key(pointer)) | |
while pointer < len(raw_line) and raw_line[pointer].isdigit(): | |
keys.add(create_key(pointer)) | |
pointer += 1 | |
number = PartNumber( | |
id=get_part_number_id(), | |
value=int(raw_line[start_pointer:pointer]) | |
) | |
for key in keys: | |
partial_lookup_table[key] = number | |
else: | |
pointer += 1 | |
return partial_lookup_table | |
def find_adjacent(lookup_table: dict, x :int, y: int) -> Set[PartNumber]: | |
""" | |
Find left, right, up, down, diagonal up left, | |
diagonal up right, diagonal down left, diagonal down right | |
return a set of unique of PartNumber objects | |
""" | |
adjacent = set() | |
for x_offset in range(-1, 2): | |
for y_offset in range(-1, 2): | |
if x_offset == 0 and y_offset == 0: | |
continue | |
if found := lookup_table.get(f'{x + x_offset},{y + y_offset}'): | |
adjacent.add(found) | |
return adjacent | |
def solve(): | |
lookup_table_result: Dict[str, PartNumber] = {} | |
# enumerate each of part numbers to a unique id | |
for y_idx, line in enumerate(_input.split('\n')): | |
lookup_table_result.update( | |
parse_numbers_per_line(line, y_idx) | |
) | |
considered_parts: Set[PartNumber] = set() | |
# find all the parts that are adjacent to a non-digit | |
for y_idx, line in enumerate(_input.split('\n')): | |
for x_idx, char in enumerate(line): | |
if char != '.' and not char.isdigit(): | |
considered_parts.update( | |
find_adjacent(lookup_table_result, x_idx, y_idx) | |
) | |
print(sum( | |
part.value | |
for part in considered_parts | |
)) | |
if __name__ == '__main__': | |
""" | |
Make a lookup table of all the parts where the key is the | |
x,y and the value is the `PartNumber` instance. | |
Since length coordinate of a part can span multiple x coordinates, | |
The lookup table allows multiple keys to refer to the same object. | |
example: | |
partA = PartNumber(id=1,value=35) | |
{ | |
'2,2': partA | |
'3,2': partA | |
} | |
when lookup 2,2 and 3,2, they both refer to the same object id=1 and value=35 | |
When later summed up, we can use a set to remove duplicates | |
total = set([partA, partA, partB]) -> {partA, partB} | |
""" | |
solve() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment