hades

joined 6 months ago
MODERATOR OF
[–] hades@programming.dev 2 points 3 weeks ago

ah that’s just because i needed rounding towards infinity and not towards zero. In other words i wanted -10/100 to be -1 and not zero. But i couldn’t figure it out on the spot, so i just made it never negative :)

[–] hades@programming.dev 3 points 3 weeks ago

Thanks! I don’t mind tips at all.

[–] hades@programming.dev 3 points 3 weeks ago (2 children)

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

[–] hades@programming.dev 1 points 3 weeks ago

you could say that, yeah, it downloads the inputs, submits the answers, and keeps track of wrong answers. You can grab my entire repository here: https://github.com/hades/aoc25/tree/master

[–] hades@programming.dev 6 points 3 weeks ago (8 children)

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()
    }
}
[–] hades@programming.dev 36 points 3 weeks ago

It uses Gemini on web or else it gets the hose again.

[–] hades@programming.dev 1 points 3 weeks ago

Rust

use std::collections::{HashSet, VecDeque};

use itertools::Itertools;

fn neighbours_of(i: usize, j: usize, side: usize) -> impl Iterator<Item = (usize, usize)> {
    [
        (i, j.wrapping_sub(1)),
        (i, j + 1),
        (
            if (i + j) % 2 == 0 {
                i.wrapping_sub(1)
            } else {
                i + 1
            },
            j,
        ),
    ]
    .into_iter()
    .filter(move |&(i, j)| i < side && j >= i && j < (2 * side - i - 1))
}

pub fn solve_part_1(input: &str) -> String {
    let data = input
        .lines()
        .map(|l| l.chars().collect::<Vec<_>>())
        .collect::<Vec<_>>();
    let side = data.len();
    let mut queue = VecDeque::new();
    queue.push_back((0, 0));
    let mut pairs = 0;
    let mut visited = HashSet::new();
    while let Some((i, j)) = queue.pop_front() {
        if visited.contains(&(i, j)) {
            continue;
        }
        for (ni, nj) in neighbours_of(i, j, side) {
            if visited.contains(&(ni, nj)) {
                continue;
            }
            if data[i][j] == 'T' && data[ni][nj] == 'T' {
                pairs += 1;
            }
            queue.push_back((ni, nj));
        }
        visited.insert((i, j));
    }
    pairs.to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let data = input
        .lines()
        .map(|l| l.chars().collect::<Vec<_>>())
        .collect::<Vec<_>>();
    let side = data.len();
    let mut front = (0..data.len())
        .cartesian_product(0..data[0].len())
        .filter(|&(i, j)| data[i][j] == 'S')
        .collect::<HashSet<_>>();
    let mut visited = HashSet::new();
    let mut steps = 0;
    while !front.is_empty() {
        let mut next_front = HashSet::new();
        for (i, j) in front.drain() {
            if data[i][j] == 'E' {
                return steps.to_string();
            }
            visited.insert((i, j));
            for (ni, nj) in neighbours_of(i, j, side) {
                if (data[ni][nj] == 'T' || data[ni][nj] == 'E') && !visited.contains(&(ni, nj)) {
                    next_front.insert((ni, nj));
                }
            }
        }
        steps += 1;
        front = next_front;
    }
    panic!("exit not found");
}

pub fn rotate(data: &[Vec<char>]) -> Vec<Vec<char>> {
    let mut result = vec![vec!['.'; data[0].len()]; data.len()];
    let side = data.len();
    for i in 0..data.len() {
        for j in i..(data[0].len() - i) {
            if (i + j) % 2 == 0 {
                result[i][j] = data[side - (i + j) / 2 - 1][i * 2 + side - (i + j) / 2 - 1];
            } else {
                result[i][j] = data[side - (i + j).div_ceil(2) - 1][side - (j - i).div_ceil(2) + i];
            }
        }
    }
    result
}

pub fn solve_part_3(input: &str) -> String {
    let data = input
        .lines()
        .map(|l| l.chars().collect::<Vec<_>>())
        .collect::<Vec<_>>();
    let side = data.len();
    let data = [data.clone(), rotate(&data), rotate(&rotate(&data))];
    let mut front = (0..data[0].len())
        .cartesian_product(0..data[0][0].len())
        .filter(|&(i, j)| data[0][i][j] == 'S')
        .map(|(i, j)| (i, j, 0))
        .collect::<HashSet<_>>();
    let mut visited = HashSet::new();
    let mut steps = 0;
    while !front.is_empty() {
        let mut next_front = HashSet::new();
        for (i, j, rotation) in front.drain() {
            if data[rotation][i][j] == 'E' {
                return steps.to_string();
            }
            visited.insert((i, j, rotation));
            let next_rotation = (rotation + 1) % 3;
            for (ni, nj) in neighbours_of(i, j, side) {
                if (data[next_rotation][ni][nj] == 'T' || data[next_rotation][ni][nj] == 'E')
                    && !visited.contains(&(ni, nj, next_rotation))
                {
                    next_front.insert((ni, nj, next_rotation));
                }
            }
            if (data[next_rotation][i][j] == 'T' || data[next_rotation][i][j] == 'E')
                && !visited.contains(&(i, j, next_rotation))
            {
                next_front.insert((i, j, next_rotation));
            }
        }
        steps += 1;
        front = next_front;
    }
    panic!("exit not found");
}
[–] hades@programming.dev 1 points 3 weeks ago

