do day20 for 2020

This commit is contained in:
2022-09-25 23:43:24 -04:00
parent 81acc2d472
commit 8deb5610a0
37 changed files with 2141 additions and 10 deletions
+56
View File
@@ -0,0 +1,56 @@
#![allow(dead_code)]
fn parse_input(input: &str) -> Vec<u32> {
input
.split_ascii_whitespace()
.map(|x| x.parse().unwrap())
.collect()
}
fn part_1_solution(input: &str) -> u64 {
parse_input(input)
.as_slice()
.windows(2)
.fold(0, |acc, w| if w[0] < w[1] { acc + 1 } else { acc })
}
fn part_2_solution(input: &str) -> u64 {
parse_input(input)
.as_slice()
.windows(4)
.fold(0, |acc, w| if w[0] < w[3] { acc + 1 } else { acc })
}
#[cfg(test)]
mod part1 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/1_test.txt");
assert_eq!(part_1_solution(input), 7);
}
#[test]
fn run() {
let input = include_str!("../inputs/1.txt");
assert_eq!(part_1_solution(input), 1139);
}
}
#[cfg(test)]
mod part2 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/1_test.txt");
assert_eq!(part_2_solution(input), 5);
}
#[test]
fn run() {
let input = include_str!("../inputs/1.txt");
assert_eq!(part_2_solution(input), 1103);
}
}
+105
View File
@@ -0,0 +1,105 @@
/**
* File: day10.rs
* Author: Adam Jeniski; @Ajetski
*/
use std::collections::HashMap;
type Stack = Vec<char>;
fn part_1_solution(input: &str) -> u64 {
let point_table = HashMap::<char, u64>::from([(')', 3), (']', 57), ('}', 1197), ('>', 25137)]);
let match_table = HashMap::<char, char>::from([(')', '('), (']', '['), ('}', '{'), ('>', '<')]);
let mut count = 0;
for line in input.split_ascii_whitespace() {
let mut stack = Stack::new();
for c in line.chars() {
if "([{<".contains(c) {
stack.push(c);
} else {
let start = stack.pop().unwrap();
if start != *(match_table.get(&c).unwrap()) {
count += point_table.get(&c).unwrap();
break;
}
}
}
}
count
}
fn part_2_solution(input: &str) -> u64 {
let point_table = HashMap::<char, u64>::from([(')', 1), (']', 2), ('}', 3), ('>', 4)]);
let match_table_from_start =
HashMap::<char, char>::from([('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')]);
let match_table_from_end =
HashMap::<char, char>::from([(')', '('), (']', '['), ('}', '{'), ('>', '<')]);
let mut counts = vec![];
'outer: for line in input.split_ascii_whitespace() {
let mut stack = Stack::new();
for c in line.chars() {
if "([{<".contains(c) {
stack.push(c);
} else {
let start = stack.pop().unwrap();
println!("{} {}", start, c);
if start != *(match_table_from_end.get(&c).unwrap()) {
continue 'outer;
}
}
}
let mut count = 0;
while !stack.is_empty() {
let c = stack.pop().unwrap();
count *= 5;
count += point_table
.get(match_table_from_start.get(&c).unwrap())
.unwrap();
}
counts.push(count);
}
counts.sort();
counts[counts.len() / 2]
}
#[cfg(test)]
mod part1 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/10_test.txt");
assert_eq!(part_1_solution(input), 26397);
}
#[test]
fn run() {
let input = include_str!("../inputs/10.txt");
assert_eq!(part_1_solution(input), 339477);
}
}
#[cfg(test)]
mod part2 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/10_test.txt");
assert_eq!(part_2_solution(input), 288957);
}
#[test]
fn run() {
let input = include_str!("../inputs/10.txt");
assert_eq!(part_2_solution(input), 3049320156);
}
}
+141
View File
@@ -0,0 +1,141 @@
/**
* File: day11.rs
* Author: Adam Jeniski; @Ajetski
*/
fn part_1_solution(mut input: Vec<Vec<i32>>) -> u64 {
fn flash(data: &mut Vec<Vec<i32>>, row: usize, col: usize, count: &mut u64) {
*count += 1;
//println!("flash {row} {col}");
data[row][col] = 0;
for (i, j) in (-1i32..=1).flat_map(|i| (-1i32..=1).map(move |j| (i, j))) {
// compute coords as i32 so we can do bounds checking
let x = i + row as i32;
let y = j + col as i32;
//print!("{row}, {col}; {x}, {y}. ");
// skip out of bounds
if x < 0 || y < 0 || x >= data.len() as i32 || y >= data[x as usize].len() as i32 {
//println!("exiting");
continue;
}
// convert coords to usize for easy indexing
let x = x as usize;
let y = y as usize;
if data[x][y] != 0 {
data[x][y] += 1;
//println!("updated data to {}", data[x][y]);
}
if data[x][y] > 9 {
//println!("flushing");
flash(data, x, y, count);
}
}
}
let mut count = 0;
for _ in 0..100 {
for row in &mut input {
for cell in row {
*cell += 1;
}
}
for i in 0..input.len() {
for j in 0..input[i].len() {
if input[i][j] > 9 {
flash(&mut input, i, j, &mut count);
}
}
}
}
count
}
fn part_2_solution(mut input: Vec<Vec<i32>>) -> u64 {
fn flash(data: &mut Vec<Vec<i32>>, row: usize, col: usize) {
//println!("flash {row} {col}");
data[row][col] = 0;
for (i, j) in (-1i32..=1).flat_map(|i| (-1i32..=1).map(move |j| (i, j))) {
// compute coords as i32 so we can do bounds checking
let x = i + row as i32;
let y = j + col as i32;
//print!("{row}, {col}; {x}, {y}. ");
// skip out of bounds
if x < 0 || y < 0 || x >= data.len() as i32 || y >= data[x as usize].len() as i32 {
//println!("exiting");
continue;
}
// convert coords to usize for easy indexing
let x = x as usize;
let y = y as usize;
if data[x][y] != 0 {
data[x][y] += 1;
//println!("updated data to {}", data[x][y]);
}
if data[x][y] > 9 {
//println!("flushing");
flash(data, x, y);
}
}
}
for count in 1..100000 {
for row in &mut input {
for cell in row {
*cell += 1;
}
}
for i in 0..input.len() {
for j in 0..input[i].len() {
if input[i][j] > 9 {
flash(&mut input, i, j);
}
}
}
if input.iter().all(|row| row.iter().all(|cell| cell == &0)) {
return count;
}
}
todo!()
}
#[cfg(test)]
mod part1 {
use super::*;
fn parse_input(input: &str) -> Vec<Vec<i32>> {
input
.split_ascii_whitespace()
.map(|s| s.chars().map(|c| c.to_string().parse().unwrap()).collect())
.collect()
}
#[test]
fn day_11_sample_part_one() {
let input = include_str!("../inputs/11_test.txt");
let sol = part_1_solution(parse_input(input));
assert_eq!(sol, 1656);
}
#[test]
fn day_11_test_part_one() {
let input = include_str!("../inputs/11.txt");
assert_eq!(part_1_solution(parse_input(input)), 1725);
}
#[test]
fn day_11_sample_part_two() {
let input = include_str!("../inputs/11_test.txt");
assert_eq!(part_2_solution(parse_input(input)), 195);
}
#[test]
fn day_11_test_part_two() {
let input = include_str!("../inputs/11.txt");
assert_eq!(part_2_solution(parse_input(input)), 308);
}
}
+116
View File
@@ -0,0 +1,116 @@
use std::collections::HashSet;
type Point = (i32, i32);
#[derive(Debug)]
enum Fold {
X(i32),
Y(i32),
}
fn part_one(points: Vec<Point>, folds: Vec<Fold>) -> i32 {
match folds.first().expect("first fold") {
Fold::X(coord) => points
.into_iter()
.map(|(x, y): Point| (x.abs_diff(*coord) as i32, y))
.collect::<HashSet<_>>(),
Fold::Y(coord) => points
.into_iter()
.map(|(x, y): Point| (x, y.abs_diff(*coord) as i32))
.collect::<HashSet<_>>(),
}
.len() as i32
}
fn part_two(points: Vec<Point>, folds: Vec<Fold>) {
let mut points = points.into_iter().collect::<HashSet<_>>();
for fold in folds {
points = match fold {
Fold::X(coord) => points
.into_iter()
.map(|(x, y): Point| (coord - x.abs_diff(coord) as i32, y))
.collect::<HashSet<_>>(),
Fold::Y(coord) => points
.into_iter()
.map(|(x, y): Point| (x, coord - y.abs_diff(coord) as i32))
.collect::<HashSet<_>>(),
};
}
let max_x = points
.iter()
.fold(0, |acc, (x, _)| if *x > acc { *x } else { acc });
let max_y = points
.iter()
.fold(0, |acc, (_, y)| if *y > acc { *y } else { acc });
let lines: Vec<Vec<char>> = (0..=max_y)
.map(|y| {
(0..=max_x)
.map(|x| if points.contains(&(x, y)) { '#' } else { ' ' })
.collect()
})
.collect();
for line in lines {
let line = line.into_iter().collect::<String>();
println!("{line}");
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_inputs(input: &str) -> (Vec<Point>, Vec<Fold>) {
let mut sections = input.split("\n\n");
(
sections
.next()
.expect("coords section")
.lines()
.map(|line| {
let mut coords = line
.split(',')
.map(|num_str| num_str.parse().expect("coord number"));
(
coords.next().expect("coord x value"),
coords.next().expect("coord y value"),
)
})
.collect(),
sections
.next()
.expect("fold instructions section")
.lines()
.map(|line| {
let (direction, num) =
line.split(' ').nth(2).expect("fold equation").split_at(2);
let num = num.parse().expect("coordinate to fold at");
if direction == "x=" {
Fold::X(num)
} else {
Fold::Y(num)
}
})
.collect(),
)
}
#[test]
fn part_one_sample_test() {
let input = include_str!("../inputs/13_test.txt");
let (points, folds) = parse_inputs(input);
assert_eq!(part_one(points, folds), 17);
}
#[test]
fn part_one_test() {
let input = include_str!("../inputs/13.txt");
let (points, folds) = parse_inputs(input);
assert_eq!(part_one(points, folds), 653);
}
#[test]
fn part_two_test() {
let input = include_str!("../inputs/13.txt");
let (points, folds) = parse_inputs(input);
part_two(points, folds);
}
}
+121
View File
@@ -0,0 +1,121 @@
use std::collections::HashMap;
type Transforms = HashMap<(char, char), char>;
fn part_one(input: String, transforms: Transforms) -> i32 {
let mut input: Vec<char> = input.chars().collect();
for _ in 1..=10 {
let pairs = input.windows(2);
let mut next = vec![input[0]];
for pair in pairs {
let key = (pair[0], pair[1]);
if let Some(val) = transforms.get(&key) {
next.push(*val);
}
next.push(pair[1]);
}
input = next;
}
let mut counts = HashMap::<char, i32>::new();
for char in input {
if char != '\n' {
counts.insert(
char,
match counts.get(&char) {
Some(val) => *val + 1,
_ => 1,
},
);
}
}
counts.iter().max_by_key(|c| c.1).unwrap().1 - counts.iter().min_by_key(|c| c.1).unwrap().1
}
fn part_two(input: String, transforms: Transforms) -> u128 {
type Bag = HashMap<(char, char), u128>;
let mut last_pair = ('a', 'b'); //hacky
let mut bag = {
let mut bag = Bag::new();
let mut iter1 = input.chars().filter(|c| *c != '\n');
let mut iter2 = iter1.clone();
iter2.next();
while let (Some(c1), Some(c2)) = (iter1.next(), iter2.next()) {
last_pair = (c1, c2);
bag.entry((c1, c2)).and_modify(|e| *e += 1).or_insert(1);
}
bag
};
for _ in 0..40 {
let mut new_bag = Bag::new();
for ((c1, c2), val) in bag {
let transformation = transforms[&(c1, c2)];
if last_pair == (c1, c2) {
last_pair = (transformation, c2);
}
new_bag
.entry((c1, transformation))
.and_modify(|e| *e += val)
.or_insert(val);
new_bag
.entry((transformation, c2))
.and_modify(|e| *e += val)
.or_insert(val);
}
bag = new_bag;
}
let counts = {
type Counts = HashMap<char, u128>;
let mut counts = Counts::new();
for ((c1, _), val) in bag {
counts.entry(c1).and_modify(|e| *e += val).or_insert(val);
}
counts.entry(last_pair.1).and_modify(|e| *e += 1);
counts
};
counts.iter().max_by_key(|(_, val)| **val).unwrap().1
- counts.iter().min_by_key(|(_, val)| **val).unwrap().1
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_input(input: &str) -> (String, Transforms) {
let (input, transform_list) = input.split_once("\n\n").expect("two sections");
let mut transforms = Transforms::new();
for line in transform_list.split('\n') {
let (input, output) = line.split_once(" -> ").expect("transform");
let mut chars = input.chars();
transforms.insert(
(chars.next().unwrap(), chars.next().unwrap()),
output.chars().next().unwrap(),
);
}
(input.to_owned(), transforms)
}
#[test]
fn part_one_test_sample() {
let (input, transforms) = parse_input(include_str!("../inputs/14_test.txt"));
assert_eq!(part_one(input, transforms), 1588);
}
#[test]
fn part_one_test() {
let (input, transforms) = parse_input(include_str!("../inputs/14.txt"));
assert_eq!(part_one(input, transforms), 2112);
}
#[test]
fn part_two_sample() {
let (input, transforms) = parse_input(include_str!("../inputs/14_test.txt"));
assert_eq!(part_two(input, transforms), 2188189693529);
}
#[test]
fn part_two_test() {
let (input, transforms) = parse_input(include_str!("../inputs/14.txt"));
assert_eq!(part_two(input, transforms), 3243771149914);
}
}
+152
View File
@@ -0,0 +1,152 @@
use std::{
cmp::Ordering,
collections::{BinaryHeap, HashMap},
vec,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct Path {
x: usize,
y: usize,
length: u32,
}
impl From<&Path> for u32 {
fn from(val: &Path) -> Self {
val.length
}
}
impl Ord for Path {
fn cmp(&self, other: &Self) -> Ordering {
u32::from(other).cmp(&u32::from(self))
}
}
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
fn part_one(input: Vec<Vec<u32>>) -> Result<u32, &'static str> {
let mut queue = BinaryHeap::new();
let mut dp = HashMap::new();
let num_rows = input.len() as i32;
let num_cols = input[0].len() as i32;
let target_x = num_rows as usize - 1;
let target_y = num_cols as usize - 1;
queue.push(Path {
x: 0,
y: 0,
length: 0,
});
dp.insert((0, 0), 0);
'outer: while let Some(path) = queue.pop() {
if path.x == target_x && path.y == target_y {
return Ok(path.length);
}
// skip this if we've seen better
if let Some(length) = dp.get(&(path.x, path.y)) {
if *length < path.length {
continue 'outer;
}
}
'inner: for (x_offset, y_offset) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
let x = x_offset + path.x as i32;
let y = y_offset + path.y as i32;
if x < 0 || x >= num_rows || y < 0 || y >= num_cols {
continue 'inner;
}
let x = x as usize;
let y = y as usize;
let length = path.length + input[x][y];
let entry = dp.entry((x, y)).or_insert(u32::MAX);
if length < *entry {
*entry = length;
queue.push(Path { x, y, length });
}
}
}
Err("couldn't find an answer")
}
fn part_two(mut input: Vec<Vec<u32>>) -> Result<u32, &'static str> {
let num_rows = input.len();
let num_cols = input[0].len();
for offset in 1..5 {
for row in input.iter_mut().take(num_rows) {
for idx in 0..num_cols {
let mut val = row[idx] + offset;
while val > 9 {
val -= 9;
}
if val == 0 {
val = 1;
}
row.push(val);
}
}
}
for offset in 1..5 {
for row_idx in 0..num_rows {
input.push(vec![]);
let last_row_idx = input.len();
for col_idx in 0..input[row_idx].len() {
let mut val = input[row_idx][col_idx] + offset;
while val > 9 {
val -= 9;
}
if val == 0 {
val = 1;
}
input[last_row_idx - 1].push(val);
}
}
}
part_one(input)
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_input(input: &str) -> Vec<Vec<u32>> {
input
.split_ascii_whitespace()
.map(|s| s.chars().map(|c| c.to_digit(10).unwrap()).collect())
.collect()
}
#[test]
fn part_one_sample_test() {
let input = parse_input(include_str!("../inputs/15_test.txt"));
assert!(part_one(input) == Ok(40));
}
#[test]
fn part_one_test() {
let input = parse_input(include_str!("../inputs/15.txt"));
assert!(part_one(input) == Ok(581));
}
#[test]
fn part_two_sample_test() {
let input = parse_input(include_str!("../inputs/15_test.txt"));
assert!(part_two(input) == Ok(315));
}
#[test]
fn part_two_test() {
let input = parse_input(include_str!("../inputs/15.txt"));
assert!(part_two(input) == Ok(2916));
}
}
+65
View File
@@ -0,0 +1,65 @@
pub fn run(lines: Vec<String>) {
let mut h1 = 0;
let mut d1 = 0;
let mut aim = 0;
let mut d2 = 0;
let mut h2 = 0;
for line in lines {
let parts = line.split(" ");
let command = parts.clone().nth(0).unwrap();
let num: i32 = parts.clone().nth(1).unwrap().parse().unwrap();
match command {
"forward" => {
h1 += num;
h2 += num;
d2 += aim * num;
},
"up" => {
d1 -= num;
aim -= num;
},
"down" => {
d1 += num;
aim += num;
},
_ => println!("unknown command: '{}'", command)
}
}
println!("{}, {}", h1 * d1, h2 * d2);
}
#[cfg(test)]
mod part1 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/2_test.txt");
assert_eq!(part_1_solution(input), 150);
}
#[test]
fn run() {
let input = include_str!("../inputs/2.txt");
assert_eq!(part_1_solution(input), 1660158);
}
}
#[cfg(test)]
mod part2 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/2_test.txt");
assert_eq!(part_2_solution(input), 900);
}
#[test]
fn run() {
let input = include_str!("../inputs/2.txt");
assert_eq!(part_2_solution(input), 1604592846);
}
}
+129
View File
@@ -0,0 +1,129 @@
/**
* File: day09.rs
* Author: Adam Jeniski; @Ajetski
*/
use std::collections::{HashSet, LinkedList};
type Position = (usize, usize);
fn is_local_min(data: &Vec<Vec<u64>>, (row, col): Position) -> bool {
if row > 0 && data[row][col] >= data[row - 1][col] {
return false;
} else if row < data.len() - 1 && data[row][col] >= data[row + 1][col] {
return false;
} else if col > 0 && data[row][col] >= data[row][col - 1] {
return false;
} else if col < data[row].len() - 1 && data[row][col] >= data[row][col + 1] {
return false;
}
true
}
fn get_local_mins(data: &Vec<Vec<u64>>) -> Vec<Position> {
let mut mins = vec![];
for row in 0..data.len() {
for col in 0..data[row].len() {
if is_local_min(data, (row, col)) {
mins.push((row, col));
}
}
}
mins
}
/** use a BFS to count all of the nodes in the basin */
fn get_basin_size_from_min(data: &Vec<Vec<u64>>, start: Position) -> u64 {
/* queue: pop from front; push to back */
let mut search_queue = LinkedList::from([start]);
let mut visited = HashSet::from([start]);
let mut count = 1;
while !search_queue.is_empty() {
let (row, col) = search_queue.pop_front().unwrap();
let mut directions = vec![];
if row > 0 {
directions.push((row - 1, col));
}
if col > 0 {
directions.push((row, col - 1));
}
if row < data.len() - 1 {
directions.push((row + 1, col));
}
if col < data[row].len() - 1 {
directions.push((row, col + 1));
}
for (next_row, next_col) in directions {
if data[next_row][next_col] != 9 && !visited.contains(&(next_row, next_col)) {
search_queue.push_back((next_row, next_col));
count += 1;
}
visited.insert((next_row, next_col));
}
}
count
}
fn parse_input(input: &str) -> Vec<Vec<u64>> {
input
.split_ascii_whitespace()
.map(|a| a.chars().map(|c| c.to_string().parse().unwrap()).collect())
.collect()
}
fn part_1_solution(input: &str) -> u64 {
let data = parse_input(input);
let mins = get_local_mins(&data);
mins.len() as u64
+ mins.iter().fold(0, |acc, (row, col)| {
acc + data[row.to_owned()][col.to_owned()]
})
}
fn part_2_solution(input: &str) -> u64 {
let data = parse_input(input);
let mins = get_local_mins(&data);
let mut basin_sizes: Vec<u64> = mins
.iter()
.map(|k| get_basin_size_from_min(&data, k.to_owned()))
.collect();
basin_sizes.sort();
basin_sizes.reverse();
basin_sizes.iter().take(3).fold(1, |prod, curr| prod * curr)
}
#[cfg(test)]
mod part1 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/9_test.txt");
assert_eq!(part_1_solution(input), 15);
}
#[test]
fn run() {
let input = include_str!("../inputs/9.txt");
assert_eq!(part_1_solution(input), 502);
}
}
#[cfg(test)]
mod part2 {
use super::*;
#[test]
fn run_sample() {
let input = include_str!("../inputs/9_test.txt");
assert_eq!(part_2_solution(input), 1134);
}
#[test]
fn run() {
let input = include_str!("../inputs/9.txt");
assert_eq!(part_2_solution(input), 1330560);
}
}
+13
View File
@@ -0,0 +1,13 @@
#![allow(dead_code)]
/**
* File: lib.rs
* Author: Adam Jeniski; @Ajetski
*/
pub(crate) mod day1;
pub(crate) mod day9;
pub(crate) mod day10;
pub(crate) mod day11;
pub(crate) mod day13;
pub(crate) mod day14;
pub(crate) mod day15;