diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..73099c5 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 36fe7955bb9fdcf07b113265aa58eb3b +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_modules/day01/day1a.html b/_modules/day01/day1a.html new file mode 100644 index 0000000..34cb8b7 --- /dev/null +++ b/_modules/day01/day1a.html @@ -0,0 +1,191 @@ + + + + + + day01.day1a — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day01.day1a

+"""day1a solution."""
+
+
+from typing import Optional
+
+INPUT = "day01/input.txt"
+INPUT_SMALL = "day01/input-small.txt"
+
+
+
+[docs] +def get_first_last(line: str) -> tuple[str, str]: + """Returns first and last numeric character of a string. + + It can be the same character. + + Args: + line (str): string to parse + + Raises: + ValueError: When there are no numbers in the string + + Returns: + tuple[str, str]: first char, last char + """ + first: Optional[str] = None + last: str + for char in line: + if char.isnumeric(): + if first is None: + first = char + last = char + + if first is None: + raise ValueError("first should be set by now") + return (first, last)
+ + + +
+[docs] +def get_input(input_file: str) -> list[str]: + """Grabs list of lines to parse from input file. + + Args: + input_file (str): input file's name + + Returns: + list[str]: list of lines to parse + """ + with open(input_file, "r", encoding="utf8") as file: + return list(file)
+ + + +
+[docs] +def part1(lines: list[str]) -> int: + """Runs day1/part1 of adventofcode2023. + + Args: + lines (list[str]): list of lines to parse + + Returns: + int: sum of results for each line. + """ + total = 0 + for line in lines: + first, last = get_first_last(line) + data = int(first + last) + total += data + return total
+ + + +
+[docs] +def main() -> None: + """Grabs input then processes it.""" + lines = get_input(INPUT) + print(part1(lines))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day01/day1b.html b/_modules/day01/day1b.html new file mode 100644 index 0000000..6690882 --- /dev/null +++ b/_modules/day01/day1b.html @@ -0,0 +1,232 @@ + + + + + + day01.day1b — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day01.day1b

+"""day1b solution."""
+
+
+from dataclasses import dataclass
+
+INPUT = "day01/input.txt"
+INPUT_SMALL = "day01/input-small2.txt"
+
+
+
+[docs] +@dataclass +class IndexValue: + """index to value mapping.""" + + index: int = 0 + value: str = "1" + + def __str__(self) -> str: + """to_string function. + + Returns: + str: ``(i:{index}, v:{value})`` + """ + return f"(i:{self.index}, v:{self.value})"
+ + + +
+[docs] +@dataclass +class WordNumber: + """Simple mapping from word to integer.""" + + word: str = "one" + number: str = "1"
+ + + +MAPPINGS: list[WordNumber] = [ + WordNumber("one", "1"), + WordNumber("two", "2"), + WordNumber("three", "3"), + WordNumber("four", "4"), + WordNumber("five", "5"), + WordNumber("six", "6"), + WordNumber("seven", "7"), + WordNumber("eight", "8"), + WordNumber("nine", "9"), +] + + +
+[docs] +def process_line(line: str) -> int: + """Processes a line, returning the first and last integer. + + Substrings like ``nine`` and ``one`` count as integers, + as do ``1`` and ``9``. + + Args: + line (str): a line to process + + Returns: + int: integer value of the two numbers concatenated + """ + index_to_chars = {} + for index, char in enumerate(line): + if char.isnumeric(): + index_to_chars[index] = char + + for mapping in MAPPINGS: + substring = line[index : index + len(mapping.word)] + if substring == mapping.word: + index_to_chars[index] = mapping.number + + print(index_to_chars, line) + first_index = min(index_to_chars.keys()) + last_index = max(index_to_chars.keys()) + + return int(index_to_chars[first_index] + index_to_chars[last_index])
+ + + +
+[docs] +def get_input(input_file: str) -> list[str]: + """Opens a file and returns a list of strings to handle. + + Args: + input_file (str): filepath of input + + Returns: + list[str]: list of strings to parse + """ + with open(input_file, "r", encoding="utf8") as file: + return list(file)
+ + + +
+[docs] +def part2(lines: list[str]) -> int: + """Returns sum of "first/last" numbers in line. + + Args: + lines (list[str]): list of lines to handle + + Returns: + int: sum of "first/last" numbers in line + """ + total = sum(process_line(line) for line in lines) + return total
+ + + +
+[docs] +def main() -> None: + """Grabs input and then processes it.""" + lines = get_input(INPUT) + print(part2(lines))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day01/tests/test_day1a.html b/_modules/day01/tests/test_day1a.html new file mode 100644 index 0000000..1f7776e --- /dev/null +++ b/_modules/day01/tests/test_day1a.html @@ -0,0 +1,146 @@ + + + + + + day01.tests.test_day1a — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day01.tests.test_day1a

+"""Tests for day1a."""
+import pytest
+
+from day01.day1a import INPUT_SMALL, get_first_last, get_input, part1
+
+
+
+[docs] +def test_get_first_last() -> None: + """Tests a variety of strings on ``get_first_last()`` function.""" + assert get_first_last("abcdef123asdf4") == ("1", "4") + assert get_first_last("1") == ("1", "1") + assert get_first_last("01") == ("0", "1") + + assert get_first_last("1abc2") == ("1", "2") + assert get_first_last("pqr3stu8vwx") == ("3", "8") + assert get_first_last("a1b2c3d4e5f") == ("1", "5") + assert get_first_last("treb7uchet") == ("7", "7") + + with pytest.raises(ValueError): + get_first_last("")
+ + + +
+[docs] +def test_part1() -> None: + """Tests ``part1()`` function.""" + lines: list[str] = get_input(INPUT_SMALL) + assert part1(lines) == 142
+ + + +
+[docs] +def test_get_input() -> None: + """Tests ``get_input`` function.""" + lines: list[str] = get_input(INPUT_SMALL) + assert len(lines) == 4 + assert lines[0].strip() == "1abc2"
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day01/tests/test_day1b.html b/_modules/day01/tests/test_day1b.html new file mode 100644 index 0000000..7c831a6 --- /dev/null +++ b/_modules/day01/tests/test_day1b.html @@ -0,0 +1,150 @@ + + + + + + day01.tests.test_day1b — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day01.tests.test_day1b

+"""Tests for day01b."""
+from day01.day1b import INPUT_SMALL, IndexValue, get_input, part2, process_line
+
+
+
+[docs] +def test_process_line() -> None: + """Tests ``process_line()`` function.""" + assert process_line("two1nine") == 29 + assert process_line("eightwothree") == 83 + assert process_line("abcone2threexyz") == 13 + assert process_line("xtwone3four") == 24 + assert process_line("4nineeightseven2") == 42 + assert process_line("zoneight234") == 14 + assert process_line("7pqrstsixteen") == 76 + assert process_line("zoneight") == 18
+ + + +
+[docs] +def test_get_input() -> None: + """Tests ``get_input()`` function.""" + lines: list[str] = get_input(INPUT_SMALL) + assert len(lines) == 7 + assert lines[0].strip() == "two1nine"
+ + + +
+[docs] +def test_index_value() -> None: + """Tests ``index_value`` class.""" + index_val: IndexValue = IndexValue(0, "1") + assert str(index_val) == ("(i:0, v:1)")
+ + + +
+[docs] +def test_part2() -> None: + """Tests ``part2()`` function.""" + lines: list[str] = get_input(INPUT_SMALL) + assert part2(lines) == 281
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day02/day2.html b/_modules/day02/day2.html new file mode 100644 index 0000000..c5bd46d --- /dev/null +++ b/_modules/day02/day2.html @@ -0,0 +1,270 @@ + + + + + + day02.day2 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day02.day2

+"""day2 solution."""
+from enum import StrEnum
+
+INPUT = "day02/input.txt"
+INPUT_SMALL = "day02/input-small.txt"
+
+
+
+[docs] +class Color(StrEnum): + """Color enum.""" + + RED = "red" + GREEN = "green" + BLUE = "blue"
+ + + +
+[docs] +class Draw: + """Class representing a draw from the bag.""" + + red: int = 0 + green: int = 0 + blue: int = 0 + + def __init__(self, string: str): + """string: ``1 blue, 4 green, 5 red``.""" + self.parse_colors_count(string) + +
+[docs] + def parse_color_count(self, string: str) -> tuple[int, Color]: + """string: ``1 blue``.""" + num, color = string.split(" ") + return int(num), Color(color)
+ + +
+[docs] + def parse_colors_count(self, string: str) -> None: + """string: ``1 blue, 4 green, 5 red``.""" + for color_count in string.split(","): + color_count = color_count.strip() + num, color = self.parse_color_count(color_count) + if color == Color.RED: + self.red = num + elif color == Color.GREEN: + self.green = num + else: # color == Color.BLUE: + self.blue = num
+
+ + + +
+[docs] +class Game: + """Game class, showing multiple draws.""" + + id: int + red: int = 0 + green: int = 0 + blue: int = 0 + + def __init__(self, string: str): + """Create a game from an input string. + + Args: + string (str): a game string + """ + game_id_str, draw_str = string.split(":") + self.id = int(game_id_str.replace("Game ", "")) + self.draws = self.parse_draws(draw_str) + self.red = max(draw.red for draw in self.draws) + self.green = max(draw.green for draw in self.draws) + self.blue = max(draw.blue for draw in self.draws) + +
+[docs] + def parse_draws(self, string: str) -> list[Draw]: + """string: ``1 blue; 4 green, 5 blue; 11 red, 3 blue``.""" + result = [] + for draw_str in string.split(";"): + draw_str = draw_str.strip() + draw = Draw(draw_str) + result.append(draw) + return result
+ + + def __str__(self) -> str: + """Return summary of game id and minimal rgb.""" + return f"Game {self.id}: {self.red},{self.green},{self.blue}" + +
+[docs] + def power_level(self) -> int: + """Returns r*g*b.""" + return self.red * self.green * self.blue
+
+ + + +
+[docs] +def game_filter(game: Game) -> bool: + """Returns true if the game satisfies the constraints of Question 1.""" + return game.red <= 12 and game.green <= 13 and game.blue <= 14
+ + + +
+[docs] +def get_games(input_file: str) -> list[Game]: + """Gets the games from the input file. + + Args: + input_file (str): input file name + + Returns: + list[Game]: list of Games + """ + with open(input_file, "r", encoding="utf8") as file: + games: list[Game] = [] + for line in file: + game = Game(line) + games.append(game) + return games
+ + + +
+[docs] +def part1(games: list[Game]) -> int: + """Solves part 1.""" + filtered_games = filter(game_filter, games) + return sum(game.id for game in filtered_games)
+ + + +
+[docs] +def part2(games: list[Game]) -> int: + """Solves part2.""" + return sum(game.power_level() for game in games)
+ + + +
+[docs] +def main() -> None: + """Parses data into data structures, then prints out answer to q1 and q2.""" + games = get_games(INPUT) + + # Q1: + print(part1(games)) + + # Q2: + print(part2(games))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day02/tests/test_day2.html b/_modules/day02/tests/test_day2.html new file mode 100644 index 0000000..de6f06c --- /dev/null +++ b/_modules/day02/tests/test_day2.html @@ -0,0 +1,189 @@ + + + + + + day02.tests.test_day2 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day02.tests.test_day2

+"""tests for day02."""
+from day02.day2 import INPUT_SMALL, Draw, Game, game_filter, get_games, part1, part2
+
+
+
+[docs] +def get_game1() -> Game: + """Returns a sample game.""" + return Game("Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green")
+ + + +
+[docs] +def get_game2() -> Game: + """Returns another sample game.""" + return Game("Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue")
+ + + +
+[docs] +def test_draw() -> None: + """Test draws.""" + draw1: Draw = Draw("3 blue, 4 red") + draw2: Draw = Draw("4 blue, 3 green") + draw3: Draw = Draw("4 blue, 3 green, 1 red") + + assert [draw1.red, draw1.green, draw1.blue] == [4, 0, 3] + assert [draw2.red, draw2.green, draw2.blue] == [0, 3, 4] + assert [draw3.red, draw3.green, draw3.blue] == [1, 3, 4]
+ + + +
+[docs] +def test_game() -> None: + """Test games.""" + game1: Game = get_game1() + game2: Game = get_game2() + + assert game1.id == 1 + assert game1.red == 4 + assert game1.blue == 6 + assert game1.green == 2 + + assert game2.id == 2 + assert game2.red == 1 + assert game2.green == 3 + assert game2.blue == 4 + + assert str(game1) == "Game 1: 4,2,6" + assert str(game2) == "Game 2: 1,3,4"
+ + + +
+[docs] +def test_part1() -> None: + """Tests get_games, game_filter, part1.""" + games: list[Game] = get_games(INPUT_SMALL) + + # test game_filter + expected: list[bool] = [True, True, False, False, True] + for index, game in enumerate(games): + assert game_filter(game) == expected[index] + # test part1() + assert part1(games) == 8
+ + + +
+[docs] +def test_part2() -> None: + """Tests power.""" + games: list[Game] = get_games(INPUT_SMALL) + + # test game_filter + expected = [48, 12, 1560, 630, 36] + for index, game in enumerate(games): + assert game.power_level() == expected[index] + # test part1() + assert part2(games) == 2286
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day03/day3.html b/_modules/day03/day3.html new file mode 100644 index 0000000..dfd77b2 --- /dev/null +++ b/_modules/day03/day3.html @@ -0,0 +1,150 @@ + + + + + + day03.day3 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day03.day3

+"""Day 3 implementation."""
+
+
+from day03.lib.classes import Gear, Matrix, PartNumber
+from day03.lib.parsers import get_matrix
+
+INPUT = "day03/input.txt"
+INPUT_SMALL = "day03/input-small.txt"
+
+
+
+[docs] +def part1(part_numbers: list[PartNumber]) -> int: + """Return sum of valid partnumbers.""" + return sum([part_number.value for part_number in part_numbers])
+ + + +
+[docs] +def part2(part_numbers: list[PartNumber], matrix: Matrix) -> int: + """Return sum of valid gear values.""" + gears: list[Gear] = matrix.get_gears(part_numbers) + return sum(gear.gear_ratio for gear in gears)
+ + + +
+[docs] +def main() -> None: + """Main entrypoint; grab input then run part1 and part2.""" + matrix: Matrix = get_matrix(INPUT) + part_numbers: list["PartNumber"] = matrix.get_part_numbers() + part_numbers = matrix.filter_engine_parts(part_numbers) + + # q1 + print(part1(part_numbers)) + + # q2 + print(part2(part_numbers, matrix))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day03/lib/classes.html b/_modules/day03/lib/classes.html new file mode 100644 index 0000000..ba23d4d --- /dev/null +++ b/_modules/day03/lib/classes.html @@ -0,0 +1,276 @@ + + + + + + day03.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day03.lib.classes

+"""Day3 classes."""
+import re
+from dataclasses import dataclass
+
+NON_PART = "123456789."
+NUMBER_REGEX = r"\d+"
+
+
+
+[docs] +@dataclass +class PartNumber: + """Class respresenting a potential part number, and its position.""" + + col: int + row: int + length: int + value: int + +
+[docs] + def touching(self, col: int, row: int, row_size: int) -> bool: + """Returns if a given coordinate is touching this PartNumber.""" + start_x = max(0, self.col - 1) + end_x = min(self.end_index, row_size) + + if not start_x <= col <= end_x: + return False + if not self.row - 1 <= row <= self.row + 1: + return False + return True
+ + + @property + def end_index(self) -> int: + """Returns the end column index of the number.""" + return self.col + self.length
+ + + +
+[docs] +@dataclass +class Gear: + """Class representing a potential gear (``*`` icon).""" + + col: int + row: int + + part_numbers: list[PartNumber] | None = None + + @property + def gear_ratio(self) -> int: + """If we have exactly two parts, returns the gear ratio.""" + if self.part_numbers is None: + raise ValueError("self.part_numbers not initialized") + if len(self.part_numbers) == 2: + return self.part_numbers[0].value * self.part_numbers[1].value + return 0 # or None..
+ + + +
+[docs] +@dataclass +class Matrix: + """Represents the entire 2d array.""" + + data: list[str] + + @property + def row_size(self) -> int: + """How long each row is.""" + return len(self.data[0]) + + @property + def row_count(self) -> int: + """How many rows there are.""" + return len(self.data) + +
+[docs] + def get_part_numbers(self) -> list[PartNumber]: + """Retrieve numbered words like 456 from the matrix.""" + results = [] + + for row, line in enumerate(self.data): + matches = re.finditer(NUMBER_REGEX, line) + for match in matches: + start, end = match.start(), match.end() + value = int(line[start:end]) + part_number = PartNumber( + row=row, col=start, length=end - start, value=value + ) + results.append(part_number) + return results
+ + +
+[docs] + @staticmethod + def is_engine_part_row(row: str) -> bool: + """Returns if there is an engine part in this row.""" + return any(char not in NON_PART for char in row)
+ + +
+[docs] + def is_engine_part(self, part_number: PartNumber) -> bool: + """Return whether a part_number is an engine part by looking at its surroundings.""" + start_x = max(0, part_number.col - 1) + end_x = min(part_number.end_index + 1, self.row_size) + + if ( + part_number.row >= 1 + and self.is_engine_part_row( # row above + self.data[part_number.row - 1][start_x:end_x] + ) + ): + return True + if ( # row below + part_number.row < self.row_count - 1 + and self.is_engine_part_row(self.data[part_number.row + 1][start_x:end_x]) + ): + return True + + # left one + if self.data[part_number.row][start_x] not in NON_PART: + return True + + # right one + if self.data[part_number.row][end_x - 1] not in NON_PART: + return True + + return False
+ + +
+[docs] + def get_gears(self, part_numbers: list[PartNumber]) -> list[Gear]: + """Retrieve gears from the matrix.""" + results = [] + for row, line in enumerate(self.data): + for col, char in enumerate(line): + if char == "*": + gear = Gear(col=col, row=row) + gear.part_numbers = self.find_gear_parts(gear, part_numbers) + results.append(gear) + return results
+ + +
+[docs] + def find_gear_parts( + self, gear: Gear, part_numbers: list[PartNumber] + ) -> list[PartNumber]: + """Returns a list of part_numbers that are touching a given gear.""" + result = [] + for part_number in part_numbers: + if part_number.touching(gear.col, gear.row, self.row_size): + result.append(part_number) + return result
+ + +
+[docs] + def filter_engine_parts(self, part_numbers: list[PartNumber]) -> list[PartNumber]: + """Return the legit part numbers.""" + return list(filter(self.is_engine_part, part_numbers))
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day03/lib/parsers.html b/_modules/day03/lib/parsers.html new file mode 100644 index 0000000..2b677c9 --- /dev/null +++ b/_modules/day03/lib/parsers.html @@ -0,0 +1,118 @@ + + + + + + day03.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day03.lib.parsers

+"""Functions to parse from a file into well defined classes."""
+from day03.lib.classes import Matrix
+
+
+
+[docs] +def get_matrix(path: str) -> Matrix: + """Convert text file to matrix.""" + with open(path, "r", encoding="utf8") as file: + data = file.readlines() + data = [line.strip() for line in data] + return Matrix(data=data)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day03/tests/test_classes.html b/_modules/day03/tests/test_classes.html new file mode 100644 index 0000000..9d36988 --- /dev/null +++ b/_modules/day03/tests/test_classes.html @@ -0,0 +1,204 @@ + + + + + + day03.tests.test_classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day03.tests.test_classes

+"""tests day3's classes."""
+from dataclasses import dataclass
+
+import pytest
+
+from day03.day3 import INPUT_SMALL
+from day03.lib.classes import Gear, Matrix, PartNumber
+from day03.lib.parsers import get_matrix
+
+
+
+[docs] +@dataclass +class PartNumberTouchTest: + """Result for a part number touching something else.""" + + col: int + row: int + row_size: int + result: bool
+ + + +
+[docs] +def test_part_number() -> None: + """Tests part number class.""" + part_number = PartNumber(0, 0, 3, 467) + + assert part_number.end_index == 3 + + tests = [ + PartNumberTouchTest(0, 0, 10, True), + PartNumberTouchTest(0, 0, 10, True), + PartNumberTouchTest(1, 0, 10, True), + PartNumberTouchTest(2, 0, 10, True), + PartNumberTouchTest(3, 0, 10, True), + PartNumberTouchTest(0, 0, 10, True), + PartNumberTouchTest(1, 1, 10, True), + PartNumberTouchTest(2, 1, 10, True), + PartNumberTouchTest(3, 1, 10, True), + PartNumberTouchTest(1, -1, 10, True), + PartNumberTouchTest(2, -1, 10, True), + PartNumberTouchTest(3, -1, 10, True), + PartNumberTouchTest(4, 0, 10, False), + ] + for test in tests: + assert part_number.touching(test.col, test.row, test.row_size) == test.result
+ + + +
+[docs] +def test_matrix() -> None: + """Tests matrix.""" + matrix: Matrix = get_matrix(INPUT_SMALL) + part_numbers: list["PartNumber"] = matrix.get_part_numbers() + assert (matrix.row_count, matrix.row_size) == (10, 10) + assert len(part_numbers) == 10 + part_numbers = matrix.filter_engine_parts(part_numbers) + assert len(part_numbers) == 8 + + assert not Matrix.is_engine_part_row("123.24.56") + assert Matrix.is_engine_part_row("123.#24.56") + assert Matrix.is_engine_part_row("123.*24.56") + + gears: list[Gear] = matrix.get_gears(part_numbers) + + assert len(gears) == 3 + + matrix2: Matrix = Matrix(["*755."]) + assert matrix2.is_engine_part(PartNumber(1, 0, 3, 755))
+ + + +
+[docs] +def test_gear() -> None: + """Tests gear class.""" + # 69... + # ..*78 + # 54... + part_number1 = PartNumber(0, 0, 2, 69) + part_number2 = PartNumber(3, 1, 2, 78) + part_number3 = PartNumber(0, 2, 2, 54) + + gear1 = Gear(1, 2, [part_number1]) + gear2 = Gear(1, 2, [part_number1, part_number2]) + gear3 = Gear(1, 2, [part_number1, part_number2, part_number3]) + + assert gear1.gear_ratio == 0 + assert gear2.gear_ratio == 69 * 78 + assert gear3.gear_ratio == 0 + + gear_not_init = Gear(1, 1) + + with pytest.raises(ValueError): + gear_not_init.gear_ratio
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day03/tests/test_day3.html b/_modules/day03/tests/test_day3.html new file mode 100644 index 0000000..2142a30 --- /dev/null +++ b/_modules/day03/tests/test_day3.html @@ -0,0 +1,135 @@ + + + + + + day03.tests.test_day3 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day03.tests.test_day3

+"""test day3 main functions."""
+from typing import TYPE_CHECKING
+
+from day03.day3 import INPUT_SMALL, part1, part2
+from day03.lib.parsers import get_matrix
+
+if TYPE_CHECKING:
+    from day03.lib.classes import Matrix, PartNumber
+
+
+
+[docs] +def test_part1() -> None: + """Test part1.""" + matrix: Matrix = get_matrix(INPUT_SMALL) + part_numbers: list[PartNumber] = matrix.get_part_numbers() + part_numbers = matrix.filter_engine_parts(part_numbers) + assert part1(part_numbers) == 4361
+ + + +
+[docs] +def test_part2() -> None: + """Test part2.""" + matrix: Matrix = get_matrix(INPUT_SMALL) + part_numbers: list[PartNumber] = matrix.get_part_numbers() + part_numbers = matrix.filter_engine_parts(part_numbers) + assert part2(part_numbers, matrix) == 467835
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day04/day4.html b/_modules/day04/day4.html new file mode 100644 index 0000000..20d4a67 --- /dev/null +++ b/_modules/day04/day4.html @@ -0,0 +1,265 @@ + + + + + + day04.day4 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day04.day4

+"""Day 4 solution."""
+
+INPUT = "day04/input.txt"
+INPUT_SMALL = "day04/input-small.txt"
+
+
+
+[docs] +def split_numbers(string: str) -> set[int]: + """Splits a list of string'ed numbers into a set. + + E.g: `` 39 40 41 42 `` -> ``set(39,40,41,42)`` + + Args: + string (str): a list of string'ed numbers + + Returns: + set[int]: a set of integers + """ + string = string.strip() + + return {int(number) for number in string.split()}
+ + + +
+[docs] +class Card: + """a card with winners and numbers we own.""" + + id: int = 0 + winners: set[int] + have: set[int] + + def __init__(self, input_string: str): + """Construct a Card from a simple input string. + + Args: + input_string (str): ``Card 1: 41 48 | 83 86`` + """ + line = input_string.strip() + card_id_str, numbers_str = line.split(":") + winners_str, have_str = numbers_str.split("|") + + self.id = int(card_id_str.split()[1]) + self.winners = split_numbers(winners_str) + self.have = split_numbers(have_str) + +
+[docs] + def get_points(self) -> int: + """Returns how many points the card is worth. + + Returns: + int: 0 for no match, otherwise 2^(matches-1) + """ + matches = self.get_matches() + + if matches == 0: + points = 0 + else: + points = 2 ** (matches - 1) + + return points
+ + +
+[docs] + def get_matches(self) -> int: + """Returns how many winners intersect with what we have.""" + intersection = self.winners.intersection(self.have) + return len(intersection)
+
+ + + +
+[docs] +class Inventory: + """Total inventory of cards based on Question2 accumulation.""" + + # mapping of card to how many more cards it makes + memoized: dict[int, int] + all_cards: list[Card] + + def __init__(self, all_cards: list[Card]): + """An inventory from a list of cards. + + Args: + all_cards (list[Card]): list of original cards + """ + self.all_cards = all_cards + self.memoized = self.calculate_mappings() + +
+[docs] + def calculate_mappings(self) -> dict[int, int]: + """Returns map of card_id -> cards owned.""" + mappings = {} + reversed_cards = self.all_cards[::-1] + for card in reversed_cards: + matches = card.get_matches() + if matches == 0: + mappings[card.id] = 1 + else: + total = sum(mappings[card.id + i] for i in range(1, matches + 1)) + mappings[card.id] = 1 + total + return mappings
+ + +
+[docs] + def total_cards(self) -> int: + """Return total cards in inventory.""" + return sum(self.memoized.values())
+
+ + + +
+[docs] +def grab_data(filename: str) -> list[Card]: + """Converts file into wellformed cards.""" + with open(filename, "r", encoding="utf8") as file: + result = [Card(line) for line in file] + return result
+ + + +
+[docs] +def part1(cards: list[Card]) -> int: + """Return sum of points for each card in list.""" + return sum(card.get_points() for card in cards)
+ + + +
+[docs] +def part2(cards: list[Card]) -> int: + """Return total number of cards in our inventory.""" + inventory: Inventory = Inventory(cards) + return inventory.total_cards()
+ + + +
+[docs] +def main() -> None: + """Loads input file then runs part1 and part2.""" + cards: list[Card] = grab_data(INPUT) + # Q1 + print(part1(cards)) + # Q2 + print(part2(cards))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day04/tests/test_day4.html b/_modules/day04/tests/test_day4.html new file mode 100644 index 0000000..d1ed17e --- /dev/null +++ b/_modules/day04/tests/test_day4.html @@ -0,0 +1,177 @@ + + + + + + day04.tests.test_day4 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day04.tests.test_day4

+"""tests for day04."""
+from day04.day4 import (
+    INPUT_SMALL,
+    Card,
+    Inventory,
+    grab_data,
+    part1,
+    part2,
+    split_numbers,
+)
+
+
+
+[docs] +def test_split_numbers() -> None: + """Test ``split_numbers``.""" + assert split_numbers("6 7 8 9 10") == {6, 7, 8, 9, 10} + assert split_numbers("") == set() + assert split_numbers("6 6 6 6 6") == {6}
+ + + +
+[docs] +def test_grab_data() -> None: + """Test ``grab_data()``.""" + cards: list[Card] = grab_data(INPUT_SMALL) + assert len(cards) == 6 + assert cards[0].id == 1 and cards[-1].id == 6
+ + + +
+[docs] +def test_card() -> None: + """Test ``Card`` class.""" + card = Card("Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53\n") + assert card.get_points() == 8 + assert card.get_matches() == 4 + + card = Card("Card 1: 41 48 83 86 17 | 1 2 3 4 5 6 7 8\n") + assert card.get_points() == 0 + assert card.get_matches() == 0
+ + + +
+[docs] +def test_inventory() -> None: + """Test ``Inventory`` class.""" + cards: list[Card] = grab_data(INPUT_SMALL) + inventory: Inventory = Inventory(cards) + assert inventory.total_cards() == 30
+ + + +
+[docs] +def test_part1() -> None: + """Test ``part1()``.""" + cards: list[Card] = grab_data(INPUT_SMALL) + assert part1(cards) == 13
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + cards: list[Card] = grab_data(INPUT_SMALL) + assert part2(cards) == 30
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day05/day5.html b/_modules/day05/day5.html new file mode 100644 index 0000000..0671400 --- /dev/null +++ b/_modules/day05/day5.html @@ -0,0 +1,198 @@ + + + + + + day05.day5 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day05.day5

+"""Day5 solution."""
+
+from day05.lib.classes import MappingRange, NamedMap
+from day05.lib.parsers import grab_inputs
+
+INPUT = "day05/input.txt"
+INPUT_SMALL = "day05/input-small.txt"
+INPUT_GAPS = "day05/input-gaps.txt"
+
+
+
+[docs] +def get_location(seed: int, maps: list[NamedMap]) -> int: + """Given a seed, returns the final location.""" + result = seed + for named_map in maps: + result = named_map.get_mapping(result) + return result
+ + + +
+[docs] +def get_location_ranges( + seed_ranges: list[MappingRange], maps: list[NamedMap] +) -> list[MappingRange]: + """Given a list of MappingRange, returns a list of MappingRange's for the final location.""" + result = seed_ranges[:] + for named_map in maps: + result = named_map.get_mapping_ranges(result) + return result
+ + + +
+[docs] +def seed_to_mapping_ranges(data: list[int]) -> list[MappingRange]: + """Pair up seeds into mapping ranges. + + instead of seeds: 1, 2, 3, 4, 5, 6 + we want MappingRange[1,2], MappingRange(3,4), MappingRange(5,6) + They are in the format [start, size] + + Args: + data (list[int]): list of seeds + + Returns: + list[MappingRange]: list of mapping ranges + """ + pairs = list(zip(data[::2], data[1::2])) + result: list[MappingRange] = [] + for pair in pairs: + start, size = pair + mapping_range = MappingRange(start, start + size) + result.append(mapping_range) + return result
+ + + +
+[docs] +def part1(seeds: list[int], maps: list[NamedMap]) -> int: + """Return the final location with lowest value.""" + locations = [get_location(seed, maps) for seed in seeds] + locations.sort() + return locations[0]
+ + + +
+[docs] +def part2(seeds: list[int], maps: list[NamedMap]) -> int: + """Parses multiple seed ranges, and finds the lowest location start.""" + start_ranges = seed_to_mapping_ranges(seeds) + end_locations = get_location_ranges(start_ranges, maps) + return min(location.start for location in end_locations)
+ + + +
+[docs] +def main() -> None: + """Main function, solve all the problems.""" + seeds, maps = grab_inputs(INPUT) + # q1 + print(part1(seeds, maps)) + # q2 + print(part2(seeds, maps))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day05/lib/classes.html b/_modules/day05/lib/classes.html new file mode 100644 index 0000000..7de43c9 --- /dev/null +++ b/_modules/day05/lib/classes.html @@ -0,0 +1,295 @@ + + + + + + day05.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day05.lib.classes

+"""classes for day05."""
+from bisect import bisect_left
+from dataclasses import dataclass, field
+
+INT_MAX = 4294967296
+
+
+
+[docs] +@dataclass +class MappingRange: + """Simple class for start/end range.""" + + start: int + end: int
+ + + +
+[docs] +@dataclass(order=True) +class Mapping: + """Simple range based mapping.""" + + src_start: int + src_end: int = field(init=False) + dest_start: int + dest_end: int = field(init=False, repr=False) + size: int + + injected: bool = False + + def __post_init__(self) -> None: + """Finalize pre-calculated fields.""" + self.src_end = self.src_start + self.size + self.dest_end = self.dest_start + self.size + +
+[docs] + def get_mapping(self, src_value: int) -> int: + """Converts from src_value to destination.""" + if self.src_start <= src_value < self.src_end: + return src_value - self.src_start + self.dest_start + + raise ValueError(f"Item not within mapping range {src_value, self}")
+ + +
+[docs] + def get_mappings(self, start: int, end: int) -> tuple[MappingRange, int]: + """Returns the chunk from start to end, followed by the remainder.""" + dest_start = start - self.src_start + self.dest_start + dest_end_uncapped = end - self.src_start + self.dest_start + + if dest_end_uncapped < self.dest_end: + remaining = 0 + else: + remaining = dest_end_uncapped - self.dest_end + + dest_end_capped = min(dest_end_uncapped, self.dest_end) + + return MappingRange(dest_start, dest_end_capped), remaining
+
+ + + +
+[docs] +class NamedMap: + """a named map with a list of mappings.""" + + name: str + mappings: list[Mapping] + + def __init__(self, name: str): + """Create empty NamedMap from just its name. + + This one is a bit weird; the client should add mappings + after construction, then call finalize_mappings + This is to keep the file parsing code simpler + + Args: + name (str): name of the mapping list + """ + self.name = name + self.mappings = [] + +
+[docs] + def add_mapping(self, mapping: Mapping) -> None: + """Adds a mapping to our list.""" + self.mappings.append(mapping)
+ + +
+[docs] + def finalize_mappings(self) -> None: + """Post processes the mappings. + + * Sorts the mappings + * Fills in any missing ranges + * Homogenizes so min_mapping is 0, and max_mapping is INT_MAX + """ + mappings = self.mappings + mappings.sort() + # Find gaps in the mappings and fill them in + filled_in_mappings = [] + prev_mapping = mappings[0] + for mapping in mappings[1:]: + start = prev_mapping.src_end + end = mapping.src_start + if start < end: + injected_mapping = Mapping(start, start, end - start, True) + filled_in_mappings.append(injected_mapping) + prev_mapping = mapping + + # inject our mappings. We add them to the end, then sort + mappings.extend(filled_in_mappings) + mappings.sort() + + self.mappings = self.extend_mapping_range(mappings)
+ + +
+[docs] + def extend_mapping_range(self, mappings: list[Mapping]) -> list[Mapping]: + """Ensure that mappings go from 0 -> INT_MAX.""" + if mappings[0].src_start != 0: + injected_mapping = Mapping(0, 0, mappings[0].src_start, True) + mappings.insert(0, injected_mapping) + if mappings[-1].src_end != INT_MAX: + start = mappings[-1].src_end + injected_mapping = Mapping(start, start, INT_MAX - start, True) + mappings.append(injected_mapping) + + return mappings
+ + +
+[docs] + def get_mapping(self, value: int) -> int: + """Uses binary search to grab the correct mapping, then apply it to one value.""" + mapping_idx = bisect_left(self.mappings, value, key=lambda m: m.src_end - 1) + mapping = self.mappings[mapping_idx] + return mapping.get_mapping(value)
+ + +
+[docs] + def get_mapping_ranges( + self, src_mapping_ranges: list[MappingRange] + ) -> list[MappingRange]: + """Given a list of mapping ranges, returns a new list of mapping ranges.""" + result = [] + for src_mapping_range in src_mapping_ranges: + mappings = self.get_mapping_range(src_mapping_range) + result.extend(mappings) + return result
+ + +
+[docs] + def get_mapping_range(self, src_mapping_range: MappingRange) -> list[MappingRange]: + """Remaps a source range to a list of destination ranges.""" + # make a quick copy first + src_start, src_end = src_mapping_range.start, src_mapping_range.end + result: list[MappingRange] = [] + + # find out where the start is + mapping_start_idx = bisect_left( + self.mappings, src_start, key=lambda m: m.src_end + ) + remaining = src_end - src_start + + while remaining != 0: + mapping_start = self.mappings[mapping_start_idx] + mapping_range, remaining = mapping_start.get_mappings(src_start, src_end) + result.append(mapping_range) + src_start = src_end - remaining # move src_start upwards over time + mapping_start_idx += 1 # no need to re-bisect. just go next + + return result
+ + + def __str__(self) -> str: + """Return string for list of mappings.""" + result = str(self.name) + "\n" + result += "\n".join(str(mapping) for mapping in self.mappings) + return result
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day05/lib/parsers.html b/_modules/day05/lib/parsers.html new file mode 100644 index 0000000..914f528 --- /dev/null +++ b/_modules/day05/lib/parsers.html @@ -0,0 +1,140 @@ + + + + + + day05.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day05.lib.parsers

+"""Parsing from source file to well defined classes."""
+from day05.lib.classes import Mapping, NamedMap
+
+
+
+[docs] +def grab_inputs(path: str) -> tuple[list[int], list[NamedMap]]: + """Parses the source file.""" + seeds: list[int] + maps: list[NamedMap] = [] + named_map: NamedMap + + with open(path, "r", encoding="utf8") as file: + lines = file.readlines() + + for line in lines: + line = line.strip() + if line.startswith("seeds"): # grab the seeds + seeds_str = line.split(":")[1].split() + seeds = [int(seed) for seed in seeds_str] + elif line.endswith("map:"): # start a map segment + current_map_name = line.split(" map:")[0] + named_map = NamedMap(current_map_name) + maps.append(named_map) + elif len(line.strip()) != 0: # add a mapping to our named_map + numbers = [int(item) for item in line.split()] + dest, src, size = numbers + mapping = Mapping(src_start=src, dest_start=dest, size=size) + named_map.add_mapping(mapping) + + for named_map in maps: + named_map.finalize_mappings() + + return seeds, maps
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day05/tests/test_day5.html b/_modules/day05/tests/test_day5.html new file mode 100644 index 0000000..6aaa544 --- /dev/null +++ b/_modules/day05/tests/test_day5.html @@ -0,0 +1,184 @@ + + + + + + day05.tests.test_day5 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day05.tests.test_day5

+"""Day5 mainfile tests."""
+from typing import TYPE_CHECKING
+
+import pytest
+
+from day05.day5 import INPUT_GAPS, INPUT_SMALL, part1, part2, seed_to_mapping_ranges
+from day05.lib.classes import Mapping, MappingRange
+from day05.lib.parsers import grab_inputs
+
+if TYPE_CHECKING:
+    from day05.lib.classes import NamedMap
+
+
+
+[docs] +def test_mapping() -> None: + """Test ``NamedMap`` class.""" + seeds: list[int] + maps: list[NamedMap] + seeds, maps = grab_inputs(INPUT_SMALL) + + assert len(seeds) == 4 + assert len(maps) == 7 + + results = [79, 81, 81, 81, 74, 78, 78, 82] + + for index, map in enumerate(maps): + assert map.get_mapping(results[index]) == results[index + 1] + + # call mapping with integer outside its range + mapping: Mapping = maps[0].mappings[0] + with pytest.raises(ValueError): + mapping.get_mapping(mapping.src_end + 1) + + seeds, maps = grab_inputs(INPUT_GAPS) + + assert str(maps[0]) == "\n".join( + [ + "seed-to-soil", + "Mapping(src_start=0, src_end=15, dest_start=0, size=15, injected=True)", + "Mapping(src_start=15, src_end=63, dest_start=15, size=48, injected=False)", + "Mapping(src_start=63, src_end=100, dest_start=63, size=37, injected=True)", + "Mapping(src_start=100, src_end=120, dest_start=100, size=20, injected=False)", + "Mapping(src_start=120, src_end=4294967290, dest_start=120, size=4294967170, injected=True)", + "Mapping(src_start=4294967290, src_end=4294967296, dest_start=4294967290, size=6, injected=False)", + ] + )
+ + + +
+[docs] +def test_part1() -> None: + """Test ``part1()``.""" + seeds, maps = grab_inputs(INPUT_SMALL) + assert part1(seeds, maps) == 35
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + seeds, maps = grab_inputs(INPUT_SMALL) + assert part2(seeds, maps) == 46
+ + + +
+[docs] +def test_seed_to_mapping_ranges() -> None: + """Test construction of mapping ranges for part2.""" + data = [79, 14, 55, 13] + # 79, len(14) -> 79, 93 + # 55, len(13) -> 55, 68 + ranges = seed_to_mapping_ranges(data) + assert ranges[0] == MappingRange(79, 93) + assert ranges[1] == MappingRange(55, 68)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day06/day6.html b/_modules/day06/day6.html new file mode 100644 index 0000000..26a10a3 --- /dev/null +++ b/_modules/day06/day6.html @@ -0,0 +1,264 @@ + + + + + + day06.day6 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day06.day6

+"""Day06 solution."""
+import math
+from dataclasses import dataclass
+
+INPUT_SMALL = "day06/input-small.txt"
+INPUT = "day06/input.txt"
+
+
+
+[docs] +@dataclass +class Race: + """Simple class representing a race and its record.""" + + time: int + record_distance: int
+ + + +
+[docs] +@dataclass +class RaceStrat: + """class representing a strategy (charge time + run_time).""" + + charge_time: int + run_time: int + + @property + def distance(self) -> int: + """Returns how far we moved.""" + return self.run_time * self.speed + + @property + def speed(self) -> int: + """Return the speed after the charge time.""" + return self.charge_time
+ + + +
+[docs] +def read_inputs(path: str) -> list[Race]: + """Disgusting but short i guess.""" + with open(path, "r", encoding="utf8") as file: + times = [int(item) for item in file.readline().split()[1:]] + distance = [int(item) for item in file.readline().split()[1:]] + items = [Race(times[i], distance[i]) for i in range(len(times))] + return items
+ + + +
+[docs] +def calculate_race(race: Race) -> int: + """Naive calcuation of a race.""" + results: list[RaceStrat] = [] + for i in range(race.time): + charge_time = i + run_time = race.time - i + race_strat = RaceStrat(charge_time, run_time) + if race_strat.distance > race.record_distance: + results.append(race_strat) + + return len(results)
+ + + +""" + let charge_time = x + total_time = 42899189 + target_distance = 308117012911467 + remaining_time = total_time - x + + This gives the equation + x * (total_time - x) > target_distance + This is a quadratic formula + + x * (total_time - x) - target_distance = 0 + If you solve this, you get where the graph first beats and last beats the target + + Expand/simplify + -x^2 + total_time * x - target_distance = 0 + swap sign: + x^2 - total_time * x + target_distance = 0 + + now solve using quadratic fromula + x_top = total_time +- sqrt((total-time^2)-4*1*target_distance) + ------------------------------------------------------ + x_bottom = 2 + Once you solve +-, you get the intercepts. The total solution is the higher number minus the lower +""" + + +
+[docs] +def calculate_constant_time(race: Race) -> int: + """TL;DR Quadratic formula.""" + x_top = race.time - math.sqrt((race.time * race.time) - 4 * race.record_distance) + x_neg = x_top / 2 + + # start is always x_neg j, end is always race.time - start + start = math.ceil(x_neg) + end = math.floor(race.time - start) + + # add one because hypothetically if end == start, then you'd get 0 which is wrong. + # typical fencepost :) + return end - start + 1
+ + + +
+[docs] +def part1(races: list[Race]) -> int: + """Returns the sum of the amnount of ways we can win each race.""" + permutations = 1 + for race in races: + num_strats = calculate_race(race) + permutations *= num_strats + return permutations
+ + + +
+[docs] +def part2(race: Race) -> int: + """Return amoutn of ways we can win the race.""" + return calculate_constant_time(race)
+ + + +
+[docs] +def get_giga_race(races: list[Race]) -> Race: + """Converts from a list of races into one giga race.""" + giga_time = int("".join([str(race.time) for race in races])) + giga_distance = int("".join([str(race.record_distance) for race in races])) + giga_race = Race(giga_time, giga_distance) + return giga_race
+ + + +
+[docs] +def main() -> None: + """Solves Day 6.""" + races = read_inputs(INPUT) + + # q1 Dataclasses, brute force lmao + print(part1(races)) + + # q2 Quadratic formula; constant time. + giga_race = get_giga_race(races) + print(part2(giga_race))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day06/tests/test_day6.html b/_modules/day06/tests/test_day6.html new file mode 100644 index 0000000..df36e94 --- /dev/null +++ b/_modules/day06/tests/test_day6.html @@ -0,0 +1,176 @@ + + + + + + day06.tests.test_day6 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day06.tests.test_day6

+"""tests for day06."""
+from day06.day6 import (
+    INPUT_SMALL,
+    Race,
+    calculate_constant_time,
+    calculate_race,
+    get_giga_race,
+    part1,
+    part2,
+    read_inputs,
+)
+
+
+
+[docs] +def test_read_inputs() -> None: + """Tests reading inputs from file.""" + races: list[Race] = read_inputs(INPUT_SMALL) + assert races[0] == Race(7, 9) + assert races[1] == Race(15, 40) + assert races[2] == Race(30, 200)
+ + + +
+[docs] +def test_get_giga_race() -> None: + """Tests reading the input for the giga race.""" + races: list[Race] = read_inputs(INPUT_SMALL) + race = get_giga_race(races) + assert race == Race(71530, 940200)
+ + + +
+[docs] +def test_part1() -> None: + """Tests part1.""" + races: list[Race] = read_inputs(INPUT_SMALL) + assert part1(races) == 288
+ + + +
+[docs] +def test_part2() -> None: + """Tests part2.""" + races: list[Race] = read_inputs(INPUT_SMALL) + race = get_giga_race(races) + assert part2(race) == 71503
+ + + +
+[docs] +def test_calculate_race() -> None: + """Tests calculating the race in both constant-time and brute-force.""" + race = Race(7, 9) + assert calculate_race(race) == 4 + assert calculate_constant_time(race) == 4
+ + + +
+[docs] +def test_calculate_constant_time() -> None: + """Test calculating constant time.""" + assert calculate_constant_time(Race(7, 9)) == 4 + + assert calculate_constant_time(Race(71530, 940200)) == 71503
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day07/day7.html b/_modules/day07/day7.html new file mode 100644 index 0000000..6230969 --- /dev/null +++ b/_modules/day07/day7.html @@ -0,0 +1,229 @@ + + + + + + day07.day7 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day07.day7

+"""day7 solution."""
+from collections import defaultdict
+from dataclasses import dataclass, field
+from functools import total_ordering
+from typing import Any, ClassVar, Self
+
+INPUT = "day07/input.txt"
+INPUT_SMALL = "day07/input-small.txt"
+
+
+
+[docs] +@total_ordering +@dataclass(eq=False) +class Hand: + """Simple hand class, uses cards_inted and of_a_kind for sorting.""" + + cards: str + bet: int + + cards_inted: list[int] = field(init=False, repr=False) + of_a_kind: list[int] = field(init=False) + CARD_MAPPING: ClassVar[str] = "23456789TJQKA" + + def __post_init__(self) -> None: + """Convert cards to ints.""" + self.cards_inted = [self.CARD_MAPPING.index(card) for card in self.cards] + self.bet = int(self.bet) + self.of_a_kind = self.calculate_of_a_kind() + +
+[docs] + def calculate_of_a_kind(self) -> list[int]: + """Figure out card sets.""" + card_sets: dict[str, int] = defaultdict(int) + for card in self.cards: + card_sets[card] += 1 + return sorted(card_sets.values(), reverse=True)
+ + + def __lt__(self, other: Self) -> Any: + """Less than comparator function.""" + if not isinstance(other, Hand): + raise ValueError("using __lt__ on non identical class") + # compare our sets + for ours, theirs in zip(self.of_a_kind, other.of_a_kind): + if ours != theirs: + return ours < theirs + # compare our individual cards + return self.cards_inted < other.cards_inted # int lists easy to compare + + def __eq__(self, other: object) -> bool: + """Compares if two hands are equal by comparing cards in same order and value.""" + if not isinstance(other, Hand): + raise ValueError("using __lt__ on non identical class") + return self.cards_inted == other.cards_inted
+ + + +
+[docs] +class HandPart2(Hand): + """Part two; implements joker rule.""" + + CARD_MAPPING = "J23456789TQKA" # new card ordering + + # override +
+[docs] + def calculate_of_a_kind(self) -> list[int]: + """Figure out card sets; jokers will be added to the biggest card set.""" + card_sets: dict[str, int] = defaultdict(int) + for card in self.cards: + card_sets[card] += 1 + + jokers = card_sets.pop("J", 0) + if len(card_sets) == 0: + return [jokers] + + of_a_kind = sorted(card_sets.values(), reverse=True) + of_a_kind[0] += jokers + return of_a_kind
+
+ + + +
+[docs] +def parse_lines(cls: type, path: str) -> list[Hand]: + """Open input file and parse into hand structures.""" + with open(path, "r", encoding="utf8") as file: + # wow super cool list comprehension thingo i'm so cool + results = [cls(*line.split()) for line in file] + return results
+ + + +
+[docs] +def calculate_hands(cls: type, input_path: str) -> int: + """Generates class `cls` then calculates points.""" + hands = sorted(parse_lines(cls, input_path)) + + score = 0 + for rank, hand in enumerate(hands): + score += (rank + 1) * hand.bet + return score
+ + + +
+[docs] +def main() -> None: + """Main func.""" + # Q1 + print(calculate_hands(Hand, INPUT)) + + # Q2 + print(calculate_hands(HandPart2, INPUT))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day07/tests/test_day7.html b/_modules/day07/tests/test_day7.html new file mode 100644 index 0000000..b49ee8b --- /dev/null +++ b/_modules/day07/tests/test_day7.html @@ -0,0 +1,169 @@ + + + + + + day07.tests.test_day7 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day07.tests.test_day7

+"""tests for day07."""
+import pytest
+
+from day07.day7 import INPUT_SMALL, Hand, HandPart2, calculate_hands, parse_lines
+
+
+
+[docs] +def test_calculate_hands() -> None: + """Test calculating hands.""" + assert calculate_hands(Hand, INPUT_SMALL) == 6440 + assert calculate_hands(HandPart2, INPUT_SMALL) == 5905
+ + + +
+[docs] +def test_parser() -> None: + """Test the input parsing code.""" + hands: list[Hand] = parse_lines(Hand, INPUT_SMALL) + assert len(hands) == 5 + assert hands[0].cards == "32T3K" + assert hands[-1].cards == "QQQJA" + assert hands[0] == Hand("32T3K", 765)
+ + + +
+[docs] +def test_hand() -> None: + """Test `Hand` class.""" + hand1 = Hand("KK677", 0) + hand2 = Hand("KTJJT", 0) + hand3 = Hand("KK677", 0) + assert hand2 < hand1 + assert hand1 > hand2 + assert hand1 == hand3 + + # mypy actually stops us from doing `hand1 < 1` + # So we disable it so we can test our raise, + # in case a dev decides to do the comparison + with pytest.raises(ValueError): + hand1 < 1 # type: ignore[operator] + + # here we don't need to disable it, since ruff + # tells us to use __eq__(self, other:object) -> bool: + # This means we do expect people to use hand1 == 6 and + # we want to throw that error + with pytest.raises(ValueError): + hand1 == 6 + + data = [ + ("32T3K", 765), + ("T55J5", 684), + ("KK677", 28), + ("KTJJT", 220), + ("QQQJA", 483), + ("JJJJJ", 483), + ] + + hands = (HandPart2(cards, bet) for cards, bet in data) + oaks = [hand.calculate_of_a_kind() for hand in hands] + assert [oak[0] for oak in oaks] == [2, 4, 2, 4, 4, 5]
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day08/day8.html b/_modules/day08/day8.html new file mode 100644 index 0000000..8f297f6 --- /dev/null +++ b/_modules/day08/day8.html @@ -0,0 +1,370 @@ + + + + + + day08.day8 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day08.day8

+"""day8 solution."""
+import itertools
+import math
+from dataclasses import dataclass, field
+from typing import Iterator
+
+INPUT = "day08/input.txt"
+INPUT_A = "day08/input-a.txt"
+INPUT_B = "day08/input-b.txt"
+INPUT_C = "day08/input-c.txt"
+
+
+
+[docs] +@dataclass +class Location: + """A location on our map, with names of other locations.""" + + name: str + left: str + right: str
+ + + +
+[docs] +@dataclass +class LocationStep: + """Location + how many steps to get here.""" + + location: Location + steps: int + + def __hash__(self) -> int: + """Custom hash function so we can compare/set this class.""" + return hash(self.location.name + str(self.steps))
+ + + +
+[docs] +class WorldMap: + """world map class.""" + + mappings: dict[str, Location] + + def __init__(self) -> None: + """Initialize an empty world map.""" + self.mappings = {} + +
+[docs] + def add_location(self, location: Location) -> None: + """Add location to our mappings.""" + self.mappings[location.name] = location
+
+ + + +
+[docs] +@dataclass +class Directions: + """Simple directions string.""" + + steps: str + +
+[docs] + def get_step(self, index: int) -> str: + """Returns a step given its index.""" + return self.steps[index % len(self.steps)]
+ + +
+[docs] + def get_steps_iterator(self) -> Iterator[str]: + """Returns a iterator that loops through indefinitely.""" + return itertools.cycle(self.steps)
+
+ + + +
+[docs] +@dataclass +class Cycle: + """Find a cycle.""" + + start_location: Location + location_steps: list[LocationStep] # all the steps, including non-looping + cycle_start: LocationStep # the step that loops + + cycle_start_index: int = field(init=False) # index of cycle_start + end_zs: list[int] = field(init=False) + + @property + def cycle_length(self) -> int: + """Return length of the repeating part.""" + return len(self.location_steps) - self.cycle_start_index + + def __post_init__(self) -> None: + """Initializes the start indices and and end indices.""" + self.cycle_start_index = self.location_steps.index(self.cycle_start) + end_zs: list[int] = [] + for index, location_step in enumerate(self.location_steps): + if location_step.location.name.endswith("Z"): + end_zs.append(index) + self.end_zs = end_zs + +
+[docs] + def get_location(self, index: int) -> LocationStep: + """Gets a location given a step index.""" + if index < len(self.location_steps): + return self.location_steps[index] + + # 2nd half of array is from cycle_start_index -> end + index -= len(self.location_steps) + index += self.cycle_start_index + index %= self.cycle_length + + return self.location_steps[index]
+
+ + + +
+[docs] +def read_input(path: str) -> tuple[Directions, WorldMap]: + """Reads input into directions/world_map.""" + with open(path, "r", encoding="utf8") as file: + directions = Directions(file.readline().strip()) + world_map = WorldMap() + for line in file: + line = line.strip() + if len(line) == 0: + continue + # 0123456789012345 + # GXF = (XQB, GFH) + name, left, right = line[0:3], line[7:10], line[12:15] + location = Location(name, left, right) + world_map.add_location(location) + return directions, world_map
+ + + +
+[docs] +def follow_directions(directions: Directions, world_map: WorldMap) -> int: + """Follows directions until we hit zzz.""" + mappings = world_map.mappings + node: Location = mappings["AAA"] + nodes_visited = 0 + for step in directions.get_steps_iterator(): + if step == "L": + node = mappings[node.left] + else: + node = mappings[node.right] + nodes_visited += 1 + if node.name == "ZZZ": + return nodes_visited + + raise AssertionError("Unreachable; iterator is infinite")
+ + + +
+[docs] +def get_location_as(world_map: WorldMap) -> list[Location]: + """Returns locations with an A at the end.""" + return [ + location + for location in world_map.mappings.values() + if location.name.endswith("A") + ]
+ + + +
+[docs] +def follow_directions_multi(directions: Directions, world_map: WorldMap) -> int: + """Follow all mappings ending in A until all of them are ZZZ.""" + nodes: list[Location] = get_location_as(world_map) + + cycles = [find_cycle(node, world_map, directions) for node in nodes] + + for cycle in cycles: + print( + cycle.start_location, + cycle.cycle_start_index, + len(cycle.location_steps), + cycle.cycle_length, + cycle.end_zs, + ) + + # each cycle only has one z in it. + # Also, each path is + # [beginning][loop------z--endloop] + # endloop.size == beginning.size + + # That means it can be simplified by finding the lcm + + lcm = math.lcm(*[cycle.cycle_length for cycle in cycles]) + print(lcm) # 13,663,968,099,527 + + index = cycles[0].end_zs[0] + index = lcm + for cycle in cycles: + print(cycle.get_location(index)) + + return lcm
+ + + +
+[docs] +def find_cycle( + location: Location, world_map: WorldMap, directions: Directions +) -> Cycle: + """Finds the cycle from a start location.""" + start_location = location + nodes: list[LocationStep] = [] + step_count = 0 + mappings = world_map.mappings + + node = LocationStep(location, step_count) + location_steps_lookup = set() + + while True: + nodes.append(node) + location_steps_lookup.add(node) + + direction = directions.steps[step_count] + if direction == "L": + location = mappings[location.left] + else: + location = mappings[location.right] + + node = LocationStep(location, step_count + 1) + if node in location_steps_lookup: + break + + step_count += 1 + step_count %= len(directions.steps) + + return Cycle(start_location, nodes, node)
+ + + +
+[docs] +def main() -> None: + """Main function, solve the things.""" + # q1 + directions, world_map = read_input(INPUT) + nodes_visited: int = follow_directions(directions, world_map) + print(nodes_visited) + # q2 + follow_directions_multi(directions, world_map)
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day08/tests/test_day8.html b/_modules/day08/tests/test_day8.html new file mode 100644 index 0000000..78afbe3 --- /dev/null +++ b/_modules/day08/tests/test_day8.html @@ -0,0 +1,178 @@ + + + + + + day08.tests.test_day8 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day08.tests.test_day8

+"""day08 tests."""
+from day08.day8 import (
+    INPUT_A,
+    INPUT_B,
+    INPUT_C,
+    Cycle,
+    Directions,
+    Location,
+    find_cycle,
+    follow_directions,
+    follow_directions_multi,
+    get_location_as,
+    read_input,
+)
+
+
+
+[docs] +def test_part1() -> None: + """Test ``part1()``.""" + directions, world_map = read_input(INPUT_A) + assert follow_directions(directions, world_map) == 2 + + directions, world_map = read_input(INPUT_B) + assert follow_directions(directions, world_map) == 6
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + directions, world_map = read_input(INPUT_C) + assert follow_directions_multi(directions, world_map) == 6
+ + + +
+[docs] +def test_location_as() -> None: + """Test finding of the ``a`` locations.""" + _, world_map = read_input(INPUT_C) + locations: list[Location] = get_location_as(world_map) + names = [location.name for location in locations] + assert set(names) == {"11A", "22A"}
+ + + +
+[docs] +def test_find_cycle() -> None: + """Test finding cycles.""" + directions, world_map = read_input(INPUT_C) + locations: list[Location] = get_location_as(world_map) + + location_11A: Location = [loc for loc in locations if loc.name == "11A"][0] + location_22A: Location = [loc for loc in locations if loc.name == "22A"][0] + + cycle_11A: Cycle = find_cycle(location_11A, world_map, directions) + cycle_22A: Cycle = find_cycle(location_22A, world_map, directions) + assert cycle_11A.cycle_length == 2 + assert cycle_22A.cycle_length == 6
+ + + +
+[docs] +def test_directions() -> None: + """Test directions class.""" + directions: Directions = Directions("LRLRLLR") + assert directions.get_step(0) == "L" + assert directions.get_step(7) == "L"
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day09/day9.html b/_modules/day09/day9.html new file mode 100644 index 0000000..3ce8b73 --- /dev/null +++ b/_modules/day09/day9.html @@ -0,0 +1,249 @@ + + + + + + day09.day9 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day09.day9

+"""day9 solution."""
+
+from dataclasses import dataclass
+from typing import Callable
+
+INPUT = "day09/input.txt"
+INPUT_SMALL = "day09/input-small.txt"
+
+
+
+[docs] +@dataclass +class ValueArray: + """Class representing an array and its subarrays.""" + + sub_arrays: list[list[int]] + + def __post_init__(self) -> None: + """Creates sub arrays.""" + current: list[int] = self.sub_arrays[0] + while set(current) != {0}: + current = interpolate(current) + self.sub_arrays.append(current) + +
+[docs] + def generic_extrapolate( + self, + add_to_array: Callable[[list[int], int], None], + calc_value: Callable[[list[int], list[int]], int], + ) -> None: + """Generic extrapolation.""" + for i in range(-1, -len(self.sub_arrays) - 1, -1): + array: list[int] = self.sub_arrays[i] + if i == -1: + add_to_array(array, 0) + else: + array_below: list[int] = self.sub_arrays[i + 1] + new_value = calc_value(array, array_below) + add_to_array(array, new_value)
+ + +
+[docs] + def extrapolate_right(self) -> None: + """Extrapolates to the right.""" + + def add_to_array(array: list[int], value: int) -> None: + array.append(value) + + def calculate_value(array: list[int], array_below: list[int]) -> int: + return array[-1] + array_below[-1] + + self.generic_extrapolate(add_to_array, calculate_value)
+ + +
+[docs] + def extrapolate_left(self) -> None: + """Extrapolates to the left.""" + + def add_to_array(array: list[int], value: int) -> None: + array.insert(0, value) + + def calculate_value(array: list[int], array_below: list[int]) -> int: + return array[0] - array_below[0] + + self.generic_extrapolate(add_to_array, calculate_value)
+
+ + + +
+[docs] +def get_input(path: str) -> list[ValueArray]: + """Turns inputs into nice ValueArrays.""" + result = [] + with open(path, "r", encoding="utf8") as file: + for line in file: + values = ValueArray([[int(item) for item in line.split()]]) + result.append(values) + return result
+ + + +
+[docs] +def interpolate(values: list[int]) -> list[int]: + """Interpolate a list using element-wise diffs. + + Converts ``3 3 3 3`` + to ``0 0 0`` + Converts ``1 2 3 4`` + to ``1 1 1`` + + Args: + values (list[int]): list of values + + Returns: + list[int]: interpolated list + """ + result = [values[i + 1] - values[i] for i in range(len(values) - 1)] + + return result
+ + + +
+[docs] +def part1(inputs: list[ValueArray]) -> int: + """Interpolates then extrapolates array to the right.""" + for input in inputs: + input.extrapolate_right() + return sum(input.sub_arrays[0][-1] for input in inputs)
+ + + +
+[docs] +def part2(inputs: list[ValueArray]) -> int: + """Interpolates then extrapolates array to the left.""" + for input in inputs: + input.extrapolate_left() + + return sum(input.sub_arrays[0][0] for input in inputs)
+ + + +
+[docs] +def main() -> None: + """Main function.""" + inputs = get_input(INPUT) + + # q1 + print(part1(inputs)) + + # q2 + print(part2(inputs))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day09/tests/test_day9.html b/_modules/day09/tests/test_day9.html new file mode 100644 index 0000000..8b2d80e --- /dev/null +++ b/_modules/day09/tests/test_day9.html @@ -0,0 +1,145 @@ + + + + + + day09.tests.test_day9 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day09.tests.test_day9

+"""day09 tests."""
+from day09.day9 import INPUT_SMALL, ValueArray, get_input, interpolate, part1, part2
+
+
+
+[docs] +def test_get_input() -> None: + """Test input grabbing function.""" + values: list[ValueArray] = get_input(INPUT_SMALL) + assert len(values) == 3 + assert len(values[0].sub_arrays) == 3 + assert values[0].sub_arrays[0] == [0, 3, 6, 9, 12, 15]
+ + + +
+[docs] +def test_interpolate() -> None: + """Test ``interpolate()`` function.""" + values = [0, 3, 6, 9, 12, 15] + assert interpolate(values) == [3, 3, 3, 3, 3]
+ + + +
+[docs] +def test_part1() -> None: + """Test ``part1()``.""" + values: list[ValueArray] = get_input(INPUT_SMALL) + assert part1(values) == 114
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + values: list[ValueArray] = get_input(INPUT_SMALL) + assert part2(values) == 2
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/day10.html b/_modules/day10/day10.html new file mode 100644 index 0000000..e4ed775 --- /dev/null +++ b/_modules/day10/day10.html @@ -0,0 +1,421 @@ + + + + + + day10.day10 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.day10

+"""day10 solution."""
+
+
+from day10.lib.direction import Direction
+from day10.lib.pipebounds import PipeBounds
+from day10.lib.pipes import Pipe, PipeMap
+from day10.lib.position import Position
+
+INPUT = "day10/input.txt"
+INPUT_A = "day10/input-a.txt"
+INPUT_B = "day10/input-b.txt"
+INPUT_C = "day10/input-c.txt"
+INPUT_D = "day10/input-d.txt"
+
+
+
+[docs] +def process_input_line(row: int, line: str) -> list[Pipe]: + """Process a single line of input.""" + return [Pipe(row, col, char) for col, char in enumerate(line.strip())]
+ + + +
+[docs] +def read_input(path: str) -> PipeMap: + """Read the map.""" + with open(path, "r", encoding="utf8") as file: + pipes = [process_input_line(row, line) for row, line in enumerate(file)] + pipe_map = PipeMap(pipes) + return pipe_map
+ + + +
+[docs] +def find_s(pipe_map: PipeMap) -> Position: + """Finds the S pipe.""" + for row_idx, row in enumerate(pipe_map.pipes): + finder = (col_idx for col_idx, pipe in enumerate(row) if pipe.is_start) + try: + col_idx = next(finder) + return Position(row_idx, col_idx) + except StopIteration: + continue + + raise AssertionError("No S pipe found!")
+ + + +
+[docs] +def calculate_s(start: Position, pipe_map: PipeMap) -> str: + """Calculate what the "S" character is as a pipe. + + We should have exactly two pipes going into us + + Args: + start (Position): position of S + pipe_map (PipeMap): map of pipes + + Raises: + ValueError: ``S`` doesn't have any connecting pipes + AssertionError: ``S`` does not match any known pipe. + + Returns: + str: The character representing the pipe + """ + connecting = [] + pipe_directions = [Direction.NORTH, Direction.WEST, Direction.EAST, Direction.SOUTH] + + for direction in pipe_directions: + pos: Position = start.next_position(direction) + tile: Pipe | None = pipe_map.get_pipe_safe(pos) + if tile is None: # e.g. S is on an edge + continue + opposite_direction = direction.opposite() + if opposite_direction in Pipe.PIPE_DIRECTION[tile.character]: + connecting.append(direction) + + if len(connecting) == 0: + raise ValueError("S is not a a valid pipe") + + # should now have connecting == [NORTH, EAST]: + for character, pipe_directions in Pipe.PIPE_DIRECTION.items(): + if set(connecting) == set(pipe_directions): + return character + + raise AssertionError("No mapping found for `s` pipe")
+ + + +
+[docs] +def find_cycles(pipe_map: PipeMap) -> list[Pipe]: + """Finds the pipe path starting from S.""" + # first find S, and re-assign it + s_position: Position = find_s(pipe_map) + s_char: str = calculate_s(s_position, pipe_map) + s_pipe: Pipe = pipe_map.get_pipe(s_position) + s_pipe.character = s_char + + pipe_path: list[Pipe] = [] + current_pipe: Pipe = s_pipe + direction: Direction | None = None + while current_pipe != s_pipe or len(pipe_path) == 0: + pipe_path.append(current_pipe) + direction, position = current_pipe.next_position(direction) + current_pipe = pipe_map.get_pipe(position) + current_pipe.is_loop = True + + return pipe_path
+ + + +
+[docs] +def flood_fill(pipe_map: PipeMap) -> int: + """Flood fills a pipemap from one starting tile. + + Args: + pipe_map (PipeMap): pipemap to fill + + Returns: + int: how many tiles were filled + """ + visited_positions = set() + to_visit: list[Position] = [Position(0, 0)] + + directions = list(Direction) + num_outside = 0 + while len(to_visit) > 0: + position = to_visit.pop() + + if position in visited_positions: + continue + + # mark the position as outside + pipe: Pipe | None = pipe_map.get_pipe_safe(position) + if pipe is not None and not pipe.is_loop: + pipe.pipe_bounds = PipeBounds.OUTSIDE + num_outside += 1 + for direction in directions: + new_position = position.next_position(direction) + to_visit.append(new_position) + visited_positions.add(position) + + return num_outside
+ + + +
+[docs] +def process_big_input_line(row: int, line: str) -> list[Pipe]: + """Process a single line of input.""" + return [ + Pipe(row, col, char, is_loop=(char != " ")) for col, char in enumerate(line) + ]
+ + + +
+[docs] +def expand_map(pipe_map: PipeMap) -> PipeMap: + """Expands each pipe into a 3x3 tile.""" + big_map = [] + + for row in pipe_map.pipes: + # three rows per original row: + big_rows = ["", "", ""] + + for col in row: + big_pipe = expand_pipe(col.character, col.is_loop) + + for row_idx, big_row in enumerate(big_pipe): + big_rows[row_idx] += big_row + + big_map.extend(big_rows) + + # now convert big_map into "nice" pipes: + pipes = [process_big_input_line(row, line) for row, line in enumerate(big_map)] + + pipe_map = PipeMap(pipes) + return pipe_map
+ + + +
+[docs] +def reduce_map(big_map: PipeMap, small_map: PipeMap) -> PipeMap: + """Converts from fat map back down to small map.""" + rows = [] + for row_idx in range(small_map.height): + row_tiles = [] + big_map_row_idx = row_idx * 3 + 1 + for col_idx in range(small_map.width): + big_map_col_idx = col_idx * 3 + 1 + position = Position(big_map_row_idx, big_map_col_idx) + tile = big_map.get_pipe(position) + new_tile = Pipe( + row_idx, + col_idx, + tile.character, + is_loop=tile.is_loop, + pipe_bounds=tile.pipe_bounds, + ) + row_tiles.append(new_tile) + rows.append(row_tiles) + + return PipeMap(rows)
+ + + +
+[docs] +def expand_pipe(character: str, is_loop: bool) -> tuple[str, str, str]: + """Expands a pipe character to big boi 3x3.""" + # fmt: off + if not is_loop: + return (" ", + " ", + " ") + + if character == "|": + return (" | ", + " | ", + " | ") + + if character == "-": + return (" ", + "---", + " ") + + if character == "L": + return (" | ", + " L-", + " ") + if character == "J": + return (" | ", + "-J ", + " ") + if character == "7": + return (" ", + "-7 ", + " | ") + + if character == "F": + return (" ", + " F-", + " | ") + raise ValueError("unknown character {character}")
+ + # fmt: on + + +
+[docs] +def part1(pipe_map: PipeMap) -> int: + """Finds length of S loop.""" + pipe_path = find_cycles(pipe_map) + return len(pipe_path) // 2
+ + + +
+[docs] +def part2(pipe_map: PipeMap) -> int: + """Finds tiles "inside" the loop.""" + find_cycles(pipe_map) + + # print our map before we mutate it + print(pipe_map) + + big_map: PipeMap = expand_map(pipe_map) + # you can use this to view it lol. + with open("day10/big_unfilled.txt", "w", encoding="utf8") as file: + file.write(str(big_map)) + + flood_fill(big_map) + with open("day10/big_filled.txt", "w", encoding="utf8") as file: + file.write(str(big_map)) + + small_map: PipeMap = reduce_map(big_map, pipe_map) + + pipes = small_map.pipes + total_unknown = 0 + for row in pipes: + total_unknown += sum( + 1 if col.pipe_bounds == PipeBounds.UNKNOWN else 0 for col in row + ) + + # extra step; mark unknown asn inside. + for row in pipes: + for col in row: + if col.pipe_bounds == PipeBounds.UNKNOWN: + col.pipe_bounds = PipeBounds.INSIDE + print(small_map) + + return total_unknown
+ + + +
+[docs] +def main() -> None: + """Read input and run part1/part2.""" + pipe_map = read_input(INPUT) + # q1 + print(part1(pipe_map)) + # q2. Flood fill the outside, then subtract length of loop. + print(part2(pipe_map))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/lib/direction.html b/_modules/day10/lib/direction.html new file mode 100644 index 0000000..a18ff9a --- /dev/null +++ b/_modules/day10/lib/direction.html @@ -0,0 +1,144 @@ + + + + + + day10.lib.direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.lib.direction

+"""direction Enum."""
+from enum import Enum
+
+
+
+[docs] +class Direction(Enum): + """Cardinal (NSEW) direction Enum.""" + + NORTH = 1 + SOUTH = 2 + EAST = 3 + WEST = 4 + +
+[docs] + def opposite(self) -> "Direction": + """Return opposite direction. + + e.g. ``E``<->``W`` + and ``N``<->``S`` + + Raises: + AssertionError: if direction is invalid + + Returns: + Direction: opposite direction. + """ + if self == Direction.NORTH: + return Direction.SOUTH + if self == Direction.SOUTH: + return Direction.NORTH + if self == Direction.EAST: + return Direction.WEST + if self == Direction.WEST: + return Direction.EAST + raise AssertionError("invalid direction")
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/lib/pipebounds.html b/_modules/day10/lib/pipebounds.html new file mode 100644 index 0000000..62e72c7 --- /dev/null +++ b/_modules/day10/lib/pipebounds.html @@ -0,0 +1,119 @@ + + + + + + day10.lib.pipebounds — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.lib.pipebounds

+"""PipeBounds class."""
+from enum import Enum
+
+
+
+[docs] +class PipeBounds(Enum): + """Whether this tile is a pipe, inside, outside or currently unknown.""" + + INSIDE = 0 + OUTSIDE = 1 + UNKNOWN = 2 + PIPE = 3
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/lib/pipes.html b/_modules/day10/lib/pipes.html new file mode 100644 index 0000000..45ded2e --- /dev/null +++ b/_modules/day10/lib/pipes.html @@ -0,0 +1,256 @@ + + + + + + day10.lib.pipes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.lib.pipes

+"""Pipe class."""
+from dataclasses import dataclass, field
+from typing import ClassVar
+
+from day10.lib.direction import Direction
+from day10.lib.pipebounds import PipeBounds
+from day10.lib.position import Position
+
+PIPE_FONT = {
+    "|": ["│", "║", "|", "X"],
+    "-": ["─", "═", "-", "X"],
+    "L": ["└", "╚", "L", "X"],
+    "J": ["┘", "╝", "J", "X"],
+    "7": ["┐", "╗", "7", "X"],
+    "F": ["┌", "╔", "F", "X"],
+    ".": [".", ".", ".", "X"],
+    "S": ["S", "S", "S", "S"],  # S is an "L" piece. manually done forehead
+}
+
+
+FONT = 1
+
+
+
+[docs] +@dataclass +class Pipe: + """The location and character representing the pipe.""" + + row: int + col: int + character: str + + is_start: bool = field(init=False) + font: int = FONT + is_loop: bool = False + pipe_bounds: PipeBounds = PipeBounds.UNKNOWN + + PIPE_DIRECTION: ClassVar[dict[str, list[Direction]]] = { + "|": [Direction.NORTH, Direction.SOUTH], + "-": [Direction.WEST, Direction.EAST], + "L": [Direction.NORTH, Direction.EAST], + "J": [Direction.NORTH, Direction.WEST], + "7": [Direction.WEST, Direction.SOUTH], + "F": [Direction.SOUTH, Direction.EAST], + ".": [], + } + + def __post_init__(self) -> None: + """Calculate if we are the start and update pipe_bounds if required.""" + self.is_start = self.character == "S" + if self.is_loop: + self.pipe_bounds = PipeBounds.PIPE + + def __str__(self) -> str: + """Return neat string representing us.""" + if self.is_start: + return "S" + if self.is_loop: + return PIPE_FONT[self.character][FONT] + if self.pipe_bounds == PipeBounds.INSIDE: + return "*" + if self.pipe_bounds == PipeBounds.OUTSIDE: + return "." + return " " + +
+[docs] + def next_direction(self, prev_direction: Direction | None = None) -> Direction: + """Determine next direction based on where we came from.""" + mapping = self.PIPE_DIRECTION[self.character] + if prev_direction == mapping[0]: + return mapping[1] + return mapping[0]
+ + +
+[docs] + def next_position( + self, prev_direction: Direction | None + ) -> tuple[Direction, Position]: + """Calculate the next position. + + Return where we came from if we move to next tile, and the new position + + Args: + prev_direction (Direction | None): Where we came from + + Returns: + tuple[Direction, Position]: Next direction and position. + """ + next_direction = self.next_direction(prev_direction) + return next_direction.opposite(), self.position.next_position(next_direction)
+ + + @property + def position(self) -> Position: + """Our current position.""" + return Position(self.row, self.col) + + def __hash__(self) -> int: # pragma: no cover + """Custom hash function so we can compare pipes.""" + return hash(f"{self.row},{self.col}")
+ + + +
+[docs] +@dataclass +class PipeMap: + """A 2d array of pipes.""" + + pipes: list[list[Pipe]] + + width: int = field(init=False, repr=False) + height: int = field(init=False, repr=False) + + def __post_init__(self) -> None: + """Pre-store width/height.""" + self.width = len(self.pipes[0]) + self.height = len(self.pipes) + +
+[docs] + def get_pipe(self, position: Position) -> Pipe: + """Returns a pipe given its position.""" + if not self.is_in_map(position): + raise ValueError(f"Position outside map {position}") + return self.pipes[position.row][position.col]
+ + +
+[docs] + def is_in_map(self, position: Position) -> bool: + """Returns whether a position is in the map.""" + return 0 <= position.row < self.height and 0 <= position.col < self.width
+ + +
+[docs] + def get_pipe_safe(self, position: Position) -> Pipe | None: + """Gets a pipe or returns None if position is out of map.""" + if self.is_in_map(position): + return self.pipes[position.row][position.col] + return None
+ + + def __str__(self) -> str: + """Human readable string of map.""" + return "\n".join("".join(str(pipe) for pipe in line) for line in self.pipes)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/lib/position.html b/_modules/day10/lib/position.html new file mode 100644 index 0000000..4c27b04 --- /dev/null +++ b/_modules/day10/lib/position.html @@ -0,0 +1,140 @@ + + + + + + day10.lib.position — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.lib.position

+"""Position class."""
+from dataclasses import dataclass
+
+from day10.lib.direction import Direction
+
+
+
+[docs] +@dataclass +class Position: + """Simple 2d coordinate.""" + + row: int + col: int + +
+[docs] + def next_position(self, direction: Direction) -> "Position": + """Determine next position based on direction.""" + if direction == Direction.EAST: + return Position(self.row, self.col + 1) + elif direction == Direction.NORTH: + return Position(self.row - 1, self.col) + elif direction == Direction.WEST: + return Position(self.row, self.col - 1) + elif direction == Direction.SOUTH: + return Position(self.row + 1, self.col) + + raise AssertionError(f"invalid direction {direction}")
+ + + def __hash__(self) -> int: + """Custom hash function so we can compare positions.""" + return hash(f"{self.row}:{self.col}")
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/tests/test_day10.html b/_modules/day10/tests/test_day10.html new file mode 100644 index 0000000..051fbec --- /dev/null +++ b/_modules/day10/tests/test_day10.html @@ -0,0 +1,170 @@ + + + + + + day10.tests.test_day10 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.tests.test_day10

+"""Tests for main functions in day10."""
+from typing import TYPE_CHECKING
+
+import pytest
+
+from day10.day10 import (
+    INPUT_A,
+    INPUT_B,
+    INPUT_C,
+    INPUT_D,
+    calculate_s,
+    expand_pipe,
+    find_s,
+    part1,
+    part2,
+    read_input,
+)
+from day10.lib.position import Position
+
+INPUT_E = "day10/tests/input-e.txt"
+INPUT_F = "day10/tests/input-f.txt"
+
+
+if TYPE_CHECKING:
+    from day10.lib.pipes import PipeMap
+
+
+
+[docs] +def test_day10() -> None: + """Test day10.""" + pipe_map_a: PipeMap = read_input(INPUT_A) + pipe_map_b: PipeMap = read_input(INPUT_B) + # q1 + assert part1(pipe_map_a) == 4 + assert part1(pipe_map_b) == 8 + + s_pos: Position = find_s(pipe_map_b) + assert s_pos == Position(2, 0) + assert calculate_s(s_pos, pipe_map_b) == "F" + + # q2 + pipe_map_c: PipeMap = read_input(INPUT_C) + assert part2(pipe_map_c) == 4 + pipe_map_d: PipeMap = read_input(INPUT_D) + assert part2(pipe_map_d) == 8 + + # pipe_map no s + pipe_map_no_s: PipeMap = read_input(INPUT_E) + with pytest.raises(AssertionError): + s_pos = find_s(pipe_map_no_s) + + # pipe map, s is incalculable + pipe_map_bad_s: PipeMap = read_input(INPUT_F) + s_pos = find_s(pipe_map_bad_s) + + with pytest.raises(ValueError): + calculate_s(s_pos, pipe_map_no_s) + + with pytest.raises(ValueError): + expand_pipe("^", True) + + with pytest.raises(ValueError): + pipe_map_bad_s.get_pipe(Position(-1, -1))
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day10/tests/test_direction.html b/_modules/day10/tests/test_direction.html new file mode 100644 index 0000000..c530c27 --- /dev/null +++ b/_modules/day10/tests/test_direction.html @@ -0,0 +1,118 @@ + + + + + + day10.tests.test_direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day10.tests.test_direction

+"""Tests for direction class."""
+from day10.lib.direction import Direction
+
+
+
+[docs] +def test_direction() -> None: + """Test direction class.""" + assert Direction.EAST.opposite() == Direction.WEST + assert Direction.WEST.opposite() == Direction.EAST + assert Direction.NORTH.opposite() == Direction.SOUTH + assert Direction.SOUTH.opposite() == Direction.NORTH
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day11/day11.html b/_modules/day11/day11.html new file mode 100644 index 0000000..525faa6 --- /dev/null +++ b/_modules/day11/day11.html @@ -0,0 +1,298 @@ + + + + + + day11.day11 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day11.day11

+"""day11 solution."""
+from dataclasses import dataclass, field
+from typing import Iterator
+
+INPUT = "day11/input.txt"
+INPUT_SMALL = "day11/input-small.txt"
+
+
+
+[docs] +@dataclass +class Galaxy: + """Galaxy represented as its position and unique id.""" + + row: int + col: int + id: int + +
+[docs] + def distance(self, other: "Galaxy", universe: "Universe") -> int: + """Return distance from one galaxy to another. + + Accounts for "expanded" spaces in the universe + """ + start_row, end_row = sorted([self.row, other.row]) + start_col, end_col = sorted([self.col, other.col]) + result = 0 + expansion_rate = universe.expansion_rate + + for row in range(start_row, end_row): + result += universe[row][start_col].get_point_distance(expansion_rate) + for col in range(start_col, end_col): + result += universe[end_row][col].get_point_distance(expansion_rate) + return result
+
+ + + +
+[docs] +@dataclass +class Point: + """Point represented by character and whether it is expanded.""" + + row: int + col: int + item: str + is_expanded: bool = field(default=False, init=False) + +
+[docs] + def get_point_distance(self, expansion_rate: int) -> int: + """Returns how big this "point" is based on if its expanded.""" + if self.is_expanded: + return expansion_rate + return 1
+ + + def __str__(self) -> str: + """Show ``@`` if we're expanded, otherwise original value.""" + if self.is_expanded: + return "@" + return self.item
+ + + +
+[docs] +@dataclass +class Universe: + """Universe class; 2d array of . and #.""" + + contents: list[list[Point]] + expansion_rate: int = 2 + + num_rows: int = field(repr=False, init=False) + num_cols: int = field(repr=False, init=False) + + def __post_init__(self) -> None: + """Initialize num_rows/num_cols.""" + self.num_rows = len(self.contents) + self.num_cols = len(self.contents[0]) + + def __getitem__(self, row_index: int) -> list[Point]: + """Returns index into a row. + + We can just use ``universe[row]`` + instead of ``universe.contents[row]`` + """ + return self.contents[row_index] + + def __iter__(self) -> Iterator[list[Point]]: + """Returns iterator over all rows. + + We can just use ``for row in universe`` + instead of ``for row in universe.contents`` + """ + for row in self.contents: + yield row + +
+[docs] + def expand_contents(self) -> None: + """Expands the contents of the universe.""" + for row in self.contents: + if is_empty(row): + for item in row: + item.is_expanded = True + + col_index = 0 + while col_index < len(self.contents[0]): + col = [row[col_index] for row in self.contents] + if is_empty(col): + for item in col: + item.is_expanded = True + col_index += 1
+ + +
+[docs] + def grab_galaxies(self) -> list[Galaxy]: + """Grabs all galaxies.""" + results = [] + galaxy_id = 0 + for index_row, row in enumerate(self.contents): + for index_col, col in enumerate(row): + if col.item == "#": + results.append(Galaxy(index_row, index_col, galaxy_id)) + galaxy_id += 1 + return results
+ + + def __str__(self) -> str: + """Custom string function to print this universe.""" + return "\n".join("".join(str(col) for col in row) for row in self.contents)
+ + + +
+[docs] +def is_empty(items: list[Point]) -> bool: + """Returns True if all the content is ``.``.""" + has_content = any(x.item == "#" for x in items) + return not has_content
+ + + +
+[docs] +def parse_input(path: str) -> Universe: + """Parse input file and return a universe.""" + with open(path, "r", encoding="utf8") as file: + rows = [] + for row, line in enumerate(file): + line = line.strip() + row_points = [Point(row, col, item) for col, item in enumerate(line)] + rows.append(row_points) + return Universe(rows)
+ + + +
+[docs] +def get_total_distance(galaxies: list[Galaxy], universe: Universe) -> int: + """Returns total distance of all galaxies.""" + result = 0 + for index, galaxy in enumerate(galaxies): + for galaxy2 in galaxies[index + 1 :]: + distance = galaxy.distance(galaxy2, universe) + result += distance + return result
+ + + +
+[docs] +def main() -> None: + """Main function, runs q1 and q2.""" + universe: Universe = parse_input(INPUT) + universe.expand_contents() + galaxies = universe.grab_galaxies() + + # q1: expansion = 2 + print(get_total_distance(galaxies, universe)) + # q2: expansion = 1000000 + universe.expansion_rate = 1000000 + print(get_total_distance(galaxies, universe))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day11/tests/test_day11.html b/_modules/day11/tests/test_day11.html new file mode 100644 index 0000000..6197aa9 --- /dev/null +++ b/_modules/day11/tests/test_day11.html @@ -0,0 +1,175 @@ + + + + + + day11.tests.test_day11 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day11.tests.test_day11

+"""Tests for day11."""
+from day11.day11 import INPUT_SMALL, Universe, get_total_distance, is_empty, parse_input
+
+
+
+[docs] +def test_day11() -> None: + """Test day11 main function.""" + universe: Universe = parse_input(INPUT_SMALL) + assert universe.num_rows == 10 + assert universe.num_cols == 10 + universe.expand_contents() + galaxies = universe.grab_galaxies() + + # q1: expansion = 2 + assert galaxies[0].distance(galaxies[6], universe) == 15 + assert galaxies[2].distance(galaxies[5], universe) == 17 + assert galaxies[7].distance(galaxies[8], universe) == 5 + + assert get_total_distance(galaxies, universe) == 374 + # q2: expansion = 1000000 + universe.expansion_rate = 10 + assert get_total_distance(galaxies, universe) == 1030 + universe.expansion_rate = 100 + assert get_total_distance(galaxies, universe) == 8410
+ + + +
+[docs] +def test_is_empty() -> None: + """Tests is_empty function on universe.""" + universe: Universe = parse_input(INPUT_SMALL) + assert is_empty(universe.contents[3]) + assert not is_empty(universe.contents[0]) + assert not is_empty(universe.contents[1]) + assert not is_empty(universe.contents[2])
+ + + +
+[docs] +def test_expansion() -> None: + """Test expansion of universe.""" + universe: Universe = parse_input(INPUT_SMALL) + assert universe.num_rows == 10 + assert universe.num_cols == 10 + universe.expand_contents() + + print(universe) + + assert str(universe) == "\n".join( + [ + "..@#.@..@.", + "..@..@.#@.", + "#.@..@..@.", + "@@@@@@@@@@", + "..@..@#.@.", + ".#@..@..@.", + "..@..@..@#", + "@@@@@@@@@@", + "..@..@.#@.", + "#.@.#@..@.", + ] + ) + + # test cool iterator stuff + for index, row in enumerate(universe): + assert universe[index] == row
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day12/day12.html b/_modules/day12/day12.html new file mode 100644 index 0000000..e5133ef --- /dev/null +++ b/_modules/day12/day12.html @@ -0,0 +1,266 @@ + + + + + + day12.day12 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day12.day12

+"""day12 solution."""
+from dataclasses import dataclass, field
+
+INPUT = "day12/input.txt"
+INPUT_SMALL = "day12/input-small.txt"
+
+
+
+[docs] +@dataclass +class State: + """Thes tate of a spring.""" + + items: str + broken_springs: list[int] + +
+[docs] + def valid(self) -> int: + """Returns true IFF we are completed without errors.""" + if len(self.items) == 0 and len(self.broken_springs) == 0: + return 1 + return 0
+ + + def __getitem__(self, index_or_slice: slice | int) -> str: + """Allows us to grab an index or slice of our items.""" + # slice access + if isinstance(index_or_slice, slice): + _slice = index_or_slice + return self.items[_slice.start : _slice.stop : _slice.step] + # index access + index: int = index_or_slice + if index >= len(self.items): + return "." + return self.items[index] + + def __hash__(self) -> int: + """Custom hash so we can use ``set()``.""" + return hash(str(self.items) + ":" + str(self.broken_springs))
+ + + +
+[docs] +@dataclass +class SpringLine: + """Springline class.""" + + items: str + broken_springs: list[int] + big_cache: dict[State, int] = field(init=False, repr=False, default_factory=dict) + +
+[docs] + def unfold(self) -> "SpringLine": + """Makes it 5x bigger (part2).""" + return SpringLine("?".join([self.items] * 5), self.broken_springs * 5)
+ + +
+[docs] + def calculate(self) -> int: + """Brute force with backtracking lets go...""" + first_state = State(self.items[:], self.broken_springs[:]) + return self.calculate_recursive(first_state)
+ + +
+[docs] + def set_and_return(self, state: State, value: int) -> int: + """Sets and returns in one line.""" + self.big_cache[state] = value + return value
+ + +
+[docs] + def calculate_recursive(self, state: State) -> int: + """Recursive with memoization. + + 1. memoized + 2. state.empty -> return if we are valid + 3. state[0] == "." chop it and continue + 4. state[0] == "#". get next number, and "enforce" it, chopping things. If anything is wrong, fail + """ + if state in self.big_cache: + return self.big_cache[state] + if len(state.items) == 0: + return self.set_and_return(state, state.valid()) + if state[0] == ".": + dot_state = State(state.items[1:], state.broken_springs[:]) + return self.set_and_return(state, self.calculate_recursive(dot_state)) + if state[0] == "#": + if len(state.broken_springs) == 0: + return self.set_and_return(state, 0) + + # commit to the state or die trying + broken = state.broken_springs[0] + items = state[:broken] + + if len(items) < broken: # at end of array and not enough elements + return self.set_and_return(state, 0) + if items.count(".") > 0: # only accept # and ? + return self.set_and_return(state, 0) + if state[broken] == "#": # check right hand side, needs to be ? or . + return self.set_and_return(state, 0) + + state = State(state[broken + 1 :], state.broken_springs[1:]) + return self.set_and_return(state, self.calculate_recursive(state)) + if state[0] == "?": + hash_state = State("#" + state.items[1:], state.broken_springs[:]) + dot_state = State("." + state.items[1:], state.broken_springs[:]) + + result = self.calculate_recursive(hash_state) + result += self.calculate_recursive(dot_state) + return self.set_and_return(state, result) + + raise AssertionError("First char not in `.#?` and list not empty")
+
+ + + +
+[docs] +def get_input(path: str) -> list[SpringLine]: + """Returns list of SpringLines from input file.""" + result = [] + with open(path, "r", encoding="utf8") as file: + for line in file: + items, broken_csv = line.split() + int_springs = [int(item) for item in broken_csv.split(",")] + spring_line = SpringLine(items, int_springs) + result.append(spring_line) + return result
+ + + +
+[docs] +def calculate_sum(spring_lines: list[SpringLine]) -> int: + """Calculates every spring line and then adds the totals.""" + return sum(spring_line.calculate() for spring_line in spring_lines)
+ + + +
+[docs] +def main() -> None: + """Main function.""" + spring_lines = get_input(INPUT) + # q1 + print(calculate_sum(spring_lines)) + # q2 + spring_lines_big = [spring_line.unfold() for spring_line in spring_lines] + print(calculate_sum(spring_lines_big))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day12/tests/test_day12.html b/_modules/day12/tests/test_day12.html new file mode 100644 index 0000000..b772881 --- /dev/null +++ b/_modules/day12/tests/test_day12.html @@ -0,0 +1,144 @@ + + + + + + day12.tests.test_day12 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day12.tests.test_day12

+"""day12 tests."""
+from day12.day12 import INPUT_SMALL, SpringLine, calculate_sum, get_input
+
+
+
+[docs] +def test_calculate_sum() -> None: + """Test ``calculate_sum()``.""" + spring_lines: list[SpringLine] = get_input(INPUT_SMALL) + assert calculate_sum(spring_lines) == 21 + + spring_lines = [spring_line.unfold() for spring_line in spring_lines] + assert calculate_sum(spring_lines) == 525152
+ + + +
+[docs] +def test_parser() -> None: + """Test ``get_input()``.""" + spring_lines: list[SpringLine] = get_input(INPUT_SMALL) + assert spring_lines[0].items == "???.###" + assert spring_lines[0].broken_springs == [1, 1, 3] + assert len(spring_lines) == 6
+ + + +
+[docs] +def test_spring_line() -> None: + """Test ``SpringLine`` class.""" + spring_line = SpringLine("?###????????", [3, 2, 1]) + assert spring_line.calculate() == 10 + + spring_line = SpringLine("???.###", [1, 1, 3]) + unfolded = spring_line.unfold() + assert unfolded.items == "???.###????.###????.###????.###????.###" + assert unfolded.broken_springs == [1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3]
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day13/day13.html b/_modules/day13/day13.html new file mode 100644 index 0000000..d781fb0 --- /dev/null +++ b/_modules/day13/day13.html @@ -0,0 +1,239 @@ + + + + + + day13.day13 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day13.day13

+"""day13 solution."""
+
+from dataclasses import dataclass
+from typing import Optional
+
+INPUT = "day13/input.txt"
+INPUT_SMALL = "day13/input-small.txt"
+
+
+
+[docs] +def distance(left: str, right: str) -> int: + """Returns edit distance of two strings.""" + return sum(a != b for a, b in zip(left, right))
+ + + +
+[docs] +@dataclass +class Maze: + """2d array of tiles.""" + + tiles: list[str] + +
+[docs] + def solve(self, distance: int = 0) -> int: + """Solves a maze given an edit distance. + + ``0`` == ``Equal mirror`` + ``1`` == ``smudge in mirror`` + """ + row_reflect = self.reflect_rows(distance) + col_reflect = self.reflect_cols(distance) + return self.score(row_reflect, col_reflect)
+ + +
+[docs] + def reflect_rows(self, distance: int) -> Optional[int]: + """Check a row's reflection.""" + return self.check_reflection(self.tiles, distance)
+ + +
+[docs] + def reflect_cols(self, distance: int) -> Optional[int]: + """Checks reflection of cols by flipping rows/cols.""" + cols: list[list[str]] = [[] for _ in range(len(self.tiles[0]))] + for row in self.tiles: + for col_index, col in enumerate(row): + cols[col_index].append(col) + + cols_strs = ["".join(str(col)) for col in cols] + return self.check_reflection(cols_strs, distance)
+ + +
+[docs] + def check_reflection(self, data: list[str], target_dist: int) -> Optional[int]: + """Checks a reflection on rows or cols.""" + for index in range(len(data)): + if index == 0: + continue + + top_blocks = reversed(data[:index]) + bottom_blocks = data[index:] + pairs = zip(top_blocks, bottom_blocks) # accounts for shorter list + total_diff = sum(distance(top, bottom) for top, bottom in pairs) + + if total_diff == target_dist: + return index + + return None
+ + +
+[docs] + def score(self, row_reflect: Optional[int], col_reflect: Optional[int]) -> int: + """Returns score for q1.""" + if col_reflect is not None: + return col_reflect + if row_reflect is not None: + return 100 * row_reflect + raise AssertionError("expected a mirror!")
+
+ + + +
+[docs] +def read_input(path: str) -> list[Maze]: + """Read input file into well defined Mazes. + + Args: + path (str): filename of input + + Returns: + list[Maze]: list of well defined Mazes. + """ + mazes: list[Maze] = [] + with open(path) as file: + lines: list[str] = [] + for line in file: + if line.strip() == "": + maze = Maze(lines) + mazes.append(maze) + lines = [] + else: + lines.append(line.strip()) + maze = Maze(lines) + mazes.append(maze) + + return mazes
+ + + +
+[docs] +def main() -> None: + """Loads input then runs q1/q2.""" + mazes: list[Maze] = read_input(INPUT) + + # q1 + print(sum(maze.solve(0) for maze in mazes)) + + # q2 + print(sum(maze.solve(distance=1) for maze in mazes))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day13/tests/test_day13.html b/_modules/day13/tests/test_day13.html new file mode 100644 index 0000000..1fdf685 --- /dev/null +++ b/_modules/day13/tests/test_day13.html @@ -0,0 +1,134 @@ + + + + + + day13.tests.test_day13 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day13.tests.test_day13

+"""Day13 tests."""
+from day13.day13 import INPUT_SMALL, Maze, distance, read_input
+
+
+
+[docs] +def test_day13() -> None: + """Test day13.""" + mazes: list[Maze] = read_input(INPUT_SMALL) + assert len(mazes) == 2 + assert mazes[0].solve() == 5 + assert mazes[1].solve() == 400 + + assert mazes[0].solve(1) == 300 + assert mazes[1].solve(1) == 100
+ + + +
+[docs] +def test_distance() -> None: + """Test ``distance()`` function.""" + str1 = "#...##..#" + str2 = "##..##..#" + str3 = "######..#" + + assert distance(str1, str2) == 1 + assert distance(str1, str3) == 3
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day14/day14.html b/_modules/day14/day14.html new file mode 100644 index 0000000..2db2fb3 --- /dev/null +++ b/_modules/day14/day14.html @@ -0,0 +1,342 @@ + + + + + + day14.day14 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day14.day14

+"""day14 solution."""
+
+from dataclasses import dataclass
+from typing import Any, Optional
+
+from day14.lib.direction import Direction
+
+INPUT_SMALL = "day14/input-small.txt"
+INPUT = "day14/input.txt"
+
+
+
+[docs] +@dataclass +class World: + """2d array of boulders (square/round) and empty space.""" + + data: Any + left_is: Direction = Direction.West + + score: int | None = None + +
+[docs] + def rotate_world_cw(self) -> "World": + """Rotate world clockwise.""" + rotated = list(zip(*self.data[::-1])) + return World(rotated, self.left_is.next_direction_ccw())
+ + +
+[docs] + def rotate_world_ccw(self) -> "World": + """Rotate world anti-clockwise.""" + rotated = list(zip(*self.data))[::-1] + return World(rotated, self.left_is.next_direction_cw())
+ + + def __hash__(self) -> int: + """Custom hash function for use with ``set()``.""" + return hash(str(self.data) + ":" + str(self.left_is)) + +
+[docs] + def to_string(self) -> str: + """Well defined string of our world.""" + return "\n".join(str(row) for row in self.data)
+ + +
+[docs] + def as_orientiented_north(self) -> str: + """Well defined string if our world has up as North.""" + world = self.correct_side() + return world.to_string()
+ + +
+[docs] + def get_score(self) -> int: + """Get score of the world.""" + world = self.correct_side() + return naive_score(world.data)
+ + +
+[docs] + def correct_side(self) -> "World": + """Return world oriented with North at the top.""" + world = self + if world.left_is == Direction.West: + return self + elif world.left_is == Direction.North: + return world.rotate_world_cw() + elif world.left_is == Direction.East: + world = world.rotate_world_cw() + world = world.rotate_world_cw() + return world + elif world.left_is == Direction.South: + return world.rotate_world_ccw() + raise AssertionError(f"Unsupported Direction: {world.left_is}")
+
+ + + +
+[docs] +def naive_score(world_rows: list[str]) -> int: + r"""Returns score assuming west is pointing left. + + For each row, a round boulder ``O``'s score is + ``num_rows`` minus its ``index`` (higher weight to the left.) + """ + num_rows = len(world_rows) + score = 0 + for index, row in enumerate(world_rows): + o_count = row.count("O") + score += o_count * (num_rows - index) + return score
+ + + +
+[docs] +def simulate_row(row: list[str]) -> tuple[list[str], int]: + """Simulates a row; returns its value and the new row. + + Assumes that we are moving boulders to the left + """ + square_indices = [-1] + [i for i, x in enumerate(row) if x == "#"] + [len(row)] + pairs = zip(square_indices, square_indices[1:]) + row_score = 0 + new_row = "" + for start, end in pairs: + sub_array = row[start + 1 : end] + o_count = sub_array.count("O") + start_score = len(row) - (start + 1) + end_score = len(row) - start - o_count + sub_array_score = int((start_score + end_score) / 2 * o_count) + row_score += sub_array_score + new_row += "O" * o_count + new_row += "." * (end - start - o_count - 1) + new_row += "#" + + new_row = new_row[:-1] + return list(new_row), row_score
+ + + +
+[docs] +def simulate_world(world_rows: list[list[str]]) -> list[list[str]]: + """Simulates world by rolling to the left.""" + result = [] + for world_row in world_rows: + result.append(simulate_row(world_row)[0]) + return result
+ + + +
+[docs] +def get_input(path: str) -> World: + """Grabs input, rotated so that left is north.""" + with open(path) as file: + lines = [line.strip() for line in file] + + return World(lines)
+ + + +
+[docs] +def question1(world: World) -> int: + """Returns world's score after rotating the world once.""" + while world.left_is != Direction.North: + world = world.rotate_world_ccw() + return sum(simulate_row(row)[1] for row in world.data)
+ + + +
+[docs] +def question2(world: World) -> int: + """Finds a loop in world rotation. + + Once the loop is found we can estimate the world's state + after any amount of rotations + + Args: + world (World): world to spin + + Returns: + int: "weight" to the north after 1000000000 cycles. + """ + cache: dict[World, int] = {} + cycle_results: list[World] = [] + + # starting state: left_is north + while world.left_is != Direction.North: + world = world.rotate_world_ccw() + world.data = simulate_world(world.data) + + cycle_index = 0 + cycle_start: Optional[int] = None + cycle_end: Optional[int] = None + + while cycle_start is None and cycle_end is None: + for direction in [ + Direction.North, # left_is north + Direction.West, # left_is west + Direction.South, # left_is south + Direction.East, # left_is east + ]: + if direction == Direction.East: # calculate score *before* + if world in cache: + cycle_start = cache[world] + cycle_end = cycle_index + else: + cache[world] = cycle_index + cycle_results.append(world) + world = world.rotate_world_cw() + world.data = simulate_world(world.data) + world.score = world.get_score() + + cycle_index += 1 + + if cycle_end is None or cycle_start is None: + raise AssertionError("cycle_end and cycle_end should not be None") + + cycle_length = cycle_end - cycle_start + print(f"cycle length: {cycle_length}") + + final_target = 1000000000 + target = (final_target - cycle_start - 1) % cycle_length + result = cycle_results[cycle_start + target] + + if result.score is None: + raise AssertionError("No score found!") + + return result.score
+ + + +
+[docs] +def main() -> None: + """Get input and run question1/question2.""" + world = get_input(INPUT) + + print(question1(world)) + print(question2(world))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day14/lib/direction.html b/_modules/day14/lib/direction.html new file mode 100644 index 0000000..ffd9155 --- /dev/null +++ b/_modules/day14/lib/direction.html @@ -0,0 +1,137 @@ + + + + + + day14.lib.direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day14.lib.direction

+"""Direction class."""
+from enum import Enum, IntEnum
+
+
+
+[docs] +class Direction(IntEnum): + """Cardinal directions.""" + + North = 0 + West = 1 + South = 2 + East = 3 + + __str__ = Enum.__str__ + +
+[docs] + def next_direction_cw(self) -> "Direction": + """If we are pointing west, the next direction clockwise is north.""" + int_value = int(self) + return Direction((int_value - 1 + len(Direction)) % len(Direction))
+ + +
+[docs] + def next_direction_ccw(self) -> "Direction": + """If we are pointing west, the next direction ccw is south.""" + int_value = int(self) + return Direction((int_value + 1) % len(Direction))
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day14/tests/test_day14.html b/_modules/day14/tests/test_day14.html new file mode 100644 index 0000000..a6b5422 --- /dev/null +++ b/_modules/day14/tests/test_day14.html @@ -0,0 +1,204 @@ + + + + + + day14.tests.test_day14 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day14.tests.test_day14

+"""Tests for main day14 functions."""
+from day14.day14 import (
+    INPUT_SMALL,
+    World,
+    get_input,
+    question1,
+    question2,
+    simulate_row,
+)
+from day14.lib.direction import Direction
+
+INPUT_ARROW = "day14/input-arrow.txt"
+
+
+
+[docs] +def test_get_input() -> None: + """Test ``get_input()``.""" + world: World = get_input(INPUT_SMALL) + assert len(world.data) == 10 # rows + assert len(world.data[0]) == 10 # cols
+ + + +
+[docs] +def test_questions() -> None: + """Test main question functions.""" + world: World = get_input(INPUT_SMALL) + assert question1(world) == 136 + assert question2(world) == 64
+ + + +
+[docs] +def test_simulate_row() -> None: + """Test simulation of a row.""" + assert simulate_row(list("OO.#O....O"))[0] == list("OO.#OO....") + assert simulate_row(list(".........O"))[0] == list("O.........")
+ + + +
+[docs] +def test_rotate_world() -> None: + """Test rotating the world.""" + world: World = get_input(INPUT_ARROW) + + assert world.left_is == Direction.West + + ARROW_WEST = [ + (".", ".", ".", ".", ".", "."), + (".", ".", ".", ".", ".", "."), + (".", ".", "#", ".", ".", "."), + (".", "#", "#", ".", ".", "."), + ("#", "#", "#", "#", "#", "#"), + (".", "#", "#", ".", ".", "."), + (".", ".", "#", ".", ".", "."), + (".", ".", ".", ".", ".", "."), + (".", ".", ".", ".", ".", "."), + ] + assert world.rotate_world_ccw().data == ARROW_WEST + assert world.rotate_world_ccw().left_is == Direction.North + + ARROW_SOUTH = [ + (".", ".", ".", ".", "#", ".", ".", ".", "."), + (".", ".", ".", ".", "#", ".", ".", ".", "."), + (".", ".", ".", ".", "#", ".", ".", ".", "."), + (".", ".", "#", "#", "#", "#", "#", ".", "."), + (".", ".", ".", "#", "#", "#", ".", ".", "."), + (".", ".", ".", ".", "#", ".", ".", ".", "."), + ] + assert world.rotate_world_cw().rotate_world_cw().data == ARROW_SOUTH + assert world.rotate_world_cw().rotate_world_cw().left_is == Direction.East + + ARROW_EAST = [ + (".", ".", ".", ".", ".", "."), + (".", ".", ".", ".", ".", "."), + (".", ".", ".", "#", ".", "."), + (".", ".", ".", "#", "#", "."), + ("#", "#", "#", "#", "#", "#"), + (".", ".", ".", "#", "#", "."), + (".", ".", ".", "#", ".", "."), + (".", ".", ".", ".", ".", "."), + (".", ".", ".", ".", ".", "."), + ] + + assert world.rotate_world_cw().data == ARROW_EAST + assert world.rotate_world_cw().left_is == Direction.South + + world1 = world.rotate_world_cw().rotate_world_ccw() + world2 = world.rotate_world_ccw() + world3 = world.rotate_world_cw() + world4 = world.rotate_world_cw().rotate_world_cw() + assert world1.as_orientiented_north() == world2.as_orientiented_north() + assert world3.as_orientiented_north() == world4.as_orientiented_north() + assert world4.as_orientiented_north() == world1.as_orientiented_north()
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day14/tests/test_direction.html b/_modules/day14/tests/test_direction.html new file mode 100644 index 0000000..9083d93 --- /dev/null +++ b/_modules/day14/tests/test_direction.html @@ -0,0 +1,123 @@ + + + + + + day14.tests.test_direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day14.tests.test_direction

+"""Test direction class."""
+from day14.lib.direction import Direction
+
+
+
+[docs] +def test_direction() -> None: + """Test ``Direction`` class.""" + assert Direction.North.next_direction_cw() == Direction.East + assert Direction.East.next_direction_cw() == Direction.South + assert Direction.South.next_direction_cw() == Direction.West + assert Direction.West.next_direction_cw() == Direction.North + + assert Direction.North.next_direction_ccw() == Direction.West + assert Direction.East.next_direction_ccw() == Direction.North + assert Direction.South.next_direction_ccw() == Direction.East + assert Direction.West.next_direction_ccw() == Direction.South
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day15/day15.html b/_modules/day15/day15.html new file mode 100644 index 0000000..10a4d84 --- /dev/null +++ b/_modules/day15/day15.html @@ -0,0 +1,204 @@ + + + + + + day15.day15 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day15.day15

+"""Day15 solution."""
+from day15.lib.classes import AddRemove, Box, Lens, Step
+
+INPUT = "day15/input.txt"
+INPUT_SMALL = "day15/input-small.txt"
+
+
+
+[docs] +def get_input(path: str) -> list[str]: + """Get input into list of instructions to parse.""" + with open(path) as file: + data = file.read() + raw_steps = data.split(",") + return raw_steps
+ + + +
+[docs] +def get_string_hash(string: str) -> int: + """Returns a string's ``hash``.""" + value: int = 0 + for char in string: + value += ord(char) + value *= 17 + value %= 256 + return value
+ + + +
+[docs] +def parse_step_pt2(raw_step: str) -> Step: + """Handles as step in part 2.""" + if len(splits := raw_step.split("=")) == 2: + box = get_string_hash(splits[0]) + strength = int(splits[1].strip()) + return Step(splits[0], box, AddRemove.Add, strength) + elif len(splits := raw_step.split("-")) == 2: + box = get_string_hash(splits[0]) + return Step(splits[0], box, AddRemove.Remove) + + raise AssertionError("Raw step does not contain `-` or `=`")
+ + + +
+[docs] +def process_steps_pt2(steps: list[Step]) -> int: + """Process a list of steps.""" + boxes: list[Box] = [Box(i) for i in range(256)] + + for step in steps: + if step.process == AddRemove.Remove: + boxes[step.box].remove_lens(step.lens_name) + else: + if step.focal_length is None: + raise AssertionError("focal length should not be None") + lens = Lens(step.lens_name, step.focal_length) + boxes[step.box].add_lens(lens) + + return sum(box.calculate_power() for box in boxes)
+ + + +
+[docs] +def question1(raw_steps: list[str]) -> int: + """Returns the sum of hashes for every step.""" + return sum(get_string_hash(raw_step) for raw_step in raw_steps)
+ + + +
+[docs] +def question2(raw_steps: list[str]) -> int: + """Process each step into "lens" boxes and return the total lens power.""" + steps = [parse_step_pt2(raw_step) for raw_step in raw_steps] + return process_steps_pt2(steps)
+ + + +
+[docs] +def main() -> None: + """Read input and call question1/question2.""" + raw_steps = get_input(INPUT) + + # q1 + print(question1(raw_steps)) + + # q2 + print(question2(raw_steps))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day15/lib/classes.html b/_modules/day15/lib/classes.html new file mode 100644 index 0000000..f4614a2 --- /dev/null +++ b/_modules/day15/lib/classes.html @@ -0,0 +1,207 @@ + + + + + + day15.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day15.lib.classes

+"""Classes for day15."""
+from dataclasses import dataclass, field
+from enum import IntEnum
+
+
+
+[docs] +class AddRemove(IntEnum): + """Simple instruction to add or remove lens.""" + + Add = 0 + Remove = 1
+ + + +
+[docs] +@dataclass +class Step: + """well defined step.""" + + lens_name: str + box: int + process: AddRemove + focal_length: int | None = None
+ + + +
+[docs] +@dataclass +class Lens: + """Lens object.""" + + name: str + focal_length: int + + def __hash__(self) -> int: # pragma: no cover + """Custom hash function for use with ``set()``.""" + return hash(str(self.name) + ":" + str(self.focal_length)) + + def __str__(self) -> str: + """Custom compact string representation.""" + return f"[{self.name} {self.focal_length}]"
+ + + +
+[docs] +@dataclass +class Box: + r"""Box can contain a variety of ``Lens``\es.""" + + id: int = 0 + contents: list[Lens] = field(default_factory=list) + +
+[docs] + def add_lens(self, lens: Lens) -> None: + """Add/replace a lens to this box. + + If a lens name already exists, swap its power; + otherwise just add it + """ + for existing_lens in self.contents: + if lens.name == existing_lens.name: + existing_lens.focal_length = lens.focal_length + return + self.contents.append(lens)
+ + +
+[docs] + def remove_lens(self, lens_name: str) -> None: + """If a lens with a matching name is inside, remove it.""" + to_remove = None + for existing_lens in self.contents: + if existing_lens.name == lens_name: + to_remove = existing_lens + break + if to_remove is not None: + self.contents.remove(to_remove)
+ + + def __str__(self) -> str: + """Custom string for our box to see its id/contents easily.""" + return f"Box {self.id}: " + " ".join(str(lens) for lens in self.contents) + +
+[docs] + def calculate_power(self) -> int: + """Calculates power of the box by summing powers of the lenses.""" + result = 0 + for slot_number, lens in enumerate(self.contents): + box_power = 1 + self.id + slot_power = slot_number + 1 + power = box_power * slot_power * lens.focal_length + result += power + + return result
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day15/tests/test_classes.html b/_modules/day15/tests/test_classes.html new file mode 100644 index 0000000..a34d352 --- /dev/null +++ b/_modules/day15/tests/test_classes.html @@ -0,0 +1,147 @@ + + + + + + day15.tests.test_classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day15.tests.test_classes

+"""Tests classes for day15."""
+from day15.lib.classes import Box, Lens
+
+
+
+[docs] +def test_box() -> None: + """Test box class.""" + box = Box(0) + rn = Lens("rn", 1) + box.add_lens(rn) + assert box.contents == [rn] + cm = Lens("cm", 2) + box.add_lens(cm) + assert box.contents == [rn, cm] + + box = Box(3) + box.add_lens(pc := Lens("pc", 4)) + box.add_lens(ot := Lens("ot", 9)) + box.add_lens(ab := Lens("ab", 5)) + assert box.contents == [pc, ot, ab] + box.remove_lens("pc") + assert box.contents == [ot, ab] + box.add_lens(pc := Lens("pc", 6)) + assert box.contents == [ot, ab, pc] + box.add_lens(ot2 := Lens("ot", 7)) + assert box.contents == [ot2, ab, pc] + assert box.contents[0].focal_length == 7 + + assert str(box) == "Box 3: [ot 7] [ab 5] [pc 6]"
+ + + +
+[docs] +def test_lens() -> None: + """Tests lens class.""" + lens: Lens = Lens("rn", 1) + assert lens.focal_length == 1 + assert lens.name == "rn" + assert str(lens) == "[rn 1]"
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day15/tests/test_day15.html b/_modules/day15/tests/test_day15.html new file mode 100644 index 0000000..779069b --- /dev/null +++ b/_modules/day15/tests/test_day15.html @@ -0,0 +1,165 @@ + + + + + + day15.tests.test_day15 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day15.tests.test_day15

+"""Day15 main functions."""
+from day15.day15 import (
+    INPUT_SMALL,
+    get_input,
+    get_string_hash,
+    parse_step_pt2,
+    question1,
+    question2,
+)
+from day15.lib.classes import AddRemove, Step
+
+
+
+[docs] +def test_get_input() -> None: + """Test reading input function.""" + steps: list[str] = get_input(INPUT_SMALL) + assert len(steps) == 11 + assert steps[0] == "rn=1"
+ + + +
+[docs] +def test_parse_pt2() -> None: + """Test parsing input for part2.""" + steps: list[str] = get_input(INPUT_SMALL) + step: Step = parse_step_pt2(steps[0]) + assert step.lens_name == "rn" + assert step.process == AddRemove.Add
+ + + +
+[docs] +def test_questions() -> None: + """Test question1/question2.""" + steps: list[str] = get_input(INPUT_SMALL) + assert question1(steps) == 1320 + assert question2(steps) == 145
+ + + +
+[docs] +def test_get_string_hash() -> None: + """Test string hashing function.""" + assert get_string_hash("rn=1") == 30 + assert get_string_hash("rn=1") == 30 + assert get_string_hash("cm-") == 253 + assert get_string_hash("qp=3") == 97 + assert get_string_hash("cm=2") == 47 + assert get_string_hash("qp-") == 14 + assert get_string_hash("pc=4") == 180 + assert get_string_hash("ot=9") == 9 + assert get_string_hash("ab=5") == 197 + assert get_string_hash("pc-") == 48 + assert get_string_hash("pc=6") == 214 + assert get_string_hash("ot=7") == 231
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/day16.html b/_modules/day16/day16.html new file mode 100644 index 0000000..4ee73e7 --- /dev/null +++ b/_modules/day16/day16.html @@ -0,0 +1,186 @@ + + + + + + day16.day16 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.day16

+"""day16 solution."""
+
+
+import os
+
+from tqdm.contrib.concurrent import process_map
+
+from day16.lib.direction import Direction
+from day16.lib.laser import Laser
+from day16.lib.parsers import get_input
+from day16.lib.world import World
+
+INPUT = "day16/input.txt"
+INPUT_SMALL = "day16/input-small.txt"
+
+
+
+[docs] +def solve_task(task: Laser, world: World) -> int: + """Calculates number of energized tiles.""" + return world.solve(task).num_energized()
+ + + +
+[docs] +def solve_task_wrapper(args: tuple[Laser, World]) -> int: + """Wraps solve_task in multiprocessing, since it only takes one arg.""" + task, world = args + return solve_task(task, world)
+ + + +
+[docs] +def part1(world: World) -> int: + """Return number of energized tiles.""" + laser = Laser(0, 0, Direction.EAST) + solved_world = world.solve(laser) + return solved_world.num_energized()
+ + + +
+[docs] +def part2(world: World) -> int: + """Calculate most energized tiles by firing laser from every entrypoint.""" + tasks = [] + # part 2: brute force coz our impl is already cached/backtracked + # 1T -> 3.3s + # 12T -> 1.1s + for col in range(world.num_cols): + tasks.append((Laser(0, col, Direction.SOUTH), world)) + tasks.append((Laser(world.num_rows - 1, col, Direction.NORTH), world)) + + for row in range(world.num_rows): + tasks.append((Laser(row, 0, Direction.EAST), world)) + tasks.append((Laser(row, world.num_cols - 1, Direction.WEST), world)) + + cpu_count = os.cpu_count() or 1 + chunk_size = (len(tasks) // cpu_count) + 1 + results: list[int] + results = process_map(solve_task_wrapper, tasks, chunksize=chunk_size) # type: ignore + + return max(results)
+ + + +
+[docs] +def main() -> None: + """Read input and run part1/part2.""" + world = get_input(INPUT) + + print(part1(world)) + print(part2(world))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/lib/cells.html b/_modules/day16/lib/cells.html new file mode 100644 index 0000000..9590c21 --- /dev/null +++ b/_modules/day16/lib/cells.html @@ -0,0 +1,273 @@ + + + + + + day16.lib.cells — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.lib.cells

+"""Cell classes."""
+from abc import ABC, abstractmethod
+from typing import Callable, Dict, Type, TypeVar
+
+from day16.lib.direction import Direction
+from day16.lib.laser import Laser
+
+
+
+[docs] +class Cell(ABC): + """Abstract cell class.""" + + contents: str + + # each cell can register itself to us + CELL_TYPES: Dict[str, Type["Cell"]] = {} + + def __init__(self, contents: str): + """Default constructor, sets our contents.""" + self.contents = contents + +
+[docs] + @staticmethod + def register_cell_type( + cell_contents: str, + ) -> Callable[[type["Cell"]], type["Cell"]]: + """Registers a cell type to this factory.""" + + def decorator(cls: Type["Cell"]) -> Type["Cell"]: + Cell.CELL_TYPES[cell_contents] = cls + return cls + + return decorator
+ + +
+[docs] + @staticmethod + def construct(contents: str) -> "Cell": + """Construct proper cell from given contents.""" + try: + return Cell.CELL_TYPES[contents](contents) + except KeyError: + raise AssertionError(f"unrecognized content {contents}")
+ + +
+[docs] + @abstractmethod + def next_lasers(self, laser: Laser) -> list[Laser]: + """Return next lasers given a laser entering this cell.""" + raise AssertionError("Not supported", laser)
+
+ + + +
+[docs] +@Cell.register_cell_type(".") +class DotCell(Cell): + """A dot cell.""" + +
+[docs] + def next_lasers(self, laser: Laser) -> list[Laser]: + """Lasers pass directly through this tile.""" + row, col = laser.direction.offset(laser.row, laser.col) + return [Laser(row, col, laser.direction)]
+
+ + + +
+[docs] +@Cell.register_cell_type("-") +class DashCell(Cell): + """A ``-`` cell.""" + +
+[docs] + def next_lasers(self, laser: Laser) -> list[Laser]: + """Lasers must end up going east/west after passing through this cell.""" + if laser.direction in [Direction.EAST, Direction.WEST]: + row, col = laser.direction.offset(laser.row, laser.col) + return [Laser(row, col, laser.direction)] + elif laser.direction in [Direction.NORTH, Direction.SOUTH]: + row, col = laser.row, laser.col + return [ + Laser(row, col + 1, Direction.EAST), + Laser(row, col - 1, Direction.WEST), + ] + raise AssertionError(f"Unknown direction {laser.direction}")
+
+ + + +
+[docs] +@Cell.register_cell_type("|") +class PipeCell(Cell): + """A ``|`` cell.""" + +
+[docs] + def next_lasers(self, laser: Laser) -> list[Laser]: + """Lasers must end up going north/south after passing through this cell.""" + if laser.direction in [Direction.NORTH, Direction.SOUTH]: + row, col = laser.direction.offset(laser.row, laser.col) + return [Laser(row, col, laser.direction)] + elif laser.direction in [Direction.EAST, Direction.WEST]: + row, col = laser.row, laser.col + return [ + Laser(row - 1, col, Direction.NORTH), + Laser(row + 1, col, Direction.SOUTH), + ] + raise AssertionError(f"Unknown direction {laser.direction}")
+
+ + + +
+[docs] +@Cell.register_cell_type("/") +class ForwardSlashCell(Cell): + """A ``/`` cell.""" + +
+[docs] + def next_lasers(self, laser: Laser) -> list[Laser]: + """Lasers go diagonal mode.""" + row, col = laser.row, laser.col + if laser.direction == Direction.EAST: + return [Laser(row - 1, col, Direction.NORTH)] + if laser.direction == Direction.NORTH: + return [Laser(row, col + 1, Direction.EAST)] + if laser.direction == Direction.SOUTH: + return [Laser(row, col - 1, Direction.WEST)] + if laser.direction == Direction.WEST: + return [Laser(row + 1, col, Direction.SOUTH)] + raise AssertionError(f"Unknown direction {laser.direction}")
+
+ + + +
+[docs] +@Cell.register_cell_type("\\") +class BackSlashCell(Cell): + r"""A ``\`` cell.""" + +
+[docs] + def next_lasers(self, laser: Laser) -> list[Laser]: + """Lasers go diagonal mode.""" + row, col = laser.row, laser.col + if laser.direction == Direction.EAST: + return [Laser(row + 1, col, Direction.SOUTH)] + if laser.direction == Direction.SOUTH: + return [Laser(row, col + 1, Direction.EAST)] + if laser.direction == Direction.NORTH: + return [Laser(row, col - 1, Direction.WEST)] + if laser.direction == Direction.WEST: + return [Laser(row - 1, col, Direction.NORTH)] + raise AssertionError(f"Unknown direction {laser.direction}")
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/lib/direction.html b/_modules/day16/lib/direction.html new file mode 100644 index 0000000..357cb9f --- /dev/null +++ b/_modules/day16/lib/direction.html @@ -0,0 +1,146 @@ + + + + + + day16.lib.direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.lib.direction

+"""Direction class."""
+from enum import IntEnum
+
+
+
+[docs] +class Direction(IntEnum): + """Simple direction enum.""" + + NORTH = 0 + EAST = 1 + SOUTH = 2 + WEST = 3 + + def __str__(self) -> str: + """Return ``NORTH`` etc.""" + return self.name + +
+[docs] + def opposite(self) -> "Direction": + """Returns opposite direction.""" + int_value = int(self) + return Direction((int_value + 2) % len(Direction))
+ + +
+[docs] + def offset(self, row: int, col: int) -> tuple[int, int]: + """Offset a row/col by our direction.""" + if self == Direction.NORTH: + return (row - 1, col) + if self == Direction.EAST: + return (row, col + 1) + if self == Direction.SOUTH: + return (row + 1, col) + if self == Direction.WEST: + return (row, col - 1) + raise AssertionError("direction not suppported", self)
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/lib/laser.html b/_modules/day16/lib/laser.html new file mode 100644 index 0000000..b2de1a5 --- /dev/null +++ b/_modules/day16/lib/laser.html @@ -0,0 +1,122 @@ + + + + + + day16.lib.laser — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.lib.laser

+"""laser instance class."""
+from dataclasses import dataclass
+
+from day16.lib.direction import Direction
+
+
+
+[docs] +@dataclass(frozen=True) # frozen so we can hash +class Laser: + """Laser position + direction.""" + + row: int + col: int + + direction: Direction
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/lib/parsers.html b/_modules/day16/lib/parsers.html new file mode 100644 index 0000000..b217e05 --- /dev/null +++ b/_modules/day16/lib/parsers.html @@ -0,0 +1,123 @@ + + + + + + day16.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.lib.parsers

+"""Parsers for input file."""
+from day16.lib.cells import Cell
+from day16.lib.world import World
+
+
+
+[docs] +def get_input(path: str) -> World: + """Read input file and return well formed :class:World.""" + with open(path) as file: + all_cells: list[list[Cell]] = [] + for line in file: + line = line.strip() + cells = [Cell.construct(char) for char in line] + all_cells.append(cells) + world = World(all_cells) + return world
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/lib/world.html b/_modules/day16/lib/world.html new file mode 100644 index 0000000..620a39c --- /dev/null +++ b/_modules/day16/lib/world.html @@ -0,0 +1,209 @@ + + + + + + day16.lib.world — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.lib.world

+"""Well defined world classes."""
+from dataclasses import dataclass, field
+
+from day16.lib.cells import Cell
+from day16.lib.laser import Laser
+
+
+
+[docs] +class SolvedWorld: + """A solved world class, stores how many lasers are in each tile.""" + + data: list[list[list[Laser]]] + + def __init__(self, num_rows: int, num_cols: int): + """Initialises empty 2d array of lists of lasers.""" + self.data = [[[] for _ in range(num_cols)] for _ in range(num_rows)] + +
+[docs] + def already_solved(self, laser: Laser) -> bool: + """Returns true if laser already calculated.""" + solutions = self.data[laser.row][laser.col] + if laser in solutions: + return True + return False
+ + +
+[docs] + def add_laser(self, laser: Laser) -> None: + """Adds laser to cell.""" + solutions = self.data[laser.row][laser.col] + solutions.append(laser)
+ + + def __str__(self) -> str: + """Custom str to show how many lasers on each tile.""" + row_strs = [] + for row in self.data: + row_str = "".join(str(len(col)) for col in row) + row_strs.append(row_str) + return "\n".join(row_strs) + +
+[docs] + def num_energized(self) -> int: + """Return number of energized cells.""" + result: int = 0 + for row in self.data: + result += sum(1 if len(col) >= 1 else 0 for col in row) + return result
+
+ + + +
+[docs] +@dataclass +class World: + """The input world (mirrors/empty tiles).""" + + data: list[list[Cell]] + + num_rows: int = field(init=False, repr=False) + num_cols: int = field(init=False, repr=False) + + def __post_init__(self) -> None: + """Initializes our num_rows/num_cols.""" + self.num_rows = len(self.data) + self.num_cols = len(self.data[0]) + +
+[docs] + def solve(self, start_laser: Laser) -> SolvedWorld: + """Solve our world.""" + solved_world = SolvedWorld(self.num_rows, self.num_cols) + active_lasers = [start_laser] + while len(active_lasers) > 0: + laser = active_lasers.pop(0) + if self.is_oob(laser): + continue + if solved_world.already_solved(laser): + continue + solved_world.add_laser(laser) + + cell: Cell = self.data[laser.row][laser.col] + next_lasers = cell.next_lasers(laser) + active_lasers.extend(next_lasers) + return solved_world
+ + +
+[docs] + def is_oob(self, laser: Laser) -> bool: + """True if laser is out of bounds.""" + return ( + laser.row < 0 + or laser.row >= self.num_rows + or laser.col < 0 + or laser.col >= self.num_cols + )
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/tests/test_cells.html b/_modules/day16/tests/test_cells.html new file mode 100644 index 0000000..b95ecc2 --- /dev/null +++ b/_modules/day16/tests/test_cells.html @@ -0,0 +1,241 @@ + + + + + + day16.tests.test_cells — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.tests.test_cells

+"""Tests for each cell."""
+from day16.lib.cells import (
+    BackSlashCell,
+    Cell,
+    DashCell,
+    DotCell,
+    ForwardSlashCell,
+    PipeCell,
+)
+from day16.lib.direction import Direction
+from day16.lib.laser import Laser
+
+WEST_LASER = Laser(5, 4, Direction.WEST)
+EAST_LASER = Laser(5, 6, Direction.EAST)
+NORTH_LASER = Laser(4, 5, Direction.NORTH)
+SOUTH_LASER = Laser(6, 5, Direction.SOUTH)
+
+
+
+[docs] +def test_cell() -> None: + """Tests ``Cell.construct()``.""" + dot_cell = Cell.construct(".") + assert isinstance(dot_cell, DotCell) + + dash_cell = Cell.construct("-") + assert isinstance(dash_cell, DashCell) + + pipe_cell = Cell.construct("|") + assert isinstance(pipe_cell, PipeCell) + + fslash_cell = Cell.construct("/") + assert isinstance(fslash_cell, ForwardSlashCell) + + bslash_cell = Cell.construct("\\") + assert isinstance(bslash_cell, BackSlashCell)
+ + + +
+[docs] +def test_dotcell() -> None: + """Test ``DotCell``.""" + laser: Laser = Laser(5, 5, Direction.NORTH) + cell: Cell = DotCell(".") + assert cell.next_lasers(laser) == [NORTH_LASER] + + laser = Laser(5, 5, Direction.EAST) + assert cell.next_lasers(laser) == [EAST_LASER] + + laser = Laser(5, 5, Direction.WEST) + assert cell.next_lasers(laser) == [WEST_LASER] + + laser = Laser(5, 5, Direction.SOUTH) + assert cell.next_lasers(laser) == [SOUTH_LASER]
+ + + +
+[docs] +def test_dashcell() -> None: + """Test ``DashCell``.""" + laser: Laser = Laser(5, 5, Direction.NORTH) + cell: Cell = DashCell("-") + + assert set(cell.next_lasers(laser)) == {WEST_LASER, EAST_LASER} + + laser = Laser(5, 5, Direction.EAST) + assert cell.next_lasers(laser) == [EAST_LASER] + + laser = Laser(5, 5, Direction.WEST) + assert cell.next_lasers(laser) == [WEST_LASER] + + laser = Laser(5, 5, Direction.SOUTH) + assert set(cell.next_lasers(laser)) == {WEST_LASER, EAST_LASER}
+ + + +
+[docs] +def test_pipecell() -> None: + """Test ``PipeCell``.""" + laser: Laser = Laser(5, 5, Direction.NORTH) + cell: Cell = PipeCell("|") + + assert cell.next_lasers(laser) == [NORTH_LASER] + + laser = Laser(5, 5, Direction.EAST) + assert set(cell.next_lasers(laser)) == {NORTH_LASER, SOUTH_LASER} + + laser = Laser(5, 5, Direction.WEST) + assert set(cell.next_lasers(laser)) == {NORTH_LASER, SOUTH_LASER} + + laser = Laser(5, 5, Direction.SOUTH) + assert cell.next_lasers(laser) == [SOUTH_LASER]
+ + + +
+[docs] +def test_forwardslashcell() -> None: + """Test ``ForwardSlashCell``.""" + laser: Laser = Laser(5, 5, Direction.NORTH) + cell: Cell = ForwardSlashCell("/") + + assert cell.next_lasers(laser) == [EAST_LASER] + + laser = Laser(5, 5, Direction.EAST) + assert cell.next_lasers(laser) == [NORTH_LASER] + + laser = Laser(5, 5, Direction.WEST) + assert cell.next_lasers(laser) == [SOUTH_LASER] + + laser = Laser(5, 5, Direction.SOUTH) + assert cell.next_lasers(laser) == [WEST_LASER]
+ + + +
+[docs] +def test_backslashcell() -> None: + """Test ``BackSlashCell``.""" + laser: Laser = Laser(5, 5, Direction.NORTH) + cell: Cell = BackSlashCell("\\") + + assert cell.next_lasers(laser) == [WEST_LASER] + + laser = Laser(5, 5, Direction.EAST) + assert cell.next_lasers(laser) == [SOUTH_LASER] + + laser = Laser(5, 5, Direction.WEST) + assert cell.next_lasers(laser) == [NORTH_LASER] + + laser = Laser(5, 5, Direction.SOUTH) + assert cell.next_lasers(laser) == [EAST_LASER]
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/tests/test_day16.html b/_modules/day16/tests/test_day16.html new file mode 100644 index 0000000..3123ad6 --- /dev/null +++ b/_modules/day16/tests/test_day16.html @@ -0,0 +1,131 @@ + + + + + + day16.tests.test_day16 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.tests.test_day16

+"""Test day16 main functions."""
+from typing import TYPE_CHECKING
+
+from day16.day16 import INPUT_SMALL, part1, part2
+from day16.lib.parsers import get_input
+
+if TYPE_CHECKING:
+    from day16.lib.world import World
+
+
+
+[docs] +def test_part1() -> None: + """Test part1.""" + world: World = get_input(INPUT_SMALL) + assert part1(world) == 46
+ + + +
+[docs] +def test_part2() -> None: + """Test part2.""" + world: World = get_input(INPUT_SMALL) + assert part2(world) == 51
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/tests/test_direction.html b/_modules/day16/tests/test_direction.html new file mode 100644 index 0000000..79bfcef --- /dev/null +++ b/_modules/day16/tests/test_direction.html @@ -0,0 +1,125 @@ + + + + + + day16.tests.test_direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.tests.test_direction

+"""Test direction."""
+from day16.lib.direction import Direction
+
+
+
+[docs] +def test_direction() -> None: + """Test ``Direction`` class.""" + assert Direction.WEST.opposite() == Direction.EAST + assert Direction.EAST.opposite() == Direction.WEST + assert Direction.SOUTH.opposite() == Direction.NORTH + assert Direction.NORTH.opposite() == Direction.SOUTH + + assert Direction.EAST.offset(0, 0) == (0, 1) + assert Direction.WEST.offset(0, 0) == (0, -1) + assert Direction.NORTH.offset(0, 0) == (-1, 0) + assert Direction.SOUTH.offset(0, 0) == (1, 0) + + assert str(Direction.WEST) == "WEST"
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day16/tests/test_world.html b/_modules/day16/tests/test_world.html new file mode 100644 index 0000000..f77cc6f --- /dev/null +++ b/_modules/day16/tests/test_world.html @@ -0,0 +1,141 @@ + + + + + + day16.tests.test_world — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day16.tests.test_world

+"""Test World class."""
+from typing import TYPE_CHECKING
+
+from day16.day16 import INPUT_SMALL
+from day16.lib.direction import Direction
+from day16.lib.laser import Laser
+from day16.lib.parsers import get_input
+
+if TYPE_CHECKING:
+    from day16.lib.world import SolvedWorld, World
+
+
+
+[docs] +def test_world() -> None: + """Test ``World`` class.""" + world: World = get_input(INPUT_SMALL) + start_laser: Laser = Laser(0, 0, Direction.EAST) + solved_world: SolvedWorld = world.solve(start_laser) + + print(solved_world) + assert str(solved_world) == "\n".join( + [ + "1211110000", + "0100010000", + "0100011111", + "0100011000", + "0100011000", + "0100011000", + "0100122100", + "1211111100", + "0111121100", + "0100010100", + ] + )
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/day17.html b/_modules/day17/day17.html new file mode 100644 index 0000000..48f121c --- /dev/null +++ b/_modules/day17/day17.html @@ -0,0 +1,174 @@ + + + + + + day17.day17 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.day17

+"""day17 solution."""
+from typing import Optional
+
+from colorama import Back
+
+from day17.lib.classes import Step, WorldPart1, WorldPart2
+from day17.lib.parsers import get_input
+
+INPUT = "day17/input.txt"
+INPUT_SMALL = "day17/input-small.txt"
+INPUT_PT2 = "day17/input-pt2.txt"
+
+
+
+[docs] +def solve_and_print(world: WorldPart1) -> int: + """Solve and print.""" + result = world.solve() + world_string = [[str(val) for val in row] for row in world.costs] + + step: Optional[Step] = result + while step is not None: + output_str = Back.GREEN + str(step.direction) + Back.BLACK + world_string[step.row][step.col] = output_str + step = step.src_step + + print("\n".join("".join(val for val in row) for row in world_string)) + return result.total_cost
+ + + +
+[docs] +def part1(input: list[list[int]]) -> int: + """Load input and solve part1. + + (minimum path, max 3 in one direction) + """ + world: WorldPart1 = WorldPart1(input) + return solve_and_print(world)
+ + + +
+[docs] +def part2(input: list[list[int]]) -> int: + """Load input and solve part2. + + (minimum path, min 4, max 10 in one direction) + """ + world: WorldPart2 = WorldPart2(input) + return solve_and_print(world)
+ + + +
+[docs] +def main() -> None: + """Load input and run part1/part2.""" + input: list[list[int]] = get_input(INPUT) + # q1 + print(f"num steps: {part1(input)}") + # q2 + print(f"num steps: {part2(input)}")
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/lib/classes.html b/_modules/day17/lib/classes.html new file mode 100644 index 0000000..2c14ed6 --- /dev/null +++ b/_modules/day17/lib/classes.html @@ -0,0 +1,345 @@ + + + + + + day17.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.lib.classes

+"""classes for day 17."""
+from dataclasses import dataclass, field
+from queue import PriorityQueue
+from typing import Optional
+
+from day17.lib.direction import ALL_DIRECTIONS, Direction
+
+
+
+[docs] +@dataclass(order=True, frozen=True) +class Step: + """Represents one "step", which could be a multi-step.""" + + total_cost: int + row: int + col: int + direction: Direction + consecutive_steps: int + + src_step: Optional["Step"] = field(repr=False, hash=False)
+ + + +
+[docs] +class TileCache: + """A cache of shortest routes to a tile from each direction.""" + + cache: dict[Direction, list[Step | None]] + cache_min: int # min steps in direction + cache_max: int # max steps in direction + + def __init__(self, cache_min: int, cache_max: int): + """Initialize the tile with an empty entry from each direction.""" + cache_length = cache_max - cache_min + 1 + self.cache = {key: [None] * cache_length for key in ALL_DIRECTIONS} + self.cache_min = cache_min + self.cache_max = cache_max + + def __getitem__(self, dir_steps: tuple[Direction, int]) -> Step | None: + """Lookup our cache based on how many steps in one direction we took to get here.""" + direction, steps = dir_steps + return self.cache[direction][steps - self.cache_min] + + def __setitem__(self, dir_steps: tuple[Direction, int], item: Step) -> None: + """Set steps based on how many steps in one direction we took to get here.""" + direction, steps = dir_steps + self.cache[direction][steps - self.cache_min] = item
+ + + +
+[docs] +class SolutionCache: + """2d array of tilecaches.""" + + cache: list[list[TileCache]] + + def __init__(self, num_rows: int, num_cols: int, cache_min: int, cache_max: int): + """Generate empty tile cache.""" + self.cache = [ + [TileCache(cache_min, cache_max) for _ in range(num_cols)] + for _ in range(num_rows) + ] + +
+[docs] + def add_solution(self, step: Step) -> bool: + """Adds solution to cache returns whether an improvement was made.""" + tile_cache = self.cache[step.row][step.col] + existing_item = tile_cache[step.direction, step.consecutive_steps] + if existing_item is None: + tile_cache[step.direction, step.consecutive_steps] = step + return True + + # due to the way that we run in BFS, we shouldn't be getting + # into this branch + if step.total_cost < existing_item.total_cost: + raise AssertionError("this shouldn't be possible") + # tile_cache[step.direction, step.consecutive_steps] = step + # return True + return False
+
+ + + +
+[docs] +@dataclass +class WorldPart1: + """World for part1.""" + + costs: list[list[int]] + num_rows: int = field(init=False) + num_cols: int = field(init=False) + + def __post_init__(self) -> None: + """Post initialize cached properties.""" + self.num_rows = len(self.costs) + self.num_cols = len(self.costs[0]) + + def __getitem__(self, row_col: tuple[int, int]) -> int | None: + """Returns cost at given row/col.""" + row, col = row_col + if self.is_oob(row, col): + return None + return self.costs[row][col] + +
+[docs] + def create_step(self, step: Step, direction: Direction) -> Step | None: + """Create step from previous step and a given direction. + + Returns None if the step is invalid or suboptimal + """ + row, col = direction.offset(step.row, step.col) + if (cost := self[row, col]) is None: + return None + + if direction == step.direction.opposite(): + return None + if direction == step.direction: + consecutive = step.consecutive_steps + 1 + if consecutive > 3: + return None + else: + consecutive = 1 + + return Step(step.total_cost + cost, row, col, direction, consecutive, step)
+ + +
+[docs] + def solve(self) -> Step: + """Solve using dynamic programming. + + Returns final step which contains src steps; + so we have the entire path + """ + # we need to do this via DP + solution_cache = SolutionCache(self.num_rows, self.num_cols, 1, 3) + step: Step = Step(0, 0, 0, Direction.NORTH, 0, None) + steps_to_explore: PriorityQueue[Step] = PriorityQueue() + steps_to_explore.put(step) + + while not steps_to_explore.empty(): + step = steps_to_explore.get() + if step.row == self.num_rows - 1 and step.col == self.num_cols - 1: + return step # result! + if not solution_cache.add_solution(step): + continue + + for direction in ALL_DIRECTIONS: + if (new_step := self.create_step(step, direction)) is not None: + steps_to_explore.put(new_step) + + raise AssertionError("No solution found!")
+ + +
+[docs] + def is_oob(self, row: int, col: int) -> bool: + """Returns if we are out of bounds. + + Args: + row (int): row to check + col (int): col to check + + Returns: + bool: if we are out of bounds. + """ + return row < 0 or row >= self.num_rows or col < 0 or col >= self.num_cols
+
+ + + +
+[docs] +class WorldPart2(WorldPart1): + """Extension of part1 with a few overrides.""" + +
+[docs] + def create_step(self, step: Step, direction: Direction) -> Step | None: + """Create step from previous step and a given direction.""" + if direction == step.direction.opposite(): + return None + if direction == step.direction: + row, col = direction.offset(step.row, step.col) + cost = self[row, col] + if cost is None: + return None + consecutive = step.consecutive_steps + 1 + if consecutive > 10: + return None + return Step(step.total_cost + cost, row, col, direction, consecutive, step) + else: + consecutive = 4 + row_cols = direction.offset_list(step.row, step.col) + multi_cost = 0 + for row_col in row_cols: + row, col = row_col + cost = self[row, col] + if cost is None: + return None + multi_cost += cost + + return Step( + step.total_cost + multi_cost, row, col, direction, consecutive, step + )
+ + +
+[docs] + def solve(self) -> Step: + """Solve using DP. + + Returns final step which contains src steps; + so we have the entire path + """ + # we need to do this via DP + solution_cache = SolutionCache(self.num_rows, self.num_cols, 4, 10) + step: Step = Step(0, 0, 0, Direction.NORTH, 0, None) + steps_to_explore: PriorityQueue[Step] = PriorityQueue() + steps_to_explore.put(step) + + while not steps_to_explore.empty(): + step = steps_to_explore.get() + if step.row == self.num_rows - 1 and step.col == self.num_cols - 1: + return step # result! + if not solution_cache.add_solution(step): + continue + + for direction in ALL_DIRECTIONS: + if (new_step := self.create_step(step, direction)) is not None: + steps_to_explore.put(new_step) + raise AssertionError("No solution found!")
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/lib/direction.html b/_modules/day17/lib/direction.html new file mode 100644 index 0000000..b87b151 --- /dev/null +++ b/_modules/day17/lib/direction.html @@ -0,0 +1,173 @@ + + + + + + day17.lib.direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.lib.direction

+"""Direction class."""
+from enum import IntEnum
+
+
+
+[docs] +class Direction(IntEnum): + """Simple direction enum.""" + + NORTH = 0 + EAST = 1 + SOUTH = 2 + WEST = 3 + + def __str__(self) -> str: + """Human readable string (``^>v<``).""" + if self == Direction.NORTH: + return "^" + elif self == Direction.EAST: + return ">" + elif self == Direction.SOUTH: + return "v" + elif self == Direction.WEST: + return "<" + raise AssertionError("invalid value") + +
+[docs] + def opposite(self) -> "Direction": + """Return opposite direction ``W``<->``E`` ``S``<->``N``.""" + int_value = int(self) + return Direction((int_value + 2) % len(Direction))
+ + +
+[docs] + def offset(self, row: int, col: int) -> tuple[int, int]: + """Offset a position by our direction.""" + if self == Direction.NORTH: + return (row - 1, col) + if self == Direction.EAST: + return (row, col + 1) + if self == Direction.SOUTH: + return (row + 1, col) + if self == Direction.WEST: + return (row, col - 1) + raise AssertionError("direction not suppported", self)
+ + +
+[docs] + def offset_list(self, row: int, col: int, size: int = 4) -> list[tuple[int, int]]: + """Offset a position by our direction and size vector.""" + offsets = range(1, size + 1) + if self == Direction.NORTH: + return [(row - i, col) for i in offsets] + if self == Direction.EAST: + return [((row, col + i)) for i in offsets] + if self == Direction.SOUTH: + return [(row + i, col) for i in offsets] + if self == Direction.WEST: + return [(row, col - i) for i in offsets] + raise AssertionError("direction not suppported", self)
+
+ + + +ALL_DIRECTIONS = list(Direction) +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/lib/parsers.html b/_modules/day17/lib/parsers.html new file mode 100644 index 0000000..8be8e81 --- /dev/null +++ b/_modules/day17/lib/parsers.html @@ -0,0 +1,118 @@ + + + + + + day17.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.lib.parsers

+"""parse input file."""
+
+
+
+[docs] +def get_input(path: str) -> list[list[int]]: + """Convert input into world dataclass.""" + with open(path, "r", encoding="utf8") as file: + data: list[list[int]] = [] + for line in file: + data.append([int(char) for char in line.strip()]) + return data
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/tests/test_day17.html b/_modules/day17/tests/test_day17.html new file mode 100644 index 0000000..8c54889 --- /dev/null +++ b/_modules/day17/tests/test_day17.html @@ -0,0 +1,121 @@ + + + + + + day17.tests.test_day17 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.tests.test_day17

+"""Test main functions for day17."""
+from day17.day17 import INPUT_PT2, INPUT_SMALL, part1, part2
+from day17.lib.parsers import get_input
+
+
+
+[docs] +def test_parts() -> None: + """Test ``part1()`` and ``part2``.""" + input: list[list[int]] = get_input(INPUT_SMALL) + assert part1(input) == 102 + assert part2(input) == 94 + + input_pt2: list[list[int]] = get_input(INPUT_PT2) + assert part2(input_pt2) == 71
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/tests/test_direction.html b/_modules/day17/tests/test_direction.html new file mode 100644 index 0000000..a3a1543 --- /dev/null +++ b/_modules/day17/tests/test_direction.html @@ -0,0 +1,128 @@ + + + + + + day17.tests.test_direction — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.tests.test_direction

+"""Test direction class."""
+from day17.lib.direction import Direction
+
+
+
+[docs] +def test_direction() -> None: + """Test ``Direction`` class.""" + assert Direction.WEST.opposite() == Direction.EAST + assert Direction.EAST.opposite() == Direction.WEST + assert Direction.SOUTH.opposite() == Direction.NORTH + assert Direction.NORTH.opposite() == Direction.SOUTH + + assert Direction.EAST.offset(0, 0) == (0, 1) + assert Direction.WEST.offset(0, 0) == (0, -1) + assert Direction.NORTH.offset(0, 0) == (-1, 0) + assert Direction.SOUTH.offset(0, 0) == (1, 0) + + assert Direction.EAST.offset_list(0, 0) == [(0, 1), (0, 2), (0, 3), (0, 4)] + assert Direction.WEST.offset_list(0, 0) == [(0, -1), (0, -2), (0, -3), (0, -4)] + assert Direction.NORTH.offset_list(0, 0) == [(-1, 0), (-2, 0), (-3, 0), (-4, 0)] + assert Direction.SOUTH.offset_list(0, 0) == [(1, 0), (2, 0), (3, 0), (4, 0)]
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day17/tests/test_parsers.html b/_modules/day17/tests/test_parsers.html new file mode 100644 index 0000000..fd5f963 --- /dev/null +++ b/_modules/day17/tests/test_parsers.html @@ -0,0 +1,119 @@ + + + + + + day17.tests.test_parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day17.tests.test_parsers

+"""Test parsing function."""
+from day17.day17 import INPUT_SMALL
+from day17.lib.parsers import get_input
+
+
+
+[docs] +def test_parser() -> None: + """Test parsing function.""" + data: list[list[int]] = get_input(INPUT_SMALL) + + assert data[0] == [2, 4, 1, 3, 4, 3, 2, 3, 1, 1, 3, 2, 3] + assert len(data) == 13
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day18/day18a.html b/_modules/day18/day18a.html new file mode 100644 index 0000000..03ef152 --- /dev/null +++ b/_modules/day18/day18a.html @@ -0,0 +1,369 @@ + + + + + + day18.day18a — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day18.day18a

+"""day18 solution."""
+
+from dataclasses import dataclass
+from enum import StrEnum
+from queue import Queue
+
+from day18.lib.tile import EdgeTile, HoleTile, Tile
+
+INPUT = "day18/input.txt"
+INPUT_SMALL = "day18/input-small.txt"
+
+
+
+[docs] +@dataclass +class Position: + """Simple 2d point.""" + + row: int = 0 + col: int = 0
+ + + +
+[docs] +class Direction(StrEnum): + """Cardinal direction in ``UDLR``.""" + + Up = "U" + Down = "D" + Left = "L" + Right = "R" + + def __repr__(self) -> str: # pragma: no cover + """Custom repr for easy printing.""" + return str(self) + + def __str__(self) -> str: + """Up/Down/Left/Right.""" + return self.name
+ + + +
+[docs] +@dataclass(frozen=True) +class Command: + """Well defined command dataclass.""" + + direction: Direction + steps: int + color: str
+ + + +
+[docs] +def generate_offsets( + position: Position, direction: Direction, steps: int +) -> list[Position]: + """Generate position offsets. + + Args: + position (Position): base position + direction (Direction): direction of travel + steps (int): number of steps to generate + + Raises: + AssertionError: If direciton is invalid. + + Returns: + list[Position]: list of new positions. + """ + if direction == Direction.Right: + return [Position(position.row, position.col + i) for i in range(1, steps + 1)] + if direction == Direction.Left: + return [Position(position.row, position.col - i) for i in range(1, steps + 1)] + if direction == Direction.Down: + return [Position(position.row + i, position.col) for i in range(1, steps + 1)] + if direction == Direction.Up: + return [Position(position.row - i, position.col) for i in range(1, steps + 1)] + raise AssertionError(f"Direction not supported{direction}")
+ + + +
+[docs] +class Matrix: + """2d array representing world.""" + + contents: list[list[Tile]] + + min_pos: Position + max_pos: Position + + num_rows: int + num_cols: int + + wall_tiles = 0 + dug_tiles = 0 + + def __init__(self, min_pos: Position, max_pos: Position) -> None: + """Generate 2d array with min/max position for offsetting.""" + self.num_rows = max_pos.row - min_pos.row + 1 + self.num_cols = max_pos.col - min_pos.col + 1 + self.contents = [ + [Tile() for _ in range(self.num_cols)] for _ in range(self.num_rows) + ] + +
+[docs] + def process_command(self, miner_pos: Position, command: Command) -> Position: + """Process command. + + This moves the miner around. + + Args: + miner_pos (Position): miner's current position + command (Command): command to process + + Returns: + Position: miner's new position. + """ + offsets = generate_offsets(miner_pos, command.direction, command.steps) + self.wall_tiles += len(offsets) + for offset in offsets: + self.contents[offset.row][offset.col] = EdgeTile(color=command.color) + return offsets[-1]
+ + +
+[docs] + def dig_out(self) -> None: + """Dig out non-perimeter tiles using flood-fill.""" + # cache for what's been visited + visited: list[list[bool]] = [ + [False for _ in range(self.num_cols)] for _ in range(self.num_rows) + ] + + # list of nodes to explore + to_process: Queue[Position] = Queue() + to_process.put(Position(self.num_rows // 2, self.num_cols // 2)) + + while not to_process.empty(): + position: Position = to_process.get() + if self.is_oob(position): + raise AssertionError("pre-allocated matrix shouldn't cause OOB") + if visited[position.row][position.col]: + continue + if self.contents[position.row][position.col].contents != ".": + continue + self.contents[position.row][position.col] = HoleTile() + visited[position.row][position.col] = True + self.dug_tiles += 1 + + for direction in Direction: + to_process.put(generate_offsets(position, direction, 1)[0])
+ + +
+[docs] + def is_oob(self, position: Position) -> bool: + """Returns if a position is out of bounds. + + Args: + position (Position): position to check. + + Returns: + bool: True if out of bounds. + """ + return ( + position.row < 0 + or position.row >= self.num_rows + or position.col < 0 + or position.col >= self.num_cols + )
+ + + def __str__(self) -> str: + """Custom __str__ for pretty printing matrix.""" + return "\n".join("".join(str(tile) for tile in row) for row in self.contents)
+ + + +
+[docs] +def get_matrix_range(commands: list[Command]) -> tuple[Position, Position]: + """Calculate minimum and maximum position in matrix. + + Since we start in the middle somewhere, we get negative positions. + This can be useds to offset the matrix when we construct it. + + Args: + commands (list[Command]): list of commands that will be run. + + Returns: + tuple[Position, Position]: [min,max] positions. + """ + position = Position() + max_row, max_col = 0, 0 + min_row, min_col = 0, 0 + for command in commands: + position = generate_offsets(position, command.direction, command.steps)[-1] + min_row = min(position.row, min_row) + min_col = min(position.col, min_col) + max_row = max(position.row, max_row) + max_col = max(position.col, max_col) + return Position(min_row, min_col), Position(max_row, max_col)
+ + + +
+[docs] +def get_input(path: str) -> list[Command]: + """Reads input from file into well-formed list of commands.""" + commands = [] + with open(path, encoding="utf-8") as file: + for line in file: + direction, steps, color = line.strip().split() + instruction = Command(Direction(direction), int(steps), color[2:-1]) + commands.append(instruction) + return commands
+ + + +
+[docs] +def get_solution(commands: list[Command]) -> int: + """Calculates solution. + + 1. Pre-calculates the range + 2. Creates edge tiles + 3. Flood fill centre. + 4. Count tiles. + """ + min_pos, max_pos = get_matrix_range(commands) + + matrix: Matrix = Matrix(min_pos, max_pos) + position: Position = Position(-min_pos.row, -min_pos.col) + for command in commands: + position = matrix.process_command(position, command) + + print(matrix) + matrix.dig_out() + print("\n" * 10) + print(matrix) + print(f"Dug: {matrix.dug_tiles}") + print(f"Wall: {matrix.wall_tiles}") + print(f"Total: {matrix.dug_tiles + matrix.wall_tiles}") + return matrix.dug_tiles + matrix.wall_tiles
+ + + +
+[docs] +def main() -> None: + """Load data and then find solution to part1.""" + commands: list[Command] = get_input(INPUT) + get_solution(commands)
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day18/day18b.html b/_modules/day18/day18b.html new file mode 100644 index 0000000..9264c57 --- /dev/null +++ b/_modules/day18/day18b.html @@ -0,0 +1,231 @@ + + + + + + day18.day18b — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day18.day18b

+"""Day18b solution."""
+from dataclasses import dataclass
+from enum import IntEnum
+
+INPUT = "day18/input.txt"
+INPUT_SMALL = "day18/input-small.txt"
+
+
+
+[docs] +@dataclass +class Position: + """Simple 2d vector.""" + + row: int = 0 + col: int = 0
+ + + +
+[docs] +class Direction(IntEnum): + """Direction as an integer enum.""" + + Right = 0 + Down = 1 + Left = 2 + Up = 3
+ + + +
+[docs] +@dataclass(init=False) +class Command: + """Command from hexstring.""" + + direction: Direction + steps: int + + def __init__(self, hexcode: str): + """Converts from hexcode to well formed direction+steps.""" + self.steps = int(hexcode[:5], 16) + self.direction = Direction(int(hexcode[-1]))
+ + + +
+[docs] +def get_input(path: str) -> list[Command]: + """Grabs input from file, parsing into well-formed commands.""" + commands = [] + with open(path, encoding="utf-8") as file: + for line in file: + hexcode = line.strip().split()[2] + instruction = Command(hexcode[2:-1]) + commands.append(instruction) + return commands
+ + + +
+[docs] +def process_command(command: Command, position: Position) -> Position: + """Process a command and return new position.""" + if command.direction == Direction.Right: + return Position(position.row, position.col + command.steps) + if command.direction == Direction.Down: + return Position(position.row + command.steps, position.col) + if command.direction == Direction.Left: + return Position(position.row, position.col - command.steps) + if command.direction == Direction.Up: + return Position(position.row - command.steps, position.col) + raise AssertionError(f"unsupported directoin {command.direction}")
+ + + +
+[docs] +def calculate_area(positions: list[Position], perimeter: int) -> int: + """Calculate area using shoelace area.""" + # total_area = shoelace_area + (perimeter_length // 2) + 1 + + # shoelace assumes that each point is in centre, but each + # perimeter tile is only "half" counted + n = len(positions) # of corners + area = 0 + for i in range(n): + j = (i + 1) % n + area += positions[i].row * positions[j].col + area -= positions[j].row * positions[i].col + area = abs(area) // 2 + + return area + (perimeter // 2) + 1
+ + + +
+[docs] +def get_solution(commands: list[Command]) -> int: + """Get solution via processing commands then running shoelace area.""" + positions: list[Position] = [] + position = Position() + + for command in commands: + position = process_command(command, position) + positions.append(position) + + perimeter = sum(command.steps for command in commands) + + return calculate_area(positions, perimeter)
+ + + +
+[docs] +def main() -> None: + """Grab input and then pass it into solver.""" + commands: list[Command] = get_input(INPUT) + + print(get_solution(commands))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day18/lib/tile.html b/_modules/day18/lib/tile.html new file mode 100644 index 0000000..250a679 --- /dev/null +++ b/_modules/day18/lib/tile.html @@ -0,0 +1,158 @@ + + + + + + day18.lib.tile — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day18.lib.tile

+"""Tile Class."""
+from dataclasses import dataclass
+
+
+
+[docs] +@dataclass +class Tile: + """Tile for part 1, represents a non-dugout tile.""" + + contents: str = "." + + def __str__(self) -> str: + """Custom str for easy printing.""" + return self.contents
+ + + +
+[docs] +@dataclass(kw_only=True) +class EdgeTile(Tile): + """Edge tile (``#``).""" + + contents: str = "#" + color: str + + # 38 -> 48 for background + TEXT_WHITE = "\033[38;2;255;255;255m" + +
+[docs] + def text_color(self, r: int, g: int, b: int) -> str: + """Return ansicode color of edge based on input.""" + return f"\033[38;2;{r};{g};{b}m"
+ + + def __str__(self) -> str: + """Return colored string of ``#`` based on hexcode.""" + r, g, b = [int(self.color[i * 2 : i * 2 + 2], 16) for i in range(3)] + + return f"{self.text_color(r,g,b)}{self.contents}{self.TEXT_WHITE}"
+ + + +
+[docs] +@dataclass(kw_only=True) +class HoleTile(Tile): + """Dug out tile.""" + + contents: str = " "
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day18/tests/test_day18a.html b/_modules/day18/tests/test_day18a.html new file mode 100644 index 0000000..feb44c8 --- /dev/null +++ b/_modules/day18/tests/test_day18a.html @@ -0,0 +1,121 @@ + + + + + + day18.tests.test_day18a — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day18.tests.test_day18a

+"""test day18a main function."""
+from day18.day18a import INPUT_SMALL, Command, Direction, get_input, get_solution
+
+
+
+[docs] +def test_day18a() -> None: + """Test day18a.""" + commands: list[Command] = get_input(INPUT_SMALL) + assert len(commands) == 14 + assert commands[0].steps == 6 and commands[0].direction == Direction.Right + + assert get_solution(commands) == 62 + + assert str(Direction.Right) == "Right"
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day18/tests/test_day18b.html b/_modules/day18/tests/test_day18b.html new file mode 100644 index 0000000..e0ea45d --- /dev/null +++ b/_modules/day18/tests/test_day18b.html @@ -0,0 +1,156 @@ + + + + + + day18.tests.test_day18b — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day18.tests.test_day18b

+"""test day18b main functions."""
+from day18.day18b import INPUT_SMALL, Command, Direction, get_input, get_solution
+
+
+
+[docs] +def test_day18b() -> None: + """Test day18b main function.""" + commands: list[Command] = get_input(INPUT_SMALL) + assert len(commands) == 14 + assert commands[0].steps == 461937 and commands[0].direction == Direction.Right + assert commands[1].steps == 56407 and commands[1].direction == Direction.Down + + assert get_solution(commands) == 952408144115
+ + + +
+[docs] +def test_command() -> None: + """Test hex code conversion.""" + assert Command("70c710").steps == 461937 + assert Command("0dc571").steps == 56407 + assert Command("5713f0").steps == 356671 + assert Command("d2c081").steps == 863240 + assert Command("59c680").steps == 367720 + assert Command("411b91").steps == 266681 + assert Command("8ceee2").steps == 577262 + assert Command("caa173").steps == 829975 + assert Command("1b58a2").steps == 112010 + assert Command("caa171").steps == 829975 + assert Command("7807d2").steps == 491645 + assert Command("a77fa3").steps == 686074 + assert Command("015232").steps == 5411 + assert Command("7a21e3").steps == 500254 + + assert Command("70c710").direction == Direction.Right + assert Command("0dc571").direction == Direction.Down + assert Command("5713f0").direction == Direction.Right + assert Command("d2c081").direction == Direction.Down + assert Command("59c680").direction == Direction.Right + assert Command("411b91").direction == Direction.Down + assert Command("8ceee2").direction == Direction.Left + assert Command("caa173").direction == Direction.Up + assert Command("1b58a2").direction == Direction.Left + assert Command("caa171").direction == Direction.Down + assert Command("7807d2").direction == Direction.Left + assert Command("a77fa3").direction == Direction.Up + assert Command("015232").direction == Direction.Left + assert Command("7a21e3").direction == Direction.Up
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day19/day19.html b/_modules/day19/day19.html new file mode 100644 index 0000000..43fb2f6 --- /dev/null +++ b/_modules/day19/day19.html @@ -0,0 +1,247 @@ + + + + + + day19.day19 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day19.day19

+"""Day19 solution."""
+from queue import Queue
+
+from day19.lib.classes import Part, PartRange, PartRangeDest, Workflow
+from day19.lib.parsers import parse_part_string, parse_workflow_string
+
+"""
+parsing classes section
+"""
+INPUT = "day19/input.txt"
+INPUT_SMALL = "day19/input-small.txt"
+
+
+
+[docs] +def get_input(path: str) -> tuple[list[Workflow], list[Part]]: + """Open file and parse. + + Returns well formed workflows and parts classes. + + Args: + path (str): filepath for data + + Returns: + tuple[list[Workflow], list[Part]]: list of workflows and parts. + """ + workflows: list[Workflow] = [] + parts: list[Part] = [] + parsing_parts: bool = False + with open(path, encoding="utf8") as file: + for line in file: + if len(line.strip()) == 0: + parsing_parts = True + continue + if not parsing_parts: + workflow: Workflow = parse_workflow_string(line) + workflows.append(workflow) + else: + part: Part = parse_part_string(line) + parts.append(part) + return (workflows, parts)
+ + + +
+[docs] +def process_part(workflows: dict[str, Workflow], part: Part) -> int: + """Processes a part. + + Returns the part rating (or 0 if rejected) + + Args: + workflows (dict[str, Workflow]): list of workflows. + part (Part): part to process + + Returns: + int: value of part (or 0 if rejected) + """ + # ends are `A` and R + # start is `in` + workflow = workflows["in"] + while True: + workflow_name = workflow.process_part(part) + if workflow_name == "A": + return part.rating + if workflow_name == "R": + return 0 + workflow = workflows[workflow_name]
+ + + +
+[docs] +def solve_part2(workflows: dict[str, Workflow]) -> int: + """Solve part2. + + Assumes xmas values from 0 <= xmas <= 4000. + Returns total number of parts that pass. + + Args: + workflows (dict[str, Workflow]): workflows to test + + Returns: + int: total number of parts that pass. + """ + min_xmas = Part(1, 1, 1, 1) + max_xmas = Part(4001, 4001, 4001, 4001) + part_range = PartRange(min_xmas, max_xmas) + + starting_condition = PartRangeDest(part_range, "in") + + to_process: Queue[PartRangeDest] = Queue() + to_process.put(starting_condition) + result = 0 + while not to_process.empty(): + part_range_dest = to_process.get() + part_range, dest = part_range_dest.part_range, part_range_dest.destination + + to_add: list[PartRangeDest] = workflows[dest].process_part_range(part_range) + for item in to_add: + if item.destination == "A": + result += item.part_range.size() + elif item.destination != "R": + to_process.put(item) + return result
+ + + +
+[docs] +def part1(workflows: list[Workflow], parts: list[Part]) -> int: + """Solve part1.""" + workflows_mapping: dict[str, Workflow] = {wf.name: wf for wf in workflows} + total = sum(process_part(workflows_mapping, part) for part in parts) + + return total
+ + + +
+[docs] +def part2(workflows: list[Workflow]) -> int: + """Solve part2.""" + workflows_mapping: dict[str, Workflow] = {wf.name: wf for wf in workflows} + return solve_part2(workflows_mapping)
+ + + +
+[docs] +def main() -> None: + """Load input file and run part1/part2.""" + # combined + workflows, parts = get_input(INPUT) + + print(part1(workflows, parts)) + print(part2(workflows))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day19/lib/classes.html b/_modules/day19/lib/classes.html new file mode 100644 index 0000000..b666381 --- /dev/null +++ b/_modules/day19/lib/classes.html @@ -0,0 +1,436 @@ + + + + + + day19.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day19.lib.classes

+"""well defined classes for day19."""
+from dataclasses import dataclass
+from enum import StrEnum
+from typing import Optional
+
+
+
+[docs] +@dataclass +class Part: + """Well defined part with x,m,a,s values.""" + + x: int + m: int + a: int + s: int + + @property + def rating(self) -> int: + """Returns rating of part (sum of xmas).""" + return sum([self.x, self.m, self.a, self.s]) + +
+[docs] + def clone_modify(self, component: "Component", value: int) -> "Part": + """Clones this part and modifies one component. + + Args: + component (Component): component to change + value (int): new value of component + + Returns: + Part: clone of this part with one component changed. + """ + x, m, a, s = self.x, self.m, self.a, self.s + if component == Component.X: + x = value + if component == Component.M: + m = value + if component == Component.A: + a = value + if component == Component.S: + s = value + + return Part(x, m, a, s)
+ + +
+[docs] + def get_value(self, component: "Component") -> int: + """Returns value of a component inside this part.""" + if component == Component.X: + return self.x + if component == Component.M: + return self.m + if component == Component.A: + return self.a + if component == Component.S: + return self.s + raise AssertionError(f"Unsupported component{component}")
+
+ + + +
+[docs] +class Component(StrEnum): + """A well defined component inside a part.""" + + X = "x" + M = "m" + A = "a" + S = "s"
+ + + +
+[docs] +@dataclass +class PartRange: + """A range of parts (min/max) based on component values.""" + + min_values: Part # from + max_values: Part # to, non-inclusive + +
+[docs] + def size(self) -> int: + """Returns the size of the partrange.""" + return ( + (self.max_values.x - self.min_values.x) + * (self.max_values.m - self.min_values.m) + * (self.max_values.a - self.min_values.a) + * (self.max_values.s - self.min_values.s) + )
+ + +
+[docs] + def split( + self, component: Component, split_value: int + ) -> tuple[Optional["PartRange"], Optional["PartRange"]]: + """Split a partrange in two, using a chosen component and splitvalue. + + In the case that our range falls on one whole side, we return None. + E.g. + range = 0-100; split == 200 -> return [(0-100), None] + range = 100-200; split == 50 -> return [None, (100-200)] + range = 100-200, split == 150 -> return [(100-150), (150-200)] + """ + min_value = self.min_values.get_value(component) + max_value = self.max_values.get_value(component) + if split_value >= max_value: + return (self, None) + if split_value < min_value: + return (None, self) + + mid_high = self.min_values.clone_modify(component, split_value) + mid_low = self.max_values.clone_modify(component, split_value) + + return ( + PartRange(self.min_values, mid_low), + PartRange(mid_high, self.max_values), + )
+ + + def __str__(self) -> str: + """Compact string representing our range.""" + return ", ".join( + [ + f"{self.min_values.x}<=x<={self.max_values.x-1}", + f"{self.min_values.m}<=m<={self.max_values.m-1}", + f"{self.min_values.a}<=a<={self.max_values.a-1}", + f"{self.min_values.s}<=s<={self.max_values.s-1}", + ] + )
+ + + +
+[docs] +@dataclass +class PartRangeDest: + """Combinatoin of partrange and a destination workflow.""" + + part_range: PartRange + destination: str + + def __str__(self) -> str: + """Compact string representation.""" + return self.destination + ":" + str(self.part_range)
+ + + +
+[docs] +class Comparator(StrEnum): + """Well defined comparators ``<`` and ``>``.""" + + LessThan = "<" + GreaterThan = ">"
+ + + +
+[docs] +@dataclass +class Condition: + """A condition for a part to succeed/fail.""" + + component: Component + sign: Comparator + value: int + +
+[docs] + def process_part(self, part: Part) -> bool: + """Checks a part to see if it matches our condition. + + Args: + part (Part): part to check + + Raises: + AssertionError: if component/sign are unsupported + + Returns: + bool: True if the part passes our condition. + """ + part_val: int + if self.component == Component.X: + part_val = part.x + elif self.component == Component.M: + part_val = part.m + elif self.component == Component.A: + part_val = part.a + elif self.component == Component.S: + part_val = part.s + else: + raise AssertionError(f"Unsupported component: {self.component}") + + if self.sign == Comparator.GreaterThan: + return part_val > self.value + elif self.sign == Comparator.LessThan: + return part_val < self.value + else: + raise AssertionError(f"Unsupported comparator: {self.sign}")
+ + +
+[docs] + def process_part_range( + self, part_range: PartRange + ) -> tuple[Optional[PartRange], Optional[PartRange]]: + """Splits a part range based on success/fail. + + Args: + part_range (PartRange): Partrange to check. + + Raises: + AssertionError: If we have an unknown comparator + + Returns: + tuple[Optional[PartRange], Optional[PartRange]]: successful part range, failed partrange + """ + if self.sign == Comparator.LessThan: + success, fail = part_range.split(self.component, self.value) + return (success, fail) + if self.sign == Comparator.GreaterThan: + fail, success = part_range.split(self.component, self.value + 1) + return (success, fail) + raise AssertionError(f"Unknown comparator: {self.sign}")
+
+ + + +
+[docs] +@dataclass +class Rule: + """A Rule consists of a condition + destination.""" + + destination: str + condition: Condition | None = None + +
+[docs] + def process_part(self, part: Part) -> str | None: + """Processes a part. + + Returns next workflow if successful, + or None if we failed this rule + """ + if self.condition is None: # always pass + return self.destination + if self.condition.process_part(part): + return self.destination + return None
+ + +
+[docs] + def process_part_range( + self, part_range: PartRange + ) -> tuple[Optional[PartRangeDest], Optional[PartRange]]: + """Processes a PartRange. + + Returns next workflow and partrange for succeeding parts. + Returns the remainder partrange that failed. + + Args: + part_range (PartRange): base partrange. + + Returns: + tuple[Optional[PartRangeDest], Optional[PartRange]]: success, fail + """ + success: Optional[PartRange] + fail: Optional[PartRange] + if self.condition is None: # pass all + success, fail = part_range, None + else: # split up range + success, fail = self.condition.process_part_range(part_range) + if success is not None: + return (PartRangeDest(success, self.destination), fail) + + return None, fail
+
+ + + +
+[docs] +@dataclass(eq=True) +class Workflow: + """The name of the workflow + a bunch of rules for parts to follow.""" + + name: str + rules: list[Rule] + +
+[docs] + def process_part(self, part: Part) -> str: + """Processes a part, returns the next workflow.""" + for rule in self.rules: + destination = rule.process_part(part) + if destination is not None: + return destination + raise AssertionError("uh oh, hit the end of workflow!")
+ + +
+[docs] + def process_part_range(self, part_range: PartRange) -> list[PartRangeDest]: + """Follow rule list, splitting off PartRanges. + + Each success has to branch off. + Each failure continues down the chain. + """ + results: list[PartRangeDest] = [] + remainder: Optional[PartRange] = part_range + + index = 0 + while remainder is not None: + rule = self.rules[index] + success, remainder = rule.process_part_range(remainder) + if success is not None: + results.append(success) + index += 1 + + return results
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day19/lib/parsers.html b/_modules/day19/lib/parsers.html new file mode 100644 index 0000000..832a937 --- /dev/null +++ b/_modules/day19/lib/parsers.html @@ -0,0 +1,173 @@ + + + + + + day19.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day19.lib.parsers

+"""functions to create classes from pure text."""
+
+from day19.lib.classes import Comparator, Component, Condition, Part, Rule, Workflow
+
+
+
+[docs] +def parse_part_string(part_string: str) -> Part: + r"""Returns a part from a string representation. + + e.g. ``{x=787,m=2655,a=1222,s=2876}\n`` + """ + part_string = part_string.strip() + part_string = part_string[1:-1] + # Remove curly braces and split the string into key-value pairs + key_value_pairs = part_string.split(",") + + # Create a dictionary from the key-value pairs + part_dict = {} + for pair in key_value_pairs: + key, value = pair.split("=") + part_dict[key.strip()] = int(value) + + # Create a Part instance using the dictionary + return Part(**part_dict)
+ + + +
+[docs] +def parse_workflow_string(workflow_string: str) -> Workflow: + r"""Returns a workflow from a string representation. + + e.g. ``px{a<2006:qkq,m>2090:A,rfg}\n`` + """ + workflow_string = workflow_string.strip() + rule_start = workflow_string.index("{") + name = workflow_string[:rule_start] + rules_str = workflow_string[rule_start + 1 : -1] + rule_strs = rules_str.split(",") + rules: list[Rule] = [parse_rule_string(rule_str) for rule_str in rule_strs] + + return Workflow(name, rules)
+ + + +
+[docs] +def parse_rule_string(rule_string: str) -> Rule: + """e.g. ``a<2006:qkq`` or ``rfg``.""" + dest_split = rule_string.split(":") + if len(dest_split) == 1: + return Rule(dest_split[0]) + else: + condition = parse_condition_string(dest_split[0]) + return Rule(dest_split[1], condition)
+ + + +
+[docs] +def parse_condition_string(cond_string: str) -> Condition: + """e.g. ``a<2006``.""" + component = Component(cond_string[0]) + operator = Comparator(cond_string[1]) + value = int(cond_string[2:]) + return Condition(component, operator, value)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day19/tests/test_classes.html b/_modules/day19/tests/test_classes.html new file mode 100644 index 0000000..03fab07 --- /dev/null +++ b/_modules/day19/tests/test_classes.html @@ -0,0 +1,187 @@ + + + + + + day19.tests.test_classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day19.tests.test_classes

+"""Tests for day19 classes."""
+from day19.lib.classes import (
+    Comparator,
+    Component,
+    Condition,
+    Part,
+    PartRange,
+    PartRangeDest,
+    Rule,
+    Workflow,
+)
+
+
+
+[docs] +def get_part_range() -> tuple[Part, Part]: + """Returns a reusable partrange for our tests.""" + return Part(100, 100, 100, 100), Part(150, 200, 150, 150)
+ + + +
+[docs] +def test_part_range() -> None: + """Test ``PartRange`` class.""" + low: Part = Part(100, 100, 100, 100) + high: Part = Part(150, 200, 150, 150) + part_range: PartRange = PartRange(low, high) + low_split, high_split = part_range.split(Component.M, 300) + assert low_split == part_range + assert high_split is None + + low_split, high_split = part_range.split(Component.M, 25) + assert low_split is None + assert high_split == part_range + + assert str(part_range) == "100<=x<=149, 100<=m<=199, 100<=a<=149, 100<=s<=149"
+ + + +
+[docs] +def test_part_range_dest() -> None: + """Test ``PartRangeDest`` class.""" + part_range: PartRange = PartRange(*get_part_range()) + part_range_dest = PartRangeDest(part_range, "test") + assert ( + str(part_range_dest) + == "test:100<=x<=149, 100<=m<=199, 100<=a<=149, 100<=s<=149" + )
+ + + +
+[docs] +def test_rule() -> None: + """Test ``Rule`` class.""" + rule = Rule("test", Condition(Component.M, Comparator.LessThan, 50)) + + low: Part = Part(100, 100, 100, 100) + high: Part = Part(150, 200, 150, 150) + part_range = PartRange(low, high) + dest, rest = rule.process_part_range(part_range) + assert dest is None + assert rest == part_range
+ + + +
+[docs] +def test_workflow() -> None: + """Test ``Workflow`` class.""" + rule1: Rule = Rule("test", Condition(Component.M, Comparator.LessThan, 50)) + rule2: Rule = Rule("rest") + workflow: Workflow = Workflow("workflow1", [rule1, rule2]) + + low: Part = Part(100, 100, 100, 100) + high: Part = Part(150, 200, 150, 150) + part_range = PartRange(low, high) + results = workflow.process_part_range(part_range) + assert results[0].destination == "rest"
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day19/tests/test_day19.html b/_modules/day19/tests/test_day19.html new file mode 100644 index 0000000..ddf03e3 --- /dev/null +++ b/_modules/day19/tests/test_day19.html @@ -0,0 +1,118 @@ + + + + + + day19.tests.test_day19 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day19.tests.test_day19

+"""Test day19 main classes."""
+from day19.day19 import INPUT_SMALL, get_input, part1, part2
+
+
+
+[docs] +def test_day19() -> None: + """Test ``part1()`` and ``part2()``.""" + workflows, parts = get_input(INPUT_SMALL) + + assert part1(workflows, parts) == 19114 + assert part2(workflows) == 167409079868000
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day19/tests/test_parsers.html b/_modules/day19/tests/test_parsers.html new file mode 100644 index 0000000..7e20c88 --- /dev/null +++ b/_modules/day19/tests/test_parsers.html @@ -0,0 +1,180 @@ + + + + + + day19.tests.test_parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day19.tests.test_parsers

+"""Test lib.parsers."""
+
+
+from day19.lib.classes import Comparator, Component, Condition, Part, Rule, Workflow
+from day19.lib.parsers import (
+    parse_condition_string,
+    parse_part_string,
+    parse_rule_string,
+    parse_workflow_string,
+)
+
+
+
+[docs] +def test_parse_part_string() -> None: + r"""E.g: ``{x=787,m=2655,a=1222,s=2876}\n``.""" + part: Part = parse_part_string("{x=787,m=2655,a=1222,s=2876}\n") + assert part == Part(787, 2655, 1222, 2876) + + part = parse_part_string("{x=1,m=2,a=3,s=4}\n") + assert part == Part(1, 2, 3, 4)
+ + + +
+[docs] +def test_parse_workflow_string() -> None: + r"""E.g: ``px{a<2006:qkq,m>2090:A,rfg}\n``.""" + workflow: Workflow = parse_workflow_string("px{a<2006:qkq,m>2090:A,rfg}\n") + rules = [ + Rule("qkq", Condition(Component.A, Comparator.LessThan, 2006)), + Rule("A", Condition(Component.M, Comparator.GreaterThan, 2090)), + Rule("rfg", None), + ] + workflow2 = Workflow("px", rules) + assert workflow == workflow2 # confirm that workflow.eq works + assert workflow.name == workflow2.name + assert workflow.rules[0] == workflow2.rules[0] + assert workflow.rules[1] == workflow2.rules[1] + assert workflow.rules[2] == workflow2.rules[2] + assert workflow.rules == workflow2.rules
+ + + +
+[docs] +def test_parse_rule_string() -> None: + """E.g: ``a<2006:qkq`` or ``rfg``.""" + rule: Rule = parse_rule_string("a<2006:qkq") + rule2: Rule = Rule("qkq", Condition(Component.A, Comparator.LessThan, 2006)) + + assert rule.destination == rule2.destination + assert rule.condition == rule2.condition + assert rule == rule2 + + rule = parse_rule_string("rfg") + rule2 = Rule("rfg") + + assert rule.destination == rule2.destination + assert rule.condition == rule2.condition + assert rule == rule2
+ + + +
+[docs] +def test_parse_condition_string() -> None: + """E.g. ``a<2006``.""" + condition: Condition = parse_condition_string("a<2006") + condition2: Condition = Condition(Component.A, Comparator.LessThan, 2006) + assert condition == condition2 + assert condition.component == condition2.component + assert condition.sign == condition2.sign + assert condition.value == condition2.value
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day20/day20.html b/_modules/day20/day20.html new file mode 100644 index 0000000..201a74e --- /dev/null +++ b/_modules/day20/day20.html @@ -0,0 +1,413 @@ + + + + + + day20.day20 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day20.day20

+"""Day20 solution."""
+import math
+import os
+import shutil
+from queue import Queue
+from typing import Optional, Type, TypeVar, cast
+
+import graphviz
+from tqdm.contrib.concurrent import process_map
+
+from day20.lib.classes import (
+    BaseModule,
+    BroadcastModule,
+    ConjunctionModule,
+    FlipFlopModule,
+    LoopCounter,
+    ModuleGroups,
+    Pulse,
+    PulseTarget,
+    SinkModule,
+)
+from day20.lib.parsers import get_modules
+
+FILE_A = "day20/input-a.txt"
+FILE_B = "day20/input-b.txt"
+FILE_PT2 = "day20/input-test2.txt"
+FILE_PROD = "day20/input.txt"
+
+FILE = FILE_PROD
+VIS_FOLDER = "day20/vis"
+EXPORT_GRAPHS = False
+
+
+
+[docs] +def simulate( + modules: dict[str, BaseModule], stored_pulses: Optional[list[PulseTarget]] = None +) -> tuple[int, int]: + """Simulate a list of modules. + + If you pass in stored_pulses, we will append every pulse to it + """ + pulses: Queue[PulseTarget] = Queue() + pulses.put(PulseTarget(Pulse.LOW, "button", "broadcaster")) + low = 0 + high = 0 + + while not pulses.empty(): + pulse_target: PulseTarget = pulses.get() + if stored_pulses is not None: + stored_pulses.append(pulse_target) + if pulse_target.pulse == Pulse.LOW: + low += 1 + else: + high += 1 + + module: BaseModule = modules[pulse_target.target] + results: list[PulseTarget] = module.handle_pulse( + pulse_target.src, pulse_target.pulse + ) + for result in results: + pulses.put(result) + + return low, high
+ + + +
+[docs] +def get_loop_paths( + start_switch: str, module_map: dict[str, BaseModule] +) -> list[BaseModule]: + """Given a start path, returns the longest path until we hit a conjunction module. + + It should be n FlipFlops and then a single conjunction + """ + path: list[BaseModule] = [] + current_module: BaseModule = module_map[start_switch] + while isinstance(current_module, FlipFlopModule): + path.append(current_module) + + if len(current_module.outputs) == 1: + current_module = module_map[current_module.outputs[0]] + else: + # return flipflop in outputs + outputs: list[BaseModule] = [ + module_map[output] for output in current_module.outputs + ] + filtered = [node for node in outputs if isinstance(node, FlipFlopModule)] + assert len(filtered) == 1 + current_module = filtered[0] + + path.append(current_module) # should be a ConjunctionModule + assert isinstance(current_module, ConjunctionModule) + return path
+ + + +
+[docs] +def path_is_start_state(modules: list[BaseModule]) -> bool: + """For every module in the path, make sure its in its "initial" state.""" + return all(module.is_initial_state() for module in modules)
+ + + +T = TypeVar("T", bound=BaseModule) + + +
+[docs] +def get_typed_module( + module_map: dict[str, BaseModule], key: str, module_type: Type[T] +) -> T: + """Typecast a module.""" + return cast(T, module_map[key])
+ + + +
+[docs] +def get_module_groups(module_map: dict[str, BaseModule]) -> ModuleGroups: + """Splits the modules into their respective pipelines.""" + broadcaster = get_typed_module(module_map, "broadcaster", BroadcastModule) + + loop_paths = [ + get_loop_paths(node_name, module_map) for node_name in broadcaster.outputs + ] + loop_tails: list[ConjunctionModule] = [] + + # for each loop, the conjunction module goes to one other conjunction. + # grab this set of conjunctions, they are known as loop_tails + for loop_path in loop_paths: + last_node = loop_path[-1] + + nodes = [module_map[node_name] for node_name in last_node.outputs] + filtered = [node for node in nodes if isinstance(node, ConjunctionModule)] + assert len(filtered) == 1 + loop_tails.extend(filtered) + + last_join_name = loop_tails[0].outputs[0] + last_conjunction = get_typed_module(module_map, last_join_name, ConjunctionModule) + sink = get_typed_module(module_map, "rx", SinkModule) + + return ModuleGroups(broadcaster, loop_paths, loop_tails, last_conjunction, sink)
+ + + +
+[docs] +def graph_modules(module_groups: ModuleGroups, index: int) -> graphviz.Digraph: + """Graphs the modules.""" + index_str = str(index).zfill(4) + graph_attr = {"labelloc": "t", "label": index_str} + dot = graphviz.Digraph(f"Push {index_str}", format="png", graph_attr=graph_attr) + + # broadcaster + with dot.subgraph(graph_attr={"rank": "source"}) as s: + s.node(module_groups.head.name) + + # loop nodes + for index in range(max(len(loop) for loop in module_groups.loops)): + with dot.subgraph(graph_attr={"rank": "same"}) as s: + s.attr(rank="same") + for loop_path in module_groups.loops: + if index < len(loop_path) - 1: + node = loop_path[index] + s.node(node.name) + + # loop tails + with dot.subgraph(graph_attr={"rank": "same"}) as s: + for node in module_groups.loop_tails: + s.node(node.name) + # penultimate + with dot.subgraph(graph_attr={"rank": "same"}) as s: + s.node(module_groups.penultimate.name) + + with dot.subgraph(graph_attr={"rank": "sink"}) as s: + s.node(module_groups.sink.name) + + # now that we've done ranks, just add all of them with their arrows. + for node in module_groups.all_nodes: + node.add_to_graph(dot) + + return dot
+ + + +
+[docs] +def export_graph( + dots: list[graphviz.Graph], + module_groups: ModuleGroups, + simulation_counter: int, + export_graphs: bool, +) -> None: + """Export a graphviz datatype if graphing is enabled.""" + if export_graphs: + dot = graph_modules(module_groups, simulation_counter) + dots.append(dot)
+ + + +
+[docs] +def part2( + modules: list[BaseModule], export_graphs: bool = False +) -> tuple[int, list[graphviz.Graph]]: + """We find out the loop length for each of the 4~ paths.""" + module_map = {module.name: module for module in modules} + module_groups: ModuleGroups = get_module_groups(module_map) + + # graph modules in initial state + dots: list[graphviz.Graph] = [] + simulation_counter = 0 + loop_counter: LoopCounter = LoopCounter(len(module_groups.loops)) + + # output our initial state: + export_graph(dots, module_groups, simulation_counter, export_graphs) + + # run simulation, screenshotting everytime one of the paths "loops" + while not loop_counter.finished: + simulate(module_map) + simulation_counter += 1 + for loop_path in module_groups.loops: + if path_is_start_state(loop_path): + loop_end_name = loop_path[-1].name + loop_counter.add_result(loop_end_name, simulation_counter) + export_graph(dots, module_groups, simulation_counter, export_graphs) + + print(loop_counter) + result = math.lcm(*list(loop_counter.loop_lengths.values())) + return result, dots
+ + + +
+[docs] +def part1(modules: list[BaseModule]) -> int: + """Counts low/high count for each module.""" + module_map = {module.name: module for module in modules} + low_total = 0 + high_total = 0 + for _ in range(1000): + low, high = simulate(module_map) + low_total += low + high_total += high + + print(low_total, high_total) + return low_total * high_total
+ + + +
+[docs] +def output_graph(dot: graphviz.Graph, directory: str) -> None: + """Saves a dot to file.""" + dot.render(directory=directory)
+ + + +
+[docs] +def output_graph_wrapper(args: tuple[graphviz.Graph, str]) -> None: + """Since process_map doesnt support star_args, we gotta use this.""" + dot, directory = args + output_graph(dot, directory)
+ + + +
+[docs] +def output_files(dots: list[graphviz.Graph], directory: str) -> None: + """Saves a list of dots to file.""" + if len(dots) == 0: + return + shutil.rmtree(directory, ignore_errors=True) + os.makedirs(directory, exist_ok=True) + dot_dirs = [(dot, directory) for dot in dots] + process_map(output_graph_wrapper, dot_dirs, chunksize=4) # type: ignore + + # Cleanup *.gv files + for item in os.listdir(directory): + if item.endswith(".gv"): + os.remove(os.path.join(directory, item))
+ + + +
+[docs] +def main() -> None: + """Loads data from file then runs part1/part2.""" + modules = get_modules(FILE) + # q1 + print(part1(modules)) + + # q2 + # Reload because part1 ruins stuff + + modules = get_modules(FILE) + result, dots = part2(modules, EXPORT_GRAPHS) + print(result) + output_files(dots, VIS_FOLDER)
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day20/lib/classes.html b/_modules/day20/lib/classes.html new file mode 100644 index 0000000..9a2f3c6 --- /dev/null +++ b/_modules/day20/lib/classes.html @@ -0,0 +1,447 @@ + + + + + + day20.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day20.lib.classes

+"""Classes for day20."""
+import itertools
+from abc import ABC, abstractmethod
+from dataclasses import dataclass, field
+from enum import Flag
+
+from graphviz import Digraph
+
+
+
+[docs] +class Pulse(Flag): + """Simple True/False enum.""" + + LOW = False + HIGH = True + + def __str__(self) -> str: + """Custom str to show ``low`` and ``high``.""" + if self.name is not None: + return self.name.lower() + raise AssertionError("no valid value for this Pulse")
+ + + +
+[docs] +@dataclass +class MappingModule: + """map to a list of outputs.""" + + name: str + outputs: list[str]
+ + + +
+[docs] +@dataclass +class PulseTarget: + """A pulse(low/high) from src to dest.""" + + pulse: Pulse + src: str + target: str + + def __str__(self) -> str: + """Custom arrow string to match problem description.""" + return f"{self.src} -{self.pulse}-> {self.target}"
+ + + +
+[docs] +@dataclass +class BaseModule(ABC): + """Abstract base module.""" + + name: str + outputs: list[str] = field(repr=False) + num_low: int = field(init=False, default=0) + num_high: int = field(init=False, default=0) + + def __post_init__(self) -> None: + """Stop users from constructing BaseModule.""" + if self.__class__ == BaseModule: + raise AssertionError("Cannot instantiate abstract class") + +
+[docs] + def arrow_color(self) -> str: + """Return arrow color for graphviz.""" + return "#000000"
+ + +
+[docs] + def handle_pulse(self, input: str, pulse: Pulse) -> list[PulseTarget]: + """Keep track of lows/highs through all modules.""" + if pulse == Pulse.LOW: + self.num_low += 1 + else: + self.num_high += 1 + return []
+ + +
+[docs] + def add_to_graph(self, dot: Digraph) -> None: + """Adds edges only to the graph. inheritors need to handle their repr.""" + attrs = {"color": self.arrow_color()} + + for output in self.outputs: + dot.edge(self.name, output, **attrs)
+ + +
+[docs] + @abstractmethod + def is_initial_state(self) -> bool: + """Returns if the module is in the initial state.""" + raise AssertionError("Implement me")
+
+ + + +
+[docs] +@dataclass +class FlipFlopModule(BaseModule): + """If we receive HIGH, we are a sink (do nothing). + + If we receive LOW, flip our current value and send it to everyone + """ + + state: Pulse = Pulse.LOW + +
+[docs] + def handle_pulse(self, input: str, pulse: Pulse) -> list[PulseTarget]: + """Handle pulse by forwarding if we receive low.""" + super().handle_pulse(input, pulse) + if pulse: + return [] + # if we receive low, become the opposite and send it. + # if we receive high, do ont send, do not modify state. + + self.state = Pulse(not self.state) + return [PulseTarget(self.state, self.name, target) for target in self.outputs]
+ + +
+[docs] + def add_to_graph(self, dot: Digraph) -> None: + """Adds ourselves to a graphviz digraph.""" + attrs = {"shape": "box", "style": "filled"} + if self.state == Pulse.LOW: + attrs["fillcolor"] = "#FF0000" + else: + attrs["fillcolor"] = "#0000FF" + dot.node(self.name, **attrs) + super().add_to_graph(dot)
+ + +
+[docs] + def is_initial_state(self) -> bool: + """Returns true if we are in our initial state.""" + return self.state == Pulse.LOW
+
+ + + +
+[docs] +@dataclass +class ConjunctionModule(BaseModule): + """Keeps track of all inputs. + + Changes internal state, then sends high/low based on internal state + """ + + inputs: dict[str, Pulse] = field(init=False, repr=False) + +
+[docs] + def arrow_color(self) -> str: + """Returns red.""" + return "#FF0000"
+ + +
+[docs] + def set_inputs(self, inputs: list[str]) -> None: + """Sets our list of input modules. + + Initializes their values to Low. + """ + self.inputs = {input_name: Pulse.LOW for input_name in inputs}
+ + +
+[docs] + def handle_pulse(self, input: str, pulse: Pulse) -> list[PulseTarget]: + """Store pulse, then send based on current state. + + If all our values are high, we send low to all our outputs. + Otherwise, we send ``LOW`` to all our outputs. + """ + super().handle_pulse(input, pulse) + self.inputs[input] = pulse + if all(self.inputs.values()): + return [ + PulseTarget(Pulse.LOW, self.name, target) for target in self.outputs + ] + return [PulseTarget(Pulse.HIGH, self.name, target) for target in self.outputs]
+ + +
+[docs] + def add_to_graph(self, dot: Digraph) -> None: + """Add this module to a GraphViz Digraph.""" + count = self.current_count() + length = len(list(self.inputs.values())) + label = f"{self.name} {count}/{length}" + dot.node(self.name, label=label) + super().add_to_graph(dot)
+ + +
+[docs] + def current_count(self) -> int: + """Returns current count of inputs that sent ``high``.""" + return list(self.inputs.values()).count(Pulse.HIGH)
+ + +
+[docs] + def is_initial_state(self) -> bool: + """Returns True if all our inputs are LOW.""" + return self.current_count() == 0
+
+ + + +
+[docs] +@dataclass +class BroadcastModule(BaseModule): + """Broadcasts to all outputs.""" + +
+[docs] + def handle_pulse(self, input: str, pulse: Pulse) -> list[PulseTarget]: + """Broadcasts to all outputs immediately.""" + super().handle_pulse(input, pulse) + return [PulseTarget(pulse, self.name, target) for target in self.outputs]
+ + +
+[docs] + def add_to_graph(self, dot: Digraph) -> None: + """Add node to graphviz digraph.""" + dot.node(self.name) + super().add_to_graph(dot)
+ + +
+[docs] + def is_initial_state(self) -> bool: + """Always true.""" + return True
+
+ + + +
+[docs] +@dataclass +class SinkModule(BaseModule): + """Sink module, gets something but sends it no where else.""" + +
+[docs] + def handle_pulse(self, input: str, pulse: Pulse) -> list[PulseTarget]: + """Always eats inputs and never sends onwards.""" + super().handle_pulse(input, pulse) + return []
+ + +
+[docs] + def add_to_graph(self, dot: Digraph) -> None: + """Adds this node to the graph.""" + dot.node(self.name) + super().add_to_graph(dot)
+ + +
+[docs] + def is_initial_state(self) -> bool: + """Always true.""" + return True
+
+ + + +
+[docs] +@dataclass +class ModuleGroups: + """A group of modules for part2.""" + + head: BroadcastModule + loops: list[list[BaseModule]] + loop_tails: list[ConjunctionModule] + penultimate: ConjunctionModule + sink: SinkModule + all_nodes: list[BaseModule] = field(init=False) + + def __post_init__(self) -> None: + """Sets up our ``all_nodes`` property.""" + all_nodes: list[BaseModule] = [] + all_nodes.append(self.head) + all_nodes.extend(list(itertools.chain(*self.loops))) + all_nodes.extend(self.loop_tails) + all_nodes.append(self.penultimate) + all_nodes.append(self.sink) + self.all_nodes = all_nodes
+ + + +
+[docs] +@dataclass +class LoopCounter: + """Keeps track of loop lengths.""" + + target_loop_count: int = field(repr=False) + loop_lengths: dict[str, int] = field(default_factory=dict, init=False) + + @property + def num_results(self) -> int: + """Returns number of loop_lenghts submitted.""" + return len(self.loop_lengths) + + @property + def finished(self) -> bool: + """Returns True if our loop_lengths are equal to our target.""" + return self.num_results == self.target_loop_count + +
+[docs] + def add_result(self, loop_name: str, value: int) -> None: + """Adds a result to our loop_count. + + If we already had a loop with that name, we ignore it. + """ + if loop_name not in self.loop_lengths: + self.loop_lengths[loop_name] = value
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day20/lib/parsers.html b/_modules/day20/lib/parsers.html new file mode 100644 index 0000000..05d9bb4 --- /dev/null +++ b/_modules/day20/lib/parsers.html @@ -0,0 +1,189 @@ + + + + + + day20.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day20.lib.parsers

+"""Day20 parsers."""
+from collections import defaultdict
+
+from day20.lib.classes import (
+    BaseModule,
+    BroadcastModule,
+    ConjunctionModule,
+    FlipFlopModule,
+    SinkModule,
+)
+
+
+
+[docs] +def parse_line(line: str) -> BaseModule: + """Parses a line into a BaseModule. + + e.g. ``%a -> inv, con``. + """ + module_type_name, destinations = line.strip().split(" -> ") + destination_list: list[str] = destinations.split(", ") + if module_type_name == "broadcaster": + return BroadcastModule("broadcaster", destination_list) + module_name = module_type_name[1:] + if module_type_name.startswith("%"): + return FlipFlopModule(module_name, destination_list) + if module_type_name.startswith("&"): + return ConjunctionModule(module_name, destination_list) + raise AssertionError(f"Unparsable line: {line}")
+ + + +
+[docs] +def get_modules(filename: str) -> list[BaseModule]: + """Opens a file and returns all the modules. + + Args: + filename (str): name of file to open + + Returns: + list[BaseModule]: list of modules. + """ + modules: list[BaseModule] = [] + with open(filename, encoding="utf8") as file: + for line in file: + module: BaseModule = parse_line(line) + modules.append(module) + + modules = finalize_modules(modules) + return modules
+ + + +
+[docs] +def finalize_modules(modules: list[BaseModule]) -> list[BaseModule]: + """Finalize construction of all modules. + + For each module, calculate its inputs. + Then inject the inputs into our conjunction modules + Modifies `modules` inplace, and returns it + """ + inputs: dict[str, list[str]] = defaultdict(list) + + all_module_names: set[str] = set() + for module in modules: + for output in module.outputs: + inputs[output].append(module.name) + all_module_names.add(output) + all_module_names.add(module.name) + + # add inputs to all conjunctions, find out missing outputs + for module in modules: + if isinstance(module, ConjunctionModule): + module.set_inputs(inputs[module.name]) + all_module_names.remove(module.name) + + for item in all_module_names: + module = SinkModule(item, []) + modules.append(module) + + return modules
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day20/tests/test_classes.html b/_modules/day20/tests/test_classes.html new file mode 100644 index 0000000..f3ed52f --- /dev/null +++ b/_modules/day20/tests/test_classes.html @@ -0,0 +1,146 @@ + + + + + + day20.tests.test_classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day20.tests.test_classes

+"""Tests for day20 classes."""
+from day20.lib.classes import BroadcastModule, LoopCounter, Pulse, SinkModule
+
+
+
+[docs] +def test_modules() -> None: + """Ensure that sink/broadcast module are always in default state.""" + sink: SinkModule = SinkModule("rx", []) + assert sink.is_initial_state() + broadcast: BroadcastModule = BroadcastModule("broadcast", ["sink"]) + assert broadcast.is_initial_state() + broadcast.handle_pulse("button", Pulse.LOW) + assert broadcast.is_initial_state() + sink.handle_pulse("broadcast", Pulse.LOW) + assert sink.is_initial_state()
+ + + +
+[docs] +def test_loop_counter() -> None: + """Test ``LoopCounter`` class.""" + loop_counter: LoopCounter = LoopCounter(4) + loop_counter.add_result("loop1", 4) + loop_counter.add_result("loop1", 6) + loop_counter.add_result("loop1", 8) + assert not loop_counter.finished + loop_counter.add_result("loop2", 9) + assert not loop_counter.finished + loop_counter.add_result("loop3", 10) + assert not loop_counter.finished + loop_counter.add_result("loop4", 15) + assert loop_counter.finished + assert loop_counter.loop_lengths == { + "loop1": 4, + "loop2": 9, + "loop3": 10, + "loop4": 15, + }
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day20/tests/test_day20.html b/_modules/day20/tests/test_day20.html new file mode 100644 index 0000000..4ce104c --- /dev/null +++ b/_modules/day20/tests/test_day20.html @@ -0,0 +1,170 @@ + + + + + + day20.tests.test_day20 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day20.tests.test_day20

+"""Test day20 main functions."""
+import os
+import tempfile
+from typing import TYPE_CHECKING
+
+from day20.day20 import FILE_A, FILE_B, FILE_PT2, output_files, part1, part2, simulate
+from day20.lib.parsers import get_modules
+
+if TYPE_CHECKING:
+    from day20.lib.classes import PulseTarget
+
+
+
+[docs] +def test_day20() -> None: + """Test day20 main functions.""" + modules = get_modules(FILE_A) + assert part1(modules) == 32000000 + + modules = get_modules(FILE_A) + + stored_pulses: list[PulseTarget] = [] + modules_map = {module.name: module for module in modules} + simulate(modules_map, stored_pulses) + assert "\n".join(str(pulse) for pulse in stored_pulses) == "\n".join( + [ + "button -low-> broadcaster", + "broadcaster -low-> a", + "broadcaster -low-> b", + "broadcaster -low-> c", + "a -high-> b", + "b -high-> c", + "c -high-> inv", + "inv -low-> a", + "a -low-> b", + "b -low-> c", + "c -low-> inv", + "inv -high-> a", + ] + ) + + modules = get_modules(FILE_B) + assert part1(modules) == 11687500
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + modules = get_modules(FILE_PT2) + result, dots = part2(modules, True) + assert result == 495 + with tempfile.TemporaryDirectory(prefix="unit_test_outputs") as temp_dir: + output_files(dots, temp_dir) + assert len(os.listdir(temp_dir)) == 16 + + # run it without exporting. + modules = get_modules(FILE_PT2) + result, dots = part2(modules, False) + assert result == 495 + + with tempfile.TemporaryDirectory(prefix="unit_test_outputs") as temp_dir: + output_files(dots, temp_dir)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day20/tests/test_parsers.html b/_modules/day20/tests/test_parsers.html new file mode 100644 index 0000000..437923b --- /dev/null +++ b/_modules/day20/tests/test_parsers.html @@ -0,0 +1,167 @@ + + + + + + day20.tests.test_parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day20.tests.test_parsers

+"""Test parsing code."""
+from day20.day20 import FILE_A, FILE_B
+from day20.lib.classes import (
+    BaseModule,
+    BroadcastModule,
+    ConjunctionModule,
+    FlipFlopModule,
+)
+from day20.lib.parsers import finalize_modules, get_modules, parse_line
+
+
+
+[docs] +def test_parse_line() -> None: + """Test ``parse_line()``.""" + module: BaseModule = parse_line("broadcaster -> a") + assert isinstance(module, BroadcastModule) + module = parse_line("%a -> inv, con") + assert isinstance(module, FlipFlopModule) + assert module.outputs == ["inv", "con"] + + module = parse_line("&a -> inv, con") + assert isinstance(module, ConjunctionModule) + assert module.outputs == ["inv", "con"]
+ + + +
+[docs] +def test_get_modules() -> None: + """Test ``get_modules``.""" + modules: list[BaseModule] = get_modules(FILE_A) + assert len(modules) == 5 + assert modules[0].outputs == ["a", "b", "c"] + assert modules[1].outputs == ["b"] + assert modules[2].outputs == ["c"] + assert modules[3].outputs == ["inv"] + assert modules[4].outputs == ["a"] + + assert isinstance(modules[0], BroadcastModule) + assert isinstance(modules[1], FlipFlopModule) + assert isinstance(modules[2], FlipFlopModule) + assert isinstance(modules[3], FlipFlopModule) + assert isinstance(modules[4], ConjunctionModule)
+ + + +
+[docs] +def test_finalize_modules() -> None: + """Test ``finalize_modules()``.""" + modules: list[BaseModule] = get_modules(FILE_A) + modules = finalize_modules(modules) + + assert isinstance(modules[4], ConjunctionModule) + assert set(modules[4].inputs.keys()) == {"c"} + + modules = get_modules(FILE_B) + modules = finalize_modules(modules) + assert isinstance(modules[4], ConjunctionModule) + assert set(modules[4].inputs.keys()) == {"a", "b"}
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day21/day21.html b/_modules/day21/day21.html new file mode 100644 index 0000000..6b2d12c --- /dev/null +++ b/_modules/day21/day21.html @@ -0,0 +1,290 @@ + + + + + + day21.day21 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day21.day21

+"""day21 solution."""
+
+from dataclasses import dataclass
+from queue import Queue
+from typing import Optional
+
+from day21.lib.classes import (
+    BaseDistanceMaze,
+    DistanceMaze,
+    DistanceMazes,
+    GiantNodeParser,
+    GiantNodeType,
+    Maze,
+    Position,
+    PositionDist,
+)
+from day21.lib.parsers import parse_maze
+
+# first calculate minimum distance for every tile
+# everything that is even, is a winner for 64
+# there is no way to even get back onto an odd number. i am genius
+# calculate if there is a cycle that lands on itself in an odd number of steps
+# same for every tile i guess. DP weird champ
+
+FILE_SMALL = "day21/input-small.txt"
+FILE_MAIN = "day21/input.txt"
+FILE = FILE_MAIN
+
+
+GIGA_TARGET = 26_501_365  # even parity
+
+
+
+[docs] +def mini_solve( + start_pos: Position, maze: Maze, steps: int, distances: BaseDistanceMaze +) -> BaseDistanceMaze: + """Given a BaseDistanceMaze, runs `steps` steps then returns the maze.""" + nodes: Queue[PositionDist] = Queue() + nodes.put(PositionDist(start_pos.row, start_pos.col, distance=0)) + + distance_reached: bool = False + while not nodes.empty() and not distance_reached: + pos: PositionDist = nodes.get() + if pos.distance >= steps + 1: + distance_reached = True + continue + # expand + distance: Optional[int] = distances[pos] + maze_node: Optional[str] = maze[pos] + + if distance is None: # oob + continue + if distance != -1: # already explored + continue + if maze_node == "#": # hitting a wall + continue + # undiscovered! + distances[pos] = pos.distance + + south = pos.replace(row=pos.row + 1) + north = pos.replace(row=pos.row - 1) + east = pos.replace(col=pos.col + 1) + west = pos.replace(col=pos.col - 1) + + for direction in [north, south, east, west]: + nodes.put(direction) + return distances
+ + + +
+[docs] +@dataclass +class SmartSteps: + """How many boards to edge of solution, and steps to simulate.""" + + boards_to_edge: int + steps: int
+ + + +
+[docs] +def naive_solve( + start_pos: Position, maze: Maze, steps: int, distances: BaseDistanceMaze +) -> int: + """Naively solve a maze.""" + distances = mini_solve(start_pos, maze, steps, distances) + print(distances.overlay(maze)) + return distances.calc_steps(steps % 2)
+ + + +
+[docs] +def calculate_smart_steps(board_size: int, steps: int) -> SmartSteps: + """Given a board size and num steps, calculate how many steps we actually need.""" + steps_remaining = steps % board_size + if steps_remaining != board_size // 2: + raise ValueError("big mode only supported for steps_remaining == maze_rows//2") + boards_to_edge = steps // board_size + print("boards_to_edge", boards_to_edge) + + if boards_to_edge % 2 == 0: + sim_steps = board_size * 2 + steps_remaining + else: + sim_steps = board_size * 3 + steps_remaining + return SmartSteps(boards_to_edge, sim_steps)
+ + + +# FILE_MAIN is 131 x 131. +# This means we need 130 steps(?) to hit the corners, +# and 131 to get to next centre +
+[docs] +def solve( + start_pos: Position, + maze: Maze, + steps: int, + unlimited_map: bool = False, + brute_force: bool = False, # for > 3 boards wide, use smart algo +) -> int: + """Solve the maze. + + Args: + start_pos (Position): start position + maze (Maze): maze to solve + steps (int): number of steps + unlimited_map (bool, optional): whether the map is infinite (Default=False) + brute_force (bool, optional): Whether to brute force. (Defaults=False). + + Returns: + int: number of valid positions. + """ + distances: BaseDistanceMaze + + if unlimited_map: + distances = DistanceMazes(maze.num_rows, maze.num_cols) + else: # small + distances = DistanceMaze(maze.num_rows, maze.num_cols) + + # if we are small, or havent got smart_solve enabled, brute force it. + if not unlimited_map or (unlimited_map and brute_force): + return naive_solve(start_pos, maze, steps, distances) + + smart_steps: SmartSteps = calculate_smart_steps(maze.num_rows, steps) + distances = mini_solve(start_pos, maze, smart_steps.steps, distances) + + if maze.num_cols <= 5: + distances.overlay(maze) + + if not isinstance(distances, DistanceMazes): + raise AssertionError("ya done goof here") + giant_parser = GiantNodeParser(distances, smart_steps.boards_to_edge) + remainder = steps % 2 + result = 0 + for node_type in GiantNodeType: + node = giant_parser.get_node(node_type) + node_steps = node.calc_steps(remainder) + node_count = giant_parser.get_node_count(node_type) + node_type_steps = node_steps * node_count + print(f"{node_type.name}, count: {node_count}, steps: {node_steps}") + result += node_type_steps + + return result
+ + + +
+[docs] +def main() -> None: + """Load data then solve part1/part2.""" + start_pos, maze = parse_maze(FILE) + # part1 + print(solve(start_pos, maze, 64)) + + # part2 + print(solve(start_pos, maze, GIGA_TARGET, True, False))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day21/lib/classes.html b/_modules/day21/lib/classes.html new file mode 100644 index 0000000..c32c466 --- /dev/null +++ b/_modules/day21/lib/classes.html @@ -0,0 +1,585 @@ + + + + + + day21.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day21.lib.classes

+"""classes for day21."""
+
+
+from abc import ABC, abstractmethod
+from collections import defaultdict
+from dataclasses import dataclass
+from enum import Enum
+from typing import Optional
+
+from colorama import Back
+
+
+
+[docs] +@dataclass(unsafe_hash=True) +class Position: + """Simple 2d vector. + + It implements an unsafe hash since PositionDist inherits from this. + """ + + row: int + col: int + + def __str__(self) -> str: + """Prettyprint position.""" + return f"{self.row}, {self.col}"
+ + + +
+[docs] +@dataclass(kw_only=True) +class PositionDist(Position): + """Position + distance.""" + + distance: int + + def __str__(self) -> str: + """Pretty-print.""" + return f"{self.row}, {self.col}, d={self.distance}" + +
+[docs] + def replace( + self, + row: Optional[int] = None, + col: Optional[int] = None, + distance: Optional[int] = None, + ) -> "PositionDist": + """Return a copy with given args changed. + + Distance will +1 if not supplied + """ + row = row if row is not None else self.row + col = col if col is not None else self.col + distance = distance if distance is not None else self.distance + 1 + return PositionDist(row, col, distance=distance)
+
+ + + +
+[docs] +class Maze: + """2d grid of items.""" + + grid: list[str] + + num_rows: int + num_cols: int + + def __init__(self, data: list[str]) -> None: + """Constructs maze from list of strings.""" + self.grid = data + self.num_rows = len(data) + self.num_cols = len(data[0]) + + def __str__(self) -> str: + """Pretty-print.""" + return "\n".join(row for row in self.grid) + + def __getitem__(self, position: Position) -> Optional[str]: + """Get item via position. Always wraps.""" + if not isinstance(position, Position): + raise AssertionError(f"position is not a Position, {type(position)}") + + row = position.row % self.num_rows + col = position.col % self.num_cols + return self.grid[row][col]
+ + + +
+[docs] +class BaseDistanceMaze(ABC): + """Abstract distance maze.""" + +
+[docs] + @abstractmethod + def overlay(self, maze: Maze) -> str: + """Overlays on top of a maze.""" + raise AssertionError("Not implemented")
+ + +
+[docs] + @abstractmethod + def calc_steps(self, remainder: int) -> int: + """Calculate steps. + + Matches remainder == 1 or remainder == 0 + When modulo'ing by 2 + """
+ + + @abstractmethod + def __setitem__(self, position: Position, value: int) -> None: + """Sets the 2d arrays value based on a position.""" + + @abstractmethod + def __getitem__(self, position: Position) -> Optional[int]: + """Get the integer distance based on position."""
+ + + +
+[docs] +class DistanceMaze(BaseDistanceMaze): + """Distance Maze == Maze.size.""" + + grid: list[list[int]] + num_rows: int + num_cols: int + + def __init__(self, num_rows: int, num_cols: int) -> None: + """Creates empty distance maze.""" + self.num_rows = num_rows + self.num_cols = num_cols + + self.grid = [[-1 for _ in range(num_cols)] for _ in range(num_rows)] + + def __setitem__(self, position: Position, value: int) -> None: + """Sets item at given position. Silently fails if out of bounds.""" + if not isinstance(position, Position): + raise AssertionError(f"position is not a Position, {type(position)}") + if self.is_oob(position): + return + self.grid[position.row][position.col] = value + + def __getitem__(self, position: Position) -> Optional[int]: + """Get item via position. Returns None if out of bounds.""" + if not isinstance(position, Position): + raise AssertionError(f"position is not a Position, {type(position)}") + if self.is_oob(position): + return None + return self.grid[position.row][position.col] + + def __str__(self) -> str: + """Prettyprint distance maze.""" + return "\n".join( + "".join(self.int_to_str(col) for col in row) for row in self.grid + ) + +
+[docs] + def is_oob(self, position: Position) -> bool: + """True if position is out of bounds.""" + return ( + position.row < 0 + or position.row >= self.num_rows + or position.col < 0 + or position.col >= self.num_cols + )
+ + +
+[docs] + def int_to_str(self, value: int) -> str: + """Convert integert to string.""" + if value == -1: + return "_" + return str(value)[-1]
+ + +
+[docs] + def calc_steps(self, remainder: int) -> int: + """Calculate steps, based on odd/even steps.""" + + def is_valid(val: int) -> bool: + return val % 2 == remainder and val >= 0 + + result = 0 + for row in self.grid: + result += sum(1 if is_valid(item) else 0 for item in row) + return result
+ + +
+[docs] + def overlay(self, maze: Maze) -> str: + """Overlay this distance_maze on a maze.""" + new_strings: list[str] = [] + base_str: str = str(self) + is_complete = self.is_complete() + for row, line in enumerate(base_str.split("\n")): + other_str = maze.grid[row] + my_str = "" + for col, value in enumerate(line): + if is_complete and self.centre_cell(row, col): + if value in "02468": + my_str += Back.GREEN + value + Back.BLACK + else: + my_str += Back.RED + value + Back.BLACK + elif value == "_": + my_str += other_str[col] + else: + my_str += value + + new_strings.append(my_str) + return "\n".join(new_strings)
+ + +
+[docs] + def centre_cell(self, row: int, col: int) -> bool: + """Returns true if coordinate is the centre cell of the maze.""" + return row == self.num_rows // 2 and col == self.num_cols // 2
+ + +
+[docs] + def is_complete(self) -> bool: + """Returns true if all cells are filled.""" + return ( + self.grid[0][0] != -1 + and self.grid[0][-1] != -1 + and self.grid[-1][0] != -1 + and self.grid[-1][-1] != -1 + )
+
+ + + +
+[docs] +class DistanceMazes(BaseDistanceMaze): + """An array of distance mazes, able to extend infinitely.""" + + grid: dict[Position, DistanceMaze] + + rows_per_maze: int + cols_per_maze: int + + def __init__(self, num_rows: int, num_cols: int): + """Initializes a growable 2d array of mazes.""" + self.rows_per_maze = num_rows + self.cols_per_maze = num_cols + self.grid = defaultdict(lambda: DistanceMaze(num_rows, num_cols)) + +
+[docs] + def get_big_grid(self, position: Position) -> DistanceMaze: + """Big grid coordinate.""" + return self.grid[position]
+ + + def __getitem__(self, position: Position) -> int: + """Global coordinate 0 .. ± infinity.""" + big_pos, sub_pos = self.get_split_pos(position) + + result = self.grid[big_pos][sub_pos] + if result is None: + raise AssertionError("Unexpected result, None") + return result + + def __setitem__(self, position: Position, value: int) -> None: + """Sets a position's value.""" + if not isinstance(position, Position): + raise AssertionError(f"position is not a Position, {type(position)}") + big_pos, sub_pos = self.get_split_pos(position) + self.grid[big_pos][sub_pos] = value + +
+[docs] + def get_split_pos(self, position: Position) -> tuple[Position, Position]: + """Split global position. + + Into big map and small map positions. + + Args: + position (Position): global position + + Returns: + tuple[Position, Position]: big coord, small coord + """ + big_grid_row: int = position.row // self.rows_per_maze + big_grid_col: int = position.col // self.cols_per_maze + + sub_grid_row: int = position.row % self.rows_per_maze + sub_grid_col: int = position.col % self.cols_per_maze + big_pos = Position(big_grid_row, big_grid_col) + sub_pos = Position(sub_grid_row, sub_grid_col) + return big_pos, sub_pos
+ + +
+[docs] + def overlay(self, maze: Maze) -> str: + """Overlay our gigamap onto a maze.""" + coords = list(self.grid.keys()) + rows = sorted([coord.row for coord in coords]) + cols = sorted([coord.col for coord in coords]) + + if len(rows) == 0: + raise AssertionError("grid has no subgrids!") + else: + min_row, max_row = rows[0], rows[-1] + min_col, max_col = cols[0], cols[-1] + + result = "" + for big_row in range(min_row, max_row + 1): + grids = [] + for big_col in range(min_col, max_col + 1): + sub_grid: DistanceMaze = self.grid[Position(big_row, big_col)] + grids.append(sub_grid.overlay(maze)) + + grid_splits = [grid.split("\n") for grid in grids] + + row_lines = "\n".join(" ".join(lines) for lines in zip(*grid_splits)) + result += row_lines + result += "\n\n" + return result
+ + +
+[docs] + def calc_steps(self, remainder: int) -> int: + """Calculate steps given parity.""" + distance_mazes = self.grid.values() + result = sum(sub_grid.calc_steps(remainder) for sub_grid in distance_mazes) + return result
+
+ + + +
+[docs] +class GiantNodeType(Enum): + """A bunch of giant node types. + + turn each "maze" into a node type. + assume parity == EVEN + e.g. 9 x 9 maze matrix + mazes_center_to_bottom == 9 //2 == 4 + + BIG -> mazes_center_to_bottom - 1 == 3 + SMALL -> mazes_center_to_bottom == 4 + FULL_EVEN -> (mazes_center_to_bottom-1) ^2 == 9 + FULL_ODD -> mazes_center_to_bottom^2 == 16 + """ + + FULL_EVEN = 0 # same as start tile + FULL_ODD = 1 # 1 away from start tile + NORTH_TIP = 2 + NORTH_EAST_BIG = 3 + NORTH_EAST_SMALL = 4 + EAST_TIP = 5 + SOUTH_EAST_BIG = 6 + SOUTH_EAST_SMALL = 7 + SOUTH_TIP = 8 + SOUTH_WEST_BIG = 9 + SOUTH_WEST_SMALL = 10 + WEST_TIP = 11 + NORTH_WEST_BIG = 12 + NORTH_WEST_SMALL = 13
+ + + +
+[docs] +class GiantNodeParser: + """Convert from mazes to giant nodes.""" + + distance_mazes: DistanceMazes + full_edge_dist: int + edge_dist: int + + def __init__(self, distance_mazes: DistanceMazes, nodes_to_edge: int) -> None: + """Initialize Parser. + + e.g. 5x5, with steps == 10+2 + nodes_to_edge == 2 + 5x5, steps == 15+2 + nodes_to_edge == 3 + """ + if nodes_to_edge < 3: + raise ValueError("this shit only works with at least 3 bignodes!") + self.distance_mazes = distance_mazes + self.full_edge_dist = nodes_to_edge # edge dist of mega map + self.edge_dist = max(pos.row for pos in distance_mazes.grid) + print("nodes_to_edge", nodes_to_edge) + print("calculated_nodes_to_edge", self.edge_dist) + +
+[docs] + def get_node(self, node_type: GiantNodeType) -> DistanceMaze: # noqa: C901 + """Returns a giant node given its type.""" + if node_type == GiantNodeType.FULL_EVEN: + return self.distance_mazes.get_big_grid(Position(0, 0)) + elif node_type == GiantNodeType.FULL_ODD: + return self.distance_mazes.get_big_grid(Position(0, 1)) + elif node_type == GiantNodeType.NORTH_TIP: + return self.distance_mazes.get_big_grid(Position(-self.edge_dist, 0)) + elif node_type == GiantNodeType.NORTH_EAST_BIG: + return self.distance_mazes.get_big_grid(Position(-self.edge_dist + 1, 1)) + elif node_type == GiantNodeType.NORTH_EAST_SMALL: + return self.distance_mazes.get_big_grid(Position(-self.edge_dist, 1)) + elif node_type == GiantNodeType.EAST_TIP: + return self.distance_mazes.get_big_grid(Position(0, self.edge_dist)) + elif node_type == GiantNodeType.SOUTH_EAST_BIG: + return self.distance_mazes.get_big_grid(Position(1, self.edge_dist - 1)) + elif node_type == GiantNodeType.SOUTH_EAST_SMALL: + return self.distance_mazes.get_big_grid(Position(1, self.edge_dist)) + elif node_type == GiantNodeType.SOUTH_TIP: + return self.distance_mazes.get_big_grid(Position(self.edge_dist, 0)) + elif node_type == GiantNodeType.SOUTH_WEST_BIG: + return self.distance_mazes.get_big_grid(Position(self.edge_dist - 1, -1)) + elif node_type == GiantNodeType.SOUTH_WEST_SMALL: + return self.distance_mazes.get_big_grid(Position(self.edge_dist, -1)) + elif node_type == GiantNodeType.WEST_TIP: + return self.distance_mazes.get_big_grid(Position(0, -self.edge_dist)) + elif node_type == GiantNodeType.NORTH_WEST_BIG: + return self.distance_mazes.get_big_grid(Position(-1, -self.edge_dist + 1)) + elif node_type == GiantNodeType.NORTH_WEST_SMALL: + return self.distance_mazes.get_big_grid(Position(-1, -self.edge_dist)) + raise AssertionError(f"Unknown node_type: {node_type}")
+ + +
+[docs] + def get_node_count(self, node_type: GiantNodeType) -> int: + """Returns how many of the giant node are required.""" + remainder = self.full_edge_dist % 2 + if node_type == GiantNodeType.FULL_EVEN: + if remainder == 0: + return (self.full_edge_dist - 1) * (self.full_edge_dist - 1) + else: # odd_parity + return self.full_edge_dist * self.full_edge_dist + elif node_type == GiantNodeType.FULL_ODD: + if remainder == 0: + return self.full_edge_dist * self.full_edge_dist + else: # odd_parity + return (self.full_edge_dist - 1) * (self.full_edge_dist - 1) + elif node_type in [ + GiantNodeType.NORTH_TIP, + GiantNodeType.EAST_TIP, + GiantNodeType.WEST_TIP, + GiantNodeType.SOUTH_TIP, + ]: + return 1 + elif node_type in [ + GiantNodeType.NORTH_EAST_BIG, + GiantNodeType.NORTH_WEST_BIG, + GiantNodeType.SOUTH_EAST_BIG, + GiantNodeType.SOUTH_WEST_BIG, + ]: + return self.full_edge_dist - 1 + elif node_type in [ + GiantNodeType.NORTH_EAST_SMALL, + GiantNodeType.NORTH_WEST_SMALL, + GiantNodeType.SOUTH_EAST_SMALL, + GiantNodeType.SOUTH_WEST_SMALL, + ]: + return self.full_edge_dist + raise AssertionError(f"Unknown node type: {node_type}")
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day21/lib/parsers.html b/_modules/day21/lib/parsers.html new file mode 100644 index 0000000..3be8e94 --- /dev/null +++ b/_modules/day21/lib/parsers.html @@ -0,0 +1,125 @@ + + + + + + day21.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day21.lib.parsers

+"""Parsing code for day21."""
+from day21.lib.classes import Maze, Position
+
+
+
+[docs] +def parse_maze(filename: str) -> tuple[Position, Maze]: + """Returns a well defined Maze class.""" + maze_rows: list[str] = [] + with open(filename, encoding="utf8") as file: + for row, line in enumerate(file): + line = line.strip() + if "S" in line: + col = line.index("S") + start_pos = Position(row, col) + maze_rows.append(line) + if start_pos is None: + raise AssertionError("no start position!") + return start_pos, Maze(maze_rows)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/day22.html b/_modules/day22/day22.html new file mode 100644 index 0000000..edc1fbd --- /dev/null +++ b/_modules/day22/day22.html @@ -0,0 +1,275 @@ + + + + + + day22.day22 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.day22

+"""Day22 solution."""
+import vpython
+
+from day22.lib.classes import BoxData, Matrix
+from day22.lib.parsers import get_boxes
+from day22.lib.vis import CAMERA_AXIS_1, CAMERA_POS_1, bind_keys, follow_block, init_vis
+
+INPUT = "day22/input.txt"
+INPUT_SMALL = "day22/input-small.txt"
+
+
+AUTO_START = False
+ANIMATE = True
+
+
+
+[docs] +class Visualization: + """Visualization class.""" + + boxes: list[BoxData] + matrix: Matrix + has_started: bool + + def __init__(self, boxes: list[BoxData], animate: bool = True) -> None: + """Initialize Vis. + + Args: + boxes (list[BoxData]): list of boxes to visualize + animate (bool, optional): Whether to animate the visualizatoin. (Default=True) + """ + self.boxes = boxes + self.boxes.sort(key=lambda x: x.z_val_bot) + self.matrix = Matrix() + self.animate = animate + if self.animate: + init_vis(self.boxes) + bind_keys(self.start) + + self.has_started = False + +
+[docs] + def vis_rate(self, rate: float) -> None: + """Wait a given amount if we are animating.""" + if self.animate: + vpython.rate(rate)
+ + +
+[docs] + def follow_block(self, y: float, box: BoxData) -> None: + """Snap camera to a given box.""" + if self.animate: + follow_block(y, box)
+ + +
+[docs] + def start(self) -> None: + """Start visualization calcuations.""" + if self.has_started: # pragma: no cover + return + self.has_started = True + + if self.animate: + camera_height = vpython.scene.camera.pos.y + else: + camera_height = 0 + + for box in self.boxes: + while self.matrix.can_fall_down(box): + box.fall() + self.vis_rate(165) + self.matrix.register_box(box) + supports = self.matrix.get_supports(box) + box.set_supports(supports) + self.follow_block(camera_height, box) + + # have to do this after all boxes dropped + for box in self.boxes: + hats = self.matrix.get_hats(box) + box.set_hats(hats) + + print(self.calculate_part1()) + print(self.calculate_part2()) + + self.animate_part1() + self.animate_part2()
+ + +
+[docs] + def calculate_part1(self) -> int: + """Calculate part1. (number of boxes that can fly up).""" + # short answer + return sum(1 if self.matrix.can_fly_up(item) else 0 for item in self.boxes)
+ + +
+[docs] + def calculate_part2(self) -> int: + """Calculate part2 (number of boxes that will fall if each box is removed.""" + return sum(len(box.recursive_fall({box})) for box in self.boxes)
+ + +
+[docs] + def animate_part1(self) -> None: + """Animate part 1.""" + if self.animate: + vpython.scene.camera.pos = CAMERA_POS_1 + vpython.scene.camera.axis = CAMERA_AXIS_1 + for box in self.boxes: + if self.matrix.can_fly_up(box): + box.select() + self.vis_rate(60) + + for box in self.boxes: + if self.matrix.can_fly_up(box): + box.unselect() + self.vis_rate(60)
+ + +
+[docs] + def animate_part2(self) -> None: + """Animate part2.""" + if self.animate: + vpython.scene.camera.pos = CAMERA_POS_1 + vpython.scene.camera.axis = CAMERA_AXIS_1 + reversed_boxes = sorted( + self.boxes, key=lambda box: box.end_pos.z, reverse=True + ) + for box in reversed_boxes: + to_fall = box.recursive_fall({box}) + to_fall_sorted = sorted(to_fall, key=lambda x: x.z_val_bot) + if len(to_fall_sorted) == 0: + continue + + for faller in to_fall_sorted: + faller.select() + + self.vis_rate(20) + + for faller in to_fall: + faller.unselect()
+
+ + + +
+[docs] +def main() -> None: + """Grab boxes and solve, while animating solution.""" + boxes: list[BoxData] = get_boxes(INPUT) + vis = Visualization(boxes, ANIMATE) + + if AUTO_START: + vis.start() + + while True: + vpython.rate(165) # we control sleeping + while vis.has_started: # hand over vpython.sleep to vis + pass
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/lib/classes.html b/_modules/day22/lib/classes.html new file mode 100644 index 0000000..ca1305c --- /dev/null +++ b/_modules/day22/lib/classes.html @@ -0,0 +1,348 @@ + + + + + + day22.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.lib.classes

+"""Classes for day22."""
+from dataclasses import dataclass, field
+from typing import Optional
+
+import vpython
+
+
+
+[docs] +@dataclass +class Vector3: + """Simple 3d vector.""" + + x: int + y: int + z: int
+ + + +
+[docs] +@dataclass(unsafe_hash=True) +class BoxData: + """A box in 3d space.""" + + name: str = field(hash=True) + start_pos: Vector3 = field(hash=False) + end_pos: Vector3 = field(hash=False) + vbox: Optional[vpython.box] = field( + init=False, repr=False, hash=False, default=None + ) + supports: set["BoxData"] = field( + default_factory=set, hash=False, repr=False, init=False + ) # list of blocks we support + hats: set["BoxData"] = field( + default_factory=set, hash=False, repr=False, init=False + ) + total_hats: set["BoxData"] = field( + default_factory=set, hash=False, repr=False, init=False + ) + + @property + def vpos(self) -> vpython.vector: + """Pos according to vpython.""" + pos = self.start_pos + return vpython.vector( + pos.x + self.length / 2, pos.z + self.height / 2, pos.y + self.width / 2 + ) + + @property + def length(self) -> float: + """Length according to vpython.""" + return float(self.end_pos.x - self.start_pos.x + 1) + + @property + def width(self) -> float: + """Width according to vpython.""" + return float(self.end_pos.y - self.start_pos.y + 1) + + @property + def height(self) -> float: + """Height according to vpython.""" + return float(self.end_pos.z - self.start_pos.z + 1) + + @property + def z_val_bot(self) -> int: + """Return lowest z value (self.start_pos.z).""" + return self.start_pos.z + + @property + def z_val_top(self) -> int: + """Return maximum z value(self.end_pos.z).""" + return self.end_pos.z + + #################################### + # Visualisation calls (not ci'ed) # + #################################### +
+[docs] + def set_vbox(self, vbox: vpython.box) -> None: # pragma: no cover + """Store a vpython box onto this boxdata.""" + self.vbox = vbox
+ + +
+[docs] + def fall(self) -> None: + """Move block down vertically.""" + self.start_pos.z -= 1 + self.end_pos.z -= 1 + # vbox y == boxdata z + if self.vbox is not None: # pragma: no cover + self.vbox.pos.y -= 1
+ + +
+[docs] + def select(self) -> None: + """Select a box by offsetting it to the side.""" + if self.vbox is not None: # pragma: no cover + self.vbox.pos.x += 30 + self.vbox.pos.z -= 30
+ + +
+[docs] + def unselect(self) -> None: + """Unselect a box by putting it back.""" + if self.vbox is not None: # pragma: no cover + self.vbox.pos.x -= 30 + self.vbox.pos.z += 30
+ + + ################################################ + # Calculations; supports, hats, recursive fall # + ################################################ + +
+[docs] + def set_supports(self, supports: set["BoxData"]) -> None: + """Set the BoxData's that support us.""" + self.supports = supports
+ + +
+[docs] + def set_hats(self, hats: set["BoxData"]) -> None: + """Set the BoxData's that we support.""" + self.hats = hats
+ + +
+[docs] + def recursive_fall(self, already_falling: set["BoxData"]) -> set["BoxData"]: + """Returns all boxes above us that fall if we fall.""" + to_process: list[BoxData] = [] # items that will fall if this brick is removed + result: set["BoxData"] = set() + for hat in self.hats.difference(already_falling): + remaining_supports = hat.supports.difference(already_falling) + # if no children support this parent + if len(remaining_supports) == 0: + result.add(hat) + already_falling.add(hat) + to_process.append(hat) + + # for each parent that falls + for node in to_process: + # recursively call chain_remove on this parent + result.update(node.recursive_fall(already_falling)) + + return result
+
+ + + +
+[docs] +class Matrix: + """3d matrix.""" + + # z, x, y + layers: list[list[list[Optional[BoxData]]]] + + def __init__(self, z_height: int = 400, xy: int = 10): + """Initialize based on size.""" + self.layers = [] + for _ in range(z_height): + layer: list[list[Optional[BoxData]]] = [ + [None for _ in range(xy)] for _ in range(xy) + ] + self.layers.append(layer) + +
+[docs] + def can_fall_down(self, box: BoxData) -> bool: + """Whether a given box can fall downwards. + + Args: + box (BoxData): box to test + + Returns: + bool: True if the box can fall. + """ + if box.z_val_bot == 1: + return False + for x in range(box.start_pos.x, box.end_pos.x + 1): + for y in range(box.start_pos.y, box.end_pos.y + 1): + if self.layers[box.z_val_bot - 1][x][y] is not None: + return False + return True
+ + +
+[docs] + def register_box(self, box: BoxData) -> None: + """Register box into matrix.""" + for z in range(box.start_pos.z, box.end_pos.z + 1): + for x in range(box.start_pos.x, box.end_pos.x + 1): + for y in range(box.start_pos.y, box.end_pos.y + 1): + if self.layers[z][x][y] is not None: + raise AssertionError("Overlap should not occur") + + self.layers[z][x][y] = box
+ + +
+[docs] + def get_supports(self, box: BoxData) -> set[BoxData]: + """Return which boxes are supporting this box.""" + if box.z_val_bot == 1: + return set() + + supports: set[BoxData] = set() + for x in range(box.start_pos.x, box.end_pos.x + 1): + for y in range(box.start_pos.y, box.end_pos.y + 1): + value = self.layers[box.z_val_bot - 1][x][y] + if value is not None: + supports.add(value) + return supports
+ + +
+[docs] + def get_hats(self, box: BoxData) -> set[BoxData]: + """Return which boxes are resting on this box.""" + hats: set[BoxData] = set() # list of items we're supporting + for x in range(box.start_pos.x, box.end_pos.x + 1): + for y in range(box.start_pos.y, box.end_pos.y + 1): + value = self.layers[box.z_val_top + 1][x][y] + if value is not None: + hats.add(value) + return hats
+ + +
+[docs] + def can_fly_up(self, box: BoxData) -> bool: + """Check cells above our block. If they're clear we can fly up.""" + # all our "hats" need to have > 1 supports + hats = list(box.hats) + return all(len(hat.supports) > 1 for hat in hats)
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/lib/parsers.html b/_modules/day22/lib/parsers.html new file mode 100644 index 0000000..68490c1 --- /dev/null +++ b/_modules/day22/lib/parsers.html @@ -0,0 +1,138 @@ + + + + + + day22.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.lib.parsers

+"""Parse vectors/boxes from string to class."""
+from day22.lib.classes import BoxData, Vector3
+
+
+
+[docs] +def parse_vector(string: str) -> Vector3: + """Returns a wellformed vector from a string. + + e.g. 1,2,3 + """ + x, y, z = string.split(",") + return Vector3(int(x), int(y), int(z))
+ + + +
+[docs] +def get_boxes(path: str) -> list[BoxData]: + """Returns a wellformed box from a string. + + E.g. 1,2,3~1,2,4 + """ + boxes: list[BoxData] = [] + with open(path, encoding="utf8") as file: + for index, line in enumerate(file): + line = line.strip() + vec1, vec2 = line.split("~") + from_vec = parse_vector(vec1) + to_vec = parse_vector(vec2) + boxes.append(BoxData(str(index), from_vec, to_vec)) + return boxes
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/lib/vis.html b/_modules/day22/lib/vis.html new file mode 100644 index 0000000..4bc6398 --- /dev/null +++ b/_modules/day22/lib/vis.html @@ -0,0 +1,237 @@ + + + + + + day22.lib.vis — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.lib.vis

+"""Visualization classes."""
+import random
+from typing import Any
+
+import vpython
+
+from day22.lib.classes import BoxData, Matrix, Vector3
+
+
+
+[docs] +def construct_box(box_data: BoxData, color: vpython.vector) -> vpython.box: + """Constructs a vpython box from a box_data. + + Args: + box_data (BoxData): box data to mimic + color (vpython.vector): color of box for vis. + + Returns: + vpython.box: a vpython.box + """ + return vpython.box( + pos=box_data.vpos, + length=box_data.length, + height=box_data.height, + width=box_data.width, + color=color, + )
+ + + +
+[docs] +def random_color() -> vpython.vector: + """Returns a random color compatible with vpython.""" + hsv = vpython.vector(random.random(), random.uniform(0.5, 1.0), 1.0) + return vpython.color.hsv_to_rgb(hsv)
+ + + +CAMERA_START = vpython.vector(40.0164, 11.0337, 39.9238) +CAMERA_AXIS = -CAMERA_START + +CAMERA_POS_1 = vpython.vector(111.512, 122.347, 65.0144) +CAMERA_AXIS_1 = vpython.vector(-83.3746, -20.0517, -69.4084) + + +
+[docs] +def init_vis(boxes: list[BoxData]) -> None: + """Initialize vis boxes. Only called if we're visualizing.""" + for box in boxes: + color = random_color() + vbox = construct_box(box, color) + box.set_vbox(vbox) + + ground = BoxData("ground", Vector3(0, 0, 0), Vector3(10, 10, 0)) + construct_box(ground, random_color()) + # init camera: + vpython.scene.camera.axis = CAMERA_AXIS + vpython.scene.camera.pos = CAMERA_START + vpython.scene.height = 600 + vpython.scene.width = 400
+ + + +
+[docs] +def bind_keys(on_key_down: Any) -> None: + """Bind keyboard events, so that ``enter`` calls the given callback.""" + + def callback(evt: Any) -> None: + character = evt.key + if character == "shift": + return + print(character) + if character in ["\n", "enter", "return"]: + on_key_down() + + vpython.scene.bind("keydown", callback)
+ + + +
+[docs] +def follow_block(y: float, box: BoxData) -> None: + """Force camera to follow a block.""" + pos = vpython.scene.camera.pos + pos.y = max(y + box.start_pos.z, pos.y) + vpython.scene.camera.pos = pos
+ + + +
+[docs] +def animate_part1(boxes: list[BoxData], matrix: Matrix) -> None: + """Animates part1.""" + vpython.scene.camera.pos = CAMERA_POS_1 + vpython.scene.camera.axis = CAMERA_AXIS_1 + for box in boxes: + if matrix.can_fly_up(box): + box.select() + vpython.rate(60) + + for box in boxes: + if matrix.can_fly_up(box): + box.unselect() + vpython.rate(60)
+ + + +
+[docs] +def animate_part2(boxes: list[BoxData]) -> None: + """Animates part2.""" + vpython.scene.camera.pos = CAMERA_POS_1 + vpython.scene.camera.axis = CAMERA_AXIS_1 + reversed_boxes = sorted(boxes, key=lambda box: box.end_pos.z, reverse=True) + for box in reversed_boxes: + to_fall = box.recursive_fall({box}) + to_fall_sorted = sorted(to_fall, key=lambda x: x.z_val_bot) + if len(to_fall_sorted) == 0: + continue + + for faller in to_fall_sorted: + faller.select() + + vpython.rate(20) + + for faller in to_fall: + faller.unselect()
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/tests/test_classes.html b/_modules/day22/tests/test_classes.html new file mode 100644 index 0000000..6098f9b --- /dev/null +++ b/_modules/day22/tests/test_classes.html @@ -0,0 +1,127 @@ + + + + + + day22.tests.test_classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.tests.test_classes

+"""Test classes in day22."""
+from typing import TYPE_CHECKING
+
+from day22.lib.classes import BoxData, Vector3
+
+if TYPE_CHECKING:
+    import vpython
+
+
+
+[docs] +def test_box_data() -> None: + """Tests ``BoxData`` class.""" + start: Vector3 = Vector3(1, 1, 1) + end: Vector3 = Vector3(1, 4, 1) + box: BoxData = BoxData("memes", start, end) + vec: vpython.vector = box.vpos + # our source z is vpython's y + assert vec.x == 1.5 + assert vec.z == 3.0 + assert vec.y == 1.5
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/tests/test_day22.html b/_modules/day22/tests/test_day22.html new file mode 100644 index 0000000..f3328e0 --- /dev/null +++ b/_modules/day22/tests/test_day22.html @@ -0,0 +1,126 @@ + + + + + + day22.tests.test_day22 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.tests.test_day22

+"""Test main functions of day22."""
+from typing import TYPE_CHECKING
+
+from day22.day22 import INPUT_SMALL, Visualization
+from day22.lib.parsers import get_boxes
+
+if TYPE_CHECKING:
+    from day22.lib.classes import BoxData
+
+
+
+[docs] +def test_visualization() -> None: + """Tests results from day22 vis class.""" + boxes: list[BoxData] = get_boxes(INPUT_SMALL) + vis = Visualization(boxes, False) + + vis.start() + assert vis.calculate_part1() == 5 + assert vis.calculate_part2() == 7
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day22/tests/test_parsers.html b/_modules/day22/tests/test_parsers.html new file mode 100644 index 0000000..b7bf4b6 --- /dev/null +++ b/_modules/day22/tests/test_parsers.html @@ -0,0 +1,120 @@ + + + + + + day22.tests.test_parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day22.tests.test_parsers

+"""Tests parsing functions."""
+from day22.day22 import INPUT_SMALL
+from day22.lib.classes import BoxData, Vector3
+from day22.lib.parsers import get_boxes
+
+
+
+[docs] +def test_parser() -> None: + """Test ``get_boxes()`` function.""" + boxes: list[BoxData] = get_boxes(INPUT_SMALL) + assert len(boxes) == 7 + assert boxes[0].start_pos == Vector3(1, 0, 1) + assert boxes[0].end_pos == Vector3(1, 2, 1)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/day23.html b/_modules/day23/day23.html new file mode 100644 index 0000000..9ecb468 --- /dev/null +++ b/_modules/day23/day23.html @@ -0,0 +1,148 @@ + + + + + + day23.day23 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.day23

+"""Day23 solution."""
+from day23.lib.classes import Maze, Path, Solver1
+from day23.lib.classes2 import Solver2
+from day23.lib.parsers import get_maze
+
+INPUT = "day23/input.txt"
+INPUT_SMALL = "day23/input-small.txt"
+
+
+
+[docs] +def part1(maze: Maze) -> int: + """Solve part1 (maximal distance given one-ways).""" + solver = Solver1(maze, True) + paths: list[Path] = solver.solve() + paths.sort(key=lambda path: len(path), reverse=True) + print(paths[0].overlay(maze)) + return len(paths[0])
+ + + +
+[docs] +def part2(maze: Maze) -> int: + """Solve part2 (maximal distance, no one-ways).""" + solver = Solver2(maze) + return solver.solve()
+ + + +
+[docs] +def main() -> None: + """Read data then solve part1/part2.""" + maze: Maze = get_maze(INPUT) + + print(part1(maze)) + print(part2(maze))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/lib/classes.html b/_modules/day23/lib/classes.html new file mode 100644 index 0000000..05ee646 --- /dev/null +++ b/_modules/day23/lib/classes.html @@ -0,0 +1,441 @@ + + + + + + day23.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.lib.classes

+"""Classes for part1."""
+from copy import deepcopy
+from dataclasses import dataclass
+from queue import Queue
+from typing import Any, Optional
+
+
+
+[docs] +@dataclass(frozen=True, slots=True) +class Position: + """Simple 2d Vector.""" + + row: int + col: int + +
+[docs] + def copy_modify( + self, row: Optional[int] = None, col: Optional[int] = None + ) -> "Position": + """Copies us and offsets by the given offset.""" + if row is None: + row = self.row + else: + row = self.row + row + if col is None: + col = self.col + else: + col = self.col + col + return Position(row, col)
+ + + def __str__(self) -> str: + """Pretty-printable representation.""" + return f"{self.row}, {self.col}" + +
+[docs] + def expand(self) -> list["Position"]: + """Expand in 4 cardinal directions.""" + return [ + self.copy_modify(row=-1), + self.copy_modify(row=+1), + self.copy_modify(col=-1), + self.copy_modify(col=+1), + ]
+
+ + + +
+[docs] +class Path: + """A list of Positions.""" + + route: list[Position] + nodes: set[Position] + + def __init__(self) -> None: + """Creates an empty path.""" + self.route = [] + self.nodes = set() + +
+[docs] + def can_add(self, position: Position) -> bool: + """Whether we can add a given position. + + If we have already visited the position, we can't + """ + return position not in self.nodes
+ + +
+[docs] + def add(self, position: Position) -> None: + """Add a position to the path.""" + self.route.append(position) + self.nodes.add(position)
+ + +
+[docs] + def copy(self) -> "Path": + """Copy us.""" + result = Path() + result.route = self.route[:] + result.nodes = set(result.route) + return result
+ + +
+[docs] + def flip(self) -> "Path": + """Flip a path by reversing the order.""" + result = self.copy() + result.route.reverse() + return result
+ + +
+[docs] + def last(self) -> Position: + """Return last position in path. + + Raises: + ValueError: if we have no positions. + + Returns: + Position: last position in path + """ + if len(self.route) == 0: + raise ValueError("Don't call last when i'm empty 4head") + return self.route[-1]
+ + +
+[docs] + def overlay(self, maze: "Maze") -> str: + """Overlay us onto a maze, return a neat string. + + Args: + maze (Maze): maze to overlay. + + Returns: + str: a nice string to print. + """ + base_str = str(maze) + char_array: list[list[str]] = [list(line) for line in base_str.split("\n")] + for node in self.route: + char_array[node.row][node.col] = "O" + return "\n".join("".join(char for char in row) for row in char_array)
+ + + def __len__(self) -> int: + """Length of path. + + Will be offset by -1 because problem is like that. + """ + return len(self.route) - 1 + + def __eq__(self, other: object) -> Any: + """Whether another path is equal.""" + if not isinstance(other, Path): + raise AssertionError("eq only works on Path") + return self.route == other.route + + def __hash__(self) -> int: + """Custom hash function so we can be added to a set.""" + return hash(",".join(str(item) for item in self.route))
+ + + +
+[docs] +class Maze: + """2d array of chars.""" + + grid: list[list[str]] # 2d array of chars + num_rows: int + num_cols: int + + def __init__(self, data: list[list[str]]) -> None: + """Initializes us and our row/col fields.""" + self.grid = data + self.num_rows = len(data) + self.num_cols = len(data[0]) + + def __str__(self) -> str: + """Pretty-print.""" + return "\n".join("".join(col for col in row) for row in self.grid) + + def __getitem__(self, position: Position) -> Optional[str]: + """Get item via position. Returns None if out of bounds.""" + if not isinstance(position, Position): + raise AssertionError(f"position is not a Position, {type(position)}") + if self.is_oob(position): + return None + return self.grid[position.row][position.col] + + def __setitem__(self, position: Position, value: str) -> None: + """Get item via position. Returns None if out of bounds.""" + if not isinstance(position, Position): + raise AssertionError(f"position is not a Position, {type(position)}") + if self.is_oob(position): + raise AssertionError("can't set outside our maze!") + self.grid[position.row][position.col] = value + +
+[docs] + def is_oob(self, position: Position) -> bool: + """True if position is out of bounds.""" + return ( + position.row < 0 + or position.row >= self.num_rows + or position.col < 0 + or position.col >= self.num_cols + )
+ + +
+[docs] + def copy(self) -> "Maze": + """Copies us.""" + result = Maze(deepcopy(self.grid)) + return result
+ + +
+[docs] + def get_cell_branches(self, position: Position) -> int: + """Returns how many branches come out of this tile.""" + result = 0 + if self[position] != ".": + return 0 + for direction in position.expand(): + tile = self[direction] + if tile is not None and tile != "#": + result += 1 + return result
+
+ + + +
+[docs] +class Solver1: + """Solver for part1.""" + + maze: Maze + handle_hills: bool + + def __init__(self, maze: Maze, handle_hills: bool = True) -> None: + """Create a solver, by storing our maze and whether we want to handle hills. + + Args: + maze (Maze): maze to solve + handle_hills (bool, optional): Whether ``<^V>`` are one-way. (Default=True) + """ + self.maze = maze + self.handle_hills = handle_hills + +
+[docs] + def solve(self) -> list[Path]: + """Solve the maze, using bfs.""" + paths: Queue[Path] = Queue() + first_path = Path() + first_path.add(Position(0, 1)) + paths.put(first_path) + # bfs all paths simultaneously + results: list[Path] = [] + count = 1 + while not paths.empty(): + path = paths.get() + if path.last().row == self.maze.num_rows - 1: + results.append(path) + continue + + expansions = self.expand_path(path) + for expansion in expansions: + paths.put(expansion) + count += 1 + + return results
+ + +
+[docs] + def expand_hill(self, position: Position, tile: str) -> list[Position]: + """Expand valid positions based on our current tile.""" + if tile == "^": + return [position.copy_modify(row=-1)] + elif tile == "v": + return [position.copy_modify(row=+1)] + elif tile == "<": + return [position.copy_modify(col=-1)] + elif tile == ">": + return [position.copy_modify(col=+1)] + else: + return position.expand()
+ + +
+[docs] + def expand_path(self, path: Path) -> list[Path]: + """Expand path based on current path.""" + current_pos: Position = path.last() + current_tile: Optional[str] = self.maze[current_pos] + if current_tile is None: + raise AssertionError("there's no shot we got a tile outside the maze") + expansions: list[Position] + if self.handle_hills: + expansions = self.expand_hill(current_pos, current_tile) + else: + expansions = current_pos.expand() + + valid_expansions = [] + for expansion in expansions: + expansion_tile = self.maze[expansion] + if ( + path.can_add(expansion) + and expansion_tile is not None + and expansion_tile != "#" + ): + valid_expansions.append(expansion) + return generate_paths(path, valid_expansions)
+
+ + + +
+[docs] +def generate_paths(path: Path, expansions: list[Position]) -> list[Path]: + """Given a path and valid expansions, (optionally) copies the path. + + Returns a list of new paths. + If there is only one expansion, modifies it in-place + """ + if len(expansions) == 0: + return [] + elif len(expansions) == 1: + path.add(expansions[0]) + return [path] + else: + result = [] + for expansion in expansions[1:]: + new_path = path.copy() + new_path.add(expansion) + result.append(new_path) + path.add(expansions[0]) + result.append(path) + + return result
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/lib/classes2.html b/_modules/day23/lib/classes2.html new file mode 100644 index 0000000..cc3294a --- /dev/null +++ b/_modules/day23/lib/classes2.html @@ -0,0 +1,356 @@ + + + + + + day23.lib.classes2 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.lib.classes2

+"""part 2 solution."""
+import math
+import os
+import time
+from concurrent.futures import ProcessPoolExecutor as Pool
+from dataclasses import dataclass, field
+from queue import Queue
+from typing import Any
+
+import colorama
+
+from day23.lib import classes
+from day23.lib.classes import Maze, Path, Position
+
+colorama.init(convert=True)
+
+
+
+[docs] +@dataclass(eq=True) +class Node: + """Node representing a fork to another.""" + + name: int = field(compare=True) + position: Position = field(compare=False) + edges: list["Edge"] = field(default_factory=list, repr=False, compare=False) + + def __str__(self) -> str: + """Pretty-print.""" + return f"{self.name}: ({self.position}) {[str(edge) for edge in self.edges]}"
+ + + +
+[docs] +@dataclass +class Edge: + """Edge class, representing a path between nodes.""" + + node1: int + node2: int + path: Path = field(repr=False) + length: int = 0 + + def __post_init__(self) -> None: + """Cache our length.""" + self.length = len(self.path) + +
+[docs] + def flip(self) -> "Edge": + """Reverse a path.""" + return Edge(self.node2, self.node1, self.path.flip())
+ + + def __str__(self) -> str: + """Pretty-print.""" + return f"{self.node1}->{self.node2}, {self.length}"
+ + + +
+[docs] +class Solver2: + """Solver for part 2.""" + + input_maze: Maze + + def __init__(self, maze: Maze) -> None: + """Store maze that we need to solve.""" + self.input_maze = maze + +
+[docs] + @staticmethod + def get_nodes(maze: Maze) -> dict[Position, Node]: + """Gets nodes and marks them on the given maze. + + Note that the maze is modified in-place! + Nodes are *not* populated with edges + """ + nodes: list[Node] = [] + + start = Position(0, 1) + nodes.append(Node(0, start)) + name = 1 + for row in range(maze.num_rows): + for col in range(maze.num_cols): + pos = Position(row, col) + if maze.get_cell_branches(pos) > 2: + node = Node(name, pos) + name += 1 + nodes.append(node) + + # add start and end coz they are dumb + end = Position(maze.num_rows - 1, maze.num_cols - 2) + nodes.append(Node(name, end)) + + for node in nodes: + maze[node.position] = colorama.Back.GREEN + "X" + colorama.Back.BLACK + return {node.position: node for node in nodes}
+ + +
+[docs] + @staticmethod + def calculate_edges( + start_node: Node, nodes: dict[Position, Node], maze: Maze + ) -> None: + """Calculate edges of the maze. + + Modifies the maze inplace, filling it in with #. + Modifies the node and its connecting nodes by adding Edges + """ + first_path = Path() + first_path.add(start_node.position) + paths: Queue[Path] = Queue() + paths.put(first_path) + while not paths.empty(): + path = paths.get() + pos = path.last() + if pos != start_node.position and pos in nodes: + # reached an edge + edge = Edge(start_node.name, nodes[pos].name, path) + start_node.edges.append(edge) + end_node = nodes[pos] + end_node.edges.append(edge.flip()) + continue + expansions = Solver2.expand_path(path, maze) + for path in expansions: + paths.put(path)
+ + +
+[docs] + @staticmethod + def expand_path(path: Path, maze: Maze) -> list[Path]: + """Expands a path, nuking that section of the maze using #.""" + current_pos: Position = path.last() + expansions = current_pos.expand() + + valid_expansions = [] + for expansion in expansions: + expansion_tile = maze[expansion] + if ( + path.can_add(expansion) + and expansion_tile is not None + and expansion_tile != "#" + ): + valid_expansions.append(expansion) + if expansion_tile == ".": + maze[expansion] = "#" + return classes.generate_paths(path, valid_expansions)
+ + +
+[docs] + def build_nodes(self) -> list[Node]: + """Build nodes and edges on a copy of the maze.""" + # make backup of maze + maze_copy = self.input_maze.copy() + nodes: dict[Position, Node] = self.get_nodes(maze_copy) + print(maze_copy) + for node in nodes.values(): + self.calculate_edges(node, nodes, maze_copy) + + return list(nodes.values())
+ + +
+[docs] + def solve(self) -> int: + """Solves the maze.""" + nodes: list[Node] = self.build_nodes() + + print("\n".join(str(node) for node in nodes)) + start = time.time() + cpu_count = os.cpu_count() or 2 + levels = int(math.log(cpu_count, 2)) + result = solve2(nodes, 0, len(nodes) - 1, 0, set(), levels) + print(f"Executed in: {time.time() - start}") + return result
+
+ + + +
+[docs] +def solve2( + nodes: list[Node], + current: int, + destination: int, + distance: int, + seen: set[int], + forks_remaining: int, +) -> int: + """Solves a dfs by creating forking into multiprocessing.""" + if current == destination: + return distance + + best = 0 + seen.add(current) + + # run the code in this thread + if forks_remaining == 0 or len(nodes[current].edges) == 1: + for edge in nodes[current].edges: + neighbor, weight = edge.node2, edge.length + if neighbor in seen: + continue + + result = solve2( + nodes, + neighbor, + destination, + distance + weight, + seen, + forks_remaining, + ) + best = max(best, result) + else: # Use multiprocessing.Pool for parallel execution + tasks = [] + for edge in nodes[current].edges: + neighbor, weight = edge.node2, edge.length + if neighbor in seen: + continue + tasks.append( + [ + nodes, + neighbor, + destination, + distance + weight, + seen, + forks_remaining - 1, + ] + ) + with Pool(len(tasks)) as pool: + for result in pool.map(solve2_helper, tasks): + best = max(best, result) + + seen.remove(current) + + return best
+ + + +
+[docs] +def solve2_helper(args: list[Any]) -> int: + """ThreadPoolExecutor doesnt have starmap so we use a helper.""" + return solve2(*args)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/lib/parsers.html b/_modules/day23/lib/parsers.html new file mode 100644 index 0000000..df9c935 --- /dev/null +++ b/_modules/day23/lib/parsers.html @@ -0,0 +1,118 @@ + + + + + + day23.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.lib.parsers

+"""Day23 parsers."""
+from day23.lib.classes import Maze
+
+
+
+[docs] +def get_maze(path: str) -> Maze: + """Parse input file and return wellformed maze.""" + rows: list[list[str]] = [] + with open(path, encoding="utf8") as file: + rows = [list(line.strip()) for line in file] + return Maze(rows)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/tests/test_classes.html b/_modules/day23/tests/test_classes.html new file mode 100644 index 0000000..1b65253 --- /dev/null +++ b/_modules/day23/tests/test_classes.html @@ -0,0 +1,239 @@ + + + + + + day23.tests.test_classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.tests.test_classes

+"""Tests for day23 classes."""
+import pytest
+
+from day23.day23 import INPUT_SMALL
+from day23.lib.classes import Maze, Path, Position, Solver1, generate_paths
+from day23.lib.parsers import get_maze
+
+
+
+[docs] +def test_position() -> None: + """Test ``Position`` class.""" + pos: Position = Position(0, 0) + pos2 = pos.copy_modify() + assert pos == pos2 + pos3 = pos.copy_modify(row=1) + assert pos3.row == 1 and pos3.col == 0 + pos4 = pos.copy_modify(col=1) + assert pos4.row == 0 and pos4.col == 1
+ + + +
+[docs] +def test_maze() -> None: + """Test ``Maze`` class.""" + maze: Maze = get_maze(INPUT_SMALL) + assert maze[Position(0, 0)] == "#" + assert maze[Position(0, 1)] == "." + assert maze[Position(-1, 0)] is None + + position_checks = [ + (Position(0, 1), 1), + (Position(1, 1), 2), + (Position(3, 11), 3), + (Position(5, 3), 3), + ] + + for pos, result in position_checks: + print(pos, result) + assert maze.get_cell_branches(pos) == result
+ + + +
+[docs] +def test_solver1() -> None: + """Test ``Solver`` class.""" + maze: Maze = get_maze(INPUT_SMALL) + solver: Solver1 = Solver1(maze) + + expands = [ + ( + Position(5, 5), + " ", + {Position(4, 5), Position(6, 5), Position(5, 4), Position(5, 6)}, + ), + (Position(5, 5), "^", {Position(4, 5)}), + (Position(5, 5), ">", {Position(5, 6)}), + (Position(5, 5), "<", {Position(5, 4)}), + (Position(5, 5), "v", {Position(6, 5)}), + ] + + for pos, tile, result in expands: + assert set(solver.expand_hill(pos, tile)) == result + + # solve part2 naively, to make sure the code works + maze = get_maze(INPUT_SMALL) + solver1b: Solver1 = Solver1(maze, handle_hills=False) + paths = solver1b.solve() + path_lengths = [len(path) for path in paths] + path_lengths.sort(reverse=True) + assert path_lengths[0] == 154
+ + + +
+[docs] +def test_path() -> None: + """Test ``Path`` class.""" + path = Path() + + # assert that path.last() fails when calling on empty path + with pytest.raises(ValueError): + path.last() + path.add(Position(0, 0)) + path.add(Position(0, 1)) + assert path.last() == Position(0, 1) + + path2 = path.copy() + + assert path2.last() == Position(0, 1) + path2.add(Position(0, 2)) + assert path.last() == Position(0, 1) + assert path2.last() == Position(0, 2)
+ + + +
+[docs] +def test_generate_paths() -> None: + """Test ``generate_paths()``.""" + path = Path() + path.add(Position(0, 0)) + path.add(Position(0, 1)) + + # manually generate the paths + path1 = path.copy() + path1.add(Position(0, 2)) + path2 = path.copy() + path2.add(Position(-1, 1)) + + # note that the original path is modified in-place! + paths: list[Path] = generate_paths(path, [Position(0, 2), Position(-1, 1)]) + assert len(paths) == 2 + + auto_path1 = paths[0] + auto_path2 = paths[1] + assert {auto_path1, auto_path2} == {path1, path2} + + paths = generate_paths(path, []) + assert (len(paths)) == 0 + + # test that we modify the path inplace when passed one position + path_before = path # reference + len_before = len(path_before.route) + paths = generate_paths(path, [Position(69, 69)]) + assert ( + len(paths) == 1 + and paths[0] == path_before + and len(path_before.route) == len(paths[0].route) + and len_before + 1 == len(path_before.route) + )
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/tests/test_classes2.html b/_modules/day23/tests/test_classes2.html new file mode 100644 index 0000000..f05e327 --- /dev/null +++ b/_modules/day23/tests/test_classes2.html @@ -0,0 +1,142 @@ + + + + + + day23.tests.test_classes2 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.tests.test_classes2

+"""Test day23 part 2."""
+from typing import TYPE_CHECKING
+
+from day23.day23 import INPUT_SMALL
+from day23.lib.classes import Position
+from day23.lib.classes2 import Node, Solver2
+from day23.lib.parsers import get_maze
+
+if TYPE_CHECKING:
+    from day23.lib.classes import Maze
+
+
+
+[docs] +def test_solver2() -> None: + """Test ``Solver2`` class.""" + maze: Maze = get_maze(INPUT_SMALL) + + # get_nodes + nodes: dict[Position, Node] = Solver2.get_nodes(maze.copy()) + assert len(nodes) == 9 + + # calculate_edges + start_pos = Position(0, 1) + Solver2.calculate_edges(nodes[start_pos], nodes, maze.copy()) + assert len(nodes[start_pos].edges) == 1 + assert nodes[start_pos].edges[0].length == 15 + + # build_nodes + solver = Solver2(maze) + nodes_list: list[Node] = solver.build_nodes() + print(nodes_list) + assert len(nodes_list[0].edges) == 1 + assert nodes_list[0].edges[0].length == 15 + + assert solver.solve() == 154
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/tests/test_day23.html b/_modules/day23/tests/test_day23.html new file mode 100644 index 0000000..cb47961 --- /dev/null +++ b/_modules/day23/tests/test_day23.html @@ -0,0 +1,148 @@ + + + + + + day23.tests.test_day23 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.tests.test_day23

+"""Test day23 main functions."""
+from typing import TYPE_CHECKING
+
+from day23.day23 import INPUT_SMALL, part1, part2
+from day23.lib.classes import Solver1
+from day23.lib.parsers import get_maze
+
+if TYPE_CHECKING:
+    from day23.lib.classes import Maze, Path
+
+
+
+[docs] +def test_solver() -> None: + """Test ``Solver1``.""" + maze: Maze = get_maze(INPUT_SMALL) + + solver = Solver1(maze) + paths: list[Path] = solver.solve() + + path_lengths = [len(path) for path in paths] + path_lengths.sort() + + assert path_lengths == [74, 82, 82, 86, 90, 94]
+ + + +
+[docs] +def test_part1() -> None: + """Test ``part1()``.""" + maze: Maze = get_maze(INPUT_SMALL) + assert part1(maze) == 94
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + maze: Maze = get_maze(INPUT_SMALL) + assert part2(maze) == 154
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day23/tests/test_parsers.html b/_modules/day23/tests/test_parsers.html new file mode 100644 index 0000000..601b16f --- /dev/null +++ b/_modules/day23/tests/test_parsers.html @@ -0,0 +1,123 @@ + + + + + + day23.tests.test_parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day23.tests.test_parsers

+"""Test parsers."""
+from typing import TYPE_CHECKING
+
+from day23.day23 import INPUT_SMALL
+from day23.lib.parsers import get_maze
+
+if TYPE_CHECKING:
+    from day23.lib.classes import Maze
+
+
+
+[docs] +def test_get_maze() -> None: + """Test ``get_maze()``.""" + maze: Maze = get_maze(INPUT_SMALL) + assert maze.num_rows == 23 + assert maze.num_cols == 23
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day24/day24.html b/_modules/day24/day24.html new file mode 100644 index 0000000..d380e89 --- /dev/null +++ b/_modules/day24/day24.html @@ -0,0 +1,231 @@ + + + + + + day24.day24 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day24.day24

+"""day24 solution."""
+from typing import Optional
+
+import z3
+
+from day24.lib.classes import Hailstone, Vector2
+from day24.lib.parsers import parse_input
+
+INPUT = ("day24/input.txt", Vector2(200000000000000, 400000000000000))
+INPUT_SMALL = ("day24/input-small.txt", Vector2(7, 27))
+
+
+
+[docs] +def get_intersection_2d(left: Hailstone, right: Hailstone) -> Optional[Vector2]: + """Returns intersection of two hailstones.""" + pos1 = left.position.xy + dir1 = left.velocity.xy + pos2 = right.position.xy + dir2 = right.velocity.xy + + determinant = (dir1.x * dir2.y) - (dir1.y * dir2.x) + + if determinant == 0: + return None # parallel + + t1 = ((pos2.x - pos1.x) * dir2.y - (pos2.y - pos1.y) * dir2.x) / determinant + t2 = ((pos2.x - pos1.x) * dir1.y - (pos2.y - pos1.y) * dir1.x) / determinant + + if t1 < 0: # in the past :( + return None + if t2 < 0: # in the past :( + return None + intersection_point = Vector2(pos1.x + t1 * dir1.x, pos1.y + t1 * dir1.y) + return intersection_point
+ + + +
+[docs] +def within_2d(point: Vector2, min_max: Vector2) -> bool: + """Returns whether a point is inside a given rectangle. + + x and y are both symmetric and defined by min_max. + """ + return min_max.x <= point.x <= min_max.y and min_max.x <= point.y <= min_max.y
+ + + +
+[docs] +def part1(hailstones: list[Hailstone], valid_range: Vector2) -> int: + """Solve part1: list of hailstones that are within a given rectangle.""" + result = 0 + print(len(hailstones)) + left: Hailstone + right: Hailstone + for index, left in enumerate(hailstones[:-1]): + for right in hailstones[index + 1 :]: + intersection: Optional[Vector2] = get_intersection_2d(left, right) + + if intersection is None: + continue + + if within_2d(intersection, valid_range): + result += 1 + + return result
+ + + +
+[docs] +def part2(hailstones: list[Hailstone]) -> int: + """Solve part2: a magic hailstone that passes through all other hailstones.""" + x, y, z = z3.Reals("x y z") + vx, vy, vz = z3.Reals("vx vy vz") + + solver = z3.Solver() + + for index, hail in enumerate(hailstones[:3]): + pos = hail.position + vel = hail.velocity + + t = z3.Real(f"t{index}") + solver.add(t >= 0) + solver.add(x + vx * t == pos.x + vel.x * t) + solver.add(y + vy * t == pos.y + vel.y * t) + solver.add(z + vz * t == pos.z + vel.z * t) + + print(solver.check()) + + model = solver.model() + + rx, ry, rz = ( + model.eval(x).as_long(), + model.eval(y).as_long(), + model.eval(z).as_long(), + ) + + if not isinstance(rx, int): + raise AssertionError("rx is non-int!") + if not isinstance(ry, int): + raise AssertionError("ry is non-int!") + if not isinstance(rz, int): + raise AssertionError("rz is non-int!") + return rx + ry + rz
+ + + +
+[docs] +def main() -> None: + """Loads input then solves.""" + input_data, valid_range = INPUT + + hailstones: list[Hailstone] = parse_input(input_data) + print(len(hailstones)) + print(part1(hailstones, valid_range)) + + print(part2(hailstones))
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day24/lib/classes.html b/_modules/day24/lib/classes.html new file mode 100644 index 0000000..281f31a --- /dev/null +++ b/_modules/day24/lib/classes.html @@ -0,0 +1,146 @@ + + + + + + day24.lib.classes — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day24.lib.classes

+"""Day24 classes."""
+from dataclasses import dataclass
+
+
+
+[docs] +@dataclass(frozen=True, slots=True) +class Vector3: + """Simple 3d vector.""" + + x: float + y: float + z: float + + @property + def xy(self) -> "Vector2": + """Convert to vector2.""" + return Vector2(self.x, self.y)
+ + + +
+[docs] +@dataclass(frozen=True, slots=True) +class Vector2: + """Simple vector2.""" + + x: float + y: float
+ + + +
+[docs] +@dataclass(frozen=True) +class Hailstone: + """Hailstone has a 3d vector for pos/velocity.""" + + position: Vector3 + velocity: Vector3
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day24/lib/parsers.html b/_modules/day24/lib/parsers.html new file mode 100644 index 0000000..5591196 --- /dev/null +++ b/_modules/day24/lib/parsers.html @@ -0,0 +1,144 @@ + + + + + + day24.lib.parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day24.lib.parsers

+"""Day23 parsers."""
+from day24.lib.classes import Hailstone, Vector3
+
+
+
+[docs] +def parse_vector3(line: str) -> Vector3: + """Parse a vector3. + + E.g. 1,2,3 + """ + line = line.strip() + x, y, z = line.split(",") + return Vector3(int(x), int(y), int(z))
+ + + +
+[docs] +def parse_input(filename: str) -> list[Hailstone]: + r"""Parse input lines. + + Lines in the format ``1,2,3@4,5,6\n``. + + Args: + filename (str): file to open + + Returns: + list[Hailstone]: list of Hailstones + """ + result: list[Hailstone] = [] + with open(filename, "r", encoding="utf8") as file: + for line in file: + line = line.strip() + left, right = line.split("@") + pos, velocity = parse_vector3(left), parse_vector3(right) + result.append(Hailstone(pos, velocity)) + return result
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day24/tests/test_day24.html b/_modules/day24/tests/test_day24.html new file mode 100644 index 0000000..b6e1cb2 --- /dev/null +++ b/_modules/day24/tests/test_day24.html @@ -0,0 +1,158 @@ + + + + + + day24.tests.test_day24 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day24.tests.test_day24

+"""Test main functions in day24."""
+from day24.day24 import INPUT_SMALL, get_intersection_2d, part1, part2, within_2d
+from day24.lib.classes import Hailstone, Vector2, Vector3
+from day24.lib.parsers import parse_input
+
+
+
+[docs] +def test_part1() -> None: + """Test ``part1()``.""" + file_path, valid_range = INPUT_SMALL + hailstones: list[Hailstone] = parse_input(file_path) + assert part1(hailstones, valid_range) == 2
+ + + +
+[docs] +def test_part2() -> None: + """Test ``part2()``.""" + file_path, valid_range = INPUT_SMALL + hailstones: list[Hailstone] = parse_input(file_path) + assert part2(hailstones) == 47
+ + + +
+[docs] +def test_get_intersection_2d() -> None: + """Test ``get_intersection_2d()``.""" + hailstone_a = Hailstone(Vector3(20, 25, 34), Vector3(-2, -2, -4)) + hailstone_b = Hailstone(Vector3(12, 31, 28), Vector3(-1, -2, -1)) + + assert get_intersection_2d(hailstone_a, hailstone_b) == Vector2(-2, 3)
+ + + +
+[docs] +def test_within_2d() -> None: + """Test ``within_2d()``.""" + valid_range = Vector2(7, 27) + v1 = Vector2(14 + 1 / 3, 15 + 1 / 3) + v2 = Vector2(11 + 6 / 9, 16 + 6 / 9) + v3 = Vector2(6.2, 19.4) + v4 = Vector2(-6, -5) + v5 = Vector2(-2, 3) + assert within_2d(v1, valid_range) + assert within_2d(v2, valid_range) + assert not within_2d(v3, valid_range) + assert not within_2d(v4, valid_range) + assert not within_2d(v5, valid_range)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day24/tests/test_parsers.html b/_modules/day24/tests/test_parsers.html new file mode 100644 index 0000000..8a09f82 --- /dev/null +++ b/_modules/day24/tests/test_parsers.html @@ -0,0 +1,141 @@ + + + + + + day24.tests.test_parsers — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day24.tests.test_parsers

+"""Test parsing functions."""
+from day24.day24 import INPUT_SMALL
+from day24.lib.classes import Hailstone, Vector3
+from day24.lib.parsers import parse_input, parse_vector3
+
+
+
+[docs] +def test_vector3() -> None: + """Test ``parse_vector3()``.""" + v1: Vector3 = parse_vector3(" 2, 4, 8") + assert v1.x == 2 + assert v1.y == 4 + assert v1.z == 8 + + v2: Vector3 = parse_vector3("3,5,-107") + assert v2.x == 3 + assert v2.y == 5 + assert v2.z == -107
+ + + +
+[docs] +def test_parser() -> None: + """Test ``parse_input()``.""" + file_path, _ = INPUT_SMALL + hailstones: list[Hailstone] = parse_input(file_path) + + assert len(hailstones) == 5 + assert hailstones[0].position == Vector3(19, 13, 30) + assert hailstones[0].velocity == Vector3(-2, 1, -2) + + assert hailstones[-1].position == Vector3(20, 19, 15) + assert hailstones[-1].velocity == Vector3(1, -5, -3)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day25/day25.html b/_modules/day25/day25.html new file mode 100644 index 0000000..23e5d44 --- /dev/null +++ b/_modules/day25/day25.html @@ -0,0 +1,204 @@ + + + + + + day25.day25 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day25.day25

+"""day25 solution."""
+from dataclasses import dataclass
+
+import matplotlib.pyplot as plt
+import networkx as nx
+
+INPUT_SMALL = "day25/input-small.txt"
+INPUT = "day25/input.txt"
+SHOW_GRAPH = False
+
+
+
+[docs] +@dataclass +class Connection: + """Connection between two nodes.""" + + src: str + dests: list[str] + +
+[docs] + def node_names(self) -> list[str]: + """Return all nodes in a connection.""" + return [self.src] + self.dests
+
+ + + +
+[docs] +def parse_connection(line: str) -> Connection: + """Parse connection into well defined class. + + E.g. ``src: dest1 dest2 dest3``. + """ + src, dests = line.split(":") + return Connection(src, dests.split())
+ + + +
+[docs] +def get_data(path: str) -> list[Connection]: + """Loads data and parses it into list of connections.""" + connections: list[Connection] = [] + with open(path, "r", encoding="utf8") as file: + for line in file: + connections.append(parse_connection(line)) + + return connections
+ + + +
+[docs] +def show_graph(graph: nx.Graph) -> None: # pragma: no cover + """Draws a graph that you can see.""" + nx.draw(graph, with_labels=True) + plt.draw() + plt.show()
+ + + +
+[docs] +def solve_nodes(connections: list[Connection]) -> int: + """Graphs the modules.""" + G = nx.Graph() + + nodes: set[str] = set() + for connection in connections: + to_add = connection.node_names() + for node_name in to_add: + if node_name not in nodes: + nodes.add(node_name) + G.add_node(node_name) + if node_name != connection.src: + G.add_edge(connection.src, node_name) + if SHOW_GRAPH: # pragma: no cover + show_graph(G) + cut_value, partition = nx.stoer_wagner(G) + print(f"num_cuts: {cut_value}") + return len(partition[0]) * len(partition[1])
+ + + +
+[docs] +def main() -> None: + """Load data and solve.""" + conns = get_data(INPUT) + result = solve_nodes(conns) + print(result)
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/day25/tests/test_day25.html b/_modules/day25/tests/test_day25.html new file mode 100644 index 0000000..d866a71 --- /dev/null +++ b/_modules/day25/tests/test_day25.html @@ -0,0 +1,136 @@ + + + + + + day25.tests.test_day25 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for day25.tests.test_day25

+"""Test day25 main functions."""
+from day25.day25 import INPUT_SMALL, Connection, get_data, parse_connection, solve_nodes
+
+
+
+[docs] +def test_get_data() -> None: + """Test ``get_data()``.""" + conns: list[Connection] = get_data(INPUT_SMALL) + assert len(conns) == 13 + assert conns[0].src == "jqt"
+ + + +
+[docs] +def test_parse_connection() -> None: + """Test ``parse_connection()``.""" + conn: Connection = parse_connection("zmx: vfl mgb tmr bsn") + assert conn.src == "zmx" + assert set(conn.dests) == {"vfl", "mgb", "tmr", "bsn"}
+ + + +
+[docs] +def test_day25() -> None: + """Test ``solve_nodes()``.""" + conns: list[Connection] = get_data(INPUT_SMALL) + assert solve_nodes(conns) == 54
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/download_inputs.html b/_modules/download_inputs.html new file mode 100644 index 0000000..4a878f7 --- /dev/null +++ b/_modules/download_inputs.html @@ -0,0 +1,169 @@ + + + + + + download_inputs — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for download_inputs

+"""Downloads the input files from adventofcode.com."""
+import os
+import time
+
+import dotenv
+import requests
+
+BASE_URL = "https://adventofcode.com/2023/day/{}/input"
+
+
+
+[docs] +def main() -> None: + """Main function, checks which folders you have, and then downloads files.""" + dotenv.load_dotenv() + session = os.environ.get("SESSION", None) + if session is None: + print("Enter session (https://adventofcode.com/2023/day/1/input)") + print("then F12, networking, refresh, cookies:") + session = input() + if session.startswith("session="): + session = session[len("session=") :] + print("using session:", session) + + # grab last day + subfolders: list[str] = [ + f.name for f in os.scandir(".") if f.is_dir() and f.name.startswith("day") + ] + + last_day_name: str = max(subfolders) + last_day: int = int(last_day_name[3:]) + + for day in range(1, last_day + 1): + download_file(day, session)
+ + + +
+[docs] +def download_file(day: int, session: str) -> None: + """Downloads a given day, given your session key.""" + output_path = os.path.join(f"day{day:02d}", "input.txt") + if os.path.exists(output_path): + print("No download; file already exists") + return + + # Create the subdirectory if it doesn't exist + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + # Download the file + url = BASE_URL.format(day) + response = requests.get(url, cookies={"session": session}) + time.sleep(1) # don't spam the guys server, ok + if response.status_code == 200: + with open(output_path, "wb") as file: + file.write(response.content) + print(f"Downloaded {url} to {output_path}") + else: + print(f"Failed to download {url} (Status code: {response.status_code})")
+ + + +if __name__ == "__main__": + main() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 0000000..b3d0ec9 --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,211 @@ + + + + + + Overview: module code — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +

All modules for which code is available

+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/maker.html b/_modules/maker.html new file mode 100644 index 0000000..36fe711 --- /dev/null +++ b/_modules/maker.html @@ -0,0 +1,126 @@ + + + + + + maker — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for maker

+"""maker of accessory files."""
+import os
+from pathlib import Path
+from typing import Iterable
+
+
+
+[docs] +def touch_days(days: Iterable[int]) -> None: + """Touches each day, creating /lib and /tests and relevant init files.""" + for day in days: + os.makedirs(f"day{day:02}/lib", exist_ok=True) + os.makedirs(f"day{day:02}/tests", exist_ok=True) + Path(f"day{day:02}/tests/__init__.py").touch() + Path(f"day{day:02}/__init__.py").touch() + Path(f"day{day:02}/lib/__init__.py").touch()
+ + + +if __name__ == "__main__": + touch_days(range(1, 26)) +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_sources/autodoc.md.txt b/_sources/autodoc.md.txt new file mode 100644 index 0000000..96794bd --- /dev/null +++ b/_sources/autodoc.md.txt @@ -0,0 +1,21 @@ +# Automatic documentation + + +The documentation in this repo was setup using this guide: +https://redandgreen.co.uk/sphinx-to-github-pages-via-github-actions/ + + +## Key commands + +* Setup from scratch: +`cd /docs && sphinx-quickstart` + +* Generate .rst's automatically (from root directory) +`sphinx-apidoc -o docs .` + +* Manually generate html (already done in Github Action): +`cd docs && make html` + +* Clean html + +`cd docs && make clean` \ No newline at end of file diff --git a/_sources/ci.md.txt b/_sources/ci.md.txt new file mode 100644 index 0000000..64f0267 --- /dev/null +++ b/_sources/ci.md.txt @@ -0,0 +1,40 @@ +# Continuous Integration + +CI allows lots of processes to occur automatically + +Think of `pre-commit` as "local" and `github actions` as cloud. + +1. `pre-commit` is a tool that runs arbitrary scripts at `pre-commit` and `pre-push` time. You can reuse it's config for github-action based scripts, to ensure all ci scripts are run. + +2. `github actions` (see `.github/workflows`) are workflows that run "on the cloud" on github.com. They run based on a few conditions such as pushing/merging, PRs etc. They mainly just setup an linux box to install `pre-commit` and then duplicate what was being done on a local environment. They also publish our html documentation (such as this page) into the `gh-pages` branch. + + +## Tools + + +### Pre-commit + +`pre-commit` is both the name of a tool, and of a stage in committing. + +This section details what is done at the stages +1. `pre-commit`: runs `ruff-linting` and `mypy` and `mixed-crlf`. These are all very fast and ensure commits have barebones checks before going in. + +2. `pre-push`: runs `pytest unit tests` as well as `coverage` + +### ruff +A simple tool that does linting + +### mypy +Type-checking tool + +### mixed-crlf +Ensures that we don't commit any `crlf` files. Converts all to `lf` + +### pytest +Unit testing framework + +### coverage +`Coverage` is a simple tool that tells you what % of your code is covered by tests. This can be easily gamed, for example a test that is `assert main() == 0` will result in very high coverage but not necessarily good tests. + +### sphinx +Generates `.rst` files and `html` files automatically. You can inject markdown manually. Check the [autodoc](autodoc.md) page for more information. \ No newline at end of file diff --git a/_sources/day01.rst.txt b/_sources/day01.rst.txt new file mode 100644 index 0000000..60cdd58 --- /dev/null +++ b/_sources/day01.rst.txt @@ -0,0 +1,37 @@ +day01 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day01.tests + +Submodules +---------- + +day01.day1a module +------------------ + +.. automodule:: day01.day1a + :members: + :undoc-members: + :show-inheritance: + +day01.day1b module +------------------ + +.. automodule:: day01.day1b + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day01 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day01.tests.rst.txt b/_sources/day01.tests.rst.txt new file mode 100644 index 0000000..e47a62d --- /dev/null +++ b/_sources/day01.tests.rst.txt @@ -0,0 +1,29 @@ +day01.tests package +=================== + +Submodules +---------- + +day01.tests.test\_day1a module +------------------------------ + +.. automodule:: day01.tests.test_day1a + :members: + :undoc-members: + :show-inheritance: + +day01.tests.test\_day1b module +------------------------------ + +.. automodule:: day01.tests.test_day1b + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day01.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day02.rst.txt b/_sources/day02.rst.txt new file mode 100644 index 0000000..64b4389 --- /dev/null +++ b/_sources/day02.rst.txt @@ -0,0 +1,29 @@ +day02 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day02.tests + +Submodules +---------- + +day02.day2 module +----------------- + +.. automodule:: day02.day2 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day02 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day02.tests.rst.txt b/_sources/day02.tests.rst.txt new file mode 100644 index 0000000..e2d8929 --- /dev/null +++ b/_sources/day02.tests.rst.txt @@ -0,0 +1,21 @@ +day02.tests package +=================== + +Submodules +---------- + +day02.tests.test\_day2 module +----------------------------- + +.. automodule:: day02.tests.test_day2 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day02.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day03.lib.rst.txt b/_sources/day03.lib.rst.txt new file mode 100644 index 0000000..fd74687 --- /dev/null +++ b/_sources/day03.lib.rst.txt @@ -0,0 +1,29 @@ +day03.lib package +================= + +Submodules +---------- + +day03.lib.classes module +------------------------ + +.. automodule:: day03.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day03.lib.parsers module +------------------------ + +.. automodule:: day03.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day03.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day03.rst.txt b/_sources/day03.rst.txt new file mode 100644 index 0000000..04f51c9 --- /dev/null +++ b/_sources/day03.rst.txt @@ -0,0 +1,30 @@ +day03 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day03.lib + day03.tests + +Submodules +---------- + +day03.day3 module +----------------- + +.. automodule:: day03.day3 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day03 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day03.tests.rst.txt b/_sources/day03.tests.rst.txt new file mode 100644 index 0000000..76a7f2d --- /dev/null +++ b/_sources/day03.tests.rst.txt @@ -0,0 +1,29 @@ +day03.tests package +=================== + +Submodules +---------- + +day03.tests.test\_classes module +-------------------------------- + +.. automodule:: day03.tests.test_classes + :members: + :undoc-members: + :show-inheritance: + +day03.tests.test\_day3 module +----------------------------- + +.. automodule:: day03.tests.test_day3 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day03.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day04.rst.txt b/_sources/day04.rst.txt new file mode 100644 index 0000000..67548a1 --- /dev/null +++ b/_sources/day04.rst.txt @@ -0,0 +1,29 @@ +day04 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day04.tests + +Submodules +---------- + +day04.day4 module +----------------- + +.. automodule:: day04.day4 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day04 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day04.tests.rst.txt b/_sources/day04.tests.rst.txt new file mode 100644 index 0000000..cc516bd --- /dev/null +++ b/_sources/day04.tests.rst.txt @@ -0,0 +1,21 @@ +day04.tests package +=================== + +Submodules +---------- + +day04.tests.test\_day4 module +----------------------------- + +.. automodule:: day04.tests.test_day4 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day04.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day05.lib.rst.txt b/_sources/day05.lib.rst.txt new file mode 100644 index 0000000..3ffd31b --- /dev/null +++ b/_sources/day05.lib.rst.txt @@ -0,0 +1,29 @@ +day05.lib package +================= + +Submodules +---------- + +day05.lib.classes module +------------------------ + +.. automodule:: day05.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day05.lib.parsers module +------------------------ + +.. automodule:: day05.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day05.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day05.rst.txt b/_sources/day05.rst.txt new file mode 100644 index 0000000..1d4b062 --- /dev/null +++ b/_sources/day05.rst.txt @@ -0,0 +1,30 @@ +day05 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day05.lib + day05.tests + +Submodules +---------- + +day05.day5 module +----------------- + +.. automodule:: day05.day5 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day05 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day05.tests.rst.txt b/_sources/day05.tests.rst.txt new file mode 100644 index 0000000..4bc87cc --- /dev/null +++ b/_sources/day05.tests.rst.txt @@ -0,0 +1,21 @@ +day05.tests package +=================== + +Submodules +---------- + +day05.tests.test\_day5 module +----------------------------- + +.. automodule:: day05.tests.test_day5 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day05.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day06.rst.txt b/_sources/day06.rst.txt new file mode 100644 index 0000000..ef136cb --- /dev/null +++ b/_sources/day06.rst.txt @@ -0,0 +1,29 @@ +day06 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day06.tests + +Submodules +---------- + +day06.day6 module +----------------- + +.. automodule:: day06.day6 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day06 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day06.tests.rst.txt b/_sources/day06.tests.rst.txt new file mode 100644 index 0000000..3816dcb --- /dev/null +++ b/_sources/day06.tests.rst.txt @@ -0,0 +1,21 @@ +day06.tests package +=================== + +Submodules +---------- + +day06.tests.test\_day6 module +----------------------------- + +.. automodule:: day06.tests.test_day6 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day06.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day07.rst.txt b/_sources/day07.rst.txt new file mode 100644 index 0000000..d19e814 --- /dev/null +++ b/_sources/day07.rst.txt @@ -0,0 +1,29 @@ +day07 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day07.tests + +Submodules +---------- + +day07.day7 module +----------------- + +.. automodule:: day07.day7 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day07 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day07.tests.rst.txt b/_sources/day07.tests.rst.txt new file mode 100644 index 0000000..182d9c5 --- /dev/null +++ b/_sources/day07.tests.rst.txt @@ -0,0 +1,21 @@ +day07.tests package +=================== + +Submodules +---------- + +day07.tests.test\_day7 module +----------------------------- + +.. automodule:: day07.tests.test_day7 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day07.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day08.rst.txt b/_sources/day08.rst.txt new file mode 100644 index 0000000..0652dda --- /dev/null +++ b/_sources/day08.rst.txt @@ -0,0 +1,29 @@ +day08 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day08.tests + +Submodules +---------- + +day08.day8 module +----------------- + +.. automodule:: day08.day8 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day08 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day08.tests.rst.txt b/_sources/day08.tests.rst.txt new file mode 100644 index 0000000..b8e9097 --- /dev/null +++ b/_sources/day08.tests.rst.txt @@ -0,0 +1,21 @@ +day08.tests package +=================== + +Submodules +---------- + +day08.tests.test\_day8 module +----------------------------- + +.. automodule:: day08.tests.test_day8 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day08.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day09.rst.txt b/_sources/day09.rst.txt new file mode 100644 index 0000000..4b84e06 --- /dev/null +++ b/_sources/day09.rst.txt @@ -0,0 +1,29 @@ +day09 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day09.tests + +Submodules +---------- + +day09.day9 module +----------------- + +.. automodule:: day09.day9 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day09 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day09.tests.rst.txt b/_sources/day09.tests.rst.txt new file mode 100644 index 0000000..5b18657 --- /dev/null +++ b/_sources/day09.tests.rst.txt @@ -0,0 +1,21 @@ +day09.tests package +=================== + +Submodules +---------- + +day09.tests.test\_day9 module +----------------------------- + +.. automodule:: day09.tests.test_day9 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day09.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day10.lib.rst.txt b/_sources/day10.lib.rst.txt new file mode 100644 index 0000000..acbb2cd --- /dev/null +++ b/_sources/day10.lib.rst.txt @@ -0,0 +1,45 @@ +day10.lib package +================= + +Submodules +---------- + +day10.lib.direction module +-------------------------- + +.. automodule:: day10.lib.direction + :members: + :undoc-members: + :show-inheritance: + +day10.lib.pipebounds module +--------------------------- + +.. automodule:: day10.lib.pipebounds + :members: + :undoc-members: + :show-inheritance: + +day10.lib.pipes module +---------------------- + +.. automodule:: day10.lib.pipes + :members: + :undoc-members: + :show-inheritance: + +day10.lib.position module +------------------------- + +.. automodule:: day10.lib.position + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day10.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day10.rst.txt b/_sources/day10.rst.txt new file mode 100644 index 0000000..ec95c84 --- /dev/null +++ b/_sources/day10.rst.txt @@ -0,0 +1,30 @@ +day10 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day10.lib + day10.tests + +Submodules +---------- + +day10.day10 module +------------------ + +.. automodule:: day10.day10 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day10 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day10.tests.rst.txt b/_sources/day10.tests.rst.txt new file mode 100644 index 0000000..e8fa27a --- /dev/null +++ b/_sources/day10.tests.rst.txt @@ -0,0 +1,29 @@ +day10.tests package +=================== + +Submodules +---------- + +day10.tests.test\_day10 module +------------------------------ + +.. automodule:: day10.tests.test_day10 + :members: + :undoc-members: + :show-inheritance: + +day10.tests.test\_direction module +---------------------------------- + +.. automodule:: day10.tests.test_direction + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day10.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day11.rst.txt b/_sources/day11.rst.txt new file mode 100644 index 0000000..35e4540 --- /dev/null +++ b/_sources/day11.rst.txt @@ -0,0 +1,29 @@ +day11 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day11.tests + +Submodules +---------- + +day11.day11 module +------------------ + +.. automodule:: day11.day11 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day11 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day11.tests.rst.txt b/_sources/day11.tests.rst.txt new file mode 100644 index 0000000..e23295d --- /dev/null +++ b/_sources/day11.tests.rst.txt @@ -0,0 +1,21 @@ +day11.tests package +=================== + +Submodules +---------- + +day11.tests.test\_day11 module +------------------------------ + +.. automodule:: day11.tests.test_day11 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day11.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day12.rst.txt b/_sources/day12.rst.txt new file mode 100644 index 0000000..6903c85 --- /dev/null +++ b/_sources/day12.rst.txt @@ -0,0 +1,29 @@ +day12 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day12.tests + +Submodules +---------- + +day12.day12 module +------------------ + +.. automodule:: day12.day12 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day12 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day12.tests.rst.txt b/_sources/day12.tests.rst.txt new file mode 100644 index 0000000..864d31e --- /dev/null +++ b/_sources/day12.tests.rst.txt @@ -0,0 +1,21 @@ +day12.tests package +=================== + +Submodules +---------- + +day12.tests.test\_day12 module +------------------------------ + +.. automodule:: day12.tests.test_day12 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day12.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day13.rst.txt b/_sources/day13.rst.txt new file mode 100644 index 0000000..bc35349 --- /dev/null +++ b/_sources/day13.rst.txt @@ -0,0 +1,29 @@ +day13 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day13.tests + +Submodules +---------- + +day13.day13 module +------------------ + +.. automodule:: day13.day13 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day13 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day13.tests.rst.txt b/_sources/day13.tests.rst.txt new file mode 100644 index 0000000..fc709fa --- /dev/null +++ b/_sources/day13.tests.rst.txt @@ -0,0 +1,21 @@ +day13.tests package +=================== + +Submodules +---------- + +day13.tests.test\_day13 module +------------------------------ + +.. automodule:: day13.tests.test_day13 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day13.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day14.lib.rst.txt b/_sources/day14.lib.rst.txt new file mode 100644 index 0000000..0198d6d --- /dev/null +++ b/_sources/day14.lib.rst.txt @@ -0,0 +1,21 @@ +day14.lib package +================= + +Submodules +---------- + +day14.lib.direction module +-------------------------- + +.. automodule:: day14.lib.direction + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day14.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day14.rst.txt b/_sources/day14.rst.txt new file mode 100644 index 0000000..a918db1 --- /dev/null +++ b/_sources/day14.rst.txt @@ -0,0 +1,30 @@ +day14 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day14.lib + day14.tests + +Submodules +---------- + +day14.day14 module +------------------ + +.. automodule:: day14.day14 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day14 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day14.tests.rst.txt b/_sources/day14.tests.rst.txt new file mode 100644 index 0000000..1a045fe --- /dev/null +++ b/_sources/day14.tests.rst.txt @@ -0,0 +1,29 @@ +day14.tests package +=================== + +Submodules +---------- + +day14.tests.test\_day14 module +------------------------------ + +.. automodule:: day14.tests.test_day14 + :members: + :undoc-members: + :show-inheritance: + +day14.tests.test\_direction module +---------------------------------- + +.. automodule:: day14.tests.test_direction + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day14.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day15.lib.rst.txt b/_sources/day15.lib.rst.txt new file mode 100644 index 0000000..4f55625 --- /dev/null +++ b/_sources/day15.lib.rst.txt @@ -0,0 +1,21 @@ +day15.lib package +================= + +Submodules +---------- + +day15.lib.classes module +------------------------ + +.. automodule:: day15.lib.classes + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day15.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day15.rst.txt b/_sources/day15.rst.txt new file mode 100644 index 0000000..b812fdf --- /dev/null +++ b/_sources/day15.rst.txt @@ -0,0 +1,30 @@ +day15 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day15.lib + day15.tests + +Submodules +---------- + +day15.day15 module +------------------ + +.. automodule:: day15.day15 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day15 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day15.tests.rst.txt b/_sources/day15.tests.rst.txt new file mode 100644 index 0000000..43676df --- /dev/null +++ b/_sources/day15.tests.rst.txt @@ -0,0 +1,29 @@ +day15.tests package +=================== + +Submodules +---------- + +day15.tests.test\_classes module +-------------------------------- + +.. automodule:: day15.tests.test_classes + :members: + :undoc-members: + :show-inheritance: + +day15.tests.test\_day15 module +------------------------------ + +.. automodule:: day15.tests.test_day15 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day15.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day16.lib.rst.txt b/_sources/day16.lib.rst.txt new file mode 100644 index 0000000..089764a --- /dev/null +++ b/_sources/day16.lib.rst.txt @@ -0,0 +1,53 @@ +day16.lib package +================= + +Submodules +---------- + +day16.lib.cells module +---------------------- + +.. automodule:: day16.lib.cells + :members: + :undoc-members: + :show-inheritance: + +day16.lib.direction module +-------------------------- + +.. automodule:: day16.lib.direction + :members: + :undoc-members: + :show-inheritance: + +day16.lib.laser module +---------------------- + +.. automodule:: day16.lib.laser + :members: + :undoc-members: + :show-inheritance: + +day16.lib.parsers module +------------------------ + +.. automodule:: day16.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +day16.lib.world module +---------------------- + +.. automodule:: day16.lib.world + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day16.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day16.rst.txt b/_sources/day16.rst.txt new file mode 100644 index 0000000..f22b605 --- /dev/null +++ b/_sources/day16.rst.txt @@ -0,0 +1,30 @@ +day16 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day16.lib + day16.tests + +Submodules +---------- + +day16.day16 module +------------------ + +.. automodule:: day16.day16 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day16 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day16.tests.rst.txt b/_sources/day16.tests.rst.txt new file mode 100644 index 0000000..d014aee --- /dev/null +++ b/_sources/day16.tests.rst.txt @@ -0,0 +1,45 @@ +day16.tests package +=================== + +Submodules +---------- + +day16.tests.test\_cells module +------------------------------ + +.. automodule:: day16.tests.test_cells + :members: + :undoc-members: + :show-inheritance: + +day16.tests.test\_day16 module +------------------------------ + +.. automodule:: day16.tests.test_day16 + :members: + :undoc-members: + :show-inheritance: + +day16.tests.test\_direction module +---------------------------------- + +.. automodule:: day16.tests.test_direction + :members: + :undoc-members: + :show-inheritance: + +day16.tests.test\_world module +------------------------------ + +.. automodule:: day16.tests.test_world + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day16.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day17.lib.rst.txt b/_sources/day17.lib.rst.txt new file mode 100644 index 0000000..f490413 --- /dev/null +++ b/_sources/day17.lib.rst.txt @@ -0,0 +1,37 @@ +day17.lib package +================= + +Submodules +---------- + +day17.lib.classes module +------------------------ + +.. automodule:: day17.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day17.lib.direction module +-------------------------- + +.. automodule:: day17.lib.direction + :members: + :undoc-members: + :show-inheritance: + +day17.lib.parsers module +------------------------ + +.. automodule:: day17.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day17.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day17.rst.txt b/_sources/day17.rst.txt new file mode 100644 index 0000000..eacb0fc --- /dev/null +++ b/_sources/day17.rst.txt @@ -0,0 +1,30 @@ +day17 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day17.lib + day17.tests + +Submodules +---------- + +day17.day17 module +------------------ + +.. automodule:: day17.day17 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day17 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day17.tests.rst.txt b/_sources/day17.tests.rst.txt new file mode 100644 index 0000000..acddde1 --- /dev/null +++ b/_sources/day17.tests.rst.txt @@ -0,0 +1,37 @@ +day17.tests package +=================== + +Submodules +---------- + +day17.tests.test\_day17 module +------------------------------ + +.. automodule:: day17.tests.test_day17 + :members: + :undoc-members: + :show-inheritance: + +day17.tests.test\_direction module +---------------------------------- + +.. automodule:: day17.tests.test_direction + :members: + :undoc-members: + :show-inheritance: + +day17.tests.test\_parsers module +-------------------------------- + +.. automodule:: day17.tests.test_parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day17.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day18.lib.rst.txt b/_sources/day18.lib.rst.txt new file mode 100644 index 0000000..d53dba8 --- /dev/null +++ b/_sources/day18.lib.rst.txt @@ -0,0 +1,21 @@ +day18.lib package +================= + +Submodules +---------- + +day18.lib.tile module +--------------------- + +.. automodule:: day18.lib.tile + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day18.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day18.rst.txt b/_sources/day18.rst.txt new file mode 100644 index 0000000..419bf6c --- /dev/null +++ b/_sources/day18.rst.txt @@ -0,0 +1,38 @@ +day18 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day18.lib + day18.tests + +Submodules +---------- + +day18.day18a module +------------------- + +.. automodule:: day18.day18a + :members: + :undoc-members: + :show-inheritance: + +day18.day18b module +------------------- + +.. automodule:: day18.day18b + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day18 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day18.tests.rst.txt b/_sources/day18.tests.rst.txt new file mode 100644 index 0000000..feef201 --- /dev/null +++ b/_sources/day18.tests.rst.txt @@ -0,0 +1,29 @@ +day18.tests package +=================== + +Submodules +---------- + +day18.tests.test\_day18a module +------------------------------- + +.. automodule:: day18.tests.test_day18a + :members: + :undoc-members: + :show-inheritance: + +day18.tests.test\_day18b module +------------------------------- + +.. automodule:: day18.tests.test_day18b + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day18.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day19.lib.rst.txt b/_sources/day19.lib.rst.txt new file mode 100644 index 0000000..c485ba9 --- /dev/null +++ b/_sources/day19.lib.rst.txt @@ -0,0 +1,29 @@ +day19.lib package +================= + +Submodules +---------- + +day19.lib.classes module +------------------------ + +.. automodule:: day19.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day19.lib.parsers module +------------------------ + +.. automodule:: day19.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day19.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day19.rst.txt b/_sources/day19.rst.txt new file mode 100644 index 0000000..4d90390 --- /dev/null +++ b/_sources/day19.rst.txt @@ -0,0 +1,30 @@ +day19 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day19.lib + day19.tests + +Submodules +---------- + +day19.day19 module +------------------ + +.. automodule:: day19.day19 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day19 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day19.tests.rst.txt b/_sources/day19.tests.rst.txt new file mode 100644 index 0000000..1171ab7 --- /dev/null +++ b/_sources/day19.tests.rst.txt @@ -0,0 +1,37 @@ +day19.tests package +=================== + +Submodules +---------- + +day19.tests.test\_classes module +-------------------------------- + +.. automodule:: day19.tests.test_classes + :members: + :undoc-members: + :show-inheritance: + +day19.tests.test\_day19 module +------------------------------ + +.. automodule:: day19.tests.test_day19 + :members: + :undoc-members: + :show-inheritance: + +day19.tests.test\_parsers module +-------------------------------- + +.. automodule:: day19.tests.test_parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day19.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day20.lib.rst.txt b/_sources/day20.lib.rst.txt new file mode 100644 index 0000000..c0c73e3 --- /dev/null +++ b/_sources/day20.lib.rst.txt @@ -0,0 +1,29 @@ +day20.lib package +================= + +Submodules +---------- + +day20.lib.classes module +------------------------ + +.. automodule:: day20.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day20.lib.parsers module +------------------------ + +.. automodule:: day20.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day20.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day20.rst.txt b/_sources/day20.rst.txt new file mode 100644 index 0000000..2fdd18f --- /dev/null +++ b/_sources/day20.rst.txt @@ -0,0 +1,30 @@ +day20 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day20.lib + day20.tests + +Submodules +---------- + +day20.day20 module +------------------ + +.. automodule:: day20.day20 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day20 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day20.tests.rst.txt b/_sources/day20.tests.rst.txt new file mode 100644 index 0000000..443bcb1 --- /dev/null +++ b/_sources/day20.tests.rst.txt @@ -0,0 +1,37 @@ +day20.tests package +=================== + +Submodules +---------- + +day20.tests.test\_classes module +-------------------------------- + +.. automodule:: day20.tests.test_classes + :members: + :undoc-members: + :show-inheritance: + +day20.tests.test\_day20 module +------------------------------ + +.. automodule:: day20.tests.test_day20 + :members: + :undoc-members: + :show-inheritance: + +day20.tests.test\_parsers module +-------------------------------- + +.. automodule:: day20.tests.test_parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day20.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day21.lib.rst.txt b/_sources/day21.lib.rst.txt new file mode 100644 index 0000000..b010439 --- /dev/null +++ b/_sources/day21.lib.rst.txt @@ -0,0 +1,29 @@ +day21.lib package +================= + +Submodules +---------- + +day21.lib.classes module +------------------------ + +.. automodule:: day21.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day21.lib.parsers module +------------------------ + +.. automodule:: day21.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day21.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day21.rst.txt b/_sources/day21.rst.txt new file mode 100644 index 0000000..723d81a --- /dev/null +++ b/_sources/day21.rst.txt @@ -0,0 +1,29 @@ +day21 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day21.lib + +Submodules +---------- + +day21.day21 module +------------------ + +.. automodule:: day21.day21 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day21 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day22.lib.rst.txt b/_sources/day22.lib.rst.txt new file mode 100644 index 0000000..2ddbbfd --- /dev/null +++ b/_sources/day22.lib.rst.txt @@ -0,0 +1,37 @@ +day22.lib package +================= + +Submodules +---------- + +day22.lib.classes module +------------------------ + +.. automodule:: day22.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day22.lib.parsers module +------------------------ + +.. automodule:: day22.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +day22.lib.vis module +-------------------- + +.. automodule:: day22.lib.vis + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day22.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day22.rst.txt b/_sources/day22.rst.txt new file mode 100644 index 0000000..07614eb --- /dev/null +++ b/_sources/day22.rst.txt @@ -0,0 +1,30 @@ +day22 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day22.lib + day22.tests + +Submodules +---------- + +day22.day22 module +------------------ + +.. automodule:: day22.day22 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day22 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day22.tests.rst.txt b/_sources/day22.tests.rst.txt new file mode 100644 index 0000000..9bd1812 --- /dev/null +++ b/_sources/day22.tests.rst.txt @@ -0,0 +1,37 @@ +day22.tests package +=================== + +Submodules +---------- + +day22.tests.test\_classes module +-------------------------------- + +.. automodule:: day22.tests.test_classes + :members: + :undoc-members: + :show-inheritance: + +day22.tests.test\_day22 module +------------------------------ + +.. automodule:: day22.tests.test_day22 + :members: + :undoc-members: + :show-inheritance: + +day22.tests.test\_parsers module +-------------------------------- + +.. automodule:: day22.tests.test_parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day22.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day23.lib.rst.txt b/_sources/day23.lib.rst.txt new file mode 100644 index 0000000..3020a96 --- /dev/null +++ b/_sources/day23.lib.rst.txt @@ -0,0 +1,37 @@ +day23.lib package +================= + +Submodules +---------- + +day23.lib.classes module +------------------------ + +.. automodule:: day23.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day23.lib.classes2 module +------------------------- + +.. automodule:: day23.lib.classes2 + :members: + :undoc-members: + :show-inheritance: + +day23.lib.parsers module +------------------------ + +.. automodule:: day23.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day23.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day23.rst.txt b/_sources/day23.rst.txt new file mode 100644 index 0000000..c3a1ee7 --- /dev/null +++ b/_sources/day23.rst.txt @@ -0,0 +1,30 @@ +day23 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day23.lib + day23.tests + +Submodules +---------- + +day23.day23 module +------------------ + +.. automodule:: day23.day23 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day23 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day23.tests.rst.txt b/_sources/day23.tests.rst.txt new file mode 100644 index 0000000..23b600e --- /dev/null +++ b/_sources/day23.tests.rst.txt @@ -0,0 +1,45 @@ +day23.tests package +=================== + +Submodules +---------- + +day23.tests.test\_classes module +-------------------------------- + +.. automodule:: day23.tests.test_classes + :members: + :undoc-members: + :show-inheritance: + +day23.tests.test\_classes2 module +--------------------------------- + +.. automodule:: day23.tests.test_classes2 + :members: + :undoc-members: + :show-inheritance: + +day23.tests.test\_day23 module +------------------------------ + +.. automodule:: day23.tests.test_day23 + :members: + :undoc-members: + :show-inheritance: + +day23.tests.test\_parsers module +-------------------------------- + +.. automodule:: day23.tests.test_parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day23.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day24.lib.rst.txt b/_sources/day24.lib.rst.txt new file mode 100644 index 0000000..55d37b3 --- /dev/null +++ b/_sources/day24.lib.rst.txt @@ -0,0 +1,29 @@ +day24.lib package +================= + +Submodules +---------- + +day24.lib.classes module +------------------------ + +.. automodule:: day24.lib.classes + :members: + :undoc-members: + :show-inheritance: + +day24.lib.parsers module +------------------------ + +.. automodule:: day24.lib.parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day24.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day24.rst.txt b/_sources/day24.rst.txt new file mode 100644 index 0000000..ee30783 --- /dev/null +++ b/_sources/day24.rst.txt @@ -0,0 +1,30 @@ +day24 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day24.lib + day24.tests + +Submodules +---------- + +day24.day24 module +------------------ + +.. automodule:: day24.day24 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day24 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day24.tests.rst.txt b/_sources/day24.tests.rst.txt new file mode 100644 index 0000000..51f4f43 --- /dev/null +++ b/_sources/day24.tests.rst.txt @@ -0,0 +1,29 @@ +day24.tests package +=================== + +Submodules +---------- + +day24.tests.test\_day24 module +------------------------------ + +.. automodule:: day24.tests.test_day24 + :members: + :undoc-members: + :show-inheritance: + +day24.tests.test\_parsers module +-------------------------------- + +.. automodule:: day24.tests.test_parsers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day24.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day25.rst.txt b/_sources/day25.rst.txt new file mode 100644 index 0000000..e290dad --- /dev/null +++ b/_sources/day25.rst.txt @@ -0,0 +1,29 @@ +day25 package +============= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + day25.tests + +Submodules +---------- + +day25.day25 module +------------------ + +.. automodule:: day25.day25 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day25 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/day25.tests.rst.txt b/_sources/day25.tests.rst.txt new file mode 100644 index 0000000..d9e9405 --- /dev/null +++ b/_sources/day25.tests.rst.txt @@ -0,0 +1,21 @@ +day25.tests package +=================== + +Submodules +---------- + +day25.tests.test\_day25 module +------------------------------ + +.. automodule:: day25.tests.test_day25 + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: day25.tests + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/download_inputs.rst.txt b/_sources/download_inputs.rst.txt new file mode 100644 index 0000000..f619ed7 --- /dev/null +++ b/_sources/download_inputs.rst.txt @@ -0,0 +1,7 @@ +download\_inputs module +======================= + +.. automodule:: download_inputs + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..b0d627f --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,23 @@ +.. adventofcode2023 documentation master file, created by + sphinx-quickstart on Fri Dec 29 20:26:23 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to adventofcode2023's documentation! +============================================ + +.. toctree:: + :caption: Contents: + :maxdepth: 1 + + autodoc + ci + modules + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/_sources/maker.rst.txt b/_sources/maker.rst.txt new file mode 100644 index 0000000..2a0b2a0 --- /dev/null +++ b/_sources/maker.rst.txt @@ -0,0 +1,7 @@ +maker module +============ + +.. automodule:: maker + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/modules.rst.txt b/_sources/modules.rst.txt new file mode 100644 index 0000000..540e1a3 --- /dev/null +++ b/_sources/modules.rst.txt @@ -0,0 +1,33 @@ +adventofcode2023 +================ + +.. toctree:: + :maxdepth: 1 + + day01 + day02 + day03 + day04 + day05 + day06 + day07 + day08 + day09 + day10 + day11 + day12 + day13 + day14 + day15 + day16 + day17 + day18 + day19 + day20 + day21 + day22 + day23 + day24 + day25 + download_inputs + maker diff --git a/_static/_sphinx_javascript_frameworks_compat.js b/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000..8141580 --- /dev/null +++ b/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..30fee9d --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css new file mode 100644 index 0000000..c718cee --- /dev/null +++ b/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff b/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff2 b/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff b/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff2 b/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/_static/css/fonts/fontawesome-webfont.eot b/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/_static/css/fonts/fontawesome-webfont.svg b/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/css/fonts/fontawesome-webfont.ttf b/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/_static/css/fonts/fontawesome-webfont.woff b/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/_static/css/fonts/fontawesome-webfont.woff2 b/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/_static/css/fonts/lato-bold-italic.woff b/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff differ diff --git a/_static/css/fonts/lato-bold-italic.woff2 b/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/_static/css/fonts/lato-bold.woff b/_static/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff differ diff --git a/_static/css/fonts/lato-bold.woff2 b/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff2 differ diff --git a/_static/css/fonts/lato-normal-italic.woff b/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff differ diff --git a/_static/css/fonts/lato-normal-italic.woff2 b/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/_static/css/fonts/lato-normal.woff b/_static/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/_static/css/fonts/lato-normal.woff differ diff --git a/_static/css/fonts/lato-normal.woff2 b/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/_static/css/fonts/lato-normal.woff2 differ diff --git a/_static/css/theme.css b/_static/css/theme.css new file mode 100644 index 0000000..19a446a --- /dev/null +++ b/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..7e4c114 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/jquery.js b/_static/jquery.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/html5shiv.min.js b/_static/js/html5shiv.min.js new file mode 100644 index 0000000..cd1c674 --- /dev/null +++ b/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/theme.js b/_static/js/theme.js new file mode 100644 index 0000000..1fddb6e --- /dev/null +++ b/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..84ab303 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..7918c3f --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/autodoc.html b/autodoc.html new file mode 100644 index 0000000..4f14fca --- /dev/null +++ b/autodoc.html @@ -0,0 +1,131 @@ + + + + + + + Automatic documentation — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Automatic documentation

+

The documentation in this repo was setup using this guide: +https://redandgreen.co.uk/sphinx-to-github-pages-via-github-actions/

+
+

Key commands

+
    +
  • Setup from scratch: +cd /docs && sphinx-quickstart

  • +
  • Generate .rst’s automatically (from root directory) +sphinx-apidoc -o docs .

  • +
  • Manually generate html (already done in Github Action): +cd docs && make html

  • +
  • Clean html

  • +
+

cd docs && make clean

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/ci.html b/ci.html new file mode 100644 index 0000000..4d10f15 --- /dev/null +++ b/ci.html @@ -0,0 +1,167 @@ + + + + + + + Continuous Integration — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Continuous Integration

+

CI allows lots of processes to occur automatically

+

Think of pre-commit as “local” and github actions as cloud.

+
    +
  1. pre-commit is a tool that runs arbitrary scripts at pre-commit and pre-push time. You can reuse it’s config for github-action based scripts, to ensure all ci scripts are run.

  2. +
  3. github actions (see .github/workflows) are workflows that run “on the cloud” on github.com. They run based on a few conditions such as pushing/merging, PRs etc. They mainly just setup an linux box to install pre-commit and then duplicate what was being done on a local environment. They also publish our html documentation (such as this page) into the gh-pages branch.

  4. +
+
+

Tools

+
+

Pre-commit

+

pre-commit is both the name of a tool, and of a stage in committing.

+

This section details what is done at the stages

+
    +
  1. pre-commit: runs ruff-linting and mypy and mixed-crlf. These are all very fast and ensure commits have barebones checks before going in.

  2. +
  3. pre-push: runs pytest unit tests as well as coverage

  4. +
+
+
+

ruff

+

A simple tool that does linting

+
+
+

mypy

+

Type-checking tool

+
+
+

mixed-crlf

+

Ensures that we don’t commit any crlf files. Converts all to lf

+
+
+

pytest

+

Unit testing framework

+
+
+

coverage

+

Coverage is a simple tool that tells you what % of your code is covered by tests. This can be easily gamed, for example a test that is assert main() == 0 will result in very high coverage but not necessarily good tests.

+
+
+

sphinx

+

Generates .rst files and html files automatically. You can inject markdown manually. Check the autodoc page for more information.

+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day01.html b/day01.html new file mode 100644 index 0000000..372b6b2 --- /dev/null +++ b/day01.html @@ -0,0 +1,361 @@ + + + + + + + day01 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day01 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day01.day1a module

+

day1a solution.

+
+
+day01.day1a.get_first_last(line: str) tuple[str, str][source]
+

Returns first and last numeric character of a string.

+

It can be the same character.

+
+
Parameters:
+

line (str) – string to parse

+
+
Raises:
+

ValueError – When there are no numbers in the string

+
+
Returns:
+

first char, last char

+
+
Return type:
+

tuple[str, str]

+
+
+
+ +
+
+day01.day1a.get_input(input_file: str) list[str][source]
+

Grabs list of lines to parse from input file.

+
+
Parameters:
+

input_file (str) – input file’s name

+
+
Returns:
+

list of lines to parse

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+day01.day1a.main() None[source]
+

Grabs input then processes it.

+
+ +
+
+day01.day1a.part1(lines: list[str]) int[source]
+

Runs day1/part1 of adventofcode2023.

+
+
Parameters:
+

lines (list[str]) – list of lines to parse

+
+
Returns:
+

sum of results for each line.

+
+
Return type:
+

int

+
+
+
+ +
+
+

day01.day1b module

+

day1b solution.

+
+
+class day01.day1b.IndexValue(index: int = 0, value: str = '1')[source]
+

Bases: object

+

index to value mapping.

+
+
+index: int = 0
+
+ +
+
+value: str = '1'
+
+ +
+ +
+
+class day01.day1b.WordNumber(word: str = 'one', number: str = '1')[source]
+

Bases: object

+

Simple mapping from word to integer.

+
+
+number: str = '1'
+
+ +
+
+word: str = 'one'
+
+ +
+ +
+
+day01.day1b.get_input(input_file: str) list[str][source]
+

Opens a file and returns a list of strings to handle.

+
+
Parameters:
+

input_file (str) – filepath of input

+
+
Returns:
+

list of strings to parse

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+day01.day1b.main() None[source]
+

Grabs input and then processes it.

+
+ +
+
+day01.day1b.part2(lines: list[str]) int[source]
+

Returns sum of “first/last” numbers in line.

+
+
Parameters:
+

lines (list[str]) – list of lines to handle

+
+
Returns:
+

sum of “first/last” numbers in line

+
+
Return type:
+

int

+
+
+
+ +
+
+day01.day1b.process_line(line: str) int[source]
+

Processes a line, returning the first and last integer.

+

Substrings like nine and one count as integers, +as do 1 and 9.

+
+
Parameters:
+

line (str) – a line to process

+
+
Returns:
+

integer value of the two numbers concatenated

+
+
Return type:
+

int

+
+
+
+ +
+
+

Module contents

+

day01 package.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day01.tests.html b/day01.tests.html new file mode 100644 index 0000000..773a4e2 --- /dev/null +++ b/day01.tests.html @@ -0,0 +1,211 @@ + + + + + + + day01.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day01.tests package

+
+

Submodules

+
+
+

day01.tests.test_day1a module

+

Tests for day1a.

+
+
+day01.tests.test_day1a.test_get_first_last() None[source]
+

Tests a variety of strings on get_first_last() function.

+
+ +
+
+day01.tests.test_day1a.test_get_input() None[source]
+

Tests get_input function.

+
+ +
+
+day01.tests.test_day1a.test_part1() None[source]
+

Tests part1() function.

+
+ +
+
+

day01.tests.test_day1b module

+

Tests for day01b.

+
+
+day01.tests.test_day1b.test_get_input() None[source]
+

Tests get_input() function.

+
+ +
+
+day01.tests.test_day1b.test_index_value() None[source]
+

Tests index_value class.

+
+ +
+
+day01.tests.test_day1b.test_part2() None[source]
+

Tests part2() function.

+
+ +
+
+day01.tests.test_day1b.test_process_line() None[source]
+

Tests process_line() function.

+
+ +
+
+

Module contents

+

day01 tests package.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day02.html b/day02.html new file mode 100644 index 0000000..b990588 --- /dev/null +++ b/day02.html @@ -0,0 +1,330 @@ + + + + + + + day02 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day02 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day02.day2 module

+

day2 solution.

+
+
+class day02.day2.Color(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: StrEnum

+

Color enum.

+
+
+BLUE = 'blue'
+
+ +
+
+GREEN = 'green'
+
+ +
+
+RED = 'red'
+
+ +
+ +
+
+class day02.day2.Draw(string: str)[source]
+

Bases: object

+

Class representing a draw from the bag.

+
+
+blue: int = 0
+
+ +
+
+green: int = 0
+
+ +
+
+parse_color_count(string: str) tuple[int, Color][source]
+

string: 1 blue.

+
+ +
+
+parse_colors_count(string: str) None[source]
+

string: 1 blue, 4 green, 5 red.

+
+ +
+
+red: int = 0
+
+ +
+ +
+
+class day02.day2.Game(string: str)[source]
+

Bases: object

+

Game class, showing multiple draws.

+
+
+blue: int = 0
+
+ +
+
+green: int = 0
+
+ +
+
+id: int
+
+ +
+
+parse_draws(string: str) list[Draw][source]
+

string: 1 blue; 4 green, 5 blue; 11 red, 3 blue.

+
+ +
+
+power_level() int[source]
+

Returns r*g*b.

+
+ +
+
+red: int = 0
+
+ +
+ +
+
+day02.day2.game_filter(game: Game) bool[source]
+

Returns true if the game satisfies the constraints of Question 1.

+
+ +
+
+day02.day2.get_games(input_file: str) list[Game][source]
+

Gets the games from the input file.

+
+
Parameters:
+

input_file (str) – input file name

+
+
Returns:
+

list of Games

+
+
Return type:
+

list[Game]

+
+
+
+ +
+
+day02.day2.main() None[source]
+

Parses data into data structures, then prints out answer to q1 and q2.

+
+ +
+
+day02.day2.part1(games: list[Game]) int[source]
+

Solves part 1.

+
+ +
+
+day02.day2.part2(games: list[Game]) int[source]
+

Solves part2.

+
+ +
+
+

Module contents

+

day02 implementation.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day02.tests.html b/day02.tests.html new file mode 100644 index 0000000..67a40ce --- /dev/null +++ b/day02.tests.html @@ -0,0 +1,200 @@ + + + + + + + day02.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day02.tests package

+
+

Submodules

+
+
+

day02.tests.test_day2 module

+

tests for day02.

+
+
+day02.tests.test_day2.get_game1() Game[source]
+

Returns a sample game.

+
+ +
+
+day02.tests.test_day2.get_game2() Game[source]
+

Returns another sample game.

+
+ +
+
+day02.tests.test_day2.test_draw() None[source]
+

Test draws.

+
+ +
+
+day02.tests.test_day2.test_game() None[source]
+

Test games.

+
+ +
+
+day02.tests.test_day2.test_part1() None[source]
+

Tests get_games, game_filter, part1.

+
+ +
+
+day02.tests.test_day2.test_part2() None[source]
+

Tests power.

+
+ +
+
+

Module contents

+

tests for day02.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day03.html b/day03.html new file mode 100644 index 0000000..c56f266 --- /dev/null +++ b/day03.html @@ -0,0 +1,257 @@ + + + + + + + day03 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day03 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day03.day3 module

+

Day 3 implementation.

+
+
+day03.day3.main() None[source]
+

Main entrypoint; grab input then run part1 and part2.

+
+ +
+
+day03.day3.part1(part_numbers: list[PartNumber]) int[source]
+

Return sum of valid partnumbers.

+
+ +
+
+day03.day3.part2(part_numbers: list[PartNumber], matrix: Matrix) int[source]
+

Return sum of valid gear values.

+
+ +
+
+

Module contents

+

day03 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day03.lib.html b/day03.lib.html new file mode 100644 index 0000000..7b66f93 --- /dev/null +++ b/day03.lib.html @@ -0,0 +1,302 @@ + + + + + + + day03.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day03.lib package

+
+

Submodules

+
+
+

day03.lib.classes module

+

Day3 classes.

+
+
+class day03.lib.classes.Gear(col: int, row: int, part_numbers: list[PartNumber] | None = None)[source]
+

Bases: object

+

Class representing a potential gear (* icon).

+
+
+col: int
+
+ +
+
+property gear_ratio: int
+

If we have exactly two parts, returns the gear ratio.

+
+ +
+
+part_numbers: list[PartNumber] | None = None
+
+ +
+
+row: int
+
+ +
+ +
+
+class day03.lib.classes.Matrix(data: list[str])[source]
+

Bases: object

+

Represents the entire 2d array.

+
+
+data: list[str]
+
+ +
+
+filter_engine_parts(part_numbers: list[PartNumber]) list[PartNumber][source]
+

Return the legit part numbers.

+
+ +
+
+find_gear_parts(gear: Gear, part_numbers: list[PartNumber]) list[PartNumber][source]
+

Returns a list of part_numbers that are touching a given gear.

+
+ +
+
+get_gears(part_numbers: list[PartNumber]) list[Gear][source]
+

Retrieve gears from the matrix.

+
+ +
+
+get_part_numbers() list[PartNumber][source]
+

Retrieve numbered words like 456 from the matrix.

+
+ +
+
+is_engine_part(part_number: PartNumber) bool[source]
+

Return whether a part_number is an engine part by looking at its surroundings.

+
+ +
+
+static is_engine_part_row(row: str) bool[source]
+

Returns if there is an engine part in this row.

+
+ +
+
+property row_count: int
+

How many rows there are.

+
+ +
+
+property row_size: int
+

How long each row is.

+
+ +
+ +
+
+class day03.lib.classes.PartNumber(col: int, row: int, length: int, value: int)[source]
+

Bases: object

+

Class respresenting a potential part number, and its position.

+
+
+col: int
+
+ +
+
+property end_index: int
+

Returns the end column index of the number.

+
+ +
+
+length: int
+
+ +
+
+row: int
+
+ +
+
+touching(col: int, row: int, row_size: int) bool[source]
+

Returns if a given coordinate is touching this PartNumber.

+
+ +
+
+value: int
+
+ +
+ +
+
+

day03.lib.parsers module

+

Functions to parse from a file into well defined classes.

+
+
+day03.lib.parsers.get_matrix(path: str) Matrix[source]
+

Convert text file to matrix.

+
+ +
+
+

Module contents

+

day03 libraries.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day03.tests.html b/day03.tests.html new file mode 100644 index 0000000..82d3d70 --- /dev/null +++ b/day03.tests.html @@ -0,0 +1,226 @@ + + + + + + + day03.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day03.tests package

+
+

Submodules

+
+
+

day03.tests.test_classes module

+

tests day3’s classes.

+
+
+class day03.tests.test_classes.PartNumberTouchTest(col: int, row: int, row_size: int, result: bool)[source]
+

Bases: object

+

Result for a part number touching something else.

+
+
+col: int
+
+ +
+
+result: bool
+
+ +
+
+row: int
+
+ +
+
+row_size: int
+
+ +
+ +
+
+day03.tests.test_classes.test_gear() None[source]
+

Tests gear class.

+
+ +
+
+day03.tests.test_classes.test_matrix() None[source]
+

Tests matrix.

+
+ +
+
+day03.tests.test_classes.test_part_number() None[source]
+

Tests part number class.

+
+ +
+
+

day03.tests.test_day3 module

+

test day3 main functions.

+
+
+day03.tests.test_day3.test_part1() None[source]
+

Test part1.

+
+ +
+
+day03.tests.test_day3.test_part2() None[source]
+

Test part2.

+
+ +
+
+

Module contents

+

day03 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day04.html b/day04.html new file mode 100644 index 0000000..d5050b1 --- /dev/null +++ b/day04.html @@ -0,0 +1,306 @@ + + + + + + + day04 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day04 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day04.day4 module

+

Day 4 solution.

+
+
+class day04.day4.Card(input_string: str)[source]
+

Bases: object

+

a card with winners and numbers we own.

+
+
+get_matches() int[source]
+

Returns how many winners intersect with what we have.

+
+ +
+
+get_points() int[source]
+

Returns how many points the card is worth.

+
+
Returns:
+

0 for no match, otherwise 2^(matches-1)

+
+
Return type:
+

int

+
+
+
+ +
+
+have: set[int]
+
+ +
+
+id: int = 0
+
+ +
+
+winners: set[int]
+
+ +
+ +
+
+class day04.day4.Inventory(all_cards: list[Card])[source]
+

Bases: object

+

Total inventory of cards based on Question2 accumulation.

+
+
+all_cards: list[Card]
+
+ +
+
+calculate_mappings() dict[int, int][source]
+

Returns map of card_id -> cards owned.

+
+ +
+
+memoized: dict[int, int]
+
+ +
+
+total_cards() int[source]
+

Return total cards in inventory.

+
+ +
+ +
+
+day04.day4.grab_data(filename: str) list[Card][source]
+

Converts file into wellformed cards.

+
+ +
+
+day04.day4.main() None[source]
+

Loads input file then runs part1 and part2.

+
+ +
+
+day04.day4.part1(cards: list[Card]) int[source]
+

Return sum of points for each card in list.

+
+ +
+
+day04.day4.part2(cards: list[Card]) int[source]
+

Return total number of cards in our inventory.

+
+ +
+
+day04.day4.split_numbers(string: str) set[int][source]
+

Splits a list of string’ed numbers into a set.

+

E.g: `` 39 40 41 42 `` -> set(39,40,41,42)

+
+
Parameters:
+

string (str) – a list of string’ed numbers

+
+
Returns:
+

a set of integers

+
+
Return type:
+

set[int]

+
+
+
+ +
+
+

Module contents

+

day04 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day04.tests.html b/day04.tests.html new file mode 100644 index 0000000..dea9026 --- /dev/null +++ b/day04.tests.html @@ -0,0 +1,200 @@ + + + + + + + day04.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day04.tests package

+
+

Submodules

+
+
+

day04.tests.test_day4 module

+

tests for day04.

+
+
+day04.tests.test_day4.test_card() None[source]
+

Test Card class.

+
+ +
+
+day04.tests.test_day4.test_grab_data() None[source]
+

Test grab_data().

+
+ +
+
+day04.tests.test_day4.test_inventory() None[source]
+

Test Inventory class.

+
+ +
+
+day04.tests.test_day4.test_part1() None[source]
+

Test part1().

+
+ +
+
+day04.tests.test_day4.test_part2() None[source]
+

Test part2().

+
+ +
+
+day04.tests.test_day4.test_split_numbers() None[source]
+

Test split_numbers.

+
+ +
+
+

Module contents

+

Tests for part04.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day05.html b/day05.html new file mode 100644 index 0000000..1659a2b --- /dev/null +++ b/day05.html @@ -0,0 +1,280 @@ + + + + + + + day05 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day05 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day05.day5 module

+

Day5 solution.

+
+
+day05.day5.get_location(seed: int, maps: list[NamedMap]) int[source]
+

Given a seed, returns the final location.

+
+ +
+
+day05.day5.get_location_ranges(seed_ranges: list[MappingRange], maps: list[NamedMap]) list[MappingRange][source]
+

Given a list of MappingRange, returns a list of MappingRange’s for the final location.

+
+ +
+
+day05.day5.main() None[source]
+

Main function, solve all the problems.

+
+ +
+
+day05.day5.part1(seeds: list[int], maps: list[NamedMap]) int[source]
+

Return the final location with lowest value.

+
+ +
+
+day05.day5.part2(seeds: list[int], maps: list[NamedMap]) int[source]
+

Parses multiple seed ranges, and finds the lowest location start.

+
+ +
+
+day05.day5.seed_to_mapping_ranges(data: list[int]) list[MappingRange][source]
+

Pair up seeds into mapping ranges.

+

instead of seeds: 1, 2, 3, 4, 5, 6 +we want MappingRange[1,2], MappingRange(3,4), MappingRange(5,6) +They are in the format [start, size]

+
+
Parameters:
+

data (list[int]) – list of seeds

+
+
Returns:
+

list of mapping ranges

+
+
Return type:
+

list[MappingRange]

+
+
+
+ +
+
+

Module contents

+

day05 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day05.lib.html b/day05.lib.html new file mode 100644 index 0000000..dd61585 --- /dev/null +++ b/day05.lib.html @@ -0,0 +1,299 @@ + + + + + + + day05.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day05.lib package

+
+

Submodules

+
+
+

day05.lib.classes module

+

classes for day05.

+
+
+class day05.lib.classes.Mapping(src_start: int, dest_start: int, size: int, injected: bool = False)[source]
+

Bases: object

+

Simple range based mapping.

+
+
+dest_end: int
+
+ +
+
+dest_start: int
+
+ +
+
+get_mapping(src_value: int) int[source]
+

Converts from src_value to destination.

+
+ +
+
+get_mappings(start: int, end: int) tuple[MappingRange, int][source]
+

Returns the chunk from start to end, followed by the remainder.

+
+ +
+
+injected: bool = False
+
+ +
+
+size: int
+
+ +
+
+src_end: int
+
+ +
+
+src_start: int
+
+ +
+ +
+
+class day05.lib.classes.MappingRange(start: int, end: int)[source]
+

Bases: object

+

Simple class for start/end range.

+
+
+end: int
+
+ +
+
+start: int
+
+ +
+ +
+
+class day05.lib.classes.NamedMap(name: str)[source]
+

Bases: object

+

a named map with a list of mappings.

+
+
+add_mapping(mapping: Mapping) None[source]
+

Adds a mapping to our list.

+
+ +
+
+extend_mapping_range(mappings: list[Mapping]) list[Mapping][source]
+

Ensure that mappings go from 0 -> INT_MAX.

+
+ +
+
+finalize_mappings() None[source]
+

Post processes the mappings.

+
    +
  • Sorts the mappings

  • +
  • Fills in any missing ranges

  • +
  • Homogenizes so min_mapping is 0, and max_mapping is INT_MAX

  • +
+
+ +
+
+get_mapping(value: int) int[source]
+

Uses binary search to grab the correct mapping, then apply it to one value.

+
+ +
+
+get_mapping_range(src_mapping_range: MappingRange) list[MappingRange][source]
+

Remaps a source range to a list of destination ranges.

+
+ +
+
+get_mapping_ranges(src_mapping_ranges: list[MappingRange]) list[MappingRange][source]
+

Given a list of mapping ranges, returns a new list of mapping ranges.

+
+ +
+
+mappings: list[Mapping]
+
+ +
+
+name: str
+
+ +
+ +
+
+

day05.lib.parsers module

+

Parsing from source file to well defined classes.

+
+
+day05.lib.parsers.grab_inputs(path: str) tuple[list[int], list[NamedMap]][source]
+

Parses the source file.

+
+ +
+
+

Module contents

+

libraries for day05.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day05.tests.html b/day05.tests.html new file mode 100644 index 0000000..4660230 --- /dev/null +++ b/day05.tests.html @@ -0,0 +1,189 @@ + + + + + + + day05.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day05.tests package

+
+

Submodules

+
+
+

day05.tests.test_day5 module

+

Day5 mainfile tests.

+
+
+day05.tests.test_day5.test_mapping() None[source]
+

Test NamedMap class.

+
+ +
+
+day05.tests.test_day5.test_part1() None[source]
+

Test part1().

+
+ +
+
+day05.tests.test_day5.test_part2() None[source]
+

Test part2().

+
+ +
+
+day05.tests.test_day5.test_seed_to_mapping_ranges() None[source]
+

Test construction of mapping ranges for part2.

+
+ +
+
+

Module contents

+

Tests for day05.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day06.html b/day06.html new file mode 100644 index 0000000..0274780 --- /dev/null +++ b/day06.html @@ -0,0 +1,283 @@ + + + + + + + day06 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day06 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day06.day6 module

+

Day06 solution.

+
+
+class day06.day6.Race(time: int, record_distance: int)[source]
+

Bases: object

+

Simple class representing a race and its record.

+
+
+record_distance: int
+
+ +
+
+time: int
+
+ +
+ +
+
+class day06.day6.RaceStrat(charge_time: int, run_time: int)[source]
+

Bases: object

+

class representing a strategy (charge time + run_time).

+
+
+charge_time: int
+
+ +
+
+property distance: int
+

Returns how far we moved.

+
+ +
+
+run_time: int
+
+ +
+
+property speed: int
+

Return the speed after the charge time.

+
+ +
+ +
+
+day06.day6.calculate_constant_time(race: Race) int[source]
+

TL;DR Quadratic formula.

+
+ +
+
+day06.day6.calculate_race(race: Race) int[source]
+

Naive calcuation of a race.

+
+ +
+
+day06.day6.get_giga_race(races: list[Race]) Race[source]
+

Converts from a list of races into one giga race.

+
+ +
+
+day06.day6.main() None[source]
+

Solves Day 6.

+
+ +
+
+day06.day6.part1(races: list[Race]) int[source]
+

Returns the sum of the amnount of ways we can win each race.

+
+ +
+
+day06.day6.part2(race: Race) int[source]
+

Return amoutn of ways we can win the race.

+
+ +
+
+day06.day6.read_inputs(path: str) list[Race][source]
+

Disgusting but short i guess.

+
+ +
+
+

Module contents

+

day06 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day06.tests.html b/day06.tests.html new file mode 100644 index 0000000..af916d9 --- /dev/null +++ b/day06.tests.html @@ -0,0 +1,200 @@ + + + + + + + day06.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day06.tests package

+
+

Submodules

+
+
+

day06.tests.test_day6 module

+

tests for day06.

+
+
+day06.tests.test_day6.test_calculate_constant_time() None[source]
+

Test calculating constant time.

+
+ +
+
+day06.tests.test_day6.test_calculate_race() None[source]
+

Tests calculating the race in both constant-time and brute-force.

+
+ +
+
+day06.tests.test_day6.test_get_giga_race() None[source]
+

Tests reading the input for the giga race.

+
+ +
+
+day06.tests.test_day6.test_part1() None[source]
+

Tests part1.

+
+ +
+
+day06.tests.test_day6.test_part2() None[source]
+

Tests part2.

+
+ +
+
+day06.tests.test_day6.test_read_inputs() None[source]
+

Tests reading inputs from file.

+
+ +
+
+

Module contents

+

tests for day6.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day07.html b/day07.html new file mode 100644 index 0000000..5344640 --- /dev/null +++ b/day07.html @@ -0,0 +1,262 @@ + + + + + + + day07 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day07 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day07.day7 module

+

day7 solution.

+
+
+class day07.day7.Hand(cards: str, bet: int)[source]
+

Bases: object

+

Simple hand class, uses cards_inted and of_a_kind for sorting.

+
+
+CARD_MAPPING: ClassVar[str] = '23456789TJQKA'
+
+ +
+
+bet: int
+
+ +
+
+calculate_of_a_kind() list[int][source]
+

Figure out card sets.

+
+ +
+
+cards: str
+
+ +
+
+cards_inted: list[int]
+
+ +
+
+of_a_kind: list[int]
+
+ +
+ +
+
+class day07.day7.HandPart2(cards: str, bet: int)[source]
+

Bases: Hand

+

Part two; implements joker rule.

+
+
+CARD_MAPPING: ClassVar[str] = 'J23456789TQKA'
+
+ +
+
+calculate_of_a_kind() list[int][source]
+

Figure out card sets; jokers will be added to the biggest card set.

+
+ +
+ +
+
+day07.day7.calculate_hands(cls: type, input_path: str) int[source]
+

Generates class cls then calculates points.

+
+ +
+
+day07.day7.main() None[source]
+

Main func.

+
+ +
+
+day07.day7.parse_lines(cls: type, path: str) list[Hand][source]
+

Open input file and parse into hand structures.

+
+ +
+
+

Module contents

+

day07 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day07.tests.html b/day07.tests.html new file mode 100644 index 0000000..3dde40b --- /dev/null +++ b/day07.tests.html @@ -0,0 +1,182 @@ + + + + + + + day07.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day07.tests package

+
+

Submodules

+
+
+

day07.tests.test_day7 module

+

tests for day07.

+
+
+day07.tests.test_day7.test_calculate_hands() None[source]
+

Test calculating hands.

+
+ +
+
+day07.tests.test_day7.test_hand() None[source]
+

Test Hand class.

+
+ +
+
+day07.tests.test_day7.test_parser() None[source]
+

Test the input parsing code.

+
+ +
+
+

Module contents

+

Tests for day07.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day08.html b/day08.html new file mode 100644 index 0000000..35d56d3 --- /dev/null +++ b/day08.html @@ -0,0 +1,357 @@ + + + + + + + day08 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day08 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day08.day8 module

+

day8 solution.

+
+
+class day08.day8.Cycle(start_location: Location, location_steps: list[LocationStep], cycle_start: LocationStep)[source]
+

Bases: object

+

Find a cycle.

+
+
+property cycle_length: int
+

Return length of the repeating part.

+
+ +
+
+cycle_start: LocationStep
+
+ +
+
+cycle_start_index: int
+
+ +
+
+end_zs: list[int]
+
+ +
+
+get_location(index: int) LocationStep[source]
+

Gets a location given a step index.

+
+ +
+
+location_steps: list[LocationStep]
+
+ +
+
+start_location: Location
+
+ +
+ +
+
+class day08.day8.Directions(steps: str)[source]
+

Bases: object

+

Simple directions string.

+
+
+get_step(index: int) str[source]
+

Returns a step given its index.

+
+ +
+
+get_steps_iterator() Iterator[str][source]
+

Returns a iterator that loops through indefinitely.

+
+ +
+
+steps: str
+
+ +
+ +
+
+class day08.day8.Location(name: str, left: str, right: str)[source]
+

Bases: object

+

A location on our map, with names of other locations.

+
+
+left: str
+
+ +
+
+name: str
+
+ +
+
+right: str
+
+ +
+ +
+
+class day08.day8.LocationStep(location: Location, steps: int)[source]
+

Bases: object

+

Location + how many steps to get here.

+
+
+location: Location
+
+ +
+
+steps: int
+
+ +
+ +
+
+class day08.day8.WorldMap[source]
+

Bases: object

+

world map class.

+
+
+add_location(location: Location) None[source]
+

Add location to our mappings.

+
+ +
+
+mappings: dict[str, Location]
+
+ +
+ +
+
+day08.day8.find_cycle(location: Location, world_map: WorldMap, directions: Directions) Cycle[source]
+

Finds the cycle from a start location.

+
+ +
+
+day08.day8.follow_directions(directions: Directions, world_map: WorldMap) int[source]
+

Follows directions until we hit zzz.

+
+ +
+
+day08.day8.follow_directions_multi(directions: Directions, world_map: WorldMap) int[source]
+

Follow all mappings ending in A until all of them are ZZZ.

+
+ +
+
+day08.day8.get_location_as(world_map: WorldMap) list[Location][source]
+

Returns locations with an A at the end.

+
+ +
+
+day08.day8.main() None[source]
+

Main function, solve the things.

+
+ +
+
+day08.day8.read_input(path: str) tuple[Directions, WorldMap][source]
+

Reads input into directions/world_map.

+
+ +
+
+

Module contents

+

Day08 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day08.tests.html b/day08.tests.html new file mode 100644 index 0000000..23ded28 --- /dev/null +++ b/day08.tests.html @@ -0,0 +1,194 @@ + + + + + + + day08.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day08.tests package

+
+

Submodules

+
+
+

day08.tests.test_day8 module

+

day08 tests.

+
+
+day08.tests.test_day8.test_directions() None[source]
+

Test directions class.

+
+ +
+
+day08.tests.test_day8.test_find_cycle() None[source]
+

Test finding cycles.

+
+ +
+
+day08.tests.test_day8.test_location_as() None[source]
+

Test finding of the a locations.

+
+ +
+
+day08.tests.test_day8.test_part1() None[source]
+

Test part1().

+
+ +
+
+day08.tests.test_day8.test_part2() None[source]
+

Test part2().

+
+ +
+
+

Module contents

+

day08 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day09.html b/day09.html new file mode 100644 index 0000000..4b35627 --- /dev/null +++ b/day09.html @@ -0,0 +1,267 @@ + + + + + + + day09 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day09 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day09.day9 module

+

day9 solution.

+
+
+class day09.day9.ValueArray(sub_arrays: list[list[int]])[source]
+

Bases: object

+

Class representing an array and its subarrays.

+
+
+extrapolate_left() None[source]
+

Extrapolates to the left.

+
+ +
+
+extrapolate_right() None[source]
+

Extrapolates to the right.

+
+ +
+
+generic_extrapolate(add_to_array: Callable[[list[int], int], None], calc_value: Callable[[list[int], list[int]], int]) None[source]
+

Generic extrapolation.

+
+ +
+
+sub_arrays: list[list[int]]
+
+ +
+ +
+
+day09.day9.get_input(path: str) list[ValueArray][source]
+

Turns inputs into nice ValueArrays.

+
+ +
+
+day09.day9.interpolate(values: list[int]) list[int][source]
+

Interpolate a list using element-wise diffs.

+
+
Converts 3 3 3 3

to 0 0 0

+
+
Converts 1 2 3 4

to 1 1 1

+
+
+
+
Parameters:
+

values (list[int]) – list of values

+
+
Returns:
+

interpolated list

+
+
Return type:
+

list[int]

+
+
+
+ +
+
+day09.day9.main() None[source]
+

Main function.

+
+ +
+
+day09.day9.part1(inputs: list[ValueArray]) int[source]
+

Interpolates then extrapolates array to the right.

+
+ +
+
+day09.day9.part2(inputs: list[ValueArray]) int[source]
+

Interpolates then extrapolates array to the left.

+
+ +
+
+

Module contents

+

day9 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day09.tests.html b/day09.tests.html new file mode 100644 index 0000000..f66b3fe --- /dev/null +++ b/day09.tests.html @@ -0,0 +1,188 @@ + + + + + + + day09.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day09.tests package

+
+

Submodules

+
+
+

day09.tests.test_day9 module

+

day09 tests.

+
+
+day09.tests.test_day9.test_get_input() None[source]
+

Test input grabbing function.

+
+ +
+
+day09.tests.test_day9.test_interpolate() None[source]
+

Test interpolate() function.

+
+ +
+
+day09.tests.test_day9.test_part1() None[source]
+

Test part1().

+
+ +
+
+day09.tests.test_day9.test_part2() None[source]
+

Test part2().

+
+ +
+
+

Module contents

+

day09 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day10.html b/day10.html new file mode 100644 index 0000000..57dde4f --- /dev/null +++ b/day10.html @@ -0,0 +1,370 @@ + + + + + + + day10 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day10 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day10.day10 module

+

day10 solution.

+
+
+day10.day10.calculate_s(start: Position, pipe_map: PipeMap) str[source]
+

Calculate what the “S” character is as a pipe.

+

We should have exactly two pipes going into us

+
+
Parameters:
+
    +
  • start (Position) – position of S

  • +
  • pipe_map (PipeMap) – map of pipes

  • +
+
+
Raises:
+
    +
  • ValueErrorS doesn’t have any connecting pipes

  • +
  • AssertionErrorS does not match any known pipe.

  • +
+
+
Returns:
+

The character representing the pipe

+
+
Return type:
+

str

+
+
+
+ +
+
+day10.day10.expand_map(pipe_map: PipeMap) PipeMap[source]
+

Expands each pipe into a 3x3 tile.

+
+ +
+
+day10.day10.expand_pipe(character: str, is_loop: bool) tuple[str, str, str][source]
+

Expands a pipe character to big boi 3x3.

+
+ +
+
+day10.day10.find_cycles(pipe_map: PipeMap) list[Pipe][source]
+

Finds the pipe path starting from S.

+
+ +
+
+day10.day10.find_s(pipe_map: PipeMap) Position[source]
+

Finds the S pipe.

+
+ +
+
+day10.day10.flood_fill(pipe_map: PipeMap) int[source]
+

Flood fills a pipemap from one starting tile.

+
+
Parameters:
+

pipe_map (PipeMap) – pipemap to fill

+
+
Returns:
+

how many tiles were filled

+
+
Return type:
+

int

+
+
+
+ +
+
+day10.day10.main() None[source]
+

Read input and run part1/part2.

+
+ +
+
+day10.day10.part1(pipe_map: PipeMap) int[source]
+

Finds length of S loop.

+
+ +
+
+day10.day10.part2(pipe_map: PipeMap) int[source]
+

Finds tiles “inside” the loop.

+
+ +
+
+day10.day10.process_big_input_line(row: int, line: str) list[Pipe][source]
+

Process a single line of input.

+
+ +
+
+day10.day10.process_input_line(row: int, line: str) list[Pipe][source]
+

Process a single line of input.

+
+ +
+
+day10.day10.read_input(path: str) PipeMap[source]
+

Read the map.

+
+ +
+
+day10.day10.reduce_map(big_map: PipeMap, small_map: PipeMap) PipeMap[source]
+

Converts from fat map back down to small map.

+
+ +
+
+

Module contents

+

Day10 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day10.lib.html b/day10.lib.html new file mode 100644 index 0000000..a7eb4f0 --- /dev/null +++ b/day10.lib.html @@ -0,0 +1,390 @@ + + + + + + + day10.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day10.lib package

+
+

Submodules

+
+
+

day10.lib.direction module

+

direction Enum.

+
+
+class day10.lib.direction.Direction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: Enum

+

Cardinal (NSEW) direction Enum.

+
+
+EAST = 3
+
+ +
+
+NORTH = 1
+
+ +
+
+SOUTH = 2
+
+ +
+
+WEST = 4
+
+ +
+
+opposite() Direction[source]
+

Return opposite direction.

+

e.g. E``<->``W +and N``<->``S

+
+
Raises:
+

AssertionError – if direction is invalid

+
+
Returns:
+

opposite direction.

+
+
Return type:
+

Direction

+
+
+
+ +
+ +
+
+

day10.lib.pipebounds module

+

PipeBounds class.

+
+
+class day10.lib.pipebounds.PipeBounds(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: Enum

+

Whether this tile is a pipe, inside, outside or currently unknown.

+
+
+INSIDE = 0
+
+ +
+
+OUTSIDE = 1
+
+ +
+
+PIPE = 3
+
+ +
+
+UNKNOWN = 2
+
+ +
+ +
+
+

day10.lib.pipes module

+

Pipe class.

+
+
+class day10.lib.pipes.Pipe(row: int, col: int, character: str, font: int = 1, is_loop: bool = False, pipe_bounds: PipeBounds = PipeBounds.UNKNOWN)[source]
+

Bases: object

+

The location and character representing the pipe.

+
+
+PIPE_DIRECTION: ClassVar[dict[str, list[Direction]]] = {'-': [Direction.WEST, Direction.EAST], '.': [], '7': [Direction.WEST, Direction.SOUTH], 'F': [Direction.SOUTH, Direction.EAST], 'J': [Direction.NORTH, Direction.WEST], 'L': [Direction.NORTH, Direction.EAST], '|': [Direction.NORTH, Direction.SOUTH]}
+
+ +
+
+character: str
+
+ +
+
+col: int
+
+ +
+
+font: int = 1
+
+ +
+
+is_loop: bool = False
+
+ +
+
+is_start: bool
+
+ +
+
+next_direction(prev_direction: Direction | None = None) Direction[source]
+

Determine next direction based on where we came from.

+
+ +
+
+next_position(prev_direction: Direction | None) tuple[Direction, Position][source]
+

Calculate the next position.

+

Return where we came from if we move to next tile, and the new position

+
+
Parameters:
+

prev_direction (Direction | None) – Where we came from

+
+
Returns:
+

Next direction and position.

+
+
Return type:
+

tuple[Direction, Position]

+
+
+
+ +
+
+pipe_bounds: PipeBounds = 2
+
+ +
+
+property position: Position
+

Our current position.

+
+ +
+
+row: int
+
+ +
+ +
+
+class day10.lib.pipes.PipeMap(pipes: list[list[Pipe]])[source]
+

Bases: object

+

A 2d array of pipes.

+
+
+get_pipe(position: Position) Pipe[source]
+

Returns a pipe given its position.

+
+ +
+
+get_pipe_safe(position: Position) Pipe | None[source]
+

Gets a pipe or returns None if position is out of map.

+
+ +
+
+height: int
+
+ +
+
+is_in_map(position: Position) bool[source]
+

Returns whether a position is in the map.

+
+ +
+
+pipes: list[list[Pipe]]
+
+ +
+
+width: int
+
+ +
+ +
+
+

day10.lib.position module

+

Position class.

+
+
+class day10.lib.position.Position(row: int, col: int)[source]
+

Bases: object

+

Simple 2d coordinate.

+
+
+col: int
+
+ +
+
+next_position(direction: Direction) Position[source]
+

Determine next position based on direction.

+
+ +
+
+row: int
+
+ +
+ +
+
+

Module contents

+

library modules for day10.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day10.tests.html b/day10.tests.html new file mode 100644 index 0000000..250f7f4 --- /dev/null +++ b/day10.tests.html @@ -0,0 +1,181 @@ + + + + + + + day10.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day10.tests package

+
+

Submodules

+
+
+

day10.tests.test_day10 module

+

Tests for main functions in day10.

+
+
+day10.tests.test_day10.test_day10() None[source]
+

Test day10.

+
+ +
+
+

day10.tests.test_direction module

+

Tests for direction class.

+
+
+day10.tests.test_direction.test_direction() None[source]
+

Test direction class.

+
+ +
+
+

Module contents

+

Tests for day10.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day11.html b/day11.html new file mode 100644 index 0000000..506ed38 --- /dev/null +++ b/day11.html @@ -0,0 +1,315 @@ + + + + + + + day11 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day11 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day11.day11 module

+

day11 solution.

+
+
+class day11.day11.Galaxy(row: int, col: int, id: int)[source]
+

Bases: object

+

Galaxy represented as its position and unique id.

+
+
+col: int
+
+ +
+
+distance(other: Galaxy, universe: Universe) int[source]
+

Return distance from one galaxy to another.

+

Accounts for “expanded” spaces in the universe

+
+ +
+
+id: int
+
+ +
+
+row: int
+
+ +
+ +
+
+class day11.day11.Point(row: int, col: int, item: str)[source]
+

Bases: object

+

Point represented by character and whether it is expanded.

+
+
+col: int
+
+ +
+
+get_point_distance(expansion_rate: int) int[source]
+

Returns how big this “point” is based on if its expanded.

+
+ +
+
+is_expanded: bool = False
+
+ +
+
+item: str
+
+ +
+
+row: int
+
+ +
+ +
+
+class day11.day11.Universe(contents: list[list[Point]], expansion_rate: int = 2)[source]
+

Bases: object

+

Universe class; 2d array of . and #.

+
+
+contents: list[list[Point]]
+
+ +
+
+expand_contents() None[source]
+

Expands the contents of the universe.

+
+ +
+
+expansion_rate: int = 2
+
+ +
+
+grab_galaxies() list[Galaxy][source]
+

Grabs all galaxies.

+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+ +
+
+day11.day11.get_total_distance(galaxies: list[Galaxy], universe: Universe) int[source]
+

Returns total distance of all galaxies.

+
+ +
+
+day11.day11.is_empty(items: list[Point]) bool[source]
+

Returns True if all the content is ..

+
+ +
+
+day11.day11.main() None[source]
+

Main function, runs q1 and q2.

+
+ +
+
+day11.day11.parse_input(path: str) Universe[source]
+

Parse input file and return a universe.

+
+ +
+
+

Module contents

+

Day11 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day11.tests.html b/day11.tests.html new file mode 100644 index 0000000..f16f25d --- /dev/null +++ b/day11.tests.html @@ -0,0 +1,182 @@ + + + + + + + day11.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day11.tests package

+
+

Submodules

+
+
+

day11.tests.test_day11 module

+

Tests for day11.

+
+
+day11.tests.test_day11.test_day11() None[source]
+

Test day11 main function.

+
+ +
+
+day11.tests.test_day11.test_expansion() None[source]
+

Test expansion of universe.

+
+ +
+
+day11.tests.test_day11.test_is_empty() None[source]
+

Tests is_empty function on universe.

+
+ +
+
+

Module contents

+

tests for day11.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day12.html b/day12.html new file mode 100644 index 0000000..7af8538 --- /dev/null +++ b/day12.html @@ -0,0 +1,281 @@ + + + + + + + day12 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day12 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day12.day12 module

+

day12 solution.

+
+
+class day12.day12.SpringLine(items: str, broken_springs: list[int])[source]
+

Bases: object

+

Springline class.

+
+
+big_cache: dict[State, int]
+
+ +
+
+broken_springs: list[int]
+
+ +
+
+calculate() int[source]
+

Brute force with backtracking lets go…

+
+ +
+
+calculate_recursive(state: State) int[source]
+

Recursive with memoization.

+
    +
  1. memoized

  2. +
  3. state.empty -> return if we are valid

  4. +
  5. state[0] == “.” chop it and continue

  6. +
  7. state[0] == “#”. get next number, and “enforce” it, chopping things. If anything is wrong, fail

  8. +
+
+ +
+
+items: str
+
+ +
+
+set_and_return(state: State, value: int) int[source]
+

Sets and returns in one line.

+
+ +
+
+unfold() SpringLine[source]
+

Makes it 5x bigger (part2).

+
+ +
+ +
+
+class day12.day12.State(items: str, broken_springs: list[int])[source]
+

Bases: object

+

Thes tate of a spring.

+
+
+broken_springs: list[int]
+
+ +
+
+items: str
+
+ +
+
+valid() int[source]
+

Returns true IFF we are completed without errors.

+
+ +
+ +
+
+day12.day12.calculate_sum(spring_lines: list[SpringLine]) int[source]
+

Calculates every spring line and then adds the totals.

+
+ +
+
+day12.day12.get_input(path: str) list[SpringLine][source]
+

Returns list of SpringLines from input file.

+
+ +
+
+day12.day12.main() None[source]
+

Main function.

+
+ +
+
+

Module contents

+

day12 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day12.tests.html b/day12.tests.html new file mode 100644 index 0000000..db87824 --- /dev/null +++ b/day12.tests.html @@ -0,0 +1,182 @@ + + + + + + + day12.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day12.tests package

+
+

Submodules

+
+
+

day12.tests.test_day12 module

+

day12 tests.

+
+
+day12.tests.test_day12.test_calculate_sum() None[source]
+

Test calculate_sum().

+
+ +
+
+day12.tests.test_day12.test_parser() None[source]
+

Test get_input().

+
+ +
+
+day12.tests.test_day12.test_spring_line() None[source]
+

Test SpringLine class.

+
+ +
+
+

Module contents

+

day12 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day13.html b/day13.html new file mode 100644 index 0000000..f68aea4 --- /dev/null +++ b/day13.html @@ -0,0 +1,259 @@ + + + + + + + day13 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day13 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day13.day13 module

+

day13 solution.

+
+
+class day13.day13.Maze(tiles: list[str])[source]
+

Bases: object

+

2d array of tiles.

+
+
+check_reflection(data: list[str], target_dist: int) int | None[source]
+

Checks a reflection on rows or cols.

+
+ +
+
+reflect_cols(distance: int) int | None[source]
+

Checks reflection of cols by flipping rows/cols.

+
+ +
+
+reflect_rows(distance: int) int | None[source]
+

Check a row’s reflection.

+
+ +
+
+score(row_reflect: int | None, col_reflect: int | None) int[source]
+

Returns score for q1.

+
+ +
+
+solve(distance: int = 0) int[source]
+

Solves a maze given an edit distance.

+

0 == Equal mirror +1 == smudge in mirror

+
+ +
+
+tiles: list[str]
+
+ +
+ +
+
+day13.day13.distance(left: str, right: str) int[source]
+

Returns edit distance of two strings.

+
+ +
+
+day13.day13.main() None[source]
+

Loads input then runs q1/q2.

+
+ +
+
+day13.day13.read_input(path: str) list[Maze][source]
+

Read input file into well defined Mazes.

+
+
Parameters:
+

path (str) – filename of input

+
+
Returns:
+

list of well defined Mazes.

+
+
Return type:
+

list[Maze]

+
+
+
+ +
+
+

Module contents

+

Day13 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day13.tests.html b/day13.tests.html new file mode 100644 index 0000000..0c0ca3a --- /dev/null +++ b/day13.tests.html @@ -0,0 +1,176 @@ + + + + + + + day13.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day13.tests package

+
+

Submodules

+
+
+

day13.tests.test_day13 module

+

Day13 tests.

+
+
+day13.tests.test_day13.test_day13() None[source]
+

Test day13.

+
+ +
+
+day13.tests.test_day13.test_distance() None[source]
+

Test distance() function.

+
+ +
+
+

Module contents

+

day13 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day14.html b/day14.html new file mode 100644 index 0000000..6cc61d9 --- /dev/null +++ b/day14.html @@ -0,0 +1,330 @@ + + + + + + + day14 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day14 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day14.day14 module

+

day14 solution.

+
+
+class day14.day14.World(data: Any, left_is: Direction = Direction.West, score: int | None = None)[source]
+

Bases: object

+

2d array of boulders (square/round) and empty space.

+
+
+as_orientiented_north() str[source]
+

Well defined string if our world has up as North.

+
+ +
+
+correct_side() World[source]
+

Return world oriented with North at the top.

+
+ +
+
+data: Any
+
+ +
+
+get_score() int[source]
+

Get score of the world.

+
+ +
+
+left_is: Direction = 1
+
+ +
+
+rotate_world_ccw() World[source]
+

Rotate world anti-clockwise.

+
+ +
+
+rotate_world_cw() World[source]
+

Rotate world clockwise.

+
+ +
+
+score: int | None = None
+
+ +
+
+to_string() str[source]
+

Well defined string of our world.

+
+ +
+ +
+
+day14.day14.get_input(path: str) World[source]
+

Grabs input, rotated so that left is north.

+
+ +
+
+day14.day14.main() None[source]
+

Get input and run question1/question2.

+
+ +
+
+day14.day14.naive_score(world_rows: list[str]) int[source]
+

Returns score assuming west is pointing left.

+

For each row, a round boulder O’s score is +num_rows minus its index (higher weight to the left.)

+
+ +
+
+day14.day14.question1(world: World) int[source]
+

Returns world’s score after rotating the world once.

+
+ +
+
+day14.day14.question2(world: World) int[source]
+

Finds a loop in world rotation.

+

Once the loop is found we can estimate the world’s state +after any amount of rotations

+
+
Parameters:
+

world (World) – world to spin

+
+
Returns:
+

“weight” to the north after 1000000000 cycles.

+
+
Return type:
+

int

+
+
+
+ +
+
+day14.day14.simulate_row(row: list[str]) tuple[list[str], int][source]
+

Simulates a row; returns its value and the new row.

+

Assumes that we are moving boulders to the left

+
+ +
+
+day14.day14.simulate_world(world_rows: list[list[str]]) list[list[str]][source]
+

Simulates world by rolling to the left.

+
+ +
+
+

Module contents

+

Day14 Solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day14.lib.html b/day14.lib.html new file mode 100644 index 0000000..294599a --- /dev/null +++ b/day14.lib.html @@ -0,0 +1,204 @@ + + + + + + + day14.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day14.lib package

+
+

Submodules

+
+
+

day14.lib.direction module

+

Direction class.

+
+
+class day14.lib.direction.Direction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: IntEnum

+

Cardinal directions.

+
+
+East = 3
+
+ +
+
+North = 0
+
+ +
+
+South = 2
+
+ +
+
+West = 1
+
+ +
+
+next_direction_ccw() Direction[source]
+

If we are pointing west, the next direction ccw is south.

+
+ +
+
+next_direction_cw() Direction[source]
+

If we are pointing west, the next direction clockwise is north.

+
+ +
+ +
+
+

Module contents

+

day14 library modules.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day14.tests.html b/day14.tests.html new file mode 100644 index 0000000..cab0ca4 --- /dev/null +++ b/day14.tests.html @@ -0,0 +1,199 @@ + + + + + + + day14.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day14.tests package

+
+

Submodules

+
+
+

day14.tests.test_day14 module

+

Tests for main day14 functions.

+
+
+day14.tests.test_day14.test_get_input() None[source]
+

Test get_input().

+
+ +
+
+day14.tests.test_day14.test_questions() None[source]
+

Test main question functions.

+
+ +
+
+day14.tests.test_day14.test_rotate_world() None[source]
+

Test rotating the world.

+
+ +
+
+day14.tests.test_day14.test_simulate_row() None[source]
+

Test simulation of a row.

+
+ +
+
+

day14.tests.test_direction module

+

Test direction class.

+
+
+day14.tests.test_direction.test_direction() None[source]
+

Test Direction class.

+
+ +
+
+

Module contents

+

Tests for day14.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day15.html b/day15.html new file mode 100644 index 0000000..603cdb6 --- /dev/null +++ b/day15.html @@ -0,0 +1,272 @@ + + + + + + + day15 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day15 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day15.day15 module

+

Day15 solution.

+
+
+day15.day15.get_input(path: str) list[str][source]
+

Get input into list of instructions to parse.

+
+ +
+
+day15.day15.get_string_hash(string: str) int[source]
+

Returns a string’s hash.

+
+ +
+
+day15.day15.main() None[source]
+

Read input and call question1/question2.

+
+ +
+
+day15.day15.parse_step_pt2(raw_step: str) Step[source]
+

Handles as step in part 2.

+
+ +
+
+day15.day15.process_steps_pt2(steps: list[Step]) int[source]
+

Process a list of steps.

+
+ +
+
+day15.day15.question1(raw_steps: list[str]) int[source]
+

Returns the sum of hashes for every step.

+
+ +
+
+day15.day15.question2(raw_steps: list[str]) int[source]
+

Process each step into “lens” boxes and return the total lens power.

+
+ +
+
+

Module contents

+

Day15 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day15.lib.html b/day15.lib.html new file mode 100644 index 0000000..559a5fd --- /dev/null +++ b/day15.lib.html @@ -0,0 +1,263 @@ + + + + + + + day15.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day15.lib package

+
+

Submodules

+
+
+

day15.lib.classes module

+

Classes for day15.

+
+
+class day15.lib.classes.AddRemove(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: IntEnum

+

Simple instruction to add or remove lens.

+
+
+Add = 0
+
+ +
+
+Remove = 1
+
+ +
+ +
+
+class day15.lib.classes.Box(id: int = 0, contents: list[~day15.lib.classes.Lens] = <factory>)[source]
+

Bases: object

+

Box can contain a variety of Lenses.

+
+
+add_lens(lens: Lens) None[source]
+

Add/replace a lens to this box.

+

If a lens name already exists, swap its power; +otherwise just add it

+
+ +
+
+calculate_power() int[source]
+

Calculates power of the box by summing powers of the lenses.

+
+ +
+
+contents: list[Lens]
+
+ +
+
+id: int = 0
+
+ +
+
+remove_lens(lens_name: str) None[source]
+

If a lens with a matching name is inside, remove it.

+
+ +
+ +
+
+class day15.lib.classes.Lens(name: str, focal_length: int)[source]
+

Bases: object

+

Lens object.

+
+
+focal_length: int
+
+ +
+
+name: str
+
+ +
+ +
+
+class day15.lib.classes.Step(lens_name: str, box: int, process: AddRemove, focal_length: int | None = None)[source]
+

Bases: object

+

well defined step.

+
+
+box: int
+
+ +
+
+focal_length: int | None = None
+
+ +
+
+lens_name: str
+
+ +
+
+process: AddRemove
+
+ +
+ +
+
+

Module contents

+

Library modules for classes.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day15.tests.html b/day15.tests.html new file mode 100644 index 0000000..eb403df --- /dev/null +++ b/day15.tests.html @@ -0,0 +1,205 @@ + + + + + + + day15.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day15.tests package

+
+

Submodules

+
+
+

day15.tests.test_classes module

+

Tests classes for day15.

+
+
+day15.tests.test_classes.test_box() None[source]
+

Test box class.

+
+ +
+
+day15.tests.test_classes.test_lens() None[source]
+

Tests lens class.

+
+ +
+
+

day15.tests.test_day15 module

+

Day15 main functions.

+
+
+day15.tests.test_day15.test_get_input() None[source]
+

Test reading input function.

+
+ +
+
+day15.tests.test_day15.test_get_string_hash() None[source]
+

Test string hashing function.

+
+ +
+
+day15.tests.test_day15.test_parse_pt2() None[source]
+

Test parsing input for part2.

+
+ +
+
+day15.tests.test_day15.test_questions() None[source]
+

Test question1/question2.

+
+ +
+
+

Module contents

+

Tests for day15.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day16.html b/day16.html new file mode 100644 index 0000000..d8ecb33 --- /dev/null +++ b/day16.html @@ -0,0 +1,314 @@ + + + + + + + day16 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day16 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day16.day16 module

+

day16 solution.

+
+
+day16.day16.main() None[source]
+

Read input and run part1/part2.

+
+ +
+
+day16.day16.part1(world: World) int[source]
+

Return number of energized tiles.

+
+ +
+
+day16.day16.part2(world: World) int[source]
+

Calculate most energized tiles by firing laser from every entrypoint.

+
+ +
+
+day16.day16.solve_task(task: Laser, world: World) int[source]
+

Calculates number of energized tiles.

+
+ +
+
+day16.day16.solve_task_wrapper(args: tuple[Laser, World]) int[source]
+

Wraps solve_task in multiprocessing, since it only takes one arg.

+
+ +
+
+

Module contents

+

Day16 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day16.lib.html b/day16.lib.html new file mode 100644 index 0000000..7b955f6 --- /dev/null +++ b/day16.lib.html @@ -0,0 +1,412 @@ + + + + + + + day16.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day16.lib package

+
+

Submodules

+
+
+

day16.lib.cells module

+

Cell classes.

+
+
+class day16.lib.cells.BackSlashCell(contents: str)[source]
+

Bases: Cell

+

A \ cell.

+
+
+next_lasers(laser: Laser) list[Laser][source]
+

Lasers go diagonal mode.

+
+ +
+ +
+
+class day16.lib.cells.Cell(contents: str)[source]
+

Bases: ABC

+

Abstract cell class.

+
+
+CELL_TYPES: Dict[str, Type[Cell]] = {'-': <class 'day16.lib.cells.DashCell'>, '.': <class 'day16.lib.cells.DotCell'>, '/': <class 'day16.lib.cells.ForwardSlashCell'>, '\\': <class 'day16.lib.cells.BackSlashCell'>, '|': <class 'day16.lib.cells.PipeCell'>}
+
+ +
+
+static construct(contents: str) Cell[source]
+

Construct proper cell from given contents.

+
+ +
+
+contents: str
+
+ +
+
+abstract next_lasers(laser: Laser) list[Laser][source]
+

Return next lasers given a laser entering this cell.

+
+ +
+
+static register_cell_type(cell_contents: str) Callable[[type[Cell]], type[Cell]][source]
+

Registers a cell type to this factory.

+
+ +
+ +
+
+class day16.lib.cells.DashCell(contents: str)[source]
+

Bases: Cell

+

A - cell.

+
+
+next_lasers(laser: Laser) list[Laser][source]
+

Lasers must end up going east/west after passing through this cell.

+
+ +
+ +
+
+class day16.lib.cells.DotCell(contents: str)[source]
+

Bases: Cell

+

A dot cell.

+
+
+next_lasers(laser: Laser) list[Laser][source]
+

Lasers pass directly through this tile.

+
+ +
+ +
+
+class day16.lib.cells.ForwardSlashCell(contents: str)[source]
+

Bases: Cell

+

A / cell.

+
+
+next_lasers(laser: Laser) list[Laser][source]
+

Lasers go diagonal mode.

+
+ +
+ +
+
+class day16.lib.cells.PipeCell(contents: str)[source]
+

Bases: Cell

+

A | cell.

+
+
+next_lasers(laser: Laser) list[Laser][source]
+

Lasers must end up going north/south after passing through this cell.

+
+ +
+ +
+
+

day16.lib.direction module

+

Direction class.

+
+
+class day16.lib.direction.Direction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: IntEnum

+

Simple direction enum.

+
+
+EAST = 1
+
+ +
+
+NORTH = 0
+
+ +
+
+SOUTH = 2
+
+ +
+
+WEST = 3
+
+ +
+
+offset(row: int, col: int) tuple[int, int][source]
+

Offset a row/col by our direction.

+
+ +
+
+opposite() Direction[source]
+

Returns opposite direction.

+
+ +
+ +
+
+

day16.lib.laser module

+

laser instance class.

+
+
+class day16.lib.laser.Laser(row: int, col: int, direction: Direction)[source]
+

Bases: object

+

Laser position + direction.

+
+
+col: int
+
+ +
+
+direction: Direction
+
+ +
+
+row: int
+
+ +
+ +
+
+

day16.lib.parsers module

+

Parsers for input file.

+
+
+day16.lib.parsers.get_input(path: str) World[source]
+

Read input file and return well formed :class:World.

+
+ +
+
+

day16.lib.world module

+

Well defined world classes.

+
+
+class day16.lib.world.SolvedWorld(num_rows: int, num_cols: int)[source]
+

Bases: object

+

A solved world class, stores how many lasers are in each tile.

+
+
+add_laser(laser: Laser) None[source]
+

Adds laser to cell.

+
+ +
+
+already_solved(laser: Laser) bool[source]
+

Returns true if laser already calculated.

+
+ +
+
+data: list[list[list[Laser]]]
+
+ +
+
+num_energized() int[source]
+

Return number of energized cells.

+
+ +
+ +
+
+class day16.lib.world.World(data: list[list[Cell]])[source]
+

Bases: object

+

The input world (mirrors/empty tiles).

+
+
+data: list[list[Cell]]
+
+ +
+
+is_oob(laser: Laser) bool[source]
+

True if laser is out of bounds.

+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+
+solve(start_laser: Laser) SolvedWorld[source]
+

Solve our world.

+
+ +
+ +
+
+

Module contents

+

library modules for day16.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day16.tests.html b/day16.tests.html new file mode 100644 index 0000000..afc9f57 --- /dev/null +++ b/day16.tests.html @@ -0,0 +1,237 @@ + + + + + + + day16.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day16.tests package

+
+

Submodules

+
+
+

day16.tests.test_cells module

+

Tests for each cell.

+
+
+day16.tests.test_cells.test_backslashcell() None[source]
+

Test BackSlashCell.

+
+ +
+
+day16.tests.test_cells.test_cell() None[source]
+

Tests Cell.construct().

+
+ +
+
+day16.tests.test_cells.test_dashcell() None[source]
+

Test DashCell.

+
+ +
+
+day16.tests.test_cells.test_dotcell() None[source]
+

Test DotCell.

+
+ +
+
+day16.tests.test_cells.test_forwardslashcell() None[source]
+

Test ForwardSlashCell.

+
+ +
+
+day16.tests.test_cells.test_pipecell() None[source]
+

Test PipeCell.

+
+ +
+
+

day16.tests.test_day16 module

+

Test day16 main functions.

+
+
+day16.tests.test_day16.test_part1() None[source]
+

Test part1.

+
+ +
+
+day16.tests.test_day16.test_part2() None[source]
+

Test part2.

+
+ +
+
+

day16.tests.test_direction module

+

Test direction.

+
+
+day16.tests.test_direction.test_direction() None[source]
+

Test Direction class.

+
+ +
+
+

day16.tests.test_world module

+

Test World class.

+
+
+day16.tests.test_world.test_world() None[source]
+

Test World class.

+
+ +
+
+

Module contents

+

Tests for day16.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day17.html b/day17.html new file mode 100644 index 0000000..1da2b30 --- /dev/null +++ b/day17.html @@ -0,0 +1,279 @@ + + + + + + + day17 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day17 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day17.day17 module

+

day17 solution.

+
+
+day17.day17.main() None[source]
+

Load input and run part1/part2.

+
+ +
+
+day17.day17.part1(input: list[list[int]]) int[source]
+

Load input and solve part1.

+

(minimum path, max 3 in one direction)

+
+ +
+
+day17.day17.part2(input: list[list[int]]) int[source]
+

Load input and solve part2.

+

(minimum path, min 4, max 10 in one direction)

+
+ +
+
+day17.day17.solve_and_print(world: WorldPart1) int[source]
+

Solve and print.

+
+ +
+
+

Module contents

+

Day17 Solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day17.lib.html b/day17.lib.html new file mode 100644 index 0000000..ba11444 --- /dev/null +++ b/day17.lib.html @@ -0,0 +1,379 @@ + + + + + + + day17.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day17.lib package

+
+

Submodules

+
+
+

day17.lib.classes module

+

classes for day 17.

+
+
+class day17.lib.classes.SolutionCache(num_rows: int, num_cols: int, cache_min: int, cache_max: int)[source]
+

Bases: object

+

2d array of tilecaches.

+
+
+add_solution(step: Step) bool[source]
+

Adds solution to cache returns whether an improvement was made.

+
+ +
+
+cache: list[list[TileCache]]
+
+ +
+ +
+
+class day17.lib.classes.Step(total_cost: int, row: int, col: int, direction: Direction, consecutive_steps: int, src_step: Step | None)[source]
+

Bases: object

+

Represents one “step”, which could be a multi-step.

+
+
+col: int
+
+ +
+
+consecutive_steps: int
+
+ +
+
+direction: Direction
+
+ +
+
+row: int
+
+ +
+
+src_step: Step | None
+
+ +
+
+total_cost: int
+
+ +
+ +
+
+class day17.lib.classes.TileCache(cache_min: int, cache_max: int)[source]
+

Bases: object

+

A cache of shortest routes to a tile from each direction.

+
+
+cache: dict[Direction, list[Step | None]]
+
+ +
+
+cache_max: int
+
+ +
+
+cache_min: int
+
+ +
+ +
+
+class day17.lib.classes.WorldPart1(costs: list[list[int]])[source]
+

Bases: object

+

World for part1.

+
+
+costs: list[list[int]]
+
+ +
+
+create_step(step: Step, direction: Direction) Step | None[source]
+

Create step from previous step and a given direction.

+

Returns None if the step is invalid or suboptimal

+
+ +
+
+is_oob(row: int, col: int) bool[source]
+

Returns if we are out of bounds.

+
+
Parameters:
+
    +
  • row (int) – row to check

  • +
  • col (int) – col to check

  • +
+
+
Returns:
+

if we are out of bounds.

+
+
Return type:
+

bool

+
+
+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+
+solve() Step[source]
+

Solve using dynamic programming.

+

Returns final step which contains src steps; +so we have the entire path

+
+ +
+ +
+
+class day17.lib.classes.WorldPart2(costs: list[list[int]])[source]
+

Bases: WorldPart1

+

Extension of part1 with a few overrides.

+
+
+create_step(step: Step, direction: Direction) Step | None[source]
+

Create step from previous step and a given direction.

+
+ +
+
+solve() Step[source]
+

Solve using DP.

+

Returns final step which contains src steps; +so we have the entire path

+
+ +
+ +
+
+

day17.lib.direction module

+

Direction class.

+
+
+class day17.lib.direction.Direction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: IntEnum

+

Simple direction enum.

+
+
+EAST = 1
+
+ +
+
+NORTH = 0
+
+ +
+
+SOUTH = 2
+
+ +
+
+WEST = 3
+
+ +
+
+offset(row: int, col: int) tuple[int, int][source]
+

Offset a position by our direction.

+
+ +
+
+offset_list(row: int, col: int, size: int = 4) list[tuple[int, int]][source]
+

Offset a position by our direction and size vector.

+
+ +
+
+opposite() Direction[source]
+

Return opposite direction W``<->``E S``<->``N.

+
+ +
+ +
+
+

day17.lib.parsers module

+

parse input file.

+
+
+day17.lib.parsers.get_input(path: str) list[list[int]][source]
+

Convert input into world dataclass.

+
+ +
+
+

Module contents

+

library modules for day 17.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day17.tests.html b/day17.tests.html new file mode 100644 index 0000000..cfe96ca --- /dev/null +++ b/day17.tests.html @@ -0,0 +1,191 @@ + + + + + + + day17.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day17.tests package

+
+

Submodules

+
+
+

day17.tests.test_day17 module

+

Test main functions for day17.

+
+
+day17.tests.test_day17.test_parts() None[source]
+

Test part1() and part2.

+
+ +
+
+

day17.tests.test_direction module

+

Test direction class.

+
+
+day17.tests.test_direction.test_direction() None[source]
+

Test Direction class.

+
+ +
+
+

day17.tests.test_parsers module

+

Test parsing function.

+
+
+day17.tests.test_parsers.test_parser() None[source]
+

Test parsing function.

+
+ +
+
+

Module contents

+

Tests for day 17.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day18.html b/day18.html new file mode 100644 index 0000000..d52e79c --- /dev/null +++ b/day18.html @@ -0,0 +1,544 @@ + + + + + + + day18 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day18 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day18.day18a module

+

day18 solution.

+
+
+class day18.day18a.Command(direction: Direction, steps: int, color: str)[source]
+

Bases: object

+

Well defined command dataclass.

+
+
+color: str
+
+ +
+
+direction: Direction
+
+ +
+
+steps: int
+
+ +
+ +
+
+class day18.day18a.Direction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: StrEnum

+

Cardinal direction in UDLR.

+
+
+Down = 'D'
+
+ +
+
+Left = 'L'
+
+ +
+
+Right = 'R'
+
+ +
+
+Up = 'U'
+
+ +
+ +
+
+class day18.day18a.Matrix(min_pos: Position, max_pos: Position)[source]
+

Bases: object

+

2d array representing world.

+
+
+contents: list[list[Tile]]
+
+ +
+
+dig_out() None[source]
+

Dig out non-perimeter tiles using flood-fill.

+
+ +
+
+dug_tiles = 0
+
+ +
+
+is_oob(position: Position) bool[source]
+

Returns if a position is out of bounds.

+
+
Parameters:
+

position (Position) – position to check.

+
+
Returns:
+

True if out of bounds.

+
+
Return type:
+

bool

+
+
+
+ +
+
+max_pos: Position
+
+ +
+
+min_pos: Position
+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+
+process_command(miner_pos: Position, command: Command) Position[source]
+

Process command.

+

This moves the miner around.

+
+
Parameters:
+
    +
  • miner_pos (Position) – miner’s current position

  • +
  • command (Command) – command to process

  • +
+
+
Returns:
+

miner’s new position.

+
+
Return type:
+

Position

+
+
+
+ +
+
+wall_tiles = 0
+
+ +
+ +
+
+class day18.day18a.Position(row: int = 0, col: int = 0)[source]
+

Bases: object

+

Simple 2d point.

+
+
+col: int = 0
+
+ +
+
+row: int = 0
+
+ +
+ +
+
+day18.day18a.generate_offsets(position: Position, direction: Direction, steps: int) list[Position][source]
+

Generate position offsets.

+
+
Parameters:
+
    +
  • position (Position) – base position

  • +
  • direction (Direction) – direction of travel

  • +
  • steps (int) – number of steps to generate

  • +
+
+
Raises:
+

AssertionError – If direciton is invalid.

+
+
Returns:
+

list of new positions.

+
+
Return type:
+

list[Position]

+
+
+
+ +
+
+day18.day18a.get_input(path: str) list[Command][source]
+

Reads input from file into well-formed list of commands.

+
+ +
+
+day18.day18a.get_matrix_range(commands: list[Command]) tuple[Position, Position][source]
+

Calculate minimum and maximum position in matrix.

+

Since we start in the middle somewhere, we get negative positions. +This can be useds to offset the matrix when we construct it.

+
+
Parameters:
+

commands (list[Command]) – list of commands that will be run.

+
+
Returns:
+

[min,max] positions.

+
+
Return type:
+

tuple[Position, Position]

+
+
+
+ +
+
+day18.day18a.get_solution(commands: list[Command]) int[source]
+

Calculates solution.

+
    +
  1. Pre-calculates the range

  2. +
  3. Creates edge tiles

  4. +
  5. Flood fill centre.

  6. +
  7. Count tiles.

  8. +
+
+ +
+
+day18.day18a.main() None[source]
+

Load data and then find solution to part1.

+
+ +
+
+

day18.day18b module

+

Day18b solution.

+
+
+class day18.day18b.Command(hexcode: str)[source]
+

Bases: object

+

Command from hexstring.

+
+
+direction: Direction
+
+ +
+
+steps: int
+
+ +
+ +
+
+class day18.day18b.Direction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: IntEnum

+

Direction as an integer enum.

+
+
+Down = 1
+
+ +
+
+Left = 2
+
+ +
+
+Right = 0
+
+ +
+
+Up = 3
+
+ +
+ +
+
+class day18.day18b.Position(row: int = 0, col: int = 0)[source]
+

Bases: object

+

Simple 2d vector.

+
+
+col: int = 0
+
+ +
+
+row: int = 0
+
+ +
+ +
+
+day18.day18b.calculate_area(positions: list[Position], perimeter: int) int[source]
+

Calculate area using shoelace area.

+
+ +
+
+day18.day18b.get_input(path: str) list[Command][source]
+

Grabs input from file, parsing into well-formed commands.

+
+ +
+
+day18.day18b.get_solution(commands: list[Command]) int[source]
+

Get solution via processing commands then running shoelace area.

+
+ +
+
+day18.day18b.main() None[source]
+

Grab input and then pass it into solver.

+
+ +
+
+day18.day18b.process_command(command: Command, position: Position) Position[source]
+

Process a command and return new position.

+
+ +
+
+

Module contents

+

Day18 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day18.lib.html b/day18.lib.html new file mode 100644 index 0000000..aa8ed55 --- /dev/null +++ b/day18.lib.html @@ -0,0 +1,218 @@ + + + + + + + day18.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day18.lib package

+
+

Submodules

+
+
+

day18.lib.tile module

+

Tile Class.

+
+
+class day18.lib.tile.EdgeTile(*, contents: str = '#', color: str)[source]
+

Bases: Tile

+

Edge tile (#).

+
+
+TEXT_WHITE = '\x1b[38;2;255;255;255m'
+
+ +
+
+color: str
+
+ +
+
+contents: str = '#'
+
+ +
+
+text_color(r: int, g: int, b: int) str[source]
+

Return ansicode color of edge based on input.

+
+ +
+ +
+
+class day18.lib.tile.HoleTile(*, contents: str = ' ')[source]
+

Bases: Tile

+

Dug out tile.

+
+
+contents: str = ' '
+
+ +
+ +
+
+class day18.lib.tile.Tile(contents: str = '.')[source]
+

Bases: object

+

Tile for part 1, represents a non-dugout tile.

+
+
+contents: str = '.'
+
+ +
+ +
+
+

Module contents

+

day18 library modules.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day18.tests.html b/day18.tests.html new file mode 100644 index 0000000..d388a86 --- /dev/null +++ b/day18.tests.html @@ -0,0 +1,188 @@ + + + + + + + day18.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day18.tests package

+
+

Submodules

+
+
+

day18.tests.test_day18a module

+

test day18a main function.

+
+
+day18.tests.test_day18a.test_day18a() None[source]
+

Test day18a.

+
+ +
+
+

day18.tests.test_day18b module

+

test day18b main functions.

+
+
+day18.tests.test_day18b.test_command() None[source]
+

Test hex code conversion.

+
+ +
+
+day18.tests.test_day18b.test_day18b() None[source]
+

Test day18b main function.

+
+ +
+
+

Module contents

+

day18 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day19.html b/day19.html new file mode 100644 index 0000000..d30555d --- /dev/null +++ b/day19.html @@ -0,0 +1,350 @@ + + + + + + + day19 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day19 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day19.day19 module

+

Day19 solution.

+
+
+day19.day19.get_input(path: str) tuple[list[Workflow], list[Part]][source]
+

Open file and parse.

+

Returns well formed workflows and parts classes.

+
+
Parameters:
+

path (str) – filepath for data

+
+
Returns:
+

list of workflows and parts.

+
+
Return type:
+

tuple[list[Workflow], list[Part]]

+
+
+
+ +
+
+day19.day19.main() None[source]
+

Load input file and run part1/part2.

+
+ +
+
+day19.day19.part1(workflows: list[Workflow], parts: list[Part]) int[source]
+

Solve part1.

+
+ +
+
+day19.day19.part2(workflows: list[Workflow]) int[source]
+

Solve part2.

+
+ +
+
+day19.day19.process_part(workflows: dict[str, Workflow], part: Part) int[source]
+

Processes a part.

+

Returns the part rating (or 0 if rejected)

+
+
Parameters:
+
    +
  • workflows (dict[str, Workflow]) – list of workflows.

  • +
  • part (Part) – part to process

  • +
+
+
Returns:
+

value of part (or 0 if rejected)

+
+
Return type:
+

int

+
+
+
+ +
+
+day19.day19.solve_part2(workflows: dict[str, Workflow]) int[source]
+

Solve part2.

+

Assumes xmas values from 0 <= xmas <= 4000. +Returns total number of parts that pass.

+
+
Parameters:
+

workflows (dict[str, Workflow]) – workflows to test

+
+
Returns:
+

total number of parts that pass.

+
+
Return type:
+

int

+
+
+
+ +
+
+

Module contents

+

Day19 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day19.lib.html b/day19.lib.html new file mode 100644 index 0000000..57f9b50 --- /dev/null +++ b/day19.lib.html @@ -0,0 +1,486 @@ + + + + + + + day19.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day19.lib package

+
+

Submodules

+
+
+

day19.lib.classes module

+

well defined classes for day19.

+
+
+class day19.lib.classes.Comparator(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: StrEnum

+

Well defined comparators < and >.

+
+
+GreaterThan = '>'
+
+ +
+
+LessThan = '<'
+
+ +
+ +
+
+class day19.lib.classes.Component(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: StrEnum

+

A well defined component inside a part.

+
+
+A = 'a'
+
+ +
+
+M = 'm'
+
+ +
+
+S = 's'
+
+ +
+
+X = 'x'
+
+ +
+ +
+
+class day19.lib.classes.Condition(component: Component, sign: Comparator, value: int)[source]
+

Bases: object

+

A condition for a part to succeed/fail.

+
+
+component: Component
+
+ +
+
+process_part(part: Part) bool[source]
+

Checks a part to see if it matches our condition.

+
+
Parameters:
+

part (Part) – part to check

+
+
Raises:
+

AssertionError – if component/sign are unsupported

+
+
Returns:
+

True if the part passes our condition.

+
+
Return type:
+

bool

+
+
+
+ +
+
+process_part_range(part_range: PartRange) tuple[PartRange | None, PartRange | None][source]
+

Splits a part range based on success/fail.

+
+
Parameters:
+

part_range (PartRange) – Partrange to check.

+
+
Raises:
+

AssertionError – If we have an unknown comparator

+
+
Returns:
+

successful part range, failed partrange

+
+
Return type:
+

tuple[Optional[PartRange], Optional[PartRange]]

+
+
+
+ +
+
+sign: Comparator
+
+ +
+
+value: int
+
+ +
+ +
+
+class day19.lib.classes.Part(x: int, m: int, a: int, s: int)[source]
+

Bases: object

+

Well defined part with x,m,a,s values.

+
+
+a: int
+
+ +
+
+clone_modify(component: Component, value: int) Part[source]
+

Clones this part and modifies one component.

+
+
Parameters:
+
    +
  • component (Component) – component to change

  • +
  • value (int) – new value of component

  • +
+
+
Returns:
+

clone of this part with one component changed.

+
+
Return type:
+

Part

+
+
+
+ +
+
+get_value(component: Component) int[source]
+

Returns value of a component inside this part.

+
+ +
+
+m: int
+
+ +
+
+property rating: int
+

Returns rating of part (sum of xmas).

+
+ +
+
+s: int
+
+ +
+
+x: int
+
+ +
+ +
+
+class day19.lib.classes.PartRange(min_values: Part, max_values: Part)[source]
+

Bases: object

+

A range of parts (min/max) based on component values.

+
+
+max_values: Part
+
+ +
+
+min_values: Part
+
+ +
+
+size() int[source]
+

Returns the size of the partrange.

+
+ +
+
+split(component: Component, split_value: int) tuple[PartRange | None, PartRange | None][source]
+

Split a partrange in two, using a chosen component and splitvalue.

+

In the case that our range falls on one whole side, we return None. +E.g. +range = 0-100; split == 200 -> return [(0-100), None] +range = 100-200; split == 50 -> return [None, (100-200)] +range = 100-200, split == 150 -> return [(100-150), (150-200)]

+
+ +
+ +
+
+class day19.lib.classes.PartRangeDest(part_range: PartRange, destination: str)[source]
+

Bases: object

+

Combinatoin of partrange and a destination workflow.

+
+
+destination: str
+
+ +
+
+part_range: PartRange
+
+ +
+ +
+
+class day19.lib.classes.Rule(destination: str, condition: Condition | None = None)[source]
+

Bases: object

+

A Rule consists of a condition + destination.

+
+
+condition: Condition | None = None
+
+ +
+
+destination: str
+
+ +
+
+process_part(part: Part) str | None[source]
+

Processes a part.

+

Returns next workflow if successful, +or None if we failed this rule

+
+ +
+
+process_part_range(part_range: PartRange) tuple[PartRangeDest | None, PartRange | None][source]
+

Processes a PartRange.

+

Returns next workflow and partrange for succeeding parts. +Returns the remainder partrange that failed.

+
+
Parameters:
+

part_range (PartRange) – base partrange.

+
+
Returns:
+

success, fail

+
+
Return type:
+

tuple[Optional[PartRangeDest], Optional[PartRange]]

+
+
+
+ +
+ +
+
+class day19.lib.classes.Workflow(name: str, rules: list[Rule])[source]
+

Bases: object

+

The name of the workflow + a bunch of rules for parts to follow.

+
+
+name: str
+
+ +
+
+process_part(part: Part) str[source]
+

Processes a part, returns the next workflow.

+
+ +
+
+process_part_range(part_range: PartRange) list[PartRangeDest][source]
+

Follow rule list, splitting off PartRanges.

+

Each success has to branch off. +Each failure continues down the chain.

+
+ +
+
+rules: list[Rule]
+
+ +
+ +
+
+

day19.lib.parsers module

+

functions to create classes from pure text.

+
+
+day19.lib.parsers.parse_condition_string(cond_string: str) Condition[source]
+

e.g. a<2006.

+
+ +
+
+day19.lib.parsers.parse_part_string(part_string: str) Part[source]
+

Returns a part from a string representation.

+

e.g. {x=787,m=2655,a=1222,s=2876}\n

+
+ +
+
+day19.lib.parsers.parse_rule_string(rule_string: str) Rule[source]
+

e.g. a<2006:qkq or rfg.

+
+ +
+
+day19.lib.parsers.parse_workflow_string(workflow_string: str) Workflow[source]
+

Returns a workflow from a string representation.

+

e.g. px{a<2006:qkq,m>2090:A,rfg}\n

+
+ +
+
+

Module contents

+

Library modules for part19.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day19.tests.html b/day19.tests.html new file mode 100644 index 0000000..7ce49d0 --- /dev/null +++ b/day19.tests.html @@ -0,0 +1,233 @@ + + + + + + + day19.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day19.tests package

+
+

Submodules

+
+
+

day19.tests.test_classes module

+

Tests for day19 classes.

+
+
+day19.tests.test_classes.get_part_range() tuple[Part, Part][source]
+

Returns a reusable partrange for our tests.

+
+ +
+
+day19.tests.test_classes.test_part_range() None[source]
+

Test PartRange class.

+
+ +
+
+day19.tests.test_classes.test_part_range_dest() None[source]
+

Test PartRangeDest class.

+
+ +
+
+day19.tests.test_classes.test_rule() None[source]
+

Test Rule class.

+
+ +
+
+day19.tests.test_classes.test_workflow() None[source]
+

Test Workflow class.

+
+ +
+
+

day19.tests.test_day19 module

+

Test day19 main classes.

+
+
+day19.tests.test_day19.test_day19() None[source]
+

Test part1() and part2().

+
+ +
+
+

day19.tests.test_parsers module

+

Test lib.parsers.

+
+
+day19.tests.test_parsers.test_parse_condition_string() None[source]
+

E.g. a<2006.

+
+ +
+
+day19.tests.test_parsers.test_parse_part_string() None[source]
+

E.g: {x=787,m=2655,a=1222,s=2876}\n.

+
+ +
+
+day19.tests.test_parsers.test_parse_rule_string() None[source]
+

E.g: a<2006:qkq or rfg.

+
+ +
+
+day19.tests.test_parsers.test_parse_workflow_string() None[source]
+

E.g: px{a<2006:qkq,m>2090:A,rfg}\n.

+
+ +
+
+

Module contents

+

Tests for day19.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day20.html b/day20.html new file mode 100644 index 0000000..e5cc3d6 --- /dev/null +++ b/day20.html @@ -0,0 +1,374 @@ + + + + + + + day20 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day20 package

+
+

Subpackages

+
+ +
+
+
+

Submodules

+
+
+

day20.day20 module

+

Day20 solution.

+
+
+day20.day20.export_graph(dots: list[Graph], module_groups: ModuleGroups, simulation_counter: int, export_graphs: bool) None[source]
+

Export a graphviz datatype if graphing is enabled.

+
+ +
+
+day20.day20.get_loop_paths(start_switch: str, module_map: dict[str, BaseModule]) list[BaseModule][source]
+

Given a start path, returns the longest path until we hit a conjunction module.

+

It should be n FlipFlops and then a single conjunction

+
+ +
+
+day20.day20.get_module_groups(module_map: dict[str, BaseModule]) ModuleGroups[source]
+

Splits the modules into their respective pipelines.

+
+ +
+
+day20.day20.get_typed_module(module_map: dict[str, BaseModule], key: str, module_type: Type[T]) T[source]
+

Typecast a module.

+
+ +
+
+day20.day20.graph_modules(module_groups: ModuleGroups, index: int) Digraph[source]
+

Graphs the modules.

+
+ +
+
+day20.day20.main() None[source]
+

Loads data from file then runs part1/part2.

+
+ +
+
+day20.day20.output_files(dots: list[Graph], directory: str) None[source]
+

Saves a list of dots to file.

+
+ +
+
+day20.day20.output_graph(dot: Graph, directory: str) None[source]
+

Saves a dot to file.

+
+ +
+
+day20.day20.output_graph_wrapper(args: tuple[Graph, str]) None[source]
+

Since process_map doesnt support star_args, we gotta use this.

+
+ +
+
+day20.day20.part1(modules: list[BaseModule]) int[source]
+

Counts low/high count for each module.

+
+ +
+
+day20.day20.part2(modules: list[BaseModule], export_graphs: bool = False) tuple[int, list[Graph]][source]
+

We find out the loop length for each of the 4~ paths.

+
+ +
+
+day20.day20.path_is_start_state(modules: list[BaseModule]) bool[source]
+

For every module in the path, make sure its in its “initial” state.

+
+ +
+
+day20.day20.simulate(modules: dict[str, BaseModule], stored_pulses: list[PulseTarget] | None = None) tuple[int, int][source]
+

Simulate a list of modules.

+

If you pass in stored_pulses, we will append every pulse to it

+
+ +
+
+

Module contents

+

Day20 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day20.lib.html b/day20.lib.html new file mode 100644 index 0000000..4e25b7a --- /dev/null +++ b/day20.lib.html @@ -0,0 +1,515 @@ + + + + + + + day20.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day20.lib package

+
+

Submodules

+
+
+

day20.lib.classes module

+

Classes for day20.

+
+
+class day20.lib.classes.BaseModule(name: str, outputs: list[str])[source]
+

Bases: ABC

+

Abstract base module.

+
+
+add_to_graph(dot: Digraph) None[source]
+

Adds edges only to the graph. inheritors need to handle their repr.

+
+ +
+
+arrow_color() str[source]
+

Return arrow color for graphviz.

+
+ +
+
+handle_pulse(input: str, pulse: Pulse) list[PulseTarget][source]
+

Keep track of lows/highs through all modules.

+
+ +
+
+abstract is_initial_state() bool[source]
+

Returns if the module is in the initial state.

+
+ +
+
+name: str
+
+ +
+
+num_high: int = 0
+
+ +
+
+num_low: int = 0
+
+ +
+
+outputs: list[str]
+
+ +
+ +
+
+class day20.lib.classes.BroadcastModule(name: str, outputs: list[str])[source]
+

Bases: BaseModule

+

Broadcasts to all outputs.

+
+
+add_to_graph(dot: Digraph) None[source]
+

Add node to graphviz digraph.

+
+ +
+
+handle_pulse(input: str, pulse: Pulse) list[PulseTarget][source]
+

Broadcasts to all outputs immediately.

+
+ +
+
+is_initial_state() bool[source]
+

Always true.

+
+ +
+ +
+
+class day20.lib.classes.ConjunctionModule(name: str, outputs: list[str])[source]
+

Bases: BaseModule

+

Keeps track of all inputs.

+

Changes internal state, then sends high/low based on internal state

+
+
+add_to_graph(dot: Digraph) None[source]
+

Add this module to a GraphViz Digraph.

+
+ +
+
+arrow_color() str[source]
+

Returns red.

+
+ +
+
+current_count() int[source]
+

Returns current count of inputs that sent high.

+
+ +
+
+handle_pulse(input: str, pulse: Pulse) list[PulseTarget][source]
+

Store pulse, then send based on current state.

+

If all our values are high, we send low to all our outputs. +Otherwise, we send LOW to all our outputs.

+
+ +
+
+inputs: dict[str, Pulse]
+
+ +
+
+is_initial_state() bool[source]
+

Returns True if all our inputs are LOW.

+
+ +
+
+set_inputs(inputs: list[str]) None[source]
+

Sets our list of input modules.

+

Initializes their values to Low.

+
+ +
+ +
+
+class day20.lib.classes.FlipFlopModule(name: str, outputs: list[str], state: Pulse = Pulse.LOW)[source]
+

Bases: BaseModule

+

If we receive HIGH, we are a sink (do nothing).

+

If we receive LOW, flip our current value and send it to everyone

+
+
+add_to_graph(dot: Digraph) None[source]
+

Adds ourselves to a graphviz digraph.

+
+ +
+
+handle_pulse(input: str, pulse: Pulse) list[PulseTarget][source]
+

Handle pulse by forwarding if we receive low.

+
+ +
+
+is_initial_state() bool[source]
+

Returns true if we are in our initial state.

+
+ +
+
+state: Pulse = False
+
+ +
+ +
+
+class day20.lib.classes.LoopCounter(target_loop_count: int)[source]
+

Bases: object

+

Keeps track of loop lengths.

+
+
+add_result(loop_name: str, value: int) None[source]
+

Adds a result to our loop_count.

+

If we already had a loop with that name, we ignore it.

+
+ +
+
+property finished: bool
+

Returns True if our loop_lengths are equal to our target.

+
+ +
+
+loop_lengths: dict[str, int]
+
+ +
+
+property num_results: int
+

Returns number of loop_lenghts submitted.

+
+ +
+
+target_loop_count: int
+
+ +
+ +
+
+class day20.lib.classes.MappingModule(name: str, outputs: list[str])[source]
+

Bases: object

+

map to a list of outputs.

+
+
+name: str
+
+ +
+
+outputs: list[str]
+
+ +
+ +
+
+class day20.lib.classes.ModuleGroups(head: BroadcastModule, loops: list[list[BaseModule]], loop_tails: list[ConjunctionModule], penultimate: ConjunctionModule, sink: SinkModule)[source]
+

Bases: object

+

A group of modules for part2.

+
+
+all_nodes: list[BaseModule]
+
+ +
+
+head: BroadcastModule
+
+ +
+
+loop_tails: list[ConjunctionModule]
+
+ +
+
+loops: list[list[BaseModule]]
+
+ +
+
+penultimate: ConjunctionModule
+
+ +
+
+sink: SinkModule
+
+ +
+ +
+
+class day20.lib.classes.Pulse(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: Flag

+

Simple True/False enum.

+
+
+HIGH = True
+
+ +
+
+LOW = False
+
+ +
+ +
+
+class day20.lib.classes.PulseTarget(pulse: Pulse, src: str, target: str)[source]
+

Bases: object

+

A pulse(low/high) from src to dest.

+
+
+pulse: Pulse
+
+ +
+
+src: str
+
+ +
+
+target: str
+
+ +
+ +
+
+class day20.lib.classes.SinkModule(name: str, outputs: list[str])[source]
+

Bases: BaseModule

+

Sink module, gets something but sends it no where else.

+
+
+add_to_graph(dot: Digraph) None[source]
+

Adds this node to the graph.

+
+ +
+
+handle_pulse(input: str, pulse: Pulse) list[PulseTarget][source]
+

Always eats inputs and never sends onwards.

+
+ +
+
+is_initial_state() bool[source]
+

Always true.

+
+ +
+ +
+
+

day20.lib.parsers module

+

Day20 parsers.

+
+
+day20.lib.parsers.finalize_modules(modules: list[BaseModule]) list[BaseModule][source]
+

Finalize construction of all modules.

+

For each module, calculate its inputs. +Then inject the inputs into our conjunction modules +Modifies modules inplace, and returns it

+
+ +
+
+day20.lib.parsers.get_modules(filename: str) list[BaseModule][source]
+

Opens a file and returns all the modules.

+
+
Parameters:
+

filename (str) – name of file to open

+
+
Returns:
+

list of modules.

+
+
Return type:
+

list[BaseModule]

+
+
+
+ +
+
+day20.lib.parsers.parse_line(line: str) BaseModule[source]
+

Parses a line into a BaseModule.

+

e.g. %a -> inv, con.

+
+ +
+
+

Module contents

+

Library modules for day20.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day20.tests.html b/day20.tests.html new file mode 100644 index 0000000..d578104 --- /dev/null +++ b/day20.tests.html @@ -0,0 +1,215 @@ + + + + + + + day20.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day20.tests package

+
+

Submodules

+
+
+

day20.tests.test_classes module

+

Tests for day20 classes.

+
+
+day20.tests.test_classes.test_loop_counter() None[source]
+

Test LoopCounter class.

+
+ +
+
+day20.tests.test_classes.test_modules() None[source]
+

Ensure that sink/broadcast module are always in default state.

+
+ +
+
+

day20.tests.test_day20 module

+

Test day20 main functions.

+
+
+day20.tests.test_day20.test_day20() None[source]
+

Test day20 main functions.

+
+ +
+
+day20.tests.test_day20.test_part2() None[source]
+

Test part2().

+
+ +
+
+

day20.tests.test_parsers module

+

Test parsing code.

+
+
+day20.tests.test_parsers.test_finalize_modules() None[source]
+

Test finalize_modules().

+
+ +
+
+day20.tests.test_parsers.test_get_modules() None[source]
+

Test get_modules.

+
+ +
+
+day20.tests.test_parsers.test_parse_line() None[source]
+

Test parse_line().

+
+ +
+
+

Module contents

+

Tests for day20.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day21.html b/day21.html new file mode 100644 index 0000000..50def09 --- /dev/null +++ b/day21.html @@ -0,0 +1,322 @@ + + + + + + + day21 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day21 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day21.day21 module

+

day21 solution.

+
+
+class day21.day21.SmartSteps(boards_to_edge: int, steps: int)[source]
+

Bases: object

+

How many boards to edge of solution, and steps to simulate.

+
+
+boards_to_edge: int
+
+ +
+
+steps: int
+
+ +
+ +
+
+day21.day21.calculate_smart_steps(board_size: int, steps: int) SmartSteps[source]
+

Given a board size and num steps, calculate how many steps we actually need.

+
+ +
+
+day21.day21.main() None[source]
+

Load data then solve part1/part2.

+
+ +
+
+day21.day21.mini_solve(start_pos: Position, maze: Maze, steps: int, distances: BaseDistanceMaze) BaseDistanceMaze[source]
+

Given a BaseDistanceMaze, runs steps steps then returns the maze.

+
+ +
+
+day21.day21.naive_solve(start_pos: Position, maze: Maze, steps: int, distances: BaseDistanceMaze) int[source]
+

Naively solve a maze.

+
+ +
+
+day21.day21.solve(start_pos: Position, maze: Maze, steps: int, unlimited_map: bool = False, brute_force: bool = False) int[source]
+

Solve the maze.

+
+
Parameters:
+
    +
  • start_pos (Position) – start position

  • +
  • maze (Maze) – maze to solve

  • +
  • steps (int) – number of steps

  • +
  • unlimited_map (bool, optional) – whether the map is infinite (Default=False)

  • +
  • brute_force (bool, optional) – Whether to brute force. (Defaults=False).

  • +
+
+
Returns:
+

number of valid positions.

+
+
Return type:
+

int

+
+
+
+ +
+
+

Module contents

+

Day21 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day21.lib.html b/day21.lib.html new file mode 100644 index 0000000..16d9790 --- /dev/null +++ b/day21.lib.html @@ -0,0 +1,489 @@ + + + + + + + day21.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day21.lib package

+
+

Submodules

+
+
+

day21.lib.classes module

+

classes for day21.

+
+
+class day21.lib.classes.BaseDistanceMaze[source]
+

Bases: ABC

+

Abstract distance maze.

+
+
+abstract calc_steps(remainder: int) int[source]
+

Calculate steps.

+

Matches remainder == 1 or remainder == 0 +When modulo’ing by 2

+
+ +
+
+abstract overlay(maze: Maze) str[source]
+

Overlays on top of a maze.

+
+ +
+ +
+
+class day21.lib.classes.DistanceMaze(num_rows: int, num_cols: int)[source]
+

Bases: BaseDistanceMaze

+

Distance Maze == Maze.size.

+
+
+calc_steps(remainder: int) int[source]
+

Calculate steps, based on odd/even steps.

+
+ +
+
+centre_cell(row: int, col: int) bool[source]
+

Returns true if coordinate is the centre cell of the maze.

+
+ +
+
+grid: list[list[int]]
+
+ +
+
+int_to_str(value: int) str[source]
+

Convert integert to string.

+
+ +
+
+is_complete() bool[source]
+

Returns true if all cells are filled.

+
+ +
+
+is_oob(position: Position) bool[source]
+

True if position is out of bounds.

+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+
+overlay(maze: Maze) str[source]
+

Overlay this distance_maze on a maze.

+
+ +
+ +
+
+class day21.lib.classes.DistanceMazes(num_rows: int, num_cols: int)[source]
+

Bases: BaseDistanceMaze

+

An array of distance mazes, able to extend infinitely.

+
+
+calc_steps(remainder: int) int[source]
+

Calculate steps given parity.

+
+ +
+
+cols_per_maze: int
+
+ +
+
+get_big_grid(position: Position) DistanceMaze[source]
+

Big grid coordinate.

+
+ +
+
+get_split_pos(position: Position) tuple[Position, Position][source]
+

Split global position.

+

Into big map and small map positions.

+
+
Parameters:
+

position (Position) – global position

+
+
Returns:
+

big coord, small coord

+
+
Return type:
+

tuple[Position, Position]

+
+
+
+ +
+
+grid: dict[Position, DistanceMaze]
+
+ +
+
+overlay(maze: Maze) str[source]
+

Overlay our gigamap onto a maze.

+
+ +
+
+rows_per_maze: int
+
+ +
+ +
+
+class day21.lib.classes.GiantNodeParser(distance_mazes: DistanceMazes, nodes_to_edge: int)[source]
+

Bases: object

+

Convert from mazes to giant nodes.

+
+
+distance_mazes: DistanceMazes
+
+ +
+
+edge_dist: int
+
+ +
+
+full_edge_dist: int
+
+ +
+
+get_node(node_type: GiantNodeType) DistanceMaze[source]
+

Returns a giant node given its type.

+
+ +
+
+get_node_count(node_type: GiantNodeType) int[source]
+

Returns how many of the giant node are required.

+
+ +
+ +
+
+class day21.lib.classes.GiantNodeType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
+

Bases: Enum

+

A bunch of giant node types.

+

turn each “maze” into a node type. +assume parity == EVEN +e.g. 9 x 9 maze matrix +mazes_center_to_bottom == 9 //2 == 4

+

BIG -> mazes_center_to_bottom - 1 == 3 +SMALL -> mazes_center_to_bottom == 4 +FULL_EVEN -> (mazes_center_to_bottom-1) ^2 == 9 +FULL_ODD -> mazes_center_to_bottom^2 == 16

+
+
+EAST_TIP = 5
+
+ +
+
+FULL_EVEN = 0
+
+ +
+
+FULL_ODD = 1
+
+ +
+
+NORTH_EAST_BIG = 3
+
+ +
+
+NORTH_EAST_SMALL = 4
+
+ +
+
+NORTH_TIP = 2
+
+ +
+
+NORTH_WEST_BIG = 12
+
+ +
+
+NORTH_WEST_SMALL = 13
+
+ +
+
+SOUTH_EAST_BIG = 6
+
+ +
+
+SOUTH_EAST_SMALL = 7
+
+ +
+
+SOUTH_TIP = 8
+
+ +
+
+SOUTH_WEST_BIG = 9
+
+ +
+
+SOUTH_WEST_SMALL = 10
+
+ +
+
+WEST_TIP = 11
+
+ +
+ +
+
+class day21.lib.classes.Maze(data: list[str])[source]
+

Bases: object

+

2d grid of items.

+
+
+grid: list[str]
+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+ +
+
+class day21.lib.classes.Position(row: int, col: int)[source]
+

Bases: object

+

Simple 2d vector.

+

It implements an unsafe hash since PositionDist inherits from this.

+
+
+col: int
+
+ +
+
+row: int
+
+ +
+ +
+
+class day21.lib.classes.PositionDist(row: int, col: int, *, distance: int)[source]
+

Bases: Position

+

Position + distance.

+
+
+distance: int
+
+ +
+
+replace(row: int | None = None, col: int | None = None, distance: int | None = None) PositionDist[source]
+

Return a copy with given args changed.

+

Distance will +1 if not supplied

+
+ +
+ +
+
+

day21.lib.parsers module

+

Parsing code for day21.

+
+
+day21.lib.parsers.parse_maze(filename: str) tuple[Position, Maze][source]
+

Returns a well defined Maze class.

+
+ +
+
+

Module contents

+

Library modules for day21.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day22.html b/day22.html new file mode 100644 index 0000000..caeac9d --- /dev/null +++ b/day22.html @@ -0,0 +1,323 @@ + + + + + + + day22 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day22 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day22.day22 module

+

Day22 solution.

+
+
+class day22.day22.Visualization(boxes: list[BoxData], animate: bool = True)[source]
+

Bases: object

+

Visualization class.

+
+
+animate_part1() None[source]
+

Animate part 1.

+
+ +
+
+animate_part2() None[source]
+

Animate part2.

+
+ +
+
+boxes: list[BoxData]
+
+ +
+
+calculate_part1() int[source]
+

Calculate part1. (number of boxes that can fly up).

+
+ +
+
+calculate_part2() int[source]
+

Calculate part2 (number of boxes that will fall if each box is removed.

+
+ +
+
+follow_block(y: float, box: BoxData) None[source]
+

Snap camera to a given box.

+
+ +
+
+has_started: bool
+
+ +
+
+matrix: Matrix
+
+ +
+
+start() None[source]
+

Start visualization calcuations.

+
+ +
+
+vis_rate(rate: float) None[source]
+

Wait a given amount if we are animating.

+
+ +
+ +
+
+day22.day22.main() None[source]
+

Grab boxes and solve, while animating solution.

+
+ +
+
+

Module contents

+

day22 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day22.lib.html b/day22.lib.html new file mode 100644 index 0000000..60dfe51 --- /dev/null +++ b/day22.lib.html @@ -0,0 +1,438 @@ + + + + + + + day22.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day22.lib package

+
+

Submodules

+
+
+

day22.lib.classes module

+

Classes for day22.

+
+
+class day22.lib.classes.BoxData(name: str, start_pos: Vector3, end_pos: Vector3)[source]
+

Bases: object

+

A box in 3d space.

+
+
+end_pos: Vector3
+
+ +
+
+fall() None[source]
+

Move block down vertically.

+
+ +
+
+hats: set[BoxData]
+
+ +
+
+property height: float
+

Height according to vpython.

+
+ +
+
+property length: float
+

Length according to vpython.

+
+ +
+
+name: str
+
+ +
+
+recursive_fall(already_falling: set[BoxData]) set[BoxData][source]
+

Returns all boxes above us that fall if we fall.

+
+ +
+
+select() None[source]
+

Select a box by offsetting it to the side.

+
+ +
+
+set_hats(hats: set[BoxData]) None[source]
+

Set the BoxData’s that we support.

+
+ +
+
+set_supports(supports: set[BoxData]) None[source]
+

Set the BoxData’s that support us.

+
+ +
+
+set_vbox(vbox: box) None[source]
+

Store a vpython box onto this boxdata.

+
+ +
+
+start_pos: Vector3
+
+ +
+
+supports: set[BoxData]
+
+ +
+
+total_hats: set[BoxData]
+
+ +
+
+unselect() None[source]
+

Unselect a box by putting it back.

+
+ +
+
+vbox: box | None = None
+
+ +
+
+property vpos: vector
+

Pos according to vpython.

+
+ +
+
+property width: float
+

Width according to vpython.

+
+ +
+
+property z_val_bot: int
+

Return lowest z value (self.start_pos.z).

+
+ +
+
+property z_val_top: int
+

Return maximum z value(self.end_pos.z).

+
+ +
+ +
+
+class day22.lib.classes.Matrix(z_height: int = 400, xy: int = 10)[source]
+

Bases: object

+

3d matrix.

+
+
+can_fall_down(box: BoxData) bool[source]
+

Whether a given box can fall downwards.

+
+
Parameters:
+

box (BoxData) – box to test

+
+
Returns:
+

True if the box can fall.

+
+
Return type:
+

bool

+
+
+
+ +
+
+can_fly_up(box: BoxData) bool[source]
+

Check cells above our block. If they’re clear we can fly up.

+
+ +
+
+get_hats(box: BoxData) set[BoxData][source]
+

Return which boxes are resting on this box.

+
+ +
+
+get_supports(box: BoxData) set[BoxData][source]
+

Return which boxes are supporting this box.

+
+ +
+
+layers: list[list[list[BoxData | None]]]
+
+ +
+
+register_box(box: BoxData) None[source]
+

Register box into matrix.

+
+ +
+ +
+
+class day22.lib.classes.Vector3(x: int, y: int, z: int)[source]
+

Bases: object

+

Simple 3d vector.

+
+
+x: int
+
+ +
+
+y: int
+
+ +
+
+z: int
+
+ +
+ +
+
+

day22.lib.parsers module

+

Parse vectors/boxes from string to class.

+
+
+day22.lib.parsers.get_boxes(path: str) list[BoxData][source]
+

Returns a wellformed box from a string.

+

E.g. 1,2,3~1,2,4

+
+ +
+
+day22.lib.parsers.parse_vector(string: str) Vector3[source]
+

Returns a wellformed vector from a string.

+

e.g. 1,2,3

+
+ +
+
+

day22.lib.vis module

+

Visualization classes.

+
+
+day22.lib.vis.animate_part1(boxes: list[BoxData], matrix: Matrix) None[source]
+

Animates part1.

+
+ +
+
+day22.lib.vis.animate_part2(boxes: list[BoxData]) None[source]
+

Animates part2.

+
+ +
+
+day22.lib.vis.bind_keys(on_key_down: Any) None[source]
+

Bind keyboard events, so that enter calls the given callback.

+
+ +
+
+day22.lib.vis.construct_box(box_data: BoxData, color: vector) box[source]
+

Constructs a vpython box from a box_data.

+
+
Parameters:
+
    +
  • box_data (BoxData) – box data to mimic

  • +
  • color (vpython.vector) – color of box for vis.

  • +
+
+
Returns:
+

a vpython.box

+
+
Return type:
+

vpython.box

+
+
+
+ +
+
+day22.lib.vis.follow_block(y: float, box: BoxData) None[source]
+

Force camera to follow a block.

+
+ +
+
+day22.lib.vis.init_vis(boxes: list[BoxData]) None[source]
+

Initialize vis boxes. Only called if we’re visualizing.

+
+ +
+
+day22.lib.vis.random_color() vector[source]
+

Returns a random color compatible with vpython.

+
+ +
+
+

Module contents

+

Library classes for day22.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day22.tests.html b/day22.tests.html new file mode 100644 index 0000000..a36297f --- /dev/null +++ b/day22.tests.html @@ -0,0 +1,191 @@ + + + + + + + day22.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day22.tests package

+
+

Submodules

+
+
+

day22.tests.test_classes module

+

Test classes in day22.

+
+
+day22.tests.test_classes.test_box_data() None[source]
+

Tests BoxData class.

+
+ +
+
+

day22.tests.test_day22 module

+

Test main functions of day22.

+
+
+day22.tests.test_day22.test_visualization() None[source]
+

Tests results from day22 vis class.

+
+ +
+
+

day22.tests.test_parsers module

+

Tests parsing functions.

+
+
+day22.tests.test_parsers.test_parser() None[source]
+

Test get_boxes() function.

+
+ +
+
+

Module contents

+

Day22 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day23.html b/day23.html new file mode 100644 index 0000000..992957f --- /dev/null +++ b/day23.html @@ -0,0 +1,297 @@ + + + + + + + day23 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day23 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day23.day23 module

+

Day23 solution.

+
+
+day23.day23.main() None[source]
+

Read data then solve part1/part2.

+
+ +
+
+day23.day23.part1(maze: Maze) int[source]
+

Solve part1 (maximal distance given one-ways).

+
+ +
+
+day23.day23.part2(maze: Maze) int[source]
+

Solve part2 (maximal distance, no one-ways).

+
+ +
+
+

Module contents

+

Day23 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day23.lib.html b/day23.lib.html new file mode 100644 index 0000000..1a31eb3 --- /dev/null +++ b/day23.lib.html @@ -0,0 +1,480 @@ + + + + + + + day23.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day23.lib package

+
+

Submodules

+
+
+

day23.lib.classes module

+

Classes for part1.

+
+
+class day23.lib.classes.Maze(data: list[list[str]])[source]
+

Bases: object

+

2d array of chars.

+
+
+copy() Maze[source]
+

Copies us.

+
+ +
+
+get_cell_branches(position: Position) int[source]
+

Returns how many branches come out of this tile.

+
+ +
+
+grid: list[list[str]]
+
+ +
+
+is_oob(position: Position) bool[source]
+

True if position is out of bounds.

+
+ +
+
+num_cols: int
+
+ +
+
+num_rows: int
+
+ +
+ +
+
+class day23.lib.classes.Path[source]
+

Bases: object

+

A list of Positions.

+
+
+add(position: Position) None[source]
+

Add a position to the path.

+
+ +
+
+can_add(position: Position) bool[source]
+

Whether we can add a given position.

+

If we have already visited the position, we can’t

+
+ +
+
+copy() Path[source]
+

Copy us.

+
+ +
+
+flip() Path[source]
+

Flip a path by reversing the order.

+
+ +
+
+last() Position[source]
+

Return last position in path.

+
+
Raises:
+

ValueError – if we have no positions.

+
+
Returns:
+

last position in path

+
+
Return type:
+

Position

+
+
+
+ +
+
+nodes: set[Position]
+
+ +
+
+overlay(maze: Maze) str[source]
+

Overlay us onto a maze, return a neat string.

+
+
Parameters:
+

maze (Maze) – maze to overlay.

+
+
Returns:
+

a nice string to print.

+
+
Return type:
+

str

+
+
+
+ +
+
+route: list[Position]
+
+ +
+ +
+
+class day23.lib.classes.Position(row: int, col: int)[source]
+

Bases: object

+

Simple 2d Vector.

+
+
+col: int
+
+ +
+
+copy_modify(row: int | None = None, col: int | None = None) Position[source]
+

Copies us and offsets by the given offset.

+
+ +
+
+expand() list[Position][source]
+

Expand in 4 cardinal directions.

+
+ +
+
+row: int
+
+ +
+ +
+
+class day23.lib.classes.Solver1(maze: Maze, handle_hills: bool = True)[source]
+

Bases: object

+

Solver for part1.

+
+
+expand_hill(position: Position, tile: str) list[Position][source]
+

Expand valid positions based on our current tile.

+
+ +
+
+expand_path(path: Path) list[Path][source]
+

Expand path based on current path.

+
+ +
+
+handle_hills: bool
+
+ +
+
+maze: Maze
+
+ +
+
+solve() list[Path][source]
+

Solve the maze, using bfs.

+
+ +
+ +
+
+day23.lib.classes.generate_paths(path: Path, expansions: list[Position]) list[Path][source]
+

Given a path and valid expansions, (optionally) copies the path.

+

Returns a list of new paths. +If there is only one expansion, modifies it in-place

+
+ +
+
+

day23.lib.classes2 module

+

part 2 solution.

+
+
+class day23.lib.classes2.Edge(node1: int, node2: int, path: Path, length: int = 0)[source]
+

Bases: object

+

Edge class, representing a path between nodes.

+
+
+flip() Edge[source]
+

Reverse a path.

+
+ +
+
+length: int = 0
+
+ +
+
+node1: int
+
+ +
+
+node2: int
+
+ +
+
+path: Path
+
+ +
+ +
+
+class day23.lib.classes2.Node(name: int, position: ~day23.lib.classes.Position, edges: list[~day23.lib.classes2.Edge] = <factory>)[source]
+

Bases: object

+

Node representing a fork to another.

+
+
+edges: list[Edge]
+
+ +
+
+name: int
+
+ +
+
+position: Position
+
+ +
+ +
+
+class day23.lib.classes2.Solver2(maze: Maze)[source]
+

Bases: object

+

Solver for part 2.

+
+
+build_nodes() list[Node][source]
+

Build nodes and edges on a copy of the maze.

+
+ +
+
+static calculate_edges(start_node: Node, nodes: dict[Position, Node], maze: Maze) None[source]
+

Calculate edges of the maze.

+

Modifies the maze inplace, filling it in with #. +Modifies the node and its connecting nodes by adding Edges

+
+ +
+
+static expand_path(path: Path, maze: Maze) list[Path][source]
+

Expands a path, nuking that section of the maze using #.

+
+ +
+
+static get_nodes(maze: Maze) dict[Position, Node][source]
+

Gets nodes and marks them on the given maze.

+

Note that the maze is modified in-place! +Nodes are not populated with edges

+
+ +
+
+input_maze: Maze
+
+ +
+
+solve() int[source]
+

Solves the maze.

+
+ +
+ +
+
+day23.lib.classes2.solve2(nodes: list[Node], current: int, destination: int, distance: int, seen: set[int], forks_remaining: int) int[source]
+

Solves a dfs by creating forking into multiprocessing.

+
+ +
+
+day23.lib.classes2.solve2_helper(args: list[Any]) int[source]
+

ThreadPoolExecutor doesnt have starmap so we use a helper.

+
+ +
+
+

day23.lib.parsers module

+

Day23 parsers.

+
+
+day23.lib.parsers.get_maze(path: str) Maze[source]
+

Parse input file and return wellformed maze.

+
+ +
+
+

Module contents

+

Library functions for day23.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day23.tests.html b/day23.tests.html new file mode 100644 index 0000000..f127473 --- /dev/null +++ b/day23.tests.html @@ -0,0 +1,237 @@ + + + + + + + day23.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day23.tests package

+
+

Submodules

+
+
+

day23.tests.test_classes module

+

Tests for day23 classes.

+
+
+day23.tests.test_classes.test_generate_paths() None[source]
+

Test generate_paths().

+
+ +
+
+day23.tests.test_classes.test_maze() None[source]
+

Test Maze class.

+
+ +
+
+day23.tests.test_classes.test_path() None[source]
+

Test Path class.

+
+ +
+
+day23.tests.test_classes.test_position() None[source]
+

Test Position class.

+
+ +
+
+day23.tests.test_classes.test_solver1() None[source]
+

Test Solver class.

+
+ +
+
+

day23.tests.test_classes2 module

+

Test day23 part 2.

+
+
+day23.tests.test_classes2.test_solver2() None[source]
+

Test Solver2 class.

+
+ +
+
+

day23.tests.test_day23 module

+

Test day23 main functions.

+
+
+day23.tests.test_day23.test_part1() None[source]
+

Test part1().

+
+ +
+
+day23.tests.test_day23.test_part2() None[source]
+

Test part2().

+
+ +
+
+day23.tests.test_day23.test_solver() None[source]
+

Test Solver1.

+
+ +
+
+

day23.tests.test_parsers module

+

Test parsers.

+
+
+day23.tests.test_parsers.test_get_maze() None[source]
+

Test get_maze().

+
+ +
+
+

Module contents

+

Tests for day23.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day24.html b/day24.html new file mode 100644 index 0000000..b48b63b --- /dev/null +++ b/day24.html @@ -0,0 +1,256 @@ + + + + + + + day24 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day24 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day24.day24 module

+

day24 solution.

+
+
+day24.day24.get_intersection_2d(left: Hailstone, right: Hailstone) Vector2 | None[source]
+

Returns intersection of two hailstones.

+
+ +
+
+day24.day24.main() None[source]
+

Loads input then solves.

+
+ +
+
+day24.day24.part1(hailstones: list[Hailstone], valid_range: Vector2) int[source]
+

Solve part1: list of hailstones that are within a given rectangle.

+
+ +
+
+day24.day24.part2(hailstones: list[Hailstone]) int[source]
+

Solve part2: a magic hailstone that passes through all other hailstones.

+
+ +
+
+day24.day24.within_2d(point: Vector2, min_max: Vector2) bool[source]
+

Returns whether a point is inside a given rectangle.

+

x and y are both symmetric and defined by min_max.

+
+ +
+
+

Module contents

+

Day 24 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day24.lib.html b/day24.lib.html new file mode 100644 index 0000000..ce9fdc9 --- /dev/null +++ b/day24.lib.html @@ -0,0 +1,256 @@ + + + + + + + day24.lib package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day24.lib package

+
+

Submodules

+
+
+

day24.lib.classes module

+

Day24 classes.

+
+
+class day24.lib.classes.Hailstone(position: Vector3, velocity: Vector3)[source]
+

Bases: object

+

Hailstone has a 3d vector for pos/velocity.

+
+
+position: Vector3
+
+ +
+
+velocity: Vector3
+
+ +
+ +
+
+class day24.lib.classes.Vector2(x: float, y: float)[source]
+

Bases: object

+

Simple vector2.

+
+
+x: float
+
+ +
+
+y: float
+
+ +
+ +
+
+class day24.lib.classes.Vector3(x: float, y: float, z: float)[source]
+

Bases: object

+

Simple 3d vector.

+
+
+x: float
+
+ +
+
+property xy: Vector2
+

Convert to vector2.

+
+ +
+
+y: float
+
+ +
+
+z: float
+
+ +
+ +
+
+

day24.lib.parsers module

+

Day23 parsers.

+
+
+day24.lib.parsers.parse_input(filename: str) list[Hailstone][source]
+

Parse input lines.

+

Lines in the format 1,2,3@4,5,6\n.

+
+
Parameters:
+

filename (str) – file to open

+
+
Returns:
+

list of Hailstones

+
+
Return type:
+

list[Hailstone]

+
+
+
+ +
+
+day24.lib.parsers.parse_vector3(line: str) Vector3[source]
+

Parse a vector3.

+

E.g. 1,2,3

+
+ +
+
+

Module contents

+

Day24 library modules.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day24.tests.html b/day24.tests.html new file mode 100644 index 0000000..7ef11f1 --- /dev/null +++ b/day24.tests.html @@ -0,0 +1,205 @@ + + + + + + + day24.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day24.tests package

+
+

Submodules

+
+
+

day24.tests.test_day24 module

+

Test main functions in day24.

+
+
+day24.tests.test_day24.test_get_intersection_2d() None[source]
+

Test get_intersection_2d().

+
+ +
+
+day24.tests.test_day24.test_part1() None[source]
+

Test part1().

+
+ +
+
+day24.tests.test_day24.test_part2() None[source]
+

Test part2().

+
+ +
+
+day24.tests.test_day24.test_within_2d() None[source]
+

Test within_2d().

+
+ +
+
+

day24.tests.test_parsers module

+

Test parsing functions.

+
+
+day24.tests.test_parsers.test_parser() None[source]
+

Test parse_input().

+
+ +
+
+day24.tests.test_parsers.test_vector3() None[source]
+

Test parse_vector3().

+
+ +
+
+

Module contents

+

day24 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day25.html b/day25.html new file mode 100644 index 0000000..7f43ce0 --- /dev/null +++ b/day25.html @@ -0,0 +1,243 @@ + + + + + + + day25 package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day25 package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

day25.day25 module

+

day25 solution.

+
+
+class day25.day25.Connection(src: str, dests: list[str])[source]
+

Bases: object

+

Connection between two nodes.

+
+
+dests: list[str]
+
+ +
+
+node_names() list[str][source]
+

Return all nodes in a connection.

+
+ +
+
+src: str
+
+ +
+ +
+
+day25.day25.get_data(path: str) list[Connection][source]
+

Loads data and parses it into list of connections.

+
+ +
+
+day25.day25.main() None[source]
+

Load data and solve.

+
+ +
+
+day25.day25.parse_connection(line: str) Connection[source]
+

Parse connection into well defined class.

+

E.g. src: dest1 dest2 dest3.

+
+ +
+
+day25.day25.show_graph(graph: Graph) None[source]
+

Draws a graph that you can see.

+
+ +
+
+day25.day25.solve_nodes(connections: list[Connection]) int[source]
+

Graphs the modules.

+
+ +
+
+

Module contents

+

day25 solution.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/day25.tests.html b/day25.tests.html new file mode 100644 index 0000000..fe478e0 --- /dev/null +++ b/day25.tests.html @@ -0,0 +1,182 @@ + + + + + + + day25.tests package — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

day25.tests package

+
+

Submodules

+
+
+

day25.tests.test_day25 module

+

Test day25 main functions.

+
+
+day25.tests.test_day25.test_day25() None[source]
+

Test solve_nodes().

+
+ +
+
+day25.tests.test_day25.test_get_data() None[source]
+

Test get_data().

+
+ +
+
+day25.tests.test_day25.test_parse_connection() None[source]
+

Test parse_connection().

+
+ +
+
+

Module contents

+

day25 tests.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/download_inputs.html b/download_inputs.html new file mode 100644 index 0000000..dcd391d --- /dev/null +++ b/download_inputs.html @@ -0,0 +1,160 @@ + + + + + + + download_inputs module — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

download_inputs module

+

Downloads the input files from adventofcode.com.

+
+
+download_inputs.download_file(day: int, session: str) None[source]
+

Downloads a given day, given your session key.

+
+ +
+
+download_inputs.main() None[source]
+

Main function, checks which folders you have, and then downloads files.

+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 0000000..29dc813 --- /dev/null +++ b/genindex.html @@ -0,0 +1,3818 @@ + + + + + + Index — adventofcode2023 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | H + | I + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + | X + | Y + | Z + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

Q

+ + + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + + +
+ +

X

+ + + +
+ +

Y

+ + +
+ +

Z

+ + + +
+ + + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..1cbb407 --- /dev/null +++ b/index.html @@ -0,0 +1,127 @@ + + + + + + + Welcome to adventofcode2023’s documentation! — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Welcome to adventofcode2023’s documentation!

+ +
+
+

Indices and tables

+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/maker.html b/maker.html new file mode 100644 index 0000000..43db1da --- /dev/null +++ b/maker.html @@ -0,0 +1,151 @@ + + + + + + + maker module — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

maker module

+

maker of accessory files.

+
+
+maker.touch_days(days: Iterable[int]) None[source]
+

Touches each day, creating /lib and /tests and relevant init files.

+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/modules.html b/modules.html new file mode 100644 index 0000000..a25e366 --- /dev/null +++ b/modules.html @@ -0,0 +1,173 @@ + + + + + + + adventofcode2023 — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000..dc56c70 Binary files /dev/null and b/objects.inv differ diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 0000000..7fd7ee9 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,997 @@ + + + + + + Python Module Index — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Python Module Index

+ +
+ d | + m +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ d
+ day01 +
    + day01.day1a +
    + day01.day1b +
    + day01.tests +
    + day01.tests.test_day1a +
    + day01.tests.test_day1b +
+ day02 +
    + day02.day2 +
    + day02.tests +
    + day02.tests.test_day2 +
+ day03 +
    + day03.day3 +
    + day03.lib +
    + day03.lib.classes +
    + day03.lib.parsers +
    + day03.tests +
    + day03.tests.test_classes +
    + day03.tests.test_day3 +
+ day04 +
    + day04.day4 +
    + day04.tests +
    + day04.tests.test_day4 +
+ day05 +
    + day05.day5 +
    + day05.lib +
    + day05.lib.classes +
    + day05.lib.parsers +
    + day05.tests +
    + day05.tests.test_day5 +
+ day06 +
    + day06.day6 +
    + day06.tests +
    + day06.tests.test_day6 +
+ day07 +
    + day07.day7 +
    + day07.tests +
    + day07.tests.test_day7 +
+ day08 +
    + day08.day8 +
    + day08.tests +
    + day08.tests.test_day8 +
+ day09 +
    + day09.day9 +
    + day09.tests +
    + day09.tests.test_day9 +
+ day10 +
    + day10.day10 +
    + day10.lib +
    + day10.lib.direction +
    + day10.lib.pipebounds +
    + day10.lib.pipes +
    + day10.lib.position +
    + day10.tests +
    + day10.tests.test_day10 +
    + day10.tests.test_direction +
+ day11 +
    + day11.day11 +
    + day11.tests +
    + day11.tests.test_day11 +
+ day12 +
    + day12.day12 +
    + day12.tests +
    + day12.tests.test_day12 +
+ day13 +
    + day13.day13 +
    + day13.tests +
    + day13.tests.test_day13 +
+ day14 +
    + day14.day14 +
    + day14.lib +
    + day14.lib.direction +
    + day14.tests +
    + day14.tests.test_day14 +
    + day14.tests.test_direction +
+ day15 +
    + day15.day15 +
    + day15.lib +
    + day15.lib.classes +
    + day15.tests +
    + day15.tests.test_classes +
    + day15.tests.test_day15 +
+ day16 +
    + day16.day16 +
    + day16.lib +
    + day16.lib.cells +
    + day16.lib.direction +
    + day16.lib.laser +
    + day16.lib.parsers +
    + day16.lib.world +
    + day16.tests +
    + day16.tests.test_cells +
    + day16.tests.test_day16 +
    + day16.tests.test_direction +
    + day16.tests.test_world +
+ day17 +
    + day17.day17 +
    + day17.lib +
    + day17.lib.classes +
    + day17.lib.direction +
    + day17.lib.parsers +
    + day17.tests +
    + day17.tests.test_day17 +
    + day17.tests.test_direction +
    + day17.tests.test_parsers +
+ day18 +
    + day18.day18a +
    + day18.day18b +
    + day18.lib +
    + day18.lib.tile +
    + day18.tests +
    + day18.tests.test_day18a +
    + day18.tests.test_day18b +
+ day19 +
    + day19.day19 +
    + day19.lib +
    + day19.lib.classes +
    + day19.lib.parsers +
    + day19.tests +
    + day19.tests.test_classes +
    + day19.tests.test_day19 +
    + day19.tests.test_parsers +
+ day20 +
    + day20.day20 +
    + day20.lib +
    + day20.lib.classes +
    + day20.lib.parsers +
    + day20.tests +
    + day20.tests.test_classes +
    + day20.tests.test_day20 +
    + day20.tests.test_parsers +
+ day21 +
    + day21.day21 +
    + day21.lib +
    + day21.lib.classes +
    + day21.lib.parsers +
+ day22 +
    + day22.day22 +
    + day22.lib +
    + day22.lib.classes +
    + day22.lib.parsers +
    + day22.lib.vis +
    + day22.tests +
    + day22.tests.test_classes +
    + day22.tests.test_day22 +
    + day22.tests.test_parsers +
+ day23 +
    + day23.day23 +
    + day23.lib +
    + day23.lib.classes +
    + day23.lib.classes2 +
    + day23.lib.parsers +
    + day23.tests +
    + day23.tests.test_classes +
    + day23.tests.test_classes2 +
    + day23.tests.test_day23 +
    + day23.tests.test_parsers +
+ day24 +
    + day24.day24 +
    + day24.lib +
    + day24.lib.classes +
    + day24.lib.parsers +
    + day24.tests +
    + day24.tests.test_day24 +
    + day24.tests.test_parsers +
+ day25 +
    + day25.day25 +
    + day25.tests +
    + day25.tests.test_day25 +
+ download_inputs +
 
+ m
+ maker +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 0000000..6abb1db --- /dev/null +++ b/search.html @@ -0,0 +1,124 @@ + + + + + + Search — adventofcode2023 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + + + +
+ +
+ +
+
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 0000000..27c4651 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["autodoc", "ci", "day01", "day01.tests", "day02", "day02.tests", "day03", "day03.lib", "day03.tests", "day04", "day04.tests", "day05", "day05.lib", "day05.tests", "day06", "day06.tests", "day07", "day07.tests", "day08", "day08.tests", "day09", "day09.tests", "day10", "day10.lib", "day10.tests", "day11", "day11.tests", "day12", "day12.tests", "day13", "day13.tests", "day14", "day14.lib", "day14.tests", "day15", "day15.lib", "day15.tests", "day16", "day16.lib", "day16.tests", "day17", "day17.lib", "day17.tests", "day18", "day18.lib", "day18.tests", "day19", "day19.lib", "day19.tests", "day20", "day20.lib", "day20.tests", "day21", "day21.lib", "day22", "day22.lib", "day22.tests", "day23", "day23.lib", "day23.tests", "day24", "day24.lib", "day24.tests", "day25", "day25.tests", "download_inputs", "index", "maker", "modules"], "filenames": ["autodoc.md", "ci.md", "day01.rst", "day01.tests.rst", "day02.rst", "day02.tests.rst", "day03.rst", "day03.lib.rst", "day03.tests.rst", "day04.rst", "day04.tests.rst", "day05.rst", "day05.lib.rst", "day05.tests.rst", "day06.rst", "day06.tests.rst", "day07.rst", "day07.tests.rst", "day08.rst", "day08.tests.rst", "day09.rst", "day09.tests.rst", "day10.rst", "day10.lib.rst", "day10.tests.rst", "day11.rst", "day11.tests.rst", "day12.rst", "day12.tests.rst", "day13.rst", "day13.tests.rst", "day14.rst", "day14.lib.rst", "day14.tests.rst", "day15.rst", "day15.lib.rst", "day15.tests.rst", "day16.rst", "day16.lib.rst", "day16.tests.rst", "day17.rst", "day17.lib.rst", "day17.tests.rst", "day18.rst", "day18.lib.rst", "day18.tests.rst", "day19.rst", "day19.lib.rst", "day19.tests.rst", "day20.rst", "day20.lib.rst", "day20.tests.rst", "day21.rst", "day21.lib.rst", "day22.rst", "day22.lib.rst", "day22.tests.rst", "day23.rst", "day23.lib.rst", "day23.tests.rst", "day24.rst", "day24.lib.rst", "day24.tests.rst", "day25.rst", "day25.tests.rst", "download_inputs.rst", "index.rst", "maker.rst", "modules.rst"], "titles": ["Automatic documentation", "Continuous Integration", "day01 package", "day01.tests package", "day02 package", "day02.tests package", "day03 package", "day03.lib package", "day03.tests package", "day04 package", "day04.tests package", "day05 package", "day05.lib package", "day05.tests package", "day06 package", "day06.tests package", "day07 package", "day07.tests package", "day08 package", "day08.tests package", "day09 package", "day09.tests package", "day10 package", "day10.lib package", "day10.tests package", "day11 package", "day11.tests package", "day12 package", "day12.tests package", "day13 package", "day13.tests package", "day14 package", "day14.lib package", "day14.tests package", "day15 package", "day15.lib package", "day15.tests package", "day16 package", "day16.lib package", "day16.tests package", "day17 package", "day17.lib package", "day17.tests package", "day18 package", "day18.lib package", "day18.tests package", "day19 package", "day19.lib package", "day19.tests package", "day20 package", "day20.lib package", "day20.tests package", "day21 package", "day21.lib package", "day22 package", "day22.lib package", "day22.tests package", "day23 package", "day23.lib package", "day23.tests package", "day24 package", "day24.lib package", "day24.tests package", "day25 package", "day25.tests package", "download_inputs module", "Welcome to adventofcode2023\u2019s documentation!", "maker module", "adventofcode2023"], "terms": {"The": [0, 22, 23, 38, 47], "thi": [0, 1, 7, 23, 25, 35, 38, 43, 47, 49, 50, 53, 55, 58], "repo": 0, "wa": [0, 1, 41], "setup": [0, 1], "us": [0, 12, 16, 20, 41, 43, 47, 49, 58], "guid": 0, "http": 0, "redandgreen": 0, "co": 0, "uk": 0, "sphinx": 0, "github": [0, 1], "page": [0, 1, 66], "via": [0, 43], "action": [0, 1], "from": [0, 2, 4, 7, 12, 14, 15, 18, 22, 23, 25, 27, 37, 38, 41, 43, 46, 47, 49, 50, 53, 55, 56, 65], "scratch": 0, "cd": 0, "doc": 0, "quickstart": 0, "gener": [0, 1, 16, 20, 43], "rst": [0, 1], "": [0, 1, 2, 8, 11, 22, 23, 29, 31, 34, 41, 43, 46, 47, 48, 55], "root": 0, "directori": [0, 49], "apidoc": 0, "o": [0, 31], "manual": [0, 1], "html": [0, 1], "alreadi": [0, 35, 38, 50, 58], "done": [0, 1], "make": [0, 27, 49], "clean": 0, "ci": 1, "allow": 1, "lot": 1, "process": [1, 2, 12, 22, 34, 35, 43, 46, 47], "occur": 1, "automat": [1, 66], "think": 1, "local": 1, "cloud": 1, "i": [1, 7, 9, 12, 14, 22, 23, 25, 27, 31, 32, 35, 38, 41, 43, 49, 50, 52, 53, 54, 58, 60], "run": [1, 2, 6, 9, 22, 25, 29, 31, 37, 40, 43, 46, 49, 52], "arbitrari": 1, "script": 1, "push": 1, "time": [1, 14, 15], "you": [1, 49, 63, 65], "can": [1, 2, 14, 31, 35, 43, 54, 55, 58, 63], "reus": 1, "config": 1, "base": [1, 2, 4, 7, 8, 9, 12, 14, 16, 18, 20, 23, 25, 27, 29, 31, 32, 35, 38, 41, 43, 44, 47, 50, 52, 53, 54, 55, 58, 61, 63], "ensur": [1, 12, 51], "all": [1, 11, 18, 25, 50, 53, 55, 60, 63], "ar": [1, 2, 7, 11, 18, 27, 31, 32, 38, 41, 47, 50, 51, 53, 54, 55, 58, 60], "see": [1, 47, 63], "workflow": [1, 46, 47, 48], "com": [1, 65], "thei": [1, 11, 55], "few": [1, 41], "condit": [1, 46, 47], "merg": 1, "pr": 1, "etc": 1, "mainli": 1, "just": [1, 35], "an": [1, 7, 18, 20, 29, 41, 43, 47, 53], "linux": 1, "box": [1, 34, 35, 36, 54, 55], "instal": 1, "duplic": 1, "what": [1, 9, 22], "being": 1, "environ": 1, "also": 1, "publish": 1, "our": [1, 9, 12, 18, 23, 31, 38, 41, 47, 48, 50, 53, 55, 58], "document": 1, "gh": 1, "branch": [1, 47, 58], "both": [1, 15, 60], "name": [1, 2, 4, 11, 12, 18, 23, 32, 34, 35, 38, 41, 43, 46, 47, 49, 50, 53, 54, 55, 57, 58], "stage": 1, "section": [1, 58], "detail": 1, "lint": 1, "These": 1, "veri": 1, "fast": 1, "have": [1, 7, 9, 22, 41, 47, 58, 65], "barebon": 1, "check": [1, 29, 41, 43, 47, 55, 65], "befor": 1, "go": [1, 12, 22, 27, 38], "unit": 1, "test": [1, 2, 4, 6, 9, 11, 14, 16, 18, 20, 22, 25, 27, 29, 31, 34, 37, 40, 43, 46, 49, 54, 55, 57, 60, 63, 67], "well": [1, 7, 12, 29, 31, 35, 38, 43, 46, 47, 53, 63], "A": [1, 18, 23, 38, 41, 46, 47, 48, 50, 53, 55, 58], "simpl": [1, 2, 12, 14, 16, 18, 23, 35, 38, 41, 43, 50, 53, 55, 58, 61], "doe": [1, 22], "type": [1, 2, 4, 9, 11, 16, 20, 22, 23, 29, 31, 32, 35, 38, 41, 43, 46, 47, 49, 50, 52, 53, 55, 58, 61], "we": [1, 7, 9, 11, 14, 18, 22, 23, 27, 31, 32, 41, 43, 47, 49, 50, 52, 54, 55, 58], "don": 1, "t": [1, 22, 49, 58], "ani": [1, 12, 22, 31, 55, 58], "file": [1, 2, 4, 7, 9, 12, 15, 16, 25, 27, 29, 38, 41, 43, 46, 49, 50, 58, 61, 65, 67], "convert": [1, 7, 9, 12, 14, 20, 22, 41, 53, 61], "lf": 1, "framework": 1, "tell": 1, "your": [1, 65], "code": [1, 17, 45, 51, 53], "cover": 1, "easili": 1, "game": [1, 4, 5], "exampl": 1, "assert": 1, "main": [1, 2, 4, 6, 8, 9, 11, 14, 16, 18, 20, 22, 24, 25, 26, 27, 29, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, 46, 48, 49, 51, 52, 54, 56, 57, 59, 60, 62, 63, 64, 65], "0": [1, 2, 4, 9, 12, 20, 23, 27, 29, 32, 35, 38, 41, 43, 46, 47, 50, 53, 58], "result": [1, 2, 6, 8, 50, 56], "high": [1, 49, 50], "necessarili": 1, "good": 1, "inject": [1, 11, 12, 50], "markdown": 1, "autodoc": 1, "more": 1, "inform": 1, "test_day1a": 2, "test_get_first_last": [2, 3], "test_get_input": [2, 3, 20, 21, 31, 33, 34, 36], "test_part1": [2, 3, 4, 5, 6, 8, 9, 10, 11, 13, 14, 15, 18, 19, 20, 21, 37, 39, 57, 59, 60, 62], "test_day1b": 2, "test_index_valu": [2, 3], "test_part2": [2, 3, 4, 5, 6, 8, 9, 10, 11, 13, 14, 15, 18, 19, 20, 21, 37, 39, 49, 51, 57, 59, 60, 62], "test_process_lin": [2, 3], "solut": [2, 4, 6, 8, 9, 11, 14, 16, 18, 20, 22, 25, 27, 29, 31, 34, 37, 40, 41, 43, 46, 49, 52, 54, 57, 58, 60, 63], "get_first_last": [2, 3], "line": [2, 22, 27, 50, 61, 63], "str": [2, 4, 7, 9, 12, 14, 16, 18, 20, 22, 23, 25, 27, 29, 31, 34, 35, 38, 41, 43, 44, 46, 47, 49, 50, 53, 55, 58, 61, 63, 65], "tupl": [2, 4, 12, 18, 22, 23, 31, 37, 38, 41, 43, 46, 47, 48, 49, 53], "sourc": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67], "return": [2, 4, 5, 6, 7, 9, 11, 12, 14, 18, 20, 22, 23, 25, 27, 29, 31, 34, 37, 38, 41, 43, 44, 46, 47, 48, 49, 50, 52, 53, 55, 58, 60, 61, 63], "first": 2, "last": [2, 57, 58], "numer": 2, "charact": [2, 22, 23, 25], "string": [2, 3, 4, 9, 18, 29, 31, 34, 36, 47, 53, 55, 58], "It": [2, 49, 53], "same": 2, "paramet": [2, 4, 9, 11, 20, 22, 23, 29, 31, 41, 43, 46, 47, 50, 52, 53, 55, 58, 61], "pars": [2, 4, 7, 11, 12, 16, 17, 25, 34, 36, 41, 42, 43, 46, 50, 51, 53, 55, 56, 58, 61, 62, 63], "rais": [2, 22, 23, 43, 47, 58], "valueerror": [2, 22, 58], "when": [2, 43, 53], "number": [2, 7, 8, 9, 27, 37, 38, 43, 46, 50, 52, 54], "char": [2, 58], "get_input": [2, 3, 20, 27, 28, 31, 33, 34, 37, 38, 40, 41, 43, 46], "input_fil": [2, 4], "list": [2, 4, 6, 7, 9, 11, 12, 14, 16, 18, 20, 22, 23, 25, 27, 29, 31, 34, 35, 38, 40, 41, 43, 46, 47, 49, 50, 53, 54, 55, 58, 60, 61, 63], "grab": [2, 6, 12, 21, 25, 31, 43, 54], "input": [2, 4, 6, 9, 15, 16, 17, 18, 20, 21, 22, 25, 27, 29, 31, 34, 36, 37, 38, 40, 41, 43, 44, 46, 49, 50, 58, 60, 61, 65], "none": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 62, 63, 64, 65, 67], "part1": [2, 3, 4, 5, 6, 8, 9, 10, 11, 13, 14, 15, 19, 20, 21, 22, 37, 39, 40, 41, 42, 43, 46, 48, 49, 52, 54, 55, 57, 58, 59, 60, 62], "int": [2, 4, 6, 7, 8, 9, 11, 12, 14, 16, 18, 20, 22, 23, 25, 27, 29, 31, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 54, 55, 57, 58, 60, 63, 65, 67], "day1": 2, "adventofcode2023": 2, "sum": [2, 6, 9, 14, 34, 35, 47], "each": [2, 7, 9, 14, 22, 31, 34, 38, 39, 41, 47, 49, 50, 53, 54, 67], "class": [2, 3, 4, 6, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 23, 24, 25, 27, 28, 29, 31, 32, 33, 34, 36, 38, 39, 40, 42, 43, 44, 46, 48, 49, 51, 52, 54, 56, 57, 59, 60, 63], "indexvalu": 2, "index": [2, 7, 18, 31, 49, 66], "valu": [2, 4, 6, 7, 11, 12, 20, 23, 27, 31, 32, 35, 38, 41, 43, 46, 47, 50, 53, 55], "1": [2, 4, 9, 11, 20, 23, 29, 31, 32, 35, 38, 41, 43, 44, 47, 50, 53, 54, 55, 61], "object": [2, 4, 7, 8, 9, 12, 14, 16, 18, 20, 23, 25, 27, 29, 31, 35, 38, 41, 43, 44, 47, 50, 52, 53, 54, 55, 58, 61, 63], "map": [2, 9, 11, 12, 13, 18, 22, 23, 50, 52, 53], "wordnumb": 2, "word": [2, 7], "one": [2, 12, 14, 22, 25, 27, 37, 40, 41, 47, 57, 58], "integ": [2, 9, 43], "open": [2, 16, 46, 50, 61], "handl": [2, 34, 50], "filepath": [2, 46], "part2": [2, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15, 19, 20, 21, 22, 27, 36, 37, 39, 40, 42, 46, 48, 49, 50, 51, 52, 54, 55, 57, 59, 60, 62], "process_lin": [2, 3], "substr": 2, "like": [2, 7], "nine": 2, "count": [2, 43, 49, 50], "do": [2, 50], "9": [2, 53], "two": [2, 7, 16, 22, 29, 47, 60, 63], "concaten": 2, "day1a": 3, "varieti": [3, 35], "function": [3, 7, 8, 11, 18, 20, 21, 24, 25, 26, 27, 30, 33, 36, 39, 42, 45, 47, 51, 56, 58, 59, 62, 64, 65], "day01b": 3, "index_valu": 3, "test_day2": 4, "get_game1": [4, 5], "get_game2": [4, 5], "test_draw": [4, 5], "test_gam": [4, 5], "color": [4, 43, 44, 50, 55], "qualnam": [4, 23, 32, 35, 38, 41, 43, 47, 50, 53], "start": [4, 11, 12, 18, 22, 23, 32, 35, 38, 41, 43, 47, 49, 50, 52, 53, 54], "boundari": [4, 23, 32, 35, 38, 41, 43, 47, 50, 53], "strenum": [4, 43, 47], "enum": [4, 23, 38, 41, 43, 50, 53], "blue": 4, "green": 4, "red": [4, 50], "draw": [4, 5, 63], "repres": [4, 7, 14, 20, 22, 23, 25, 41, 43, 44, 58], "bag": 4, "parse_color_count": 4, "parse_colors_count": 4, "4": [4, 9, 11, 20, 23, 40, 41, 49, 53, 55, 58, 61], "5": [4, 11, 53, 61], "show": 4, "multipl": [4, 11], "id": [4, 9, 25, 34, 35], "parse_draw": 4, "11": [4, 53], "3": [4, 6, 11, 20, 23, 32, 38, 40, 41, 43, 53, 55, 61], "power_level": 4, "r": [4, 43, 44], "g": [4, 9, 23, 44, 47, 48, 50, 53, 55, 61, 63], "b": [4, 44], "game_filt": [4, 5], "bool": [4, 7, 8, 12, 22, 23, 25, 38, 41, 43, 47, 49, 50, 52, 53, 54, 55, 58, 60], "true": [4, 25, 27, 38, 43, 47, 50, 53, 54, 55, 58], "satisfi": 4, "constraint": 4, "question": [4, 33], "get_gam": [4, 5], "get": [4, 18, 23, 27, 31, 34, 43, 50, 58], "data": [4, 6, 7, 11, 29, 31, 37, 38, 43, 46, 49, 52, 53, 55, 57, 58, 63], "structur": [4, 16], "print": [4, 40, 58], "out": [4, 16, 23, 38, 41, 43, 44, 49, 53, 58], "answer": 4, "q1": [4, 25, 29], "q2": [4, 25, 29], "solv": [4, 11, 14, 18, 29, 37, 38, 40, 41, 46, 52, 54, 57, 58, 60, 63], "part": [4, 7, 8, 16, 18, 34, 44, 46, 47, 48, 54, 58, 59], "implement": [4, 6, 16, 53], "sampl": 5, "anoth": [5, 25, 58], "power": [5, 34, 35], "lib": [6, 11, 22, 31, 34, 37, 40, 43, 46, 48, 49, 52, 54, 57, 60, 67], "gear": [6, 7, 8], "col": [6, 7, 8, 22, 23, 25, 29, 37, 38, 40, 41, 43, 52, 53, 57, 58], "gear_ratio": [6, 7], "part_numb": [6, 7], "row": [6, 7, 8, 22, 23, 25, 29, 31, 33, 37, 38, 40, 41, 43, 52, 53, 57, 58], "matrix": [6, 7, 8, 43, 53, 54, 55], "filter_engine_part": [6, 7], "find_gear_part": [6, 7], "get_gear": [6, 7], "get_part_numb": [6, 7], "is_engine_part": [6, 7], "is_engine_part_row": [6, 7], "row_count": [6, 7], "row_siz": [6, 7, 8], "partnumb": [6, 7], "end_index": [6, 7], "length": [6, 7, 18, 22, 49, 50, 54, 55, 57, 58], "touch": [6, 7, 8, 67], "parser": [6, 11, 37, 40, 46, 48, 49, 52, 54, 57, 59, 60], "get_matrix": [6, 7], "test_class": [6, 34, 46, 49, 54, 57], "partnumbertouchtest": [6, 8], "test_gear": [6, 8], "test_matrix": [6, 8], "test_part_numb": [6, 8], "test_day3": 6, "dai": [6, 9, 14, 41, 42, 60, 65, 67], "entrypoint": [6, 37], "valid": [6, 27, 52, 58], "day3": [7, 8], "potenti": 7, "icon": 7, "properti": [7, 14, 18, 23, 47, 50, 55, 61], "If": [7, 27, 32, 35, 43, 47, 49, 50, 55, 58], "exactli": [7, 22], "ratio": 7, "entir": [7, 41], "2d": [7, 23, 25, 29, 31, 41, 43, 53, 58], "arrai": [7, 20, 23, 25, 29, 31, 41, 43, 53, 58], "legit": 7, "given": [7, 11, 12, 18, 23, 29, 38, 41, 49, 52, 53, 54, 55, 57, 58, 60, 65], "retriev": 7, "456": 7, "whether": [7, 23, 25, 41, 52, 55, 58, 60], "engin": 7, "look": 7, "its": [7, 14, 18, 20, 23, 25, 31, 35, 49, 50, 53, 58], "surround": 7, "static": [7, 38, 58], "how": [7, 9, 14, 18, 22, 25, 38, 52, 53, 58], "mani": [7, 9, 18, 22, 38, 52, 53, 58], "long": 7, "respres": 7, "posit": [7, 22, 25, 38, 41, 43, 52, 53, 57, 58, 59, 60, 61], "end": [7, 11, 12, 18, 38], "column": 7, "coordin": [7, 23, 53], "defin": [7, 12, 29, 31, 35, 38, 43, 47, 53, 60, 63], "path": [7, 12, 14, 16, 18, 20, 22, 25, 27, 29, 31, 34, 38, 40, 41, 43, 46, 49, 55, 57, 58, 59, 63], "text": [7, 47], "librari": [7, 12, 23, 32, 35, 38, 41, 44, 47, 50, 53, 55, 58, 61], "someth": [8, 50], "els": [8, 50], "test_day4": 9, "test_card": [9, 10], "test_grab_data": [9, 10], "test_inventori": [9, 10], "test_split_numb": [9, 10], "card": [9, 10, 16], "input_str": 9, "winner": 9, "own": 9, "get_match": 9, "intersect": [9, 60], "get_point": 9, "point": [9, 16, 25, 31, 32, 43, 60], "worth": 9, "match": [9, 22, 35, 47, 53], "otherwis": [9, 35, 50], "2": [9, 11, 20, 23, 25, 32, 34, 38, 41, 43, 44, 53, 55, 58, 59, 61], "set": [9, 16, 27, 50, 55, 58], "inventori": [9, 10], "all_card": 9, "total": [9, 25, 27, 34, 46], "question2": [9, 31, 34, 36], "accumul": 9, "calculate_map": 9, "dict": [9, 18, 23, 27, 38, 41, 46, 49, 50, 53, 58], "card_id": 9, "memoiz": [9, 27], "total_card": 9, "grab_data": [9, 10], "filenam": [9, 29, 50, 53, 61], "wellform": [9, 55, 58], "load": [9, 29, 40, 43, 46, 49, 52, 60, 63], "split_numb": [9, 10], "split": [9, 46, 47, 49, 53], "ed": 9, "e": [9, 23, 35, 41, 47, 48, 50, 53, 55, 61, 63], "39": 9, "40": 9, "41": 9, "42": 9, "part04": 10, "dest_end": [11, 12], "dest_start": [11, 12], "get_map": [11, 12], "size": [11, 12, 41, 46, 47, 52, 53], "src_end": [11, 12], "src_start": [11, 12], "mappingrang": [11, 12], "namedmap": [11, 12, 13], "add_map": [11, 12], "extend_mapping_rang": [11, 12], "finalize_map": [11, 12], "get_mapping_rang": [11, 12], "grab_input": [11, 12], "test_day5": 11, "test_map": [11, 13], "test_seed_to_mapping_rang": [11, 13], "get_loc": [11, 18], "seed": 11, "final": [11, 41, 50], "locat": [11, 18, 19, 23], "get_location_rang": 11, "seed_rang": 11, "problem": 11, "lowest": [11, 55], "rang": [11, 12, 13, 43, 47], "find": [11, 18, 19, 22, 31, 43, 49], "seed_to_mapping_rang": 11, "pair": 11, "up": [11, 31, 38, 43, 54, 55], "instead": 11, "6": [11, 14, 53, 61], "want": 11, "format": [11, 61], "fals": [12, 23, 25, 49, 50, 52], "src_valu": 12, "destin": [12, 46, 47, 58], "chunk": 12, "follow": [12, 18, 47, 55], "remaind": [12, 47, 53], "add": [12, 18, 27, 34, 35, 38, 41, 50, 57, 58], "int_max": 12, "post": 12, "sort": [12, 16], "fill": [12, 22, 43, 53, 58], "miss": 12, "homogen": 12, "so": [12, 31, 41, 55, 58], "min_map": 12, "max_map": 12, "binari": 12, "search": [12, 66], "correct": 12, "appli": 12, "src_mapping_rang": 12, "remap": 12, "new": [12, 23, 31, 43, 47, 58], "day5": 13, "mainfil": 13, "construct": [13, 37, 38, 39, 43, 50, 55], "test_day6": 14, "test_calculate_constant_tim": [14, 15], "test_calculate_rac": [14, 15], "test_get_giga_rac": [14, 15], "test_read_input": [14, 15], "race": [14, 15], "record_dist": 14, "record": 14, "racestrat": 14, "charge_tim": 14, "run_tim": 14, "strategi": 14, "charg": 14, "distanc": [14, 25, 29, 30, 52, 53, 57, 58], "far": 14, "move": [14, 23, 31, 43, 55], "speed": 14, "after": [14, 31, 38], "calculate_constant_tim": 14, "tl": 14, "dr": 14, "quadrat": 14, "formula": 14, "calculate_rac": 14, "naiv": [14, 52], "calcuat": [14, 54], "get_giga_rac": 14, "giga": [14, 15], "amnount": 14, "wai": [14, 57], "win": 14, "amoutn": 14, "read_input": [14, 18, 22, 29], "disgust": 14, "short": 14, "guess": 14, "calcul": [15, 16, 17, 22, 23, 27, 35, 37, 38, 43, 50, 52, 53, 54, 58], "constant": 15, "brute": [15, 27, 52], "forc": [15, 27, 52, 55], "read": [15, 18, 22, 29, 34, 36, 37, 38, 43, 57], "day6": 15, "test_day7": 16, "test_calculate_hand": [16, 17], "test_hand": [16, 17], "test_pars": [16, 17, 27, 28, 40, 46, 49, 54, 57, 60], "hand": [16, 17], "bet": 16, "cards_int": 16, "of_a_kind": 16, "card_map": 16, "classvar": [16, 23], "23456789tjqka": 16, "calculate_of_a_kind": 16, "figur": 16, "handpart2": 16, "joker": 16, "rule": [16, 46, 47, 48], "j23456789tqka": 16, "ad": [16, 58], "biggest": 16, "calculate_hand": 16, "cl": 16, "input_path": 16, "func": 16, "parse_lin": [16, 49, 50, 51], "test_day8": 18, "test_direct": [18, 19, 22, 31, 37, 40], "test_find_cycl": [18, 19], "test_location_a": [18, 19], "cycl": [18, 19, 31], "start_loc": 18, "location_step": 18, "locationstep": 18, "cycle_start": 18, "cycle_length": 18, "repeat": 18, "cycle_start_index": 18, "end_z": 18, "step": [18, 34, 35, 40, 41, 43, 52, 53], "direct": [18, 19, 22, 24, 31, 33, 37, 39, 40, 42, 43, 58], "get_step": 18, "get_steps_iter": 18, "iter": [18, 67], "loop": [18, 22, 31, 49, 50], "through": [18, 38, 50, 60], "indefinit": 18, "left": [18, 20, 29, 31, 43, 60], "right": [18, 20, 29, 43, 60], "other": [18, 25, 60], "here": 18, "worldmap": 18, "world": [18, 31, 33, 37, 39, 40, 41, 43], "add_loc": 18, "find_cycl": [18, 22], "world_map": 18, "follow_direct": 18, "until": [18, 49], "hit": [18, 49], "zzz": 18, "follow_directions_multi": 18, "them": [18, 58], "get_location_a": 18, "thing": [18, 27], "test_day9": 20, "test_interpol": [20, 21], "valuearrai": 20, "sub_arrai": 20, "subarrai": 20, "extrapolate_left": 20, "extrapol": 20, "extrapolate_right": 20, "generic_extrapol": 20, "add_to_arrai": 20, "callabl": [20, 38], "calc_valu": 20, "turn": [20, 53], "nice": [20, 58], "interpol": [20, 21], "element": 20, "wise": 20, "diff": 20, "east": [22, 23, 31, 32, 37, 38, 40, 41], "north": [22, 23, 31, 32, 37, 38, 40, 41], "south": [22, 23, 31, 32, 37, 38, 40, 41], "west": [22, 23, 31, 32, 37, 38, 40, 41], "opposit": [22, 23, 37, 38, 40, 41], "pipebound": 22, "insid": [22, 23, 35, 47, 60], "outsid": [22, 23], "pipe": 22, "unknown": [22, 23, 47], "pipe_direct": [22, 23], "font": [22, 23], "is_loop": [22, 23], "is_start": [22, 23], "next_direct": [22, 23], "next_posit": [22, 23], "pipe_bound": [22, 23], "pipemap": [22, 23], "get_pip": [22, 23], "get_pipe_saf": [22, 23], "height": [22, 23, 54, 55], "is_in_map": [22, 23], "width": [22, 23, 54, 55], "test_day10": 22, "calculate_": 22, "pipe_map": 22, "should": [22, 49], "u": [22, 43, 55, 58], "doesn": 22, "connect": [22, 58, 63], "assertionerror": [22, 23, 43, 47], "known": 22, "expand_map": 22, "expand": [22, 25, 57, 58], "3x3": 22, "tile": [22, 23, 29, 37, 38, 41, 43, 58], "expand_pip": 22, "big": [22, 25, 53], "boi": 22, "find_": 22, "flood_fil": 22, "flood": [22, 43], "were": 22, "process_big_input_lin": 22, "singl": [22, 49], "process_input_lin": 22, "reduce_map": 22, "big_map": 22, "small_map": 22, "fat": 22, "back": [22, 55], "down": [22, 43, 47, 55], "small": [22, 53], "cardin": [23, 32, 43, 58], "nsew": 23, "w": [23, 41], "n": [23, 41, 47, 48, 49, 61], "invalid": [23, 41, 43], "current": [23, 43, 50, 58], "7": [23, 53], "f": 23, "j": 23, "l": [23, 43], "prev_direct": 23, "determin": 23, "next": [23, 27, 32, 38, 47], "where": [23, 50], "came": 23, "test_day11": 25, "test_expans": [25, 26], "test_is_empti": [25, 26], "galaxi": 25, "uniqu": 25, "univers": [25, 26], "account": 25, "space": [25, 31, 55], "item": [25, 27, 53], "get_point_dist": 25, "expansion_r": 25, "is_expand": 25, "expand_cont": 25, "grab_galaxi": 25, "num_col": [25, 37, 38, 40, 41, 43, 52, 53, 57, 58], "num_row": [25, 31, 37, 38, 40, 41, 43, 52, 53, 57, 58], "get_total_dist": 25, "is_empti": [25, 26], "parse_input": [25, 60, 61, 62], "expans": [26, 58], "test_day12": 27, "test_calculate_sum": [27, 28], "test_spring_lin": [27, 28], "springlin": [27, 28], "broken_spr": 27, "big_cach": 27, "state": [27, 31, 49, 50, 51], "backtrack": 27, "let": 27, "calculate_recurs": 27, "recurs": 27, "empti": [27, 31, 38], "chop": 27, "continu": [27, 47, 66], "enforc": 27, "anyth": 27, "wrong": 27, "fail": [27, 47], "set_and_return": 27, "unfold": 27, "5x": 27, "bigger": 27, "Thes": 27, "tate": 27, "spring": 27, "iff": 27, "complet": 27, "without": 27, "error": 27, "calculate_sum": [27, 28], "spring_lin": 27, "everi": [27, 34, 37, 49], "test_day13": 29, "test_dist": [29, 30], "maze": [29, 52, 53, 57, 58, 59], "check_reflect": 29, "target_dist": 29, "reflect": 29, "reflect_col": 29, "flip": [29, 50, 57, 58], "reflect_row": 29, "score": [29, 31], "row_reflect": 29, "col_reflect": 29, "edit": 29, "equal": [29, 50], "mirror": [29, 38], "smudg": 29, "next_direction_ccw": [31, 32], "next_direction_cw": [31, 32], "test_day14": 31, "test_quest": [31, 33, 34, 36], "test_rotate_world": [31, 33], "test_simulate_row": [31, 33], "left_i": 31, "boulder": 31, "squar": 31, "round": 31, "as_orientiented_north": 31, "ha": [31, 47, 61], "correct_sid": 31, "orient": 31, "top": [31, 53], "get_scor": 31, "rotate_world_ccw": 31, "rotat": [31, 33], "anti": 31, "clockwis": [31, 32], "rotate_world_cw": 31, "to_str": 31, "question1": [31, 34, 36], "naive_scor": 31, "world_row": 31, "assum": [31, 46, 53], "For": [31, 49, 50], "minu": 31, "higher": 31, "weight": 31, "onc": 31, "found": 31, "estim": 31, "amount": [31, 54], "spin": 31, "1000000000": 31, "simulate_row": 31, "simul": [31, 33, 49, 52], "simulate_world": 31, "roll": 31, "intenum": [32, 35, 38, 41, 43], "ccw": 32, "addremov": [34, 35], "remov": [34, 35, 54], "add_len": [34, 35], "calculate_pow": [34, 35], "remove_len": [34, 35], "len": [34, 35, 36], "focal_length": [34, 35], "lens_nam": [34, 35], "test_box": [34, 36], "test_len": [34, 36], "test_day15": 34, "test_get_string_hash": [34, 36], "test_parse_pt2": [34, 36], "instruct": [34, 35], "get_string_hash": 34, "hash": [34, 36, 53], "call": [34, 55], "parse_step_pt2": 34, "raw_step": 34, "process_steps_pt2": 34, "factori": [35, 38, 58], "contain": [35, 41], "replac": [35, 52, 53], "exist": 35, "swap": 35, "lens": 35, "cell": [37, 39, 53, 55], "backslashcel": [37, 38, 39], "next_las": [37, 38], "cell_typ": [37, 38], "register_cell_typ": [37, 38], "dashcel": [37, 38, 39], "dotcel": [37, 38, 39], "forwardslashcel": [37, 38, 39], "pipecel": [37, 38, 39], "offset": [37, 38, 40, 41, 43, 55, 58], "laser": 37, "solvedworld": [37, 38], "add_las": [37, 38], "already_solv": [37, 38], "num_energ": [37, 38], "is_oob": [37, 38, 40, 41, 43, 52, 53, 57, 58], "test_cel": 37, "test_backslashcel": [37, 39], "test_dashcel": [37, 39], "test_dotcel": [37, 39], "test_forwardslashcel": [37, 39], "test_pipecel": [37, 39], "test_day16": 37, "test_world": 37, "energ": [37, 38], "most": 37, "fire": 37, "solve_task": 37, "task": 37, "solve_task_wrapp": 37, "arg": [37, 49, 53, 58], "wrap": 37, "multiprocess": [37, 58], "sinc": [37, 43, 49, 53], "onli": [37, 50, 55, 58], "take": 37, "diagon": 38, "mode": 38, "abc": [38, 50, 53], "abstract": [38, 50, 53], "proper": 38, "enter": [38, 55], "cell_cont": 38, "regist": [38, 55], "must": 38, "pass": [38, 43, 46, 47, 49, 60], "dot": [38, 49, 50], "directli": 38, "instanc": 38, "form": [38, 43, 46], "store": [38, 50, 55], "bound": [38, 41, 43, 53, 58], "start_las": 38, "solutioncach": [40, 41], "add_solut": [40, 41], "cach": [40, 41], "consecutive_step": [40, 41], "src_step": [40, 41], "total_cost": [40, 41], "tilecach": [40, 41], "cache_max": [40, 41], "cache_min": [40, 41], "worldpart1": [40, 41], "cost": [40, 41], "create_step": [40, 41], "worldpart2": [40, 41], "offset_list": [40, 41], "test_day17": 40, "test_part": [40, 42], "minimum": [40, 43], "max": [40, 43, 47], "min": [40, 43, 47], "10": [40, 53, 55], "solve_and_print": 40, "17": [41, 42], "improv": 41, "made": 41, "which": [41, 55, 65], "could": 41, "multi": 41, "shortest": 41, "rout": [41, 57, 58], "creat": [41, 43, 47, 58, 67], "previou": 41, "suboptim": 41, "dynam": 41, "program": 41, "src": [41, 49, 50, 63], "extens": 41, "overrid": 41, "dp": 41, "vector": [41, 43, 53, 55, 58, 61], "dataclass": [41, 43], "edgetil": [43, 44], "text_whit": [43, 44], "text_color": [43, 44], "holetil": [43, 44], "test_day18a": 43, "test_day18b": 43, "test_command": [43, 45], "command": 43, "udlr": 43, "d": 43, "min_po": 43, "max_po": 43, "dig_out": 43, "dig": 43, "non": [43, 44], "perimet": 43, "dug_til": 43, "process_command": 43, "miner_po": 43, "miner": 43, "around": 43, "wall_til": 43, "generate_offset": 43, "travel": 43, "direciton": 43, "get_matrix_rang": 43, "maximum": [43, 55], "middl": 43, "somewher": 43, "neg": 43, "get_solut": 43, "pre": 43, "edg": [43, 44, 50, 52, 57, 58], "centr": [43, 53], "hexcod": 43, "hexstr": 43, "calculate_area": 43, "area": 43, "shoelac": 43, "solver": [43, 58, 59], "x1b": 44, "38": 44, "255": 44, "255m": 44, "ansicod": 44, "dug": 44, "dugout": 44, "day18a": 45, "day18b": 45, "hex": 45, "convers": 45, "compar": [46, 47], "greaterthan": [46, 47], "lessthan": [46, 47], "compon": [46, 47], "m": [46, 47, 48], "x": [46, 47, 48, 53, 54, 55, 60, 61], "process_part": [46, 47], "process_part_rang": [46, 47], "sign": [46, 47], "clone_modifi": [46, 47], "get_valu": [46, 47], "rate": [46, 47, 54], "partrang": [46, 47, 48], "max_valu": [46, 47], "min_valu": [46, 47], "partrangedest": [46, 47, 48], "part_rang": [46, 47], "parse_condition_str": [46, 47], "parse_part_str": [46, 47], "parse_rule_str": [46, 47], "parse_workflow_str": [46, 47], "get_part_rang": [46, 48], "test_part_rang": [46, 48], "test_part_range_dest": [46, 48], "test_rul": [46, 48], "test_workflow": [46, 48], "test_day19": 46, "test_parse_condition_str": [46, 48], "test_parse_part_str": [46, 48], "test_parse_rule_str": [46, 48], "test_parse_workflow_str": [46, 48], "reject": 46, "solve_part2": 46, "xma": [46, 47], "4000": 46, "succe": 47, "unsupport": 47, "success": 47, "option": [47, 52, 58], "clone": 47, "modifi": [47, 50, 58], "chang": [47, 50, 53], "split_valu": 47, "chosen": 47, "splitvalu": 47, "In": 47, "case": 47, "fall": [47, 54, 55], "whole": 47, "side": [47, 55], "100": 47, "200": 47, "50": 47, "150": 47, "combinatoin": 47, "consist": 47, "succeed": 47, "bunch": [47, 53], "off": 47, "failur": 47, "chain": 47, "pure": 47, "cond_str": 47, "2006": [47, 48], "part_str": 47, "represent": 47, "787": [47, 48], "2655": [47, 48], "1222": [47, 48], "2876": [47, 48], "rule_str": 47, "qkq": [47, 48], "rfg": [47, 48], "workflow_str": 47, "px": [47, 48], "2090": [47, 48], "part19": 47, "reusabl": 48, "basemodul": [49, 50], "add_to_graph": [49, 50], "arrow_color": [49, 50], "handle_puls": [49, 50], "is_initial_st": [49, 50], "num_high": [49, 50], "num_low": [49, 50], "output": [49, 50], "broadcastmodul": [49, 50], "conjunctionmodul": [49, 50], "current_count": [49, 50], "set_input": [49, 50], "flipflopmodul": [49, 50], "loopcount": [49, 50, 51], "add_result": [49, 50], "finish": [49, 50], "loop_length": [49, 50], "num_result": [49, 50], "target_loop_count": [49, 50], "mappingmodul": [49, 50], "modulegroup": [49, 50], "all_nod": [49, 50], "head": [49, 50], "loop_tail": [49, 50], "penultim": [49, 50], "sink": [49, 50, 51], "puls": [49, 50], "low": [49, 50], "pulsetarget": [49, 50], "target": [49, 50], "sinkmodul": [49, 50], "finalize_modul": [49, 50, 51], "get_modul": [49, 50, 51], "test_loop_count": [49, 51], "test_modul": [49, 51], "test_day20": 49, "test_finalize_modul": [49, 51], "test_get_modul": [49, 51], "test_parse_lin": [49, 51], "export_graph": 49, "graph": [49, 50, 63], "module_group": 49, "simulation_count": 49, "export": 49, "graphviz": [49, 50], "datatyp": 49, "enabl": 49, "get_loop_path": 49, "start_switch": 49, "module_map": 49, "longest": 49, "conjunct": [49, 50], "flipflop": 49, "get_module_group": 49, "respect": 49, "pipelin": 49, "get_typed_modul": 49, "kei": [49, 65], "module_typ": 49, "typecast": 49, "graph_modul": 49, "digraph": [49, 50], "output_fil": 49, "save": 49, "output_graph": 49, "output_graph_wrapp": 49, "process_map": 49, "doesnt": [49, 58], "support": [49, 54, 55], "star_arg": 49, "gotta": 49, "path_is_start_st": 49, "sure": 49, "initi": [49, 50, 55], "stored_puls": 49, "append": 49, "inheritor": 50, "need": [50, 52], "repr": 50, "arrow": 50, "keep": 50, "track": 50, "broadcast": [50, 51], "node": [50, 53, 57, 58, 63], "immedi": 50, "alwai": [50, 51], "intern": 50, "send": 50, "sent": 50, "receiv": 50, "noth": 50, "everyon": 50, "ourselv": 50, "forward": 50, "loop_nam": 50, "loop_count": 50, "had": 50, "ignor": 50, "loop_lenght": 50, "submit": 50, "group": 50, "flag": 50, "dest": [50, 63], "eat": 50, "never": 50, "onward": 50, "Then": 50, "inplac": [50, 58], "inv": 50, "con": 50, "default": [51, 52], "basedistancemaz": [52, 53], "calc_step": [52, 53], "overlai": [52, 53, 57, 58], "distancemaz": [52, 53], "centre_cel": [52, 53], "grid": [52, 53, 57, 58], "int_to_str": [52, 53], "is_complet": [52, 53], "cols_per_maz": [52, 53], "get_big_grid": [52, 53], "get_split_po": [52, 53], "rows_per_maz": [52, 53], "giantnodepars": [52, 53], "distance_maz": [52, 53], "edge_dist": [52, 53], "full_edge_dist": [52, 53], "get_nod": [52, 53, 57, 58], "get_node_count": [52, 53], "giantnodetyp": [52, 53], "east_tip": [52, 53], "full_even": [52, 53], "full_odd": [52, 53], "north_east_big": [52, 53], "north_east_smal": [52, 53], "north_tip": [52, 53], "north_west_big": [52, 53], "north_west_smal": [52, 53], "south_east_big": [52, 53], "south_east_smal": [52, 53], "south_tip": [52, 53], "south_west_big": [52, 53], "south_west_smal": [52, 53], "west_tip": [52, 53], "positiondist": [52, 53], "parse_maz": [52, 53], "smartstep": 52, "boards_to_edg": 52, "board": 52, "calculate_smart_step": 52, "board_siz": 52, "num": 52, "actual": 52, "mini_solv": 52, "start_po": [52, 54, 55], "naive_solv": 52, "unlimited_map": 52, "brute_forc": 52, "infinit": [52, 53], "modulo": 53, "ing": 53, "odd": 53, "even": 53, "integert": 53, "abl": 53, "extend": 53, "pariti": 53, "global": 53, "Into": 53, "coord": 53, "gigamap": 53, "onto": [53, 55, 58], "nodes_to_edg": 53, "giant": 53, "node_typ": 53, "requir": 53, "mazes_center_to_bottom": 53, "16": 53, "12": 53, "13": 53, "8": 53, "unsaf": 53, "inherit": 53, "copi": [53, 57, 58], "suppli": 53, "boxdata": [54, 55, 56], "end_po": [54, 55], "hat": [54, 55], "recursive_fal": [54, 55], "select": [54, 55], "set_hat": [54, 55], "set_support": [54, 55], "set_vbox": [54, 55], "total_hat": [54, 55], "unselect": [54, 55], "vbox": [54, 55], "vpo": [54, 55], "z_val_bot": [54, 55], "z_val_top": [54, 55], "can_fall_down": [54, 55], "can_fly_up": [54, 55], "get_hat": [54, 55], "get_support": [54, 55], "layer": [54, 55], "register_box": [54, 55], "vector3": [54, 55, 60, 61], "y": [54, 55, 60, 61], "z": [54, 55, 60, 61], "get_box": [54, 55, 56], "parse_vector": [54, 55], "vi": [54, 56], "animate_part1": [54, 55], "animate_part2": [54, 55], "bind_kei": [54, 55], "construct_box": [54, 55], "follow_block": [54, 55], "init_vi": [54, 55], "random_color": [54, 55], "test_box_data": [54, 56], "test_day22": 54, "test_visu": [54, 56], "visual": [54, 55], "anim": [54, 55], "calculate_part1": 54, "fly": [54, 55], "calculate_part2": 54, "float": [54, 55, 61], "snap": 54, "camera": [54, 55], "has_start": 54, "vis_rat": 54, "wait": 54, "while": 54, "3d": [55, 61], "block": 55, "vertic": 55, "accord": 55, "vpython": 55, "already_fal": 55, "abov": 55, "put": 55, "po": [55, 61], "self": 55, "z_height": 55, "400": 55, "xy": [55, 60, 61], "downward": 55, "re": 55, "clear": 55, "rest": 55, "on_key_down": 55, "bind": 55, "keyboard": 55, "event": 55, "callback": 55, "box_data": 55, "mimic": 55, "random": 55, "compat": 55, "get_cell_branch": [57, 58], "can_add": [57, 58], "copy_modifi": [57, 58], "solver1": [57, 58, 59], "expand_hil": [57, 58], "expand_path": [57, 58], "handle_hil": [57, 58], "generate_path": [57, 58, 59], "classes2": 57, "node1": [57, 58], "node2": [57, 58], "solver2": [57, 58, 59], "build_nod": [57, 58], "calculate_edg": [57, 58], "input_maz": [57, 58], "solve2": [57, 58], "solve2_help": [57, 58], "get_maz": [57, 58, 59], "test_generate_path": [57, 59], "test_maz": [57, 59], "test_path": [57, 59], "test_posit": [57, 59], "test_solver1": [57, 59], "test_classes2": 57, "test_solver2": [57, 59], "test_day23": 57, "test_solv": [57, 59], "test_get_maz": [57, 59], "maxim": 57, "come": 58, "visit": 58, "revers": 58, "order": 58, "neat": 58, "bf": 58, "place": 58, "between": [58, 63], "fork": 58, "build": 58, "start_nod": 58, "nuke": 58, "mark": 58, "note": 58, "popul": 58, "seen": 58, "forks_remain": 58, "df": 58, "threadpoolexecutor": 58, "starmap": 58, "helper": 58, "hailston": [60, 61], "veloc": [60, 61], "vector2": [60, 61], "parse_vector3": [60, 61, 62], "test_day24": 60, "test_get_intersection_2d": [60, 62], "test_within_2d": [60, 62], "test_vector3": [60, 62], "get_intersection_2d": [60, 62], "valid_rang": 60, "within": 60, "rectangl": 60, "magic": 60, "within_2d": [60, 62], "min_max": 60, "symmetr": 60, "24": 60, "day23": [61, 68], "test_day25": 63, "test_get_data": [63, 64], "test_parse_connect": [63, 64], "node_nam": 63, "get_data": [63, 64], "parse_connect": [63, 64], "dest1": 63, "dest2": 63, "dest3": 63, "show_graph": 63, "solve_nod": [63, 64], "download": 65, "adventofcod": 65, "download_fil": 65, "session": 65, "folder": 65, "integr": 66, "modul": [66, 68], "accessori": 67, "touch_dai": 67, "relev": 67, "init": 67, "day01": 68, "packag": 68, "day02": 68, "day03": 68, "day04": 68, "day05": 68, "day06": 68, "day07": 68, "day08": 68, "day09": 68, "day10": 68, "day11": 68, "day12": 68, "day13": 68, "day14": 68, "day15": 68, "day16": 68, "day17": 68, "day18": 68, "day19": 68, "day20": 68, "day21": 68, "day22": 68, "day24": 68, "day25": 68, "download_input": 68, "maker": 68}, "objects": {"": [[2, 0, 0, "-", "day01"], [4, 0, 0, "-", "day02"], [6, 0, 0, "-", "day03"], [9, 0, 0, "-", "day04"], [11, 0, 0, "-", "day05"], [14, 0, 0, "-", "day06"], [16, 0, 0, "-", "day07"], [18, 0, 0, "-", "day08"], [20, 0, 0, "-", "day09"], [22, 0, 0, "-", "day10"], [25, 0, 0, "-", "day11"], [27, 0, 0, "-", "day12"], [29, 0, 0, "-", "day13"], [31, 0, 0, "-", "day14"], [34, 0, 0, "-", "day15"], [37, 0, 0, "-", "day16"], [40, 0, 0, "-", "day17"], [43, 0, 0, "-", "day18"], [46, 0, 0, "-", "day19"], [49, 0, 0, "-", "day20"], [52, 0, 0, "-", "day21"], [54, 0, 0, "-", "day22"], [57, 0, 0, "-", "day23"], [60, 0, 0, "-", "day24"], [63, 0, 0, "-", "day25"], [65, 0, 0, "-", "download_inputs"], [67, 0, 0, "-", "maker"]], "day01": [[2, 0, 0, "-", "day1a"], [2, 0, 0, "-", "day1b"], [3, 0, 0, "-", "tests"]], "day01.day1a": [[2, 1, 1, "", "get_first_last"], [2, 1, 1, "", "get_input"], [2, 1, 1, "", "main"], [2, 1, 1, "", "part1"]], "day01.day1b": [[2, 2, 1, "", "IndexValue"], [2, 2, 1, "", "WordNumber"], [2, 1, 1, "", "get_input"], [2, 1, 1, "", "main"], [2, 1, 1, "", "part2"], [2, 1, 1, "", "process_line"]], "day01.day1b.IndexValue": [[2, 3, 1, "", "index"], [2, 3, 1, "", "value"]], "day01.day1b.WordNumber": [[2, 3, 1, "", "number"], [2, 3, 1, "", "word"]], "day01.tests": [[3, 0, 0, "-", "test_day1a"], [3, 0, 0, "-", "test_day1b"]], "day01.tests.test_day1a": [[3, 1, 1, "", "test_get_first_last"], [3, 1, 1, "", "test_get_input"], [3, 1, 1, "", "test_part1"]], "day01.tests.test_day1b": [[3, 1, 1, "", "test_get_input"], [3, 1, 1, "", "test_index_value"], [3, 1, 1, "", "test_part2"], [3, 1, 1, "", "test_process_line"]], "day02": [[4, 0, 0, "-", "day2"], [5, 0, 0, "-", "tests"]], "day02.day2": [[4, 2, 1, "", "Color"], [4, 2, 1, "", "Draw"], [4, 2, 1, "", "Game"], [4, 1, 1, "", "game_filter"], [4, 1, 1, "", "get_games"], [4, 1, 1, "", "main"], [4, 1, 1, "", "part1"], [4, 1, 1, "", "part2"]], "day02.day2.Color": [[4, 3, 1, "", "BLUE"], [4, 3, 1, "", "GREEN"], [4, 3, 1, "", "RED"]], "day02.day2.Draw": [[4, 3, 1, "", "blue"], [4, 3, 1, "", "green"], [4, 4, 1, "", "parse_color_count"], [4, 4, 1, "", "parse_colors_count"], [4, 3, 1, "", "red"]], "day02.day2.Game": [[4, 3, 1, "", "blue"], [4, 3, 1, "", "green"], [4, 3, 1, "", "id"], [4, 4, 1, "", "parse_draws"], [4, 4, 1, "", "power_level"], [4, 3, 1, "", "red"]], "day02.tests": [[5, 0, 0, "-", "test_day2"]], "day02.tests.test_day2": [[5, 1, 1, "", "get_game1"], [5, 1, 1, "", "get_game2"], [5, 1, 1, "", "test_draw"], [5, 1, 1, "", "test_game"], [5, 1, 1, "", "test_part1"], [5, 1, 1, "", "test_part2"]], "day03": [[6, 0, 0, "-", "day3"], [7, 0, 0, "-", "lib"], [8, 0, 0, "-", "tests"]], "day03.day3": [[6, 1, 1, "", "main"], [6, 1, 1, "", "part1"], [6, 1, 1, "", "part2"]], "day03.lib": [[7, 0, 0, "-", "classes"], [7, 0, 0, "-", "parsers"]], "day03.lib.classes": [[7, 2, 1, "", "Gear"], [7, 2, 1, "", "Matrix"], [7, 2, 1, "", "PartNumber"]], "day03.lib.classes.Gear": [[7, 3, 1, "", "col"], [7, 5, 1, "", "gear_ratio"], [7, 3, 1, "", "part_numbers"], [7, 3, 1, "", "row"]], "day03.lib.classes.Matrix": [[7, 3, 1, "", "data"], [7, 4, 1, "", "filter_engine_parts"], [7, 4, 1, "", "find_gear_parts"], [7, 4, 1, "", "get_gears"], [7, 4, 1, "", "get_part_numbers"], [7, 4, 1, "", "is_engine_part"], [7, 4, 1, "", "is_engine_part_row"], [7, 5, 1, "", "row_count"], [7, 5, 1, "", "row_size"]], "day03.lib.classes.PartNumber": [[7, 3, 1, "", "col"], [7, 5, 1, "", "end_index"], [7, 3, 1, "", "length"], [7, 3, 1, "", "row"], [7, 4, 1, "", "touching"], [7, 3, 1, "", "value"]], "day03.lib.parsers": [[7, 1, 1, "", "get_matrix"]], "day03.tests": [[8, 0, 0, "-", "test_classes"], [8, 0, 0, "-", "test_day3"]], "day03.tests.test_classes": [[8, 2, 1, "", "PartNumberTouchTest"], [8, 1, 1, "", "test_gear"], [8, 1, 1, "", "test_matrix"], [8, 1, 1, "", "test_part_number"]], "day03.tests.test_classes.PartNumberTouchTest": [[8, 3, 1, "", "col"], [8, 3, 1, "", "result"], [8, 3, 1, "", "row"], [8, 3, 1, "", "row_size"]], "day03.tests.test_day3": [[8, 1, 1, "", "test_part1"], [8, 1, 1, "", "test_part2"]], "day04": [[9, 0, 0, "-", "day4"], [10, 0, 0, "-", "tests"]], "day04.day4": [[9, 2, 1, "", "Card"], [9, 2, 1, "", "Inventory"], [9, 1, 1, "", "grab_data"], [9, 1, 1, "", "main"], [9, 1, 1, "", "part1"], [9, 1, 1, "", "part2"], [9, 1, 1, "", "split_numbers"]], "day04.day4.Card": [[9, 4, 1, "", "get_matches"], [9, 4, 1, "", "get_points"], [9, 3, 1, "", "have"], [9, 3, 1, "", "id"], [9, 3, 1, "", "winners"]], "day04.day4.Inventory": [[9, 3, 1, "", "all_cards"], [9, 4, 1, "", "calculate_mappings"], [9, 3, 1, "", "memoized"], [9, 4, 1, "", "total_cards"]], "day04.tests": [[10, 0, 0, "-", "test_day4"]], "day04.tests.test_day4": [[10, 1, 1, "", "test_card"], [10, 1, 1, "", "test_grab_data"], [10, 1, 1, "", "test_inventory"], [10, 1, 1, "", "test_part1"], [10, 1, 1, "", "test_part2"], [10, 1, 1, "", "test_split_numbers"]], "day05": [[11, 0, 0, "-", "day5"], [12, 0, 0, "-", "lib"], [13, 0, 0, "-", "tests"]], "day05.day5": [[11, 1, 1, "", "get_location"], [11, 1, 1, "", "get_location_ranges"], [11, 1, 1, "", "main"], [11, 1, 1, "", "part1"], [11, 1, 1, "", "part2"], [11, 1, 1, "", "seed_to_mapping_ranges"]], "day05.lib": [[12, 0, 0, "-", "classes"], [12, 0, 0, "-", "parsers"]], "day05.lib.classes": [[12, 2, 1, "", "Mapping"], [12, 2, 1, "", "MappingRange"], [12, 2, 1, "", "NamedMap"]], "day05.lib.classes.Mapping": [[12, 3, 1, "", "dest_end"], [12, 3, 1, "", "dest_start"], [12, 4, 1, "", "get_mapping"], [12, 4, 1, "", "get_mappings"], [12, 3, 1, "", "injected"], [12, 3, 1, "", "size"], [12, 3, 1, "", "src_end"], [12, 3, 1, "", "src_start"]], "day05.lib.classes.MappingRange": [[12, 3, 1, "", "end"], [12, 3, 1, "", "start"]], "day05.lib.classes.NamedMap": [[12, 4, 1, "", "add_mapping"], [12, 4, 1, "", "extend_mapping_range"], [12, 4, 1, "", "finalize_mappings"], [12, 4, 1, "", "get_mapping"], [12, 4, 1, "", "get_mapping_range"], [12, 4, 1, "", "get_mapping_ranges"], [12, 3, 1, "", "mappings"], [12, 3, 1, "", "name"]], "day05.lib.parsers": [[12, 1, 1, "", "grab_inputs"]], "day05.tests": [[13, 0, 0, "-", "test_day5"]], "day05.tests.test_day5": [[13, 1, 1, "", "test_mapping"], [13, 1, 1, "", "test_part1"], [13, 1, 1, "", "test_part2"], [13, 1, 1, "", "test_seed_to_mapping_ranges"]], "day06": [[14, 0, 0, "-", "day6"], [15, 0, 0, "-", "tests"]], "day06.day6": [[14, 2, 1, "", "Race"], [14, 2, 1, "", "RaceStrat"], [14, 1, 1, "", "calculate_constant_time"], [14, 1, 1, "", "calculate_race"], [14, 1, 1, "", "get_giga_race"], [14, 1, 1, "", "main"], [14, 1, 1, "", "part1"], [14, 1, 1, "", "part2"], [14, 1, 1, "", "read_inputs"]], "day06.day6.Race": [[14, 3, 1, "", "record_distance"], [14, 3, 1, "", "time"]], "day06.day6.RaceStrat": [[14, 3, 1, "", "charge_time"], [14, 5, 1, "", "distance"], [14, 3, 1, "", "run_time"], [14, 5, 1, "", "speed"]], "day06.tests": [[15, 0, 0, "-", "test_day6"]], "day06.tests.test_day6": [[15, 1, 1, "", "test_calculate_constant_time"], [15, 1, 1, "", "test_calculate_race"], [15, 1, 1, "", "test_get_giga_race"], [15, 1, 1, "", "test_part1"], [15, 1, 1, "", "test_part2"], [15, 1, 1, "", "test_read_inputs"]], "day07": [[16, 0, 0, "-", "day7"], [17, 0, 0, "-", "tests"]], "day07.day7": [[16, 2, 1, "", "Hand"], [16, 2, 1, "", "HandPart2"], [16, 1, 1, "", "calculate_hands"], [16, 1, 1, "", "main"], [16, 1, 1, "", "parse_lines"]], "day07.day7.Hand": [[16, 3, 1, "", "CARD_MAPPING"], [16, 3, 1, "", "bet"], [16, 4, 1, "", "calculate_of_a_kind"], [16, 3, 1, "", "cards"], [16, 3, 1, "", "cards_inted"], [16, 3, 1, "", "of_a_kind"]], "day07.day7.HandPart2": [[16, 3, 1, "", "CARD_MAPPING"], [16, 4, 1, "", "calculate_of_a_kind"]], "day07.tests": [[17, 0, 0, "-", "test_day7"]], "day07.tests.test_day7": [[17, 1, 1, "", "test_calculate_hands"], [17, 1, 1, "", "test_hand"], [17, 1, 1, "", "test_parser"]], "day08": [[18, 0, 0, "-", "day8"], [19, 0, 0, "-", "tests"]], "day08.day8": [[18, 2, 1, "", "Cycle"], [18, 2, 1, "", "Directions"], [18, 2, 1, "", "Location"], [18, 2, 1, "", "LocationStep"], [18, 2, 1, "", "WorldMap"], [18, 1, 1, "", "find_cycle"], [18, 1, 1, "", "follow_directions"], [18, 1, 1, "", "follow_directions_multi"], [18, 1, 1, "", "get_location_as"], [18, 1, 1, "", "main"], [18, 1, 1, "", "read_input"]], "day08.day8.Cycle": [[18, 5, 1, "", "cycle_length"], [18, 3, 1, "", "cycle_start"], [18, 3, 1, "", "cycle_start_index"], [18, 3, 1, "", "end_zs"], [18, 4, 1, "", "get_location"], [18, 3, 1, "", "location_steps"], [18, 3, 1, "", "start_location"]], "day08.day8.Directions": [[18, 4, 1, "", "get_step"], [18, 4, 1, "", "get_steps_iterator"], [18, 3, 1, "", "steps"]], "day08.day8.Location": [[18, 3, 1, "", "left"], [18, 3, 1, "", "name"], [18, 3, 1, "", "right"]], "day08.day8.LocationStep": [[18, 3, 1, "", "location"], [18, 3, 1, "", "steps"]], "day08.day8.WorldMap": [[18, 4, 1, "", "add_location"], [18, 3, 1, "", "mappings"]], "day08.tests": [[19, 0, 0, "-", "test_day8"]], "day08.tests.test_day8": [[19, 1, 1, "", "test_directions"], [19, 1, 1, "", "test_find_cycle"], [19, 1, 1, "", "test_location_as"], [19, 1, 1, "", "test_part1"], [19, 1, 1, "", "test_part2"]], "day09": [[20, 0, 0, "-", "day9"], [21, 0, 0, "-", "tests"]], "day09.day9": [[20, 2, 1, "", "ValueArray"], [20, 1, 1, "", "get_input"], [20, 1, 1, "", "interpolate"], [20, 1, 1, "", "main"], [20, 1, 1, "", "part1"], [20, 1, 1, "", "part2"]], "day09.day9.ValueArray": [[20, 4, 1, "", "extrapolate_left"], [20, 4, 1, "", "extrapolate_right"], [20, 4, 1, "", "generic_extrapolate"], [20, 3, 1, "", "sub_arrays"]], "day09.tests": [[21, 0, 0, "-", "test_day9"]], "day09.tests.test_day9": [[21, 1, 1, "", "test_get_input"], [21, 1, 1, "", "test_interpolate"], [21, 1, 1, "", "test_part1"], [21, 1, 1, "", "test_part2"]], "day10": [[22, 0, 0, "-", "day10"], [23, 0, 0, "-", "lib"], [24, 0, 0, "-", "tests"]], "day10.day10": [[22, 1, 1, "", "calculate_s"], [22, 1, 1, "", "expand_map"], [22, 1, 1, "", "expand_pipe"], [22, 1, 1, "", "find_cycles"], [22, 1, 1, "", "find_s"], [22, 1, 1, "", "flood_fill"], [22, 1, 1, "", "main"], [22, 1, 1, "", "part1"], [22, 1, 1, "", "part2"], [22, 1, 1, "", "process_big_input_line"], [22, 1, 1, "", "process_input_line"], [22, 1, 1, "", "read_input"], [22, 1, 1, "", "reduce_map"]], "day10.lib": [[23, 0, 0, "-", "direction"], [23, 0, 0, "-", "pipebounds"], [23, 0, 0, "-", "pipes"], [23, 0, 0, "-", "position"]], "day10.lib.direction": [[23, 2, 1, "", "Direction"]], "day10.lib.direction.Direction": [[23, 3, 1, "", "EAST"], [23, 3, 1, "", "NORTH"], [23, 3, 1, "", "SOUTH"], [23, 3, 1, "", "WEST"], [23, 4, 1, "", "opposite"]], "day10.lib.pipebounds": [[23, 2, 1, "", "PipeBounds"]], "day10.lib.pipebounds.PipeBounds": [[23, 3, 1, "", "INSIDE"], [23, 3, 1, "", "OUTSIDE"], [23, 3, 1, "", "PIPE"], [23, 3, 1, "", "UNKNOWN"]], "day10.lib.pipes": [[23, 2, 1, "", "Pipe"], [23, 2, 1, "", "PipeMap"]], "day10.lib.pipes.Pipe": [[23, 3, 1, "", "PIPE_DIRECTION"], [23, 3, 1, "", "character"], [23, 3, 1, "", "col"], [23, 3, 1, "", "font"], [23, 3, 1, "", "is_loop"], [23, 3, 1, "", "is_start"], [23, 4, 1, "", "next_direction"], [23, 4, 1, "", "next_position"], [23, 3, 1, "", "pipe_bounds"], [23, 5, 1, "", "position"], [23, 3, 1, "", "row"]], "day10.lib.pipes.PipeMap": [[23, 4, 1, "", "get_pipe"], [23, 4, 1, "", "get_pipe_safe"], [23, 3, 1, "", "height"], [23, 4, 1, "", "is_in_map"], [23, 3, 1, "", "pipes"], [23, 3, 1, "", "width"]], "day10.lib.position": [[23, 2, 1, "", "Position"]], "day10.lib.position.Position": [[23, 3, 1, "", "col"], [23, 4, 1, "", "next_position"], [23, 3, 1, "", "row"]], "day10.tests": [[24, 0, 0, "-", "test_day10"], [24, 0, 0, "-", "test_direction"]], "day10.tests.test_day10": [[24, 1, 1, "", "test_day10"]], "day10.tests.test_direction": [[24, 1, 1, "", "test_direction"]], "day11": [[25, 0, 0, "-", "day11"], [26, 0, 0, "-", "tests"]], "day11.day11": [[25, 2, 1, "", "Galaxy"], [25, 2, 1, "", "Point"], [25, 2, 1, "", "Universe"], [25, 1, 1, "", "get_total_distance"], [25, 1, 1, "", "is_empty"], [25, 1, 1, "", "main"], [25, 1, 1, "", "parse_input"]], "day11.day11.Galaxy": [[25, 3, 1, "", "col"], [25, 4, 1, "", "distance"], [25, 3, 1, "", "id"], [25, 3, 1, "", "row"]], "day11.day11.Point": [[25, 3, 1, "", "col"], [25, 4, 1, "", "get_point_distance"], [25, 3, 1, "", "is_expanded"], [25, 3, 1, "", "item"], [25, 3, 1, "", "row"]], "day11.day11.Universe": [[25, 3, 1, "", "contents"], [25, 4, 1, "", "expand_contents"], [25, 3, 1, "", "expansion_rate"], [25, 4, 1, "", "grab_galaxies"], [25, 3, 1, "", "num_cols"], [25, 3, 1, "", "num_rows"]], "day11.tests": [[26, 0, 0, "-", "test_day11"]], "day11.tests.test_day11": [[26, 1, 1, "", "test_day11"], [26, 1, 1, "", "test_expansion"], [26, 1, 1, "", "test_is_empty"]], "day12": [[27, 0, 0, "-", "day12"], [28, 0, 0, "-", "tests"]], "day12.day12": [[27, 2, 1, "", "SpringLine"], [27, 2, 1, "", "State"], [27, 1, 1, "", "calculate_sum"], [27, 1, 1, "", "get_input"], [27, 1, 1, "", "main"]], "day12.day12.SpringLine": [[27, 3, 1, "", "big_cache"], [27, 3, 1, "", "broken_springs"], [27, 4, 1, "", "calculate"], [27, 4, 1, "", "calculate_recursive"], [27, 3, 1, "", "items"], [27, 4, 1, "", "set_and_return"], [27, 4, 1, "", "unfold"]], "day12.day12.State": [[27, 3, 1, "", "broken_springs"], [27, 3, 1, "", "items"], [27, 4, 1, "", "valid"]], "day12.tests": [[28, 0, 0, "-", "test_day12"]], "day12.tests.test_day12": [[28, 1, 1, "", "test_calculate_sum"], [28, 1, 1, "", "test_parser"], [28, 1, 1, "", "test_spring_line"]], "day13": [[29, 0, 0, "-", "day13"], [30, 0, 0, "-", "tests"]], "day13.day13": [[29, 2, 1, "", "Maze"], [29, 1, 1, "", "distance"], [29, 1, 1, "", "main"], [29, 1, 1, "", "read_input"]], "day13.day13.Maze": [[29, 4, 1, "", "check_reflection"], [29, 4, 1, "", "reflect_cols"], [29, 4, 1, "", "reflect_rows"], [29, 4, 1, "", "score"], [29, 4, 1, "", "solve"], [29, 3, 1, "", "tiles"]], "day13.tests": [[30, 0, 0, "-", "test_day13"]], "day13.tests.test_day13": [[30, 1, 1, "", "test_day13"], [30, 1, 1, "", "test_distance"]], "day14": [[31, 0, 0, "-", "day14"], [32, 0, 0, "-", "lib"], [33, 0, 0, "-", "tests"]], "day14.day14": [[31, 2, 1, "", "World"], [31, 1, 1, "", "get_input"], [31, 1, 1, "", "main"], [31, 1, 1, "", "naive_score"], [31, 1, 1, "", "question1"], [31, 1, 1, "", "question2"], [31, 1, 1, "", "simulate_row"], [31, 1, 1, "", "simulate_world"]], "day14.day14.World": [[31, 4, 1, "", "as_orientiented_north"], [31, 4, 1, "", "correct_side"], [31, 3, 1, "", "data"], [31, 4, 1, "", "get_score"], [31, 3, 1, "", "left_is"], [31, 4, 1, "", "rotate_world_ccw"], [31, 4, 1, "", "rotate_world_cw"], [31, 3, 1, "", "score"], [31, 4, 1, "", "to_string"]], "day14.lib": [[32, 0, 0, "-", "direction"]], "day14.lib.direction": [[32, 2, 1, "", "Direction"]], "day14.lib.direction.Direction": [[32, 3, 1, "", "East"], [32, 3, 1, "", "North"], [32, 3, 1, "", "South"], [32, 3, 1, "", "West"], [32, 4, 1, "", "next_direction_ccw"], [32, 4, 1, "", "next_direction_cw"]], "day14.tests": [[33, 0, 0, "-", "test_day14"], [33, 0, 0, "-", "test_direction"]], "day14.tests.test_day14": [[33, 1, 1, "", "test_get_input"], [33, 1, 1, "", "test_questions"], [33, 1, 1, "", "test_rotate_world"], [33, 1, 1, "", "test_simulate_row"]], "day14.tests.test_direction": [[33, 1, 1, "", "test_direction"]], "day15": [[34, 0, 0, "-", "day15"], [35, 0, 0, "-", "lib"], [36, 0, 0, "-", "tests"]], "day15.day15": [[34, 1, 1, "", "get_input"], [34, 1, 1, "", "get_string_hash"], [34, 1, 1, "", "main"], [34, 1, 1, "", "parse_step_pt2"], [34, 1, 1, "", "process_steps_pt2"], [34, 1, 1, "", "question1"], [34, 1, 1, "", "question2"]], "day15.lib": [[35, 0, 0, "-", "classes"]], "day15.lib.classes": [[35, 2, 1, "", "AddRemove"], [35, 2, 1, "", "Box"], [35, 2, 1, "", "Lens"], [35, 2, 1, "", "Step"]], "day15.lib.classes.AddRemove": [[35, 3, 1, "", "Add"], [35, 3, 1, "", "Remove"]], "day15.lib.classes.Box": [[35, 4, 1, "", "add_lens"], [35, 4, 1, "", "calculate_power"], [35, 3, 1, "", "contents"], [35, 3, 1, "", "id"], [35, 4, 1, "", "remove_lens"]], "day15.lib.classes.Lens": [[35, 3, 1, "", "focal_length"], [35, 3, 1, "", "name"]], "day15.lib.classes.Step": [[35, 3, 1, "", "box"], [35, 3, 1, "", "focal_length"], [35, 3, 1, "", "lens_name"], [35, 3, 1, "", "process"]], "day15.tests": [[36, 0, 0, "-", "test_classes"], [36, 0, 0, "-", "test_day15"]], "day15.tests.test_classes": [[36, 1, 1, "", "test_box"], [36, 1, 1, "", "test_lens"]], "day15.tests.test_day15": [[36, 1, 1, "", "test_get_input"], [36, 1, 1, "", "test_get_string_hash"], [36, 1, 1, "", "test_parse_pt2"], [36, 1, 1, "", "test_questions"]], "day16": [[37, 0, 0, "-", "day16"], [38, 0, 0, "-", "lib"], [39, 0, 0, "-", "tests"]], "day16.day16": [[37, 1, 1, "", "main"], [37, 1, 1, "", "part1"], [37, 1, 1, "", "part2"], [37, 1, 1, "", "solve_task"], [37, 1, 1, "", "solve_task_wrapper"]], "day16.lib": [[38, 0, 0, "-", "cells"], [38, 0, 0, "-", "direction"], [38, 0, 0, "-", "laser"], [38, 0, 0, "-", "parsers"], [38, 0, 0, "-", "world"]], "day16.lib.cells": [[38, 2, 1, "", "BackSlashCell"], [38, 2, 1, "", "Cell"], [38, 2, 1, "", "DashCell"], [38, 2, 1, "", "DotCell"], [38, 2, 1, "", "ForwardSlashCell"], [38, 2, 1, "", "PipeCell"]], "day16.lib.cells.BackSlashCell": [[38, 4, 1, "", "next_lasers"]], "day16.lib.cells.Cell": [[38, 3, 1, "", "CELL_TYPES"], [38, 4, 1, "", "construct"], [38, 3, 1, "", "contents"], [38, 4, 1, "", "next_lasers"], [38, 4, 1, "", "register_cell_type"]], "day16.lib.cells.DashCell": [[38, 4, 1, "", "next_lasers"]], "day16.lib.cells.DotCell": [[38, 4, 1, "", "next_lasers"]], "day16.lib.cells.ForwardSlashCell": [[38, 4, 1, "", "next_lasers"]], "day16.lib.cells.PipeCell": [[38, 4, 1, "", "next_lasers"]], "day16.lib.direction": [[38, 2, 1, "", "Direction"]], "day16.lib.direction.Direction": [[38, 3, 1, "", "EAST"], [38, 3, 1, "", "NORTH"], [38, 3, 1, "", "SOUTH"], [38, 3, 1, "", "WEST"], [38, 4, 1, "", "offset"], [38, 4, 1, "", "opposite"]], "day16.lib.laser": [[38, 2, 1, "", "Laser"]], "day16.lib.laser.Laser": [[38, 3, 1, "", "col"], [38, 3, 1, "", "direction"], [38, 3, 1, "", "row"]], "day16.lib.parsers": [[38, 1, 1, "", "get_input"]], "day16.lib.world": [[38, 2, 1, "", "SolvedWorld"], [38, 2, 1, "", "World"]], "day16.lib.world.SolvedWorld": [[38, 4, 1, "", "add_laser"], [38, 4, 1, "", "already_solved"], [38, 3, 1, "", "data"], [38, 4, 1, "", "num_energized"]], "day16.lib.world.World": [[38, 3, 1, "", "data"], [38, 4, 1, "", "is_oob"], [38, 3, 1, "", "num_cols"], [38, 3, 1, "", "num_rows"], [38, 4, 1, "", "solve"]], "day16.tests": [[39, 0, 0, "-", "test_cells"], [39, 0, 0, "-", "test_day16"], [39, 0, 0, "-", "test_direction"], [39, 0, 0, "-", "test_world"]], "day16.tests.test_cells": [[39, 1, 1, "", "test_backslashcell"], [39, 1, 1, "", "test_cell"], [39, 1, 1, "", "test_dashcell"], [39, 1, 1, "", "test_dotcell"], [39, 1, 1, "", "test_forwardslashcell"], [39, 1, 1, "", "test_pipecell"]], "day16.tests.test_day16": [[39, 1, 1, "", "test_part1"], [39, 1, 1, "", "test_part2"]], "day16.tests.test_direction": [[39, 1, 1, "", "test_direction"]], "day16.tests.test_world": [[39, 1, 1, "", "test_world"]], "day17": [[40, 0, 0, "-", "day17"], [41, 0, 0, "-", "lib"], [42, 0, 0, "-", "tests"]], "day17.day17": [[40, 1, 1, "", "main"], [40, 1, 1, "", "part1"], [40, 1, 1, "", "part2"], [40, 1, 1, "", "solve_and_print"]], "day17.lib": [[41, 0, 0, "-", "classes"], [41, 0, 0, "-", "direction"], [41, 0, 0, "-", "parsers"]], "day17.lib.classes": [[41, 2, 1, "", "SolutionCache"], [41, 2, 1, "", "Step"], [41, 2, 1, "", "TileCache"], [41, 2, 1, "", "WorldPart1"], [41, 2, 1, "", "WorldPart2"]], "day17.lib.classes.SolutionCache": [[41, 4, 1, "", "add_solution"], [41, 3, 1, "", "cache"]], "day17.lib.classes.Step": [[41, 3, 1, "", "col"], [41, 3, 1, "", "consecutive_steps"], [41, 3, 1, "", "direction"], [41, 3, 1, "", "row"], [41, 3, 1, "", "src_step"], [41, 3, 1, "", "total_cost"]], "day17.lib.classes.TileCache": [[41, 3, 1, "", "cache"], [41, 3, 1, "", "cache_max"], [41, 3, 1, "", "cache_min"]], "day17.lib.classes.WorldPart1": [[41, 3, 1, "", "costs"], [41, 4, 1, "", "create_step"], [41, 4, 1, "", "is_oob"], [41, 3, 1, "", "num_cols"], [41, 3, 1, "", "num_rows"], [41, 4, 1, "", "solve"]], "day17.lib.classes.WorldPart2": [[41, 4, 1, "", "create_step"], [41, 4, 1, "", "solve"]], "day17.lib.direction": [[41, 2, 1, "", "Direction"]], "day17.lib.direction.Direction": [[41, 3, 1, "", "EAST"], [41, 3, 1, "", "NORTH"], [41, 3, 1, "", "SOUTH"], [41, 3, 1, "", "WEST"], [41, 4, 1, "", "offset"], [41, 4, 1, "", "offset_list"], [41, 4, 1, "", "opposite"]], "day17.lib.parsers": [[41, 1, 1, "", "get_input"]], "day17.tests": [[42, 0, 0, "-", "test_day17"], [42, 0, 0, "-", "test_direction"], [42, 0, 0, "-", "test_parsers"]], "day17.tests.test_day17": [[42, 1, 1, "", "test_parts"]], "day17.tests.test_direction": [[42, 1, 1, "", "test_direction"]], "day17.tests.test_parsers": [[42, 1, 1, "", "test_parser"]], "day18": [[43, 0, 0, "-", "day18a"], [43, 0, 0, "-", "day18b"], [44, 0, 0, "-", "lib"], [45, 0, 0, "-", "tests"]], "day18.day18a": [[43, 2, 1, "", "Command"], [43, 2, 1, "", "Direction"], [43, 2, 1, "", "Matrix"], [43, 2, 1, "", "Position"], [43, 1, 1, "", "generate_offsets"], [43, 1, 1, "", "get_input"], [43, 1, 1, "", "get_matrix_range"], [43, 1, 1, "", "get_solution"], [43, 1, 1, "", "main"]], "day18.day18a.Command": [[43, 3, 1, "", "color"], [43, 3, 1, "", "direction"], [43, 3, 1, "", "steps"]], "day18.day18a.Direction": [[43, 3, 1, "", "Down"], [43, 3, 1, "", "Left"], [43, 3, 1, "", "Right"], [43, 3, 1, "", "Up"]], "day18.day18a.Matrix": [[43, 3, 1, "", "contents"], [43, 4, 1, "", "dig_out"], [43, 3, 1, "", "dug_tiles"], [43, 4, 1, "", "is_oob"], [43, 3, 1, "", "max_pos"], [43, 3, 1, "", "min_pos"], [43, 3, 1, "", "num_cols"], [43, 3, 1, "", "num_rows"], [43, 4, 1, "", "process_command"], [43, 3, 1, "", "wall_tiles"]], "day18.day18a.Position": [[43, 3, 1, "", "col"], [43, 3, 1, "", "row"]], "day18.day18b": [[43, 2, 1, "", "Command"], [43, 2, 1, "", "Direction"], [43, 2, 1, "", "Position"], [43, 1, 1, "", "calculate_area"], [43, 1, 1, "", "get_input"], [43, 1, 1, "", "get_solution"], [43, 1, 1, "", "main"], [43, 1, 1, "", "process_command"]], "day18.day18b.Command": [[43, 3, 1, "", "direction"], [43, 3, 1, "", "steps"]], "day18.day18b.Direction": [[43, 3, 1, "", "Down"], [43, 3, 1, "", "Left"], [43, 3, 1, "", "Right"], [43, 3, 1, "", "Up"]], "day18.day18b.Position": [[43, 3, 1, "", "col"], [43, 3, 1, "", "row"]], "day18.lib": [[44, 0, 0, "-", "tile"]], "day18.lib.tile": [[44, 2, 1, "", "EdgeTile"], [44, 2, 1, "", "HoleTile"], [44, 2, 1, "", "Tile"]], "day18.lib.tile.EdgeTile": [[44, 3, 1, "", "TEXT_WHITE"], [44, 3, 1, "", "color"], [44, 3, 1, "", "contents"], [44, 4, 1, "", "text_color"]], "day18.lib.tile.HoleTile": [[44, 3, 1, "", "contents"]], "day18.lib.tile.Tile": [[44, 3, 1, "", "contents"]], "day18.tests": [[45, 0, 0, "-", "test_day18a"], [45, 0, 0, "-", "test_day18b"]], "day18.tests.test_day18a": [[45, 1, 1, "", "test_day18a"]], "day18.tests.test_day18b": [[45, 1, 1, "", "test_command"], [45, 1, 1, "", "test_day18b"]], "day19": [[46, 0, 0, "-", "day19"], [47, 0, 0, "-", "lib"], [48, 0, 0, "-", "tests"]], "day19.day19": [[46, 1, 1, "", "get_input"], [46, 1, 1, "", "main"], [46, 1, 1, "", "part1"], [46, 1, 1, "", "part2"], [46, 1, 1, "", "process_part"], [46, 1, 1, "", "solve_part2"]], "day19.lib": [[47, 0, 0, "-", "classes"], [47, 0, 0, "-", "parsers"]], "day19.lib.classes": [[47, 2, 1, "", "Comparator"], [47, 2, 1, "", "Component"], [47, 2, 1, "", "Condition"], [47, 2, 1, "", "Part"], [47, 2, 1, "", "PartRange"], [47, 2, 1, "", "PartRangeDest"], [47, 2, 1, "", "Rule"], [47, 2, 1, "", "Workflow"]], "day19.lib.classes.Comparator": [[47, 3, 1, "", "GreaterThan"], [47, 3, 1, "", "LessThan"]], "day19.lib.classes.Component": [[47, 3, 1, "", "A"], [47, 3, 1, "", "M"], [47, 3, 1, "", "S"], [47, 3, 1, "", "X"]], "day19.lib.classes.Condition": [[47, 3, 1, "", "component"], [47, 4, 1, "", "process_part"], [47, 4, 1, "", "process_part_range"], [47, 3, 1, "", "sign"], [47, 3, 1, "", "value"]], "day19.lib.classes.Part": [[47, 3, 1, "", "a"], [47, 4, 1, "", "clone_modify"], [47, 4, 1, "", "get_value"], [47, 3, 1, "", "m"], [47, 5, 1, "", "rating"], [47, 3, 1, "", "s"], [47, 3, 1, "", "x"]], "day19.lib.classes.PartRange": [[47, 3, 1, "", "max_values"], [47, 3, 1, "", "min_values"], [47, 4, 1, "", "size"], [47, 4, 1, "", "split"]], "day19.lib.classes.PartRangeDest": [[47, 3, 1, "", "destination"], [47, 3, 1, "", "part_range"]], "day19.lib.classes.Rule": [[47, 3, 1, "", "condition"], [47, 3, 1, "", "destination"], [47, 4, 1, "", "process_part"], [47, 4, 1, "", "process_part_range"]], "day19.lib.classes.Workflow": [[47, 3, 1, "", "name"], [47, 4, 1, "", "process_part"], [47, 4, 1, "", "process_part_range"], [47, 3, 1, "", "rules"]], "day19.lib.parsers": [[47, 1, 1, "", "parse_condition_string"], [47, 1, 1, "", "parse_part_string"], [47, 1, 1, "", "parse_rule_string"], [47, 1, 1, "", "parse_workflow_string"]], "day19.tests": [[48, 0, 0, "-", "test_classes"], [48, 0, 0, "-", "test_day19"], [48, 0, 0, "-", "test_parsers"]], "day19.tests.test_classes": [[48, 1, 1, "", "get_part_range"], [48, 1, 1, "", "test_part_range"], [48, 1, 1, "", "test_part_range_dest"], [48, 1, 1, "", "test_rule"], [48, 1, 1, "", "test_workflow"]], "day19.tests.test_day19": [[48, 1, 1, "", "test_day19"]], "day19.tests.test_parsers": [[48, 1, 1, "", "test_parse_condition_string"], [48, 1, 1, "", "test_parse_part_string"], [48, 1, 1, "", "test_parse_rule_string"], [48, 1, 1, "", "test_parse_workflow_string"]], "day20": [[49, 0, 0, "-", "day20"], [50, 0, 0, "-", "lib"], [51, 0, 0, "-", "tests"]], "day20.day20": [[49, 1, 1, "", "export_graph"], [49, 1, 1, "", "get_loop_paths"], [49, 1, 1, "", "get_module_groups"], [49, 1, 1, "", "get_typed_module"], [49, 1, 1, "", "graph_modules"], [49, 1, 1, "", "main"], [49, 1, 1, "", "output_files"], [49, 1, 1, "", "output_graph"], [49, 1, 1, "", "output_graph_wrapper"], [49, 1, 1, "", "part1"], [49, 1, 1, "", "part2"], [49, 1, 1, "", "path_is_start_state"], [49, 1, 1, "", "simulate"]], "day20.lib": [[50, 0, 0, "-", "classes"], [50, 0, 0, "-", "parsers"]], "day20.lib.classes": [[50, 2, 1, "", "BaseModule"], [50, 2, 1, "", "BroadcastModule"], [50, 2, 1, "", "ConjunctionModule"], [50, 2, 1, "", "FlipFlopModule"], [50, 2, 1, "", "LoopCounter"], [50, 2, 1, "", "MappingModule"], [50, 2, 1, "", "ModuleGroups"], [50, 2, 1, "", "Pulse"], [50, 2, 1, "", "PulseTarget"], [50, 2, 1, "", "SinkModule"]], "day20.lib.classes.BaseModule": [[50, 4, 1, "", "add_to_graph"], [50, 4, 1, "", "arrow_color"], [50, 4, 1, "", "handle_pulse"], [50, 4, 1, "", "is_initial_state"], [50, 3, 1, "", "name"], [50, 3, 1, "", "num_high"], [50, 3, 1, "", "num_low"], [50, 3, 1, "", "outputs"]], "day20.lib.classes.BroadcastModule": [[50, 4, 1, "", "add_to_graph"], [50, 4, 1, "", "handle_pulse"], [50, 4, 1, "", "is_initial_state"]], "day20.lib.classes.ConjunctionModule": [[50, 4, 1, "", "add_to_graph"], [50, 4, 1, "", "arrow_color"], [50, 4, 1, "", "current_count"], [50, 4, 1, "", "handle_pulse"], [50, 3, 1, "", "inputs"], [50, 4, 1, "", "is_initial_state"], [50, 4, 1, "", "set_inputs"]], "day20.lib.classes.FlipFlopModule": [[50, 4, 1, "", "add_to_graph"], [50, 4, 1, "", "handle_pulse"], [50, 4, 1, "", "is_initial_state"], [50, 3, 1, "", "state"]], "day20.lib.classes.LoopCounter": [[50, 4, 1, "", "add_result"], [50, 5, 1, "", "finished"], [50, 3, 1, "", "loop_lengths"], [50, 5, 1, "", "num_results"], [50, 3, 1, "", "target_loop_count"]], "day20.lib.classes.MappingModule": [[50, 3, 1, "", "name"], [50, 3, 1, "", "outputs"]], "day20.lib.classes.ModuleGroups": [[50, 3, 1, "", "all_nodes"], [50, 3, 1, "", "head"], [50, 3, 1, "", "loop_tails"], [50, 3, 1, "", "loops"], [50, 3, 1, "", "penultimate"], [50, 3, 1, "", "sink"]], "day20.lib.classes.Pulse": [[50, 3, 1, "", "HIGH"], [50, 3, 1, "", "LOW"]], "day20.lib.classes.PulseTarget": [[50, 3, 1, "", "pulse"], [50, 3, 1, "", "src"], [50, 3, 1, "", "target"]], "day20.lib.classes.SinkModule": [[50, 4, 1, "", "add_to_graph"], [50, 4, 1, "", "handle_pulse"], [50, 4, 1, "", "is_initial_state"]], "day20.lib.parsers": [[50, 1, 1, "", "finalize_modules"], [50, 1, 1, "", "get_modules"], [50, 1, 1, "", "parse_line"]], "day20.tests": [[51, 0, 0, "-", "test_classes"], [51, 0, 0, "-", "test_day20"], [51, 0, 0, "-", "test_parsers"]], "day20.tests.test_classes": [[51, 1, 1, "", "test_loop_counter"], [51, 1, 1, "", "test_modules"]], "day20.tests.test_day20": [[51, 1, 1, "", "test_day20"], [51, 1, 1, "", "test_part2"]], "day20.tests.test_parsers": [[51, 1, 1, "", "test_finalize_modules"], [51, 1, 1, "", "test_get_modules"], [51, 1, 1, "", "test_parse_line"]], "day21": [[52, 0, 0, "-", "day21"], [53, 0, 0, "-", "lib"]], "day21.day21": [[52, 2, 1, "", "SmartSteps"], [52, 1, 1, "", "calculate_smart_steps"], [52, 1, 1, "", "main"], [52, 1, 1, "", "mini_solve"], [52, 1, 1, "", "naive_solve"], [52, 1, 1, "", "solve"]], "day21.day21.SmartSteps": [[52, 3, 1, "", "boards_to_edge"], [52, 3, 1, "", "steps"]], "day21.lib": [[53, 0, 0, "-", "classes"], [53, 0, 0, "-", "parsers"]], "day21.lib.classes": [[53, 2, 1, "", "BaseDistanceMaze"], [53, 2, 1, "", "DistanceMaze"], [53, 2, 1, "", "DistanceMazes"], [53, 2, 1, "", "GiantNodeParser"], [53, 2, 1, "", "GiantNodeType"], [53, 2, 1, "", "Maze"], [53, 2, 1, "", "Position"], [53, 2, 1, "", "PositionDist"]], "day21.lib.classes.BaseDistanceMaze": [[53, 4, 1, "", "calc_steps"], [53, 4, 1, "", "overlay"]], "day21.lib.classes.DistanceMaze": [[53, 4, 1, "", "calc_steps"], [53, 4, 1, "", "centre_cell"], [53, 3, 1, "", "grid"], [53, 4, 1, "", "int_to_str"], [53, 4, 1, "", "is_complete"], [53, 4, 1, "", "is_oob"], [53, 3, 1, "", "num_cols"], [53, 3, 1, "", "num_rows"], [53, 4, 1, "", "overlay"]], "day21.lib.classes.DistanceMazes": [[53, 4, 1, "", "calc_steps"], [53, 3, 1, "", "cols_per_maze"], [53, 4, 1, "", "get_big_grid"], [53, 4, 1, "", "get_split_pos"], [53, 3, 1, "", "grid"], [53, 4, 1, "", "overlay"], [53, 3, 1, "", "rows_per_maze"]], "day21.lib.classes.GiantNodeParser": [[53, 3, 1, "", "distance_mazes"], [53, 3, 1, "", "edge_dist"], [53, 3, 1, "", "full_edge_dist"], [53, 4, 1, "", "get_node"], [53, 4, 1, "", "get_node_count"]], "day21.lib.classes.GiantNodeType": [[53, 3, 1, "", "EAST_TIP"], [53, 3, 1, "", "FULL_EVEN"], [53, 3, 1, "", "FULL_ODD"], [53, 3, 1, "", "NORTH_EAST_BIG"], [53, 3, 1, "", "NORTH_EAST_SMALL"], [53, 3, 1, "", "NORTH_TIP"], [53, 3, 1, "", "NORTH_WEST_BIG"], [53, 3, 1, "", "NORTH_WEST_SMALL"], [53, 3, 1, "", "SOUTH_EAST_BIG"], [53, 3, 1, "", "SOUTH_EAST_SMALL"], [53, 3, 1, "", "SOUTH_TIP"], [53, 3, 1, "", "SOUTH_WEST_BIG"], [53, 3, 1, "", "SOUTH_WEST_SMALL"], [53, 3, 1, "", "WEST_TIP"]], "day21.lib.classes.Maze": [[53, 3, 1, "", "grid"], [53, 3, 1, "", "num_cols"], [53, 3, 1, "", "num_rows"]], "day21.lib.classes.Position": [[53, 3, 1, "", "col"], [53, 3, 1, "", "row"]], "day21.lib.classes.PositionDist": [[53, 3, 1, "", "distance"], [53, 4, 1, "", "replace"]], "day21.lib.parsers": [[53, 1, 1, "", "parse_maze"]], "day22": [[54, 0, 0, "-", "day22"], [55, 0, 0, "-", "lib"], [56, 0, 0, "-", "tests"]], "day22.day22": [[54, 2, 1, "", "Visualization"], [54, 1, 1, "", "main"]], "day22.day22.Visualization": [[54, 4, 1, "", "animate_part1"], [54, 4, 1, "", "animate_part2"], [54, 3, 1, "", "boxes"], [54, 4, 1, "", "calculate_part1"], [54, 4, 1, "", "calculate_part2"], [54, 4, 1, "", "follow_block"], [54, 3, 1, "", "has_started"], [54, 3, 1, "", "matrix"], [54, 4, 1, "", "start"], [54, 4, 1, "", "vis_rate"]], "day22.lib": [[55, 0, 0, "-", "classes"], [55, 0, 0, "-", "parsers"], [55, 0, 0, "-", "vis"]], "day22.lib.classes": [[55, 2, 1, "", "BoxData"], [55, 2, 1, "", "Matrix"], [55, 2, 1, "", "Vector3"]], "day22.lib.classes.BoxData": [[55, 3, 1, "", "end_pos"], [55, 4, 1, "", "fall"], [55, 3, 1, "", "hats"], [55, 5, 1, "", "height"], [55, 5, 1, "", "length"], [55, 3, 1, "", "name"], [55, 4, 1, "", "recursive_fall"], [55, 4, 1, "", "select"], [55, 4, 1, "", "set_hats"], [55, 4, 1, "", "set_supports"], [55, 4, 1, "", "set_vbox"], [55, 3, 1, "", "start_pos"], [55, 3, 1, "", "supports"], [55, 3, 1, "", "total_hats"], [55, 4, 1, "", "unselect"], [55, 3, 1, "", "vbox"], [55, 5, 1, "", "vpos"], [55, 5, 1, "", "width"], [55, 5, 1, "", "z_val_bot"], [55, 5, 1, "", "z_val_top"]], "day22.lib.classes.Matrix": [[55, 4, 1, "", "can_fall_down"], [55, 4, 1, "", "can_fly_up"], [55, 4, 1, "", "get_hats"], [55, 4, 1, "", "get_supports"], [55, 3, 1, "", "layers"], [55, 4, 1, "", "register_box"]], "day22.lib.classes.Vector3": [[55, 3, 1, "", "x"], [55, 3, 1, "", "y"], [55, 3, 1, "", "z"]], "day22.lib.parsers": [[55, 1, 1, "", "get_boxes"], [55, 1, 1, "", "parse_vector"]], "day22.lib.vis": [[55, 1, 1, "", "animate_part1"], [55, 1, 1, "", "animate_part2"], [55, 1, 1, "", "bind_keys"], [55, 1, 1, "", "construct_box"], [55, 1, 1, "", "follow_block"], [55, 1, 1, "", "init_vis"], [55, 1, 1, "", "random_color"]], "day22.tests": [[56, 0, 0, "-", "test_classes"], [56, 0, 0, "-", "test_day22"], [56, 0, 0, "-", "test_parsers"]], "day22.tests.test_classes": [[56, 1, 1, "", "test_box_data"]], "day22.tests.test_day22": [[56, 1, 1, "", "test_visualization"]], "day22.tests.test_parsers": [[56, 1, 1, "", "test_parser"]], "day23": [[57, 0, 0, "-", "day23"], [58, 0, 0, "-", "lib"], [59, 0, 0, "-", "tests"]], "day23.day23": [[57, 1, 1, "", "main"], [57, 1, 1, "", "part1"], [57, 1, 1, "", "part2"]], "day23.lib": [[58, 0, 0, "-", "classes"], [58, 0, 0, "-", "classes2"], [58, 0, 0, "-", "parsers"]], "day23.lib.classes": [[58, 2, 1, "", "Maze"], [58, 2, 1, "", "Path"], [58, 2, 1, "", "Position"], [58, 2, 1, "", "Solver1"], [58, 1, 1, "", "generate_paths"]], "day23.lib.classes.Maze": [[58, 4, 1, "", "copy"], [58, 4, 1, "", "get_cell_branches"], [58, 3, 1, "", "grid"], [58, 4, 1, "", "is_oob"], [58, 3, 1, "", "num_cols"], [58, 3, 1, "", "num_rows"]], "day23.lib.classes.Path": [[58, 4, 1, "", "add"], [58, 4, 1, "", "can_add"], [58, 4, 1, "", "copy"], [58, 4, 1, "", "flip"], [58, 4, 1, "", "last"], [58, 3, 1, "", "nodes"], [58, 4, 1, "", "overlay"], [58, 3, 1, "", "route"]], "day23.lib.classes.Position": [[58, 3, 1, "", "col"], [58, 4, 1, "", "copy_modify"], [58, 4, 1, "", "expand"], [58, 3, 1, "", "row"]], "day23.lib.classes.Solver1": [[58, 4, 1, "", "expand_hill"], [58, 4, 1, "", "expand_path"], [58, 3, 1, "", "handle_hills"], [58, 3, 1, "", "maze"], [58, 4, 1, "", "solve"]], "day23.lib.classes2": [[58, 2, 1, "", "Edge"], [58, 2, 1, "", "Node"], [58, 2, 1, "", "Solver2"], [58, 1, 1, "", "solve2"], [58, 1, 1, "", "solve2_helper"]], "day23.lib.classes2.Edge": [[58, 4, 1, "", "flip"], [58, 3, 1, "", "length"], [58, 3, 1, "", "node1"], [58, 3, 1, "", "node2"], [58, 3, 1, "", "path"]], "day23.lib.classes2.Node": [[58, 3, 1, "", "edges"], [58, 3, 1, "", "name"], [58, 3, 1, "", "position"]], "day23.lib.classes2.Solver2": [[58, 4, 1, "", "build_nodes"], [58, 4, 1, "", "calculate_edges"], [58, 4, 1, "", "expand_path"], [58, 4, 1, "", "get_nodes"], [58, 3, 1, "", "input_maze"], [58, 4, 1, "", "solve"]], "day23.lib.parsers": [[58, 1, 1, "", "get_maze"]], "day23.tests": [[59, 0, 0, "-", "test_classes"], [59, 0, 0, "-", "test_classes2"], [59, 0, 0, "-", "test_day23"], [59, 0, 0, "-", "test_parsers"]], "day23.tests.test_classes": [[59, 1, 1, "", "test_generate_paths"], [59, 1, 1, "", "test_maze"], [59, 1, 1, "", "test_path"], [59, 1, 1, "", "test_position"], [59, 1, 1, "", "test_solver1"]], "day23.tests.test_classes2": [[59, 1, 1, "", "test_solver2"]], "day23.tests.test_day23": [[59, 1, 1, "", "test_part1"], [59, 1, 1, "", "test_part2"], [59, 1, 1, "", "test_solver"]], "day23.tests.test_parsers": [[59, 1, 1, "", "test_get_maze"]], "day24": [[60, 0, 0, "-", "day24"], [61, 0, 0, "-", "lib"], [62, 0, 0, "-", "tests"]], "day24.day24": [[60, 1, 1, "", "get_intersection_2d"], [60, 1, 1, "", "main"], [60, 1, 1, "", "part1"], [60, 1, 1, "", "part2"], [60, 1, 1, "", "within_2d"]], "day24.lib": [[61, 0, 0, "-", "classes"], [61, 0, 0, "-", "parsers"]], "day24.lib.classes": [[61, 2, 1, "", "Hailstone"], [61, 2, 1, "", "Vector2"], [61, 2, 1, "", "Vector3"]], "day24.lib.classes.Hailstone": [[61, 3, 1, "", "position"], [61, 3, 1, "", "velocity"]], "day24.lib.classes.Vector2": [[61, 3, 1, "", "x"], [61, 3, 1, "", "y"]], "day24.lib.classes.Vector3": [[61, 3, 1, "", "x"], [61, 5, 1, "", "xy"], [61, 3, 1, "", "y"], [61, 3, 1, "", "z"]], "day24.lib.parsers": [[61, 1, 1, "", "parse_input"], [61, 1, 1, "", "parse_vector3"]], "day24.tests": [[62, 0, 0, "-", "test_day24"], [62, 0, 0, "-", "test_parsers"]], "day24.tests.test_day24": [[62, 1, 1, "", "test_get_intersection_2d"], [62, 1, 1, "", "test_part1"], [62, 1, 1, "", "test_part2"], [62, 1, 1, "", "test_within_2d"]], "day24.tests.test_parsers": [[62, 1, 1, "", "test_parser"], [62, 1, 1, "", "test_vector3"]], "day25": [[63, 0, 0, "-", "day25"], [64, 0, 0, "-", "tests"]], "day25.day25": [[63, 2, 1, "", "Connection"], [63, 1, 1, "", "get_data"], [63, 1, 1, "", "main"], [63, 1, 1, "", "parse_connection"], [63, 1, 1, "", "show_graph"], [63, 1, 1, "", "solve_nodes"]], "day25.day25.Connection": [[63, 3, 1, "", "dests"], [63, 4, 1, "", "node_names"], [63, 3, 1, "", "src"]], "day25.tests": [[64, 0, 0, "-", "test_day25"]], "day25.tests.test_day25": [[64, 1, 1, "", "test_day25"], [64, 1, 1, "", "test_get_data"], [64, 1, 1, "", "test_parse_connection"]], "download_inputs": [[65, 1, 1, "", "download_file"], [65, 1, 1, "", "main"]], "maker": [[67, 1, 1, "", "touch_days"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:attribute", "4": "py:method", "5": "py:property"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "attribute", "Python attribute"], "4": ["py", "method", "Python method"], "5": ["py", "property", "Python property"]}, "titleterms": {"automat": 0, "document": [0, 66], "kei": 0, "command": 0, "continu": 1, "integr": 1, "tool": 1, "pre": 1, "commit": 1, "ruff": 1, "mypi": 1, "mix": 1, "crlf": 1, "pytest": 1, "coverag": 1, "sphinx": 1, "day01": [2, 3], "packag": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64], "subpackag": [2, 4, 6, 9, 11, 14, 16, 18, 20, 22, 25, 27, 29, 31, 34, 37, 40, 43, 46, 49, 52, 54, 57, 60, 63], "submodul": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64], "day1a": 2, "modul": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67], "day1b": 2, "content": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66], "test": [3, 5, 8, 10, 13, 15, 17, 19, 21, 24, 26, 28, 30, 33, 36, 39, 42, 45, 48, 51, 56, 59, 62, 64], "test_day1a": 3, "test_day1b": 3, "day02": [4, 5], "day2": 4, "test_day2": 5, "day03": [6, 7, 8], "day3": 6, "lib": [7, 12, 23, 32, 35, 38, 41, 44, 47, 50, 53, 55, 58, 61], "class": [7, 12, 35, 41, 47, 50, 53, 55, 58, 61], "parser": [7, 12, 38, 41, 47, 50, 53, 55, 58, 61], "test_class": [8, 36, 48, 51, 56, 59], "test_day3": 8, "day04": [9, 10], "day4": 9, "test_day4": 10, "day05": [11, 12, 13], "day5": 11, "test_day5": 13, "day06": [14, 15], "day6": 14, "test_day6": 15, "day07": [16, 17], "day7": 16, "test_day7": 17, "day08": [18, 19], "day8": 18, "test_day8": 19, "day09": [20, 21], "day9": 20, "test_day9": 21, "day10": [22, 23, 24], "direct": [23, 32, 38, 41], "pipebound": 23, "pipe": 23, "posit": 23, "test_day10": 24, "test_direct": [24, 33, 39, 42], "day11": [25, 26], "test_day11": 26, "day12": [27, 28], "test_day12": 28, "day13": [29, 30], "test_day13": 30, "day14": [31, 32, 33], "test_day14": 33, "day15": [34, 35, 36], "test_day15": 36, "day16": [37, 38, 39], "cell": 38, "laser": 38, "world": 38, "test_cel": 39, "test_day16": 39, "test_world": 39, "day17": [40, 41, 42], "test_day17": 42, "test_pars": [42, 48, 51, 56, 59, 62], "day18": [43, 44, 45], "day18a": 43, "day18b": 43, "tile": 44, "test_day18a": 45, "test_day18b": 45, "day19": [46, 47, 48], "test_day19": 48, "day20": [49, 50, 51], "test_day20": 51, "day21": [52, 53], "day22": [54, 55, 56], "vi": 55, "test_day22": 56, "day23": [57, 58, 59], "classes2": 58, "test_classes2": 59, "test_day23": 59, "day24": [60, 61, 62], "test_day24": 62, "day25": [63, 64], "test_day25": 64, "download_input": 65, "welcom": 66, "adventofcode2023": [66, 68], "": 66, "indic": 66, "tabl": 66, "maker": 67}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.viewcode": 1, "sphinx": 60}, "alltitles": {"Automatic documentation": [[0, "automatic-documentation"]], "Key commands": [[0, "key-commands"]], "Continuous Integration": [[1, "continuous-integration"]], "Tools": [[1, "tools"]], "Pre-commit": [[1, "pre-commit"]], "ruff": [[1, "ruff"]], "mypy": [[1, "mypy"]], "mixed-crlf": [[1, "mixed-crlf"]], "pytest": [[1, "pytest"]], "coverage": [[1, "coverage"]], "sphinx": [[1, "sphinx"]], "day01 package": [[2, "day01-package"]], "Subpackages": [[2, "subpackages"], [4, "subpackages"], [6, "subpackages"], [9, "subpackages"], [11, "subpackages"], [14, "subpackages"], [16, "subpackages"], [18, "subpackages"], [20, "subpackages"], [22, "subpackages"], [25, "subpackages"], [27, "subpackages"], [29, "subpackages"], [31, "subpackages"], [34, "subpackages"], [37, "subpackages"], [40, "subpackages"], [43, "subpackages"], [46, "subpackages"], [49, "subpackages"], [52, "subpackages"], [54, "subpackages"], [57, "subpackages"], [60, "subpackages"], [63, "subpackages"]], "Submodules": [[2, "submodules"], [3, "submodules"], [4, "submodules"], [5, "submodules"], [6, "submodules"], [7, "submodules"], [8, "submodules"], [9, "submodules"], [10, "submodules"], [11, "submodules"], [12, "submodules"], [13, "submodules"], [14, "submodules"], [15, "submodules"], [16, "submodules"], [17, "submodules"], [18, "submodules"], [19, "submodules"], [20, "submodules"], [21, "submodules"], [22, "submodules"], [23, "submodules"], [24, "submodules"], [25, "submodules"], [26, "submodules"], [27, "submodules"], [28, "submodules"], [29, "submodules"], [30, "submodules"], [31, "submodules"], [32, "submodules"], [33, "submodules"], [34, "submodules"], [35, "submodules"], [36, "submodules"], [37, "submodules"], [38, "submodules"], [39, "submodules"], [40, "submodules"], [41, "submodules"], [42, "submodules"], [43, "submodules"], [44, "submodules"], [45, "submodules"], [46, "submodules"], [47, "submodules"], [48, "submodules"], [49, "submodules"], [50, "submodules"], [51, "submodules"], [52, "submodules"], [53, "submodules"], [54, "submodules"], [55, "submodules"], [56, "submodules"], [57, "submodules"], [58, "submodules"], [59, "submodules"], [60, "submodules"], [61, "submodules"], [62, "submodules"], [63, "submodules"], [64, "submodules"]], "day01.day1a module": [[2, "module-day01.day1a"]], "day01.day1b module": [[2, "module-day01.day1b"]], "Module contents": [[2, "module-day01"], [3, "module-day01.tests"], [4, "module-day02"], [5, "module-day02.tests"], [6, "module-day03"], [7, "module-day03.lib"], [8, "module-day03.tests"], [9, "module-day04"], [10, "module-day04.tests"], [11, "module-day05"], [12, "module-day05.lib"], [13, "module-day05.tests"], [14, "module-day06"], [15, "module-day06.tests"], [16, "module-day07"], [17, "module-day07.tests"], [18, "module-day08"], [19, "module-day08.tests"], [20, "module-day09"], [21, "module-day09.tests"], [22, "module-day10"], [23, "module-day10.lib"], [24, "module-day10.tests"], [25, "module-day11"], [26, "module-day11.tests"], [27, "module-day12"], [28, "module-day12.tests"], [29, "module-day13"], [30, "module-day13.tests"], [31, "module-day14"], [32, "module-day14.lib"], [33, "module-day14.tests"], [34, "module-day15"], [35, "module-day15.lib"], [36, "module-day15.tests"], [37, "module-day16"], [38, "module-day16.lib"], [39, "module-day16.tests"], [40, "module-day17"], [41, "module-day17.lib"], [42, "module-day17.tests"], [43, "module-day18"], [44, "module-day18.lib"], [45, "module-day18.tests"], [46, "module-day19"], [47, "module-day19.lib"], [48, "module-day19.tests"], [49, "module-day20"], [50, "module-day20.lib"], [51, "module-day20.tests"], [52, "module-day21"], [53, "module-day21.lib"], [54, "module-day22"], [55, "module-day22.lib"], [56, "module-day22.tests"], [57, "module-day23"], [58, "module-day23.lib"], [59, "module-day23.tests"], [60, "module-day24"], [61, "module-day24.lib"], [62, "module-day24.tests"], [63, "module-day25"], [64, "module-day25.tests"]], "day01.tests package": [[3, "day01-tests-package"]], "day01.tests.test_day1a module": [[3, "module-day01.tests.test_day1a"]], "day01.tests.test_day1b module": [[3, "module-day01.tests.test_day1b"]], "day02 package": [[4, "day02-package"]], "day02.day2 module": [[4, "module-day02.day2"]], "day02.tests package": [[5, "day02-tests-package"]], "day02.tests.test_day2 module": [[5, "module-day02.tests.test_day2"]], "day03 package": [[6, "day03-package"]], "day03.day3 module": [[6, "module-day03.day3"]], "day03.lib package": [[7, "day03-lib-package"]], "day03.lib.classes module": [[7, "module-day03.lib.classes"]], "day03.lib.parsers module": [[7, "module-day03.lib.parsers"]], "day03.tests package": [[8, "day03-tests-package"]], "day03.tests.test_classes module": [[8, "module-day03.tests.test_classes"]], "day03.tests.test_day3 module": [[8, "module-day03.tests.test_day3"]], "day04 package": [[9, "day04-package"]], "day04.day4 module": [[9, "module-day04.day4"]], "day04.tests package": [[10, "day04-tests-package"]], "day04.tests.test_day4 module": [[10, "module-day04.tests.test_day4"]], "day05 package": [[11, "day05-package"]], "day05.day5 module": [[11, "module-day05.day5"]], "day05.lib package": [[12, "day05-lib-package"]], "day05.lib.classes module": [[12, "module-day05.lib.classes"]], "day05.lib.parsers module": [[12, "module-day05.lib.parsers"]], "day05.tests package": [[13, "day05-tests-package"]], "day05.tests.test_day5 module": [[13, "module-day05.tests.test_day5"]], "day06 package": [[14, "day06-package"]], "day06.day6 module": [[14, "module-day06.day6"]], "day06.tests package": [[15, "day06-tests-package"]], "day06.tests.test_day6 module": [[15, "module-day06.tests.test_day6"]], "day07 package": [[16, "day07-package"]], "day07.day7 module": [[16, "module-day07.day7"]], "day07.tests package": [[17, "day07-tests-package"]], "day07.tests.test_day7 module": [[17, "module-day07.tests.test_day7"]], "day08 package": [[18, "day08-package"]], "day08.day8 module": [[18, "module-day08.day8"]], "day08.tests package": [[19, "day08-tests-package"]], "day08.tests.test_day8 module": [[19, "module-day08.tests.test_day8"]], "day09 package": [[20, "day09-package"]], "day09.day9 module": [[20, "module-day09.day9"]], "day09.tests package": [[21, "day09-tests-package"]], "day09.tests.test_day9 module": [[21, "module-day09.tests.test_day9"]], "day10 package": [[22, "day10-package"]], "day10.day10 module": [[22, "module-day10.day10"]], "day10.lib package": [[23, "day10-lib-package"]], "day10.lib.direction module": [[23, "module-day10.lib.direction"]], "day10.lib.pipebounds module": [[23, "module-day10.lib.pipebounds"]], "day10.lib.pipes module": [[23, "module-day10.lib.pipes"]], "day10.lib.position module": [[23, "module-day10.lib.position"]], "day10.tests package": [[24, "day10-tests-package"]], "day10.tests.test_day10 module": [[24, "module-day10.tests.test_day10"]], "day10.tests.test_direction module": [[24, "module-day10.tests.test_direction"]], "day11 package": [[25, "day11-package"]], "day11.day11 module": [[25, "module-day11.day11"]], "day11.tests package": [[26, "day11-tests-package"]], "day11.tests.test_day11 module": [[26, "module-day11.tests.test_day11"]], "day12 package": [[27, "day12-package"]], "day12.day12 module": [[27, "module-day12.day12"]], "day12.tests package": [[28, "day12-tests-package"]], "day12.tests.test_day12 module": [[28, "module-day12.tests.test_day12"]], "day13 package": [[29, "day13-package"]], "day13.day13 module": [[29, "module-day13.day13"]], "day13.tests package": [[30, "day13-tests-package"]], "day13.tests.test_day13 module": [[30, "module-day13.tests.test_day13"]], "day14 package": [[31, "day14-package"]], "day14.day14 module": [[31, "module-day14.day14"]], "day14.lib package": [[32, "day14-lib-package"]], "day14.lib.direction module": [[32, "module-day14.lib.direction"]], "day14.tests package": [[33, "day14-tests-package"]], "day14.tests.test_day14 module": [[33, "module-day14.tests.test_day14"]], "day14.tests.test_direction module": [[33, "module-day14.tests.test_direction"]], "day15 package": [[34, "day15-package"]], "day15.day15 module": [[34, "module-day15.day15"]], "day15.lib package": [[35, "day15-lib-package"]], "day15.lib.classes module": [[35, "module-day15.lib.classes"]], "day15.tests package": [[36, "day15-tests-package"]], "day15.tests.test_classes module": [[36, "module-day15.tests.test_classes"]], "day15.tests.test_day15 module": [[36, "module-day15.tests.test_day15"]], "day16 package": [[37, "day16-package"]], "day16.day16 module": [[37, "module-day16.day16"]], "day16.lib package": [[38, "day16-lib-package"]], "day16.lib.cells module": [[38, "module-day16.lib.cells"]], "day16.lib.direction module": [[38, "module-day16.lib.direction"]], "day16.lib.laser module": [[38, "module-day16.lib.laser"]], "day16.lib.parsers module": [[38, "module-day16.lib.parsers"]], "day16.lib.world module": [[38, "module-day16.lib.world"]], "day16.tests package": [[39, "day16-tests-package"]], "day16.tests.test_cells module": [[39, "module-day16.tests.test_cells"]], "day16.tests.test_day16 module": [[39, "module-day16.tests.test_day16"]], "day16.tests.test_direction module": [[39, "module-day16.tests.test_direction"]], "day16.tests.test_world module": [[39, "module-day16.tests.test_world"]], "day17 package": [[40, "day17-package"]], "day17.day17 module": [[40, "module-day17.day17"]], "day17.lib package": [[41, "day17-lib-package"]], "day17.lib.classes module": [[41, "module-day17.lib.classes"]], "day17.lib.direction module": [[41, "module-day17.lib.direction"]], "day17.lib.parsers module": [[41, "module-day17.lib.parsers"]], "day17.tests package": [[42, "day17-tests-package"]], "day17.tests.test_day17 module": [[42, "module-day17.tests.test_day17"]], "day17.tests.test_direction module": [[42, "module-day17.tests.test_direction"]], "day17.tests.test_parsers module": [[42, "module-day17.tests.test_parsers"]], "day18 package": [[43, "day18-package"]], "day18.day18a module": [[43, "module-day18.day18a"]], "day18.day18b module": [[43, "module-day18.day18b"]], "day18.lib package": [[44, "day18-lib-package"]], "day18.lib.tile module": [[44, "module-day18.lib.tile"]], "day18.tests package": [[45, "day18-tests-package"]], "day18.tests.test_day18a module": [[45, "module-day18.tests.test_day18a"]], "day18.tests.test_day18b module": [[45, "module-day18.tests.test_day18b"]], "day19 package": [[46, "day19-package"]], "day19.day19 module": [[46, "module-day19.day19"]], "day19.lib package": [[47, "day19-lib-package"]], "day19.lib.classes module": [[47, "module-day19.lib.classes"]], "day19.lib.parsers module": [[47, "module-day19.lib.parsers"]], "day19.tests package": [[48, "day19-tests-package"]], "day19.tests.test_classes module": [[48, "module-day19.tests.test_classes"]], "day19.tests.test_day19 module": [[48, "module-day19.tests.test_day19"]], "day19.tests.test_parsers module": [[48, "module-day19.tests.test_parsers"]], "day20 package": [[49, "day20-package"]], "day20.day20 module": [[49, "module-day20.day20"]], "day20.lib package": [[50, "day20-lib-package"]], "day20.lib.classes module": [[50, "module-day20.lib.classes"]], "day20.lib.parsers module": [[50, "module-day20.lib.parsers"]], "day20.tests package": [[51, "day20-tests-package"]], "day20.tests.test_classes module": [[51, "module-day20.tests.test_classes"]], "day20.tests.test_day20 module": [[51, "module-day20.tests.test_day20"]], "day20.tests.test_parsers module": [[51, "module-day20.tests.test_parsers"]], "day21 package": [[52, "day21-package"]], "day21.day21 module": [[52, "module-day21.day21"]], "day21.lib package": [[53, "day21-lib-package"]], "day21.lib.classes module": [[53, "module-day21.lib.classes"]], "day21.lib.parsers module": [[53, "module-day21.lib.parsers"]], "day22 package": [[54, "day22-package"]], "day22.day22 module": [[54, "module-day22.day22"]], "day22.lib package": [[55, "day22-lib-package"]], "day22.lib.classes module": [[55, "module-day22.lib.classes"]], "day22.lib.parsers module": [[55, "module-day22.lib.parsers"]], "day22.lib.vis module": [[55, "module-day22.lib.vis"]], "day22.tests package": [[56, "day22-tests-package"]], "day22.tests.test_classes module": [[56, "module-day22.tests.test_classes"]], "day22.tests.test_day22 module": [[56, "module-day22.tests.test_day22"]], "day22.tests.test_parsers module": [[56, "module-day22.tests.test_parsers"]], "day23 package": [[57, "day23-package"]], "day23.day23 module": [[57, "module-day23.day23"]], "day23.lib package": [[58, "day23-lib-package"]], "day23.lib.classes module": [[58, "module-day23.lib.classes"]], "day23.lib.classes2 module": [[58, "module-day23.lib.classes2"]], "day23.lib.parsers module": [[58, "module-day23.lib.parsers"]], "day23.tests package": [[59, "day23-tests-package"]], "day23.tests.test_classes module": [[59, "module-day23.tests.test_classes"]], "day23.tests.test_classes2 module": [[59, "module-day23.tests.test_classes2"]], "day23.tests.test_day23 module": [[59, "module-day23.tests.test_day23"]], "day23.tests.test_parsers module": [[59, "module-day23.tests.test_parsers"]], "day24 package": [[60, "day24-package"]], "day24.day24 module": [[60, "module-day24.day24"]], "day24.lib package": [[61, "day24-lib-package"]], "day24.lib.classes module": [[61, "module-day24.lib.classes"]], "day24.lib.parsers module": [[61, "module-day24.lib.parsers"]], "day24.tests package": [[62, "day24-tests-package"]], "day24.tests.test_day24 module": [[62, "module-day24.tests.test_day24"]], "day24.tests.test_parsers module": [[62, "module-day24.tests.test_parsers"]], "day25 package": [[63, "day25-package"]], "day25.day25 module": [[63, "module-day25.day25"]], "day25.tests package": [[64, "day25-tests-package"]], "day25.tests.test_day25 module": [[64, "module-day25.tests.test_day25"]], "download_inputs module": [[65, "module-download_inputs"]], "Welcome to adventofcode2023\u2019s documentation!": [[66, "welcome-to-adventofcode2023-s-documentation"]], "Contents:": [[66, null]], "Indices and tables": [[66, "indices-and-tables"]], "maker module": [[67, "module-maker"]], "adventofcode2023": [[68, "adventofcode2023"]]}, "indexentries": {"indexvalue (class in day01.day1b)": [[2, "day01.day1b.IndexValue"]], "wordnumber (class in day01.day1b)": [[2, "day01.day1b.WordNumber"]], "day01": [[2, "module-day01"]], "day01.day1a": [[2, "module-day01.day1a"]], "day01.day1b": [[2, "module-day01.day1b"]], "get_first_last() (in module day01.day1a)": [[2, "day01.day1a.get_first_last"]], "get_input() (in module day01.day1a)": [[2, "day01.day1a.get_input"]], "get_input() (in module day01.day1b)": [[2, "day01.day1b.get_input"]], "index (day01.day1b.indexvalue attribute)": [[2, "day01.day1b.IndexValue.index"]], "main() (in module day01.day1a)": [[2, "day01.day1a.main"]], "main() (in module day01.day1b)": [[2, "day01.day1b.main"]], "module": [[2, "module-day01"], [2, "module-day01.day1a"], [2, "module-day01.day1b"], [3, "module-day01.tests"], [3, "module-day01.tests.test_day1a"], [3, "module-day01.tests.test_day1b"], [4, "module-day02"], [4, "module-day02.day2"], [5, "module-day02.tests"], [5, "module-day02.tests.test_day2"], [6, "module-day03"], [6, "module-day03.day3"], [7, "module-day03.lib"], [7, "module-day03.lib.classes"], [7, "module-day03.lib.parsers"], [8, "module-day03.tests"], [8, "module-day03.tests.test_classes"], [8, "module-day03.tests.test_day3"], [9, "module-day04"], [9, "module-day04.day4"], [10, "module-day04.tests"], [10, "module-day04.tests.test_day4"], [11, "module-day05"], [11, "module-day05.day5"], [12, "module-day05.lib"], [12, "module-day05.lib.classes"], [12, "module-day05.lib.parsers"], [13, "module-day05.tests"], [13, "module-day05.tests.test_day5"], [14, "module-day06"], [14, "module-day06.day6"], [15, "module-day06.tests"], [15, "module-day06.tests.test_day6"], [16, "module-day07"], [16, "module-day07.day7"], [17, "module-day07.tests"], [17, "module-day07.tests.test_day7"], [18, "module-day08"], [18, "module-day08.day8"], [19, "module-day08.tests"], [19, "module-day08.tests.test_day8"], [20, "module-day09"], [20, "module-day09.day9"], [21, "module-day09.tests"], [21, "module-day09.tests.test_day9"], [22, "module-day10"], [22, "module-day10.day10"], [23, "module-day10.lib"], [23, "module-day10.lib.direction"], [23, "module-day10.lib.pipebounds"], [23, "module-day10.lib.pipes"], [23, "module-day10.lib.position"], [24, "module-day10.tests"], [24, "module-day10.tests.test_day10"], [24, "module-day10.tests.test_direction"], [25, "module-day11"], [25, "module-day11.day11"], [26, "module-day11.tests"], [26, "module-day11.tests.test_day11"], [27, "module-day12"], [27, "module-day12.day12"], [28, "module-day12.tests"], [28, "module-day12.tests.test_day12"], [29, "module-day13"], [29, "module-day13.day13"], [30, "module-day13.tests"], [30, "module-day13.tests.test_day13"], [31, "module-day14"], [31, "module-day14.day14"], [32, "module-day14.lib"], [32, "module-day14.lib.direction"], [33, "module-day14.tests"], [33, "module-day14.tests.test_day14"], [33, "module-day14.tests.test_direction"], [34, "module-day15"], [34, "module-day15.day15"], [35, "module-day15.lib"], [35, "module-day15.lib.classes"], [36, "module-day15.tests"], [36, "module-day15.tests.test_classes"], [36, "module-day15.tests.test_day15"], [37, "module-day16"], [37, "module-day16.day16"], [38, "module-day16.lib"], [38, "module-day16.lib.cells"], [38, "module-day16.lib.direction"], [38, "module-day16.lib.laser"], [38, "module-day16.lib.parsers"], [38, "module-day16.lib.world"], [39, "module-day16.tests"], [39, "module-day16.tests.test_cells"], [39, "module-day16.tests.test_day16"], [39, "module-day16.tests.test_direction"], [39, "module-day16.tests.test_world"], [40, "module-day17"], [40, "module-day17.day17"], [41, "module-day17.lib"], [41, "module-day17.lib.classes"], [41, "module-day17.lib.direction"], [41, "module-day17.lib.parsers"], [42, "module-day17.tests"], [42, "module-day17.tests.test_day17"], [42, "module-day17.tests.test_direction"], [42, "module-day17.tests.test_parsers"], [43, "module-day18"], [43, "module-day18.day18a"], [43, "module-day18.day18b"], [44, "module-day18.lib"], [44, "module-day18.lib.tile"], [45, "module-day18.tests"], [45, "module-day18.tests.test_day18a"], [45, "module-day18.tests.test_day18b"], [46, "module-day19"], [46, "module-day19.day19"], [47, "module-day19.lib"], [47, "module-day19.lib.classes"], [47, "module-day19.lib.parsers"], [48, "module-day19.tests"], [48, "module-day19.tests.test_classes"], [48, "module-day19.tests.test_day19"], [48, "module-day19.tests.test_parsers"], [49, "module-day20"], [49, "module-day20.day20"], [50, "module-day20.lib"], [50, "module-day20.lib.classes"], [50, "module-day20.lib.parsers"], [51, "module-day20.tests"], [51, "module-day20.tests.test_classes"], [51, "module-day20.tests.test_day20"], [51, "module-day20.tests.test_parsers"], [52, "module-day21"], [52, "module-day21.day21"], [53, "module-day21.lib"], [53, "module-day21.lib.classes"], [53, "module-day21.lib.parsers"], [54, "module-day22"], [54, "module-day22.day22"], [55, "module-day22.lib"], [55, "module-day22.lib.classes"], [55, "module-day22.lib.parsers"], [55, "module-day22.lib.vis"], [56, "module-day22.tests"], [56, "module-day22.tests.test_classes"], [56, "module-day22.tests.test_day22"], [56, "module-day22.tests.test_parsers"], [57, "module-day23"], [57, "module-day23.day23"], [58, "module-day23.lib"], [58, "module-day23.lib.classes"], [58, "module-day23.lib.classes2"], [58, "module-day23.lib.parsers"], [59, "module-day23.tests"], [59, "module-day23.tests.test_classes"], [59, "module-day23.tests.test_classes2"], [59, "module-day23.tests.test_day23"], [59, "module-day23.tests.test_parsers"], [60, "module-day24"], [60, "module-day24.day24"], [61, "module-day24.lib"], [61, "module-day24.lib.classes"], [61, "module-day24.lib.parsers"], [62, "module-day24.tests"], [62, "module-day24.tests.test_day24"], [62, "module-day24.tests.test_parsers"], [63, "module-day25"], [63, "module-day25.day25"], [64, "module-day25.tests"], [64, "module-day25.tests.test_day25"], [65, "module-download_inputs"], [67, "module-maker"]], "number (day01.day1b.wordnumber attribute)": [[2, "day01.day1b.WordNumber.number"]], "part1() (in module day01.day1a)": [[2, "day01.day1a.part1"]], "part2() (in module day01.day1b)": [[2, "day01.day1b.part2"]], "process_line() (in module day01.day1b)": [[2, "day01.day1b.process_line"]], "value (day01.day1b.indexvalue attribute)": [[2, "day01.day1b.IndexValue.value"]], "word (day01.day1b.wordnumber attribute)": [[2, "day01.day1b.WordNumber.word"]], "day01.tests": [[3, "module-day01.tests"]], "day01.tests.test_day1a": [[3, "module-day01.tests.test_day1a"]], "day01.tests.test_day1b": [[3, "module-day01.tests.test_day1b"]], "test_get_first_last() (in module day01.tests.test_day1a)": [[3, "day01.tests.test_day1a.test_get_first_last"]], "test_get_input() (in module day01.tests.test_day1a)": [[3, "day01.tests.test_day1a.test_get_input"]], "test_get_input() (in module day01.tests.test_day1b)": [[3, "day01.tests.test_day1b.test_get_input"]], "test_index_value() (in module day01.tests.test_day1b)": [[3, "day01.tests.test_day1b.test_index_value"]], "test_part1() (in module day01.tests.test_day1a)": [[3, "day01.tests.test_day1a.test_part1"]], "test_part2() (in module day01.tests.test_day1b)": [[3, "day01.tests.test_day1b.test_part2"]], "test_process_line() (in module day01.tests.test_day1b)": [[3, "day01.tests.test_day1b.test_process_line"]], "blue (day02.day2.color attribute)": [[4, "day02.day2.Color.BLUE"]], "color (class in day02.day2)": [[4, "day02.day2.Color"]], "draw (class in day02.day2)": [[4, "day02.day2.Draw"]], "green (day02.day2.color attribute)": [[4, "day02.day2.Color.GREEN"]], "game (class in day02.day2)": [[4, "day02.day2.Game"]], "red (day02.day2.color attribute)": [[4, "day02.day2.Color.RED"]], "blue (day02.day2.draw attribute)": [[4, "day02.day2.Draw.blue"]], "blue (day02.day2.game attribute)": [[4, "day02.day2.Game.blue"]], "day02": [[4, "module-day02"]], "day02.day2": [[4, "module-day02.day2"]], "game_filter() (in module day02.day2)": [[4, "day02.day2.game_filter"]], "get_games() (in module day02.day2)": [[4, "day02.day2.get_games"]], "green (day02.day2.draw attribute)": [[4, "day02.day2.Draw.green"]], "green (day02.day2.game attribute)": [[4, "day02.day2.Game.green"]], "id (day02.day2.game attribute)": [[4, "day02.day2.Game.id"]], "main() (in module day02.day2)": [[4, "day02.day2.main"]], "parse_color_count() (day02.day2.draw method)": [[4, "day02.day2.Draw.parse_color_count"]], "parse_colors_count() (day02.day2.draw method)": [[4, "day02.day2.Draw.parse_colors_count"]], "parse_draws() (day02.day2.game method)": [[4, "day02.day2.Game.parse_draws"]], "part1() (in module day02.day2)": [[4, "day02.day2.part1"]], "part2() (in module day02.day2)": [[4, "day02.day2.part2"]], "power_level() (day02.day2.game method)": [[4, "day02.day2.Game.power_level"]], "red (day02.day2.draw attribute)": [[4, "day02.day2.Draw.red"]], "red (day02.day2.game attribute)": [[4, "day02.day2.Game.red"]], "day02.tests": [[5, "module-day02.tests"]], "day02.tests.test_day2": [[5, "module-day02.tests.test_day2"]], "get_game1() (in module day02.tests.test_day2)": [[5, "day02.tests.test_day2.get_game1"]], "get_game2() (in module day02.tests.test_day2)": [[5, "day02.tests.test_day2.get_game2"]], "test_draw() (in module day02.tests.test_day2)": [[5, "day02.tests.test_day2.test_draw"]], "test_game() (in module day02.tests.test_day2)": [[5, "day02.tests.test_day2.test_game"]], "test_part1() (in module day02.tests.test_day2)": [[5, "day02.tests.test_day2.test_part1"]], "test_part2() (in module day02.tests.test_day2)": [[5, "day02.tests.test_day2.test_part2"]], "day03": [[6, "module-day03"]], "day03.day3": [[6, "module-day03.day3"]], "main() (in module day03.day3)": [[6, "day03.day3.main"]], "part1() (in module day03.day3)": [[6, "day03.day3.part1"]], "part2() (in module day03.day3)": [[6, "day03.day3.part2"]], "gear (class in day03.lib.classes)": [[7, "day03.lib.classes.Gear"]], "matrix (class in day03.lib.classes)": [[7, "day03.lib.classes.Matrix"]], "partnumber (class in day03.lib.classes)": [[7, "day03.lib.classes.PartNumber"]], "col (day03.lib.classes.gear attribute)": [[7, "day03.lib.classes.Gear.col"]], "col (day03.lib.classes.partnumber attribute)": [[7, "day03.lib.classes.PartNumber.col"]], "data (day03.lib.classes.matrix attribute)": [[7, "day03.lib.classes.Matrix.data"]], "day03.lib": [[7, "module-day03.lib"]], "day03.lib.classes": [[7, "module-day03.lib.classes"]], "day03.lib.parsers": [[7, "module-day03.lib.parsers"]], "end_index (day03.lib.classes.partnumber property)": [[7, "day03.lib.classes.PartNumber.end_index"]], "filter_engine_parts() (day03.lib.classes.matrix method)": [[7, "day03.lib.classes.Matrix.filter_engine_parts"]], "find_gear_parts() (day03.lib.classes.matrix method)": [[7, "day03.lib.classes.Matrix.find_gear_parts"]], "gear_ratio (day03.lib.classes.gear property)": [[7, "day03.lib.classes.Gear.gear_ratio"]], "get_gears() (day03.lib.classes.matrix method)": [[7, "day03.lib.classes.Matrix.get_gears"]], "get_matrix() (in module day03.lib.parsers)": [[7, "day03.lib.parsers.get_matrix"]], "get_part_numbers() (day03.lib.classes.matrix method)": [[7, "day03.lib.classes.Matrix.get_part_numbers"]], "is_engine_part() (day03.lib.classes.matrix method)": [[7, "day03.lib.classes.Matrix.is_engine_part"]], "is_engine_part_row() (day03.lib.classes.matrix static method)": [[7, "day03.lib.classes.Matrix.is_engine_part_row"]], "length (day03.lib.classes.partnumber attribute)": [[7, "day03.lib.classes.PartNumber.length"]], "part_numbers (day03.lib.classes.gear attribute)": [[7, "day03.lib.classes.Gear.part_numbers"]], "row (day03.lib.classes.gear attribute)": [[7, "day03.lib.classes.Gear.row"]], "row (day03.lib.classes.partnumber attribute)": [[7, "day03.lib.classes.PartNumber.row"]], "row_count (day03.lib.classes.matrix property)": [[7, "day03.lib.classes.Matrix.row_count"]], "row_size (day03.lib.classes.matrix property)": [[7, "day03.lib.classes.Matrix.row_size"]], "touching() (day03.lib.classes.partnumber method)": [[7, "day03.lib.classes.PartNumber.touching"]], "value (day03.lib.classes.partnumber attribute)": [[7, "day03.lib.classes.PartNumber.value"]], "partnumbertouchtest (class in day03.tests.test_classes)": [[8, "day03.tests.test_classes.PartNumberTouchTest"]], "col (day03.tests.test_classes.partnumbertouchtest attribute)": [[8, "day03.tests.test_classes.PartNumberTouchTest.col"]], "day03.tests": [[8, "module-day03.tests"]], "day03.tests.test_classes": [[8, "module-day03.tests.test_classes"]], "day03.tests.test_day3": [[8, "module-day03.tests.test_day3"]], "result (day03.tests.test_classes.partnumbertouchtest attribute)": [[8, "day03.tests.test_classes.PartNumberTouchTest.result"]], "row (day03.tests.test_classes.partnumbertouchtest attribute)": [[8, "day03.tests.test_classes.PartNumberTouchTest.row"]], "row_size (day03.tests.test_classes.partnumbertouchtest attribute)": [[8, "day03.tests.test_classes.PartNumberTouchTest.row_size"]], "test_gear() (in module day03.tests.test_classes)": [[8, "day03.tests.test_classes.test_gear"]], "test_matrix() (in module day03.tests.test_classes)": [[8, "day03.tests.test_classes.test_matrix"]], "test_part1() (in module day03.tests.test_day3)": [[8, "day03.tests.test_day3.test_part1"]], "test_part2() (in module day03.tests.test_day3)": [[8, "day03.tests.test_day3.test_part2"]], "test_part_number() (in module day03.tests.test_classes)": [[8, "day03.tests.test_classes.test_part_number"]], "card (class in day04.day4)": [[9, "day04.day4.Card"]], "inventory (class in day04.day4)": [[9, "day04.day4.Inventory"]], "all_cards (day04.day4.inventory attribute)": [[9, "day04.day4.Inventory.all_cards"]], "calculate_mappings() (day04.day4.inventory method)": [[9, "day04.day4.Inventory.calculate_mappings"]], "day04": [[9, "module-day04"]], "day04.day4": [[9, "module-day04.day4"]], "get_matches() (day04.day4.card method)": [[9, "day04.day4.Card.get_matches"]], "get_points() (day04.day4.card method)": [[9, "day04.day4.Card.get_points"]], "grab_data() (in module day04.day4)": [[9, "day04.day4.grab_data"]], "have (day04.day4.card attribute)": [[9, "day04.day4.Card.have"]], "id (day04.day4.card attribute)": [[9, "day04.day4.Card.id"]], "main() (in module day04.day4)": [[9, "day04.day4.main"]], "memoized (day04.day4.inventory attribute)": [[9, "day04.day4.Inventory.memoized"]], "part1() (in module day04.day4)": [[9, "day04.day4.part1"]], "part2() (in module day04.day4)": [[9, "day04.day4.part2"]], "split_numbers() (in module day04.day4)": [[9, "day04.day4.split_numbers"]], "total_cards() (day04.day4.inventory method)": [[9, "day04.day4.Inventory.total_cards"]], "winners (day04.day4.card attribute)": [[9, "day04.day4.Card.winners"]], "day04.tests": [[10, "module-day04.tests"]], "day04.tests.test_day4": [[10, "module-day04.tests.test_day4"]], "test_card() (in module day04.tests.test_day4)": [[10, "day04.tests.test_day4.test_card"]], "test_grab_data() (in module day04.tests.test_day4)": [[10, "day04.tests.test_day4.test_grab_data"]], "test_inventory() (in module day04.tests.test_day4)": [[10, "day04.tests.test_day4.test_inventory"]], "test_part1() (in module day04.tests.test_day4)": [[10, "day04.tests.test_day4.test_part1"]], "test_part2() (in module day04.tests.test_day4)": [[10, "day04.tests.test_day4.test_part2"]], "test_split_numbers() (in module day04.tests.test_day4)": [[10, "day04.tests.test_day4.test_split_numbers"]], "day05": [[11, "module-day05"]], "day05.day5": [[11, "module-day05.day5"]], "get_location() (in module day05.day5)": [[11, "day05.day5.get_location"]], "get_location_ranges() (in module day05.day5)": [[11, "day05.day5.get_location_ranges"]], "main() (in module day05.day5)": [[11, "day05.day5.main"]], "part1() (in module day05.day5)": [[11, "day05.day5.part1"]], "part2() (in module day05.day5)": [[11, "day05.day5.part2"]], "seed_to_mapping_ranges() (in module day05.day5)": [[11, "day05.day5.seed_to_mapping_ranges"]], "mapping (class in day05.lib.classes)": [[12, "day05.lib.classes.Mapping"]], "mappingrange (class in day05.lib.classes)": [[12, "day05.lib.classes.MappingRange"]], "namedmap (class in day05.lib.classes)": [[12, "day05.lib.classes.NamedMap"]], "add_mapping() (day05.lib.classes.namedmap method)": [[12, "day05.lib.classes.NamedMap.add_mapping"]], "day05.lib": [[12, "module-day05.lib"]], "day05.lib.classes": [[12, "module-day05.lib.classes"]], "day05.lib.parsers": [[12, "module-day05.lib.parsers"]], "dest_end (day05.lib.classes.mapping attribute)": [[12, "day05.lib.classes.Mapping.dest_end"]], "dest_start (day05.lib.classes.mapping attribute)": [[12, "day05.lib.classes.Mapping.dest_start"]], "end (day05.lib.classes.mappingrange attribute)": [[12, "day05.lib.classes.MappingRange.end"]], "extend_mapping_range() (day05.lib.classes.namedmap method)": [[12, "day05.lib.classes.NamedMap.extend_mapping_range"]], "finalize_mappings() (day05.lib.classes.namedmap method)": [[12, "day05.lib.classes.NamedMap.finalize_mappings"]], "get_mapping() (day05.lib.classes.mapping method)": [[12, "day05.lib.classes.Mapping.get_mapping"]], "get_mapping() (day05.lib.classes.namedmap method)": [[12, "day05.lib.classes.NamedMap.get_mapping"]], "get_mapping_range() (day05.lib.classes.namedmap method)": [[12, "day05.lib.classes.NamedMap.get_mapping_range"]], "get_mapping_ranges() (day05.lib.classes.namedmap method)": [[12, "day05.lib.classes.NamedMap.get_mapping_ranges"]], "get_mappings() (day05.lib.classes.mapping method)": [[12, "day05.lib.classes.Mapping.get_mappings"]], "grab_inputs() (in module day05.lib.parsers)": [[12, "day05.lib.parsers.grab_inputs"]], "injected (day05.lib.classes.mapping attribute)": [[12, "day05.lib.classes.Mapping.injected"]], "mappings (day05.lib.classes.namedmap attribute)": [[12, "day05.lib.classes.NamedMap.mappings"]], "name (day05.lib.classes.namedmap attribute)": [[12, "day05.lib.classes.NamedMap.name"]], "size (day05.lib.classes.mapping attribute)": [[12, "day05.lib.classes.Mapping.size"]], "src_end (day05.lib.classes.mapping attribute)": [[12, "day05.lib.classes.Mapping.src_end"]], "src_start (day05.lib.classes.mapping attribute)": [[12, "day05.lib.classes.Mapping.src_start"]], "start (day05.lib.classes.mappingrange attribute)": [[12, "day05.lib.classes.MappingRange.start"]], "day05.tests": [[13, "module-day05.tests"]], "day05.tests.test_day5": [[13, "module-day05.tests.test_day5"]], "test_mapping() (in module day05.tests.test_day5)": [[13, "day05.tests.test_day5.test_mapping"]], "test_part1() (in module day05.tests.test_day5)": [[13, "day05.tests.test_day5.test_part1"]], "test_part2() (in module day05.tests.test_day5)": [[13, "day05.tests.test_day5.test_part2"]], "test_seed_to_mapping_ranges() (in module day05.tests.test_day5)": [[13, "day05.tests.test_day5.test_seed_to_mapping_ranges"]], "race (class in day06.day6)": [[14, "day06.day6.Race"]], "racestrat (class in day06.day6)": [[14, "day06.day6.RaceStrat"]], "calculate_constant_time() (in module day06.day6)": [[14, "day06.day6.calculate_constant_time"]], "calculate_race() (in module day06.day6)": [[14, "day06.day6.calculate_race"]], "charge_time (day06.day6.racestrat attribute)": [[14, "day06.day6.RaceStrat.charge_time"]], "day06": [[14, "module-day06"]], "day06.day6": [[14, "module-day06.day6"]], "distance (day06.day6.racestrat property)": [[14, "day06.day6.RaceStrat.distance"]], "get_giga_race() (in module day06.day6)": [[14, "day06.day6.get_giga_race"]], "main() (in module day06.day6)": [[14, "day06.day6.main"]], "part1() (in module day06.day6)": [[14, "day06.day6.part1"]], "part2() (in module day06.day6)": [[14, "day06.day6.part2"]], "read_inputs() (in module day06.day6)": [[14, "day06.day6.read_inputs"]], "record_distance (day06.day6.race attribute)": [[14, "day06.day6.Race.record_distance"]], "run_time (day06.day6.racestrat attribute)": [[14, "day06.day6.RaceStrat.run_time"]], "speed (day06.day6.racestrat property)": [[14, "day06.day6.RaceStrat.speed"]], "time (day06.day6.race attribute)": [[14, "day06.day6.Race.time"]], "day06.tests": [[15, "module-day06.tests"]], "day06.tests.test_day6": [[15, "module-day06.tests.test_day6"]], "test_calculate_constant_time() (in module day06.tests.test_day6)": [[15, "day06.tests.test_day6.test_calculate_constant_time"]], "test_calculate_race() (in module day06.tests.test_day6)": [[15, "day06.tests.test_day6.test_calculate_race"]], "test_get_giga_race() (in module day06.tests.test_day6)": [[15, "day06.tests.test_day6.test_get_giga_race"]], "test_part1() (in module day06.tests.test_day6)": [[15, "day06.tests.test_day6.test_part1"]], "test_part2() (in module day06.tests.test_day6)": [[15, "day06.tests.test_day6.test_part2"]], "test_read_inputs() (in module day06.tests.test_day6)": [[15, "day06.tests.test_day6.test_read_inputs"]], "card_mapping (day07.day7.hand attribute)": [[16, "day07.day7.Hand.CARD_MAPPING"]], "card_mapping (day07.day7.handpart2 attribute)": [[16, "day07.day7.HandPart2.CARD_MAPPING"]], "hand (class in day07.day7)": [[16, "day07.day7.Hand"]], "handpart2 (class in day07.day7)": [[16, "day07.day7.HandPart2"]], "bet (day07.day7.hand attribute)": [[16, "day07.day7.Hand.bet"]], "calculate_hands() (in module day07.day7)": [[16, "day07.day7.calculate_hands"]], "calculate_of_a_kind() (day07.day7.hand method)": [[16, "day07.day7.Hand.calculate_of_a_kind"]], "calculate_of_a_kind() (day07.day7.handpart2 method)": [[16, "day07.day7.HandPart2.calculate_of_a_kind"]], "cards (day07.day7.hand attribute)": [[16, "day07.day7.Hand.cards"]], "cards_inted (day07.day7.hand attribute)": [[16, "day07.day7.Hand.cards_inted"]], "day07": [[16, "module-day07"]], "day07.day7": [[16, "module-day07.day7"]], "main() (in module day07.day7)": [[16, "day07.day7.main"]], "of_a_kind (day07.day7.hand attribute)": [[16, "day07.day7.Hand.of_a_kind"]], "parse_lines() (in module day07.day7)": [[16, "day07.day7.parse_lines"]], "day07.tests": [[17, "module-day07.tests"]], "day07.tests.test_day7": [[17, "module-day07.tests.test_day7"]], "test_calculate_hands() (in module day07.tests.test_day7)": [[17, "day07.tests.test_day7.test_calculate_hands"]], "test_hand() (in module day07.tests.test_day7)": [[17, "day07.tests.test_day7.test_hand"]], "test_parser() (in module day07.tests.test_day7)": [[17, "day07.tests.test_day7.test_parser"]], "cycle (class in day08.day8)": [[18, "day08.day8.Cycle"]], "directions (class in day08.day8)": [[18, "day08.day8.Directions"]], "location (class in day08.day8)": [[18, "day08.day8.Location"]], "locationstep (class in day08.day8)": [[18, "day08.day8.LocationStep"]], "worldmap (class in day08.day8)": [[18, "day08.day8.WorldMap"]], "add_location() (day08.day8.worldmap method)": [[18, "day08.day8.WorldMap.add_location"]], "cycle_length (day08.day8.cycle property)": [[18, "day08.day8.Cycle.cycle_length"]], "cycle_start (day08.day8.cycle attribute)": [[18, "day08.day8.Cycle.cycle_start"]], "cycle_start_index (day08.day8.cycle attribute)": [[18, "day08.day8.Cycle.cycle_start_index"]], "day08": [[18, "module-day08"]], "day08.day8": [[18, "module-day08.day8"]], "end_zs (day08.day8.cycle attribute)": [[18, "day08.day8.Cycle.end_zs"]], "find_cycle() (in module day08.day8)": [[18, "day08.day8.find_cycle"]], "follow_directions() (in module day08.day8)": [[18, "day08.day8.follow_directions"]], "follow_directions_multi() (in module day08.day8)": [[18, "day08.day8.follow_directions_multi"]], "get_location() (day08.day8.cycle method)": [[18, "day08.day8.Cycle.get_location"]], "get_location_as() (in module day08.day8)": [[18, "day08.day8.get_location_as"]], "get_step() (day08.day8.directions method)": [[18, "day08.day8.Directions.get_step"]], "get_steps_iterator() (day08.day8.directions method)": [[18, "day08.day8.Directions.get_steps_iterator"]], "left (day08.day8.location attribute)": [[18, "day08.day8.Location.left"]], "location (day08.day8.locationstep attribute)": [[18, "day08.day8.LocationStep.location"]], "location_steps (day08.day8.cycle attribute)": [[18, "day08.day8.Cycle.location_steps"]], "main() (in module day08.day8)": [[18, "day08.day8.main"]], "mappings (day08.day8.worldmap attribute)": [[18, "day08.day8.WorldMap.mappings"]], "name (day08.day8.location attribute)": [[18, "day08.day8.Location.name"]], "read_input() (in module day08.day8)": [[18, "day08.day8.read_input"]], "right (day08.day8.location attribute)": [[18, "day08.day8.Location.right"]], "start_location (day08.day8.cycle attribute)": [[18, "day08.day8.Cycle.start_location"]], "steps (day08.day8.directions attribute)": [[18, "day08.day8.Directions.steps"]], "steps (day08.day8.locationstep attribute)": [[18, "day08.day8.LocationStep.steps"]], "day08.tests": [[19, "module-day08.tests"]], "day08.tests.test_day8": [[19, "module-day08.tests.test_day8"]], "test_directions() (in module day08.tests.test_day8)": [[19, "day08.tests.test_day8.test_directions"]], "test_find_cycle() (in module day08.tests.test_day8)": [[19, "day08.tests.test_day8.test_find_cycle"]], "test_location_as() (in module day08.tests.test_day8)": [[19, "day08.tests.test_day8.test_location_as"]], "test_part1() (in module day08.tests.test_day8)": [[19, "day08.tests.test_day8.test_part1"]], "test_part2() (in module day08.tests.test_day8)": [[19, "day08.tests.test_day8.test_part2"]], "valuearray (class in day09.day9)": [[20, "day09.day9.ValueArray"]], "day09": [[20, "module-day09"]], "day09.day9": [[20, "module-day09.day9"]], "extrapolate_left() (day09.day9.valuearray method)": [[20, "day09.day9.ValueArray.extrapolate_left"]], "extrapolate_right() (day09.day9.valuearray method)": [[20, "day09.day9.ValueArray.extrapolate_right"]], "generic_extrapolate() (day09.day9.valuearray method)": [[20, "day09.day9.ValueArray.generic_extrapolate"]], "get_input() (in module day09.day9)": [[20, "day09.day9.get_input"]], "interpolate() (in module day09.day9)": [[20, "day09.day9.interpolate"]], "main() (in module day09.day9)": [[20, "day09.day9.main"]], "part1() (in module day09.day9)": [[20, "day09.day9.part1"]], "part2() (in module day09.day9)": [[20, "day09.day9.part2"]], "sub_arrays (day09.day9.valuearray attribute)": [[20, "day09.day9.ValueArray.sub_arrays"]], "day09.tests": [[21, "module-day09.tests"]], "day09.tests.test_day9": [[21, "module-day09.tests.test_day9"]], "test_get_input() (in module day09.tests.test_day9)": [[21, "day09.tests.test_day9.test_get_input"]], "test_interpolate() (in module day09.tests.test_day9)": [[21, "day09.tests.test_day9.test_interpolate"]], "test_part1() (in module day09.tests.test_day9)": [[21, "day09.tests.test_day9.test_part1"]], "test_part2() (in module day09.tests.test_day9)": [[21, "day09.tests.test_day9.test_part2"]], "calculate_s() (in module day10.day10)": [[22, "day10.day10.calculate_s"]], "day10": [[22, "module-day10"]], "day10.day10": [[22, "module-day10.day10"]], "expand_map() (in module day10.day10)": [[22, "day10.day10.expand_map"]], "expand_pipe() (in module day10.day10)": [[22, "day10.day10.expand_pipe"]], "find_cycles() (in module day10.day10)": [[22, "day10.day10.find_cycles"]], "find_s() (in module day10.day10)": [[22, "day10.day10.find_s"]], "flood_fill() (in module day10.day10)": [[22, "day10.day10.flood_fill"]], "main() (in module day10.day10)": [[22, "day10.day10.main"]], "part1() (in module day10.day10)": [[22, "day10.day10.part1"]], "part2() (in module day10.day10)": [[22, "day10.day10.part2"]], "process_big_input_line() (in module day10.day10)": [[22, "day10.day10.process_big_input_line"]], "process_input_line() (in module day10.day10)": [[22, "day10.day10.process_input_line"]], "read_input() (in module day10.day10)": [[22, "day10.day10.read_input"]], "reduce_map() (in module day10.day10)": [[22, "day10.day10.reduce_map"]], "direction (class in day10.lib.direction)": [[23, "day10.lib.direction.Direction"]], "east (day10.lib.direction.direction attribute)": [[23, "day10.lib.direction.Direction.EAST"]], "inside (day10.lib.pipebounds.pipebounds attribute)": [[23, "day10.lib.pipebounds.PipeBounds.INSIDE"]], "north (day10.lib.direction.direction attribute)": [[23, "day10.lib.direction.Direction.NORTH"]], "outside (day10.lib.pipebounds.pipebounds attribute)": [[23, "day10.lib.pipebounds.PipeBounds.OUTSIDE"]], "pipe (day10.lib.pipebounds.pipebounds attribute)": [[23, "day10.lib.pipebounds.PipeBounds.PIPE"]], "pipe_direction (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.PIPE_DIRECTION"]], "pipe (class in day10.lib.pipes)": [[23, "day10.lib.pipes.Pipe"]], "pipebounds (class in day10.lib.pipebounds)": [[23, "day10.lib.pipebounds.PipeBounds"]], "pipemap (class in day10.lib.pipes)": [[23, "day10.lib.pipes.PipeMap"]], "position (class in day10.lib.position)": [[23, "day10.lib.position.Position"]], "south (day10.lib.direction.direction attribute)": [[23, "day10.lib.direction.Direction.SOUTH"]], "unknown (day10.lib.pipebounds.pipebounds attribute)": [[23, "day10.lib.pipebounds.PipeBounds.UNKNOWN"]], "west (day10.lib.direction.direction attribute)": [[23, "day10.lib.direction.Direction.WEST"]], "character (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.character"]], "col (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.col"]], "col (day10.lib.position.position attribute)": [[23, "day10.lib.position.Position.col"]], "day10.lib": [[23, "module-day10.lib"]], "day10.lib.direction": [[23, "module-day10.lib.direction"]], "day10.lib.pipebounds": [[23, "module-day10.lib.pipebounds"]], "day10.lib.pipes": [[23, "module-day10.lib.pipes"]], "day10.lib.position": [[23, "module-day10.lib.position"]], "font (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.font"]], "get_pipe() (day10.lib.pipes.pipemap method)": [[23, "day10.lib.pipes.PipeMap.get_pipe"]], "get_pipe_safe() (day10.lib.pipes.pipemap method)": [[23, "day10.lib.pipes.PipeMap.get_pipe_safe"]], "height (day10.lib.pipes.pipemap attribute)": [[23, "day10.lib.pipes.PipeMap.height"]], "is_in_map() (day10.lib.pipes.pipemap method)": [[23, "day10.lib.pipes.PipeMap.is_in_map"]], "is_loop (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.is_loop"]], "is_start (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.is_start"]], "next_direction() (day10.lib.pipes.pipe method)": [[23, "day10.lib.pipes.Pipe.next_direction"]], "next_position() (day10.lib.pipes.pipe method)": [[23, "day10.lib.pipes.Pipe.next_position"]], "next_position() (day10.lib.position.position method)": [[23, "day10.lib.position.Position.next_position"]], "opposite() (day10.lib.direction.direction method)": [[23, "day10.lib.direction.Direction.opposite"]], "pipe_bounds (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.pipe_bounds"]], "pipes (day10.lib.pipes.pipemap attribute)": [[23, "day10.lib.pipes.PipeMap.pipes"]], "position (day10.lib.pipes.pipe property)": [[23, "day10.lib.pipes.Pipe.position"]], "row (day10.lib.pipes.pipe attribute)": [[23, "day10.lib.pipes.Pipe.row"]], "row (day10.lib.position.position attribute)": [[23, "day10.lib.position.Position.row"]], "width (day10.lib.pipes.pipemap attribute)": [[23, "day10.lib.pipes.PipeMap.width"]], "day10.tests": [[24, "module-day10.tests"]], "day10.tests.test_day10": [[24, "module-day10.tests.test_day10"]], "day10.tests.test_direction": [[24, "module-day10.tests.test_direction"]], "test_day10() (in module day10.tests.test_day10)": [[24, "day10.tests.test_day10.test_day10"]], "test_direction() (in module day10.tests.test_direction)": [[24, "day10.tests.test_direction.test_direction"]], "galaxy (class in day11.day11)": [[25, "day11.day11.Galaxy"]], "point (class in day11.day11)": [[25, "day11.day11.Point"]], "universe (class in day11.day11)": [[25, "day11.day11.Universe"]], "col (day11.day11.galaxy attribute)": [[25, "day11.day11.Galaxy.col"]], "col (day11.day11.point attribute)": [[25, "day11.day11.Point.col"]], "contents (day11.day11.universe attribute)": [[25, "day11.day11.Universe.contents"]], "day11": [[25, "module-day11"]], "day11.day11": [[25, "module-day11.day11"]], "distance() (day11.day11.galaxy method)": [[25, "day11.day11.Galaxy.distance"]], "expand_contents() (day11.day11.universe method)": [[25, "day11.day11.Universe.expand_contents"]], "expansion_rate (day11.day11.universe attribute)": [[25, "day11.day11.Universe.expansion_rate"]], "get_point_distance() (day11.day11.point method)": [[25, "day11.day11.Point.get_point_distance"]], "get_total_distance() (in module day11.day11)": [[25, "day11.day11.get_total_distance"]], "grab_galaxies() (day11.day11.universe method)": [[25, "day11.day11.Universe.grab_galaxies"]], "id (day11.day11.galaxy attribute)": [[25, "day11.day11.Galaxy.id"]], "is_empty() (in module day11.day11)": [[25, "day11.day11.is_empty"]], "is_expanded (day11.day11.point attribute)": [[25, "day11.day11.Point.is_expanded"]], "item (day11.day11.point attribute)": [[25, "day11.day11.Point.item"]], "main() (in module day11.day11)": [[25, "day11.day11.main"]], "num_cols (day11.day11.universe attribute)": [[25, "day11.day11.Universe.num_cols"]], "num_rows (day11.day11.universe attribute)": [[25, "day11.day11.Universe.num_rows"]], "parse_input() (in module day11.day11)": [[25, "day11.day11.parse_input"]], "row (day11.day11.galaxy attribute)": [[25, "day11.day11.Galaxy.row"]], "row (day11.day11.point attribute)": [[25, "day11.day11.Point.row"]], "day11.tests": [[26, "module-day11.tests"]], "day11.tests.test_day11": [[26, "module-day11.tests.test_day11"]], "test_day11() (in module day11.tests.test_day11)": [[26, "day11.tests.test_day11.test_day11"]], "test_expansion() (in module day11.tests.test_day11)": [[26, "day11.tests.test_day11.test_expansion"]], "test_is_empty() (in module day11.tests.test_day11)": [[26, "day11.tests.test_day11.test_is_empty"]], "springline (class in day12.day12)": [[27, "day12.day12.SpringLine"]], "state (class in day12.day12)": [[27, "day12.day12.State"]], "big_cache (day12.day12.springline attribute)": [[27, "day12.day12.SpringLine.big_cache"]], "broken_springs (day12.day12.springline attribute)": [[27, "day12.day12.SpringLine.broken_springs"]], "broken_springs (day12.day12.state attribute)": [[27, "day12.day12.State.broken_springs"]], "calculate() (day12.day12.springline method)": [[27, "day12.day12.SpringLine.calculate"]], "calculate_recursive() (day12.day12.springline method)": [[27, "day12.day12.SpringLine.calculate_recursive"]], "calculate_sum() (in module day12.day12)": [[27, "day12.day12.calculate_sum"]], "day12": [[27, "module-day12"]], "day12.day12": [[27, "module-day12.day12"]], "get_input() (in module day12.day12)": [[27, "day12.day12.get_input"]], "items (day12.day12.springline attribute)": [[27, "day12.day12.SpringLine.items"]], "items (day12.day12.state attribute)": [[27, "day12.day12.State.items"]], "main() (in module day12.day12)": [[27, "day12.day12.main"]], "set_and_return() (day12.day12.springline method)": [[27, "day12.day12.SpringLine.set_and_return"]], "unfold() (day12.day12.springline method)": [[27, "day12.day12.SpringLine.unfold"]], "valid() (day12.day12.state method)": [[27, "day12.day12.State.valid"]], "day12.tests": [[28, "module-day12.tests"]], "day12.tests.test_day12": [[28, "module-day12.tests.test_day12"]], "test_calculate_sum() (in module day12.tests.test_day12)": [[28, "day12.tests.test_day12.test_calculate_sum"]], "test_parser() (in module day12.tests.test_day12)": [[28, "day12.tests.test_day12.test_parser"]], "test_spring_line() (in module day12.tests.test_day12)": [[28, "day12.tests.test_day12.test_spring_line"]], "maze (class in day13.day13)": [[29, "day13.day13.Maze"]], "check_reflection() (day13.day13.maze method)": [[29, "day13.day13.Maze.check_reflection"]], "day13": [[29, "module-day13"]], "day13.day13": [[29, "module-day13.day13"]], "distance() (in module day13.day13)": [[29, "day13.day13.distance"]], "main() (in module day13.day13)": [[29, "day13.day13.main"]], "read_input() (in module day13.day13)": [[29, "day13.day13.read_input"]], "reflect_cols() (day13.day13.maze method)": [[29, "day13.day13.Maze.reflect_cols"]], "reflect_rows() (day13.day13.maze method)": [[29, "day13.day13.Maze.reflect_rows"]], "score() (day13.day13.maze method)": [[29, "day13.day13.Maze.score"]], "solve() (day13.day13.maze method)": [[29, "day13.day13.Maze.solve"]], "tiles (day13.day13.maze attribute)": [[29, "day13.day13.Maze.tiles"]], "day13.tests": [[30, "module-day13.tests"]], "day13.tests.test_day13": [[30, "module-day13.tests.test_day13"]], "test_day13() (in module day13.tests.test_day13)": [[30, "day13.tests.test_day13.test_day13"]], "test_distance() (in module day13.tests.test_day13)": [[30, "day13.tests.test_day13.test_distance"]], "world (class in day14.day14)": [[31, "day14.day14.World"]], "as_orientiented_north() (day14.day14.world method)": [[31, "day14.day14.World.as_orientiented_north"]], "correct_side() (day14.day14.world method)": [[31, "day14.day14.World.correct_side"]], "data (day14.day14.world attribute)": [[31, "day14.day14.World.data"]], "day14": [[31, "module-day14"]], "day14.day14": [[31, "module-day14.day14"]], "get_input() (in module day14.day14)": [[31, "day14.day14.get_input"]], "get_score() (day14.day14.world method)": [[31, "day14.day14.World.get_score"]], "left_is (day14.day14.world attribute)": [[31, "day14.day14.World.left_is"]], "main() (in module day14.day14)": [[31, "day14.day14.main"]], "naive_score() (in module day14.day14)": [[31, "day14.day14.naive_score"]], "question1() (in module day14.day14)": [[31, "day14.day14.question1"]], "question2() (in module day14.day14)": [[31, "day14.day14.question2"]], "rotate_world_ccw() (day14.day14.world method)": [[31, "day14.day14.World.rotate_world_ccw"]], "rotate_world_cw() (day14.day14.world method)": [[31, "day14.day14.World.rotate_world_cw"]], "score (day14.day14.world attribute)": [[31, "day14.day14.World.score"]], "simulate_row() (in module day14.day14)": [[31, "day14.day14.simulate_row"]], "simulate_world() (in module day14.day14)": [[31, "day14.day14.simulate_world"]], "to_string() (day14.day14.world method)": [[31, "day14.day14.World.to_string"]], "direction (class in day14.lib.direction)": [[32, "day14.lib.direction.Direction"]], "east (day14.lib.direction.direction attribute)": [[32, "day14.lib.direction.Direction.East"]], "north (day14.lib.direction.direction attribute)": [[32, "day14.lib.direction.Direction.North"]], "south (day14.lib.direction.direction attribute)": [[32, "day14.lib.direction.Direction.South"]], "west (day14.lib.direction.direction attribute)": [[32, "day14.lib.direction.Direction.West"]], "day14.lib": [[32, "module-day14.lib"]], "day14.lib.direction": [[32, "module-day14.lib.direction"]], "next_direction_ccw() (day14.lib.direction.direction method)": [[32, "day14.lib.direction.Direction.next_direction_ccw"]], "next_direction_cw() (day14.lib.direction.direction method)": [[32, "day14.lib.direction.Direction.next_direction_cw"]], "day14.tests": [[33, "module-day14.tests"]], "day14.tests.test_day14": [[33, "module-day14.tests.test_day14"]], "day14.tests.test_direction": [[33, "module-day14.tests.test_direction"]], "test_direction() (in module day14.tests.test_direction)": [[33, "day14.tests.test_direction.test_direction"]], "test_get_input() (in module day14.tests.test_day14)": [[33, "day14.tests.test_day14.test_get_input"]], "test_questions() (in module day14.tests.test_day14)": [[33, "day14.tests.test_day14.test_questions"]], "test_rotate_world() (in module day14.tests.test_day14)": [[33, "day14.tests.test_day14.test_rotate_world"]], "test_simulate_row() (in module day14.tests.test_day14)": [[33, "day14.tests.test_day14.test_simulate_row"]], "day15": [[34, "module-day15"]], "day15.day15": [[34, "module-day15.day15"]], "get_input() (in module day15.day15)": [[34, "day15.day15.get_input"]], "get_string_hash() (in module day15.day15)": [[34, "day15.day15.get_string_hash"]], "main() (in module day15.day15)": [[34, "day15.day15.main"]], "parse_step_pt2() (in module day15.day15)": [[34, "day15.day15.parse_step_pt2"]], "process_steps_pt2() (in module day15.day15)": [[34, "day15.day15.process_steps_pt2"]], "question1() (in module day15.day15)": [[34, "day15.day15.question1"]], "question2() (in module day15.day15)": [[34, "day15.day15.question2"]], "add (day15.lib.classes.addremove attribute)": [[35, "day15.lib.classes.AddRemove.Add"]], "addremove (class in day15.lib.classes)": [[35, "day15.lib.classes.AddRemove"]], "box (class in day15.lib.classes)": [[35, "day15.lib.classes.Box"]], "lens (class in day15.lib.classes)": [[35, "day15.lib.classes.Lens"]], "remove (day15.lib.classes.addremove attribute)": [[35, "day15.lib.classes.AddRemove.Remove"]], "step (class in day15.lib.classes)": [[35, "day15.lib.classes.Step"]], "add_lens() (day15.lib.classes.box method)": [[35, "day15.lib.classes.Box.add_lens"]], "box (day15.lib.classes.step attribute)": [[35, "day15.lib.classes.Step.box"]], "calculate_power() (day15.lib.classes.box method)": [[35, "day15.lib.classes.Box.calculate_power"]], "contents (day15.lib.classes.box attribute)": [[35, "day15.lib.classes.Box.contents"]], "day15.lib": [[35, "module-day15.lib"]], "day15.lib.classes": [[35, "module-day15.lib.classes"]], "focal_length (day15.lib.classes.lens attribute)": [[35, "day15.lib.classes.Lens.focal_length"]], "focal_length (day15.lib.classes.step attribute)": [[35, "day15.lib.classes.Step.focal_length"]], "id (day15.lib.classes.box attribute)": [[35, "day15.lib.classes.Box.id"]], "lens_name (day15.lib.classes.step attribute)": [[35, "day15.lib.classes.Step.lens_name"]], "name (day15.lib.classes.lens attribute)": [[35, "day15.lib.classes.Lens.name"]], "process (day15.lib.classes.step attribute)": [[35, "day15.lib.classes.Step.process"]], "remove_lens() (day15.lib.classes.box method)": [[35, "day15.lib.classes.Box.remove_lens"]], "day15.tests": [[36, "module-day15.tests"]], "day15.tests.test_classes": [[36, "module-day15.tests.test_classes"]], "day15.tests.test_day15": [[36, "module-day15.tests.test_day15"]], "test_box() (in module day15.tests.test_classes)": [[36, "day15.tests.test_classes.test_box"]], "test_get_input() (in module day15.tests.test_day15)": [[36, "day15.tests.test_day15.test_get_input"]], "test_get_string_hash() (in module day15.tests.test_day15)": [[36, "day15.tests.test_day15.test_get_string_hash"]], "test_lens() (in module day15.tests.test_classes)": [[36, "day15.tests.test_classes.test_lens"]], "test_parse_pt2() (in module day15.tests.test_day15)": [[36, "day15.tests.test_day15.test_parse_pt2"]], "test_questions() (in module day15.tests.test_day15)": [[36, "day15.tests.test_day15.test_questions"]], "day16": [[37, "module-day16"]], "day16.day16": [[37, "module-day16.day16"]], "main() (in module day16.day16)": [[37, "day16.day16.main"]], "part1() (in module day16.day16)": [[37, "day16.day16.part1"]], "part2() (in module day16.day16)": [[37, "day16.day16.part2"]], "solve_task() (in module day16.day16)": [[37, "day16.day16.solve_task"]], "solve_task_wrapper() (in module day16.day16)": [[37, "day16.day16.solve_task_wrapper"]], "backslashcell (class in day16.lib.cells)": [[38, "day16.lib.cells.BackSlashCell"]], "cell_types (day16.lib.cells.cell attribute)": [[38, "day16.lib.cells.Cell.CELL_TYPES"]], "cell (class in day16.lib.cells)": [[38, "day16.lib.cells.Cell"]], "dashcell (class in day16.lib.cells)": [[38, "day16.lib.cells.DashCell"]], "direction (class in day16.lib.direction)": [[38, "day16.lib.direction.Direction"]], "dotcell (class in day16.lib.cells)": [[38, "day16.lib.cells.DotCell"]], "east (day16.lib.direction.direction attribute)": [[38, "day16.lib.direction.Direction.EAST"]], "forwardslashcell (class in day16.lib.cells)": [[38, "day16.lib.cells.ForwardSlashCell"]], "laser (class in day16.lib.laser)": [[38, "day16.lib.laser.Laser"]], "north (day16.lib.direction.direction attribute)": [[38, "day16.lib.direction.Direction.NORTH"]], "pipecell (class in day16.lib.cells)": [[38, "day16.lib.cells.PipeCell"]], "south (day16.lib.direction.direction attribute)": [[38, "day16.lib.direction.Direction.SOUTH"]], "solvedworld (class in day16.lib.world)": [[38, "day16.lib.world.SolvedWorld"]], "west (day16.lib.direction.direction attribute)": [[38, "day16.lib.direction.Direction.WEST"]], "world (class in day16.lib.world)": [[38, "day16.lib.world.World"]], "add_laser() (day16.lib.world.solvedworld method)": [[38, "day16.lib.world.SolvedWorld.add_laser"]], "already_solved() (day16.lib.world.solvedworld method)": [[38, "day16.lib.world.SolvedWorld.already_solved"]], "col (day16.lib.laser.laser attribute)": [[38, "day16.lib.laser.Laser.col"]], "construct() (day16.lib.cells.cell static method)": [[38, "day16.lib.cells.Cell.construct"]], "contents (day16.lib.cells.cell attribute)": [[38, "day16.lib.cells.Cell.contents"]], "data (day16.lib.world.solvedworld attribute)": [[38, "day16.lib.world.SolvedWorld.data"]], "data (day16.lib.world.world attribute)": [[38, "day16.lib.world.World.data"]], "day16.lib": [[38, "module-day16.lib"]], "day16.lib.cells": [[38, "module-day16.lib.cells"]], "day16.lib.direction": [[38, "module-day16.lib.direction"]], "day16.lib.laser": [[38, "module-day16.lib.laser"]], "day16.lib.parsers": [[38, "module-day16.lib.parsers"]], "day16.lib.world": [[38, "module-day16.lib.world"]], "direction (day16.lib.laser.laser attribute)": [[38, "day16.lib.laser.Laser.direction"]], "get_input() (in module day16.lib.parsers)": [[38, "day16.lib.parsers.get_input"]], "is_oob() (day16.lib.world.world method)": [[38, "day16.lib.world.World.is_oob"]], "next_lasers() (day16.lib.cells.backslashcell method)": [[38, "day16.lib.cells.BackSlashCell.next_lasers"]], "next_lasers() (day16.lib.cells.cell method)": [[38, "day16.lib.cells.Cell.next_lasers"]], "next_lasers() (day16.lib.cells.dashcell method)": [[38, "day16.lib.cells.DashCell.next_lasers"]], "next_lasers() (day16.lib.cells.dotcell method)": [[38, "day16.lib.cells.DotCell.next_lasers"]], "next_lasers() (day16.lib.cells.forwardslashcell method)": [[38, "day16.lib.cells.ForwardSlashCell.next_lasers"]], "next_lasers() (day16.lib.cells.pipecell method)": [[38, "day16.lib.cells.PipeCell.next_lasers"]], "num_cols (day16.lib.world.world attribute)": [[38, "day16.lib.world.World.num_cols"]], "num_energized() (day16.lib.world.solvedworld method)": [[38, "day16.lib.world.SolvedWorld.num_energized"]], "num_rows (day16.lib.world.world attribute)": [[38, "day16.lib.world.World.num_rows"]], "offset() (day16.lib.direction.direction method)": [[38, "day16.lib.direction.Direction.offset"]], "opposite() (day16.lib.direction.direction method)": [[38, "day16.lib.direction.Direction.opposite"]], "register_cell_type() (day16.lib.cells.cell static method)": [[38, "day16.lib.cells.Cell.register_cell_type"]], "row (day16.lib.laser.laser attribute)": [[38, "day16.lib.laser.Laser.row"]], "solve() (day16.lib.world.world method)": [[38, "day16.lib.world.World.solve"]], "day16.tests": [[39, "module-day16.tests"]], "day16.tests.test_cells": [[39, "module-day16.tests.test_cells"]], "day16.tests.test_day16": [[39, "module-day16.tests.test_day16"]], "day16.tests.test_direction": [[39, "module-day16.tests.test_direction"]], "day16.tests.test_world": [[39, "module-day16.tests.test_world"]], "test_backslashcell() (in module day16.tests.test_cells)": [[39, "day16.tests.test_cells.test_backslashcell"]], "test_cell() (in module day16.tests.test_cells)": [[39, "day16.tests.test_cells.test_cell"]], "test_dashcell() (in module day16.tests.test_cells)": [[39, "day16.tests.test_cells.test_dashcell"]], "test_direction() (in module day16.tests.test_direction)": [[39, "day16.tests.test_direction.test_direction"]], "test_dotcell() (in module day16.tests.test_cells)": [[39, "day16.tests.test_cells.test_dotcell"]], "test_forwardslashcell() (in module day16.tests.test_cells)": [[39, "day16.tests.test_cells.test_forwardslashcell"]], "test_part1() (in module day16.tests.test_day16)": [[39, "day16.tests.test_day16.test_part1"]], "test_part2() (in module day16.tests.test_day16)": [[39, "day16.tests.test_day16.test_part2"]], "test_pipecell() (in module day16.tests.test_cells)": [[39, "day16.tests.test_cells.test_pipecell"]], "test_world() (in module day16.tests.test_world)": [[39, "day16.tests.test_world.test_world"]], "day17": [[40, "module-day17"]], "day17.day17": [[40, "module-day17.day17"]], "main() (in module day17.day17)": [[40, "day17.day17.main"]], "part1() (in module day17.day17)": [[40, "day17.day17.part1"]], "part2() (in module day17.day17)": [[40, "day17.day17.part2"]], "solve_and_print() (in module day17.day17)": [[40, "day17.day17.solve_and_print"]], "direction (class in day17.lib.direction)": [[41, "day17.lib.direction.Direction"]], "east (day17.lib.direction.direction attribute)": [[41, "day17.lib.direction.Direction.EAST"]], "north (day17.lib.direction.direction attribute)": [[41, "day17.lib.direction.Direction.NORTH"]], "south (day17.lib.direction.direction attribute)": [[41, "day17.lib.direction.Direction.SOUTH"]], "solutioncache (class in day17.lib.classes)": [[41, "day17.lib.classes.SolutionCache"]], "step (class in day17.lib.classes)": [[41, "day17.lib.classes.Step"]], "tilecache (class in day17.lib.classes)": [[41, "day17.lib.classes.TileCache"]], "west (day17.lib.direction.direction attribute)": [[41, "day17.lib.direction.Direction.WEST"]], "worldpart1 (class in day17.lib.classes)": [[41, "day17.lib.classes.WorldPart1"]], "worldpart2 (class in day17.lib.classes)": [[41, "day17.lib.classes.WorldPart2"]], "add_solution() (day17.lib.classes.solutioncache method)": [[41, "day17.lib.classes.SolutionCache.add_solution"]], "cache (day17.lib.classes.solutioncache attribute)": [[41, "day17.lib.classes.SolutionCache.cache"]], "cache (day17.lib.classes.tilecache attribute)": [[41, "day17.lib.classes.TileCache.cache"]], "cache_max (day17.lib.classes.tilecache attribute)": [[41, "day17.lib.classes.TileCache.cache_max"]], "cache_min (day17.lib.classes.tilecache attribute)": [[41, "day17.lib.classes.TileCache.cache_min"]], "col (day17.lib.classes.step attribute)": [[41, "day17.lib.classes.Step.col"]], "consecutive_steps (day17.lib.classes.step attribute)": [[41, "day17.lib.classes.Step.consecutive_steps"]], "costs (day17.lib.classes.worldpart1 attribute)": [[41, "day17.lib.classes.WorldPart1.costs"]], "create_step() (day17.lib.classes.worldpart1 method)": [[41, "day17.lib.classes.WorldPart1.create_step"]], "create_step() (day17.lib.classes.worldpart2 method)": [[41, "day17.lib.classes.WorldPart2.create_step"]], "day17.lib": [[41, "module-day17.lib"]], "day17.lib.classes": [[41, "module-day17.lib.classes"]], "day17.lib.direction": [[41, "module-day17.lib.direction"]], "day17.lib.parsers": [[41, "module-day17.lib.parsers"]], "direction (day17.lib.classes.step attribute)": [[41, "day17.lib.classes.Step.direction"]], "get_input() (in module day17.lib.parsers)": [[41, "day17.lib.parsers.get_input"]], "is_oob() (day17.lib.classes.worldpart1 method)": [[41, "day17.lib.classes.WorldPart1.is_oob"]], "num_cols (day17.lib.classes.worldpart1 attribute)": [[41, "day17.lib.classes.WorldPart1.num_cols"]], "num_rows (day17.lib.classes.worldpart1 attribute)": [[41, "day17.lib.classes.WorldPart1.num_rows"]], "offset() (day17.lib.direction.direction method)": [[41, "day17.lib.direction.Direction.offset"]], "offset_list() (day17.lib.direction.direction method)": [[41, "day17.lib.direction.Direction.offset_list"]], "opposite() (day17.lib.direction.direction method)": [[41, "day17.lib.direction.Direction.opposite"]], "row (day17.lib.classes.step attribute)": [[41, "day17.lib.classes.Step.row"]], "solve() (day17.lib.classes.worldpart1 method)": [[41, "day17.lib.classes.WorldPart1.solve"]], "solve() (day17.lib.classes.worldpart2 method)": [[41, "day17.lib.classes.WorldPart2.solve"]], "src_step (day17.lib.classes.step attribute)": [[41, "day17.lib.classes.Step.src_step"]], "total_cost (day17.lib.classes.step attribute)": [[41, "day17.lib.classes.Step.total_cost"]], "day17.tests": [[42, "module-day17.tests"]], "day17.tests.test_day17": [[42, "module-day17.tests.test_day17"]], "day17.tests.test_direction": [[42, "module-day17.tests.test_direction"]], "day17.tests.test_parsers": [[42, "module-day17.tests.test_parsers"]], "test_direction() (in module day17.tests.test_direction)": [[42, "day17.tests.test_direction.test_direction"]], "test_parser() (in module day17.tests.test_parsers)": [[42, "day17.tests.test_parsers.test_parser"]], "test_parts() (in module day17.tests.test_day17)": [[42, "day17.tests.test_day17.test_parts"]], "command (class in day18.day18a)": [[43, "day18.day18a.Command"]], "command (class in day18.day18b)": [[43, "day18.day18b.Command"]], "direction (class in day18.day18a)": [[43, "day18.day18a.Direction"]], "direction (class in day18.day18b)": [[43, "day18.day18b.Direction"]], "down (day18.day18a.direction attribute)": [[43, "day18.day18a.Direction.Down"]], "down (day18.day18b.direction attribute)": [[43, "day18.day18b.Direction.Down"]], "left (day18.day18a.direction attribute)": [[43, "day18.day18a.Direction.Left"]], "left (day18.day18b.direction attribute)": [[43, "day18.day18b.Direction.Left"]], "matrix (class in day18.day18a)": [[43, "day18.day18a.Matrix"]], "position (class in day18.day18a)": [[43, "day18.day18a.Position"]], "position (class in day18.day18b)": [[43, "day18.day18b.Position"]], "right (day18.day18a.direction attribute)": [[43, "day18.day18a.Direction.Right"]], "right (day18.day18b.direction attribute)": [[43, "day18.day18b.Direction.Right"]], "up (day18.day18a.direction attribute)": [[43, "day18.day18a.Direction.Up"]], "up (day18.day18b.direction attribute)": [[43, "day18.day18b.Direction.Up"]], "calculate_area() (in module day18.day18b)": [[43, "day18.day18b.calculate_area"]], "col (day18.day18a.position attribute)": [[43, "day18.day18a.Position.col"]], "col (day18.day18b.position attribute)": [[43, "day18.day18b.Position.col"]], "color (day18.day18a.command attribute)": [[43, "day18.day18a.Command.color"]], "contents (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.contents"]], "day18": [[43, "module-day18"]], "day18.day18a": [[43, "module-day18.day18a"]], "day18.day18b": [[43, "module-day18.day18b"]], "dig_out() (day18.day18a.matrix method)": [[43, "day18.day18a.Matrix.dig_out"]], "direction (day18.day18a.command attribute)": [[43, "day18.day18a.Command.direction"]], "direction (day18.day18b.command attribute)": [[43, "day18.day18b.Command.direction"]], "dug_tiles (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.dug_tiles"]], "generate_offsets() (in module day18.day18a)": [[43, "day18.day18a.generate_offsets"]], "get_input() (in module day18.day18a)": [[43, "day18.day18a.get_input"]], "get_input() (in module day18.day18b)": [[43, "day18.day18b.get_input"]], "get_matrix_range() (in module day18.day18a)": [[43, "day18.day18a.get_matrix_range"]], "get_solution() (in module day18.day18a)": [[43, "day18.day18a.get_solution"]], "get_solution() (in module day18.day18b)": [[43, "day18.day18b.get_solution"]], "is_oob() (day18.day18a.matrix method)": [[43, "day18.day18a.Matrix.is_oob"]], "main() (in module day18.day18a)": [[43, "day18.day18a.main"]], "main() (in module day18.day18b)": [[43, "day18.day18b.main"]], "max_pos (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.max_pos"]], "min_pos (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.min_pos"]], "num_cols (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.num_cols"]], "num_rows (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.num_rows"]], "process_command() (day18.day18a.matrix method)": [[43, "day18.day18a.Matrix.process_command"]], "process_command() (in module day18.day18b)": [[43, "day18.day18b.process_command"]], "row (day18.day18a.position attribute)": [[43, "day18.day18a.Position.row"]], "row (day18.day18b.position attribute)": [[43, "day18.day18b.Position.row"]], "steps (day18.day18a.command attribute)": [[43, "day18.day18a.Command.steps"]], "steps (day18.day18b.command attribute)": [[43, "day18.day18b.Command.steps"]], "wall_tiles (day18.day18a.matrix attribute)": [[43, "day18.day18a.Matrix.wall_tiles"]], "edgetile (class in day18.lib.tile)": [[44, "day18.lib.tile.EdgeTile"]], "holetile (class in day18.lib.tile)": [[44, "day18.lib.tile.HoleTile"]], "text_white (day18.lib.tile.edgetile attribute)": [[44, "day18.lib.tile.EdgeTile.TEXT_WHITE"]], "tile (class in day18.lib.tile)": [[44, "day18.lib.tile.Tile"]], "color (day18.lib.tile.edgetile attribute)": [[44, "day18.lib.tile.EdgeTile.color"]], "contents (day18.lib.tile.edgetile attribute)": [[44, "day18.lib.tile.EdgeTile.contents"]], "contents (day18.lib.tile.holetile attribute)": [[44, "day18.lib.tile.HoleTile.contents"]], "contents (day18.lib.tile.tile attribute)": [[44, "day18.lib.tile.Tile.contents"]], "day18.lib": [[44, "module-day18.lib"]], "day18.lib.tile": [[44, "module-day18.lib.tile"]], "text_color() (day18.lib.tile.edgetile method)": [[44, "day18.lib.tile.EdgeTile.text_color"]], "day18.tests": [[45, "module-day18.tests"]], "day18.tests.test_day18a": [[45, "module-day18.tests.test_day18a"]], "day18.tests.test_day18b": [[45, "module-day18.tests.test_day18b"]], "test_command() (in module day18.tests.test_day18b)": [[45, "day18.tests.test_day18b.test_command"]], "test_day18a() (in module day18.tests.test_day18a)": [[45, "day18.tests.test_day18a.test_day18a"]], "test_day18b() (in module day18.tests.test_day18b)": [[45, "day18.tests.test_day18b.test_day18b"]], "day19": [[46, "module-day19"]], "day19.day19": [[46, "module-day19.day19"]], "get_input() (in module day19.day19)": [[46, "day19.day19.get_input"]], "main() (in module day19.day19)": [[46, "day19.day19.main"]], "part1() (in module day19.day19)": [[46, "day19.day19.part1"]], "part2() (in module day19.day19)": [[46, "day19.day19.part2"]], "process_part() (in module day19.day19)": [[46, "day19.day19.process_part"]], "solve_part2() (in module day19.day19)": [[46, "day19.day19.solve_part2"]], "a (day19.lib.classes.component attribute)": [[47, "day19.lib.classes.Component.A"]], "comparator (class in day19.lib.classes)": [[47, "day19.lib.classes.Comparator"]], "component (class in day19.lib.classes)": [[47, "day19.lib.classes.Component"]], "condition (class in day19.lib.classes)": [[47, "day19.lib.classes.Condition"]], "greaterthan (day19.lib.classes.comparator attribute)": [[47, "day19.lib.classes.Comparator.GreaterThan"]], "lessthan (day19.lib.classes.comparator attribute)": [[47, "day19.lib.classes.Comparator.LessThan"]], "m (day19.lib.classes.component attribute)": [[47, "day19.lib.classes.Component.M"]], "part (class in day19.lib.classes)": [[47, "day19.lib.classes.Part"]], "partrange (class in day19.lib.classes)": [[47, "day19.lib.classes.PartRange"]], "partrangedest (class in day19.lib.classes)": [[47, "day19.lib.classes.PartRangeDest"]], "rule (class in day19.lib.classes)": [[47, "day19.lib.classes.Rule"]], "s (day19.lib.classes.component attribute)": [[47, "day19.lib.classes.Component.S"]], "workflow (class in day19.lib.classes)": [[47, "day19.lib.classes.Workflow"]], "x (day19.lib.classes.component attribute)": [[47, "day19.lib.classes.Component.X"]], "a (day19.lib.classes.part attribute)": [[47, "day19.lib.classes.Part.a"]], "clone_modify() (day19.lib.classes.part method)": [[47, "day19.lib.classes.Part.clone_modify"]], "component (day19.lib.classes.condition attribute)": [[47, "day19.lib.classes.Condition.component"]], "condition (day19.lib.classes.rule attribute)": [[47, "day19.lib.classes.Rule.condition"]], "day19.lib": [[47, "module-day19.lib"]], "day19.lib.classes": [[47, "module-day19.lib.classes"]], "day19.lib.parsers": [[47, "module-day19.lib.parsers"]], "destination (day19.lib.classes.partrangedest attribute)": [[47, "day19.lib.classes.PartRangeDest.destination"]], "destination (day19.lib.classes.rule attribute)": [[47, "day19.lib.classes.Rule.destination"]], "get_value() (day19.lib.classes.part method)": [[47, "day19.lib.classes.Part.get_value"]], "m (day19.lib.classes.part attribute)": [[47, "day19.lib.classes.Part.m"]], "max_values (day19.lib.classes.partrange attribute)": [[47, "day19.lib.classes.PartRange.max_values"]], "min_values (day19.lib.classes.partrange attribute)": [[47, "day19.lib.classes.PartRange.min_values"]], "name (day19.lib.classes.workflow attribute)": [[47, "day19.lib.classes.Workflow.name"]], "parse_condition_string() (in module day19.lib.parsers)": [[47, "day19.lib.parsers.parse_condition_string"]], "parse_part_string() (in module day19.lib.parsers)": [[47, "day19.lib.parsers.parse_part_string"]], "parse_rule_string() (in module day19.lib.parsers)": [[47, "day19.lib.parsers.parse_rule_string"]], "parse_workflow_string() (in module day19.lib.parsers)": [[47, "day19.lib.parsers.parse_workflow_string"]], "part_range (day19.lib.classes.partrangedest attribute)": [[47, "day19.lib.classes.PartRangeDest.part_range"]], "process_part() (day19.lib.classes.condition method)": [[47, "day19.lib.classes.Condition.process_part"]], "process_part() (day19.lib.classes.rule method)": [[47, "day19.lib.classes.Rule.process_part"]], "process_part() (day19.lib.classes.workflow method)": [[47, "day19.lib.classes.Workflow.process_part"]], "process_part_range() (day19.lib.classes.condition method)": [[47, "day19.lib.classes.Condition.process_part_range"]], "process_part_range() (day19.lib.classes.rule method)": [[47, "day19.lib.classes.Rule.process_part_range"]], "process_part_range() (day19.lib.classes.workflow method)": [[47, "day19.lib.classes.Workflow.process_part_range"]], "rating (day19.lib.classes.part property)": [[47, "day19.lib.classes.Part.rating"]], "rules (day19.lib.classes.workflow attribute)": [[47, "day19.lib.classes.Workflow.rules"]], "s (day19.lib.classes.part attribute)": [[47, "day19.lib.classes.Part.s"]], "sign (day19.lib.classes.condition attribute)": [[47, "day19.lib.classes.Condition.sign"]], "size() (day19.lib.classes.partrange method)": [[47, "day19.lib.classes.PartRange.size"]], "split() (day19.lib.classes.partrange method)": [[47, "day19.lib.classes.PartRange.split"]], "value (day19.lib.classes.condition attribute)": [[47, "day19.lib.classes.Condition.value"]], "x (day19.lib.classes.part attribute)": [[47, "day19.lib.classes.Part.x"]], "day19.tests": [[48, "module-day19.tests"]], "day19.tests.test_classes": [[48, "module-day19.tests.test_classes"]], "day19.tests.test_day19": [[48, "module-day19.tests.test_day19"]], "day19.tests.test_parsers": [[48, "module-day19.tests.test_parsers"]], "get_part_range() (in module day19.tests.test_classes)": [[48, "day19.tests.test_classes.get_part_range"]], "test_day19() (in module day19.tests.test_day19)": [[48, "day19.tests.test_day19.test_day19"]], "test_parse_condition_string() (in module day19.tests.test_parsers)": [[48, "day19.tests.test_parsers.test_parse_condition_string"]], "test_parse_part_string() (in module day19.tests.test_parsers)": [[48, "day19.tests.test_parsers.test_parse_part_string"]], "test_parse_rule_string() (in module day19.tests.test_parsers)": [[48, "day19.tests.test_parsers.test_parse_rule_string"]], "test_parse_workflow_string() (in module day19.tests.test_parsers)": [[48, "day19.tests.test_parsers.test_parse_workflow_string"]], "test_part_range() (in module day19.tests.test_classes)": [[48, "day19.tests.test_classes.test_part_range"]], "test_part_range_dest() (in module day19.tests.test_classes)": [[48, "day19.tests.test_classes.test_part_range_dest"]], "test_rule() (in module day19.tests.test_classes)": [[48, "day19.tests.test_classes.test_rule"]], "test_workflow() (in module day19.tests.test_classes)": [[48, "day19.tests.test_classes.test_workflow"]], "day20": [[49, "module-day20"]], "day20.day20": [[49, "module-day20.day20"]], "export_graph() (in module day20.day20)": [[49, "day20.day20.export_graph"]], "get_loop_paths() (in module day20.day20)": [[49, "day20.day20.get_loop_paths"]], "get_module_groups() (in module day20.day20)": [[49, "day20.day20.get_module_groups"]], "get_typed_module() (in module day20.day20)": [[49, "day20.day20.get_typed_module"]], "graph_modules() (in module day20.day20)": [[49, "day20.day20.graph_modules"]], "main() (in module day20.day20)": [[49, "day20.day20.main"]], "output_files() (in module day20.day20)": [[49, "day20.day20.output_files"]], "output_graph() (in module day20.day20)": [[49, "day20.day20.output_graph"]], "output_graph_wrapper() (in module day20.day20)": [[49, "day20.day20.output_graph_wrapper"]], "part1() (in module day20.day20)": [[49, "day20.day20.part1"]], "part2() (in module day20.day20)": [[49, "day20.day20.part2"]], "path_is_start_state() (in module day20.day20)": [[49, "day20.day20.path_is_start_state"]], "simulate() (in module day20.day20)": [[49, "day20.day20.simulate"]], "basemodule (class in day20.lib.classes)": [[50, "day20.lib.classes.BaseModule"]], "broadcastmodule (class in day20.lib.classes)": [[50, "day20.lib.classes.BroadcastModule"]], "conjunctionmodule (class in day20.lib.classes)": [[50, "day20.lib.classes.ConjunctionModule"]], "flipflopmodule (class in day20.lib.classes)": [[50, "day20.lib.classes.FlipFlopModule"]], "high (day20.lib.classes.pulse attribute)": [[50, "day20.lib.classes.Pulse.HIGH"]], "low (day20.lib.classes.pulse attribute)": [[50, "day20.lib.classes.Pulse.LOW"]], "loopcounter (class in day20.lib.classes)": [[50, "day20.lib.classes.LoopCounter"]], "mappingmodule (class in day20.lib.classes)": [[50, "day20.lib.classes.MappingModule"]], "modulegroups (class in day20.lib.classes)": [[50, "day20.lib.classes.ModuleGroups"]], "pulse (class in day20.lib.classes)": [[50, "day20.lib.classes.Pulse"]], "pulsetarget (class in day20.lib.classes)": [[50, "day20.lib.classes.PulseTarget"]], "sinkmodule (class in day20.lib.classes)": [[50, "day20.lib.classes.SinkModule"]], "add_result() (day20.lib.classes.loopcounter method)": [[50, "day20.lib.classes.LoopCounter.add_result"]], "add_to_graph() (day20.lib.classes.basemodule method)": [[50, "day20.lib.classes.BaseModule.add_to_graph"]], "add_to_graph() (day20.lib.classes.broadcastmodule method)": [[50, "day20.lib.classes.BroadcastModule.add_to_graph"]], "add_to_graph() (day20.lib.classes.conjunctionmodule method)": [[50, "day20.lib.classes.ConjunctionModule.add_to_graph"]], "add_to_graph() (day20.lib.classes.flipflopmodule method)": [[50, "day20.lib.classes.FlipFlopModule.add_to_graph"]], "add_to_graph() (day20.lib.classes.sinkmodule method)": [[50, "day20.lib.classes.SinkModule.add_to_graph"]], "all_nodes (day20.lib.classes.modulegroups attribute)": [[50, "day20.lib.classes.ModuleGroups.all_nodes"]], "arrow_color() (day20.lib.classes.basemodule method)": [[50, "day20.lib.classes.BaseModule.arrow_color"]], "arrow_color() (day20.lib.classes.conjunctionmodule method)": [[50, "day20.lib.classes.ConjunctionModule.arrow_color"]], "current_count() (day20.lib.classes.conjunctionmodule method)": [[50, "day20.lib.classes.ConjunctionModule.current_count"]], "day20.lib": [[50, "module-day20.lib"]], "day20.lib.classes": [[50, "module-day20.lib.classes"]], "day20.lib.parsers": [[50, "module-day20.lib.parsers"]], "finalize_modules() (in module day20.lib.parsers)": [[50, "day20.lib.parsers.finalize_modules"]], "finished (day20.lib.classes.loopcounter property)": [[50, "day20.lib.classes.LoopCounter.finished"]], "get_modules() (in module day20.lib.parsers)": [[50, "day20.lib.parsers.get_modules"]], "handle_pulse() (day20.lib.classes.basemodule method)": [[50, "day20.lib.classes.BaseModule.handle_pulse"]], "handle_pulse() (day20.lib.classes.broadcastmodule method)": [[50, "day20.lib.classes.BroadcastModule.handle_pulse"]], "handle_pulse() (day20.lib.classes.conjunctionmodule method)": [[50, "day20.lib.classes.ConjunctionModule.handle_pulse"]], "handle_pulse() (day20.lib.classes.flipflopmodule method)": [[50, "day20.lib.classes.FlipFlopModule.handle_pulse"]], "handle_pulse() (day20.lib.classes.sinkmodule method)": [[50, "day20.lib.classes.SinkModule.handle_pulse"]], "head (day20.lib.classes.modulegroups attribute)": [[50, "day20.lib.classes.ModuleGroups.head"]], "inputs (day20.lib.classes.conjunctionmodule attribute)": [[50, "day20.lib.classes.ConjunctionModule.inputs"]], "is_initial_state() (day20.lib.classes.basemodule method)": [[50, "day20.lib.classes.BaseModule.is_initial_state"]], "is_initial_state() (day20.lib.classes.broadcastmodule method)": [[50, "day20.lib.classes.BroadcastModule.is_initial_state"]], "is_initial_state() (day20.lib.classes.conjunctionmodule method)": [[50, "day20.lib.classes.ConjunctionModule.is_initial_state"]], "is_initial_state() (day20.lib.classes.flipflopmodule method)": [[50, "day20.lib.classes.FlipFlopModule.is_initial_state"]], "is_initial_state() (day20.lib.classes.sinkmodule method)": [[50, "day20.lib.classes.SinkModule.is_initial_state"]], "loop_lengths (day20.lib.classes.loopcounter attribute)": [[50, "day20.lib.classes.LoopCounter.loop_lengths"]], "loop_tails (day20.lib.classes.modulegroups attribute)": [[50, "day20.lib.classes.ModuleGroups.loop_tails"]], "loops (day20.lib.classes.modulegroups attribute)": [[50, "day20.lib.classes.ModuleGroups.loops"]], "name (day20.lib.classes.basemodule attribute)": [[50, "day20.lib.classes.BaseModule.name"]], "name (day20.lib.classes.mappingmodule attribute)": [[50, "day20.lib.classes.MappingModule.name"]], "num_high (day20.lib.classes.basemodule attribute)": [[50, "day20.lib.classes.BaseModule.num_high"]], "num_low (day20.lib.classes.basemodule attribute)": [[50, "day20.lib.classes.BaseModule.num_low"]], "num_results (day20.lib.classes.loopcounter property)": [[50, "day20.lib.classes.LoopCounter.num_results"]], "outputs (day20.lib.classes.basemodule attribute)": [[50, "day20.lib.classes.BaseModule.outputs"]], "outputs (day20.lib.classes.mappingmodule attribute)": [[50, "day20.lib.classes.MappingModule.outputs"]], "parse_line() (in module day20.lib.parsers)": [[50, "day20.lib.parsers.parse_line"]], "penultimate (day20.lib.classes.modulegroups attribute)": [[50, "day20.lib.classes.ModuleGroups.penultimate"]], "pulse (day20.lib.classes.pulsetarget attribute)": [[50, "day20.lib.classes.PulseTarget.pulse"]], "set_inputs() (day20.lib.classes.conjunctionmodule method)": [[50, "day20.lib.classes.ConjunctionModule.set_inputs"]], "sink (day20.lib.classes.modulegroups attribute)": [[50, "day20.lib.classes.ModuleGroups.sink"]], "src (day20.lib.classes.pulsetarget attribute)": [[50, "day20.lib.classes.PulseTarget.src"]], "state (day20.lib.classes.flipflopmodule attribute)": [[50, "day20.lib.classes.FlipFlopModule.state"]], "target (day20.lib.classes.pulsetarget attribute)": [[50, "day20.lib.classes.PulseTarget.target"]], "target_loop_count (day20.lib.classes.loopcounter attribute)": [[50, "day20.lib.classes.LoopCounter.target_loop_count"]], "day20.tests": [[51, "module-day20.tests"]], "day20.tests.test_classes": [[51, "module-day20.tests.test_classes"]], "day20.tests.test_day20": [[51, "module-day20.tests.test_day20"]], "day20.tests.test_parsers": [[51, "module-day20.tests.test_parsers"]], "test_day20() (in module day20.tests.test_day20)": [[51, "day20.tests.test_day20.test_day20"]], "test_finalize_modules() (in module day20.tests.test_parsers)": [[51, "day20.tests.test_parsers.test_finalize_modules"]], "test_get_modules() (in module day20.tests.test_parsers)": [[51, "day20.tests.test_parsers.test_get_modules"]], "test_loop_counter() (in module day20.tests.test_classes)": [[51, "day20.tests.test_classes.test_loop_counter"]], "test_modules() (in module day20.tests.test_classes)": [[51, "day20.tests.test_classes.test_modules"]], "test_parse_line() (in module day20.tests.test_parsers)": [[51, "day20.tests.test_parsers.test_parse_line"]], "test_part2() (in module day20.tests.test_day20)": [[51, "day20.tests.test_day20.test_part2"]], "smartsteps (class in day21.day21)": [[52, "day21.day21.SmartSteps"]], "boards_to_edge (day21.day21.smartsteps attribute)": [[52, "day21.day21.SmartSteps.boards_to_edge"]], "calculate_smart_steps() (in module day21.day21)": [[52, "day21.day21.calculate_smart_steps"]], "day21": [[52, "module-day21"]], "day21.day21": [[52, "module-day21.day21"]], "main() (in module day21.day21)": [[52, "day21.day21.main"]], "mini_solve() (in module day21.day21)": [[52, "day21.day21.mini_solve"]], "naive_solve() (in module day21.day21)": [[52, "day21.day21.naive_solve"]], "solve() (in module day21.day21)": [[52, "day21.day21.solve"]], "steps (day21.day21.smartsteps attribute)": [[52, "day21.day21.SmartSteps.steps"]], "basedistancemaze (class in day21.lib.classes)": [[53, "day21.lib.classes.BaseDistanceMaze"]], "distancemaze (class in day21.lib.classes)": [[53, "day21.lib.classes.DistanceMaze"]], "distancemazes (class in day21.lib.classes)": [[53, "day21.lib.classes.DistanceMazes"]], "east_tip (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.EAST_TIP"]], "full_even (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.FULL_EVEN"]], "full_odd (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.FULL_ODD"]], "giantnodeparser (class in day21.lib.classes)": [[53, "day21.lib.classes.GiantNodeParser"]], "giantnodetype (class in day21.lib.classes)": [[53, "day21.lib.classes.GiantNodeType"]], "maze (class in day21.lib.classes)": [[53, "day21.lib.classes.Maze"]], "north_east_big (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.NORTH_EAST_BIG"]], "north_east_small (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.NORTH_EAST_SMALL"]], "north_tip (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.NORTH_TIP"]], "north_west_big (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.NORTH_WEST_BIG"]], "north_west_small (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.NORTH_WEST_SMALL"]], "position (class in day21.lib.classes)": [[53, "day21.lib.classes.Position"]], "positiondist (class in day21.lib.classes)": [[53, "day21.lib.classes.PositionDist"]], "south_east_big (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.SOUTH_EAST_BIG"]], "south_east_small (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.SOUTH_EAST_SMALL"]], "south_tip (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.SOUTH_TIP"]], "south_west_big (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.SOUTH_WEST_BIG"]], "south_west_small (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.SOUTH_WEST_SMALL"]], "west_tip (day21.lib.classes.giantnodetype attribute)": [[53, "day21.lib.classes.GiantNodeType.WEST_TIP"]], "calc_steps() (day21.lib.classes.basedistancemaze method)": [[53, "day21.lib.classes.BaseDistanceMaze.calc_steps"]], "calc_steps() (day21.lib.classes.distancemaze method)": [[53, "day21.lib.classes.DistanceMaze.calc_steps"]], "calc_steps() (day21.lib.classes.distancemazes method)": [[53, "day21.lib.classes.DistanceMazes.calc_steps"]], "centre_cell() (day21.lib.classes.distancemaze method)": [[53, "day21.lib.classes.DistanceMaze.centre_cell"]], "col (day21.lib.classes.position attribute)": [[53, "day21.lib.classes.Position.col"]], "cols_per_maze (day21.lib.classes.distancemazes attribute)": [[53, "day21.lib.classes.DistanceMazes.cols_per_maze"]], "day21.lib": [[53, "module-day21.lib"]], "day21.lib.classes": [[53, "module-day21.lib.classes"]], "day21.lib.parsers": [[53, "module-day21.lib.parsers"]], "distance (day21.lib.classes.positiondist attribute)": [[53, "day21.lib.classes.PositionDist.distance"]], "distance_mazes (day21.lib.classes.giantnodeparser attribute)": [[53, "day21.lib.classes.GiantNodeParser.distance_mazes"]], "edge_dist (day21.lib.classes.giantnodeparser attribute)": [[53, "day21.lib.classes.GiantNodeParser.edge_dist"]], "full_edge_dist (day21.lib.classes.giantnodeparser attribute)": [[53, "day21.lib.classes.GiantNodeParser.full_edge_dist"]], "get_big_grid() (day21.lib.classes.distancemazes method)": [[53, "day21.lib.classes.DistanceMazes.get_big_grid"]], "get_node() (day21.lib.classes.giantnodeparser method)": [[53, "day21.lib.classes.GiantNodeParser.get_node"]], "get_node_count() (day21.lib.classes.giantnodeparser method)": [[53, "day21.lib.classes.GiantNodeParser.get_node_count"]], "get_split_pos() (day21.lib.classes.distancemazes method)": [[53, "day21.lib.classes.DistanceMazes.get_split_pos"]], "grid (day21.lib.classes.distancemaze attribute)": [[53, "day21.lib.classes.DistanceMaze.grid"]], "grid (day21.lib.classes.distancemazes attribute)": [[53, "day21.lib.classes.DistanceMazes.grid"]], "grid (day21.lib.classes.maze attribute)": [[53, "day21.lib.classes.Maze.grid"]], "int_to_str() (day21.lib.classes.distancemaze method)": [[53, "day21.lib.classes.DistanceMaze.int_to_str"]], "is_complete() (day21.lib.classes.distancemaze method)": [[53, "day21.lib.classes.DistanceMaze.is_complete"]], "is_oob() (day21.lib.classes.distancemaze method)": [[53, "day21.lib.classes.DistanceMaze.is_oob"]], "num_cols (day21.lib.classes.distancemaze attribute)": [[53, "day21.lib.classes.DistanceMaze.num_cols"]], "num_cols (day21.lib.classes.maze attribute)": [[53, "day21.lib.classes.Maze.num_cols"]], "num_rows (day21.lib.classes.distancemaze attribute)": [[53, "day21.lib.classes.DistanceMaze.num_rows"]], "num_rows (day21.lib.classes.maze attribute)": [[53, "day21.lib.classes.Maze.num_rows"]], "overlay() (day21.lib.classes.basedistancemaze method)": [[53, "day21.lib.classes.BaseDistanceMaze.overlay"]], "overlay() (day21.lib.classes.distancemaze method)": [[53, "day21.lib.classes.DistanceMaze.overlay"]], "overlay() (day21.lib.classes.distancemazes method)": [[53, "day21.lib.classes.DistanceMazes.overlay"]], "parse_maze() (in module day21.lib.parsers)": [[53, "day21.lib.parsers.parse_maze"]], "replace() (day21.lib.classes.positiondist method)": [[53, "day21.lib.classes.PositionDist.replace"]], "row (day21.lib.classes.position attribute)": [[53, "day21.lib.classes.Position.row"]], "rows_per_maze (day21.lib.classes.distancemazes attribute)": [[53, "day21.lib.classes.DistanceMazes.rows_per_maze"]], "visualization (class in day22.day22)": [[54, "day22.day22.Visualization"]], "animate_part1() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.animate_part1"]], "animate_part2() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.animate_part2"]], "boxes (day22.day22.visualization attribute)": [[54, "day22.day22.Visualization.boxes"]], "calculate_part1() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.calculate_part1"]], "calculate_part2() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.calculate_part2"]], "day22": [[54, "module-day22"]], "day22.day22": [[54, "module-day22.day22"]], "follow_block() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.follow_block"]], "has_started (day22.day22.visualization attribute)": [[54, "day22.day22.Visualization.has_started"]], "main() (in module day22.day22)": [[54, "day22.day22.main"]], "matrix (day22.day22.visualization attribute)": [[54, "day22.day22.Visualization.matrix"]], "start() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.start"]], "vis_rate() (day22.day22.visualization method)": [[54, "day22.day22.Visualization.vis_rate"]], "boxdata (class in day22.lib.classes)": [[55, "day22.lib.classes.BoxData"]], "matrix (class in day22.lib.classes)": [[55, "day22.lib.classes.Matrix"]], "vector3 (class in day22.lib.classes)": [[55, "day22.lib.classes.Vector3"]], "animate_part1() (in module day22.lib.vis)": [[55, "day22.lib.vis.animate_part1"]], "animate_part2() (in module day22.lib.vis)": [[55, "day22.lib.vis.animate_part2"]], "bind_keys() (in module day22.lib.vis)": [[55, "day22.lib.vis.bind_keys"]], "can_fall_down() (day22.lib.classes.matrix method)": [[55, "day22.lib.classes.Matrix.can_fall_down"]], "can_fly_up() (day22.lib.classes.matrix method)": [[55, "day22.lib.classes.Matrix.can_fly_up"]], "construct_box() (in module day22.lib.vis)": [[55, "day22.lib.vis.construct_box"]], "day22.lib": [[55, "module-day22.lib"]], "day22.lib.classes": [[55, "module-day22.lib.classes"]], "day22.lib.parsers": [[55, "module-day22.lib.parsers"]], "day22.lib.vis": [[55, "module-day22.lib.vis"]], "end_pos (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.end_pos"]], "fall() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.fall"]], "follow_block() (in module day22.lib.vis)": [[55, "day22.lib.vis.follow_block"]], "get_boxes() (in module day22.lib.parsers)": [[55, "day22.lib.parsers.get_boxes"]], "get_hats() (day22.lib.classes.matrix method)": [[55, "day22.lib.classes.Matrix.get_hats"]], "get_supports() (day22.lib.classes.matrix method)": [[55, "day22.lib.classes.Matrix.get_supports"]], "hats (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.hats"]], "height (day22.lib.classes.boxdata property)": [[55, "day22.lib.classes.BoxData.height"]], "init_vis() (in module day22.lib.vis)": [[55, "day22.lib.vis.init_vis"]], "layers (day22.lib.classes.matrix attribute)": [[55, "day22.lib.classes.Matrix.layers"]], "length (day22.lib.classes.boxdata property)": [[55, "day22.lib.classes.BoxData.length"]], "name (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.name"]], "parse_vector() (in module day22.lib.parsers)": [[55, "day22.lib.parsers.parse_vector"]], "random_color() (in module day22.lib.vis)": [[55, "day22.lib.vis.random_color"]], "recursive_fall() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.recursive_fall"]], "register_box() (day22.lib.classes.matrix method)": [[55, "day22.lib.classes.Matrix.register_box"]], "select() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.select"]], "set_hats() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.set_hats"]], "set_supports() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.set_supports"]], "set_vbox() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.set_vbox"]], "start_pos (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.start_pos"]], "supports (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.supports"]], "total_hats (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.total_hats"]], "unselect() (day22.lib.classes.boxdata method)": [[55, "day22.lib.classes.BoxData.unselect"]], "vbox (day22.lib.classes.boxdata attribute)": [[55, "day22.lib.classes.BoxData.vbox"]], "vpos (day22.lib.classes.boxdata property)": [[55, "day22.lib.classes.BoxData.vpos"]], "width (day22.lib.classes.boxdata property)": [[55, "day22.lib.classes.BoxData.width"]], "x (day22.lib.classes.vector3 attribute)": [[55, "day22.lib.classes.Vector3.x"]], "y (day22.lib.classes.vector3 attribute)": [[55, "day22.lib.classes.Vector3.y"]], "z (day22.lib.classes.vector3 attribute)": [[55, "day22.lib.classes.Vector3.z"]], "z_val_bot (day22.lib.classes.boxdata property)": [[55, "day22.lib.classes.BoxData.z_val_bot"]], "z_val_top (day22.lib.classes.boxdata property)": [[55, "day22.lib.classes.BoxData.z_val_top"]], "day22.tests": [[56, "module-day22.tests"]], "day22.tests.test_classes": [[56, "module-day22.tests.test_classes"]], "day22.tests.test_day22": [[56, "module-day22.tests.test_day22"]], "day22.tests.test_parsers": [[56, "module-day22.tests.test_parsers"]], "test_box_data() (in module day22.tests.test_classes)": [[56, "day22.tests.test_classes.test_box_data"]], "test_parser() (in module day22.tests.test_parsers)": [[56, "day22.tests.test_parsers.test_parser"]], "test_visualization() (in module day22.tests.test_day22)": [[56, "day22.tests.test_day22.test_visualization"]], "day23": [[57, "module-day23"]], "day23.day23": [[57, "module-day23.day23"]], "main() (in module day23.day23)": [[57, "day23.day23.main"]], "part1() (in module day23.day23)": [[57, "day23.day23.part1"]], "part2() (in module day23.day23)": [[57, "day23.day23.part2"]], "edge (class in day23.lib.classes2)": [[58, "day23.lib.classes2.Edge"]], "maze (class in day23.lib.classes)": [[58, "day23.lib.classes.Maze"]], "node (class in day23.lib.classes2)": [[58, "day23.lib.classes2.Node"]], "path (class in day23.lib.classes)": [[58, "day23.lib.classes.Path"]], "position (class in day23.lib.classes)": [[58, "day23.lib.classes.Position"]], "solver1 (class in day23.lib.classes)": [[58, "day23.lib.classes.Solver1"]], "solver2 (class in day23.lib.classes2)": [[58, "day23.lib.classes2.Solver2"]], "add() (day23.lib.classes.path method)": [[58, "day23.lib.classes.Path.add"]], "build_nodes() (day23.lib.classes2.solver2 method)": [[58, "day23.lib.classes2.Solver2.build_nodes"]], "calculate_edges() (day23.lib.classes2.solver2 static method)": [[58, "day23.lib.classes2.Solver2.calculate_edges"]], "can_add() (day23.lib.classes.path method)": [[58, "day23.lib.classes.Path.can_add"]], "col (day23.lib.classes.position attribute)": [[58, "day23.lib.classes.Position.col"]], "copy() (day23.lib.classes.maze method)": [[58, "day23.lib.classes.Maze.copy"]], "copy() (day23.lib.classes.path method)": [[58, "day23.lib.classes.Path.copy"]], "copy_modify() (day23.lib.classes.position method)": [[58, "day23.lib.classes.Position.copy_modify"]], "day23.lib": [[58, "module-day23.lib"]], "day23.lib.classes": [[58, "module-day23.lib.classes"]], "day23.lib.classes2": [[58, "module-day23.lib.classes2"]], "day23.lib.parsers": [[58, "module-day23.lib.parsers"]], "edges (day23.lib.classes2.node attribute)": [[58, "day23.lib.classes2.Node.edges"]], "expand() (day23.lib.classes.position method)": [[58, "day23.lib.classes.Position.expand"]], "expand_hill() (day23.lib.classes.solver1 method)": [[58, "day23.lib.classes.Solver1.expand_hill"]], "expand_path() (day23.lib.classes.solver1 method)": [[58, "day23.lib.classes.Solver1.expand_path"]], "expand_path() (day23.lib.classes2.solver2 static method)": [[58, "day23.lib.classes2.Solver2.expand_path"]], "flip() (day23.lib.classes.path method)": [[58, "day23.lib.classes.Path.flip"]], "flip() (day23.lib.classes2.edge method)": [[58, "day23.lib.classes2.Edge.flip"]], "generate_paths() (in module day23.lib.classes)": [[58, "day23.lib.classes.generate_paths"]], "get_cell_branches() (day23.lib.classes.maze method)": [[58, "day23.lib.classes.Maze.get_cell_branches"]], "get_maze() (in module day23.lib.parsers)": [[58, "day23.lib.parsers.get_maze"]], "get_nodes() (day23.lib.classes2.solver2 static method)": [[58, "day23.lib.classes2.Solver2.get_nodes"]], "grid (day23.lib.classes.maze attribute)": [[58, "day23.lib.classes.Maze.grid"]], "handle_hills (day23.lib.classes.solver1 attribute)": [[58, "day23.lib.classes.Solver1.handle_hills"]], "input_maze (day23.lib.classes2.solver2 attribute)": [[58, "day23.lib.classes2.Solver2.input_maze"]], "is_oob() (day23.lib.classes.maze method)": [[58, "day23.lib.classes.Maze.is_oob"]], "last() (day23.lib.classes.path method)": [[58, "day23.lib.classes.Path.last"]], "length (day23.lib.classes2.edge attribute)": [[58, "day23.lib.classes2.Edge.length"]], "maze (day23.lib.classes.solver1 attribute)": [[58, "day23.lib.classes.Solver1.maze"]], "name (day23.lib.classes2.node attribute)": [[58, "day23.lib.classes2.Node.name"]], "node1 (day23.lib.classes2.edge attribute)": [[58, "day23.lib.classes2.Edge.node1"]], "node2 (day23.lib.classes2.edge attribute)": [[58, "day23.lib.classes2.Edge.node2"]], "nodes (day23.lib.classes.path attribute)": [[58, "day23.lib.classes.Path.nodes"]], "num_cols (day23.lib.classes.maze attribute)": [[58, "day23.lib.classes.Maze.num_cols"]], "num_rows (day23.lib.classes.maze attribute)": [[58, "day23.lib.classes.Maze.num_rows"]], "overlay() (day23.lib.classes.path method)": [[58, "day23.lib.classes.Path.overlay"]], "path (day23.lib.classes2.edge attribute)": [[58, "day23.lib.classes2.Edge.path"]], "position (day23.lib.classes2.node attribute)": [[58, "day23.lib.classes2.Node.position"]], "route (day23.lib.classes.path attribute)": [[58, "day23.lib.classes.Path.route"]], "row (day23.lib.classes.position attribute)": [[58, "day23.lib.classes.Position.row"]], "solve() (day23.lib.classes.solver1 method)": [[58, "day23.lib.classes.Solver1.solve"]], "solve() (day23.lib.classes2.solver2 method)": [[58, "day23.lib.classes2.Solver2.solve"]], "solve2() (in module day23.lib.classes2)": [[58, "day23.lib.classes2.solve2"]], "solve2_helper() (in module day23.lib.classes2)": [[58, "day23.lib.classes2.solve2_helper"]], "day23.tests": [[59, "module-day23.tests"]], "day23.tests.test_classes": [[59, "module-day23.tests.test_classes"]], "day23.tests.test_classes2": [[59, "module-day23.tests.test_classes2"]], "day23.tests.test_day23": [[59, "module-day23.tests.test_day23"]], "day23.tests.test_parsers": [[59, "module-day23.tests.test_parsers"]], "test_generate_paths() (in module day23.tests.test_classes)": [[59, "day23.tests.test_classes.test_generate_paths"]], "test_get_maze() (in module day23.tests.test_parsers)": [[59, "day23.tests.test_parsers.test_get_maze"]], "test_maze() (in module day23.tests.test_classes)": [[59, "day23.tests.test_classes.test_maze"]], "test_part1() (in module day23.tests.test_day23)": [[59, "day23.tests.test_day23.test_part1"]], "test_part2() (in module day23.tests.test_day23)": [[59, "day23.tests.test_day23.test_part2"]], "test_path() (in module day23.tests.test_classes)": [[59, "day23.tests.test_classes.test_path"]], "test_position() (in module day23.tests.test_classes)": [[59, "day23.tests.test_classes.test_position"]], "test_solver() (in module day23.tests.test_day23)": [[59, "day23.tests.test_day23.test_solver"]], "test_solver1() (in module day23.tests.test_classes)": [[59, "day23.tests.test_classes.test_solver1"]], "test_solver2() (in module day23.tests.test_classes2)": [[59, "day23.tests.test_classes2.test_solver2"]], "day24": [[60, "module-day24"]], "day24.day24": [[60, "module-day24.day24"]], "get_intersection_2d() (in module day24.day24)": [[60, "day24.day24.get_intersection_2d"]], "main() (in module day24.day24)": [[60, "day24.day24.main"]], "part1() (in module day24.day24)": [[60, "day24.day24.part1"]], "part2() (in module day24.day24)": [[60, "day24.day24.part2"]], "within_2d() (in module day24.day24)": [[60, "day24.day24.within_2d"]], "hailstone (class in day24.lib.classes)": [[61, "day24.lib.classes.Hailstone"]], "vector2 (class in day24.lib.classes)": [[61, "day24.lib.classes.Vector2"]], "vector3 (class in day24.lib.classes)": [[61, "day24.lib.classes.Vector3"]], "day24.lib": [[61, "module-day24.lib"]], "day24.lib.classes": [[61, "module-day24.lib.classes"]], "day24.lib.parsers": [[61, "module-day24.lib.parsers"]], "parse_input() (in module day24.lib.parsers)": [[61, "day24.lib.parsers.parse_input"]], "parse_vector3() (in module day24.lib.parsers)": [[61, "day24.lib.parsers.parse_vector3"]], "position (day24.lib.classes.hailstone attribute)": [[61, "day24.lib.classes.Hailstone.position"]], "velocity (day24.lib.classes.hailstone attribute)": [[61, "day24.lib.classes.Hailstone.velocity"]], "x (day24.lib.classes.vector2 attribute)": [[61, "day24.lib.classes.Vector2.x"]], "x (day24.lib.classes.vector3 attribute)": [[61, "day24.lib.classes.Vector3.x"]], "xy (day24.lib.classes.vector3 property)": [[61, "day24.lib.classes.Vector3.xy"]], "y (day24.lib.classes.vector2 attribute)": [[61, "day24.lib.classes.Vector2.y"]], "y (day24.lib.classes.vector3 attribute)": [[61, "day24.lib.classes.Vector3.y"]], "z (day24.lib.classes.vector3 attribute)": [[61, "day24.lib.classes.Vector3.z"]], "day24.tests": [[62, "module-day24.tests"]], "day24.tests.test_day24": [[62, "module-day24.tests.test_day24"]], "day24.tests.test_parsers": [[62, "module-day24.tests.test_parsers"]], "test_get_intersection_2d() (in module day24.tests.test_day24)": [[62, "day24.tests.test_day24.test_get_intersection_2d"]], "test_parser() (in module day24.tests.test_parsers)": [[62, "day24.tests.test_parsers.test_parser"]], "test_part1() (in module day24.tests.test_day24)": [[62, "day24.tests.test_day24.test_part1"]], "test_part2() (in module day24.tests.test_day24)": [[62, "day24.tests.test_day24.test_part2"]], "test_vector3() (in module day24.tests.test_parsers)": [[62, "day24.tests.test_parsers.test_vector3"]], "test_within_2d() (in module day24.tests.test_day24)": [[62, "day24.tests.test_day24.test_within_2d"]], "connection (class in day25.day25)": [[63, "day25.day25.Connection"]], "day25": [[63, "module-day25"]], "day25.day25": [[63, "module-day25.day25"]], "dests (day25.day25.connection attribute)": [[63, "day25.day25.Connection.dests"]], "get_data() (in module day25.day25)": [[63, "day25.day25.get_data"]], "main() (in module day25.day25)": [[63, "day25.day25.main"]], "node_names() (day25.day25.connection method)": [[63, "day25.day25.Connection.node_names"]], "parse_connection() (in module day25.day25)": [[63, "day25.day25.parse_connection"]], "show_graph() (in module day25.day25)": [[63, "day25.day25.show_graph"]], "solve_nodes() (in module day25.day25)": [[63, "day25.day25.solve_nodes"]], "src (day25.day25.connection attribute)": [[63, "day25.day25.Connection.src"]], "day25.tests": [[64, "module-day25.tests"]], "day25.tests.test_day25": [[64, "module-day25.tests.test_day25"]], "test_day25() (in module day25.tests.test_day25)": [[64, "day25.tests.test_day25.test_day25"]], "test_get_data() (in module day25.tests.test_day25)": [[64, "day25.tests.test_day25.test_get_data"]], "test_parse_connection() (in module day25.tests.test_day25)": [[64, "day25.tests.test_day25.test_parse_connection"]], "download_file() (in module download_inputs)": [[65, "download_inputs.download_file"]], "download_inputs": [[65, "module-download_inputs"]], "main() (in module download_inputs)": [[65, "download_inputs.main"]], "maker": [[67, "module-maker"]], "touch_days() (in module maker)": [[67, "maker.touch_days"]]}}) \ No newline at end of file