day-24 done
This commit is contained in:
1
2023/Cargo.lock
generated
1
2023/Cargo.lock
generated
@@ -248,6 +248,7 @@ dependencies = [
|
||||
"glam",
|
||||
"itertools",
|
||||
"nom",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -11,3 +11,4 @@ repository.workspace = true
|
||||
nom = { workspace = true }
|
||||
itertools = {workspace = true }
|
||||
glam.workspace = true
|
||||
num = "0.4.1"
|
||||
|
||||
@@ -5,8 +5,9 @@ use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::separated_list1,
|
||||
number,
|
||||
sequence::{separated_pair, tuple},
|
||||
IResult, Parser, number,
|
||||
IResult, Parser,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -23,34 +24,52 @@ impl Stones {
|
||||
// (y_n - y_1)/v_y1 = (x_n - x_1)/v_x1
|
||||
// y_n = v_y1 * (x_n -x_1)/v_x1 + y_1
|
||||
// (v_y1/v_x1) * x_n - x_1 * (v_y1/v_x1) + y_1 = (v_y2/v_x2) * x_n -x_2 * (v_y2/v_x2) + y_2
|
||||
// x_n * ((v_y1/v_x1) - (v_y2/v_x2)) = x_1 * (v_y1/v_x1) - x_2 * (v_y2/v_x2) + y_2 - y_1
|
||||
// x_n * ((v_y1/v_x1) - (v_y2/v_x2)) = x_1 * (v_y1/v_x1) - x_2 * (v_y2/v_x2) + y_2 - y_1
|
||||
// x1 + v1 *t == x2 + v2*t
|
||||
// (x1-x2)/(v2-v1) = t
|
||||
let slope1 = self.velocity.y / self.velocity.x;
|
||||
let slope2 = other.velocity.y / other.velocity.x;
|
||||
let denom = slope1 - slope2;
|
||||
let x = (self.start.x *slope1 -other.start.x *slope2 + other.start.y - self.start.y) / denom;
|
||||
let x =
|
||||
(self.start.x * slope1 - other.start.x * slope2 + other.start.y - self.start.y) / denom;
|
||||
let y = slope1 * (x - self.start.x) + self.start.y;
|
||||
if !x.is_finite() || !y.is_finite() {
|
||||
return None;
|
||||
}
|
||||
let t1 = (x - self.start.x) / self.velocity.x;
|
||||
let t2 = (x - other.start.x) / other.velocity.x;
|
||||
if t1 < 0.0 || t2 < 0.0{
|
||||
if t1 < 0.0 || t2 < 0.0 {
|
||||
return None;
|
||||
}
|
||||
Some(DVec3::new(x, y, 0.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// day 24 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
#[must_use]
|
||||
pub fn part1(input: &str, min:f64, max:f64) -> String {
|
||||
pub fn part1(input: &str, min: f64, max: f64) -> String {
|
||||
let (_, stones) = parse_input(input).expect("Aoc should have valid input");
|
||||
stones.iter().combinations(2)
|
||||
stones
|
||||
.iter()
|
||||
.combinations(2)
|
||||
.filter_map(|pair| {
|
||||
let [a,b] = pair[..] else { return None};
|
||||
a.cross(b).and_then(|cross_position| (cross_position.x <= max && cross_position.y <= max && cross_position.x >= min && cross_position.y >= min).then_some(cross_position) )
|
||||
}).count().to_string()
|
||||
let [a, b] = pair[..] else { return None };
|
||||
a.cross(b).and_then(|cross_position| {
|
||||
(cross_position.x <= max
|
||||
&& cross_position.y <= max
|
||||
&& cross_position.x >= min
|
||||
&& cross_position.y >= min)
|
||||
.then_some(cross_position)
|
||||
})
|
||||
})
|
||||
.count()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_tuple(input: &str) -> IResult<&str, DVec3> {
|
||||
@@ -90,4 +109,3 @@ mod test {
|
||||
assert_eq!(result, "2".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,278 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::similar_names)]
|
||||
|
||||
use glam::I64Vec3;
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::separated_list1,
|
||||
sequence::{separated_pair, tuple},
|
||||
IResult, Parser,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use num::rational::Ratio;
|
||||
|
||||
type Cord = Ratio<i128>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Stones {
|
||||
pub start: I64Vec3,
|
||||
pub velocity: I64Vec3,
|
||||
}
|
||||
impl Stones {
|
||||
fn cross_xy(&self, other: &Self) -> Option<(Cord, Cord)> {
|
||||
let pax = Cord::from(i128::from(self.start.x));
|
||||
let pay = Cord::from(i128::from(self.start.y));
|
||||
let pbx = Cord::from(i128::from(other.start.x));
|
||||
let pby = Cord::from(i128::from(other.start.y));
|
||||
let vax = Cord::from(i128::from(self.velocity.x));
|
||||
let vay = Cord::from(i128::from(self.velocity.y));
|
||||
let vbx = Cord::from(i128::from(other.velocity.x));
|
||||
let vby = Cord::from(i128::from(other.velocity.y));
|
||||
let slope1 = vay / vax;
|
||||
let slope2 = vby / vbx;
|
||||
let denom = slope1 - slope2;
|
||||
if denom == num::Zero::zero() {
|
||||
return None;
|
||||
}
|
||||
let x = (pax * slope1 - pbx * slope2 + pby - pay) / denom;
|
||||
let y = slope1 * (x - pax) + pay;
|
||||
let t1 = (x - pax) / vax;
|
||||
let t2 = (x - pbx) / vbx;
|
||||
if t1 < num::zero() || t2 < num::zero() {
|
||||
return None;
|
||||
}
|
||||
Some((x, y))
|
||||
}
|
||||
|
||||
fn cross_xz(&self, other: &Self) -> Option<(Cord, Cord)> {
|
||||
let pax = Cord::from(i128::from(self.start.x));
|
||||
let paz = Cord::from(i128::from(self.start.z));
|
||||
let pbx = Cord::from(i128::from(other.start.x));
|
||||
let pbz = Cord::from(i128::from(other.start.z));
|
||||
let vax = Cord::from(i128::from(self.velocity.x));
|
||||
let vaz = Cord::from(i128::from(self.velocity.z));
|
||||
let vbx = Cord::from(i128::from(other.velocity.x));
|
||||
let vbz = Cord::from(i128::from(other.velocity.z));
|
||||
let slope1 = vaz / vax;
|
||||
let slope2 = vbz / vbx;
|
||||
let denom = slope1 - slope2;
|
||||
if denom == num::Zero::zero() {
|
||||
return None;
|
||||
}
|
||||
let x = (pax * slope1 - pbx * slope2 + pbz - paz) / denom;
|
||||
let z = slope1 * (x - pax) + paz;
|
||||
let t1 = (x - pax) / vax;
|
||||
let t2 = (x - pbx) / vbx;
|
||||
if t1 < num::zero() || t2 < num::zero() {
|
||||
return None;
|
||||
}
|
||||
Some((x, z))
|
||||
}
|
||||
fn stone_to_tuples(&self) -> ((Cord, Cord, Cord), (Cord, Cord, Cord)) {
|
||||
let Stones {
|
||||
start:
|
||||
I64Vec3 {
|
||||
x: p_x,
|
||||
y: p_y,
|
||||
z: p_z,
|
||||
},
|
||||
velocity:
|
||||
I64Vec3 {
|
||||
x: v_x,
|
||||
y: v_y,
|
||||
z: v_z,
|
||||
},
|
||||
} = *self;
|
||||
(
|
||||
(
|
||||
Cord::from(i128::from(p_x)),
|
||||
Cord::from(i128::from(p_y)),
|
||||
Cord::from(i128::from(p_z)),
|
||||
),
|
||||
(
|
||||
Cord::from(i128::from(v_x)),
|
||||
Cord::from(i128::from(v_y)),
|
||||
Cord::from(i128::from(v_z)),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// day 24 part 2 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
#[must_use]
|
||||
pub fn part2 (_input: &str) -> String {
|
||||
"Not Finished".to_string()
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, stones) = parse_input(input).expect("Aoc should have valid input");
|
||||
let iteresting_stones = stones
|
||||
.iter()
|
||||
.combinations(2)
|
||||
.filter_map(|pair| {
|
||||
let [a, b] = pair[..] else {
|
||||
return None;
|
||||
};
|
||||
match (a.cross_xy(b), a.cross_xz(b)) {
|
||||
(None, None) => false,
|
||||
(None, Some(_)) | (Some(_), None) => true,
|
||||
(Some((x1, _)), Some((x2, _))) => x1 != x2,
|
||||
}
|
||||
.then_some(*b)
|
||||
})
|
||||
.skip(4)
|
||||
.take(3)
|
||||
.collect::<Vec<_>>();
|
||||
let ((p_ax, p_ay, p_az), (v_ax, v_ay, v_az)) = iteresting_stones[0].stone_to_tuples();
|
||||
let ((p_bx, p_by, p_bz), (v_bx, v_by, v_bz)) = iteresting_stones[1].stone_to_tuples();
|
||||
let ((p_cx, p_cy, p_cz), (v_cx, v_cy, v_cz)) = iteresting_stones[2].stone_to_tuples();
|
||||
|
||||
//setting up the syystem of equations
|
||||
//println!("{v_az} - {v_cz} = {:?}", v_az - v_cz);
|
||||
let mut equations = [
|
||||
[
|
||||
Cord::default(),
|
||||
v_az - v_cz,
|
||||
v_cy - v_ay,
|
||||
Cord::default(),
|
||||
p_cz - p_az,
|
||||
p_ay - p_cy,
|
||||
p_ay * v_az - p_az * v_ay - p_cy * v_cz + p_cz * v_cy,
|
||||
],
|
||||
[
|
||||
v_az - v_cz,
|
||||
Cord::default(),
|
||||
v_cx - v_ax,
|
||||
p_cz - p_az,
|
||||
Cord::default(),
|
||||
p_ax - p_cx,
|
||||
p_ax * v_az - p_az * v_ax - p_cx * v_cz + p_cz * v_cx,
|
||||
],
|
||||
[
|
||||
v_cy - v_ay,
|
||||
v_ax - v_cx,
|
||||
Cord::default(),
|
||||
p_ay - p_cy,
|
||||
p_cx - p_ax,
|
||||
Cord::default(),
|
||||
p_ay * v_ax - p_ax * v_ay - p_cy * v_cx + p_cx * v_cy,
|
||||
],
|
||||
[
|
||||
Cord::default(),
|
||||
v_bz - v_cz,
|
||||
v_cy - v_by,
|
||||
Cord::default(),
|
||||
p_cz - p_bz,
|
||||
p_by - p_cy,
|
||||
p_by * v_bz - p_bz * v_by - p_cy * v_cz + p_cz * v_cy,
|
||||
],
|
||||
[
|
||||
v_bz - v_cz,
|
||||
Cord::default(),
|
||||
v_cx - v_bx,
|
||||
p_cz - p_bz,
|
||||
Cord::default(),
|
||||
p_bx - p_cx,
|
||||
p_bx * v_bz - p_bz * v_bx - p_cx * v_cz + p_cz * v_cx,
|
||||
],
|
||||
[
|
||||
v_cy - v_by,
|
||||
v_bx - v_cx,
|
||||
Cord::default(),
|
||||
p_by - p_cy,
|
||||
p_cx - p_bx,
|
||||
Cord::default(),
|
||||
p_by * v_bx - p_bx * v_by - p_cy * v_cx + p_cx * v_cy,
|
||||
],
|
||||
];
|
||||
|
||||
//println!("{equations:?}");
|
||||
//gausian elimination
|
||||
for i in 0..6 {
|
||||
//need to make sure i,i is not zero
|
||||
let none_zero_index = (i..6)
|
||||
.find(|&row| equations[row][i] != Cord::default())
|
||||
.unwrap();
|
||||
//perform swap if need be
|
||||
if i != none_zero_index {
|
||||
(equations[i], equations[none_zero_index]) = (equations[none_zero_index], equations[i]);
|
||||
}
|
||||
|
||||
// make i,i 1 and adjust the row
|
||||
let current_leader = equations[i][i];
|
||||
equations[i][i] = Cord::from_integer(1);
|
||||
for pos in &mut equations[i][(i + 1)..] {
|
||||
*pos /= current_leader;
|
||||
}
|
||||
|
||||
//gausean elimination
|
||||
for row in (i + 1)..6 {
|
||||
let mult = equations[row][i];
|
||||
if mult != Cord::default() {
|
||||
equations[row][i] = Cord::default();
|
||||
for col in (i + 1)..7 {
|
||||
equations[row][col] -= equations[i][col] * mult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//upper triangle done now to make it identity
|
||||
//but doing a trick to turn col 6
|
||||
for i in (0..6).rev() {
|
||||
for row in 0..i {
|
||||
//equality row
|
||||
equations[row][6] -= equations[i][6] * equations[row][i];
|
||||
equations[row][i] = Cord::default();
|
||||
}
|
||||
}
|
||||
|
||||
equations
|
||||
.iter()
|
||||
.take(3)
|
||||
.map(|x| x[6].to_integer())
|
||||
.sum::<i128>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_tuple(input: &str) -> IResult<&str, I64Vec3> {
|
||||
let (input, x) = complete::i64(input)?;
|
||||
let (input, _) = tuple((tag(","), complete::space0))(input)?;
|
||||
let (input, y) = complete::i64(input)?;
|
||||
let (input, _) = tuple((tag(","), complete::space0))(input)?;
|
||||
let (input, z) = complete::i64(input)?;
|
||||
Ok((input, I64Vec3::new(x, y, z)))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Stones>> {
|
||||
separated_list1(
|
||||
complete::line_ending,
|
||||
separated_pair(
|
||||
parse_tuple,
|
||||
tuple((complete::space0, tag("@"), complete::space0)),
|
||||
parse_tuple,
|
||||
)
|
||||
.map(|(start, velocity)| Stones { start, velocity }),
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "";
|
||||
const INPUT: &str = "19, 13, 30 @ -2, 1, -2
|
||||
18, 19, 22 @ -1, -1, -2
|
||||
20, 25, 34 @ -2, -2, -4
|
||||
12, 31, 28 @ -1, -2, -1
|
||||
20, 19, 15 @ 1, -5, -3";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "Not Finished".to_string());
|
||||
assert_eq!(result, "47".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user