Spyke

Replies

Comment on

🔒 - 2025 DAY 1 SOLUTIONS -🔒

Rust

#[derive(Default)]
pub struct Day1Solver {
    input: Vec<i64>,
}

impl Solver for Day1Solver {
    fn presolve(&mut self, input: &str) {
        self.input = input
            .trim()
            .split("\n")
            .map(|line| {
                if let Some(n) = line.strip_prefix('L') {
                    -n.parse::<i64>().unwrap()
                } else if let Some(n) = line.strip_prefix('R') {
                    n.parse().unwrap()
                } else {
                    panic!("what: {line}");
                }
            })
            .collect();
    }

    fn solve_part_one(&mut self) -> String {
        let mut p = 50;
        let mut count = 0;
        for n in self.input.clone() {
            p += n;
            if p % 100 == 0 {
                count += 1;
            }
        }
        count.to_string()
    }

    fn solve_part_two(&mut self) -> String {
        let mut count = 0;
        let mut p = 1000000000050;
        for i in self.input.clone() {
            if p % 100 == 0 {
                count += (i / 100).abs();
            } else {
                count += ((p + i) / 100 - p / 100).abs();
                if i < 0 && (p + i) % 100 == 0 {
                    count += 1;
                }
            }
            p += i;
        }
        count.to_string()
    }
}
rust

Comment on

Feedback for first Rust Program

Welcome to Rust, I hope you enjoyed learning and using it :)

