Now you're just showing off!

Edit: ooh, this makes it obvious that my puzzle input takes more cycles to reach the done state.
Now you're just showing off!

Edit: ooh, this makes it obvious that my puzzle input takes more cycles to reach the done state.
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(())
}
Maybe? There's some deep wizardry shown in some people's macros so a regex feels fairly basic in comparison.
I've used both and I miss features from each when using the other. Photoshop needs numeric entry for when positioning things.
Do you want something that runs on your NAS or from another computer? What OS(es) are you using?
I'm thinking more like using a CMS or Wordpress by people who don't consider themselves developers.
The link to the web app: https://therealmonte.github.io/
What's the collective noun for a group of politicians?
You pronounce the t in hot and then pronounce the p of potato?
The commands are notoriously poorly named. The underlying tech is cool and sensible, the text interface isn't.
That's true... kinda makes a mess of my simple model ๐
I'll edit in your correction, thanks.
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.