Skip to content

Instantly share code, notes, and snippets.

@luqmansen
Last active December 5, 2023 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luqmansen/94c3b121710f357f77a324e9da4d5e4d to your computer and use it in GitHub Desktop.
Save luqmansen/94c3b121710f357f77a324e9da4d5e4d to your computer and use it in GitHub Desktop.
Advent of code 2023
# 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)
@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))
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