[–] 5 points 11 months ago*

Rust

My first version worked with numbers, but since I was still sick of yesterday's pow(10) calls, I changed it to use ascii for the second half - the nice thing is that means it can work with hex input with no modification!

Clippy was complaining about "needless_range_loops", but it's way more readable this way.

struct PowerSource(Vec<Bank>);

impl FromStr for PowerSource {
    type Err = Report;

    fn from_str(s: &str) -> Result<Self> {
        let banks = s.lines().map(|l| Bank(l.to_owned())).collect();
        Ok(Self(banks))
    }
}

impl PowerSource {
    fn max_joltage(&self, num_digits: usize) -> usize {
        self.0.iter().map(|b| b.max_joltage(num_digits)).sum()
    }
}

struct Bank(String);

impl Bank {
    fn max_joltage(&self, num_digits: usize) -> usize {
        let batts = self.0.as_bytes();

        let mut digits = vec![b'0'; num_digits];
        let mut start = 0;
        for d in 0..num_digits {
            for i in start..=batts.len() - num_digits + d {
                if batts[i] > digits[d] {
                    digits[d] = batts[i];
                    start = i + 1;
                }
            }
        }

        usize::from_str(str::from_utf8(&digits).unwrap()).unwrap()
    }
}
  • source
  • [–] 2 points 11 months ago (2 children)

    Every so often I look for a library that will compile the regex at compile time - iirc, there's some stuff needing to made const fn before that can happen.

    Last time I used regex, lazy_static was the way to go; I assume that regex can go in OnceCell nowadays.

  • source
  • parent
  • context
  • [–] 4 points 11 months ago

    Another day where the dumb way would have so much quicker and easier, but I'm not competing for time.

    I decided to solve it numerically without regex or using to_string(), which was more taxing for the ol' grey matter but is perhaps fairly optimal (if I bothered to pre-compute all those pow() calls, anyway).

    Part 2 runs in 35ms (on my AMD Ryzen 7 9800X3D), whereas the to_string() version runs in 40ms. So... not really worth it, and it's less readable.

    Rust

    use std::fs;
    
    use color_eyre::eyre::{Result, bail};
    
    type InvalidChecker = fn(usize) -> bool;
    
    fn sum_invalids(input: &str, checkfn: InvalidChecker) -> Result<usize> {
        let total = input
            .trim()
            .split(',')
            .map(|idrange| {
                if let Some((start, end)) = idrange.split_once('-') {
                    let mut sum = 0;
                    for n in start.parse::<usize>()?..=end.parse::<usize>()? {
                        if checkfn(n) {
                            sum += n;
                        }
                    }
                    Ok(sum)
                } else {
                    bail!("Couldn't parse {idrange}")
                }
            })
            .sum::<Result<usize, _>>()?;
        Ok(total)
    }
    
    fn is_invalid_p1(n: usize) -> bool {
        let len = n.ilog10() + 1;
        // odd-length numbers can't repeat
        if len % 2 == 1 {
            return false;
        }
    
        let lhs = n / 10_usize.pow(len / 2);
        let rhs = n - (lhs * 10_usize.pow(len / 2));
        lhs == rhs
    }
    
    const SPANS: &[&[u32]] = &[
        &[],              // i = 0
        &[],              // i = 1
        &[1],             // i = 2
        &[1],             // i = 3
        &[1, 2],          // i = 4
        &[1],             // i = 5
        &[1, 2, 3],       // i = 6
        &[1],             // i = 7
        &[1, 2, 4],       // i = 8
        &[1, 3],          // i = 9
        &[1, 2, 5],       // i = 10
        &[1],             // i = 11
        &[1, 2, 3, 4, 6], // i = 12
    ];
    
    fn is_invalid_p2(n: usize) -> bool {
        let len = n.ilog10() + 1;
        // 1-length numbers can't repeat
        if len == 1 {
            return false;
        }
    
        SPANS[len as usize].iter().any(|&span| {
            let lhs = n / 10_usize.pow(len - span);
            let mut remainder = n;
            let mut rhs = lhs;
            (2..=(len / span)).all(|i| {
                remainder -= rhs * 10_usize.pow(len - (i - 1) * span);
                rhs = remainder / 10_usize.pow(len - i * span);
                lhs == rhs
            })
        })
    }
    
    fn part1(filepath: &str) -> Result<usize> {
        let input = fs::read_to_string(filepath)?;
        let res = sum_invalids(&input, is_invalid_p1)?;
        Ok(res)
    }
    
    fn part2(filepath: &str) -> Result<usize> {
        let input = fs::read_to_string(filepath)?;
        let res = sum_invalids(&input, is_invalid_p2)?;
        Ok(res)
    }
    
    

    to_string version:

    fn is_invalid_p2(n: usize) -> bool {
        let s = n.to_string();
        let len = s.len();
        // 1-length numbers can't repeat
        if len == 1 {
            return false;
        }
    
        SPANS[len].iter().any(|&span| {
            let span = span as usize;
            let lhs = &s[0..span].as_bytes();
            s.as_bytes().chunks(span).all(|rhs| *lhs == rhs)
        })
    }
    
  • source
  • [–] 2 points 11 months ago* (last edited 11 months ago)

    Rust

    use std::{cmp::Ordering, fs, str::FromStr};
    
    use color_eyre::eyre::{Result, bail};
    
    const DIAL_NUMBERS: isize = 100;
    
    #[derive(Clone, Copy)]
    struct Dial(usize);
    
    impl Dial {
        fn new() -> Self {
            Self(50)
        }
    
        fn rotate(&mut self, rot: isize) -> usize {
            let pass_0s;
    
            let total = rot.wrapping_add_unsigned(self.0);
            match total.cmp(&0) {
                Ordering::Equal => {
                    pass_0s = 1;
                    self.0 = 0;
                }
                Ordering::Less => {
                    // Starting on 0 means we don't cross it
                    let started_0 = if self.0 == 0 { 1 } else { 0 };
                    pass_0s = 1 + (-total / DIAL_NUMBERS) as usize - started_0;
                    self.0 = (self.0 as isize + rot).rem_euclid(DIAL_NUMBERS) as usize;
                }
                Ordering::Greater => {
                    let full_turns = total / DIAL_NUMBERS;
                    pass_0s = full_turns as usize;
                    self.0 = (total - DIAL_NUMBERS * full_turns) as usize;
                }
            };
    
            pass_0s
        }
    
        fn sequence(&mut self, s: &str) -> Result<(usize, usize)> {
            let mut end_0s = 0;
            let mut pass_0s = 0;
    
            for l in s.lines() {
                let num = isize::from_str(&l[1..]).unwrap();
                let dir = l.bytes().next().unwrap();
                pass_0s += match dir {
                    b'L' => self.rotate(-num),
                    b'R' => self.rotate(num),
                    _ => bail!("b{dir} is an invalid rotation direction"),
                };
                if self.0 == 0 {
                    end_0s += 1;
                }
            }
    
            Ok((end_0s, pass_0s))
        }
    }
    
    fn parts(filepath: &str) -> Result<(usize, usize)> {
        let input = fs::read_to_string(filepath)?;
        let mut dial = Dial::new();
        let res = dial.sequence(&input)?;
        Ok(res)
    }
    
    fn main() -> Result<()> {
        color_eyre::install()?;
    
        let (p1, p2) = parts("d01/input.txt")?;
        println!("Part 1: {p1}");
        println!("Part 2: {p2}");
        Ok(())
    }
    

    I lost a lot of time struggling with edge cases in part two since I thought it was wasteful to run rem_euclid() (i.e. modulus) when I'd already got the information to do it more efficiently. While I got there in the end my final code was a bit ugly. This prettier, "unoptimised" version runs in 1.6ms, so I was being an idiot.

  • source
  • [–] 55 points 11 months ago
      // this is bollocks, delete it
    

    That's almost certainly from a Brit.

      // this looks like I'm being a fancy arsehole, but this is all because  
      // the window shows up white for some reason when first opened, and this  
      // disguises it.
    

    Could be either.

    chefkiss.png

  • source
  • parent
  • context
  • view more: β€Ή prev next β€Ί