Ah, makes sense. Here's a link to this comment so you can view it on the web: https://programming.dev/post/41427600/20744400

[–] hades@programming.dev 2 points 3 weeks ago

I think the "how can we tell if we've been around the volcano already" is the most interesting part of this puzzle.

[–] hades@programming.dev 2 points 3 weeks ago

Rust

Shortest solution so far.

use std::collections::{BTreeMap};

use interval::{
    IntervalSet,
    ops::Range,
    prelude::{Bounded, Empty, Intersection, Union},
};

pub fn solve_part_1(input: &str) -> String {
    let mut data = BTreeMap::new();
    for v in input.lines().map(|l| {
        l.split(",")
            .map(|v| v.parse().unwrap())
            .collect::<Vec<i64>>()
    }) {
        data.entry(v[0]).or_insert(vec![]).push((v[1], v[2]));
    }
    let mut y_ranges = IntervalSet::new(0, 0);
    let mut x = 0;
    for (wall_x, openings) in data.into_iter() {
        let dx = wall_x - x;
        let mut new_ranges = IntervalSet::empty();
        for interval in y_ranges.into_iter() {
            new_ranges = new_ranges.union(&IntervalSet::new(
                interval.lower() - dx,
                interval.upper() + dx,
            ));
        }
        let mut openings_intervalset = IntervalSet::empty();
        for (opening_start, opening_size) in openings {
            openings_intervalset = openings_intervalset.union(&IntervalSet::new(
                opening_start,
                opening_start + opening_size - 1,
            ));
        }
        y_ranges = new_ranges.intersection(&openings_intervalset);
        x = wall_x;
    }
    let y = y_ranges
        .iter()
        .flat_map(|i| (i.lower()..=i.upper()))
        .find(|y| y % 2 == x % 2)
        .unwrap();
    ((y + x) / 2).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    solve_part_1(input)
}
pub fn solve_part_3(input: &str) -> String {
    solve_part_1(input)
}
[–] hades@programming.dev 1 points 3 weeks ago

Rust

use regex::Regex;
use z3::{
    Optimize, Params,
    ast::{Bool, Int},
};

#[derive(Default)]
struct Plant {
    thickness: i64,
    free: Option<i64>,
    connected: Vec<(usize, i64)>,
}

fn parse_plant_spec(input: &str) -> Plant {
    let mut result = Plant::default();
    let first_re = Regex::new(r"Plant \d+ with thickness (\d+):").unwrap();
    let free_re = Regex::new(r"- free branch with thickness (\d+)").unwrap();
    let branch_re = Regex::new(r"- branch to Plant (\d+) with thickness (-?\d+)").unwrap();
    for line in input.lines() {
        if let Some((_, [thickness])) = first_re.captures(line).map(|c| c.extract()) {
            result.thickness = thickness.parse().unwrap();
        } else if let Some((_, [thickness])) = free_re.captures(line).map(|c| c.extract()) {
            result.free = Some(thickness.parse().unwrap());
        } else if let Some((_, [plant, thickness])) = branch_re.captures(line).map(|c| c.extract())
        {
            result
                .connected
                .push((plant.parse().unwrap(), thickness.parse().unwrap()));
        } else {
            panic!("cannot parse line: {line}");
        }
    }
    result
}

fn eval_plant(plants: &[Plant], number: usize, free_branches: &[i64]) -> i64 {
    let plant = &plants[number - 1];
    if plant.free.is_some() {
        assert_eq!(1, plant.thickness);
        assert_eq!(1, plant.free.unwrap());
        free_branches[number - 1]
    } else {
        let incoming = plant
            .connected
            .iter()
            .map(|&(plant_number, branch_thickness)| {
                eval_plant(plants, plant_number, free_branches) * branch_thickness
            })
            .sum::<i64>();
        if incoming >= plant.thickness {
            incoming
        } else {
            0
        }
    }
}

