Day 2: Red-Nosed Reports

Megathread guidelines

  • 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://blocks.programming.dev if you prefer sending it through a URL

FAQ

  • Amy@lemmy.sdf.org
    link
    fedilink
    arrow-up
    1
    ·
    7 months ago

    Haskell

    This was quite fun! I got a bit distracted trying to rewrite safe in point-free style, but I think this version is the most readable. There’s probably a more monadic way of writing lessOne as well, but I can’t immediately see it.

    safe xs = any gradual [diffs, negate <$> diffs]
      where
        diffs = zipWith (-) (drop 1 xs) xs
        gradual = all (`elem` [1 .. 3])
    
    lessOne [] = []
    lessOne (x : xs) = xs : map (x :) (lessOne xs)
    
    main = do
      input :: [[Int]] <- map (map read . words) . lines <$> readFile "input02"
      print . length $ filter safe input
      print . length $ filter (any safe . lessOne) input
    
    • VegOwOtenks@lemmy.world
      link
      fedilink
      English
      arrow-up
      1
      ·
      7 months ago

      Love to see your haskell solutions!

      I am so far very amazed with the compactness of your solutions, your lessOne is very much mind-Bending. I have never used or seen <$> before, is it a monadic $?

      Also I can’t seem to find your logic for this safety condition: The levels are either all increasing or all decreasing, did you figure that it wasn’t necessary?

  • sjmulder@lemmy.sdf.org
    link
    fedilink
    arrow-up
    1
    ·
    7 months ago

    C

    First went through the input in one pass, number by number, but unfortunately that wouldn’t fly for part 2.

    Code
    #include "common.h"
    
    static int
    issafe(int *lvs, int n, int skip)
    {
    	int safe=1, asc=0,prev=0, ns=0,i;
    
    	for (i=0; safe && i<n; i++) {
    		if (i == skip)
    			{ ns = 1; continue; }
    		if (i-ns > 0)
    			safe = safe && lvs[i] != prev &&
    			    lvs[i] > prev-4 && lvs[i] < prev+4;
    		if (i-ns == 1)
    			asc = lvs[i] > prev;
    		if (i-ns > 1)
    			safe = safe && (lvs[i] > prev) == asc;
    
    		prev = lvs[i];
    	}
    
    	return safe;
    }
    
    int
    main(int argc, const char **argv)
    {
    	char buf[64], *rest, *tok;
    	int p1=0,p2=0, lvs[16],n=0, i;
    
    	if (argc > 1)
    		DISCARD(freopen(argv[1], "r", stdin));
    
    	while ((rest = fgets(buf, sizeof(buf), stdin))) {
    		for (n=0; (tok = strsep(&rest, " ")); n++) {
    			assert(n < (int)LEN(lvs));
    			lvs[n] = (int)strtol(tok, NULL, 10);
    		}
    
    		for (i=-1; i<n; i++)
    			if (issafe(lvs, n, i))
    				{ p1 += i == -1; p2++; break; }
    	}
    
    	printf("02: %d %d\n", p1, p2);
    }
    

    https://github.com/sjmulder/aoc/blob/master/2024/c/day02.c

    • Faresh@lemmy.ml
      link
      fedilink
      English
      arrow-up
      1
      ·
      7 months ago

      What is this coding style? The function type, name and open brace placement made me think GNU at first, but the code in the body doesn’t look like GCS at all.

            • Gobbel2000@programming.dev
              link
              fedilink
              arrow-up
              1
              ·
              7 months ago

              It really depends on what your parameter n is. If the only relevant size is the number of records (let’s say that is n), then this solution takes time in O(n), because it loops over records only once at a time. This ignores the length of records by considering it constant.

              If we also consider the maximum length of records (let’s call it m), then your solution, and most others I’ve seen in this thread, has a time complexity in O(n * m^2) for part 2.

  • Quant@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    7 months ago

    Uiua

    Took me a bit longer to get this one but still quite simple overall.
    Spent quite some time on getting to know the try and assert operators better.

    Run with example input here

    # Get the indices matching the ascending/
    # descending criteria
    CheckAsc ← ≡°□⍚((⊸⍤.≍⍆.)(⊸⍤.≍⇌⍆.)0)
    # Get the indices matching the distance criteria
    CheckDist ← ≡°□⍚((⊸⍤.≠1:0)0×⊓≥≤1,3⌵⧈-)
    Split     ← ⊙(▽≠1)▽,,
    
    PartOne ← (
      &rs ∞ &fo "input-2.txt"(□⊜⋕≠@ .)≠@\n.
      CheckAsc.
      ▽
      CheckDist
      ⧻⊚
    )
    
    PartTwo ← (
      &rs ∞ &fo "input-2.txt"(□⊜⋕≠@ .)≠@\n.
      CheckAsc.
      Split
      CheckDist.
      Split
      ⊙():((:°⊟)⍜¤⊞⊟:1=.⇡⧻.)(⧻⊚CheckDist▽CheckAsc.°□)
      +⧻◴⊚
    )
    
    &p "Day 2:"
    &pf "Part 1: "
    &p PartOne
    &pf "Part 2: "
    &p PartTwo
    
  • mykl@lemmy.world
    link
    fedilink
    arrow-up
    0
    ·
    edit-2
    7 months ago

    Uiua

    Uiua is still developing very quickly, and this code uses the experimental tuples function, hence the initial directive.

    Try it Live!

    # Experimental!
    "7 6 4 2 1\n1 2 7 8 9\n9 7 6 2 1\n1 3 2 4 5\n8 6 4 4 1\n1 3 6 7 9"
    ⊜(⊜⋕⊸≠@\s)⊸≠@\n # Partition at \n, then at space, parse ints.
    
    IsSorted  +⊃(≍⇌⍆.|≍⍆.)        # Compare with sorted array.
    IsSmall   /××⊃(>0|<4)⌵↘¯1-↻1. # Copy offset by 1, check diffs.
    IsSafe    ×⊃IsSmall IsSorted  # Safe if Small steps and Ordered.
    IsSafer   ±/+≡IsSafe ⧅<-1⧻.   # Choose 4 from 5, check again.
    
    &p/+≡IsSafe .            # Part1 : Is each row safe?
    &p/+≡(±+⊃IsSafe IsSafer) # Part2 : Is it safe or safer?
    
  • Gobbel2000@programming.dev
    link
    fedilink
    English
    arrow-up
    0
    ·
    7 months ago

    Rust

    The function is_sorted_by on Iterators turned out helpful for compactly finding if a report is safe. In part 2 I simply tried the same with each element removed, since all reports are very short.

    fn parse(input: String) -> Vec<Vec<i32>> {
        input.lines()
            .map(|l| l.split_whitespace().map(|w| w.parse().unwrap()).collect())
            .collect()
    }
    
    fn is_safe(report: impl DoubleEndedIterator<Item=i32> + Clone) -> bool {
        let safety = |a: &i32, b: &i32| (1..=3).contains(&(b - a));
        report.clone().is_sorted_by(safety) || report.rev().is_sorted_by(safety)
    }
    
    fn part1(input: String) {
        let reports = parse(input);
        let safe = reports.iter().filter(|r| is_safe(r.iter().copied())).count();
        println!("{safe}");
    }
    
    fn is_safe2(report: &[i32]) -> bool {
        (0..report.len()).any(|i| {  // Try with each element removed
            is_safe(report.iter().enumerate().filter(|(j, _)| *j != i).map(|(_, n)| *n))
        })
    }
    
    fn part2(input: String) {
        let reports = parse(input);
        let safe = reports.iter().filter(|r| is_safe2(r)).count();
        println!("{safe}");
    }
    
    util::aoc_main!();
    
    • Sleepless One@lemmy.ml
      link
      fedilink
      English
      arrow-up
      0
      arrow-down
      1
      ·
      7 months ago

      The is_sorted_by is a really nice approach. I originally tried using that function thinking that |a, b| a > b or |a, b| a < b would cut it but it didn’t end up working. I never thought to handle the check for the step being between 1 and 3 in the callback closure for that though.

  • Sleepless One@lemmy.ml
    link
    fedilink
    English
    arrow-up
    0
    arrow-down
    1
    ·
    7 months ago

    Rust

    use crate::utils::read_lines;
    
    pub fn solution1() {
        let reports = get_reports();
        let safe_reports = reports
            .filter(|report| report.windows(3).all(window_is_valid))
            .count();
    
        println!("Number of safe reports = {safe_reports}");
    }
    
    pub fn solution2() {
        let reports = get_reports();
        let safe_reports = reports
            .filter(|report| {
                (0..report.len()).any(|i| {
                    [&report[0..i], &report[i + 1..]]
                        .concat()
                        .windows(3)
                        .all(window_is_valid)
                })
            })
            .count();
    
        println!("Number of safe reports = {safe_reports}");
    }
    
    fn window_is_valid(window: &[usize]) -> bool {
        matches!(window[0].abs_diff(window[1]), 1..=3)
            && matches!(window[1].abs_diff(window[2]), 1..=3)
            && ((window[0] > window[1] && window[1] > window[2])
                || (window[0] < window[1] && window[1] < window[2]))
    }
    
    fn get_reports() -> impl Iterator<Item = Vec<usize>> {
        read_lines("src/day2/input.txt").map(|line| {
            line.split_ascii_whitespace()
                .map(|level| {
                    level
                        .parse()
                        .expect("Reactor level is always valid integer")
                })
                .collect()
        })
    }
    

    Definitely trickier than yesterday’s. I feel like the windows solution isn’t the best, but it was what came to mind and ended up working for me.