diff --git a/2023/python/Day07.py b/2023/python/Day07.py
new file mode 100644
index 0000000000000000000000000000000000000000..649cf54e087cdcfc8153918b9f0d6053958d8a7f
--- /dev/null
+++ b/2023/python/Day07.py
@@ -0,0 +1,167 @@
+from typing import List, Optional, Tuple
+
+from ImportData import import_data
+
+day: int = 7
+
+sample_data: List[str] = '''
+32T3K 765
+T55J5 684
+KK677 28
+KTJJT 220
+QQQJA 483
+'''.split('\n')[1:-1]
+
+
+class Hand:
+    part: int = 1
+
+    def __init__(self, cards: str, bid: str):
+        self.bid: int = int(bid)
+        self.cards_string = cards
+        self._strength_of_hand = None
+        self.original_cards: List[int] = []
+        self.sorted_cards = []
+        self.reset_hand()
+
+    @staticmethod
+    def face_to_int(card: str) -> int:
+        number: int
+        try:
+            number = int(card)
+        except ValueError:  # it's pythonic but it stinks
+            if card == 'T':
+                number = 10
+            elif card == 'J':
+                number = 11 if Hand.part == 1 else 1
+            elif card == 'Q':
+                number = 12
+            elif card == 'K':
+                number = 13
+            elif card == 'A':
+                number = 14
+            else:
+                number = 0
+        return number
+
+    @property
+    def strength_of_hand(self) -> int:
+        """
+        6 = five of a kind
+        5 = four of a kind
+        4 = full house
+        3 = three of a kind
+        2 = two pair
+        1 = one pair
+        0 = high card
+        :return: strength of the hand
+        """
+        if self._strength_of_hand is not None:
+            return self._strength_of_hand
+        number_of_pairs: int = 0
+        number_of_triples: int = 0
+        number_of_quartets: int = 0
+        number_of_quintets: int = 0
+        number_of_wildcards: int = 0
+        i: int = 0
+        while i < len(self.original_cards) - 1:
+            card: int = self.sorted_cards[i]
+            card_count: int = self.sorted_cards[i:].count(card)
+            if card == 1:
+                number_of_wildcards = card_count
+            elif card_count == 2:
+                number_of_pairs += 1
+            elif card_count == 3:
+                number_of_triples += 1
+            elif card_count == 4:
+                number_of_quartets += 1
+            elif card_count == 5:
+                number_of_quintets += 1
+            i += card_count
+        # yahtzee
+        if (number_of_quintets == 1 or
+                (number_of_quartets == 1 and number_of_wildcards == 1) or
+                (number_of_triples == 1 and number_of_wildcards == 2) or
+                (number_of_pairs == 1 and number_of_wildcards == 3) or
+                number_of_wildcards == 4):
+            self._strength_of_hand = 6
+        # four of a kind
+        elif (number_of_quartets == 1 or
+              (number_of_triples == 1 and number_of_wildcards == 1) or
+              (number_of_pairs == 1 and number_of_wildcards == 2) or
+              number_of_wildcards == 3):
+            self._strength_of_hand = 5
+        # full house, or three of a kind
+        elif number_of_triples == 1:
+            self._strength_of_hand = 4 if number_of_pairs == 1 else 3
+        # full house, or two pair
+        elif number_of_pairs == 2:
+            self._strength_of_hand = 4 if number_of_wildcards == 1 else 2
+        # three of a kind
+        elif ((number_of_pairs == 1 and number_of_wildcards == 1) or
+              number_of_wildcards == 2):
+            self._strength_of_hand = 3
+        # one pair
+        elif (number_of_pairs == 1
+              or number_of_wildcards == 1):
+            self._strength_of_hand = 1
+        else:
+            self._strength_of_hand = 0
+        return self._strength_of_hand
+
+    def is_stronger_than(self, other: "Hand") -> bool:
+        if self.strength_of_hand > other._strength_of_hand:
+            return True
+        if other._strength_of_hand > self.strength_of_hand:
+            return False
+        is_stronger: bool = False
+        i: int = 0
+        while i < len(self.original_cards) and not is_stronger:
+            if self.original_cards[i] > other.original_cards[i]:
+                is_stronger = True
+        return is_stronger
+
+    def get_sortable_key(self) -> Tuple[int, ...]:
+        keys: List[int] = self.original_cards.copy()
+        keys.insert(0, self.strength_of_hand)
+        return_value = tuple(keys)
+        return return_value
+
+    def reset_hand(self):
+        self._strength_of_hand = None
+        self.original_cards: List[int] = [Hand.face_to_int(card) for card in self.cards_string]
+        self.sorted_cards = sorted(self.original_cards)
+
+
+data_structure: type = List[Hand]
+
+
+def parse_data(data: List[str]) -> data_structure:
+    return [Hand(datum.split()[0], datum.split()[1]) for datum in data]
+
+
+def part_x(data: data_structure, part: int) -> int:
+    Hand.part = part
+    for hand in data:
+        hand.reset_hand()
+    sorted_hands: List[Hand] = sorted(data, key=lambda h: h.get_sortable_key())
+    winnings: int = 0
+    for rank, hand in enumerate(sorted_hands):
+        winning = (rank + 1) * hand.bid
+        winnings += winning
+    return winnings
+
+
+def part1(data: data_structure) -> int:
+    return part_x(data, 1)
+
+
+def part2(data: data_structure) -> int:
+    return part_x(data, 2)
+
+
+if __name__ == '__main__':
+    production_ready = False
+    raw_data = import_data(day) if production_ready else sample_data
+    print(part1(parse_data(raw_data)))
+    print(part2(parse_data(raw_data)))
diff --git a/2023/python/Day08.py b/2023/python/Day08.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ec3536ee75889745af4633d467cf7303aa60446
--- /dev/null
+++ b/2023/python/Day08.py
@@ -0,0 +1,91 @@
+import functools
+from typing import List, Dict, Tuple
+
+from ImportData import import_data
+
+day: int = 8
+
+sample_data: List[List[str]] = [
+    '''
+RL
+
+AAA = (BBB, CCC)
+BBB = (DDD, EEE)
+CCC = (ZZZ, GGG)
+DDD = (DDD, DDD)
+EEE = (EEE, EEE)
+GGG = (GGG, GGG)
+ZZZ = (ZZZ, ZZZ)
+    '''.split('\n')[1:-1],
+    '''
+LLR
+
+AAA = (BBB, BBB)
+BBB = (AAA, ZZZ)
+ZZZ = (ZZZ, ZZZ)
+    '''.split('\n')[1:-1],
+    '''
+LR
+
+11A = (11B, XXX)
+11B = (XXX, 11Z)
+11Z = (11B, XXX)
+22A = (22B, XXX)
+22B = (22C, 22C)
+22C = (22Z, 22Z)
+22Z = (22B, 22B)
+XXX = (XXX, XXX)
+    '''.split('\n')[1:-1]
+]
+
+data_structure: type = Tuple[str, Dict[str, Tuple[str, str]]]
+
+
+def parse_data(data: List[str]) -> data_structure:
+    directions: str = data[0]
+    nodes: Dict[str, Tuple[str, str]] = {}
+    for line in data[2:]:
+        key = line[:3]
+        left = line[7:10]
+        right = line[12:15]
+        nodes[key] = (left, right)
+    return directions, nodes
+
+
+def part1(data: data_structure) -> int:
+    directions, nodes = data
+    steps = 0
+    modulus = len(directions)
+    node = 'AAA'
+    while node != 'ZZZ':
+        direction = 0 if directions[steps % modulus] == 'L' else 1
+        node = nodes[node][direction]
+        steps += 1
+    return steps
+
+
+def part2(data: data_structure) -> int:
+    directions, nodes = data
+    steps = 0
+    modulus = len(directions)
+    locations = [location for location in nodes.keys() if location[-1] == 'A']
+    while functools.reduce(lambda x, y: x + y, map(lambda z: (1 if z[-1] == 'Z' else 0), locations)) != len(locations):
+        direction = 0 if directions[steps % modulus] == 'L' else 1
+        locations = [nodes[location][direction] for location in locations]
+        steps += 1
+    return steps
+
+
+if __name__ == '__main__':
+    production_ready = True
+    # raw_data = import_data(day) if production_ready else sample_data
+    # print(part1(parse_data(raw_data)))
+    # print(part2(parse_data(raw_data)))
+    if not production_ready:
+        print(part1(parse_data(sample_data[0])))
+        print(part1(parse_data(sample_data[1])))
+        print(part2(parse_data(sample_data[2])))
+    else:
+        raw_data = import_data(day)
+        print(part1(parse_data(raw_data)))
+        print(part2(parse_data(raw_data)))
diff --git a/2023/python/Day09.py b/2023/python/Day09.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4e68226b4c08559c18ff465e3c0a2a029bd15c8
--- /dev/null
+++ b/2023/python/Day09.py
@@ -0,0 +1,51 @@
+import functools
+from typing import List, Tuple
+
+from ImportData import import_data
+
+day: int = 9
+
+sample_data: List[str] = '''
+0 3 6 9 12 15
+1 3 6 10 15 21
+10 13 16 21 30 45
+'''.split('\n')[1:-1]
+
+data_structure: type = List[List[int]]
+
+
+def parse_data(data: List[str]) -> data_structure:
+    reports: List[List[int]] = []
+    for line in data:
+        reports.append([int(datum) for datum in line.split()])
+    return reports
+
+
+def extrapolator(report: List[int]) -> Tuple[int, int]:
+    differences: List[int] = []
+    no_differences: bool = True
+    for i in range(1, len(report)):
+        difference = report[i] - report[i - 1]
+        differences.append(difference)
+        if difference != 0:
+            no_differences = False
+    if no_differences:
+        return report[0], report[0]
+    else:
+        left, right = extrapolator(differences)
+        return report[0] - left, report[-1] + right
+
+
+def part1(data: data_structure) -> int:
+    return functools.reduce(lambda x, y: x + y, map(lambda report: extrapolator(report)[1], data))
+
+
+def part2(data: data_structure) -> int:
+    return functools.reduce(lambda x, y: x + y, map(lambda report: extrapolator(report)[0], data))
+
+
+if __name__ == '__main__':
+    production_ready = True
+    raw_data = import_data(day) if production_ready else sample_data
+    print(part1(parse_data(raw_data)))
+    print(part2(parse_data(raw_data)))