Day 10
This commit is contained in:
parent
9d8c5708fc
commit
dd5792817e
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -2,6 +2,10 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aoc_utils"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "d01t1"
|
||||
version = "0.1.0"
|
||||
|
@ -73,3 +77,14 @@ version = "0.1.0"
|
|||
[[package]]
|
||||
name = "d09t2"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "d10t1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aoc_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "d10t2"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
resolver = "2"
|
||||
members = [
|
||||
"aoc_utils",
|
||||
"d01t1",
|
||||
"d01t2",
|
||||
"d02t1",
|
||||
|
@ -20,4 +21,6 @@ members = [
|
|||
"d08t2",
|
||||
"d09t1",
|
||||
"d09t2",
|
||||
"d10t1",
|
||||
"d10t2",
|
||||
]
|
||||
|
|
8
aoc_utils/Cargo.toml
Normal file
8
aoc_utils/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "aoc_utils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
33
aoc_utils/src/lib.rs
Normal file
33
aoc_utils/src/lib.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
#[must_use]
|
||||
pub fn dec_if_pos(input: usize) -> usize {
|
||||
if input > 0 {
|
||||
input - 1
|
||||
} else {
|
||||
input
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn inc_if_lt(input: usize, max: usize) -> usize {
|
||||
if input < max {
|
||||
input + 1
|
||||
} else {
|
||||
input
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn test_dec() {
|
||||
assert_eq!(dec_if_pos(0), 0);
|
||||
assert_eq!(dec_if_pos(1), 0);
|
||||
}
|
||||
#[test]
|
||||
fn test_inc() {
|
||||
assert_eq!(inc_if_lt(3, 3), 3);
|
||||
assert_eq!(inc_if_lt(2, 3), 3);
|
||||
}
|
||||
}
|
9
d10t1/Cargo.toml
Normal file
9
d10t1/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "d10t1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aoc_utils = { path = "../aoc_utils" }
|
5
d10t1/demo_input.txt
Normal file
5
d10t1/demo_input.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
..F7.
|
||||
.FJ|.
|
||||
SJ.L7
|
||||
|F--J
|
||||
LJ...
|
94
d10t1/src/main.rs
Normal file
94
d10t1/src/main.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use std::fs::read_to_string;
|
||||
|
||||
fn main() {
|
||||
let input = read_to_string("input.txt").unwrap();
|
||||
println!("d10t1: {}", d10t1(&input));
|
||||
}
|
||||
|
||||
pub fn d10t1(input: &str) -> isize {
|
||||
let field = input
|
||||
.lines()
|
||||
.map(str::chars)
|
||||
.map(std::iter::Iterator::collect::<Vec<_>>)
|
||||
.collect::<Vec<_>>();
|
||||
let mut distances = vec![vec![-1; field[0].len()]; field.len()];
|
||||
let mut starting_position: Option<(usize, usize)> = None;
|
||||
for (y, line) in field.iter().enumerate() {
|
||||
for (x, field) in line.iter().enumerate() {
|
||||
if *field == 'S' {
|
||||
starting_position = Some((x, y));
|
||||
distances[y][x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
let starting_position = starting_position.unwrap();
|
||||
traverse(&mut distances, &field, starting_position);
|
||||
*distances.iter().flat_map(|a| a.iter()).max().unwrap()
|
||||
}
|
||||
|
||||
fn traverse(distances: &mut [Vec<isize>], field: &Vec<Vec<char>>, position: (usize, usize)) {
|
||||
let height = field.len() - 1;
|
||||
let width = field[0].len() - 1;
|
||||
let mut locations_to_check = vec![position];
|
||||
while let Some((x, y)) = locations_to_check.pop() {
|
||||
let starting_value = distances[y][x];
|
||||
assert_ne!(
|
||||
starting_value, -1,
|
||||
"traverse should only be called on locations that have already been set."
|
||||
);
|
||||
if x != 0 && (distances[y][x - 1] == -1 || distances[y][x - 1] > starting_value) {
|
||||
match (field[y][x], field[y][x - 1]) {
|
||||
('|' | 'L' | 'F', _) | (_, '.' | '|' | 'J' | '7') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('-' | 'J' | '7' | 'S', '-' | 'L' | 'F') => {
|
||||
distances[y][x - 1] = starting_value + 1;
|
||||
locations_to_check.push((x - 1, y));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
if x < width && (distances[y][x + 1] == -1 || distances[y][x + 1] > starting_value) {
|
||||
match (field[y][x], field[y][x + 1]) {
|
||||
('|' | 'J' | '7', _) | (_, '.' | '|' | 'L' | 'F') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('-' | 'L' | 'F' | 'S', '-' | 'J' | '7') => {
|
||||
distances[y][x + 1] = starting_value + 1;
|
||||
locations_to_check.push((x + 1, y));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
if y != 0 && (distances[y - 1][x] == -1 || distances[y - 1][x] > starting_value) {
|
||||
match (field[y][x], field[y - 1][x]) {
|
||||
('-' | 'F' | '7', _) | (_, '.' | '-' | 'J' | 'L') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('|' | 'L' | 'J' | 'S', '|' | 'F' | '7') => {
|
||||
distances[y - 1][x] = starting_value + 1;
|
||||
locations_to_check.push((x, y - 1));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
if y < height && (distances[y + 1][x] == -1 || distances[y + 1][x] > starting_value) {
|
||||
match (field[y][x], field[y + 1][x]) {
|
||||
('-' | 'L' | 'J', _) | (_, '.' | '-' | '7' | 'F') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('|' | 'F' | '7' | 'S', '|' | 'L' | 'J') => {
|
||||
distances[y + 1][x] = starting_value + 1;
|
||||
locations_to_check.push((x, y + 1));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_d10t1() {
|
||||
let input = include_str!("../demo_input.txt");
|
||||
assert_eq!(d10t1(input), 8);
|
||||
}
|
||||
}
|
8
d10t2/Cargo.toml
Normal file
8
d10t2/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "d10t2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
10
d10t2/demo_input.txt
Normal file
10
d10t2/demo_input.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
FF7FSF7F7F7F7F7F---7
|
||||
L|LJ||||||||||||F--J
|
||||
FL-7LJLJ||||||LJL-77
|
||||
F--JF--7||LJLJ7F7FJ-
|
||||
L---JF-JLJ.||-FJLJJ7
|
||||
|F|F-JF---7F7-L7L|7|
|
||||
|FFJF7L7F-JF7|JL---7
|
||||
7-L-JL7||F7|L7F-7F7|
|
||||
L.L7LFJ|||||FJL7||LJ
|
||||
L7JLJL-JLJLJL--JLJ.L
|
10
d10t2/demo_input_2.txt
Normal file
10
d10t2/demo_input_2.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
.F----7F7F7F7F-7....
|
||||
.|F--7||||||||FJ....
|
||||
.||.FJ||||||||L7....
|
||||
FJL7L7LJLJ||LJ.L-7..
|
||||
L--J.L7...LJS7F-7L7.
|
||||
....F-J..F7FJ|L7L7L7
|
||||
....L7.F7||L7|.L7L7|
|
||||
.....|FJLJ|FJ|F7|.LJ
|
||||
....FJL-7.||.||||...
|
||||
....L---J.LJ.LJLJ...
|
9
d10t2/demo_input_3.txt
Normal file
9
d10t2/demo_input_3.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
...........
|
||||
.S-------7.
|
||||
.|F-----7|.
|
||||
.||.....||.
|
||||
.||.....||.
|
||||
.|L-7.F-J|.
|
||||
.|..|.|..|.
|
||||
.L--J.L--J.
|
||||
...........
|
191
d10t2/src/main.rs
Normal file
191
d10t2/src/main.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
use std::fs::read_to_string;
|
||||
|
||||
fn main() {
|
||||
let input = read_to_string("input.txt").unwrap();
|
||||
println!("d10t2: {}", d10t2(&input));
|
||||
}
|
||||
|
||||
pub fn d10t2(input: &str) -> usize {
|
||||
let mut field = input
|
||||
.lines()
|
||||
.map(str::chars)
|
||||
.map(std::iter::Iterator::collect::<Vec<_>>)
|
||||
.collect::<Vec<_>>();
|
||||
let mut distances = vec![vec![-1; field[0].len()]; field.len()];
|
||||
let starting_position = field
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| line.iter().enumerate().map(move |(x, c)| (x, y, c)))
|
||||
.find(|&(_x, _y, c)| *c == 'S')
|
||||
.map(|(x, y, _c)| (x, y))
|
||||
.unwrap();
|
||||
distances[starting_position.1][starting_position.0] = 0;
|
||||
traverse(&mut distances, &mut field, starting_position);
|
||||
get_enclosed(&distances, &field)
|
||||
}
|
||||
|
||||
fn get_effective_char_for_start(distances: &[Vec<isize>]) -> char {
|
||||
let starting_position = distances
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| line.iter().enumerate().map(move |(x, c)| (x, y, c)))
|
||||
.find(|&(_x, _y, c)| *c == 0)
|
||||
.map(|(x, y, _c)| (x, y))
|
||||
.unwrap();
|
||||
let bounds = (distances[0].len() - 1, distances.len() - 1);
|
||||
let (x, y) = starting_position;
|
||||
let top = if y == 0 {
|
||||
false
|
||||
} else {
|
||||
distances[y - 1][x] == 1
|
||||
};
|
||||
let bottom = if y == bounds.1 {
|
||||
false
|
||||
} else {
|
||||
distances[y + 1][x] == 1
|
||||
};
|
||||
let left = if x == 0 {
|
||||
false
|
||||
} else {
|
||||
distances[y][x - 1] == 1
|
||||
};
|
||||
let right = if x == bounds.0 {
|
||||
false
|
||||
} else {
|
||||
distances[y][x + 1] == 1
|
||||
};
|
||||
match (top, bottom, left, right) {
|
||||
(true, true, false, false) => '|',
|
||||
(false, false, true, true) => '-',
|
||||
(true, false, true, false) => 'J',
|
||||
(true, false, false, true) => 'L',
|
||||
(false, true, true, false) => '7',
|
||||
(false, true, false, true) => 'F',
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_enclosed(distances: &[Vec<isize>], field: &[Vec<char>]) -> usize {
|
||||
let mut result = 0;
|
||||
let s_char = get_effective_char_for_start(distances);
|
||||
let unique_y_changes = |x: usize, until: usize| -> usize {
|
||||
field[0..until]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(idx, _)| distances[*idx][x] != -1)
|
||||
.map(|(_, s)| s[x])
|
||||
.collect::<String>()
|
||||
.replace('S', s_char.to_string().as_str())
|
||||
.replace('|', "")
|
||||
.replace("FL", "")
|
||||
.replace("FJ", "-")
|
||||
.replace("7L", "-")
|
||||
.replace("7J", "")
|
||||
.replace('.', "")
|
||||
.len()
|
||||
};
|
||||
let unique_x_changes = |y: usize, until: usize| -> usize {
|
||||
field[y][0..until]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(idx, _)| distances[y][*idx] != -1)
|
||||
.map(|(_, c)| *c)
|
||||
.collect::<String>()
|
||||
.replace('S', s_char.to_string().as_str())
|
||||
.replace('-', "")
|
||||
.replace("F7", "")
|
||||
.replace("FJ", "|")
|
||||
.replace("L7", "|")
|
||||
.replace("LJ", "")
|
||||
.replace('.', "")
|
||||
.len()
|
||||
};
|
||||
for (y, line) in distances.iter().enumerate() {
|
||||
for (x, _d) in line.iter().enumerate() {
|
||||
if distances[y][x] == -1 {
|
||||
if unique_x_changes(y, x) % 2 == 1 && unique_y_changes(x, y) % 2 == 1 {
|
||||
result += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn traverse(distances: &mut [Vec<isize>], field: &mut Vec<Vec<char>>, position: (usize, usize)) {
|
||||
let height = field.len() - 1;
|
||||
let width = field[0].len() - 1;
|
||||
let mut locations_to_check = vec![position];
|
||||
while let Some((x, y)) = locations_to_check.pop() {
|
||||
let starting_value = distances[y][x];
|
||||
assert_ne!(
|
||||
starting_value, -1,
|
||||
"traverse should only be called on locations that have already been set."
|
||||
);
|
||||
if x != 0 && (distances[y][x - 1] == -1 || distances[y][x - 1] > starting_value) {
|
||||
match (field[y][x], field[y][x - 1]) {
|
||||
('|' | 'L' | 'F', _) | (_, '.' | '|' | 'J' | '7') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('-' | 'J' | '7' | 'S', '-' | 'L' | 'F') => {
|
||||
distances[y][x - 1] = starting_value + 1;
|
||||
locations_to_check.push((x - 1, y));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
if x < width && (distances[y][x + 1] == -1 || distances[y][x + 1] > starting_value) {
|
||||
match (field[y][x], field[y][x + 1]) {
|
||||
('|' | 'J' | '7', _) | (_, '.' | '|' | 'L' | 'F') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('-' | 'L' | 'F' | 'S', '-' | 'J' | '7') => {
|
||||
distances[y][x + 1] = starting_value + 1;
|
||||
locations_to_check.push((x + 1, y));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
if y != 0 && (distances[y - 1][x] == -1 || distances[y - 1][x] > starting_value) {
|
||||
match (field[y][x], field[y - 1][x]) {
|
||||
('-' | 'F' | '7', _) | (_, '.' | '-' | 'J' | 'L') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('|' | 'L' | 'J' | 'S', '|' | 'F' | '7') => {
|
||||
distances[y - 1][x] = starting_value + 1;
|
||||
locations_to_check.push((x, y - 1));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
if y < height && (distances[y + 1][x] == -1 || distances[y + 1][x] > starting_value) {
|
||||
match (field[y][x], field[y + 1][x]) {
|
||||
('-' | 'L' | 'J', _) | (_, '.' | '-' | '7' | 'F') => {}
|
||||
('.', _) | (_, 'S') => unreachable!(),
|
||||
('|' | 'F' | '7' | 'S', '|' | 'L' | 'J') => {
|
||||
distances[y + 1][x] = starting_value + 1;
|
||||
locations_to_check.push((x, y + 1));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_d10t2() {
|
||||
let input = include_str!("../demo_input.txt");
|
||||
assert_eq!(d10t2(input), 10);
|
||||
}
|
||||
#[test]
|
||||
fn test_d10t2_2() {
|
||||
let input = include_str!("../demo_input_2.txt");
|
||||
assert_eq!(d10t2(input), 8);
|
||||
}
|
||||
#[test]
|
||||
fn test_d10t2_3() {
|
||||
let input = include_str!("../demo_input_3.txt");
|
||||
assert_eq!(d10t2(input), 4);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue