do day20 for 2020
This commit is contained in:
Executable
+56
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Executable
+65
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user