day 24 made quicker, minor improvements

This commit is contained in:
Rüdiger Ludwig 2023-08-13 16:51:24 +02:00
parent 91b264ba69
commit 41b013b5e9
8 changed files with 81 additions and 10170 deletions

View file

@ -4,6 +4,6 @@ These are my solutions for [Advent of Code 2022](https://adventofcode.com/). Tha
If you look at the code and see ways I could improve it, please do not hesitate to contact me. I am always grateful for everything that makes me a better programmer. If you look at the code and see ways I could improve it, please do not hesitate to contact me. I am always grateful for everything that makes me a better programmer.
Also, if you ever look for a programmer and think my code is any good, please also contact me at once. Also, if you ever look for a programmer and think my code is any good, please consider contacting me.
All code is published under the [Unlicense](https://unlicense.org/) All code is published under the [Unlicense](https://unlicense.org/)

5005
rest.txt

File diff suppressed because it is too large Load diff

5001
result.txt

File diff suppressed because it is too large Load diff

View file

@ -96,35 +96,12 @@ where
} }
} }
impl Add<Pos2<i32>> for Direction { impl Add<Direction> for Pos2<i32> {
type Output = Pos2<i32>; type Output = Pos2<i32>;
fn add(self, rhs: Pos2<i32>) -> Self::Output { fn add(self, rhs: Direction) -> Self::Output {
Pos2::add(rhs, self) let rhs: Pos2<i32> = rhs.into();
} self + rhs
}
impl Add<Pos2<i32>> for &Direction {
type Output = Pos2<i32>;
fn add(self, rhs: Pos2<i32>) -> Self::Output {
Pos2::add(rhs, *self)
}
}
impl Add<&Pos2<i32>> for Direction {
type Output = Pos2<i32>;
fn add(self, rhs: &Pos2<i32>) -> Self::Output {
Pos2::add(*rhs, self)
}
}
impl Add<&Pos2<i32>> for &Direction {
type Output = Pos2<i32>;
fn add(self, rhs: &Pos2<i32>) -> Self::Output {
Pos2::add(*rhs, *self)
} }
} }
@ -135,27 +112,3 @@ impl Add<Turn> for Direction {
self.turn(rhs) self.turn(rhs)
} }
} }
impl Add<&Turn> for Direction {
type Output = Self;
fn add(self, rhs: &Turn) -> Direction {
Direction::add(self, *rhs)
}
}
impl Add<Turn> for &Direction {
type Output = Direction;
fn add(self, rhs: Turn) -> Direction {
Direction::add(*self, rhs)
}
}
impl Add<&Turn> for &Direction {
type Output = Direction;
fn add(self, rhs: &Turn) -> Direction {
Direction::add(*self, *rhs)
}
}

View file

@ -1,3 +1,5 @@
#![allow(dead_code)]
use super::direction::Direction; use super::direction::Direction;
use super::{abs::Abs, math::gcd}; use super::{abs::Abs, math::gcd};
use num_traits::{CheckedAdd, CheckedSub, Float, Num, NumCast, Signed, Zero}; use num_traits::{CheckedAdd, CheckedSub, Float, Num, NumCast, Signed, Zero};
@ -157,17 +159,27 @@ where
} }
} }
impl<T, P: Into<Pos2<T>>> Add<P> for Pos2<T> impl<T> Add for Pos2<T>
where where
T: Num + Copy, T: Num + Copy,
{ {
type Output = Self; type Output = Self;
fn add(self, rhs: P) -> Self::Output {
let rhs = rhs.into(); fn add(self, rhs: Self) -> Self::Output {
Pos2::new(self.x() + rhs.x(), self.y() + rhs.y()) Pos2::new(self.x() + rhs.x(), self.y() + rhs.y())
} }
} }
impl<T> Add<(T, T)> for Pos2<T>
where
T: Num + Copy,
{
type Output = Self;
fn add(self, rhs: (T, T)) -> Self::Output {
Pos2::new(self.x() + rhs.0, self.y() + rhs.1)
}
}
impl<T, P: Into<Pos2<T>>> AddAssign<P> for Pos2<T> impl<T, P: Into<Pos2<T>>> AddAssign<P> for Pos2<T>
where where
T: AddAssign<T> + Copy, T: AddAssign<T> + Copy,

View file

