mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 12:50:32 +01:00
Implement day 19 part 1
By brute force and lots of it
This commit is contained in:
@@ -82,8 +82,9 @@ pub fn ordered<O: PartialOrd>(a: O, b: O) -> (O, O) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_input<P, O>(input: &mut dyn Read, parser: P) -> O
|
pub fn read_input<I, P, O>(mut input: I, parser: P) -> O
|
||||||
where
|
where
|
||||||
|
I: Read,
|
||||||
P: for<'a> FnOnce(&'a [u8]) -> IResult<&'a [u8], O>,
|
P: for<'a> FnOnce(&'a [u8]) -> IResult<&'a [u8], O>,
|
||||||
{
|
{
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
|
|||||||
@@ -1,9 +1,261 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::ops::Add;
|
||||||
|
use std::ops::Sub;
|
||||||
|
|
||||||
pub fn part1(_input: &mut dyn Read) -> String {
|
use nom::bytes::complete::tag;
|
||||||
todo!()
|
use nom::character::complete::newline;
|
||||||
|
use nom::combinator::map;
|
||||||
|
use nom::multi::many1;
|
||||||
|
use nom::multi::separated_list1;
|
||||||
|
use nom::sequence::delimited;
|
||||||
|
use nom::sequence::preceded;
|
||||||
|
use nom::sequence::terminated;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
use nom::IResult;
|
||||||
|
|
||||||
|
use crate::common::read_input;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
|
||||||
|
struct Point3(pub [i32; 3]);
|
||||||
|
|
||||||
|
impl Sub for Point3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Point3([
|
||||||
|
self.0[0] - rhs.0[0],
|
||||||
|
self.0[1] - rhs.0[1],
|
||||||
|
self.0[2] - rhs.0[2],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Point3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Point3([
|
||||||
|
self.0[0] + rhs.0[0],
|
||||||
|
self.0[1] + rhs.0[1],
|
||||||
|
self.0[2] + rhs.0[2],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Rotations<'a> {
|
||||||
|
points: &'a [Point3],
|
||||||
|
axes: [usize; 3],
|
||||||
|
rotation_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Rotations<'a> {
|
||||||
|
const ROTATIONS: [[i32; 3]; 8] = [
|
||||||
|
[1, 1, 1],
|
||||||
|
[1, 1, -1],
|
||||||
|
[1, -1, 1],
|
||||||
|
[1, -1, -1],
|
||||||
|
[-1, 1, 1],
|
||||||
|
[-1, 1, -1],
|
||||||
|
[-1, -1, 1],
|
||||||
|
[-1, -1, -1],
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn new(points: &'a [Point3]) -> Self {
|
||||||
|
Self {
|
||||||
|
points,
|
||||||
|
axes: [0, 1, 2],
|
||||||
|
rotation_index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Rotations<'_> {
|
||||||
|
type Item = Vec<Point3>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.rotation_index >= Self::ROTATIONS.len() {
|
||||||
|
if !next_permutation(&mut self.axes) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.rotation_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let axes = &self.axes;
|
||||||
|
let rot = &Self::ROTATIONS[self.rotation_index];
|
||||||
|
|
||||||
|
let result = self
|
||||||
|
.points
|
||||||
|
.iter()
|
||||||
|
.map(|Point3(coords)| {
|
||||||
|
Point3([
|
||||||
|
coords[axes[0]] * rot[0],
|
||||||
|
coords[axes[1]] * rot[1],
|
||||||
|
coords[axes[2]] * rot[2],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.rotation_index += 1;
|
||||||
|
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_point(input: &[u8]) -> IResult<&[u8], Point3> {
|
||||||
|
use nom::character::complete::char;
|
||||||
|
use nom::character::complete::i32;
|
||||||
|
|
||||||
|
map(
|
||||||
|
tuple((i32, preceded(char(','), i32), preceded(char(','), i32))),
|
||||||
|
|(x, y, z)| Point3([x, y, z]),
|
||||||
|
)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Vec<Point3>>> {
|
||||||
|
use nom::character::complete::i32;
|
||||||
|
let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));
|
||||||
|
|
||||||
|
let parse_scanner = preceded(parse_header, many1(terminated(parse_point, newline)));
|
||||||
|
separated_list1(newline, parse_scanner)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_overlap(correct: &[(Point3, HashSet<Point3>)], candidate: &[Point3]) -> Option<Vec<Point3>> {
|
||||||
|
let mut relative = HashSet::new();
|
||||||
|
for rot in Rotations::new(candidate) {
|
||||||
|
for &start in &rot {
|
||||||
|
relative.clear();
|
||||||
|
|
||||||
|
relative.extend(rot.iter().map(|&other| other - start));
|
||||||
|
|
||||||
|
if let Some((base, _)) = correct.iter().find(|(_, correct_relative)| {
|
||||||
|
correct_relative.intersection(&relative).count() >= 12
|
||||||
|
}) {
|
||||||
|
// Found a solution, build the correct output
|
||||||
|
let translated = relative.drain().map(|point| point + *base).collect();
|
||||||
|
|
||||||
|
return Some(translated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: &mut dyn Read) -> String {
|
||||||
|
let mut scanners = read_input(input, parse_input);
|
||||||
|
|
||||||
|
let mut points: HashSet<_> = scanners[0].iter().copied().collect();
|
||||||
|
|
||||||
|
let mut todo = vec![std::mem::take(&mut scanners[0])];
|
||||||
|
println!("Scanners: {}", scanners.len());
|
||||||
|
|
||||||
|
while let Some(matched) = todo.pop() {
|
||||||
|
if scanners.iter().all(Vec::is_empty) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let relative: Vec<(Point3, HashSet<Point3>)> = matched
|
||||||
|
.iter()
|
||||||
|
.map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for candidate in &mut scanners {
|
||||||
|
if candidate.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(result) = try_overlap(&relative, candidate) {
|
||||||
|
points.extend(result.iter().copied());
|
||||||
|
todo.push(result);
|
||||||
|
candidate.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanners.retain(|s| !s.is_empty());
|
||||||
|
|
||||||
|
points.len().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(_input: &mut dyn Read) -> String {
|
pub fn part2(_input: &mut dyn Read) -> String {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn next_permutation<T: Ord>(list: &mut [T]) -> bool {
|
||||||
|
// Based on: https://en.cppreference.com/w/cpp/algorithm/next_permutation
|
||||||
|
if list.len() <= 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((i, val1)) = list
|
||||||
|
.windows(2)
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.find_map(|(i, window)| (window[0] < window[1]).then(|| (i, &window[0])))
|
||||||
|
{
|
||||||
|
let it2 = list
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.skip(i + 1)
|
||||||
|
.rev()
|
||||||
|
.find_map(|(idx, val2)| (val1 < val2).then(|| idx))
|
||||||
|
.expect("Unreachable, ascending pair exists");
|
||||||
|
|
||||||
|
list.swap(i, it2);
|
||||||
|
list[(i + 1)..].reverse();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
list.reverse();
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::test_implementation;
|
||||||
|
|
||||||
|
const SAMPLE: &[u8] = include_bytes!("samples/19.txt");
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_permutation() {
|
||||||
|
let mut list = [1, 2, 3];
|
||||||
|
|
||||||
|
assert!(next_permutation(&mut list));
|
||||||
|
assert_eq!(list, [1, 3, 2]);
|
||||||
|
|
||||||
|
assert!(next_permutation(&mut list));
|
||||||
|
assert_eq!(list, [2, 1, 3]);
|
||||||
|
|
||||||
|
assert!(next_permutation(&mut list));
|
||||||
|
assert_eq!(list, [2, 3, 1]);
|
||||||
|
|
||||||
|
assert!(next_permutation(&mut list));
|
||||||
|
assert_eq!(list, [3, 1, 2]);
|
||||||
|
|
||||||
|
assert!(next_permutation(&mut list));
|
||||||
|
assert_eq!(list, [3, 2, 1]);
|
||||||
|
|
||||||
|
// Note the negation!
|
||||||
|
assert!(!next_permutation(&mut list));
|
||||||
|
assert_eq!(list, [1, 2, 3]);
|
||||||
|
|
||||||
|
// 24 is a bit too much to write out, but we can check the number
|
||||||
|
let mut list2 = [1, 2, 3, 4];
|
||||||
|
for _ in 1..24 {
|
||||||
|
assert!(next_permutation(&mut list2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be back to the start
|
||||||
|
assert!(!next_permutation(&mut list2));
|
||||||
|
assert_eq!(list2, [1, 2, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_part1() {
|
||||||
|
test_implementation(part1, SAMPLE, 79);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user