One major advice would be to run cargo clippy, which gives a lot of helpful hints. Apart from that I also have some feedback.

    40	struct VendorOuiData {
    41	    vendor_organization: String, // Vendor Organization

These comments should be doc-comments (///) so that cargo doc picks them up.

    49	fn fetch_oui_database(url_string: &String) -> Result<String, Box<dyn std::error::Error>> {

Not saying that your return type is bad, but in general you might be interested in crates thiserror and anyhow that simplify error handling.

    51	        .timeout(Duration::new(60, 0))

Duration::from_mins(1) would be much more readable.

    66	                panic!("{}", err);

You should almost never panic. Your function already returns a Result<>, so you should be using that for error handling.

    70	    } else {
    71	        // HTTP Status is not 200

Not Rust specific, but usually I recommend to handle errors first, and return from the function early. This way you reduce cognitive load on the reader, and reduce nesting of if-blocks.

   150	                let mut parts = line.splitn(2, '\t');

There's a convenient method to split in two substrings that you could have used:

let (mac_address, vendor_organization) = line.split_once('\t').unwrap_or(("", ""));
   166	                vendor_oui_data.vendor_address += line
   167	                    .trim()
   168	                    .split(' ')
   169	                    .filter(|s| !s.is_empty())
   170	                    .collect::<Vec<_>>()
   171	                    .join(" ")
   172	                    .as_str();

I would probably write a regular expression that replaces all contiguous whitespace with a single space instead.

   173	                vendor_oui_data.vendor_address.push('\n');

Aren't you trimming this new line off in line 181?

   181	        vendor_oui_data.vendor_address = vendor_oui_data.vendor_address.trim().to_string();

This is somewhat inefficient, since you'll be allocating and copying your string here.

Comment on

🦆 Everybody.Codes 2025 Quest 6 Solutions 🦆

Rust

use std::collections::HashMap;
use itertools::Itertools;

pub fn solve_part_1(input: &str) -> String {
    let mut mentors = 0;
    let mut pairs = 0;
    for ch in input.chars() {
        match ch {
            'A' => mentors += 1,
            'a' => pairs += mentors,
            _ => {}
        }
    }
    pairs.to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let mut mentors: HashMap<char, i64> = HashMap::new();
    let mut pairs = 0;
    for ch in input.chars() {
        match ch {
            'A'..='Z' => *mentors.entry(ch).or_default() += 1,
            'a'..='z' => pairs += *mentors.entry(ch.to_ascii_uppercase()).or_default(),
            _ => panic!("unexpected character {ch}"),
        }
    }
    pairs.to_string()
}

pub fn solve_part_3(input: &str) -> String {
    let data: Vec<_> = input.chars().collect();
    let len = data.len();
    let mentors: HashMap<char, Vec<usize>> = data
        .iter()
        .enumerate()
        .map(|(i, ch)| (*ch, i))
        .into_group_map();
    let mut pairs: i64 = 0;
    for (squire_position, ch) in data.into_iter().enumerate() {
        if ch.is_ascii_lowercase() {
            for mentor_position in mentors.get(&ch.to_ascii_uppercase()).unwrap() {
                if squire_position.abs_diff(*mentor_position) <= 1000 {
                    pairs += 1000;
                } else if (squire_position as isize)
                    .wrapping_sub_unsigned(len)
                    .abs_diff(*mentor_position as isize)
                    <= 1000
                    || (*mentor_position as isize)
                        .wrapping_sub_unsigned(len)
                        .abs_diff(squire_position as isize)
                        <= 1000
                {
                    pairs += 999;
                }
            }
        }
    }
    pairs.to_string()
}

Comment on

🔒 - 2025 DAY 1 SOLUTIONS -🔒

Reply in thread

the idea is that crossing the zero is easy to detect by dividing the total dial movement by 100. I.e. if you cross from 120 to 90 you will detect that 120/100=1 changed to 90/100=0. The only case when this doesn’t work is when you stop at zero going in the negative direction, hence the extra if

Comment on

🦆 Everybody.Codes 2025 Quest 2 Solutions 🦆

Rust

use log::debug;
use std::collections::HashSet;

use regex::Regex;

#[derive(PartialEq, Eq, Hash, Clone)]
struct Number(isize, isize);

impl Number {

  fn add(self: &Number, b: &Number) -> Number {
    Number(self.0 + b.0, self.1 + b.1)
  }

  fn mul(self: &Number, b: &Number) -> Number {
    Number(self.0 * b.0 - self.1 * b.1, self.0 * b.1 + self.1 * b.0)
  }

  fn div(self: &Number, b: &Number) -> Number {
    Number(self.0 / b.0, self.1 / b.1)
  }
}

pub fn solve_part_1(input: &str) -> String {
  let re = Regex::new(r"A=\[(\d+),(\d+)\]").unwrap();
  let (_, [x, y]) = re.captures(input).unwrap().extract();
  let a = Number(x.parse().unwrap(), y.parse().unwrap());
  let mut res = Number(0, 0);
  for _ in 0..3 {
    res = res.mul(&res);
    res = res.div(&Number(10, 10));
    res = res.add(&a);
  }
  format!("[{},{}]", res.0, res.1)
}

pub fn solve_part_2(input: &str) -> String {
  let re = Regex::new(r"A=\[([-0-9]+),([-0-9]+)\]").unwrap();
  let (_, [x, y]) = re.captures(input).unwrap().extract();
  let a = Number(x.parse().unwrap(), y.parse().unwrap());
  let mut engraved_points = 0;
  let mut pts: HashSet<_> = HashSet::new();
  for i in 0..=100 {
    for j in 0..=100 {
      let pt = Number(a.0 + 10 * i, a.1 + 10 * j);
      let mut res = Number(0, 0);
      engraved_points += 1;
      pts.insert(pt.clone());
      for _ in 0..100 {
        res = res.mul(&res);
        res = res.div(&Number(100_000, 100_000));
        res = res.add(&pt);
        if res.0.abs() > 1_000_000 || res.1.abs() > 1_000_000 {
          engraved_points -= 1;
          pts.remove(&pt);
          break;
        }
      }
    }
  }
  for i in 0..=100 {
    debug!("{}", (0..=100).map(|j| if pts.contains(&Number(a.0 + 10*i, a.1 + 10*j)) { 'X' } else {'.'}).collect::<String>());
  }
  engraved_points.to_string()
}

pub fn solve_part_3(input: &str) -> String {
  let re = Regex::new(r"A=\[([-0-9]+),([-0-9]+)\]").unwrap();
  let (_, [x, y]) = re.captures(input).unwrap().extract();
  let a = Number(x.parse().unwrap(), y.parse().unwrap());
  let mut engraved_points = 0;
  for i in 0..=1000 {
    for j in 0..=1000 {
      let pt = Number(a.0 + i, a.1 + j);
      let mut res = Number(0, 0);
      engraved_points += 1;
      for _ in 0..100 {
        res = res.mul(&res);
        res = res.div(&Number(100_000, 100_000));
        res = res.add(&pt);
        if res.0.abs() > 1_000_000 || res.1.abs() > 1_000_000 {
          engraved_points -= 1;
          break;
        }
      }
    }
  }
  engraved_points.to_string()
}

Comment on

🦆 Everybody.Codes 2025 Quest 5 Solutions 🦆

Rust

use itertools::Itertools;

type Fishbone = Vec<(i64, Option<i64>, Option<i64>)>;

fn parse_fishbone(quality_str: &str) -> Fishbone {
    let mut fishbone: Fishbone = vec![];
    'outer: for num in quality_str.split(",").map(|x| x.parse().unwrap()) {
        for e in fishbone.iter_mut() {
            if num < e.0 && e.1.is_none() {
                e.1 = Some(num);
                continue 'outer;
            }
            if num > e.0 && e.2.is_none() {
                e.2 = Some(num);
                continue 'outer;
            }
        }
        fishbone.push((num, None, None));
    }
    fishbone
}

fn compute_quality(fishbone: &Fishbone) -> i64 {
    fishbone
        .iter()
        .map(|(c, _, _)| c.to_string())
        .join("")
        .parse()
        .unwrap()
}

pub fn solve_part_1(input: &str) -> String {
    let (_, data) = input.split_once(":").unwrap();
    compute_quality(&parse_fishbone(data)).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let mut worst_quality = i64::MAX;
    let mut best_quality = i64::MIN;
    for sword in input.lines() {
        let (_, data) = sword.split_once(":").unwrap();
        let quality = compute_quality(&parse_fishbone(data));
        worst_quality = worst_quality.min(quality);
        best_quality = best_quality.max(quality);
    }
    (best_quality - worst_quality).to_string()
}

pub fn solve_part_3(input: &str) -> String {
    let mut swords: Vec<_> = input
        .lines()
        .map(|def| {
            let (id, data) = def.split_once(":").unwrap();
            let fishbone = parse_fishbone(data);
            (id.parse::<i64>().unwrap(), fishbone)
        })
        .collect();
    swords.sort_by(|a, b| {
        let cmp = compute_quality(&a.1).cmp(&compute_quality(&b.1));
        if !matches!(cmp, std::cmp::Ordering::Equal) {
            return cmp;
        }
        for (a_seg, b_seg) in a.1.iter().zip(b.1.iter()) {
            let a_val = match a_seg {
                (a, Some(b), Some(c)) => format!("{b}{a}{c}"),
                (a, Some(b), None) => format!("{b}{a}"),
                (a, None, Some(c)) => format!("{a}{c}"),
                (a, None, None) => format!("{a}"),
            };
            let b_val = match b_seg {
                (a, Some(b), Some(c)) => format!("{b}{a}{c}"),
                (a, Some(b), None) => format!("{b}{a}"),
                (a, None, Some(c)) => format!("{a}{c}"),
                (a, None, None) => format!("{a}"),
            };
            let cmp = a_val.parse::<i64>().unwrap().cmp(&b_val.parse().unwrap());
            if !matches!(cmp, std::cmp::Ordering::Equal) {
                return cmp;
            }
        }
        a.0.cmp(&b.0)
    });
    swords.reverse();
    swords
        .into_iter()
        .enumerate()
        .map(|(pos, (id, _))| id * (pos as i64 + 1))
        .sum::<i64>()
        .to_string()
}