hades, hades@programming.dev

Instance: programming.dev
Joined: 8 months ago
Posts: 22
Comments: 58

RSS feed

Posts and Comments by hades, hades@programming.dev

That’s a very interesting thought! I was thinking of writing my own Rust data type that would automatically upgrade to big integer, similarly to the int type in Python.


Just needs a bit of formal languages theory, but otherwise the algorithm is fairly simple. Here’s my code if you decide to look at it at some point: https://github.com/hades/aoc2015/blob/master/dec19.cc



Christmas, I guess :) (I just pick a random colour each time a roll is removed)



Day 7 (zoom to full screen): https://lgbt.earth/i/web/post/903381501524320591

I can probably improve this a lot, but I was afraid I’ll be too lazy after getting this far, so posting as is. Still, lmk if you have any suggestions :)



Apart from day 4 not much to visualise so far this year :/



constexpr u32 TEN = 10;
constexpr u64 TWELVE = 12;

I love the easter egg jokes you add to your code :)


Computer science is just mathematics, you can do it with pen and paper. The actual IT jobs where you don’t have to touch windows are plentiful, although it might be a bit of a red flag if you’re vehemently refusing to touch some specific software (be it windows, or any other program, or programming language).


so many years have passed since i last wrote a line of masm…


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 :)



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


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


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()
    }
}

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


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");
}

RSS feed

Posts by hades, hades@programming.dev

Comments by hades, hades@programming.dev

That’s a very interesting thought! I was thinking of writing my own Rust data type that would automatically upgrade to big integer, similarly to the int type in Python.


Just needs a bit of formal languages theory, but otherwise the algorithm is fairly simple. Here’s my code if you decide to look at it at some point: https://github.com/hades/aoc2015/blob/master/dec19.cc



Christmas, I guess :) (I just pick a random colour each time a roll is removed)



Day 7 (zoom to full screen): https://lgbt.earth/i/web/post/903381501524320591

I can probably improve this a lot, but I was afraid I’ll be too lazy after getting this far, so posting as is. Still, lmk if you have any suggestions :)



Apart from day 4 not much to visualise so far this year :/



constexpr u32 TEN = 10;
constexpr u64 TWELVE = 12;

I love the easter egg jokes you add to your code :)


Computer science is just mathematics, you can do it with pen and paper. The actual IT jobs where you don’t have to touch windows are plentiful, although it might be a bit of a red flag if you’re vehemently refusing to touch some specific software (be it windows, or any other program, or programming language).


so many years have passed since i last wrote a line of masm…


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 :)



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


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


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()
    }
}

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


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");
}