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
2016/day13-maze_of_twisty_little_cubicles/Cargo.toml
Normal file
8
2016/day13-maze_of_twisty_little_cubicles/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "day13-maze_of_twisty_little_cubicles"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
61
2016/day13-maze_of_twisty_little_cubicles/challenge.txt
Normal file
61
2016/day13-maze_of_twisty_little_cubicles/challenge.txt
Normal file
|
@ -0,0 +1,61 @@
|
|||
\--- Day 13: A Maze of Twisty Little Cubicles ---
|
||||
----------
|
||||
|
||||
You arrive at the first floor of this new building to discover a much less welcoming environment than the shiny atrium of the last one. Instead, you are in a maze of twisty little cubicles, all alike.
|
||||
|
||||
Every location in this area is addressed by a pair of non-negative integers (`x,y`). Each such coordinate is either a wall or an open space. You can't move diagonally. The cube maze starts at `0,0` and seems to extend infinitely toward *positive* `x` and `y`; negative values are *invalid*, as they represent a location outside the building. You are in a small waiting area at `1,1`.
|
||||
|
||||
While it seems chaotic, a nearby morale-boosting poster explains, the layout is actually quite logical. You can determine whether a given `x,y` coordinate will be a wall or an open space using a simple system:
|
||||
|
||||
* Find `x*x + 3*x + 2*x*y + y + y*y`.
|
||||
* Add the office designer's favorite number (your puzzle input).
|
||||
* Find the [binary representation](https://en.wikipedia.org/wiki/Binary_number) of that sum; count the *number* of [bits](https://en.wikipedia.org/wiki/Bit) that are `1`.
|
||||
* If the number of bits that are `1` is *even*, it's an *open space*.
|
||||
* If the number of bits that are `1` is *odd*, it's a *wall*.
|
||||
|
||||
For example, if the office designer's favorite number were `10`, drawing walls as `#` and open spaces as `.`, the corner of the building containing `0,0` would look like this:
|
||||
|
||||
```
|
||||
0123456789
|
||||
0 .#.####.##
|
||||
1 ..#..#...#
|
||||
2 #....##...
|
||||
3 ###.#.###.
|
||||
4 .##..#..#.
|
||||
5 ..##....#.
|
||||
6 #...##.###
|
||||
|
||||
```
|
||||
|
||||
Now, suppose you wanted to reach `7,4`. The shortest route you could take is marked as `O`:
|
||||
|
||||
```
|
||||
0123456789
|
||||
0 .#.####.##
|
||||
1 .O#..#...#
|
||||
2 #OOO.##...
|
||||
3 ###O#.###.
|
||||
4 .##OO#OO#.
|
||||
5 ..##OOO.#.
|
||||
6 #...##.###
|
||||
|
||||
```
|
||||
|
||||
Thus, reaching `7,4` would take a minimum of `11` steps (starting from your current location, `1,1`).
|
||||
|
||||
What is the *fewest number of steps required* for you to reach `31,39`?
|
||||
|
||||
Your puzzle answer was `90`.
|
||||
|
||||
\--- Part Two ---
|
||||
----------
|
||||
|
||||
*How many locations* (distinct `x,y` coordinates, including your starting location) can you reach in at most `50` steps?
|
||||
|
||||
Your puzzle answer was `135`.
|
||||
|
||||
Both parts of this puzzle are complete! They provide two gold stars: \*\*
|
||||
|
||||
At this point, all that is left is for you to [admire your Advent calendar](/2016).
|
||||
|
||||
Your puzzle input was `1352`.
|
76
2016/day13-maze_of_twisty_little_cubicles/src/lib.rs
Normal file
76
2016/day13-maze_of_twisty_little_cubicles/src/lib.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn run(input: usize, start: (usize, usize), goal: (usize, usize)) -> (usize, usize) {
|
||||
let first = count_steps(start, goal, input);
|
||||
let second = get_all(HashSet::from([start]), HashSet::from([start]), input, 50).len();
|
||||
(first, second)
|
||||
}
|
||||
|
||||
fn get_all(current: HashSet<(usize, usize)>, new_last_step: HashSet<(usize, usize)>, input: usize, steps_left: usize) -> HashSet<(usize, usize)> {
|
||||
if steps_left == 0 {
|
||||
return current;
|
||||
}
|
||||
let new_this_step: HashSet<(usize, usize)> = new_last_step.iter().flat_map(|coord| get_neighbours(*coord, input).into_iter().filter(|neighbour| !current.contains(neighbour))).collect();
|
||||
get_all(current.union(&new_this_step).cloned().collect(), new_this_step, input, steps_left-1)
|
||||
}
|
||||
|
||||
fn is_open_space((x, y): (usize, usize), input: usize) -> bool {
|
||||
(x*x + 3*x + 2*x*y + y + y*y + input).count_ones() % 2 == 0
|
||||
}
|
||||
|
||||
fn get_neighbours(current: (usize, usize), input: usize) -> Vec<(usize, usize)> {
|
||||
let mut res = vec![(current.0, current.1 + 1), (current.0 + 1, current.1)];
|
||||
if current.0 > 0 {
|
||||
res.push((current.0-1, current.1));
|
||||
}
|
||||
if current.1 > 0 {
|
||||
res.push((current.0, current.1-1));
|
||||
}
|
||||
res.into_iter().filter(|&coords| is_open_space(coords, input)).collect()
|
||||
}
|
||||
|
||||
// A* search
|
||||
fn count_steps(start: (usize, usize), goal: (usize, usize), input: usize) -> usize {
|
||||
let mut open_set = HashSet::from([start]);
|
||||
let mut g_scores = HashMap::from([(start, 0)]);
|
||||
let mut f_scores = HashMap::from([(start, start.0.abs_diff(goal.0)+start.1.abs_diff(goal.1))]);
|
||||
while !open_set.is_empty() {
|
||||
let current = *open_set.iter().min_by_key(|c| f_scores.get(c).unwrap()).unwrap();
|
||||
open_set.remove(¤t);
|
||||
let current_g = *g_scores.get(¤t).unwrap();
|
||||
if current == goal {
|
||||
return current_g;
|
||||
}
|
||||
for neighbour in get_neighbours(current, input) {
|
||||
let tentative_g_score = current_g + 1;
|
||||
if g_scores.get(&neighbour).unwrap_or(&usize::MAX) > &tentative_g_score {
|
||||
g_scores.insert(neighbour, tentative_g_score);
|
||||
open_set.insert(neighbour);
|
||||
f_scores.insert(neighbour, tentative_g_score + neighbour.0.abs_diff(goal.0) + neighbour.1.abs_diff(goal.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("No solution found");
|
||||
}
|
||||
|
||||
#[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)[..])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample() {
|
||||
let sample_input = read_file("tests/sample_input").trim().parse().unwrap();
|
||||
assert_eq!(run(sample_input, (1, 1), (7, 4)), (11, 151));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge() {
|
||||
let challenge_input = read_file("tests/challenge_input").trim().parse().unwrap();
|
||||
assert_eq!(run(challenge_input, (1, 1), (31, 39)), (90, 135));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
1352
|
|
@ -0,0 +1 @@
|
|||
10
|
Loading…
Add table
Add a link
Reference in a new issue