day23 part 2 (with a shortcut that could potentially break some cases)
I'm only using a lower bound on the number of intersections a cube has.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -13,6 +13,7 @@ dependencies = [
|
|||||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gcd 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcd 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ regex = "1.1.0"
|
|||||||
chrono = "0.4.6"
|
chrono = "0.4.6"
|
||||||
itertools = "0.7.11"
|
itertools = "0.7.11"
|
||||||
gcd = "1.1.0"
|
gcd = "1.1.0"
|
||||||
|
lazy_static = "1.2.0"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
aoc_2018::tasks::day24::task1();
|
// aoc_2018::tasks::day24::task1();
|
||||||
// aoc_2018::tasks::day23::task2();
|
aoc_2018::tasks::day23::task2();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::utils;
|
use crate::utils;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use std::collections::BinaryHeap;
|
||||||
|
|
||||||
pub fn task1() {
|
pub fn task1() {
|
||||||
let input = utils::read_file("input/day23.txt");
|
let input = utils::read_file("input/day23.txt");
|
||||||
@@ -13,9 +14,12 @@ pub fn task1() {
|
|||||||
let x = m["x"].parse::<isize>().unwrap();
|
let x = m["x"].parse::<isize>().unwrap();
|
||||||
let y = m["y"].parse::<isize>().unwrap();
|
let y = m["y"].parse::<isize>().unwrap();
|
||||||
let z = m["z"].parse::<isize>().unwrap();
|
let z = m["z"].parse::<isize>().unwrap();
|
||||||
let range = m["range"].parse::<usize>().unwrap();
|
let range = m["range"].parse::<isize>().unwrap();
|
||||||
|
|
||||||
Bot { x, y, z, range }
|
Bot {
|
||||||
|
center: Point::new(x, y, z),
|
||||||
|
range,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -41,110 +45,265 @@ pub fn task2() {
|
|||||||
let x = m["x"].parse::<isize>().unwrap();
|
let x = m["x"].parse::<isize>().unwrap();
|
||||||
let y = m["y"].parse::<isize>().unwrap();
|
let y = m["y"].parse::<isize>().unwrap();
|
||||||
let z = m["z"].parse::<isize>().unwrap();
|
let z = m["z"].parse::<isize>().unwrap();
|
||||||
let range = m["range"].parse::<usize>().unwrap();
|
let range = m["range"].parse::<isize>().unwrap();
|
||||||
|
|
||||||
Bot { x, y, z, range }
|
Bot {
|
||||||
|
center: Point::new(x, y, z),
|
||||||
|
range,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// let r_min = bots.iter().min_by_key(|it| it.range).unwrap().range;
|
let mut heap: BinaryHeap<Candidate> = BinaryHeap::new();
|
||||||
// let r_max = bots.iter().max_by_key(|it| it.range).unwrap().range;
|
heap.push(Candidate {
|
||||||
// println!("Radius min max: {}/{}", r_min, r_max);
|
count: bots.len(),
|
||||||
// let x_min = bots.iter().min_by_key(|it| it.x).unwrap().x;
|
cube: Cube::new(-1 << 32, -1 << 32, -1 << 32, 1 << 33),
|
||||||
// let x_max = bots.iter().max_by_key(|it| it.x).unwrap().x;
|
});
|
||||||
// println!("X range: {}", x_max - x_min);
|
let mut candidate_points: Vec<(Point, usize)> = Vec::new();
|
||||||
// let y_min = bots.iter().min_by_key(|it| it.y).unwrap().y;
|
let mut best_candidate_count = 0;
|
||||||
// let y_max = bots.iter().max_by_key(|it| it.y).unwrap().y;
|
|
||||||
// println!("Y range: {}", y_max - y_min);
|
|
||||||
// let z_min = bots.iter().min_by_key(|it| it.z).unwrap().z;
|
|
||||||
// let z_max = bots.iter().max_by_key(|it| it.z).unwrap().z;
|
|
||||||
// println!("Z range: {}", z_max - z_min);
|
|
||||||
|
|
||||||
let neighbor_counts: Vec<(Bot, usize)> = bots
|
while let Some(Candidate { count, cube }) = heap.pop() {
|
||||||
.iter()
|
// println!("{:?}: {} ({})", cube, count, best_candidate_count);
|
||||||
.flat_map(|bot| bot.corners())
|
if count < best_candidate_count {
|
||||||
.map(|corner| {
|
break;
|
||||||
|
}
|
||||||
|
if cube.len == 1 {
|
||||||
let count = bots
|
let count = bots
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|bot| bot.distance(&corner) <= bot.range)
|
.filter(|bot| bot.center.distance(&cube.base) <= bot.range)
|
||||||
.count();
|
.count();
|
||||||
(corner, count)
|
if count > best_candidate_count {
|
||||||
})
|
candidate_points.push((cube.base, count));
|
||||||
.collect();
|
println!("pushed with {}!", count);
|
||||||
|
best_candidate_count = count;
|
||||||
let max = neighbor_counts.iter().max_by_key(|it| it.1).unwrap().1;
|
}
|
||||||
|
} else {
|
||||||
let start = Bot {
|
for child in cube.children() {
|
||||||
x: 0,
|
heap.push(Candidate {
|
||||||
y: 0,
|
count: bots.iter().filter(|bot| child.intersects(&bot)).count(),
|
||||||
z: 0,
|
cube: child,
|
||||||
range: 0,
|
})
|
||||||
};
|
}
|
||||||
let candidates = neighbor_counts.iter().filter(|it| it.1 == max).count();
|
}
|
||||||
println!("{} points in range of {} bots", candidates, max);
|
}
|
||||||
let candidate = neighbor_counts
|
|
||||||
.iter()
|
|
||||||
.filter(|it| it.1 == max)
|
|
||||||
.min_by_key(|it| it.0.distance(&start));
|
|
||||||
|
|
||||||
|
let origin = Point::new(0, 0, 0);
|
||||||
println!(
|
println!(
|
||||||
"Corner with most bots in range: {:?}",
|
"Found {} candidates - best is {}.",
|
||||||
candidate.unwrap().0.distance(&start)
|
candidate_points.len(),
|
||||||
|
best_candidate_count
|
||||||
);
|
);
|
||||||
|
let best = candidate_points
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, count)| *count == best_candidate_count)
|
||||||
|
.min_by_key(|(point, _)| origin.distance(&point));
|
||||||
|
|
||||||
|
println!("{:?}", best);
|
||||||
|
if let Some((best, _)) = best {
|
||||||
|
println!("{}", best.x + best.y + best.z);
|
||||||
|
}
|
||||||
|
|
||||||
// wrong: 37446460,43177892,57318660; 137943012; 102224079;
|
// wrong: 37446460,43177892,57318660; 137943012; 102224079;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
struct Candidate {
|
||||||
|
count: usize,
|
||||||
|
cube: Cube,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Candidate {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.count.cmp(&other.count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Candidate {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.count.cmp(&other.count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Bot {
|
struct Bot {
|
||||||
x: isize,
|
center: Point,
|
||||||
y: isize,
|
range: isize,
|
||||||
z: isize,
|
|
||||||
range: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bot {
|
impl Bot {
|
||||||
fn distance(&self, other: &Self) -> usize {
|
fn distance(&self, other: &Self) -> isize {
|
||||||
((other.x - self.x).abs() + (other.y - self.y).abs() + (other.z - self.z).abs()) as usize
|
self.dist(&other.center)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corners(&self) -> Vec<Self> {
|
fn dist(&self, p: &Point) -> isize {
|
||||||
|
self.center.distance(&p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn corners(&self) -> Vec<Point> {
|
||||||
vec![
|
vec![
|
||||||
Bot {
|
Point {
|
||||||
x: self.x + self.range as isize,
|
x: self.center.x + self.range as isize,
|
||||||
y: self.y,
|
y: self.center.y,
|
||||||
z: self.z,
|
z: self.center.z,
|
||||||
range: 0,
|
|
||||||
},
|
},
|
||||||
Bot {
|
Point {
|
||||||
x: self.x - self.range as isize,
|
x: self.center.x - self.range as isize,
|
||||||
y: self.y,
|
y: self.center.y,
|
||||||
z: self.z,
|
z: self.center.z,
|
||||||
range: 0,
|
|
||||||
},
|
},
|
||||||
Bot {
|
Point {
|
||||||
x: self.x,
|
x: self.center.x,
|
||||||
y: self.y + self.range as isize,
|
y: self.center.y + self.range as isize,
|
||||||
z: self.z,
|
z: self.center.z,
|
||||||
range: 0,
|
|
||||||
},
|
},
|
||||||
Bot {
|
Point {
|
||||||
x: self.x,
|
x: self.center.x,
|
||||||
y: self.y - self.range as isize,
|
y: self.center.y - self.range as isize,
|
||||||
z: self.z,
|
z: self.center.z,
|
||||||
range: 0,
|
|
||||||
},
|
},
|
||||||
Bot {
|
Point {
|
||||||
x: self.x,
|
x: self.center.x,
|
||||||
y: self.y,
|
y: self.center.y,
|
||||||
z: self.z + self.range as isize,
|
z: self.center.z + self.range as isize,
|
||||||
range: 0,
|
|
||||||
},
|
},
|
||||||
Bot {
|
Point {
|
||||||
x: self.x,
|
x: self.center.x,
|
||||||
y: self.y,
|
y: self.center.y,
|
||||||
z: self.z - self.range as isize,
|
z: self.center.z - self.range as isize,
|
||||||
range: 0,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
struct Point {
|
||||||
|
x: isize,
|
||||||
|
y: isize,
|
||||||
|
z: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
fn new(x: isize, y: isize, z: isize) -> Self {
|
||||||
|
Point { x, y, z }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distance(&self, other: &Point) -> isize {
|
||||||
|
(other.x - self.x).abs() + (other.y - self.y).abs() + (other.z - self.z).abs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
struct Cube {
|
||||||
|
base: Point,
|
||||||
|
len: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cube {
|
||||||
|
fn new(x: isize, y: isize, z: isize, len: isize) -> Self {
|
||||||
|
if len < 1 {
|
||||||
|
panic!("The side length of a cube has to be at least 1");
|
||||||
|
}
|
||||||
|
if (len & (len - 1)) != 0 {
|
||||||
|
panic!("The side length has to be a power of two");
|
||||||
|
}
|
||||||
|
Cube {
|
||||||
|
base: Point::new(x, y, z),
|
||||||
|
len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children(&self) -> Vec<Self> {
|
||||||
|
let l = self.len / 2;
|
||||||
|
let x = self.base.x;
|
||||||
|
let y = self.base.y;
|
||||||
|
let z = self.base.z;
|
||||||
|
vec![
|
||||||
|
Cube::new(x + l, y + l, z, l),
|
||||||
|
Cube::new(x + l, y + l, z + l, l),
|
||||||
|
Cube::new(x + l, y, z, l),
|
||||||
|
Cube::new(x + l, y, z + l, l),
|
||||||
|
Cube::new(x, y + l, z, l),
|
||||||
|
Cube::new(x, y + l, z + l, l),
|
||||||
|
Cube::new(x, y, z, l),
|
||||||
|
Cube::new(x, y, z + l, l),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn corners(&self) -> Vec<Point> {
|
||||||
|
vec![
|
||||||
|
Point::new(
|
||||||
|
self.base.x + self.len,
|
||||||
|
self.base.y + self.len,
|
||||||
|
self.base.z + self.len,
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
self.base.x + self.len - 1,
|
||||||
|
self.base.y + self.len - 1,
|
||||||
|
self.base.z,
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
self.base.x + self.len - 1,
|
||||||
|
self.base.y,
|
||||||
|
self.base.z + self.len - 1,
|
||||||
|
),
|
||||||
|
Point::new(self.base.x + self.len - 1, self.base.y, self.base.z),
|
||||||
|
Point::new(
|
||||||
|
self.base.x,
|
||||||
|
self.base.y + self.len - 1,
|
||||||
|
self.base.z + self.len - 1,
|
||||||
|
),
|
||||||
|
Point::new(self.base.x, self.base.y + self.len - 1, self.base.z),
|
||||||
|
Point::new(self.base.x, self.base.y, self.base.z + self.len - 1),
|
||||||
|
Point::new(self.base.x, self.base.y, self.base.z),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersects(&self, bot: &Bot) -> bool {
|
||||||
|
if self
|
||||||
|
.corners()
|
||||||
|
.iter()
|
||||||
|
.any(|corner| corner.distance(&bot.center) <= bot.range)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if bot
|
||||||
|
.corners()
|
||||||
|
.iter()
|
||||||
|
.any(|corner| self.contains_point(&corner))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARNING this is a conservative check! it omits the case where no
|
||||||
|
// corners of the octahedron/cube are within each other:
|
||||||
|
// +-----+
|
||||||
|
// | |/\
|
||||||
|
// | /| \
|
||||||
|
// +---/-+ \
|
||||||
|
// although this is just a wrong 2D example, in 3D that could happen.
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_point(&self, p: &Point) -> bool {
|
||||||
|
self.base.x <= p.x
|
||||||
|
&& self.base.x + self.len > p.x
|
||||||
|
&& self.base.y <= p.y
|
||||||
|
&& self.base.y + self.len > p.y
|
||||||
|
&& self.base.z <= p.z
|
||||||
|
&& self.base.z + self.len > p.z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn intersection() {
|
||||||
|
use super::*;
|
||||||
|
let cube = Cube::new(0, 0, 0, 16);
|
||||||
|
let bot = Bot {
|
||||||
|
center: Point::new(8, 8, 8),
|
||||||
|
range: 4,
|
||||||
|
};
|
||||||
|
assert!(cube.intersects(&bot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user