diff --git a/.gitignore b/.gitignore index 1e6c582..6c62c70 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ # Ignore all text files *.txt +.lsp +.clj-kondo +.cpcache +__pycache__ diff --git a/2023/6/part_one.clj b/2023/6/part_one.clj index e16b363..55a419b 100644 --- a/2023/6/part_one.clj +++ b/2023/6/part_one.clj @@ -4,8 +4,8 @@ (->> (slurp file-name) (str/split-lines))) -(defn make-races [document] - (->> document +(defn make-races [lines] + (->> lines (map #(re-seq #"\d+" %)) (map #(map parse-long %)) (apply map vector))) diff --git a/2023/6/part_two.clj b/2023/6/part_two.clj new file mode 100644 index 0000000..efd39ca --- /dev/null +++ b/2023/6/part_two.clj @@ -0,0 +1,23 @@ +(require '[clojure.string :as str]) + +(defn read-file [file-name] + (->> (slurp file-name) + (str/split-lines))) + +(defn make-race [lines] + (->> lines + (map #(re-seq #"\d+" %)) + (map #(str/join "" %)) + (map parse-long))) + +(defn compute-ways-to-win [[t d]] + (let [x (/ (- t (Math/sqrt (- (* t t) (* 4 d)))) 2) + y (/ (+ t (Math/sqrt (- (* t t) (* 4 d)))) 2)] + (+ (- (- (Math/ceil y) 1) (+ (Math/floor x) 1)) 1))) + +(defn main [file-name] + (->> (read-file file-name) + (make-race) + (compute-ways-to-win))) + +(main "z_input.txt") diff --git a/2023/7/part_one.py b/2023/7/part_one.py index 4670e84..5d7255a 100644 --- a/2023/7/part_one.py +++ b/2023/7/part_one.py @@ -56,6 +56,19 @@ def __init__(self, hand: str, bid: str) -> None: self.type = self.find_hand_type() self.cards = self.get_cards() + def __lt__(self, other: Self): + if self.__class__ is other.__class__: + if self.type != other.type: + return self.type < other.type + else: + return self.cards < other.cards + return NotImplemented + + def __eq__(self, other: Self): + if self.__class__ is other.__class__: + return self.type == other.type and self.cards == other.cards + return NotImplemented + def find_hand_type(self) -> HandType: unique_cards = len(set(self.hand)) counts = Counter(self.hand) @@ -88,19 +101,6 @@ def to_card(self, card: str) -> Card: def get_cards(self) -> list[Card]: return [self.to_card(card) for card in self.hand] - - def __lt__(self, other: Self): - if self.__class__ is other.__class__: - if self.type != other.type: - return self.type < other.type - else: - return self.cards < other.cards - return NotImplemented - - def __eq__(self, other: Self): - if self.__class__ is other.__class__: - return self.type == other.type and self.cards == other.cards - return NotImplemented def main(text: str) -> int: diff --git a/2023/7/part_two.py b/2023/7/part_two.py index 9034599..1d06040 100644 --- a/2023/7/part_two.py +++ b/2023/7/part_two.py @@ -56,6 +56,19 @@ def __init__(self, hand: str, bid: str) -> None: self.type = self.find_hand_type() self.cards = self.get_cards() + def __lt__(self, other: Self): + if self.__class__ is other.__class__: + if self.type != other.type: + return self.type < other.type + else: + return self.cards < other.cards + return NotImplemented + + def __eq__(self, other: Self): + if self.__class__ is other.__class__: + return self.type == other.type and self.cards == other.cards + return NotImplemented + def find_hand_type(self) -> HandType: unique_cards = len(set(self.hand)) counts = Counter(self.hand) @@ -98,19 +111,6 @@ def to_card(self, card: str) -> Card: def get_cards(self) -> list[Card]: return [self.to_card(card) for card in self.hand] - - def __lt__(self, other: Self): - if self.__class__ is other.__class__: - if self.type != other.type: - return self.type < other.type - else: - return self.cards < other.cards - return NotImplemented - - def __eq__(self, other: Self): - if self.__class__ is other.__class__: - return self.type == other.type and self.cards == other.cards - return NotImplemented def main(text: str) -> int: diff --git a/2023/8/part_one.clj b/2023/8/part_one.clj new file mode 100644 index 0000000..02b5747 --- /dev/null +++ b/2023/8/part_one.clj @@ -0,0 +1,23 @@ +(require '[clojure.string :as str]) + +(defn cycle-str [coll] + (lazy-cat coll (cycle-str coll))) + +(defn build-directions [document] + (->> document + (first) + (cycle-str))) + +(defn build-network [document] + (->> document + ((comp next next)) + (map #(str/split % #"=")))) + +(defn main [file-name] + (->> (slurp file-name) + (str/split-lines) + (build-network))) + + +(main "sample.txt") + diff --git a/2023/8/part_one.py b/2023/8/part_one.py index 23215d2..2c3abaf 100644 --- a/2023/8/part_one.py +++ b/2023/8/part_one.py @@ -15,8 +15,6 @@ def build_directions(self, raw_directions: str) -> Iterator[int]: """ Builds an infinite repeating binary representation of the directions. - @Role: Parse data to build data structure. - Input -> Output --------------- 'LR' -> (0, 1, 0, 1, 0, 1, ...) @@ -25,20 +23,21 @@ def build_directions(self, raw_directions: str) -> Iterator[int]: Input ----- raw_directions: - A string of arbitrary size containing only two characters: 'L' and 'R'. - Examples: - - 'RL' - - 'LRRRLL' - - 'LRLRLRLRLRRLRLRLR' + A string of arbitrary size containing only two characters: 'L' and 'R', + which give the directions one can go, left or right. + Examples: + - 'RL' + - 'LRRRLL' + - 'LRLRLRLRLRRLRLRLR' Output ------ Iterator[int]: - An infinite iterator containing only two integers: 0 and 1. - Examples: - - (0, 1, 0, 1, 0, 1, ...) - - (1, 1, 0, 1, 1, 0, ...) - - (0, 0, 1, 0, 0, 1, ...) + A stream of 0s and 1s. + Examples: + - (0, 1, 0, 1, 0, 1, ...) + - (1, 1, 0, 1, 1, 0, ...) + - (0, 0, 1, 0, 0, 1, ...) """ to_bin = {'L': 0, 'R': 1} return itertools.cycle([to_bin[c] for c in raw_directions]) @@ -49,8 +48,6 @@ def build_network(self, raw_network: list[str]) -> Network: values are 2-tuples that contain the two nodes that are accessible from the current node by going left or right. - @Role: Parse data to build data structure. - Input -> Output --------------- ['AAA = (BBB, CCC)', 'BBB = (CCC, AAA)', 'CCC = (ZZZ, ZZZ)'] @@ -60,13 +57,12 @@ def build_network(self, raw_network: list[str]) -> Network: Input ----- raw_network: - A list of strings that contain the network information + A list of strings that contain the network information. Output ------ Network: - A dict[str, tuple[str, str]] containing the same information as raw_network, - just parsed into a dict. + A dict version of raw_network. """ network: Network = {} for line in raw_network: @@ -79,8 +75,6 @@ def find_steps_required(self) -> int: """ Navigates self.network using self.directions and finds number of steps required to reach ZZZ starting from AAA. - - @Role: Traverse data structure using another data structure. """ node, steps, destination = 'AAA', 0, 'ZZZ' for direction in self.directions: diff --git a/2023/8/part_two.py b/2023/8/part_two.py index 05404f4..dc4d2c7 100644 --- a/2023/8/part_two.py +++ b/2023/8/part_two.py @@ -1,6 +1,6 @@ import itertools from pathlib import Path -from typing import Iterator +from collections.abc import Iterator type Network = dict[str, tuple[str, str]] @@ -17,7 +17,8 @@ def build_directions(self, raw_directions: str) -> Iterator[int]: Builds an infinite repeating binary representation of the directions. @Role: Parse data to build data structure. -Input -> Output + + Input -> Output --------------- 'LR' -> (0, 1, 0, 1, 0, 1, ...) 'RRL' -> (1, 1, 0, 1, 1, 0, ...) @@ -81,85 +82,72 @@ def parallel_navigator(self) -> Iterator[list[str]]: to navigate to. Skips yielding the starting nodes (the ones ending with 'A'). """ - nodes = [] - for key in self.network: - if key.endswith('A'): - nodes.append(key) - - #nodes_to_yield = [] - #for direction in self.directions: - # for node in nodes: - # nodes_to_yield.append(self.network[node][direction]) - # yield nodes_to_yield - # nodes, nodes_to_yield = nodes_to_yield, [] - - threads = range(len(nodes)) - record = {thread: {n: set() for n in range(self.cycle_len)} for thread in threads} - locations = {thread: 0 for thread in threads} - for thread in threads: - node, n = nodes[thread], 0 - step = 0 - try: - for direction in self.directions: - if node in record[thread][n]: raise ValueError - record[thread][n].add(node) - node, n = self.network[node][direction], (n + 1) % self.cycle_len - step += 1 - except ValueError: - print(thread, node) - locations[thread] = step - - max_location = max(locations.values()) - thread_nodes = {thread: '' for thread in threads} - for thread in threads: - diff = max_location - locations[thread] - n = locations[thread] - for _ in range(diff): - node, n = self.network[node][direction], (n + 1) - thread_nodes[thread] = node - locations[thread] = n - - print(locations[thread] % self.cycle_len) - distances = {thread: set() for thread in threads} - for thread in threads: - step = 0 - node = thread_nodes[thread] - try: - for direction in self.directions: - #print(node) - step += 1 - node = self.network[node][direction] - if node == thread_nodes[thread] and step % self.cycle_len == 2: raise ValueError - if node.endswith('Z'): distances[thread].add(step) - except ValueError: - print(f'step {thread}:', step) - pass - - print(distances) - return thread_nodes - + nodes = [key for key in self.network if key.endswith('A')] + for direction in self.directions: + nodes_to_yield = [self.network[node][direction] for node in nodes] + yield nodes_to_yield + nodes, nodes_to_yield = nodes_to_yield, [] def find_steps_required(self) -> int: """ - Navigates self.network using self.parallel_navigator and finds number of steps - required to reach nodes ending with 'Z'. - - @Role: Traverse data structure using another data structure. + Navigates self.network using self.parallel_navigator and finds number + of steps required to reach _Z starting from _A. """ - pass - #steps = 0 - #for nodes in self.parallel_navigator(): - #steps += 1 - #print(nodes) - #if all(node.endswith('Z') for node in nodes): break #return steps - + steps = 1 + for nodes in self.parallel_navigator(): + if all(node.endswith('Z') for node in nodes): break + steps += 1 + return steps + + #threads = range(len(nodes)) + #record: dict[int, dict[int, set[str]]] = {thread: {n: set() for n in range(self.cycle_len)} for thread in threads} + #locations = {thread: 0 for thread in threads} + #for thread in threads: + #node, n, step = nodes[thread], 0, 0 + #try: + #for direction in self.directions: + #if node in record[thread][n]: raise ValueError + #record[thread][n].add(node) + #node, n = self.network[node][direction], (n + 1) % self.cycle_len + #step += 1 + #except ValueError: + #print('hiiii') + #print(thread, node) + #locations[thread] = step +# + #max_location = max(locations.values()) + #thread_nodes = {thread: '' for thread in threads} + #for thread in threads: + #diff = max_location - locations[thread] + #n = locations[thread] + #for _ in range(diff): + #node, n = self.network[node][direction], (n + 1) + #thread_nodes[thread] = node + #locations[thread] = n +# + #print(locations[thread] % self.cycle_len) + #distances = {thread: set() for thread in threads} + #for thread in threads: + #step = 0 + #node = thread_nodes[thread] + #try: + #for direction in self.directions: + ##print(node) + #step += 1 + #node = self.network[node][direction] + #if node == thread_nodes[thread] and step % self.cycle_len == 2: raise ValueError + #if node.endswith('Z'): distances[thread].add(step) + #except ValueError: + #print(f'step {thread}:', step) +# + #print(distances) + #return thread_nodes + def main(document: str) -> int: map = Map(document) - return map.parallel_navigator() + return map.find_steps_required() if __name__ == '__main__': - #print(main(Path('sample.txt').read_text())) - #print(main(Path('sample_2.txt').read_text())) - #print(main(Path('sample_3.txt').read_text())) + #print(main(Path('sample_3.txt').read_text())) print(main(Path('z_input.txt').read_text())) diff --git a/2023/9/part_one.py b/2023/9/part_one.py index b4ece8a..78c39bb 100644 --- a/2023/9/part_one.py +++ b/2023/9/part_one.py @@ -10,9 +10,8 @@ def parse(text: str) -> list[list[int]]: def predict_line(line: list[int]) -> int: diffs = [lead - lag for lead, lag in zip(line[1:], line)] - if sum(line) == 0: return 0 + if sum(line) == 0: return 0 return line[-1] + predict_line(diffs) if __name__ == '__main__': - print(main(Path('document.txt').read_text())) - + print(main(Path('document.txt').read_text()))