[-] Deebster@programming.dev 3 points 10 months ago

Ha, I've got that article half-read in a tab somewhere. Same problem here though - they're not in the standard library for anything I plan to use for AoC.

[-] Deebster@programming.dev 3 points 10 months ago* (last edited 10 months ago)

Now you're just showing off!

Edit: ooh, this makes it obvious that my puzzle input takes more cycles to reach the done state.

[-] Deebster@programming.dev 3 points 10 months ago* (last edited 10 months ago)

Rust

I pulled out some code from last year to make representing 2D grids as a vector easier, so this was quite straightforward. 2.5ms runtime (including reading/parsing the input twice cos of TDD).

impl Grid {
    fn neighbour(&self, i: usize, dir: Direction) -> Option<bool> {
        let width = self.width;
        let length = self.grid.len();

        use Direction::*;
        match dir {
            N if i >= width => Some(i - width),
            NE if i >= width && i % width != width - 1 => Some(i - width + 1),
            E if i % width != width - 1 => Some(i + 1),
            SE if i + width + 1 < length && i % width != width - 1 => Some(i + width + 1),
            S if i + width < length => Some(i + width),
            SW if i + width - 1 < length && !i.is_multiple_of(width) => Some(i + width - 1),
            W if !i.is_multiple_of(width) => Some(i - 1),
            NW if i >= width && !i.is_multiple_of(width) => Some(i - width - 1),
            _ => None,
        };
        .map(|i| self.grid[i])
    }

    #[rustfmt::skip]
    fn cell_accessible(&self, i: usize) -> bool {
        Direction::ALL
            .iter()
            .filter(|&&dir| self.neighbour(i, dir).unwrap_or(false))
            .count() < 4
    }

    fn num_accessible(&self) -> usize {
        self.grid
            .iter()
            .enumerate()
            .filter(|&(i, &is_occupied)| is_occupied && self.cell_accessible(i))
            .count()
    }

    fn remove_accessible(&mut self) -> Option<usize> {
        let removables = self
            .grid
            .iter()
            .enumerate()
            .filter_map(|(i, &is_occupied)| (is_occupied && self.cell_accessible(i)).then_some(i))
            .collect::<Vec<_>>();

        let count = removables.len();
        if count > 0 {
            for idx in removables {
                self.grid[idx] = false;
            }
            Some(count)
        } else {
            None
        }
    }

    fn remove_recursive(&mut self) -> usize {
        let mut total_removed = 0;
        while let Some(removed) = self.remove_accessible() {
            total_removed += removed;
        }
        total_removed
    }
}

::: spoiler Full code


use std::{fs, str::FromStr};

use color_eyre::eyre::{Report, Result};

#[derive(Debug, Copy, Clone)]
enum Direction {
    N, NE, E, SE, S, SW, W, NW,
}

impl Direction {
    const ALL: [Direction; 8] = [
        Direction::N,
        Direction::NE,
        Direction::E,
        Direction::SE,
        Direction::S,
        Direction::SW,
        Direction::W,
        Direction::NW,
    ];
}

#[derive(Debug, PartialEq, Eq, Clone)]
struct Grid {
    grid: Vec<bool>,
    width: usize,
    height: usize,
}

impl FromStr for Grid {
    type Err = Report;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let grid: Vec<_> = s
            .chars()
            .filter_map(|ch| match ch {
                '.' => Some(false),
                '@' => Some(true),
                '\n' => None,
                _ => panic!("Invalid input"),
            })
            .collect();
        let width = s
            .chars()
            .position(|ch| ch == '\n')
            .ok_or_else(|| Report::msg("grid width cannot be zero, or one line"))?;
        let height = grid.len() / width;
        Ok(Self {
            grid,
            width,
            height,
        })
    }
}

impl Grid {
    fn neighbour(&self, i: usize, dir: Direction) -> Option<bool> {
        let width = self.width;
        let length = self.grid.len();

        use Direction::*;
        match dir {
            N if i >= width => Some(i - width),
            NE if i >= width && i % width != width - 1 => Some(i - width + 1),
            E if i % width != width - 1 => Some(i + 1),
            SE if i + width + 1 < length && i % width != width - 1 => Some(i + width + 1),
            S if i + width < length => Some(i + width),
            SW if i + width - 1 < length && !i.is_multiple_of(width) => Some(i + width - 1),
            W if !i.is_multiple_of(width) => Some(i - 1),
            NW if i >= width && !i.is_multiple_of(width) => Some(i - width - 1),
            _ => None,
        };
        .map(|i| self.grid[i])
    }

    #[rustfmt::skip]
    fn cell_accessible(&self, i: usize) -> bool {
        Direction::ALL
            .iter()
            .filter(|&&dir| self.neighbour(i, dir).unwrap_or(false))
            .count() < 4
    }

    fn num_accessible(&self) -> usize {
        self.grid
            .iter()
            .enumerate()
            .filter(|&(i, &is_occupied)| is_occupied && self.cell_accessible(i))
            .count()
    }

    fn remove_accessible(&mut self) -> Option<usize> {
        let removables = self
            .grid
            .iter()
            .enumerate()
            .filter_map(|(i, &is_occupied)| (is_occupied && self.cell_accessible(i)).then_some(i))
            .collect::<Vec<_>>();

        let count = removables.len();
        if count > 0 {
            for idx in removables {
                self.grid[idx] = false;
            }
            Some(count)
        } else {
            None
        }
    }

    fn remove_recursive(&mut self) -> usize {
        let mut total_removed = 0;
        while let Some(removed) = self.remove_accessible() {
            total_removed += removed;
        }
        total_removed
    }
}

fn part1(filepath: &str) -> Result<usize> {
    let input = fs::read_to_string(filepath)?;
    let grid = Grid::from_str(&input)?;
    Ok(grid.num_accessible())
}

fn part2(filepath: &str) -> Result<usize> {
    let input = fs::read_to_string(filepath)?;
    let mut grid = Grid::from_str(&input)?;
    Ok(grid.remove_recursive())
}

fn main() -> Result<()> {
    color_eyre::install()?;

    println!("Part 1: {}", part1("d04/input.txt")?);
    println!("Part 2: {}", part2("d04/input.txt")?);
    Ok(())
}
[-] Deebster@programming.dev 3 points 10 months ago

Maybe? There's some deep wizardry shown in some people's macros so a regex feels fairly basic in comparison.

[-] Deebster@programming.dev 3 points 2 years ago

I've used both and I miss features from each when using the other. Photoshop needs numeric entry for when positioning things.

[-] Deebster@programming.dev 3 points 2 years ago

Do you want something that runs on your NAS or from another computer? What OS(es) are you using?

[-] Deebster@programming.dev 3 points 2 years ago

I'm thinking more like using a CMS or Wordpress by people who don't consider themselves developers.

[-] Deebster@programming.dev 3 points 2 years ago

What's the collective noun for a group of politicians?

[-] Deebster@programming.dev 3 points 2 years ago

You pronounce the t in hot and then pronounce the p of potato?

[-] Deebster@programming.dev 3 points 2 years ago

The commands are notoriously poorly named. The underlying tech is cool and sensible, the text interface isn't.

[-] Deebster@programming.dev 3 points 2 years ago* (last edited 2 years ago)

That's true... kinda makes a mess of my simple model ๐Ÿ˜…

I'll edit in your correction, thanks.

view more: โ€น prev next โ€บ

Deebster

0 post score
0 comment score
joined 3 years ago