pub fn solve_part_1(input: &str) -> String {
    let plants = input
        .split("\n\n")
        .map(parse_plant_spec)
        .collect::<Vec<_>>();
    eval_plant(&plants, plants.len(), &vec![1; plants.len()]).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let (plants, tests) = input.split_once("\n\n\n").unwrap();
    let plants = plants
        .split("\n\n")
        .map(parse_plant_spec)
        .collect::<Vec<_>>();
    tests
        .lines()
        .map(|test| {
            eval_plant(
                &plants,
                plants.len(),
                &test
                    .split(" ")
                    .map(|v| v.parse().unwrap())
                    .collect::<Vec<i64>>(),
            )
        })
        .sum::<i64>()
        .to_string()
}

fn eval_plant_z3(plants: &[Plant], number: usize, free_branches: &[Option<Bool>]) -> Int {
    let plant = &plants[number - 1];
    if plant.free.is_some() {
        assert_eq!(1, plant.thickness);
        assert_eq!(1, plant.free.unwrap());
        free_branches[number - 1]
            .as_ref()
            .unwrap()
            .ite(&Int::from_i64(1), &Int::from_i64(0))
    } else {
        let incoming = plant
            .connected
            .iter()
            .map(|&(plant_number, branch_thickness)| {
                eval_plant_z3(plants, plant_number, free_branches) * Int::from_i64(branch_thickness)
            })
            .reduce(|a, b| a + b);
        let incoming = incoming.unwrap_or_else(|| Int::from_i64(0));
        incoming
            .ge(Int::from_i64(plant.thickness))
            .ite(&incoming, &Int::from_i64(0))
    }
}

fn maximum_achievable_brightness(plants: &[Plant]) -> i64 {
    let mut free_branches = vec![None; plants.len()];
    plants.iter().enumerate().for_each(|(i, p)| {
        if p.free.is_some() {
            free_branches[i] = Some(Bool::fresh_const("free"));
        }
    });
    let solver = Optimize::new();
    let mut params = Params::new();
    params.set_symbol("opt.maxsat_engine", "wmax");
    solver.set_params(&params);
    let brightness = eval_plant_z3(plants, plants.len(), &free_branches);
    solver.maximize(&brightness);
    match solver.check(&[]) {
        z3::SatResult::Sat => solver
            .get_model()
            .unwrap()
            .eval(&brightness, true)
            .unwrap()
            .as_i64()
            .unwrap(),
        _ => panic!("unsat"),
    }
}

pub fn solve_part_3(input: &str) -> String {
    let (plants, tests) = input.split_once("\n\n\n").unwrap();
    let plants = plants
        .split("\n\n")
        .map(parse_plant_spec)
        .collect::<Vec<_>>();
    let maximum = maximum_achievable_brightness(&plants);
    tests
        .lines()
        .map(|test| {
            eval_plant(
                &plants,
                plants.len(),
                &test
                    .split(" ")
                    .map(|v| v.parse().unwrap())
                    .collect::<Vec<i64>>(),
            )
        })
        .map(|v| if v > 0 { maximum - v } else { 0 })
        .sum::<i64>()
        .to_string()
}
[–] hades@programming.dev 1 points 3 weeks ago

Rust

use std::{
    cmp::Reverse,
    collections::{HashMap, HashSet},
    f64::consts::PI,
};

use itertools::Itertools;
use libm::atan2;
use priority_queue::PriorityQueue;

fn l2(i: usize, j: usize) -> usize {
    i * i + j * j
}

fn erupt(data: &[Vec<char>], vi: usize, vj: usize, r: usize) -> usize {
    let r2 = r * r;
    data.iter()
        .enumerate()
        .flat_map(|(i, l)| {
            l.iter()
                .enumerate()
                .filter(move |&(j, v)| *v != '@' && l2(vi.abs_diff(i), vj.abs_diff(j)) <= r2)
        })
        .map(|(_, v)| (*v as u8 - b'0') as usize)
        .sum::<usize>()
}

fn find(data: &[Vec<char>], c: char) -> (usize, usize) {
    data.iter()
        .enumerate()
        .flat_map(|(i, l)| {
            l.iter()
                .enumerate()
                .filter(|&(_, v)| *v == c)
                .map(move |(j, _)| (i, j))
        })
        .exactly_one()
        .unwrap()
}

pub fn solve_part_1(input: &str) -> String {
    let data = input
        .lines()
        .map(|l| l.chars().collect::<Vec<_>>())
        .collect::<Vec<_>>();
    let (vi, vj) = find(&data, '@');
    erupt(&data, vi, vj, 10).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let data = input
        .lines()
        .map(|l| l.chars().collect::<Vec<_>>())
        .collect::<Vec<_>>();
    let (vi, vj) = find(&data, '@');
    let h = data.len();
    let w = data[0].len();
    let max_r = vi.max(vj).max(h - vi - 1).max(w - vj - 1);
    let mut last_eruption = 0;
    let mut max_eruption = 0;
    let mut max_eruption_r = 0;
    for r in 1..=max_r {
        let eruption = erupt(&data, vi, vj, r);
        let de = eruption - last_eruption;
        if de > max_eruption {
            max_eruption = de;
            max_eruption_r = r;
        }
        last_eruption = eruption;
    }
    (max_eruption_r * max_eruption).to_string()
}

