Quest 13: Unlocking the Mountain

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

Link to participate: https://everybody.codes/

  • hades@programming.devOPM
    link
    fedilink
    arrow-up
    1
    ·
    3 months ago

    Rust

    pub fn solve_part_1(input: &str) -> String {
        let numbers = input
            .lines()
            .map(|l| l.parse().unwrap())
            .collect::<Vec<i64>>();
        let offset = 2025 % (numbers.len() + 1);
        if offset == 0 {
            1
        } else if offset > numbers.len().div_ceil(2) {
            numbers[(numbers.len() - offset) * 2 + 1]
        } else {
            numbers[(offset - 1) * 2]
        }
        .to_string()
    }
    
    fn find_number(ranges: &[(i64, i64)], mut offset: i64, counterclockwise: bool) -> i64 {
        for (from, to) in ranges {
            let segment_size = (to - from) + 1;
            if offset >= segment_size {
                offset -= segment_size;
                continue;
            }
            return if counterclockwise {
                to - offset
            } else {
                from + offset
            };
        }
        panic!("find_number gave up and died");
    }
    
    fn solve_part_2_with_turns(input: &str, turns: i64) -> String {
        let ranges = input
            .lines()
            .map(|l| {
                let (l, r) = l.split_once("-").unwrap();
                (l.parse().unwrap(), r.parse().unwrap())
            })
            .collect::<Vec<(i64, i64)>>();
        let mut clockwise_length = 0;
        let mut clockwise_ranges = vec![];
        let mut counterclockwise_length = 0;
        let mut counterclockwise_ranges = vec![];
        for (i, (from, to)) in ranges.into_iter().enumerate() {
            if i % 2 == 0 {
                clockwise_length += to - from + 1;
                clockwise_ranges.push((from, to));
            } else {
                counterclockwise_length += to - from + 1;
                counterclockwise_ranges.push((from, to));
            }
        }
        counterclockwise_ranges.reverse();
        let offset = turns % (clockwise_length + counterclockwise_length + 1);
        if offset == 0 {
            1
        } else if offset > clockwise_length {
            find_number(
                &counterclockwise_ranges,
                offset - clockwise_length - 1,
                true,
            )
        } else {
            find_number(&clockwise_ranges, offset - 1, false)
        }
        .to_string()
    }
    
    pub fn solve_part_2(input: &str) -> String {
        solve_part_2_with_turns(input, 20252025)
    }
    
    pub fn solve_part_3(input: &str) -> String {
        solve_part_2_with_turns(input, 202520252025)
    }
    
    
  • Pyro@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    3 months ago

    Python

    A much simpler problem compared to earlier ones

    # generator to yield dial values in the required order
    # yields (value: str, is_reverse: bool)
    def yield_dial_vals(coll: list):
        n = len(coll)
        for i in range(0, n, 2):
            yield coll[i], False
        right_start = n - 2 if n % 2 == 1 else n - 1
        for i in range(right_start, -1, -2):
            yield coll[i], True
    
    def part1(data: str):
        nums = data.splitlines()
        n = len(nums)
        
        # total number of values on the dial
        #   + 1 for the '1' value
        total_vals = n + 1
        # effective rotation after full cycles
        effective_rot = 2025 % total_vals
        # if full cycles, return '1'
        if effective_rot == 0:
            return 1
        # adjust for 0-indexing the remaining dial values
        effective_rot -= 1
        
        # skip numbers on the dial until we reach the final value
        val_gen = yield_dial_vals(nums)
        for _ in range(effective_rot):
            next(val_gen)
        
        # return the final value
        return int(next(val_gen)[0])
    
    assert (t := part1("""72
    58
    47
    61
    67""")) == 67, f"Expected: 67, Actual: {t}"
    
    def part2(data: str, rotations = 20252025):
        ranges = data.splitlines()
        
        # count values on the dial other than '1'
        n = 0
        for val, _ in yield_dial_vals(ranges):
            a, b = map(int, val.split("-"))
            n += b - a + 1
        
        # total number of values on the dial
        #   + 1 for the '1' value
        total_vals = n + 1
        # effective rotation after full cycles
        effective_rot = rotations % total_vals
        # if full cycles, return '1'
        if effective_rot == 0:
            return 1
        # adjust for 0-indexing the remaining dial values
        effective_rot -= 1
        
        # iterate through ranges until we reach the one the dial lands on
        for val, is_reverse in yield_dial_vals(ranges):
            # get range bound and size
            a, b = map(int, val.split("-"))
            r = b - a + 1
            # check if the dial lands within this range
            if effective_rot < r:
                # it does!
                # return the appropriate value in the range based on direction
                if is_reverse:
                    return b - effective_rot
                else:
                    return a + effective_rot
            # consume the rotations for skipping this range
            effective_rot -= r
        
        assert False, "Should have found the target range"
    
    
    assert part2("""10-15
    12-13
    20-21
    19-23
    30-37""") == 30
    
    # part 3 is just part 2 with a larger number of rotations
    from functools import partial
    part3 = partial(part2, rotations = 202520252025)
    
      • Pyro@programming.dev
        link
        fedilink
        arrow-up
        2
        ·
        edit-2
        3 months ago

        Yeah, it’s an easy way to make sure everything is working correctly when I refactor or optimize. I used to keep samples in their own text files before but EC samples are small enough to include directly.