diff --git a/2023/python/Day10.py b/2023/python/Day10.py
new file mode 100644
index 0000000000000000000000000000000000000000..55cd7bf68a0e240a44cdec78f3a27395ed04bdab
--- /dev/null
+++ b/2023/python/Day10.py
@@ -0,0 +1,202 @@
+import functools
+from typing import List, Tuple
+
+from ImportData import import_data
+
+day: int = 10
+
+# sample_data: List[str] = '''
+# .....
+# .S-7.
+# .|.|.
+# .L-J.
+# .....
+# '''.split('\n')[1:-1]
+# sample_data: List[str] = '''
+# 7-F7-
+# .FJ|7
+# SJLL7
+# |F--J
+# LJ.LJ
+# '''.split('\n')[1:-1]
+# sample_data: List[str] = '''
+# ...........
+# .S-------7.
+# .|F-----7|.
+# .||.....||.
+# .||.....||.
+# .|L-7.F-J|.
+# .|..|.|..|.
+# .L--J.L--J.
+# ...........
+# '''.split('\n')[1:-1]
+# sample_data: List[str] = '''
+# ..........
+# .S------7.
+# .|F----7|.
+# .||....||.
+# .||....||.
+# .|L-7F-J|.
+# .|..||..|.
+# .L--JL--J.
+# ..........
+# '''.split('\n')[1:-1]
+# sample_data: List[str] = '''
+# .F----7F7F7F7F-7....
+# .|F--7||||||||FJ....
+# .||.FJ||||||||L7....
+# FJL7L7LJLJ||LJ.L-7..
+# L--J.L7...LJS7F-7L7.
+# ....F-J..F7FJ|L7L7L7
+# ....L7.F7||L7|.L7L7|
+# .....|FJLJ|FJ|F7|.LJ
+# ....FJL-7.||.||||...
+# ....L---J.LJ.LJLJ...
+# '''.split('\n')[1:-1]
+sample_data: List[str] = '''
+FF7FSF7F7F7F7F7F---7
+L|LJ||||||||||||F--J
+FL-7LJLJ||||||LJL-77
+F--JF--7||LJLJ7F7FJ-
+L---JF-JLJ.||-FJLJJ7
+|F|F-JF---7F7-L7L|7|
+|FFJF7L7F-JF7|JL---7
+7-L-JL7||F7|L7F-7F7|
+L.L7LFJ|||||FJL7||LJ
+L7JLJL-JLJLJL--JLJ.L
+'''.split('\n')[1:-1]
+
+data_structure: type = Tuple[int, int, str, List[str]]
+
+
+def parse_data(data: List[str]) -> data_structure:
+    initial_row: int = -1
+    initial_column: int = -1
+    initial_pipe: str
+    number_of_rows = len(data)
+    number_of_columns = len(data[0])
+    for row in range(number_of_rows):
+        for column in range(number_of_columns):
+            if data[row][column] == 'S':
+                initial_row = row
+                initial_column = column
+    initial_pipe = input('What is the shape of the initial pipe? ')
+    return initial_row, initial_column, initial_pipe, data
+
+
+def part1(data: data_structure) -> int:
+    row, column, pipe, pipes = data
+    previous_locations: List[Tuple[int, int]] = [(row, column), (row, column)]
+    current_locations: List[Tuple[int, int]]
+    match pipe:
+        case '|':
+            current_locations = [(row + 1, column), (row - 1, column)]
+        case '-':
+            current_locations = [(row, column + 1), (row, column - 1)]
+        case 'L':
+            current_locations = [(row, column + 1), (row - 1, column)]
+        case 'J':
+            current_locations = [(row, column - 1), (row - 1, column)]
+        case '7':
+            current_locations = [(row, column - 1), (row + 1, column)]
+        case 'F':
+            current_locations = [(row, column + 1), (row + 1, column)]
+        case _:
+            raise ValueError(f'{pipe} at ({row},{column})')
+    distance: int = 1
+    while current_locations[0] != current_locations[1]:
+        distance += 1
+        for i in range(len(current_locations)):
+            previous_row, previous_column = previous_locations[i]
+            row, column = current_locations[i]
+            pipe = pipes[row][column]
+            previous_locations[i] = current_locations[i]
+            row_change = row - previous_row
+            column_change = column - previous_column
+            match pipe:
+                case '|' | '-':
+                    current_locations[i] = (row + row_change, column + column_change)
+                case 'L' | '7':
+                    current_locations[i] = (row + column_change, column + row_change)
+                case 'J' | 'F':
+                    current_locations[i] = (row - column_change, column - row_change)
+                case _:
+                    raise ValueError(f'{pipe} at ({row}, {column})')
+    return distance
+
+
+def part2(data: data_structure) -> int:
+    row, column, pipe, pipes = data
+    # an extra copy of the pipes data, "zoomed in" to allow for gaps between pipes
+    tiles: [List[List[str]]] = [['I'] * (2 * len(pipes[0])) for _ in range(2 * len(pipes))]
+    # first, draw the main loop on the tiles copy
+    previous_location: Tuple[int, int] = (row, column)
+    current_location: Tuple[int, int]
+    match pipe:
+        case '|' | 'F':
+            current_location = (row + 1, column)
+            tiles[2 * row + 1][2 * column] = 'X'
+        case '-' | 'L':
+            current_location = (row, column + 1)
+            tiles[2 * row][2 * column + 1] = 'X'
+        case 'J' | '7':
+            current_location = (row, column - 1)
+            tiles[2 * row][2 * column - 1] = 'X'
+        case _:
+            raise ValueError(f'{pipe} at ({row},{column})')
+    previous_row, previous_column = previous_location
+    row, column = current_location
+    while tiles[2 * row][2 * column] != 'X':
+        tiles[2 * row][2 * column] = 'X'
+        pipe = pipes[row][column]
+        previous_location = current_location
+        row_change = row - previous_row
+        column_change = column - previous_column
+        match pipe:
+            case '|' | '-':
+                current_location = (row + row_change, column + column_change)
+                tiles[2 * row + row_change][2 * column + column_change] = 'X'
+            case 'L' | '7':
+                current_location = (row + column_change, column + row_change)
+                tiles[2 * row + column_change][2 * column + row_change] = 'X'
+            case 'J' | 'F':
+                current_location = (row - column_change, column - row_change)
+                tiles[2 * row - column_change][2 * column - row_change] = 'X'
+            case 'S':
+                pass
+            case _:
+                raise ValueError(f'{pipe} at ({row}, {column})')
+        previous_row, previous_column = previous_location
+        row, column = current_location
+    # now let's create a border
+    tiles = [['O'] * (2 * len(pipes[0]))] + tiles + [['O'] * (2 * len(tiles[0]))]
+    tiles = [['O'] + tile + ['O'] for tile in tiles]
+    # fill in the "outside"
+    finished: bool = False
+    while not finished:
+        finished = True
+        for row in range(1, len(tiles) - 1):
+            for column in range(1, len(tiles[0]) - 1):
+                if tiles[row][column] == 'I' and (tiles[row - 1][column] == 'O' or tiles[row][column - 1] == 'O' or
+                                                  tiles[row + 1][column] == 'O' or tiles[row][column + 1] == 'O'):
+                    tiles[row][column] = 'O'
+                    finished = False
+    # now shrink it back down to size, step 1: remove the border
+    # arguably, this step is unnecessary, but it simplifies the expression in step 2
+    tiles = [row[1:-1] for row in tiles[1:-1]]
+    # shrink it back down to size, step 2: remove the "in between" tiles
+    new_tiles: List[List[str]] = []
+    for row in range(0, len(tiles), 2):
+        new_row: List[str] = []
+        for column in range(0, len(tiles[0]), 2):
+            new_row.append(tiles[row][column])
+        new_tiles.append(new_row)
+    # count the "inside"
+    return sum([functools.reduce(lambda x, y: x + y, map(lambda x: 1 if x == 'I' else 0, row)) for row in new_tiles])
+
+
+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)))