5
submitted 3 days ago* (last edited 3 days ago) by Pyro@programming.dev to c/advent_of_code@programming.dev

Preparations for the princess's wedding are in full swing, and the royal court has announced a competition for the most beautiful ballroom decorations. One of the challenges is to design the ornamental trimming for the grand curtains surrounding the dance floor.

You can send code in code blocks by surrounding it in triple backticks (``````) and make it collapsible by surrounding it in spoiler syntax. Or you could use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL.

you are viewing a single comment's thread
view the rest of the comments
[-] Pyro@programming.dev 1 points 3 days ago* (last edited 3 days ago)

Python

Made a single cleaned-up solver for all 3 parts after solving them separately. Intersection logic can be optimized, but the inputs are small enough to not need it.

click to view code

# simple type to represent an arc
Arc = tuple[int, int]

# single solver for all 3 parts
class Solver:
    def __init__(self, forward_skips_visited = False, forbid_crossings = False):
        # a forward jump landing on a visited point slides right to the first free point (part 2)
        self.forward_skips_visited = forward_skips_visited
        # arcs may not cross, and a jump with no valid landing is skipped entirely (part 3)
        self.forbid_crossings = forbid_crossings

        # current position on the line
        self.pos = 0
        # set of visited points on the line
        self.visited = {0}
        # which side the arc will be drawn on next (down = 0, up = 1)
        self.side = 0
        # arc segments on each side of the line
        self.arcs: dict[int, list[Arc]] = { 0: [], 1: [] }

    # get the first arc / segment that crosses the given range
    def _get_crossing(self, start: int, end: int) -> Arc | None:
        low, high = min(start, end), max(start, end)
        for a, b in self.arcs[self.side]:
            if a < low < b < high or low < a < high < b:
                return a, b
        return None

    # try moving backwards by the given length, return None if not possible
    def _move_backward(self, length: int) -> int | None:
        next_pos = self.pos - length
        if next_pos < 0 or next_pos in self.visited:
            return None
        if self.forbid_crossings and self._get_crossing(next_pos, self.pos) is not None:
            return None
        return next_pos

    # try moving forwards by the given length, return None if not possible
    def _move_forward(self, length: int) -> int | None:
        next_pos = self.pos + length
        while True:
            # keep moving forward until an unvisited point is found
            if self.forward_skips_visited and next_pos in self.visited:
                next_pos += 1
                continue

            if not self.forbid_crossings:
                return next_pos

            crossing = self._get_crossing(self.pos, next_pos)
            if crossing is None:
                return next_pos

            # if the crossing arc ends before the next position, it becomes impossible to jump without crossing it
            _, end = crossing
            if end < next_pos:
                return None
            
            # clear the arc
            next_pos = end + 1

    # perform a jump of the given length according to the active rules
    def jump(self, length: int):
        next_pos = self._move_backward(length)
        if next_pos is None:
            next_pos = self._move_forward(length)
        if next_pos is None:
            return

        # update solver state
        self.arcs[self.side].append((min(self.pos, next_pos), max(self.pos, next_pos)))
        self.visited.add(next_pos)
        self.side = 1 - self.side
        self.pos = next_pos

def sum_final_positions(data: str, forward_skips_visited = False, forbid_crossings = False):
    total = 0
    for line in data.splitlines():
        solver = Solver(forward_skips_visited, forbid_crossings)
        for length in map(int, line.split(',')):
            solver.jump(length)
        total += solver.pos
    return total

def part1(data: str):
    """
    Rules:
    - For each jump, first try to move backwards by its specified length.
    - If the destination is negative or has been visited before, move forwards by the same distance.
    """
    return sum_final_positions(data)

def part2(data: str):
    """
    Additional rules:
    - Whenever a forward jump would land on a previously visited point,
        increase the destination by one until you reach the first unvisited point
    """
    return sum_final_positions(data, forward_skips_visited=True)

def part3(data: str):
    """
    Additional rules:
    - If a backward jump would cause a crossing, try moving forwards instead
    - If a forward jump would cause a crossing, keep increasing its length by one until there is no crossing
    - If no valid forward jump exists, skip that jump entirely and continue with the next jump length in the sequence
    """
    return sum_final_positions(data, forward_skips_visited=True, forbid_crossings=True)

this post was submitted on 26 Aug 2026
5 points (100.0% liked)

Advent Of Code

1243 readers
1 users here now

An unofficial home for the advent of code community on programming.dev! Other challenges are also welcome!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

Everybody Codes is another collection of programming puzzles with seasonal events.

EC 2025

AoC 2025

Solution Threads

M T W T F S S
1 2 3 4 5 6 7
8 9 10 11 12

Visualisations Megathread

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 3 years ago
MODERATORS