Solutions for 2022, as well as 2015-2018 and 2019 up to day 11
This commit is contained in:
commit
1895197c49
722 changed files with 375457 additions and 0 deletions
8
2019/day10_monitoring_station/Cargo.toml
Normal file
8
2019/day10_monitoring_station/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "day10_monitoring_station"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
215
2019/day10_monitoring_station/challenge.txt
Normal file
215
2019/day10_monitoring_station/challenge.txt
Normal file
|
@ -0,0 +1,215 @@
|
|||
You fly into the asteroid belt and reach the Ceres monitoring station. The Elves here have an emergency: they're having trouble tracking all of the asteroids and can't be sure they're safe.
|
||||
|
||||
The Elves would like to build a new monitoring station in a nearby area of space; they hand you a map of all of the asteroids in that region (your puzzle input).
|
||||
|
||||
The map indicates whether each position is empty (`.`) or contains an asteroid (`#`). The asteroids are much smaller than they appear on the map, and every asteroid is exactly in the center of its marked position. The asteroids can be described with `X,Y` coordinates where `X` is the distance from the left edge and `Y` is the distance from the top edge (so the top-left corner is `0,0` and the position immediately to its right is `1,0`).
|
||||
|
||||
Your job is to figure out which asteroid would be the best place to build a *new monitoring station*. A monitoring station can *detect* any asteroid to which it has *direct line of sight* - that is, there cannot be another asteroid *exactly* between them. This line of sight can be at any angle, not just lines aligned to the grid or diagonally. The *best* location is the asteroid that can *detect* the largest number of other asteroids.
|
||||
|
||||
For example, consider the following map:
|
||||
|
||||
```
|
||||
.#..#
|
||||
.....
|
||||
#####
|
||||
....#
|
||||
...##
|
||||
|
||||
```
|
||||
|
||||
The best location for a new monitoring station on this map is the highlighted asteroid at `3,4` because it can detect `8` asteroids, more than any other location. (The only asteroid it cannot detect is the one at `1,0`; its view of this asteroid is blocked by the asteroid at `2,2`.) All other asteroids are worse locations; they can detect `7` or fewer other asteroids. Here is the number of other asteroids a monitoring station on each asteroid could detect:
|
||||
|
||||
```
|
||||
.7..7
|
||||
.....
|
||||
67775
|
||||
....7
|
||||
...87
|
||||
|
||||
```
|
||||
|
||||
Here is an asteroid (`#`) and some examples of the ways its line of sight might be blocked. If there were another asteroid at the location of a capital letter, the locations marked with the corresponding lowercase letter would be blocked and could not be detected:
|
||||
|
||||
```
|
||||
#.........
|
||||
...A......
|
||||
...B..a...
|
||||
.EDCG....a
|
||||
..F.c.b...
|
||||
.....c....
|
||||
..efd.c.gb
|
||||
.......c..
|
||||
....f...c.
|
||||
...e..d..c
|
||||
|
||||
```
|
||||
|
||||
Here are some larger examples:
|
||||
|
||||
* Best is `5,8` with `33` other asteroids detected:
|
||||
|
||||
```
|
||||
......#.#.
|
||||
#..#.#....
|
||||
..#######.
|
||||
.#.#.###..
|
||||
.#..#.....
|
||||
..#....#.#
|
||||
#..#....#.
|
||||
.##.#..###
|
||||
##...#..#.
|
||||
.#....####
|
||||
|
||||
```
|
||||
|
||||
* Best is `1,2` with `35` other asteroids detected:
|
||||
|
||||
```
|
||||
#.#...#.#.
|
||||
.###....#.
|
||||
.#....#...
|
||||
##.#.#.#.#
|
||||
....#.#.#.
|
||||
.##..###.#
|
||||
..#...##..
|
||||
..##....##
|
||||
......#...
|
||||
.####.###.
|
||||
|
||||
```
|
||||
|
||||
* Best is `6,3` with `41` other asteroids detected:
|
||||
|
||||
```
|
||||
.#..#..###
|
||||
####.###.#
|
||||
....###.#.
|
||||
..###.##.#
|
||||
##.##.#.#.
|
||||
....###..#
|
||||
..#.#..#.#
|
||||
#..#.#.###
|
||||
.##...##.#
|
||||
.....#.#..
|
||||
|
||||
```
|
||||
|
||||
* Best is `11,13` with `210` other asteroids detected:
|
||||
|
||||
```
|
||||
.#..##.###...#######
|
||||
##.############..##.
|
||||
.#.######.########.#
|
||||
.###.#######.####.#.
|
||||
#####.##.#.##.###.##
|
||||
..#####..#.#########
|
||||
####################
|
||||
#.####....###.#.#.##
|
||||
##.#################
|
||||
#####.##.###..####..
|
||||
..######..##.#######
|
||||
####.##.####...##..#
|
||||
.#####..#.######.###
|
||||
##...#.##########...
|
||||
#.##########.#######
|
||||
.####.#.###.###.#.##
|
||||
....##.##.###..#####
|
||||
.#.#.###########.###
|
||||
#.#.#.#####.####.###
|
||||
###.##.####.##.#..##
|
||||
|
||||
```
|
||||
|
||||
Find the best location for a new monitoring station. *How many other asteroids can be detected from that location?*
|
||||
|
||||
Your puzzle answer was `263`.
|
||||
|
||||
\--- Part Two ---
|
||||
----------
|
||||
|
||||
Once you give them the coordinates, the Elves quickly deploy an Instant Monitoring Station to the location and discover the worst: there are simply too many asteroids.
|
||||
|
||||
The only solution is *complete vaporization by giant laser*.
|
||||
|
||||
Fortunately, in addition to an asteroid scanner, the new monitoring station also comes equipped with a giant rotating laser perfect for vaporizing asteroids. The laser starts by pointing *up* and always rotates *clockwise*, vaporizing any asteroid it hits.
|
||||
|
||||
If multiple asteroids are *exactly* in line with the station, the laser only has enough power to vaporize *one* of them before continuing its rotation. In other words, the same asteroids that can be *detected* can be vaporized, but if vaporizing one asteroid makes another one detectable, the newly-detected asteroid won't be vaporized until the laser has returned to the same position by rotating a full 360 degrees.
|
||||
|
||||
For example, consider the following map, where the asteroid with the new monitoring station (and laser) is marked `X`:
|
||||
|
||||
```
|
||||
.#....#####...#..
|
||||
##...##.#####..##
|
||||
##...#...#.#####.
|
||||
..#.....X...###..
|
||||
..#.#.....#....##
|
||||
|
||||
```
|
||||
|
||||
The first nine asteroids to get vaporized, in order, would be:
|
||||
|
||||
```
|
||||
.#....###24...#..
|
||||
##...##.13#67..9#
|
||||
##...#...5.8####.
|
||||
..#.....X...###..
|
||||
..#.#.....#....##
|
||||
|
||||
```
|
||||
|
||||
Note that some asteroids (the ones behind the asteroids marked `1`, `5`, and `7`) won't have a chance to be vaporized until the next full rotation. The laser continues rotating; the next nine to be vaporized are:
|
||||
|
||||
```
|
||||
.#....###.....#..
|
||||
##...##...#.....#
|
||||
##...#......1234.
|
||||
..#.....X...5##..
|
||||
..#.9.....8....76
|
||||
|
||||
```
|
||||
|
||||
The next nine to be vaporized are then:
|
||||
|
||||
```
|
||||
.8....###.....#..
|
||||
56...9#...#.....#
|
||||
34...7...........
|
||||
..2.....X....##..
|
||||
..1..............
|
||||
|
||||
```
|
||||
|
||||
Finally, the laser completes its first full rotation (`1` through `3`), a second rotation (`4` through `8`), and vaporizes the last asteroid (`9`) partway through its third rotation:
|
||||
|
||||
```
|
||||
......234.....6..
|
||||
......1...5.....7
|
||||
.................
|
||||
........X....89..
|
||||
.................
|
||||
|
||||
```
|
||||
|
||||
In the large example above (the one with the best monitoring station location at `11,13`):
|
||||
|
||||
* The 1st asteroid to be vaporized is at `11,12`.
|
||||
* The 2nd asteroid to be vaporized is at `12,1`.
|
||||
* The 3rd asteroid to be vaporized is at `12,2`.
|
||||
* The 10th asteroid to be vaporized is at `12,8`.
|
||||
* The 20th asteroid to be vaporized is at `16,0`.
|
||||
* The 50th asteroid to be vaporized is at `16,9`.
|
||||
* The 100th asteroid to be vaporized is at `10,16`.
|
||||
* The 199th asteroid to be vaporized is at `9,6`.
|
||||
* *The 200th asteroid to be vaporized is at `8,2`.*
|
||||
* The 201st asteroid to be vaporized is at `10,9`.
|
||||
* The 299th and final asteroid to be vaporized is at `11,1`.
|
||||
|
||||
The Elves are placing bets on which will be the *200th* asteroid to be vaporized. Win the bet by determining which asteroid that will be; *what do you get if you multiply its X coordinate by `100` and then add its Y coordinate?* (For example, `8,2` becomes *`802`*.)
|
||||
|
||||
Your puzzle answer was `1110`.
|
||||
|
||||
Both parts of this puzzle are complete! They provide two gold stars: \*\*
|
||||
|
||||
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
|
||||
|
||||
If you still want to see it, you can [get your puzzle input](10/input).
|
138
2019/day10_monitoring_station/src/lib.rs
Normal file
138
2019/day10_monitoring_station/src/lib.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct RationalAngle {
|
||||
upper_half: bool,
|
||||
divident: isize,
|
||||
divisor: usize,
|
||||
}
|
||||
|
||||
impl From<(isize, isize)> for RationalAngle {
|
||||
fn from((x, y): (isize, isize)) -> Self {
|
||||
let upper_half = x < 0;
|
||||
let divident = y.signum() * x / gcd(x, y);
|
||||
let divisor = (y / gcd(x, y)).unsigned_abs();
|
||||
|
||||
Self {
|
||||
upper_half,
|
||||
divident,
|
||||
divisor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RationalAngle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self.upper_half, other.upper_half) {
|
||||
(true, false) => Some(Ordering::Greater),
|
||||
(false, true) => Some(Ordering::Less),
|
||||
_ => match (self. divident as f64 / self.divisor as f64) - (other.divident as f64 / other.divisor as f64) {
|
||||
n if n < 0.0 => Some(Ordering::Greater),
|
||||
p if p > 0.0 => Some(Ordering::Less),
|
||||
_ => Some(Ordering::Equal),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for RationalAngle {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(other).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl RationalAngle {
|
||||
fn from_vector(source: (usize, usize), dest: (usize, usize)) -> Self {
|
||||
let x = dest.0 as isize - source.0 as isize;
|
||||
let y = dest.1 as isize - source.1 as isize;
|
||||
Self::from((x, y))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(input: &str) -> (usize, usize) {
|
||||
let asteroids: Vec<(usize, usize)> = input.lines().enumerate().flat_map(|(y, line)| line.chars().enumerate().filter(|(_x, c)| *c == '#').map(|(x, _c)| (x, y)).collect::<Vec<_>>()).collect();
|
||||
// let mut angles: Vec<Vec<(isize, isize)>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| reduced_angle(*a, *other)).collect()).collect();
|
||||
let mut angles: Vec<BTreeSet<RationalAngle>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| RationalAngle::from_vector(*a, *other)).collect()).collect();
|
||||
// for asteroid in angles.iter_mut() {
|
||||
// asteroid.sort_by(rotation_sort);
|
||||
// asteroid.dedup();
|
||||
// }
|
||||
let (idx, first) = angles.iter().enumerate().max_by_key(|(_idx, a)| a.len()).unwrap();
|
||||
let angle = first.iter().nth(199).unwrap();
|
||||
let second = (0, 0);
|
||||
// let second = (1..).map(|i| ((asteroids[idx].0 as isize + i * angle.divident) as usize, (asteroids[idx].1 as isize + i * angle.divisor as isize) as usize))
|
||||
// .find(|a| asteroids.contains(a))
|
||||
// .unwrap();
|
||||
(first.len(), (second.0*100 + second.1))
|
||||
}
|
||||
|
||||
fn rotation_sort(lhs: &(isize, isize), rhs: &(isize, isize)) -> Ordering {
|
||||
if lhs == rhs {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
match (lhs.0.signum(), rhs.0.signum()) {
|
||||
(-1, 1) => Ordering::Greater,
|
||||
(1, -1) => Ordering::Less,
|
||||
(0, -1) =>Ordering::Less,
|
||||
(-1, 0) => Ordering::Greater,
|
||||
(0, 1) => if lhs.1.signum() == 1 { Ordering::Less } else { Ordering::Greater },
|
||||
(1, 0) => if rhs.1.signum() == 1 { Ordering::Greater } else { Ordering::Less },
|
||||
(0, 0) => match (lhs.1.signum(), rhs.1.signum()) {
|
||||
(1, 1) | (-1, -1) => Ordering::Equal,
|
||||
(1, -1) => Ordering::Less,
|
||||
(-1, 1) => Ordering::Greater,
|
||||
_ => panic!("Unable to sort {lhs:?} and {rhs:?}"),
|
||||
},
|
||||
_ => match (lhs.1.signum(), rhs.1.signum()) {
|
||||
(1, 1) | (-1, -1) => match (lhs.0 as f64 / lhs.1 as f64) - (rhs.0 as f64 / rhs.1 as f64) {
|
||||
n if n < 0.0 => Ordering::Greater,
|
||||
p if p > 0.0 => Ordering::Less,
|
||||
_ => panic!("Unexpected sorting of {lhs:?} and {rhs:?}: Equal"),
|
||||
},
|
||||
_ => (lhs.0.signum()*lhs.1.signum()).cmp(&(lhs.0.signum()*rhs.1.signum()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(lhs: isize, rhs: isize) -> isize {
|
||||
if lhs == 0 {
|
||||
return rhs.abs();
|
||||
} else if rhs == 0 {
|
||||
return lhs.abs();
|
||||
}
|
||||
|
||||
let a = lhs.abs();
|
||||
let b = rhs.abs();
|
||||
|
||||
(1..=a.min(b)).rev().find(|i| a%i == 0 && b%i == 0).unwrap()
|
||||
}
|
||||
|
||||
fn reduced_angle(lhs: (usize, usize), rhs: (usize, usize)) -> (isize, isize) {
|
||||
let x = rhs.0 as isize - lhs.0 as isize;
|
||||
let y = rhs.1 as isize - lhs.1 as isize;
|
||||
|
||||
(x/gcd(x,y), y/gcd(x,y))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn read_file(name: &str) -> String {
|
||||
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_sample() {
|
||||
// let sample_input = read_file("tests/sample_input");
|
||||
// assert_eq!(run(&sample_input), (33, 0));
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_challenge() {
|
||||
let challenge_input = read_file("tests/challenge_input");
|
||||
assert_eq!(run(&challenge_input), (263, 1110));
|
||||
}
|
||||
}
|
33
2019/day10_monitoring_station/tests/challenge_input
Normal file
33
2019/day10_monitoring_station/tests/challenge_input
Normal file
|
@ -0,0 +1,33 @@
|
|||
.#..#..##.#...###.#............#.
|
||||
.....#..........##..#..#####.#..#
|
||||
#....#...#..#.......#...........#
|
||||
.#....#....#....#.#...#.#.#.#....
|
||||
..#..#.....#.......###.#.#.##....
|
||||
...#.##.###..#....#........#..#.#
|
||||
..#.##..#.#.#...##..........#...#
|
||||
..#..#.......................#..#
|
||||
...#..#.#...##.#...#.#..#.#......
|
||||
......#......#.....#.............
|
||||
.###..#.#..#...#..#.#.......##..#
|
||||
.#...#.................###......#
|
||||
#.#.......#..####.#..##.###.....#
|
||||
.#.#..#.#...##.#.#..#..##.#.#.#..
|
||||
##...#....#...#....##....#.#....#
|
||||
......#..#......#.#.....##..#.#..
|
||||
##.###.....#.#.###.#..#..#..###..
|
||||
#...........#.#..#..#..#....#....
|
||||
..........#.#.#..#.###...#.....#.
|
||||
...#.###........##..#..##........
|
||||
.###.....#.#.###...##.........#..
|
||||
#.#...##.....#.#.........#..#.###
|
||||
..##..##........#........#......#
|
||||
..####......#...#..........#.#...
|
||||
......##...##.#........#...##.##.
|
||||
.#..###...#.......#........#....#
|
||||
...##...#..#...#..#..#.#.#...#...
|
||||
....#......#.#............##.....
|
||||
#......####...#.....#...#......#.
|
||||
...#............#...#..#.#.#..#.#
|
||||
.#...#....###.####....#.#........
|
||||
#.#...##...#.##...#....#.#..##.#.
|
||||
.#....#.###..#..##.#.##...#.#..##
|
10
2019/day10_monitoring_station/tests/sample_input
Normal file
10
2019/day10_monitoring_station/tests/sample_input
Normal file
|
@ -0,0 +1,10 @@
|
|||
......#.#.
|
||||
#..#.#....
|
||||
..#######.
|
||||
.#.#.###..
|
||||
.#..#.....
|
||||
..#....#.#
|
||||
#..#....#.
|
||||
.##.#..###
|
||||
##...#..#.
|
||||
.#....####
|
Loading…
Add table
Add a link
Reference in a new issue