That's a six book series, if I remember rightly. I love Pratchett's stuff (Men at Arms is next on my Discworld reread) but I was thinking of his solo stuff. Normally I just start at the first published if I think I'll enjoy the whole lot.
Using leaf tea instead of bags means it's more forgiving of over brewing. I've always assumed it's because you have large pieces instead of the smaller bits plus dust you get in bags, but I have no idea really.
My rust code ran in 6s on my phone (Samsung A35 running under Termux). When I'm back at a computer it'd be interesting to compare times properly.
Rust
~~Only part 1 because I'm meant to be leaving for a holiday in a few hours and haven't packed yet. Part two looks simple enough to add:~~
part 2 plan
Change seen positions set to include direction, if pos+dir already seen then it's a loop. Test all spaces.
Edit: I did the change on my phone (which was painful).
use std::{collections::HashSet, fs, str::FromStr};
use color_eyre::eyre::{Report, Result};
type GridPosition = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Direction {
N,
E,
S,
W,
}
impl Direction {
fn clockwise(&self) -> Self {
match self {
Direction::N => Direction::E,
Direction::E => Direction::S,
Direction::S => Direction::W,
Direction::W => Direction::N,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Thing {
Guard(Direction),
Obstruction,
Space,
}
#[derive(Debug, PartialEq, Eq)]
struct LabMap {
grid: Vec<Thing>,
width: usize,
height: usize,
}
impl FromStr for LabMap {
type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let grid = s
.chars()
.filter_map(|ch| {
use Thing::*;
match ch {
'^' => Some(Guard(Direction::N)),
'>' => Some(Guard(Direction::E)),
'v' => Some(Guard(Direction::S)),
'<' => Some(Guard(Direction::W)),
'#' => Some(Obstruction),
'.' => Some(Space),
'\n' => None,
_ => unreachable!(),
}
})
.collect::<Vec<_>>();
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 LabMap {
fn neighbour(&self, i: GridPosition, dir: Direction) -> Option<GridPosition> {
let width = self.width;
let length = self.grid.len();
use Direction::*;
match dir {
N if i >= width => Some(i - width),
E if i % width != width - 1 => Some(i + 1),
S if i + width < length => Some(i + width),
W if i % width != 0 => Some(i - 1),
_ => None,
}
}
fn guard_pos(&self) -> Option<(GridPosition, Direction)> {
self.grid
.iter()
.enumerate()
.filter_map(|(pos, &thing)| match thing {
Thing::Guard(dir) => Some((pos, dir)),
_ => None,
})
.next()
}
fn path_len(&self) -> usize {
let mut positions = HashSet::new();
let mut next = self.guard_pos();
while let Some((pos, dir)) = next {
positions.insert(pos);
next = self.neighbour(pos, dir).map(|npos| match self.grid[npos] {
Thing::Space | Thing::Guard(_) => (npos, dir),
Thing::Obstruction => (pos, dir.clockwise()),
});
}
positions.len()
}
fn num_loops(&self) -> usize {
(0..self.grid.len())
.filter(|&pos| matches!(self.grid[pos], Thing::Space))
.map(|pos| {
let mut grid = self.grid.clone();
grid[pos] = Thing::Obstruction;
LabMap {
grid,
width: self.width,
height: self.height,
}
})
.filter(LabMap::is_loop)
.count()
}
fn is_loop(&self) -> bool {
let mut positions = HashSet::new();
let mut next = self.guard_pos();
while let Some((pos, dir)) = next {
let is_new = positions.insert((pos, dir));
if !is_new {
return true;
}
next = self.neighbour(pos, dir).map(|npos| match self.grid[npos] {
Thing::Space | Thing::Guard(_) => (npos, dir),
Thing::Obstruction => (pos, dir.clockwise()),
});
}
false
}
}
fn part1(filepath: &str) -> Result<usize> {
let input = fs::read_to_string(filepath)?;
let map = LabMap::from_str(&input)?;
Ok(map.path_len())
}
fn part2(filepath: &str) -> Result<usize> {
let input = fs::read_to_string(filepath)?;
let map = LabMap::from_str(&input)?;
Ok(map.num_loops())
}
fn main() -> Result<()> {
color_eyre::install()?;
println!("Part 1: {}", part1("input.txt")?);
println!("Part 2: {}", part2("input.txt")?);
Ok(())
}
Ah, you're right - I misunderstood jbrain's point to just be about the "relative to the original" understanding. Guess I'm no smarter than Google's AI.
The ExplainXKCD is great:
In truth, no such spoon is present on the probe, and Europa's icy crust is too thick to be penetrated by a spoon of such size.
The author is either being very tongue-in-cheek or very literal and humourless and I'm enjoying it both ways.
Effectively they took that money and gave it to the execs and shareholders. No way should they be allowed to take more money to pay for these upgrades - why should they be trusted to do the right thing this time around?
Surely there's a difference between, say, black swans being introduced to Britain and rabbits being introduced to Australia? Black swans aren't disrupting the British ecosystem (that I know) whereas rabbits down under are a serious pest.
I disagree that being in favour of something means you agree with everything they do, but for the record, I've been to Palestine several times and I didn't liked what Israel was doing back then, let alone the current disgusting atrocities that Netanyahu's currently perpetuating.
I think a lot of those guides want you to watch everything, whereas much of the extra stuff isn't worth it, including basically all of Crusade. I forget the details, but I'd recommend only a few of the films - I'd rather have quality over quantity and so remember it fondly.
Deebster
0 post score0 comment score
I think just announce it's a thing and let people post as they will. I don't think a daily thread is necessary, for the reason you say but also that individual posts would get more attention - and extra attention is warranted since there's a lot of extra work that goes in to them.