@ -14,11 +14,7 @@ pub const NEG_Z: UnitVector = UnitVector(Pos3::new(0, 0, -1));
impl UnitVector { impl UnitVector {
pub fn new(vector: Pos3<i8>) -> Option<Self> { pub fn new(vector: Pos3<i8>) -> Option<Self> {
if vector.is_unit() { vector.is_unit().then(|| UnitVector(vector))
Some(UnitVector(vector))
} else {
None
}
} }
pub fn x(self) -> i8 { pub fn x(self) -> i8 {

View file

@ -1,12 +1,7 @@
use std::{
collections::{BinaryHeap, HashSet},
str::FromStr,
};
use crate::common::{direction::Direction, file::split_lines, math::lcm, pos2::Pos2};
use super::template::{DayTrait, ResultType}; use super::template::{DayTrait, ResultType};
use crate::common::{direction::Direction, file::split_lines, math::lcm, pos2::Pos2};
use itertools::Itertools; use itertools::Itertools;
use std::{collections::HashSet, str::FromStr};
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = 24; const DAY_NUMBER: usize = 24;
@ -74,7 +69,7 @@ struct Valley {
storm: Storm, storm: Storm,
} }
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
struct State { struct State {
time: Time, time: Time,
trip: Trip, trip: Trip,
@ -83,57 +78,28 @@ struct State {
target: Pos2<usize>, target: Pos2<usize>,
} }
impl Eq for State {}
impl PartialEq for State {
fn eq(&self, other: &Self) -> bool {
self.time == other.time
}
}
impl PartialOrd for State {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(&other))
}
}
impl Ord for State {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.time.cmp(&other.time).reverse()
}
}
impl State { impl State {
fn new(start: Pos2<usize>, target: Pos2<usize>) -> Self { fn new(entry: Pos2<usize>, exit: Pos2<usize>) -> Self {
Self { Self {
time: Time(0), time: Time(0),
trip: Trip(0), trip: Trip(0),
position: start, position: entry,
start, start: entry,
target, target: exit,
} }
} }
fn wait(&self) -> Self { fn hit_target(&self) -> Self {
Self { Self {
time: self.time.inc(), time: self.time.inc(),
trip: self.trip, trip: self.trip.inc(),
position: self.position, position: self.target,
start: self.start, start: self.target,
target: self.target, target: self.start,
} }
} }
fn walk_to(&self, position: Pos2<usize>) -> Self { fn walk_to(&self, position: Pos2<usize>) -> Self {
if position == self.target {
Self {
time: self.time.inc(),
trip: self.trip.inc(),
position,
start: self.target,
target: self.start,
}
} else {
Self { Self {
time: self.time.inc(), time: self.time.inc(),
trip: self.trip, trip: self.trip,
@ -142,25 +108,24 @@ impl State {
target: self.target, target: self.target,
} }
} }
}
fn next_states(&self, valley: &Valley) -> Vec<State> { fn next_states(&self, valley: &Valley) -> Vec<State> {
let frame = valley.storm.get(self.time.inc()); let frame = valley.storm.get(self.time.inc());
let mut states = Vec::new(); let mut states = Vec::with_capacity(4);
let mut dir = Direction::East; let mut dir = Direction::East;
for _ in 0..4 { for _ in 0..4 {
if let Some(next_pos) = self.position.check_add(dir) { if let Some(next_pos) = self.position.check_add(dir) {
if frame.is_empty(&next_pos) || next_pos == self.target { if next_pos == self.target {
states.push(self.walk_to(next_pos)) states.push(self.hit_target());
} else if frame.is_accessible(&next_pos) {
states.push(self.walk_to(next_pos));
} }
} }
dir = dir.turn_left(); dir = dir.turn_left();
} }
if frame.is_empty(&self.position) if frame.is_accessible(&self.position) || self.position == self.start {
|| (states.is_empty() && (self.position == self.target || self.position == self.start)) states.push(self.walk_to(self.position));
{
states.push(self.wait());
} }
states states
} }
@ -237,29 +202,26 @@ impl Valley {
} }
fn cross(&self, trips: Trip) -> Result<usize, BlizzardError> { fn cross(&self, trips: Trip) -> Result<usize, BlizzardError> {
let start = State::new(self.get_entry(), self.get_exit());
let mut queue = BinaryHeap::new();
queue.push(start);
let mut seen = HashSet::new(); let mut seen = HashSet::new();
let mut advanced = Trip(0);
let start = State::new(self.get_entry(), self.get_exit());
let mut queue = vec![start];
let mut next_queue = vec![];
while let Some(current) = queue.pop() { while let Some(current) = queue.pop() {
if current.trip == trips { for next_state in current.next_states(&self) {
return Ok(current.time.get()); if next_state.trip == trips {
} return Ok(next_state.time.get());
if advanced > current.trip.inc() {
continue;
}
advanced = advanced.max(current.trip);
let normalized_time = self.normalize_time(current.time);
let fingerprint = (normalized_time, current.trip, current.position);
if seen.contains(&fingerprint) {
continue;
} }
let fingerprint = (next_state.position, next_state.trip);
if !seen.contains(&fingerprint) {
seen.insert(fingerprint); seen.insert(fingerprint);
next_queue.push(next_state);
queue.extend(current.next_states(&self)); }
}
if queue.is_empty() {
std::mem::swap(&mut queue, &mut next_queue);
seen.clear();
}
} }
Err(BlizzardError::NoPathFound) Err(BlizzardError::NoPathFound)
} }
@ -270,10 +232,6 @@ impl Valley {
fn get_exit(&self) -> Pos2<usize> { fn get_exit(&self) -> Pos2<usize> {
Pos2::new(self.exit, self.height + 1) Pos2::new(self.exit, self.height + 1)
} }
fn normalize_time(&self, time: Time) -> Time {
Time(time.get() % self.storm.len())
}
} }
impl FromStr for Valley { impl FromStr for Valley {
@ -388,7 +346,7 @@ impl Blizzards {
struct Frame(Vec<Vec<bool>>); struct Frame(Vec<Vec<bool>>);
impl Frame { impl Frame {
fn is_empty(&self, pos: &Pos2<usize>) -> bool { fn is_accessible(&self, pos: &Pos2<usize>) -> bool {
if pos.y() == 0 { if pos.y() == 0 {
false false
} else { } else {
@ -405,23 +363,20 @@ struct Storm(Vec<Frame>);
impl Storm { impl Storm {
fn new(blizzards: Blizzards) -> Self { fn new(blizzards: Blizzards) -> Self {
let mut storm = Vec::with_capacity(blizzards.period); let storm = (0..blizzards.period)
for time in 0..blizzards.period { .map(|time| blizzards.frame_at_time(time))
storm.push(blizzards.frame_at_time(time)); .collect_vec();
}
Self(storm) Self(storm)
} }
#[inline] #[inline]
#[allow(dead_code)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }
#[inline] #[inline]
pub fn get(&self, time: Time) -> &Frame { pub fn get(&self, time: Time) -> &Frame {
let index = time.get() % self.0.len(); &self.0[time.get() % self.len()]
&self.0[index]
} }
} }
@ -465,7 +420,10 @@ mod test {
assert_eq!(valley.storm.len(), 5); assert_eq!(valley.storm.len(), 5);
assert_eq!(valley.get_entry(), Pos2::new(0, 0)); assert_eq!(valley.get_entry(), Pos2::new(0, 0));
assert_eq!(valley.get_exit(), Pos2::new(4, 6)); assert_eq!(valley.get_exit(), Pos2::new(4, 6));
assert_eq!(valley.storm.get(Time(3)).is_empty(&Pos2::new(3, 2)), false); assert_eq!(
valley.storm.get(Time(3)).is_accessible(&Pos2::new(3, 2)),
false
);
Ok(()) Ok(())
} }
@ -506,16 +464,7 @@ mod test {
start: Pos2::new(0, 0), start: Pos2::new(0, 0),
target: Pos2::new(5, 5), target: Pos2::new(5, 5),
}; };
let mut next_states = position.next_states(&valley);
next_states.sort();
let expected = vec![ let expected = vec![
State {
time: Time(18),
trip: Trip(1),
position: Pos2::new(5, 5),
start: Pos2::new(5, 5),
target: Pos2::new(0, 0),
},
State { State {
time: Time(18), time: Time(18),
trip: Trip(0), trip: Trip(0),
@ -523,7 +472,15 @@ mod test {
start: Pos2::new(0, 0), start: Pos2::new(0, 0),
target: Pos2::new(5, 5), target: Pos2::new(5, 5),
}, },
State {
time: Time(18),
trip: Trip(1),
position: Pos2::new(5, 5),
start: Pos2::new(5, 5),
target: Pos2::new(0, 0),
},
]; ];
let next_states = position.next_states(&valley);
assert_eq!(next_states, expected); assert_eq!(next_states, expected);
Ok(()) Ok(())
} }

View file

@ -31,12 +31,11 @@ impl DayTrait for Day {
impl Snafu { impl Snafu {
pub fn as_str(&self) -> String { pub fn as_str(&self) -> String {
let value = self.0; if self.0 == 0 {
if value == 0 {
return String::from("0"); return String::from("0");
} }
unfold(value, |value| { unfold(self.0, |value| {
if *value == 0 { if *value == 0 {
return None; return None;
} }
@ -46,7 +45,7 @@ impl Snafu {
}) })
.map(|digit| "=-012".chars().nth(digit).unwrap()) .map(|digit| "=-012".chars().nth(digit).unwrap())
.collect_vec() .collect_vec()
.iter() .into_iter()
.rev() .rev()
.join("") .join("")
} }