pub fn solve_part_3(input: &str) -> String {
    let data = input
        .lines()
        .map(|l| l.chars().collect::<Vec<_>>())
        .collect::<Vec<_>>();
    let width = data[0].len();
    let height = data.len();
    let (vi, vj) = find(&data, '@');
    let (si, sj) = find(&data, 'S');

    let azimuth = |i: usize, j: usize| atan2(i as f64 - vi as f64, j as f64 - vj as f64);

    let small_rot = |az1: f64, az2: f64| {
        let d = az1 - az2;
        if d > PI {
            d - 2. * PI
        } else if d < -PI {
            d + 2. * PI
        } else {
            d
        }
    };

    let solve = |radius: usize| {
        let r2 = radius * radius;
        let time_limit = ((radius + 1) * 30) as i64;
        let mut queue = PriorityQueue::new();
        let mut rotations = HashMap::new();
        rotations.insert((si, sj, false), 0f64);
        let mut visited = HashSet::new();
        queue.push((si, sj, false), Reverse(0));
        while let Some(((i, j, rotated), Reverse(time))) = queue.pop() {
            if time >= time_limit {
                break;
            }
            visited.insert((i, j, rotated));
            let az = azimuth(i, j);
            let rotation = rotations[&(i, j, rotated)];
            for (di, dj) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
                let (ni, nj) = (i.wrapping_add_signed(di), j.wrapping_add_signed(dj));
                if ni >= height || nj >= width {
                    continue;
                }
                if l2(ni.abs_diff(vi), nj.abs_diff(vj)) <= r2 {
                    continue;
                }
                let is_rotated = if let Some(previous_rotation) = rotations.get(&(ni, nj, false)) {
                    let rotation = rotation + small_rot(azimuth(ni, nj), az);
                    (rotation - previous_rotation).abs() > 6.
                } else {
                    false
                };
                if (ni, nj, is_rotated) == (si, sj, true) {
                    return Some(time);
                }
                if visited.contains(&(ni, nj, is_rotated)) {
                    continue;
                }
                let new_time: i64 = time + (data[ni][nj] as i8 - '0' as i8) as i64;
                let should_update =
                    match queue.push_increase((ni, nj, is_rotated), Reverse(new_time)) {
                        None => true,
                        Some(Reverse(t)) => t > new_time,
                    };
                if should_update {
                    rotations.insert(
                        (ni, nj, is_rotated),
                        rotation + small_rot(azimuth(ni, nj), az),
                    );
                };
            }
        }
        None
    };
    let (radius, time) = (1..(width.min(height) / 2))
        .map(|radius| (radius, solve(radius)))
        .filter(|(_, s)| s.is_some())
        .min()
        .unwrap();
    (radius as i64 * time.unwrap()).to_string()
}
 

Quest 8: The Art of Connection

  • 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/

 

Quest 7: Namegraph

  • 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/

 

Quest 6: Mentorship Matrix

  • 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/

 

Quest 5: Fishbone Order

  • 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/

 

Quest 4: Teeth of the Wind

  • 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/

 

Quest 3: The Deepest Fit

  • 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/

Also, don't wait for me to make these posts, feel free to post yourself :)

 

Quest 2: From Complex to Clarity

  • 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/

 

Quest 1: Whispers in the Shell

  • 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/

 

I was solving old Advent of Code the other day, and someone suggested to me another seasonal coding challenge website: https://everybody.codes/home

As there are many people here who are actively solving AoC, I'm sure you'd be interested in that too. However, I'm not sure which community we should be using to discuss these? I'm seeing three options:

  • use this community for everybody.codes specifically (as it has largely the same format as AoC, and probably a lot of AoC solvers would be interested in that too),
  • create a dedicated community for everybody.codes (I can do that using this community as a blueprint),
  • use !challenges (the format doesn't really match, but in spirit it kinda makes sense).

WDYT?

 

If you were designing a standard library of a high level language (like Rust, C++, etc.) what would it do if while getting the current time of day (e.g. SystemTime::now()) it encountered a failed kernel system call (e.g. to clock_gettime) and why?

What do you think the existing implementations do?

  • Return error or raise exception
  • Return 0
  • Return undefined values (like stack garbage)
  • Panic/crash/segfault
view more: β€Ή prev next β€Ί