I had a similar question a while back and ended up with Voip.ms.
cabhan
When I toured the concentration camp at Dachau some years ago, the tour guide was very clear on this point: people did elect the Nazis.
In 1932, the Nazi party became the largest party in the German parliament, with 37.3% of the vote. It is true that it was not mandatory to make Hitler chancellor, but as the head of the largest party, it would have been expected.
The Nazi party received massive support in democratic elections, where the expectation of the voters would have been that if the Nazi party gained enough seats, Hitler would become chancellor.
This is an important point to me, as it shows that it is possible for democratic elections to result in a fascist government that dismantles democracy. Ignoring this historical example prevents us from applying the lesson to new situations.
Hah. I tried doing some research about what this kind of drain is called, but I have no idea. I've never had a drain like this before, but I guess it must not be too rare?
In my case, the issue is that it starts to stink a lot. We had a plumber out a few years ago, and he opened that thing up and used a plunger to remove a ton of hair. He then suggested we wash it out every now and again, but I haven't been able to do it for a while now, since I can't get that thing open.
Not at all a silly question! I have tried, yes. Also, I've been able to remove it before, but the last time I put it in, I really jammed it in, I guess. There are no threads, so the turning is really just about generating force.
This looks very promising! Have you used something like this before? Most references I see online use this to remove the entire drain, not simply to turn the little thing inside. Is that accurate?
I agree that the cards are great! Have you heard of the Fan Art Pack? It has variants of a bunch of the cards in a different style, which I also quite like.
As St IGNUcious said, proprietary software is the sin. Using vi the penance.
I bought Ghost of Tsushima in one of the recent sales, and I've been enjoying it a great deal, so I'll likely be joining you there.
Rust
I was stuck for a while, even after getting a few hints, until I read the problem more closely and realized: there is only one non-cheating path, and every free space is on it. This means that the target of any shortcut is guaranteed to be on the shortest path to the end.
This made things relatively simple. I used Dijkstra to calculate the distance from the start to each space. I then looked at every pair of points: if they are a valid distance away from each other, check how much time I would save jumping from one to the next. If that amount of time is in the range we want, then this is a valid cheat.
https://gitlab.com/bricka/advent-of-code-2024-rust/-/blob/main/src/days/day20.rs?ref_type=heads
The Code
// Critical point to note: EVERY free space is on the shortest path.
use itertools::Itertools;
use crate::search::dijkstra;
use crate::solver::DaySolver;
use crate::grid::{Coordinate, Grid};
type MyGrid = Grid<MazeElement>;
enum MazeElement {
Wall,
Free,
Start,
End,
}
impl MazeElement {
fn is_free(&self) -> bool {
!matches!(self, MazeElement::Wall)
}
}
fn parse_input(input: String) -> (MyGrid, Coordinate) {
let grid: MyGrid = input.lines()
.map(|line| line.chars().map(|c| match c {
'#' => MazeElement::Wall,
'.' => MazeElement::Free,
'S' => MazeElement::Start,
'E' => MazeElement::End,
_ => panic!("Invalid maze element: {}", c)
})
.collect())
.collect::<Vec<Vec<MazeElement>>>()
.into();
let start_pos = grid.iter().find(|(_, me)| matches!(me, MazeElement::Start)).unwrap().0;
(grid, start_pos)
}
fn solve<R>(grid: &MyGrid, start_pos: Coordinate, min_save_time: usize, in_range: R) -> usize
where R: Fn(Coordinate, Coordinate) -> bool {
let (cost_to, _) = dijkstra(
start_pos,
|&c| grid.orthogonal_neighbors_iter(c)
.filter(|&n| grid[n].is_free())
.map(|n| (n, 1))
.collect()
);
cost_to.keys()
.cartesian_product(cost_to.keys())
.map(|(&c1, &c2)| (c1, c2))
// We don't compare with ourself
.filter(|&(c1, c2)| c1 != c2)
// The two points need to be within range
.filter(|&(c1, c2)| in_range(c1, c2))
// We need to save at least `min_save_time`
.filter(|(c1, c2)| {
// Because we are working with `usize`, the subtraction
// could underflow. So we need to use `checked_sub`
// instead, and check that a) no underflow happened, and
// b) that the time saved is at least the minimum.
cost_to.get(c2).copied()
.and_then(|n| n.checked_sub(*cost_to.get(c1).unwrap()))
.and_then(|n| n.checked_sub(c1.distance_to(c2)))
.map(|n| n >= min_save_time)
.unwrap_or(false)
})
.count()
}
pub struct Day20Solver;
impl DaySolver for Day20Solver {
fn part1(&self, input: String) -> String {
let (grid, start_pos) = parse_input(input);
solve(
&grid,
start_pos,
100,
|c1, c2| c1.distance_to(&c2) == 2,
).to_string()
}
fn part2(&self, input: String) -> String {
let (grid, start_pos) = parse_input(input);
solve(
&grid,
start_pos,
100,
|c1, c2| c1.distance_to(&c2) <= 20,
).to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part1() {
let input = include_str!("../../inputs/test/20");
let (grid, start_pos) = parse_input(input.to_string());
let actual = solve(&grid, start_pos, 1, |c1, c2| c1.distance_to(&c2) == 2);
assert_eq!(44, actual);
}
#[test]
fn test_part2() {
let input = include_str!("../../inputs/test/20");
let (grid, start_pos) = parse_input(input.to_string());
let actual = solve(&grid, start_pos, 50, |c1, c2| c1.distance_to(&c2) <= 20);
assert_eq!(285, actual);
}
}
MY EYES
I finally tried resocketting the cable, and I've only seen the issue a little bit since, but not as extreme. But I'm not sure if it's real or just my perception ;). I'll keep an eye on it. Thanks for the idea!