day 24 made quicker, minor improvements
This commit is contained in:
parent
91b264ba69
commit
41b013b5e9
8 changed files with 81 additions and 10170 deletions
|
|
@ -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.
|
||||
|
||||
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/)
|
||||
|
|
|
|||
5001
result.txt
5001
result.txt
File diff suppressed because it is too large
Load diff
|
|
@ -96,35 +96,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl Add<Pos2<i32>> for Direction {
|
||||
impl Add<Direction> for Pos2<i32> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Pos2<i32>> for &Direction {
|
||||
type Output = Pos2<i32>;
|
||||
|
||||
fn add(self, rhs: &Pos2<i32>) -> Self::Output {
|
||||
Pos2::add(*rhs, *self)
|
||||
fn add(self, rhs: Direction) -> Self::Output {
|
||||
let rhs: Pos2<i32> = rhs.into();
|
||||
self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,27 +112,3 @@ impl Add<Turn> for Direction {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use super::direction::Direction;
|
||||
use super::{abs::Abs, math::gcd};
|
||||
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
|
||||
T: Num + Copy,
|
||||
{
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
where
|
||||
T: AddAssign<T> + Copy,
|
||||
|
|
|
|||
|
|
@ -14,11 +14,7 @@ pub const NEG_Z: UnitVector = UnitVector(Pos3::new(0, 0, -1));
|
|||
|
||||
impl UnitVector {
|
||||
pub fn new(vector: Pos3<i8>) -> Option<Self> {
|
||||
if vector.is_unit() {
|
||||
Some(UnitVector(vector))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
vector.is_unit().then(|| UnitVector(vector))
|
||||
}
|
||||
|
||||
pub fn x(self) -> i8 {
|
||||
|
|
|
|||
|
|
@ -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 crate::common::{direction::Direction, file::split_lines, math::lcm, pos2::Pos2};
|
||||
use itertools::Itertools;
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 24;
|
||||
|
|
@ -74,7 +69,7 @@ struct Valley {
|
|||
storm: Storm,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct State {
|
||||
time: Time,
|
||||
trip: Trip,
|
||||
|
|
@ -83,57 +78,28 @@ struct State {
|
|||
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 {
|
||||
fn new(start: Pos2<usize>, target: Pos2<usize>) -> Self {
|
||||
fn new(entry: Pos2<usize>, exit: Pos2<usize>) -> Self {
|
||||
Self {
|
||||
time: Time(0),
|
||||
trip: Trip(0),
|
||||
position: start,
|
||||
start,
|
||||
target,
|
||||
position: entry,
|
||||
start: entry,
|
||||
target: exit,
|
||||
}
|
||||
}
|
||||
|
||||
fn wait(&self) -> Self {
|
||||
fn hit_target(&self) -> Self {
|
||||
Self {
|
||||
time: self.time.inc(),
|
||||
trip: self.trip,
|
||||
position: self.position,
|
||||
start: self.start,
|
||||
target: self.target,
|
||||
trip: self.trip.inc(),
|
||||
position: self.target,
|
||||
start: self.target,
|
||||
target: self.start,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
time: self.time.inc(),
|
||||
trip: self.trip,
|
||||
|
|
@ -142,25 +108,24 @@ impl State {
|
|||
target: self.target,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_states(&self, valley: &Valley) -> Vec<State> {
|
||||
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;
|
||||
for _ in 0..4 {
|
||||
if let Some(next_pos) = self.position.check_add(dir) {
|
||||
if frame.is_empty(&next_pos) || next_pos == self.target {
|
||||
states.push(self.walk_to(next_pos))
|
||||
if next_pos == self.target {
|
||||
states.push(self.hit_target());
|
||||
} else if frame.is_accessible(&next_pos) {
|
||||
states.push(self.walk_to(next_pos));
|
||||
}
|
||||
}
|
||||
dir = dir.turn_left();
|
||||
}
|
||||
if frame.is_empty(&self.position)
|
||||
|| (states.is_empty() && (self.position == self.target || self.position == self.start))
|
||||
{
|
||||
states.push(self.wait());
|
||||
if frame.is_accessible(&self.position) || self.position == self.start {
|
||||
states.push(self.walk_to(self.position));
|
||||
}
|
||||
states
|
||||
}
|
||||
|
|
@ -237,29 +202,26 @@ impl Valley {
|
|||
}
|
||||
|
||||
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 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() {
|
||||
if current.trip == trips {
|
||||
return Ok(current.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;
|
||||
for next_state in current.next_states(&self) {
|
||||
if next_state.trip == trips {
|
||||
return Ok(next_state.time.get());
|
||||
}
|
||||
let fingerprint = (next_state.position, next_state.trip);
|
||||
if !seen.contains(&fingerprint) {
|
||||
seen.insert(fingerprint);
|
||||
|
||||
queue.extend(current.next_states(&self));
|
||||
next_queue.push(next_state);
|
||||
}
|
||||
}
|
||||
if queue.is_empty() {
|
||||
std::mem::swap(&mut queue, &mut next_queue);
|
||||
seen.clear();
|
||||
}
|
||||
}
|
||||
Err(BlizzardError::NoPathFound)
|
||||
}
|
||||
|
|
@ -270,10 +232,6 @@ impl Valley {
|
|||
fn get_exit(&self) -> Pos2<usize> {
|
||||
Pos2::new(self.exit, self.height + 1)
|
||||
}
|
||||
|
||||
fn normalize_time(&self, time: Time) -> Time {
|
||||
Time(time.get() % self.storm.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Valley {
|
||||
|
|
@ -388,7 +346,7 @@ impl Blizzards {
|
|||
struct Frame(Vec<Vec<bool>>);
|
||||
|
||||
impl Frame {
|
||||
fn is_empty(&self, pos: &Pos2<usize>) -> bool {
|
||||
fn is_accessible(&self, pos: &Pos2<usize>) -> bool {
|
||||
if pos.y() == 0 {
|
||||
false
|
||||
} else {
|
||||
|
|
@ -405,23 +363,20 @@ struct Storm(Vec<Frame>);
|
|||
|
||||
impl Storm {
|
||||
fn new(blizzards: Blizzards) -> Self {
|
||||
let mut storm = Vec::with_capacity(blizzards.period);
|
||||
for time in 0..blizzards.period {
|
||||
storm.push(blizzards.frame_at_time(time));
|
||||
}
|
||||
let storm = (0..blizzards.period)
|
||||
.map(|time| blizzards.frame_at_time(time))
|
||||
.collect_vec();
|
||||
Self(storm)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, time: Time) -> &Frame {
|
||||
let index = time.get() % self.0.len();
|
||||
&self.0[index]
|
||||
&self.0[time.get() % self.len()]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +420,10 @@ mod test {
|
|||
assert_eq!(valley.storm.len(), 5);
|
||||
assert_eq!(valley.get_entry(), Pos2::new(0, 0));
|
||||
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(())
|
||||
}
|
||||
|
|
@ -506,16 +464,7 @@ mod test {
|
|||
start: Pos2::new(0, 0),
|
||||
target: Pos2::new(5, 5),
|
||||
};
|
||||
let mut next_states = position.next_states(&valley);
|
||||
next_states.sort();
|
||||
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 {
|
||||
time: Time(18),
|
||||
trip: Trip(0),
|
||||
|
|
@ -523,7 +472,15 @@ mod test {
|
|||
start: Pos2::new(0, 0),
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,11 @@ impl DayTrait for Day {
|
|||
|
||||
impl Snafu {
|
||||
pub fn as_str(&self) -> String {
|
||||
let value = self.0;
|
||||
if value == 0 {
|
||||
if self.0 == 0 {
|
||||
return String::from("0");
|
||||
}
|
||||
|
||||
unfold(value, |value| {
|
||||
unfold(self.0, |value| {
|
||||
if *value == 0 {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -46,7 +45,7 @@ impl Snafu {
|
|||
})
|
||||
.map(|digit| "=-012".chars().nth(digit).unwrap())
|
||||
.collect_vec()
|
||||
.iter()
|
||||
.into_iter()
|
||||
.rev()
|
||||
.join("")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue