fix: change rust fmt defaults
This commit is contained in:
parent
87cfe7a587
commit
2e495f674c
4
rustfmt.toml
Normal file
4
rustfmt.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
tab_spaces = 2
|
||||||
|
hard_tabs = true
|
||||||
|
reorder_imports = true
|
||||||
|
newline_style = "Unix"
|
59
src/day1.rs
59
src/day1.rs
@ -1,47 +1,44 @@
|
|||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<Vec<u32>, ParseIntError> {
|
pub fn parse_input(input: &str) -> Result<Vec<u32>, ParseIntError> {
|
||||||
input.split_whitespace()
|
input.split_whitespace().map(|s| s.parse()).collect()
|
||||||
.map(|s| s.parse())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(depths: &[u32]) -> u32 {
|
pub fn part1(depths: &[u32]) -> u32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for i in 1..depths.len() {
|
for i in 1..depths.len() {
|
||||||
if depths[i] > depths[i-1] {
|
if depths[i] > depths[i - 1] {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(depths: &[u32]) -> u32 {
|
pub fn part2(depths: &[u32]) -> u32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for i in 2..depths.len()-1 {
|
for i in 2..depths.len() - 1 {
|
||||||
if depths[i+1] > depths[i-2] {
|
if depths[i + 1] > depths[i - 2] {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = [199, 200, 208, 210, 200, 207, 240, 269, 260, 263];
|
let input = [199, 200, 208, 210, 200, 207, 240, 269, 260, 263];
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 7);
|
assert_eq!(result, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = [199, 200, 208, 210, 200, 207, 240, 269, 260, 263];
|
let input = [199, 200, 208, 210, 200, 207, 240, 269, 260, 263];
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 5);
|
assert_eq!(result, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
208
src/day10.rs
208
src/day10.rs
@ -1,137 +1,135 @@
|
|||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum ChunkVariant {
|
enum ChunkVariant {
|
||||||
Parenthesis,
|
Parenthesis,
|
||||||
Bracket,
|
Bracket,
|
||||||
Curly,
|
Curly,
|
||||||
Pointy
|
Pointy,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Vec<String> {
|
pub fn parse_input(input: &str) -> Vec<String> {
|
||||||
input.lines().map(|s| s.into()).collect()
|
input.lines().map(|s| s.into()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_opening(c: char) -> bool {
|
fn is_opening(c: char) -> bool {
|
||||||
c == '(' || c == '[' || c == '{' || c == '<'
|
c == '(' || c == '[' || c == '{' || c == '<'
|
||||||
}
|
}
|
||||||
|
|
||||||
// I know that it is not ideal to throw a panic here. But I'm too lazy to do
|
// I know that it is not ideal to throw a panic here. But I'm too lazy to do
|
||||||
// error handling. And the input is guaranteed to be correct to this is fine.
|
// error handling. And the input is guaranteed to be correct to this is fine.
|
||||||
fn get_chunk_variant(c: char) -> ChunkVariant {
|
fn get_chunk_variant(c: char) -> ChunkVariant {
|
||||||
match c {
|
match c {
|
||||||
'('|')' => ChunkVariant::Parenthesis,
|
'(' | ')' => ChunkVariant::Parenthesis,
|
||||||
'['|']' => ChunkVariant::Bracket,
|
'[' | ']' => ChunkVariant::Bracket,
|
||||||
'{'|'}' => ChunkVariant::Curly,
|
'{' | '}' => ChunkVariant::Curly,
|
||||||
'<'|'>' => ChunkVariant::Pointy,
|
'<' | '>' => ChunkVariant::Pointy,
|
||||||
_ => panic!("Invalid chunk character")
|
_ => panic!("Invalid chunk character"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_corrupted_chunk_symbol(line: &str) -> Option<ChunkVariant> {
|
fn find_corrupted_chunk_symbol(line: &str) -> Option<ChunkVariant> {
|
||||||
let mut active_chunks: Vec<ChunkVariant> = Vec::new();
|
let mut active_chunks: Vec<ChunkVariant> = Vec::new();
|
||||||
for c in line.chars() {
|
for c in line.chars() {
|
||||||
let variant = get_chunk_variant(c);
|
let variant = get_chunk_variant(c);
|
||||||
if is_opening(c) {
|
if is_opening(c) {
|
||||||
active_chunks.push(variant);
|
active_chunks.push(variant);
|
||||||
} else {
|
} else {
|
||||||
if *active_chunks.last().unwrap() != variant {
|
if *active_chunks.last().unwrap() != variant {
|
||||||
return Some(variant);
|
return Some(variant);
|
||||||
}
|
}
|
||||||
active_chunks.pop();
|
active_chunks.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(lines: &Vec<String>) -> u32 {
|
pub fn part1(lines: &Vec<String>) -> u32 {
|
||||||
let mut score = 0;
|
let mut score = 0;
|
||||||
for line in lines {
|
for line in lines {
|
||||||
let result = find_corrupted_chunk_symbol(line);
|
let result = find_corrupted_chunk_symbol(line);
|
||||||
score += match result {
|
score += match result {
|
||||||
Some(ChunkVariant::Parenthesis) => 3,
|
Some(ChunkVariant::Parenthesis) => 3,
|
||||||
Some(ChunkVariant::Bracket) => 57,
|
Some(ChunkVariant::Bracket) => 57,
|
||||||
Some(ChunkVariant::Curly) => 1197,
|
Some(ChunkVariant::Curly) => 1197,
|
||||||
Some(ChunkVariant::Pointy) => 25137,
|
Some(ChunkVariant::Pointy) => 25137,
|
||||||
None => 0
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_unclosed_chunks(line: &str) -> Vec<ChunkVariant> {
|
fn find_unclosed_chunks(line: &str) -> Vec<ChunkVariant> {
|
||||||
let mut active_chunks: Vec<ChunkVariant> = Vec::new();
|
let mut active_chunks: Vec<ChunkVariant> = Vec::new();
|
||||||
for c in line.chars() {
|
for c in line.chars() {
|
||||||
if is_opening(c) {
|
if is_opening(c) {
|
||||||
active_chunks.push(get_chunk_variant(c));
|
active_chunks.push(get_chunk_variant(c));
|
||||||
} else {
|
} else {
|
||||||
active_chunks.pop();
|
active_chunks.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
active_chunks
|
active_chunks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(lines: &Vec<String>) -> u64 {
|
pub fn part2(lines: &Vec<String>) -> u64 {
|
||||||
let mut scores = Vec::new();
|
let mut scores = Vec::new();
|
||||||
for line in lines {
|
for line in lines {
|
||||||
let corrupted = find_corrupted_chunk_symbol(line);
|
let corrupted = find_corrupted_chunk_symbol(line);
|
||||||
if corrupted == None {
|
if corrupted == None {
|
||||||
let mut score = 0;
|
let mut score = 0;
|
||||||
let mut unclosed_chunks = find_unclosed_chunks(line);
|
let mut unclosed_chunks = find_unclosed_chunks(line);
|
||||||
unclosed_chunks.reverse();
|
unclosed_chunks.reverse();
|
||||||
for chunk in unclosed_chunks {
|
for chunk in unclosed_chunks {
|
||||||
score *= 5;
|
score *= 5;
|
||||||
score += match chunk {
|
score += match chunk {
|
||||||
ChunkVariant::Parenthesis => 1,
|
ChunkVariant::Parenthesis => 1,
|
||||||
ChunkVariant::Bracket => 2,
|
ChunkVariant::Bracket => 2,
|
||||||
ChunkVariant::Curly => 3,
|
ChunkVariant::Curly => 3,
|
||||||
ChunkVariant::Pointy => 4
|
ChunkVariant::Pointy => 4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scores.push(score);
|
scores.push(score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scores.sort();
|
scores.sort();
|
||||||
return scores[scores.len()/2];
|
return scores[scores.len() / 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
"[({(<(())[]>[[{[]{<()<>>".into(),
|
"[({(<(())[]>[[{[]{<()<>>".into(),
|
||||||
"[(()[<>])]({[<{<<[]>>(".into(),
|
"[(()[<>])]({[<{<<[]>>(".into(),
|
||||||
"{([(<{}[<>[]}>{[]{[(<()>".into(),
|
"{([(<{}[<>[]}>{[]{[(<()>".into(),
|
||||||
"(((({<>}<{<{<>}{[]{[]{}".into(),
|
"(((({<>}<{<{<>}{[]{[]{}".into(),
|
||||||
"[[<[([]))<([[{}[[()]]]".into(),
|
"[[<[([]))<([[{}[[()]]]".into(),
|
||||||
"[{[{({}]{}}([{[{{{}}([]".into(),
|
"[{[{({}]{}}([{[{{{}}([]".into(),
|
||||||
"{<[[]]>}<{[{[{[]{()[[[]".into(),
|
"{<[[]]>}<{[{[{[]{()[[[]".into(),
|
||||||
"[<(<(<(<{}))><([]([]()".into(),
|
"[<(<(<(<{}))><([]([]()".into(),
|
||||||
"<{([([[(<>()){}]>(<<{{".into(),
|
"<{([([[(<>()){}]>(<<{{".into(),
|
||||||
"<{([{{}}[<[[[<>{}]]]>[]]".into(),
|
"<{([{{}}[<[[[<>{}]]]>[]]".into(),
|
||||||
];
|
];
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 26397);
|
assert_eq!(result, 26397);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
"[({(<(())[]>[[{[]{<()<>>".into(),
|
"[({(<(())[]>[[{[]{<()<>>".into(),
|
||||||
"[(()[<>])]({[<{<<[]>>(".into(),
|
"[(()[<>])]({[<{<<[]>>(".into(),
|
||||||
"{([(<{}[<>[]}>{[]{[(<()>".into(),
|
"{([(<{}[<>[]}>{[]{[(<()>".into(),
|
||||||
"(((({<>}<{<{<>}{[]{[]{}".into(),
|
"(((({<>}<{<{<>}{[]{[]{}".into(),
|
||||||
"[[<[([]))<([[{}[[()]]]".into(),
|
"[[<[([]))<([[{}[[()]]]".into(),
|
||||||
"[{[{({}]{}}([{[{{{}}([]".into(),
|
"[{[{({}]{}}([{[{{{}}([]".into(),
|
||||||
"{<[[]]>}<{[{[{[]{()[[[]".into(),
|
"{<[[]]>}<{[{[{[]{()[[[]".into(),
|
||||||
"[<(<(<(<{}))><([]([]()".into(),
|
"[<(<(<(<{}))><([]([]()".into(),
|
||||||
"<{([([[(<>()){}]>(<<{{".into(),
|
"<{([([[(<>()){}]>(<<{{".into(),
|
||||||
"<{([{{}}[<[[[<>{}]]]>[]]".into(),
|
"<{([{{}}[<[[[<>{}]]]>[]]".into(),
|
||||||
];
|
];
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 288957);
|
assert_eq!(result, 288957);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
262
src/day11.rs
262
src/day11.rs
@ -1,171 +1,173 @@
|
|||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> [[u32; 10]; 10] {
|
pub fn parse_input(input: &str) -> [[u32; 10]; 10] {
|
||||||
input.lines()
|
input
|
||||||
.map(|s| s.chars()
|
.lines()
|
||||||
.map(|s| s.to_digit(10).unwrap())
|
.map(|s| {
|
||||||
.collect::<ArrayVec<u32, 10>>()
|
s.chars()
|
||||||
.into_inner()
|
.map(|s| s.to_digit(10).unwrap())
|
||||||
.unwrap())
|
.collect::<ArrayVec<u32, 10>>()
|
||||||
.collect::<ArrayVec<[u32; 10], 10>>()
|
.into_inner()
|
||||||
.into_inner()
|
.unwrap()
|
||||||
.unwrap()
|
})
|
||||||
|
.collect::<ArrayVec<[u32; 10], 10>>()
|
||||||
|
.into_inner()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _display_grid(grid: &[[u32; 10]; 10]) {
|
fn _display_grid(grid: &[[u32; 10]; 10]) {
|
||||||
for i in 0..grid.len() {
|
for i in 0..grid.len() {
|
||||||
for j in 0..grid[i].len() {
|
for j in 0..grid[i].len() {
|
||||||
print!("{:X}", grid[i][j]);
|
print!("{:X}", grid[i][j]);
|
||||||
}
|
}
|
||||||
print!("\n");
|
print!("\n");
|
||||||
}
|
}
|
||||||
print!("\n");
|
print!("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bump_energy(grid: &mut [[u32; 10]; 10]) {
|
fn bump_energy(grid: &mut [[u32; 10]; 10]) {
|
||||||
for i in 0..grid.len() {
|
for i in 0..grid.len() {
|
||||||
for j in 0..grid[i].len() {
|
for j in 0..grid[i].len() {
|
||||||
grid[i][j] += 1;
|
grid[i][j] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bump_energy_around(grid: &mut [[u32; 10]; 10], i: usize, j: usize) {
|
fn bump_energy_around(grid: &mut [[u32; 10]; 10], i: usize, j: usize) {
|
||||||
if j > 0 {
|
if j > 0 {
|
||||||
grid[i+0][j-1] += 1;
|
grid[i + 0][j - 1] += 1;
|
||||||
}
|
}
|
||||||
if j < 9 {
|
if j < 9 {
|
||||||
grid[i+0][j+1] += 1;
|
grid[i + 0][j + 1] += 1;
|
||||||
}
|
}
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
grid[i-1][j+0] += 1;
|
grid[i - 1][j + 0] += 1;
|
||||||
}
|
}
|
||||||
if i < 9 {
|
if i < 9 {
|
||||||
grid[i+1][j+0] += 1;
|
grid[i + 1][j + 0] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i > 0 && j > 0 {
|
if i > 0 && j > 0 {
|
||||||
grid[i-1][j-1] += 1;
|
grid[i - 1][j - 1] += 1;
|
||||||
}
|
}
|
||||||
if i < 9 && j > 0 {
|
if i < 9 && j > 0 {
|
||||||
grid[i+1][j-1] += 1;
|
grid[i + 1][j - 1] += 1;
|
||||||
}
|
}
|
||||||
if i > 0 && j < 9 {
|
if i > 0 && j < 9 {
|
||||||
grid[i-1][j+1] += 1;
|
grid[i - 1][j + 1] += 1;
|
||||||
}
|
}
|
||||||
if i < 9 && j < 9 {
|
if i < 9 && j < 9 {
|
||||||
grid[i+1][j+1] += 1;
|
grid[i + 1][j + 1] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_flashes(grid: &mut [[u32; 10]; 10]) -> u32 {
|
fn perform_flashes(grid: &mut [[u32; 10]; 10]) -> u32 {
|
||||||
let mut flashes = 0;
|
let mut flashes = 0;
|
||||||
let mut has_flashed: [[bool; 10]; 10] = [[false; 10]; 10];
|
let mut has_flashed: [[bool; 10]; 10] = [[false; 10]; 10];
|
||||||
let mut anyone_flashed = true;
|
let mut anyone_flashed = true;
|
||||||
|
|
||||||
while anyone_flashed {
|
while anyone_flashed {
|
||||||
anyone_flashed = false;
|
anyone_flashed = false;
|
||||||
|
|
||||||
for i in 0..grid.len() {
|
for i in 0..grid.len() {
|
||||||
for j in 0..grid[i].len() {
|
for j in 0..grid[i].len() {
|
||||||
if grid[i][j] > 9 && !has_flashed[i][j] {
|
if grid[i][j] > 9 && !has_flashed[i][j] {
|
||||||
flashes += 1;
|
flashes += 1;
|
||||||
has_flashed[i][j] = true;
|
has_flashed[i][j] = true;
|
||||||
anyone_flashed = true;
|
anyone_flashed = true;
|
||||||
bump_energy_around(grid, i, j);
|
bump_energy_around(grid, i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flashes;
|
return flashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_energy(grid: &mut [[u32; 10]; 10]) {
|
fn reset_energy(grid: &mut [[u32; 10]; 10]) {
|
||||||
for i in 0..grid.len() {
|
for i in 0..grid.len() {
|
||||||
for j in 0..grid[i].len() {
|
for j in 0..grid[i].len() {
|
||||||
if grid[i][j] > 9 {
|
if grid[i][j] > 9 {
|
||||||
grid[i][j] = 0;
|
grid[i][j] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_step(grid: &mut [[u32; 10]; 10]) -> u32 {
|
fn do_step(grid: &mut [[u32; 10]; 10]) -> u32 {
|
||||||
bump_energy(grid);
|
bump_energy(grid);
|
||||||
let flashes = perform_flashes(grid);
|
let flashes = perform_flashes(grid);
|
||||||
reset_energy(grid);
|
reset_energy(grid);
|
||||||
return flashes;
|
return flashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_all_zeros(grid: &[[u32; 10]; 10]) -> bool {
|
fn has_all_zeros(grid: &[[u32; 10]; 10]) -> bool {
|
||||||
for i in 0..grid.len() {
|
for i in 0..grid.len() {
|
||||||
for j in 0..grid[i].len() {
|
for j in 0..grid[i].len() {
|
||||||
if grid[i][j] != 0 {
|
if grid[i][j] != 0 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(grid: &[[u32; 10]; 10]) -> u32 {
|
pub fn part1(grid: &[[u32; 10]; 10]) -> u32 {
|
||||||
let mut flashes = 0;
|
let mut flashes = 0;
|
||||||
let mut active_grid = grid.clone();
|
let mut active_grid = grid.clone();
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
flashes += do_step(&mut active_grid);
|
flashes += do_step(&mut active_grid);
|
||||||
}
|
}
|
||||||
return flashes;
|
return flashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(grid: &[[u32; 10]; 10]) -> u32 {
|
pub fn part2(grid: &[[u32; 10]; 10]) -> u32 {
|
||||||
let mut active_grid = grid.clone();
|
let mut active_grid = grid.clone();
|
||||||
let mut step = 0;
|
let mut step = 0;
|
||||||
while !has_all_zeros(&active_grid) {
|
while !has_all_zeros(&active_grid) {
|
||||||
do_step(&mut active_grid);
|
do_step(&mut active_grid);
|
||||||
step += 1;
|
step += 1;
|
||||||
}
|
}
|
||||||
return step;
|
return step;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = [
|
let input = [
|
||||||
[5, 4, 8, 3, 1, 4, 3, 2, 2, 3],
|
[5, 4, 8, 3, 1, 4, 3, 2, 2, 3],
|
||||||
[2, 7, 4, 5, 8, 5, 4, 7, 1, 1],
|
[2, 7, 4, 5, 8, 5, 4, 7, 1, 1],
|
||||||
[5, 2, 6, 4, 5, 5, 6, 1, 7, 3],
|
[5, 2, 6, 4, 5, 5, 6, 1, 7, 3],
|
||||||
[6, 1, 4, 1, 3, 3, 6, 1, 4, 6],
|
[6, 1, 4, 1, 3, 3, 6, 1, 4, 6],
|
||||||
[6, 3, 5, 7, 3, 8, 5, 4, 7, 8],
|
[6, 3, 5, 7, 3, 8, 5, 4, 7, 8],
|
||||||
[4, 1, 6, 7, 5, 2, 4, 6, 4, 5],
|
[4, 1, 6, 7, 5, 2, 4, 6, 4, 5],
|
||||||
[2, 1, 7, 6, 8, 4, 1, 7, 2, 1],
|
[2, 1, 7, 6, 8, 4, 1, 7, 2, 1],
|
||||||
[6, 8, 8, 2, 8, 8, 1, 1, 3, 4],
|
[6, 8, 8, 2, 8, 8, 1, 1, 3, 4],
|
||||||
[4, 8, 4, 6, 8, 4, 8, 5, 5, 4],
|
[4, 8, 4, 6, 8, 4, 8, 5, 5, 4],
|
||||||
[5, 2, 8, 3, 7, 5, 1, 5, 2, 6]
|
[5, 2, 8, 3, 7, 5, 1, 5, 2, 6],
|
||||||
];
|
];
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 1656);
|
assert_eq!(result, 1656);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = [
|
let input = [
|
||||||
[5, 4, 8, 3, 1, 4, 3, 2, 2, 3],
|
[5, 4, 8, 3, 1, 4, 3, 2, 2, 3],
|
||||||
[2, 7, 4, 5, 8, 5, 4, 7, 1, 1],
|
[2, 7, 4, 5, 8, 5, 4, 7, 1, 1],
|
||||||
[5, 2, 6, 4, 5, 5, 6, 1, 7, 3],
|
[5, 2, 6, 4, 5, 5, 6, 1, 7, 3],
|
||||||
[6, 1, 4, 1, 3, 3, 6, 1, 4, 6],
|
[6, 1, 4, 1, 3, 3, 6, 1, 4, 6],
|
||||||
[6, 3, 5, 7, 3, 8, 5, 4, 7, 8],
|
[6, 3, 5, 7, 3, 8, 5, 4, 7, 8],
|
||||||
[4, 1, 6, 7, 5, 2, 4, 6, 4, 5],
|
[4, 1, 6, 7, 5, 2, 4, 6, 4, 5],
|
||||||
[2, 1, 7, 6, 8, 4, 1, 7, 2, 1],
|
[2, 1, 7, 6, 8, 4, 1, 7, 2, 1],
|
||||||
[6, 8, 8, 2, 8, 8, 1, 1, 3, 4],
|
[6, 8, 8, 2, 8, 8, 1, 1, 3, 4],
|
||||||
[4, 8, 4, 6, 8, 4, 8, 5, 5, 4],
|
[4, 8, 4, 6, 8, 4, 8, 5, 5, 4],
|
||||||
[5, 2, 8, 3, 7, 5, 1, 5, 2, 6]
|
[5, 2, 8, 3, 7, 5, 1, 5, 2, 6],
|
||||||
];
|
];
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 195);
|
assert_eq!(result, 195);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
233
src/day12.rs
233
src/day12.rs
@ -1,160 +1,175 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Vec<(String, String)> {
|
pub fn parse_input(input: &str) -> Vec<(String, String)> {
|
||||||
let mut edges: Vec<(String, String)> = Vec::new();
|
let mut edges: Vec<(String, String)> = Vec::new();
|
||||||
|
|
||||||
for line in input.lines() {
|
for line in input.lines() {
|
||||||
let (from, to) = line.split_once('-').unwrap();
|
let (from, to) = line.split_once('-').unwrap();
|
||||||
edges.push((from.into(), to.into()));
|
edges.push((from.into(), to.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return edges;
|
return edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edges_to_map(edges: &Vec<(String, String)>) -> HashMap<String, Vec<String>> {
|
fn edges_to_map(edges: &Vec<(String, String)>) -> HashMap<String, Vec<String>> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for line in edges {
|
for line in edges {
|
||||||
map.entry(line.1.clone())
|
map
|
||||||
.or_insert(Vec::new())
|
.entry(line.1.clone())
|
||||||
.push(line.0.clone());
|
.or_insert(Vec::new())
|
||||||
|
.push(line.0.clone());
|
||||||
|
|
||||||
map.entry(line.0.clone())
|
map
|
||||||
.or_insert(Vec::new())
|
.entry(line.0.clone())
|
||||||
.push(line.1.clone());
|
.or_insert(Vec::new())
|
||||||
}
|
.push(line.1.clone());
|
||||||
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_path_finished(path: &Vec<&str>) -> bool {
|
fn is_path_finished(path: &Vec<&str>) -> bool {
|
||||||
return path.contains(&"end")
|
return path.contains(&"end");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_be_appended_part1(path: &Vec<&str>, node: &str) -> bool {
|
fn can_be_appended_part1(path: &Vec<&str>, node: &str) -> bool {
|
||||||
node.to_uppercase() == node || !path.contains(&node)
|
node.to_uppercase() == node || !path.contains(&node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(edges: &Vec<(String, String)>) -> usize {
|
pub fn part1(edges: &Vec<(String, String)>) -> usize {
|
||||||
let map = edges_to_map(edges);
|
let map = edges_to_map(edges);
|
||||||
|
|
||||||
let mut finished_paths: Vec<Vec<&str>> = Vec::new();
|
let mut finished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
|
|
||||||
let mut unfinished_paths: Vec<Vec<&str>> = Vec::new();
|
let mut unfinished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
unfinished_paths.push(vec!["start"]);
|
unfinished_paths.push(vec!["start"]);
|
||||||
while unfinished_paths.len() > 0 {
|
while unfinished_paths.len() > 0 {
|
||||||
let mut new_paths = Vec::new();
|
let mut new_paths = Vec::new();
|
||||||
|
|
||||||
for path in &mut unfinished_paths {
|
for path in &mut unfinished_paths {
|
||||||
for node in map.get(*path.last().unwrap()).unwrap() {
|
for node in map.get(*path.last().unwrap()).unwrap() {
|
||||||
if can_be_appended_part1(path, node) {
|
if can_be_appended_part1(path, node) {
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(node);
|
new_path.push(node);
|
||||||
|
|
||||||
if is_path_finished(&new_path) {
|
if is_path_finished(&new_path) {
|
||||||
finished_paths.push(new_path);
|
finished_paths.push(new_path);
|
||||||
} else {
|
} else {
|
||||||
new_paths.push(new_path);
|
new_paths.push(new_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unfinished_paths = new_paths;
|
unfinished_paths = new_paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
return finished_paths.len();
|
return finished_paths.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_be_appended_part2(path: &Vec<&str>, node: &str) -> bool {
|
fn can_be_appended_part2(path: &Vec<&str>, node: &str) -> bool {
|
||||||
if node == "start" { return false; }
|
if node == "start" {
|
||||||
if node == "end" { return true; }
|
return false;
|
||||||
if node.to_uppercase() == node { return true; }
|
}
|
||||||
|
if node == "end" {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if node.to_uppercase() == node {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// If all lowercase nodes only apear once we can be assure that any lowercase
|
// If all lowercase nodes only apear once we can be assure that any lowercase
|
||||||
// node that will be added will be correct.
|
// node that will be added will be correct.
|
||||||
let mut uniq = HashSet::new();
|
let mut uniq = HashSet::new();
|
||||||
if path.into_iter().all(move |x| x.to_lowercase() != *x || uniq.insert(x)) {
|
if path
|
||||||
return true;
|
.into_iter()
|
||||||
}
|
.all(move |x| x.to_lowercase() != *x || uniq.insert(x))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return !path.contains(&node);
|
return !path.contains(&node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(edges: &Vec<(String, String)>) -> usize {
|
pub fn part2(edges: &Vec<(String, String)>) -> usize {
|
||||||
let map = edges_to_map(edges);
|
let map = edges_to_map(edges);
|
||||||
|
|
||||||
let mut finished_paths: Vec<Vec<&str>> = Vec::new();
|
let mut finished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
|
|
||||||
let mut unfinished_paths: Vec<Vec<&str>> = Vec::new();
|
let mut unfinished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
unfinished_paths.push(vec!["start"]);
|
unfinished_paths.push(vec!["start"]);
|
||||||
while unfinished_paths.len() > 0 {
|
while unfinished_paths.len() > 0 {
|
||||||
let mut new_paths = Vec::new();
|
let mut new_paths = Vec::new();
|
||||||
|
|
||||||
for path in &mut unfinished_paths {
|
for path in &mut unfinished_paths {
|
||||||
for node in map.get(*path.last().unwrap()).unwrap() {
|
for node in map.get(*path.last().unwrap()).unwrap() {
|
||||||
if can_be_appended_part2(path, node) {
|
if can_be_appended_part2(path, node) {
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(node);
|
new_path.push(node);
|
||||||
|
|
||||||
if is_path_finished(&new_path) {
|
if is_path_finished(&new_path) {
|
||||||
finished_paths.push(new_path);
|
finished_paths.push(new_path);
|
||||||
} else {
|
} else {
|
||||||
new_paths.push(new_path);
|
new_paths.push(new_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unfinished_paths = new_paths;
|
unfinished_paths = new_paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
return finished_paths.len();
|
return finished_paths.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let cave_system = parse_input("start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end");
|
let cave_system = parse_input("start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end");
|
||||||
let result = part1(&cave_system);
|
let result = part1(&cave_system);
|
||||||
assert_eq!(result, 10);
|
assert_eq!(result, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_larger_example() {
|
fn part1_larger_example() {
|
||||||
let cave_system = parse_input("dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc");
|
let cave_system = parse_input(
|
||||||
let result = part1(&cave_system);
|
"dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc",
|
||||||
assert_eq!(result, 19);
|
);
|
||||||
}
|
let result = part1(&cave_system);
|
||||||
|
assert_eq!(result, 19);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_largest_example() {
|
fn part1_largest_example() {
|
||||||
let cave_system = parse_input("fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW");
|
let cave_system = parse_input("fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW");
|
||||||
let result = part1(&cave_system);
|
let result = part1(&cave_system);
|
||||||
assert_eq!(result, 226);
|
assert_eq!(result, 226);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let cave_system = parse_input("start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end");
|
let cave_system = parse_input("start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end");
|
||||||
let result = part2(&cave_system);
|
let result = part2(&cave_system);
|
||||||
assert_eq!(result, 36);
|
assert_eq!(result, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_larger_example() {
|
fn part2_larger_example() {
|
||||||
let cave_system = parse_input("dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc");
|
let cave_system = parse_input(
|
||||||
let result = part2(&cave_system);
|
"dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc",
|
||||||
assert_eq!(result, 103);
|
);
|
||||||
}
|
let result = part2(&cave_system);
|
||||||
|
assert_eq!(result, 103);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_largest_example() {
|
fn part2_largest_example() {
|
||||||
let cave_system = parse_input("fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW");
|
let cave_system = parse_input("fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW");
|
||||||
let result = part2(&cave_system);
|
let result = part2(&cave_system);
|
||||||
assert_eq!(result, 3509);
|
assert_eq!(result, 3509);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
215
src/day13.rs
215
src/day13.rs
@ -5,156 +5,143 @@ pub struct Dot(u32, u32);
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Fold {
|
pub enum Fold {
|
||||||
X(u32),
|
X(u32),
|
||||||
Y(u32),
|
Y(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InputData {
|
pub struct InputData {
|
||||||
dots: Vec<Dot>,
|
dots: Vec<Dot>,
|
||||||
folds: Vec<Fold>
|
folds: Vec<Fold>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_dot(line: &str) -> Dot {
|
fn parse_dot(line: &str) -> Dot {
|
||||||
let (x, y) = line.split_once(',').unwrap();
|
let (x, y) = line.split_once(',').unwrap();
|
||||||
return Dot(
|
return Dot(x.parse().unwrap(), y.parse().unwrap());
|
||||||
x.parse().unwrap(),
|
|
||||||
y.parse().unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fold(line: &str) -> Fold {
|
fn parse_fold(line: &str) -> Fold {
|
||||||
let (axis, coordinate_str) = line.split_once('=').unwrap();
|
let (axis, coordinate_str) = line.split_once('=').unwrap();
|
||||||
let coordinate = coordinate_str.parse().unwrap();
|
let coordinate = coordinate_str.parse().unwrap();
|
||||||
match axis {
|
match axis {
|
||||||
"fold along x" => Fold::X(coordinate),
|
"fold along x" => Fold::X(coordinate),
|
||||||
"fold along y" => Fold::Y(coordinate),
|
"fold along y" => Fold::Y(coordinate),
|
||||||
_ => unreachable!("Unable to parse fold direction")
|
_ => unreachable!("Unable to parse fold direction"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> InputData {
|
pub fn parse_input(input: &str) -> InputData {
|
||||||
let (dots_section, folds_section) = input.split_once("\n\n").unwrap();
|
let (dots_section, folds_section) = input.split_once("\n\n").unwrap();
|
||||||
|
|
||||||
let dots = dots_section.lines()
|
let dots = dots_section.lines().map(parse_dot).collect();
|
||||||
.map(parse_dot)
|
let folds = folds_section.lines().map(parse_fold).collect();
|
||||||
.collect();
|
|
||||||
let folds = folds_section.lines()
|
|
||||||
.map(parse_fold)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
return InputData {
|
return InputData { dots, folds };
|
||||||
dots,
|
|
||||||
folds
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_fold(dots: &HashSet<Dot>, fold: &Fold) -> HashSet<Dot> {
|
fn perform_fold(dots: &HashSet<Dot>, fold: &Fold) -> HashSet<Dot> {
|
||||||
let mut folded_dots = HashSet::new();
|
let mut folded_dots = HashSet::new();
|
||||||
|
|
||||||
for dot in dots.iter() {
|
for dot in dots.iter() {
|
||||||
let folded_dot = match fold {
|
let folded_dot = match fold {
|
||||||
Fold::X(x) => {
|
Fold::X(x) => {
|
||||||
if dot.0 > *x {
|
if dot.0 > *x {
|
||||||
Dot(2*x - dot.0, dot.1)
|
Dot(2 * x - dot.0, dot.1)
|
||||||
} else {
|
} else {
|
||||||
dot.clone()
|
dot.clone()
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Fold::Y(y) => {
|
Fold::Y(y) => {
|
||||||
if dot.1 > *y {
|
if dot.1 > *y {
|
||||||
Dot(dot.0, 2*y - dot.1)
|
Dot(dot.0, 2 * y - dot.1)
|
||||||
} else {
|
} else {
|
||||||
dot.clone()
|
dot.clone()
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
folded_dots.insert(folded_dot);
|
folded_dots.insert(folded_dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return folded_dots;
|
return folded_dots;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: &InputData) -> usize {
|
pub fn part1(input: &InputData) -> usize {
|
||||||
let mut folded_dots = HashSet::new();
|
let mut folded_dots = HashSet::new();
|
||||||
for dot in &input.dots {
|
for dot in &input.dots {
|
||||||
folded_dots.insert(dot.clone());
|
folded_dots.insert(dot.clone());
|
||||||
}
|
}
|
||||||
folded_dots = perform_fold(&folded_dots, &input.folds[0]);
|
folded_dots = perform_fold(&folded_dots, &input.folds[0]);
|
||||||
folded_dots.len() as usize
|
folded_dots.len() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_dot_bounds(dots: &HashSet<Dot>) -> (u32, u32, u32, u32) {
|
fn determine_dot_bounds(dots: &HashSet<Dot>) -> (u32, u32, u32, u32) {
|
||||||
let mut min_x = u32::MAX;
|
let mut min_x = u32::MAX;
|
||||||
let mut min_y = u32::MAX;
|
let mut min_y = u32::MAX;
|
||||||
let mut max_x = u32::MIN;
|
let mut max_x = u32::MIN;
|
||||||
let mut max_y = u32::MIN;
|
let mut max_y = u32::MIN;
|
||||||
|
|
||||||
for dot in dots {
|
for dot in dots {
|
||||||
min_x = min_x.min(dot.0);
|
min_x = min_x.min(dot.0);
|
||||||
min_y = min_y.min(dot.1);
|
min_y = min_y.min(dot.1);
|
||||||
max_x = max_x.max(dot.0);
|
max_x = max_x.max(dot.0);
|
||||||
max_y = max_y.max(dot.1);
|
max_y = max_y.max(dot.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (min_x, min_y, max_x, max_y);
|
return (min_x, min_y, max_x, max_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_dots(dots: &HashSet<Dot>) {
|
fn render_dots(dots: &HashSet<Dot>) {
|
||||||
let (min_x, min_y, max_x, max_y) = determine_dot_bounds(dots);
|
let (min_x, min_y, max_x, max_y) = determine_dot_bounds(dots);
|
||||||
for y in min_y..=max_y {
|
for y in min_y..=max_y {
|
||||||
for x in min_x..=max_x {
|
for x in min_x..=max_x {
|
||||||
if dots.contains(&Dot(x, y)) {
|
if dots.contains(&Dot(x, y)) {
|
||||||
print!("#");
|
print!("#");
|
||||||
} else {
|
} else {
|
||||||
print!(".");
|
print!(".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print!("\n");
|
print!("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: &InputData) {
|
pub fn part2(input: &InputData) {
|
||||||
let mut folded_dots = HashSet::new();
|
let mut folded_dots = HashSet::new();
|
||||||
for dot in &input.dots {
|
for dot in &input.dots {
|
||||||
folded_dots.insert(dot.clone());
|
folded_dots.insert(dot.clone());
|
||||||
}
|
}
|
||||||
for fold in &input.folds {
|
for fold in &input.folds {
|
||||||
folded_dots = perform_fold(&folded_dots, fold);
|
folded_dots = perform_fold(&folded_dots, fold);
|
||||||
}
|
}
|
||||||
render_dots(&folded_dots);
|
render_dots(&folded_dots);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let dots = vec![
|
let dots = vec![
|
||||||
Dot(6, 10),
|
Dot(6, 10),
|
||||||
Dot(0, 14),
|
Dot(0, 14),
|
||||||
Dot(9, 10),
|
Dot(9, 10),
|
||||||
Dot(0, 3),
|
Dot(0, 3),
|
||||||
Dot(10, 4),
|
Dot(10, 4),
|
||||||
Dot(4, 11),
|
Dot(4, 11),
|
||||||
Dot(6, 0),
|
Dot(6, 0),
|
||||||
Dot(6, 12),
|
Dot(6, 12),
|
||||||
Dot(4, 1),
|
Dot(4, 1),
|
||||||
Dot(0, 13),
|
Dot(0, 13),
|
||||||
Dot(10, 12),
|
Dot(10, 12),
|
||||||
Dot(3, 4),
|
Dot(3, 4),
|
||||||
Dot(3, 0),
|
Dot(3, 0),
|
||||||
Dot(8, 4),
|
Dot(8, 4),
|
||||||
Dot(1, 10),
|
Dot(1, 10),
|
||||||
Dot(2 ,14),
|
Dot(2, 14),
|
||||||
Dot(8 ,10),
|
Dot(8, 10),
|
||||||
Dot(9, 0)
|
Dot(9, 0),
|
||||||
];
|
];
|
||||||
let folds = vec![
|
let folds = vec![Fold::Y(7), Fold::X(5)];
|
||||||
Fold::Y(7),
|
let result = part1(&InputData { dots, folds });
|
||||||
Fold::X(5),
|
assert_eq!(result, 17);
|
||||||
];
|
}
|
||||||
let result = part1(&InputData{ dots, folds });
|
|
||||||
assert_eq!(result, 17);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
285
src/day14.rs
285
src/day14.rs
@ -1,160 +1,187 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct InputData {
|
pub struct InputData {
|
||||||
polymer_template: String,
|
polymer_template: String,
|
||||||
rules: HashMap<[char; 2], char>
|
rules: HashMap<[char; 2], char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> InputData {
|
pub fn parse_input(input: &str) -> InputData {
|
||||||
let (polymer_template, rules_section) = input.split_once("\n\n").unwrap();
|
let (polymer_template, rules_section) = input.split_once("\n\n").unwrap();
|
||||||
let mut rules = HashMap::new();
|
let mut rules = HashMap::new();
|
||||||
for line in rules_section.lines() {
|
for line in rules_section.lines() {
|
||||||
let (pattern, expansion) = line.split_once(" -> ").unwrap();
|
let (pattern, expansion) = line.split_once(" -> ").unwrap();
|
||||||
rules.insert(
|
rules.insert(
|
||||||
[pattern.chars().nth(0).unwrap(), pattern.chars().nth(1).unwrap()],
|
[
|
||||||
expansion.chars().nth(0).unwrap()
|
pattern.chars().nth(0).unwrap(),
|
||||||
);
|
pattern.chars().nth(1).unwrap(),
|
||||||
}
|
],
|
||||||
return InputData {
|
expansion.chars().nth(0).unwrap(),
|
||||||
polymer_template: polymer_template.into(),
|
);
|
||||||
rules
|
}
|
||||||
};
|
return InputData {
|
||||||
|
polymer_template: polymer_template.into(),
|
||||||
|
rules,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn naive_expand_polymer(polymer: Vec<char>, rules: &HashMap<[char; 2], char>) -> Vec<char> {
|
fn naive_expand_polymer(polymer: Vec<char>, rules: &HashMap<[char; 2], char>) -> Vec<char> {
|
||||||
let mut new_polymer = Vec::new();
|
let mut new_polymer = Vec::new();
|
||||||
for i in 0..polymer.len()-1 {
|
for i in 0..polymer.len() - 1 {
|
||||||
new_polymer.push(polymer[i]);
|
new_polymer.push(polymer[i]);
|
||||||
let pair = [polymer[i], polymer[i+1]];
|
let pair = [polymer[i], polymer[i + 1]];
|
||||||
let rule = rules.get(&pair);
|
let rule = rules.get(&pair);
|
||||||
if rule != None {
|
if rule != None {
|
||||||
new_polymer.push(*rule.unwrap());
|
new_polymer.push(*rule.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_polymer.push(*polymer.last().unwrap());
|
new_polymer.push(*polymer.last().unwrap());
|
||||||
return new_polymer;
|
return new_polymer;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: &InputData) -> u32 {
|
pub fn part1(input: &InputData) -> u32 {
|
||||||
let mut polymer = input.polymer_template.chars().collect();
|
let mut polymer = input.polymer_template.chars().collect();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
polymer = naive_expand_polymer(polymer, &input.rules);
|
polymer = naive_expand_polymer(polymer, &input.rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut element_amounts = HashMap::new();
|
let mut element_amounts = HashMap::new();
|
||||||
for c in polymer {
|
for c in polymer {
|
||||||
let amount = element_amounts.entry(c).or_insert(0);
|
let amount = element_amounts.entry(c).or_insert(0);
|
||||||
*amount += 1;
|
*amount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let least_common_element = element_amounts.iter().min_by(|a, b| a.1.cmp(b.1)).unwrap().1;
|
let least_common_element = element_amounts
|
||||||
let most_common_element = element_amounts.iter().max_by(|a, b| a.1.cmp(b.1)).unwrap().1;
|
.iter()
|
||||||
return most_common_element - least_common_element;
|
.min_by(|a, b| a.1.cmp(b.1))
|
||||||
|
.unwrap()
|
||||||
|
.1;
|
||||||
|
let most_common_element = element_amounts
|
||||||
|
.iter()
|
||||||
|
.max_by(|a, b| a.1.cmp(b.1))
|
||||||
|
.unwrap()
|
||||||
|
.1;
|
||||||
|
return most_common_element - least_common_element;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_polymer(polymer_pairs: &HashMap<[char; 2], u64>, rules: &HashMap<[char; 2], char>) -> HashMap<[char; 2], u64> {
|
fn expand_polymer(
|
||||||
let mut new_pairs = HashMap::new();
|
polymer_pairs: &HashMap<[char; 2], u64>,
|
||||||
for entry in polymer_pairs {
|
rules: &HashMap<[char; 2], char>,
|
||||||
let pair = entry.0;
|
) -> HashMap<[char; 2], u64> {
|
||||||
if rules.contains_key(pair) {
|
let mut new_pairs = HashMap::new();
|
||||||
let rule = *rules.get(pair).unwrap();
|
for entry in polymer_pairs {
|
||||||
|
let pair = entry.0;
|
||||||
|
if rules.contains_key(pair) {
|
||||||
|
let rule = *rules.get(pair).unwrap();
|
||||||
|
|
||||||
let left_pair = [pair[0], rule];
|
let left_pair = [pair[0], rule];
|
||||||
let left_entry = new_pairs.entry(left_pair).or_insert(0);
|
let left_entry = new_pairs.entry(left_pair).or_insert(0);
|
||||||
*left_entry += entry.1;
|
*left_entry += entry.1;
|
||||||
|
|
||||||
let right_pair = [rule, pair[1]];
|
|
||||||
let right_entry = new_pairs.entry(right_pair).or_insert(0);
|
|
||||||
*right_entry += entry.1;
|
|
||||||
} else {
|
|
||||||
let new_entry = new_pairs.entry(*entry.0).or_insert(0);
|
|
||||||
*new_entry = *entry.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new_pairs;
|
|
||||||
|
|
||||||
|
let right_pair = [rule, pair[1]];
|
||||||
|
let right_entry = new_pairs.entry(right_pair).or_insert(0);
|
||||||
|
*right_entry += entry.1;
|
||||||
|
} else {
|
||||||
|
let new_entry = new_pairs.entry(*entry.0).or_insert(0);
|
||||||
|
*new_entry = *entry.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: &InputData) -> u64 {
|
pub fn part2(input: &InputData) -> u64 {
|
||||||
let polymer_template = &input.polymer_template;
|
let polymer_template = &input.polymer_template;
|
||||||
let mut polymer_pairs = HashMap::new();
|
let mut polymer_pairs = HashMap::new();
|
||||||
for i in 0..polymer_template.len()-1 {
|
for i in 0..polymer_template.len() - 1 {
|
||||||
let pair = [
|
let pair = [
|
||||||
polymer_template.chars().nth(i).unwrap(),
|
polymer_template.chars().nth(i).unwrap(),
|
||||||
polymer_template.chars().nth(i+1).unwrap()
|
polymer_template.chars().nth(i + 1).unwrap(),
|
||||||
];
|
];
|
||||||
let entry = polymer_pairs.entry(pair).or_insert(0);
|
let entry = polymer_pairs.entry(pair).or_insert(0);
|
||||||
*entry += 1;
|
*entry += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..40 {
|
for _ in 0..40 {
|
||||||
polymer_pairs = expand_polymer(&polymer_pairs, &input.rules);
|
polymer_pairs = expand_polymer(&polymer_pairs, &input.rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut element_amounts = HashMap::new();
|
let mut element_amounts = HashMap::new();
|
||||||
for entry in polymer_pairs {
|
for entry in polymer_pairs {
|
||||||
for c in entry.0 {
|
for c in entry.0 {
|
||||||
let amount = element_amounts.entry(c).or_insert(0);
|
let amount = element_amounts.entry(c).or_insert(0);
|
||||||
*amount += entry.1;
|
*amount += entry.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let least_common_element = element_amounts.iter().min_by(|a, b| a.1.cmp(b.1)).unwrap().1;
|
let least_common_element = element_amounts
|
||||||
let most_common_element = element_amounts.iter().max_by(|a, b| a.1.cmp(b.1)).unwrap().1;
|
.iter()
|
||||||
return (most_common_element - least_common_element)/2 + 1;
|
.min_by(|a, b| a.1.cmp(b.1))
|
||||||
|
.unwrap()
|
||||||
|
.1;
|
||||||
|
let most_common_element = element_amounts
|
||||||
|
.iter()
|
||||||
|
.max_by(|a, b| a.1.cmp(b.1))
|
||||||
|
.unwrap()
|
||||||
|
.1;
|
||||||
|
return (most_common_element - least_common_element) / 2 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let polymer_template = String::from("NNCB");
|
let polymer_template = String::from("NNCB");
|
||||||
let rules = HashMap::from([
|
let rules = HashMap::from([
|
||||||
(['C', 'H'], 'B'),
|
(['C', 'H'], 'B'),
|
||||||
(['H', 'H'], 'N'),
|
(['H', 'H'], 'N'),
|
||||||
(['C', 'B'], 'H'),
|
(['C', 'B'], 'H'),
|
||||||
(['N', 'H'], 'C'),
|
(['N', 'H'], 'C'),
|
||||||
(['H', 'B'], 'C'),
|
(['H', 'B'], 'C'),
|
||||||
(['H', 'C'], 'B'),
|
(['H', 'C'], 'B'),
|
||||||
(['H', 'N'], 'C'),
|
(['H', 'N'], 'C'),
|
||||||
(['N', 'N'], 'C'),
|
(['N', 'N'], 'C'),
|
||||||
(['B', 'H'], 'H'),
|
(['B', 'H'], 'H'),
|
||||||
(['N', 'C'], 'B'),
|
(['N', 'C'], 'B'),
|
||||||
(['N', 'B'], 'B'),
|
(['N', 'B'], 'B'),
|
||||||
(['B', 'N'], 'B'),
|
(['B', 'N'], 'B'),
|
||||||
(['B', 'B'], 'N'),
|
(['B', 'B'], 'N'),
|
||||||
(['B', 'C'], 'B'),
|
(['B', 'C'], 'B'),
|
||||||
(['C', 'C'], 'N'),
|
(['C', 'C'], 'N'),
|
||||||
(['C', 'N'], 'C')
|
(['C', 'N'], 'C'),
|
||||||
]);
|
]);
|
||||||
let result = part1(&InputData { polymer_template, rules });
|
let result = part1(&InputData {
|
||||||
assert_eq!(result, 1588);
|
polymer_template,
|
||||||
}
|
rules,
|
||||||
|
});
|
||||||
|
assert_eq!(result, 1588);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let polymer_template = String::from("NNCB");
|
let polymer_template = String::from("NNCB");
|
||||||
let rules = HashMap::from([
|
let rules = HashMap::from([
|
||||||
(['C', 'H'], 'B'),
|
(['C', 'H'], 'B'),
|
||||||
(['H', 'H'], 'N'),
|
(['H', 'H'], 'N'),
|
||||||
(['C', 'B'], 'H'),
|
(['C', 'B'], 'H'),
|
||||||
(['N', 'H'], 'C'),
|
(['N', 'H'], 'C'),
|
||||||
(['H', 'B'], 'C'),
|
(['H', 'B'], 'C'),
|
||||||
(['H', 'C'], 'B'),
|
(['H', 'C'], 'B'),
|
||||||
(['H', 'N'], 'C'),
|
(['H', 'N'], 'C'),
|
||||||
(['N', 'N'], 'C'),
|
(['N', 'N'], 'C'),
|
||||||
(['B', 'H'], 'H'),
|
(['B', 'H'], 'H'),
|
||||||
(['N', 'C'], 'B'),
|
(['N', 'C'], 'B'),
|
||||||
(['N', 'B'], 'B'),
|
(['N', 'B'], 'B'),
|
||||||
(['B', 'N'], 'B'),
|
(['B', 'N'], 'B'),
|
||||||
(['B', 'B'], 'N'),
|
(['B', 'B'], 'N'),
|
||||||
(['B', 'C'], 'B'),
|
(['B', 'C'], 'B'),
|
||||||
(['C', 'C'], 'N'),
|
(['C', 'C'], 'N'),
|
||||||
(['C', 'N'], 'C')
|
(['C', 'N'], 'C'),
|
||||||
]);
|
]);
|
||||||
let result = part2(&InputData { polymer_template, rules });
|
let result = part2(&InputData {
|
||||||
assert_eq!(result, 2188189693529);
|
polymer_template,
|
||||||
}
|
rules,
|
||||||
|
});
|
||||||
|
assert_eq!(result, 2188189693529);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
184
src/day15.rs
184
src/day15.rs
@ -1,114 +1,120 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use priority_queue::PriorityQueue;
|
use priority_queue::PriorityQueue;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Grid {
|
pub struct Grid {
|
||||||
rows: u32,
|
rows: u32,
|
||||||
cols: u32,
|
cols: u32,
|
||||||
data: Vec<u32>,
|
data: Vec<u32>,
|
||||||
scale: u32
|
scale: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
fn new(rows: u32, cols: u32, data: Vec<u32>) -> Grid {
|
fn new(rows: u32, cols: u32, data: Vec<u32>) -> Grid {
|
||||||
return Grid {
|
return Grid {
|
||||||
rows, cols, data,
|
rows,
|
||||||
scale: 1
|
cols,
|
||||||
}
|
data,
|
||||||
}
|
scale: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn get(&self, point: &(u32, u32)) -> u32 {
|
fn get(&self, point: &(u32, u32)) -> u32 {
|
||||||
let row = point.0 % self.rows;
|
let row = point.0 % self.rows;
|
||||||
let col = point.1 % self.cols;
|
let col = point.1 % self.cols;
|
||||||
let value = self.data[(row * self.cols + col) as usize];
|
let value = self.data[(row * self.cols + col) as usize];
|
||||||
return (value + point.0 / self.rows + point.1 / self.cols - 1) % 9 + 1;
|
return (value + point.0 / self.rows + point.1 / self.cols - 1) % 9 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width(&self) -> u32 {
|
fn width(&self) -> u32 {
|
||||||
self.cols * self.scale
|
self.cols * self.scale
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height(&self) -> u32 {
|
fn height(&self) -> u32 {
|
||||||
self.rows * self.scale
|
self.rows * self.scale
|
||||||
}
|
}
|
||||||
|
|
||||||
fn within_bounds(&self, point: &(i32, i32)) -> bool {
|
fn within_bounds(&self, point: &(i32, i32)) -> bool {
|
||||||
point.0 >= 0 && point.1 >= 0 && point.0 < self.height() as i32 && point.1 < self.width() as i32
|
point.0 >= 0 && point.1 >= 0 && point.0 < self.height() as i32 && point.1 < self.width() as i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Grid {
|
pub fn parse_input(input: &str) -> Grid {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let mut rows = 0;
|
let mut rows = 0;
|
||||||
let mut cols = 0;
|
let mut cols = 0;
|
||||||
for line in input.lines() {
|
for line in input.lines() {
|
||||||
rows += 1;
|
rows += 1;
|
||||||
for c in line.chars() {
|
for c in line.chars() {
|
||||||
if rows == 1 { cols += 1 }
|
if rows == 1 {
|
||||||
data.push(c.to_digit(10).unwrap());
|
cols += 1
|
||||||
}
|
}
|
||||||
}
|
data.push(c.to_digit(10).unwrap());
|
||||||
return Grid::new(rows, cols, data);
|
}
|
||||||
|
}
|
||||||
|
return Grid::new(rows, cols, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_neighbours(pos: &(u32, u32), grid: &Grid, offsets: &[(i32, i32)]) -> Vec<(u32, u32)> {
|
fn find_neighbours(pos: &(u32, u32), grid: &Grid, offsets: &[(i32, i32)]) -> Vec<(u32, u32)> {
|
||||||
let mut neighbours = Vec::new();
|
let mut neighbours = Vec::new();
|
||||||
for offset in offsets {
|
for offset in offsets {
|
||||||
let row = pos.0 as i32 + offset.0;
|
let row = pos.0 as i32 + offset.0;
|
||||||
let col = pos.1 as i32 + offset.1;
|
let col = pos.1 as i32 + offset.1;
|
||||||
if grid.within_bounds(&(row, col)) {
|
if grid.within_bounds(&(row, col)) {
|
||||||
neighbours.push((row as u32, col as u32));
|
neighbours.push((row as u32, col as u32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_shortest_path_cost(grid: &Grid) -> u32 {
|
fn find_shortest_path_cost(grid: &Grid) -> u32 {
|
||||||
let mut total_costs: HashMap<(u32, u32), u32> = HashMap::new();
|
let mut total_costs: HashMap<(u32, u32), u32> = HashMap::new();
|
||||||
let mut min_pq: PriorityQueue<(u32, u32), i32> = PriorityQueue::new();
|
let mut min_pq: PriorityQueue<(u32, u32), i32> = PriorityQueue::new();
|
||||||
let mut visited: HashSet<(u32, u32)> = HashSet::new();
|
let mut visited: HashSet<(u32, u32)> = HashSet::new();
|
||||||
let neighbour_offsets = [ (0, 1), (0, -1), (1, 0), (-1, 0) ];
|
let neighbour_offsets = [(0, 1), (0, -1), (1, 0), (-1, 0)];
|
||||||
min_pq.push((0, 0), 0);
|
min_pq.push((0, 0), 0);
|
||||||
total_costs.insert((0, 0), 0);
|
total_costs.insert((0, 0), 0);
|
||||||
|
|
||||||
while !min_pq.is_empty() {
|
while !min_pq.is_empty() {
|
||||||
let new_smallest = min_pq.pop().unwrap().0;
|
let new_smallest = min_pq.pop().unwrap().0;
|
||||||
visited.insert(new_smallest);
|
visited.insert(new_smallest);
|
||||||
|
|
||||||
for neighbour in find_neighbours(&new_smallest, grid, &neighbour_offsets) {
|
for neighbour in find_neighbours(&new_smallest, grid, &neighbour_offsets) {
|
||||||
if visited.contains(&neighbour) {
|
if visited.contains(&neighbour) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let alt_distance = grid.get(&neighbour);
|
let alt_distance = grid.get(&neighbour);
|
||||||
let alt_path = total_costs.get(&new_smallest).unwrap_or(&u32::MAX) + alt_distance;
|
let alt_path = total_costs.get(&new_smallest).unwrap_or(&u32::MAX) + alt_distance;
|
||||||
if alt_path < *total_costs.get(&neighbour).unwrap_or(&u32::MAX) {
|
if alt_path < *total_costs.get(&neighbour).unwrap_or(&u32::MAX) {
|
||||||
total_costs.insert(neighbour, alt_path);
|
total_costs.insert(neighbour, alt_path);
|
||||||
min_pq.push_decrease(neighbour, -(alt_path as i32));
|
min_pq.push_decrease(neighbour, -(alt_path as i32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return *total_costs.get(&(grid.height()-1, grid.width()-1)).unwrap();
|
return *total_costs
|
||||||
|
.get(&(grid.height() - 1, grid.width() - 1))
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(grid: &Grid) -> u32 {
|
pub fn part1(grid: &Grid) -> u32 {
|
||||||
find_shortest_path_cost(grid)
|
find_shortest_path_cost(grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(grid: &mut Grid) -> u32 {
|
pub fn part2(grid: &mut Grid) -> u32 {
|
||||||
grid.scale = 5;
|
grid.scale = 5;
|
||||||
find_shortest_path_cost(grid)
|
find_shortest_path_cost(grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let grid = parse_input("1163751742
|
let grid = parse_input(
|
||||||
|
"1163751742
|
||||||
1381373672
|
1381373672
|
||||||
2136511328
|
2136511328
|
||||||
3694931569
|
3694931569
|
||||||
@ -117,14 +123,16 @@ mod tests {
|
|||||||
1359912421
|
1359912421
|
||||||
3125421639
|
3125421639
|
||||||
1293138521
|
1293138521
|
||||||
2311944581");
|
2311944581",
|
||||||
let result = part1(&grid);
|
);
|
||||||
assert_eq!(result, 40);
|
let result = part1(&grid);
|
||||||
}
|
assert_eq!(result, 40);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let mut grid = parse_input("1163751742
|
let mut grid = parse_input(
|
||||||
|
"1163751742
|
||||||
1381373672
|
1381373672
|
||||||
2136511328
|
2136511328
|
||||||
3694931569
|
3694931569
|
||||||
@ -133,9 +141,9 @@ mod tests {
|
|||||||
1359912421
|
1359912421
|
||||||
3125421639
|
3125421639
|
||||||
1293138521
|
1293138521
|
||||||
2311944581");
|
2311944581",
|
||||||
let result = part2(&mut grid);
|
);
|
||||||
assert_eq!(result, 315);
|
let result = part2(&mut grid);
|
||||||
}
|
assert_eq!(result, 315);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
393
src/day16.rs
393
src/day16.rs
@ -1,256 +1,267 @@
|
|||||||
|
|
||||||
pub enum PacketBody {
|
pub enum PacketBody {
|
||||||
Literal(u64),
|
Literal(u64),
|
||||||
Operator(Vec<Packet>)
|
Operator(Vec<Packet>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Packet {
|
pub struct Packet {
|
||||||
version: u8,
|
version: u8,
|
||||||
r#type: u8,
|
r#type: u8,
|
||||||
body: PacketBody
|
body: PacketBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_bits(hex: &str) -> String {
|
fn to_bits(hex: &str) -> String {
|
||||||
let mut bits = String::new();
|
let mut bits = String::new();
|
||||||
for c in hex.bytes() {
|
for c in hex.bytes() {
|
||||||
match c {
|
match c {
|
||||||
b'0'..=b'9' => bits.push_str(format!("{:0>4b}", c - b'0').as_str()),
|
b'0'..=b'9' => bits.push_str(format!("{:0>4b}", c - b'0').as_str()),
|
||||||
b'A'..=b'F' => bits.push_str(format!("{:0>4b}", c - b'A' + 10).as_str()),
|
b'A'..=b'F' => bits.push_str(format!("{:0>4b}", c - b'A' + 10).as_str()),
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_literal_body(bits_str: &str) -> (PacketBody, u32) {
|
fn parse_literal_body(bits_str: &str) -> (PacketBody, u32) {
|
||||||
let mut value_bits = String::new();
|
let mut value_bits = String::new();
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
let bytes = bits_str.as_bytes();
|
let bytes = bits_str.as_bytes();
|
||||||
loop {
|
loop {
|
||||||
value_bits.push_str(&bits_str[cursor+1..cursor+5]);
|
value_bits.push_str(&bits_str[cursor + 1..cursor + 5]);
|
||||||
if bytes[cursor] == b'0' { break; }
|
if bytes[cursor] == b'0' {
|
||||||
cursor += 5;
|
break;
|
||||||
}
|
}
|
||||||
let value = u64::from_str_radix(&value_bits, 2).unwrap();
|
cursor += 5;
|
||||||
|
}
|
||||||
|
let value = u64::from_str_radix(&value_bits, 2).unwrap();
|
||||||
|
|
||||||
return (PacketBody::Literal(value), (cursor+5) as u32);
|
return (PacketBody::Literal(value), (cursor + 5) as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_operator_body(bits_str: &str) -> (PacketBody, u32) {
|
fn parse_operator_body(bits_str: &str) -> (PacketBody, u32) {
|
||||||
let mut size: usize = 0;
|
let mut size: usize = 0;
|
||||||
let mut packets = Vec::new();
|
let mut packets = Vec::new();
|
||||||
let bytes = bits_str.as_bytes();
|
let bytes = bits_str.as_bytes();
|
||||||
size += 1;
|
size += 1;
|
||||||
if bytes[0] == b'1' {
|
if bytes[0] == b'1' {
|
||||||
size += 11;
|
size += 11;
|
||||||
let count = u32::from_str_radix(&bits_str[1..12], 2).unwrap();
|
let count = u32::from_str_radix(&bits_str[1..12], 2).unwrap();
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let (packet, s) = parse_packet(&bits_str[size..]);
|
let (packet, s) = parse_packet(&bits_str[size..]);
|
||||||
packets.push(packet);
|
packets.push(packet);
|
||||||
size += s as usize;
|
size += s as usize;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let total_size = u32::from_str_radix(&bits_str[1..16], 2).unwrap();
|
let total_size = u32::from_str_radix(&bits_str[1..16], 2).unwrap();
|
||||||
size += 15;
|
size += 15;
|
||||||
while ((size-16) as u32) < total_size {
|
while ((size - 16) as u32) < total_size {
|
||||||
let (packet, s) = parse_packet(&bits_str[size..]);
|
let (packet, s) = parse_packet(&bits_str[size..]);
|
||||||
packets.push(packet);
|
packets.push(packet);
|
||||||
size += s as usize;
|
size += s as usize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (PacketBody::Operator(packets), size as u32);
|
return (PacketBody::Operator(packets), size as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_packet(bits: &str) -> (Packet, u32) {
|
fn parse_packet(bits: &str) -> (Packet, u32) {
|
||||||
let r#type = u8::from_str_radix(&bits[3..6], 2).unwrap();
|
let r#type = u8::from_str_radix(&bits[3..6], 2).unwrap();
|
||||||
let (body, body_size) = match r#type {
|
let (body, body_size) = match r#type {
|
||||||
4 => parse_literal_body(&bits[6..]),
|
4 => parse_literal_body(&bits[6..]),
|
||||||
_ => parse_operator_body(&bits[6..])
|
_ => parse_operator_body(&bits[6..]),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (Packet {
|
return (
|
||||||
version: u8::from_str_radix(&bits[0..3], 2).unwrap(),
|
Packet {
|
||||||
r#type,
|
version: u8::from_str_radix(&bits[0..3], 2).unwrap(),
|
||||||
body
|
r#type,
|
||||||
},
|
body,
|
||||||
body_size + 6
|
},
|
||||||
);
|
body_size + 6,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Packet {
|
pub fn parse_input(input: &str) -> Packet {
|
||||||
let (packet, _) = parse_packet(&to_bits(input));
|
let (packet, _) = parse_packet(&to_bits(input));
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum_packet_versions(packet: &Packet) -> u32 {
|
fn sum_packet_versions(packet: &Packet) -> u32 {
|
||||||
let mut sum: u32 = packet.version.into();
|
let mut sum: u32 = packet.version.into();
|
||||||
match &packet.body {
|
match &packet.body {
|
||||||
PacketBody::Operator(packets) => {
|
PacketBody::Operator(packets) => {
|
||||||
for sub_packet in packets {
|
for sub_packet in packets {
|
||||||
sum += sum_packet_versions(sub_packet);
|
sum += sum_packet_versions(sub_packet);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => ()
|
_ => (),
|
||||||
};
|
};
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(packet: &Packet) -> u32 {
|
pub fn part1(packet: &Packet) -> u32 {
|
||||||
sum_packet_versions(packet)
|
sum_packet_versions(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_sum_packets(packets: &[Packet]) -> u64 {
|
fn eval_sum_packets(packets: &[Packet]) -> u64 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for packet in packets {
|
for packet in packets {
|
||||||
sum += eval_packet(packet);
|
sum += eval_packet(packet);
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
fn eval_product_packets(packets: &[Packet]) -> u64 {
|
fn eval_product_packets(packets: &[Packet]) -> u64 {
|
||||||
let mut product = 1;
|
let mut product = 1;
|
||||||
for packet in packets {
|
for packet in packets {
|
||||||
product *= eval_packet(packet);
|
product *= eval_packet(packet);
|
||||||
}
|
}
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
fn eval_minimum_packets(packets: &[Packet]) -> u64 {
|
fn eval_minimum_packets(packets: &[Packet]) -> u64 {
|
||||||
let mut min = u64::MAX;
|
let mut min = u64::MAX;
|
||||||
for packet in packets {
|
for packet in packets {
|
||||||
min = min.min(eval_packet(packet));
|
min = min.min(eval_packet(packet));
|
||||||
}
|
}
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
fn eval_maximum_packets(packets: &[Packet]) -> u64 {
|
fn eval_maximum_packets(packets: &[Packet]) -> u64 {
|
||||||
let mut max = 0;
|
let mut max = 0;
|
||||||
for packet in packets {
|
for packet in packets {
|
||||||
max = max.max(eval_packet(packet));
|
max = max.max(eval_packet(packet));
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
fn eval_greater_packets(packets: &[Packet]) -> u64 {
|
fn eval_greater_packets(packets: &[Packet]) -> u64 {
|
||||||
let first_packet = packets.get(0).unwrap();
|
let first_packet = packets.get(0).unwrap();
|
||||||
let second_packet = packets.get(1).unwrap();
|
let second_packet = packets.get(1).unwrap();
|
||||||
if eval_packet(first_packet) > eval_packet(second_packet) { 1 } else { 0 }
|
if eval_packet(first_packet) > eval_packet(second_packet) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn eval_less_packets(packets: &[Packet]) -> u64 {
|
fn eval_less_packets(packets: &[Packet]) -> u64 {
|
||||||
let first_packet = packets.get(0).unwrap();
|
let first_packet = packets.get(0).unwrap();
|
||||||
let second_packet = packets.get(1).unwrap();
|
let second_packet = packets.get(1).unwrap();
|
||||||
if eval_packet(first_packet) < eval_packet(second_packet) { 1 } else { 0 }
|
if eval_packet(first_packet) < eval_packet(second_packet) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn eval_equal_packets(packets: &[Packet]) -> u64 {
|
fn eval_equal_packets(packets: &[Packet]) -> u64 {
|
||||||
let first_packet = packets.get(0).unwrap();
|
let first_packet = packets.get(0).unwrap();
|
||||||
let second_packet = packets.get(1).unwrap();
|
let second_packet = packets.get(1).unwrap();
|
||||||
if eval_packet(first_packet) == eval_packet(second_packet) { 1 } else { 0 }
|
if eval_packet(first_packet) == eval_packet(second_packet) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_packet(packet: &Packet) -> u64 {
|
fn eval_packet(packet: &Packet) -> u64 {
|
||||||
match &packet.body {
|
match &packet.body {
|
||||||
PacketBody::Literal(value) => *value,
|
PacketBody::Literal(value) => *value,
|
||||||
PacketBody::Operator(packets) => {
|
PacketBody::Operator(packets) => match packet.r#type {
|
||||||
match packet.r#type {
|
0 => eval_sum_packets(packets),
|
||||||
0 => eval_sum_packets(packets),
|
1 => eval_product_packets(packets),
|
||||||
1 => eval_product_packets(packets),
|
2 => eval_minimum_packets(packets),
|
||||||
2 => eval_minimum_packets(packets),
|
3 => eval_maximum_packets(packets),
|
||||||
3 => eval_maximum_packets(packets),
|
5 => eval_greater_packets(packets),
|
||||||
5 => eval_greater_packets(packets),
|
6 => eval_less_packets(packets),
|
||||||
6 => eval_less_packets(packets),
|
7 => eval_equal_packets(packets),
|
||||||
7 => eval_equal_packets(packets),
|
_ => unreachable!(),
|
||||||
_ => unreachable!()
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(packet: &Packet) -> u64 {
|
pub fn part2(packet: &Packet) -> u64 {
|
||||||
eval_packet(packet)
|
eval_packet(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example_1() {
|
fn part1_example_1() {
|
||||||
let packet = parse_input("8A004A801A8002F478");
|
let packet = parse_input("8A004A801A8002F478");
|
||||||
let result = part1(&packet);
|
let result = part1(&packet);
|
||||||
assert_eq!(result, 16);
|
assert_eq!(result, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example_2() {
|
fn part1_example_2() {
|
||||||
let packet = parse_input("620080001611562C8802118E34");
|
let packet = parse_input("620080001611562C8802118E34");
|
||||||
let result = part1(&packet);
|
let result = part1(&packet);
|
||||||
assert_eq!(result, 12);
|
assert_eq!(result, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example_3() {
|
fn part1_example_3() {
|
||||||
let packet = parse_input("C0015000016115A2E0802F182340");
|
let packet = parse_input("C0015000016115A2E0802F182340");
|
||||||
let result = part1(&packet);
|
let result = part1(&packet);
|
||||||
assert_eq!(result, 23);
|
assert_eq!(result, 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example_4() {
|
fn part1_example_4() {
|
||||||
let packet = parse_input("A0016C880162017C3686B18A3D4780");
|
let packet = parse_input("A0016C880162017C3686B18A3D4780");
|
||||||
let result = part1(&packet);
|
let result = part1(&packet);
|
||||||
assert_eq!(result, 31);
|
assert_eq!(result, 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_1() {
|
fn part2_example_1() {
|
||||||
let packet = parse_input("C200B40A82");
|
let packet = parse_input("C200B40A82");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 3);
|
assert_eq!(result, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_2() {
|
fn part2_example_2() {
|
||||||
let packet = parse_input("04005AC33890");
|
let packet = parse_input("04005AC33890");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 54);
|
assert_eq!(result, 54);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_3() {
|
fn part2_example_3() {
|
||||||
let packet = parse_input("880086C3E88112");
|
let packet = parse_input("880086C3E88112");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 7);
|
assert_eq!(result, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_4() {
|
fn part2_example_4() {
|
||||||
let packet = parse_input("CE00C43D881120");
|
let packet = parse_input("CE00C43D881120");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 9);
|
assert_eq!(result, 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_5() {
|
fn part2_example_5() {
|
||||||
let packet = parse_input("D8005AC2A8F0");
|
let packet = parse_input("D8005AC2A8F0");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 1);
|
assert_eq!(result, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_6() {
|
fn part2_example_6() {
|
||||||
let packet = parse_input("F600BC2D8F");
|
let packet = parse_input("F600BC2D8F");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 0);
|
assert_eq!(result, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_7() {
|
fn part2_example_7() {
|
||||||
let packet = parse_input("9C005AC2F8F0");
|
let packet = parse_input("9C005AC2F8F0");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 0);
|
assert_eq!(result, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example_8() {
|
fn part2_example_8() {
|
||||||
let packet = parse_input("9C0141080250320F1802104A08");
|
let packet = parse_input("9C0141080250320F1802104A08");
|
||||||
let result = part2(&packet);
|
let result = part2(&packet);
|
||||||
assert_eq!(result, 1);
|
assert_eq!(result, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
142
src/day17.rs
142
src/day17.rs
@ -1,102 +1,108 @@
|
|||||||
|
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
x0: i32, x1: i32,
|
x0: i32,
|
||||||
y0: i32, y1: i32,
|
x1: i32,
|
||||||
|
y0: i32,
|
||||||
|
y1: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Rect {
|
pub fn parse_input(input: &str) -> Rect {
|
||||||
let (x_part, y_part) = input.strip_suffix("\n")
|
let (x_part, y_part) = input
|
||||||
.or(Some(input))
|
.strip_suffix("\n")
|
||||||
.unwrap()
|
.or(Some(input))
|
||||||
.strip_prefix("target area: ")
|
.unwrap()
|
||||||
.unwrap()
|
.strip_prefix("target area: ")
|
||||||
.split_once(", ")
|
.unwrap()
|
||||||
.unwrap();
|
.split_once(", ")
|
||||||
let (x0, x1) = x_part[2..].split_once("..").unwrap();
|
.unwrap();
|
||||||
let (y0, y1) = y_part[2..].split_once("..").unwrap();
|
let (x0, x1) = x_part[2..].split_once("..").unwrap();
|
||||||
return Rect {
|
let (y0, y1) = y_part[2..].split_once("..").unwrap();
|
||||||
x0: x0.parse().unwrap(),
|
return Rect {
|
||||||
x1: x1.parse().unwrap(),
|
x0: x0.parse().unwrap(),
|
||||||
y0: y0.parse().unwrap(),
|
x1: x1.parse().unwrap(),
|
||||||
y1: y1.parse().unwrap()
|
y0: y0.parse().unwrap(),
|
||||||
}
|
y1: y1.parse().unwrap(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(x: i32) -> i32 {
|
fn sign(x: i32) -> i32 {
|
||||||
if x > 0 { 1 } else if x < 0 { -1 } else { 0 }
|
if x > 0 {
|
||||||
|
1
|
||||||
|
} else if x < 0 {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_overshot(px: i32, py: i32, target: &Rect) -> bool {
|
fn is_overshot(px: i32, py: i32, target: &Rect) -> bool {
|
||||||
px > target.x1 || py < target.y0
|
px > target.x1 || py < target.y0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_in_rect(px: i32, py: i32, target: &Rect) -> bool {
|
fn is_in_rect(px: i32, py: i32, target: &Rect) -> bool {
|
||||||
target.x0 <= px && px <= target.x1 &&
|
target.x0 <= px && px <= target.x1 && target.y0 <= py && py <= target.y1
|
||||||
target.y0 <= py && py <= target.y1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simulate(target: &Rect, initial_vx: i32, initial_vy: i32) -> i32 {
|
fn simulate(target: &Rect, initial_vx: i32, initial_vy: i32) -> i32 {
|
||||||
let mut px = 0;
|
let mut px = 0;
|
||||||
let mut py = 0;
|
let mut py = 0;
|
||||||
let mut vx = initial_vx;
|
let mut vx = initial_vx;
|
||||||
let mut vy = initial_vy;
|
let mut vy = initial_vy;
|
||||||
let mut maxy = 0;
|
let mut maxy = 0;
|
||||||
|
|
||||||
while !is_overshot(px, py, target) {
|
while !is_overshot(px, py, target) {
|
||||||
px += vx;
|
px += vx;
|
||||||
py += vy;
|
py += vy;
|
||||||
|
|
||||||
if is_in_rect(px, py, target) {
|
if is_in_rect(px, py, target) {
|
||||||
return maxy;
|
return maxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
maxy = maxy.max(py);
|
maxy = maxy.max(py);
|
||||||
|
|
||||||
vx -= sign(vx);
|
vx -= sign(vx);
|
||||||
vy -= 1;
|
vy -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(target: &Rect) -> i32 {
|
pub fn part1(target: &Rect) -> i32 {
|
||||||
let mut maxy = 0;
|
let mut maxy = 0;
|
||||||
for vx in 0..target.x1 {
|
for vx in 0..target.x1 {
|
||||||
for vy in target.y0..-target.y0 {
|
for vy in target.y0..-target.y0 {
|
||||||
maxy = maxy.max(simulate(target, vx, vy));
|
maxy = maxy.max(simulate(target, vx, vy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return maxy;
|
return maxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(target: &Rect) -> i32 {
|
pub fn part2(target: &Rect) -> i32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for vx in 0..=target.x1 {
|
for vx in 0..=target.x1 {
|
||||||
for vy in target.y0..-target.y0 {
|
for vy in target.y0..-target.y0 {
|
||||||
if simulate(target, vx, vy) >= 0 {
|
if simulate(target, vx, vy) >= 0 {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let target = parse_input("target area: x=20..30, y=-10..-5");
|
let target = parse_input("target area: x=20..30, y=-10..-5");
|
||||||
let result = part1(&target);
|
let result = part1(&target);
|
||||||
assert_eq!(result, 45);
|
assert_eq!(result, 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let target = parse_input("target area: x=20..30, y=-10..-5");
|
let target = parse_input("target area: x=20..30, y=-10..-5");
|
||||||
let result = part2(&target);
|
let result = part2(&target);
|
||||||
assert_eq!(result, 112);
|
assert_eq!(result, 112);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
429
src/day18.rs
429
src/day18.rs
@ -1,136 +1,143 @@
|
|||||||
// Solution gotten from: https://github.com/AxlLind/AdventOfCode2021/blob/main/src/bin/18.rs
|
// Solution gotten from: https://github.com/AxlLind/AdventOfCode2021/blob/main/src/bin/18.rs
|
||||||
|
|
||||||
fn parse_snailfish(line: &str) -> Vec<(i32, u8)> {
|
fn parse_snailfish(line: &str) -> Vec<(i32, u8)> {
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
let mut nums = Vec::new();
|
let mut nums = Vec::new();
|
||||||
for c in line.chars() {
|
for c in line.chars() {
|
||||||
match c {
|
match c {
|
||||||
'[' => depth += 1,
|
'[' => depth += 1,
|
||||||
']' => depth -= 1,
|
']' => depth -= 1,
|
||||||
',' => {},
|
',' => {}
|
||||||
_ => nums.push(((c as u8 - b'0') as i32, depth))
|
_ => nums.push(((c as u8 - b'0') as i32, depth)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nums;
|
return nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_deepest_index(num: &Vec<(i32, u8)>) -> usize {
|
fn find_deepest_index(num: &Vec<(i32, u8)>) -> usize {
|
||||||
let mut deepest_index = 0;
|
let mut deepest_index = 0;
|
||||||
let mut deepest_depth = 0;
|
let mut deepest_depth = 0;
|
||||||
for i in 0..num.len() {
|
for i in 0..num.len() {
|
||||||
let depth = num[i].1;
|
let depth = num[i].1;
|
||||||
if deepest_depth < depth {
|
if deepest_depth < depth {
|
||||||
deepest_depth = depth;
|
deepest_depth = depth;
|
||||||
deepest_index = i;
|
deepest_index = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return deepest_index;
|
return deepest_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Vec<Vec<(i32, u8)>> {
|
pub fn parse_input(input: &str) -> Vec<Vec<(i32, u8)>> {
|
||||||
let mut nums = Vec::new();
|
let mut nums = Vec::new();
|
||||||
for line in input.lines() {
|
for line in input.lines() {
|
||||||
nums.push(parse_snailfish(line));
|
nums.push(parse_snailfish(line));
|
||||||
}
|
}
|
||||||
return nums;
|
return nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_snailfish(a: &Vec<(i32, u8)>, b: &Vec<(i32, u8)>) -> Vec<(i32, u8)> {
|
fn add_snailfish(a: &Vec<(i32, u8)>, b: &Vec<(i32, u8)>) -> Vec<(i32, u8)> {
|
||||||
let mut added = Vec::new();
|
let mut added = Vec::new();
|
||||||
for (num, depth) in a {
|
for (num, depth) in a {
|
||||||
added.push((*num, depth+1));
|
added.push((*num, depth + 1));
|
||||||
}
|
}
|
||||||
for (num, depth) in b {
|
for (num, depth) in b {
|
||||||
added.push((*num, depth+1));
|
added.push((*num, depth + 1));
|
||||||
}
|
}
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_exploding(num: &mut Vec<(i32, u8)>) -> bool {
|
fn try_exploding(num: &mut Vec<(i32, u8)>) -> bool {
|
||||||
let i = find_deepest_index(num);
|
let i = find_deepest_index(num);
|
||||||
if num[i].1 < 5 { return false; }
|
if num[i].1 < 5 {
|
||||||
let (left_num, depth) = num[i];
|
return false;
|
||||||
let right_num = num.remove(i+1).0;
|
}
|
||||||
if i > 0 {
|
let (left_num, depth) = num[i];
|
||||||
num[i-1].0 += left_num;
|
let right_num = num.remove(i + 1).0;
|
||||||
}
|
if i > 0 {
|
||||||
if i+1 < num.len() {
|
num[i - 1].0 += left_num;
|
||||||
num[i+1].0 += right_num;
|
}
|
||||||
}
|
if i + 1 < num.len() {
|
||||||
num[i] = (0, depth-1);
|
num[i + 1].0 += right_num;
|
||||||
return true;
|
}
|
||||||
|
num[i] = (0, depth - 1);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_splitting(num: &mut Vec<(i32, u8)>) -> bool {
|
fn try_splitting(num: &mut Vec<(i32, u8)>) -> bool {
|
||||||
let target = match num.iter().position(|&(n,_)| n > 9) {
|
let target = match num.iter().position(|&(n, _)| n > 9) {
|
||||||
Some(i) => i,
|
Some(i) => i,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
let (x, depth) = num[target];
|
let (x, depth) = num[target];
|
||||||
num[target] = (x/2, depth+1);
|
num[target] = (x / 2, depth + 1);
|
||||||
num.insert(target+1, ((x+1)/2, depth+1));
|
num.insert(target + 1, ((x + 1) / 2, depth + 1));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reduce_snailfish(num: &mut Vec<(i32, u8)>) {
|
fn reduce_snailfish(num: &mut Vec<(i32, u8)>) {
|
||||||
loop {
|
loop {
|
||||||
if try_exploding(num) { continue; }
|
if try_exploding(num) {
|
||||||
if try_splitting(num) { continue; }
|
continue;
|
||||||
break;
|
}
|
||||||
}
|
if try_splitting(num) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_magnitude(mut num: Vec<(i32, u8)>) -> i32 {
|
fn get_magnitude(mut num: Vec<(i32, u8)>) -> i32 {
|
||||||
while num.len() > 1 {
|
while num.len() > 1 {
|
||||||
let i = find_deepest_index(&num);
|
let i = find_deepest_index(&num);
|
||||||
let (left_num, depth) = num[i];
|
let (left_num, depth) = num[i];
|
||||||
let right_num = num[i+1].0;
|
let right_num = num[i + 1].0;
|
||||||
num[i] = (3*left_num + 2*right_num, depth-1);
|
num[i] = (3 * left_num + 2 * right_num, depth - 1);
|
||||||
num.remove(i+1);
|
num.remove(i + 1);
|
||||||
}
|
}
|
||||||
num[0].0
|
num[0].0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_and_reduce(a: &Vec<(i32, u8)>, b: &Vec<(i32, u8)>) -> Vec<(i32, u8)> {
|
fn add_and_reduce(a: &Vec<(i32, u8)>, b: &Vec<(i32, u8)>) -> Vec<(i32, u8)> {
|
||||||
let mut result = add_snailfish(a, b);
|
let mut result = add_snailfish(a, b);
|
||||||
reduce_snailfish(&mut result);
|
reduce_snailfish(&mut result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum(nums: &Vec<Vec<(i32, u8)>>) -> Vec<(i32, u8)> {
|
fn sum(nums: &Vec<Vec<(i32, u8)>>) -> Vec<(i32, u8)> {
|
||||||
let mut result = nums[0].clone();
|
let mut result = nums[0].clone();
|
||||||
for i in 1..nums.len() {
|
for i in 1..nums.len() {
|
||||||
result = add_snailfish(&result, &nums[i]);
|
result = add_snailfish(&result, &nums[i]);
|
||||||
reduce_snailfish(&mut result);
|
reduce_snailfish(&mut result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(nums: &Vec<Vec<(i32, u8)>>) -> i32 {
|
pub fn part1(nums: &Vec<Vec<(i32, u8)>>) -> i32 {
|
||||||
return get_magnitude(sum(nums));
|
return get_magnitude(sum(nums));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(nums: &Vec<Vec<(i32, u8)>>) -> i32 {
|
pub fn part2(nums: &Vec<Vec<(i32, u8)>>) -> i32 {
|
||||||
let mut max_magnitude = 0;
|
let mut max_magnitude = 0;
|
||||||
let n = nums.len();
|
let n = nums.len();
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
for j in 0..n-1 {
|
for j in 0..n - 1 {
|
||||||
let a = &nums[i];
|
let a = &nums[i];
|
||||||
let b = &nums[j];
|
let b = &nums[j];
|
||||||
max_magnitude = max_magnitude.max(get_magnitude(add_and_reduce(a, b)));
|
max_magnitude = max_magnitude.max(get_magnitude(add_and_reduce(a, b)));
|
||||||
max_magnitude = max_magnitude.max(get_magnitude(add_and_reduce(b, a)));
|
max_magnitude = max_magnitude.max(get_magnitude(add_and_reduce(b, a)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return max_magnitude;
|
return max_magnitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let nums = parse_input("[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
|
let nums = parse_input(
|
||||||
|
"[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
|
||||||
[[[5,[2,8]],4],[5,[[9,9],0]]]
|
[[[5,[2,8]],4],[5,[[9,9],0]]]
|
||||||
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
|
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
|
||||||
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
|
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
|
||||||
@ -139,14 +146,16 @@ mod tests {
|
|||||||
[[[[5,4],[7,7]],8],[[8,3],8]]
|
[[[[5,4],[7,7]],8],[[8,3],8]]
|
||||||
[[9,3],[[9,9],[6,[4,9]]]]
|
[[9,3],[[9,9],[6,[4,9]]]]
|
||||||
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
|
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
|
||||||
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]");
|
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]",
|
||||||
let result = part1(&nums);
|
);
|
||||||
assert_eq!(result, 4140);
|
let result = part1(&nums);
|
||||||
}
|
assert_eq!(result, 4140);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let nums = parse_input("[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
|
let nums = parse_input(
|
||||||
|
"[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
|
||||||
[[[5,[2,8]],4],[5,[[9,9],0]]]
|
[[[5,[2,8]],4],[5,[[9,9],0]]]
|
||||||
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
|
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
|
||||||
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
|
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
|
||||||
@ -155,124 +164,118 @@ mod tests {
|
|||||||
[[[[5,4],[7,7]],8],[[8,3],8]]
|
[[[[5,4],[7,7]],8],[[8,3],8]]
|
||||||
[[9,3],[[9,9],[6,[4,9]]]]
|
[[9,3],[[9,9],[6,[4,9]]]]
|
||||||
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
|
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
|
||||||
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]");
|
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]",
|
||||||
let result = part2(&nums);
|
);
|
||||||
assert_eq!(result, 3993);
|
let result = part2(&nums);
|
||||||
}
|
assert_eq!(result, 3993);
|
||||||
|
}
|
||||||
|
|
||||||
fn test_explosion(initial: &str, expected: &str) {
|
fn test_explosion(initial: &str, expected: &str) {
|
||||||
let mut num = parse_snailfish(initial);
|
let mut num = parse_snailfish(initial);
|
||||||
try_exploding(&mut num);
|
try_exploding(&mut num);
|
||||||
assert_eq!(num, parse_snailfish(expected));
|
assert_eq!(num, parse_snailfish(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exploding_1() {
|
fn exploding_1() {
|
||||||
test_explosion("[[[[[9,8],1],2],3],4]", "[[[[0,9],2],3],4]");
|
test_explosion("[[[[[9,8],1],2],3],4]", "[[[[0,9],2],3],4]");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn exploding_2() {
|
fn exploding_2() {
|
||||||
test_explosion("[7,[6,[5,[4,[3,2]]]]]", "[7,[6,[5,[7,0]]]]");
|
test_explosion("[7,[6,[5,[4,[3,2]]]]]", "[7,[6,[5,[7,0]]]]");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn exploding_3() {
|
fn exploding_3() {
|
||||||
test_explosion("[[6,[5,[4,[3,2]]]],1]", "[[6,[5,[7,0]]],3]");
|
test_explosion("[[6,[5,[4,[3,2]]]],1]", "[[6,[5,[7,0]]],3]");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn exploding_4() {
|
fn exploding_4() {
|
||||||
test_explosion("[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]", "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]");
|
test_explosion(
|
||||||
}
|
"[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]",
|
||||||
#[test]
|
"[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]",
|
||||||
fn exploding_5() {
|
);
|
||||||
test_explosion("[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]", "[[3,[2,[8,0]]],[9,[5,[7,0]]]]");
|
}
|
||||||
}
|
#[test]
|
||||||
|
fn exploding_5() {
|
||||||
|
test_explosion(
|
||||||
|
"[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]",
|
||||||
|
"[[3,[2,[8,0]]],[9,[5,[7,0]]]]",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn test_sum(nums: Vec<&str>, expected: &str) {
|
fn test_sum(nums: Vec<&str>, expected: &str) {
|
||||||
let mut parsed_nums = Vec::new();
|
let mut parsed_nums = Vec::new();
|
||||||
for num in nums {
|
for num in nums {
|
||||||
parsed_nums.push(parse_snailfish(num));
|
parsed_nums.push(parse_snailfish(num));
|
||||||
}
|
}
|
||||||
assert_eq!(sum(&parsed_nums), parse_snailfish(expected));
|
assert_eq!(sum(&parsed_nums), parse_snailfish(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sum_1() {
|
fn sum_1() {
|
||||||
let nums = vec![
|
let nums = vec!["[1,1]", "[2,2]", "[3,3]", "[4,4]"];
|
||||||
"[1,1]",
|
test_sum(nums, "[[[[1,1],[2,2]],[3,3]],[4,4]]");
|
||||||
"[2,2]",
|
}
|
||||||
"[3,3]",
|
#[test]
|
||||||
"[4,4]"
|
fn sum_2() {
|
||||||
];
|
let nums = vec!["[1,1]", "[2,2]", "[3,3]", "[4,4]", "[5,5]"];
|
||||||
test_sum(nums, "[[[[1,1],[2,2]],[3,3]],[4,4]]");
|
test_sum(nums, "[[[[3,0],[5,3]],[4,4]],[5,5]]");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn sum_2() {
|
fn sum_3() {
|
||||||
let nums = vec![
|
let nums = vec!["[1,1]", "[2,2]", "[3,3]", "[4,4]", "[5,5]", "[6,6]"];
|
||||||
"[1,1]",
|
test_sum(nums, "[[[[5,0],[7,4]],[5,5]],[6,6]]");
|
||||||
"[2,2]",
|
}
|
||||||
"[3,3]",
|
#[test]
|
||||||
"[4,4]",
|
fn sum_4() {
|
||||||
"[5,5]"
|
let nums = vec![
|
||||||
];
|
"[[[0,[4,5]],[0,0]],[[[4,5],[2,6]],[9,5]]]",
|
||||||
test_sum(nums, "[[[[3,0],[5,3]],[4,4]],[5,5]]");
|
"[7,[[[3,7],[4,3]],[[6,3],[8,8]]]]",
|
||||||
}
|
"[[2,[[0,8],[3,4]]],[[[6,7],1],[7,[1,6]]]]",
|
||||||
#[test]
|
"[[[[2,4],7],[6,[0,5]]],[[[6,8],[2,8]],[[2,1],[4,5]]]]",
|
||||||
fn sum_3() {
|
"[7,[5,[[3,8],[1,4]]]]",
|
||||||
let nums = vec![
|
"[[2,[2,2]],[8,[8,1]]]",
|
||||||
"[1,1]",
|
"[2,9]",
|
||||||
"[2,2]",
|
"[1,[[[9,3],9],[[9,0],[0,7]]]]",
|
||||||
"[3,3]",
|
"[[[5,[7,4]],7],1]",
|
||||||
"[4,4]",
|
"[[[[4,2],2],6],[8,7]]",
|
||||||
"[5,5]",
|
];
|
||||||
"[6,6]",
|
test_sum(
|
||||||
];
|
nums,
|
||||||
test_sum(nums, "[[[[5,0],[7,4]],[5,5]],[6,6]]");
|
"[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]",
|
||||||
}
|
);
|
||||||
#[test]
|
}
|
||||||
fn sum_4() {
|
|
||||||
let nums = vec![
|
|
||||||
"[[[0,[4,5]],[0,0]],[[[4,5],[2,6]],[9,5]]]",
|
|
||||||
"[7,[[[3,7],[4,3]],[[6,3],[8,8]]]]",
|
|
||||||
"[[2,[[0,8],[3,4]]],[[[6,7],1],[7,[1,6]]]]",
|
|
||||||
"[[[[2,4],7],[6,[0,5]]],[[[6,8],[2,8]],[[2,1],[4,5]]]]",
|
|
||||||
"[7,[5,[[3,8],[1,4]]]]",
|
|
||||||
"[[2,[2,2]],[8,[8,1]]]",
|
|
||||||
"[2,9]",
|
|
||||||
"[1,[[[9,3],9],[[9,0],[0,7]]]]",
|
|
||||||
"[[[5,[7,4]],7],1]",
|
|
||||||
"[[[[4,2],2],6],[8,7]]"
|
|
||||||
];
|
|
||||||
test_sum(nums, "[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_magnitude(initial: &str, expected: i32) {
|
fn test_magnitude(initial: &str, expected: i32) {
|
||||||
let num = parse_snailfish(initial);
|
let num = parse_snailfish(initial);
|
||||||
assert_eq!(get_magnitude(num), expected);
|
assert_eq!(get_magnitude(num), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_1() {
|
fn magnitude_1() {
|
||||||
test_magnitude("[[1,2],[[3,4],5]]", 143);
|
test_magnitude("[[1,2],[[3,4],5]]", 143);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_2() {
|
fn magnitude_2() {
|
||||||
test_magnitude("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]", 1384);
|
test_magnitude("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]", 1384);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_3() {
|
fn magnitude_3() {
|
||||||
test_magnitude("[[[[1,1],[2,2]],[3,3]],[4,4]]", 445);
|
test_magnitude("[[[[1,1],[2,2]],[3,3]],[4,4]]", 445);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_4() {
|
fn magnitude_4() {
|
||||||
test_magnitude("[[[[3,0],[5,3]],[4,4]],[5,5]]", 791);
|
test_magnitude("[[[[3,0],[5,3]],[4,4]],[5,5]]", 791);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_5() {
|
fn magnitude_5() {
|
||||||
test_magnitude("[[[[5,0],[7,4]],[5,5]],[6,6]]", 1137);
|
test_magnitude("[[[[5,0],[7,4]],[5,5]],[6,6]]", 1137);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_6() {
|
fn magnitude_6() {
|
||||||
test_magnitude("[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]", 3488);
|
test_magnitude(
|
||||||
}
|
"[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]",
|
||||||
|
3488,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
src/day2.rs
135
src/day2.rs
@ -2,96 +2,93 @@ use std::num::ParseIntError;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseCommandError {
|
pub enum ParseCommandError {
|
||||||
ParseEnumError,
|
ParseEnumError,
|
||||||
ParseIntError(ParseIntError),
|
ParseIntError(ParseIntError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CommandType {
|
pub enum CommandType {
|
||||||
Forward,
|
Forward,
|
||||||
Down,
|
Down,
|
||||||
Up
|
Up,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Command(CommandType, u32);
|
pub struct Command(CommandType, u32);
|
||||||
|
|
||||||
fn parse_line(line: &str) -> Result<Command, ParseCommandError> {
|
fn parse_line(line: &str) -> Result<Command, ParseCommandError> {
|
||||||
let parts: Vec<&str> = line.split(' ').collect();
|
let parts: Vec<&str> = line.split(' ').collect();
|
||||||
let command = match parts[0] {
|
let command = match parts[0] {
|
||||||
"up" => Ok(CommandType::Up),
|
"up" => Ok(CommandType::Up),
|
||||||
"down" => Ok(CommandType::Down),
|
"down" => Ok(CommandType::Down),
|
||||||
"forward" => Ok(CommandType::Forward),
|
"forward" => Ok(CommandType::Forward),
|
||||||
_ => Err(ParseCommandError::ParseEnumError)
|
_ => Err(ParseCommandError::ParseEnumError),
|
||||||
}?;
|
}?;
|
||||||
let amount = parts[1].parse().map_err(ParseCommandError::ParseIntError)?;
|
let amount = parts[1].parse().map_err(ParseCommandError::ParseIntError)?;
|
||||||
Ok(Command(command, amount))
|
Ok(Command(command, amount))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<Vec<Command>, ParseCommandError> {
|
pub fn parse_input(input: &str) -> Result<Vec<Command>, ParseCommandError> {
|
||||||
input.split_terminator('\n')
|
input.split_terminator('\n').map(parse_line).collect()
|
||||||
.map(parse_line)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(commands: &[Command]) -> u32 {
|
pub fn part1(commands: &[Command]) -> u32 {
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
let mut horizontal = 0;
|
let mut horizontal = 0;
|
||||||
for command in commands {
|
for command in commands {
|
||||||
match command.0 {
|
match command.0 {
|
||||||
CommandType::Up => depth -= command.1,
|
CommandType::Up => depth -= command.1,
|
||||||
CommandType::Down => depth += command.1,
|
CommandType::Down => depth += command.1,
|
||||||
CommandType::Forward => horizontal += command.1,
|
CommandType::Forward => horizontal += command.1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return depth * horizontal;
|
return depth * horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(commands: &[Command]) -> u32 {
|
pub fn part2(commands: &[Command]) -> u32 {
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
let mut horizontal = 0;
|
let mut horizontal = 0;
|
||||||
let mut aim = 0;
|
let mut aim = 0;
|
||||||
for command in commands {
|
for command in commands {
|
||||||
match command.0 {
|
match command.0 {
|
||||||
CommandType::Up => aim -= command.1,
|
CommandType::Up => aim -= command.1,
|
||||||
CommandType::Down => aim += command.1,
|
CommandType::Down => aim += command.1,
|
||||||
CommandType::Forward => {
|
CommandType::Forward => {
|
||||||
horizontal += command.1;
|
horizontal += command.1;
|
||||||
depth += aim * command.1;
|
depth += aim * command.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return depth * horizontal;
|
return depth * horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let commands = [
|
let commands = [
|
||||||
Command(CommandType::Forward, 5),
|
Command(CommandType::Forward, 5),
|
||||||
Command(CommandType::Down, 5),
|
Command(CommandType::Down, 5),
|
||||||
Command(CommandType::Forward, 8),
|
Command(CommandType::Forward, 8),
|
||||||
Command(CommandType::Up, 3),
|
Command(CommandType::Up, 3),
|
||||||
Command(CommandType::Down, 8),
|
Command(CommandType::Down, 8),
|
||||||
Command(CommandType::Forward, 2)
|
Command(CommandType::Forward, 2),
|
||||||
];
|
];
|
||||||
let result = part1(&commands);
|
let result = part1(&commands);
|
||||||
assert_eq!(result, 150);
|
assert_eq!(result, 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let commands = [
|
let commands = [
|
||||||
Command(CommandType::Forward, 5),
|
Command(CommandType::Forward, 5),
|
||||||
Command(CommandType::Down, 5),
|
Command(CommandType::Down, 5),
|
||||||
Command(CommandType::Forward, 8),
|
Command(CommandType::Forward, 8),
|
||||||
Command(CommandType::Up, 3),
|
Command(CommandType::Up, 3),
|
||||||
Command(CommandType::Down, 8),
|
Command(CommandType::Down, 8),
|
||||||
Command(CommandType::Forward, 2)
|
Command(CommandType::Forward, 2),
|
||||||
];
|
];
|
||||||
let result = part2(&commands);
|
let result = part2(&commands);
|
||||||
assert_eq!(result, 900)
|
assert_eq!(result, 900)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
345
src/day20.rs
345
src/day20.rs
@ -1,235 +1,236 @@
|
|||||||
use std::{ops::Range, fmt::Display};
|
use std::{fmt::Display, ops::Range};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
data: Vec<bool>,
|
data: Vec<bool>,
|
||||||
default_value: bool,
|
default_value: bool,
|
||||||
offset_x: i32,
|
offset_x: i32,
|
||||||
offset_y: i32
|
offset_y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
fn new(width: usize, height: usize) -> Image {
|
fn new(width: usize, height: usize) -> Image {
|
||||||
let data = vec![false; width*height];
|
let data = vec![false; width * height];
|
||||||
Image {
|
Image {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
data,
|
data,
|
||||||
default_value: false,
|
default_value: false,
|
||||||
offset_x: 0,
|
offset_x: 0,
|
||||||
offset_y: 0
|
offset_y: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, x: i32, y: i32) -> bool {
|
fn get(&self, x: i32, y: i32) -> bool {
|
||||||
if self.in_bounds(x, y) {
|
if self.in_bounds(x, y) {
|
||||||
let index = (y + self.offset_y) * self.width as i32 + (x + self.offset_x);
|
let index = (y + self.offset_y) * self.width as i32 + (x + self.offset_x);
|
||||||
*self.data.get(index as usize).unwrap_or(&self.default_value)
|
*self.data.get(index as usize).unwrap_or(&self.default_value)
|
||||||
} else {
|
} else {
|
||||||
self.default_value
|
self.default_value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, x: i32, y: i32, value: bool) {
|
fn set(&mut self, x: i32, y: i32, value: bool) {
|
||||||
// Make image larger if it is too small
|
// Make image larger if it is too small
|
||||||
let (left, top, right, bottom) = self.bounds();
|
let (left, top, right, bottom) = self.bounds();
|
||||||
if !(left <= x && x < right && top <= y && y < bottom) {
|
if !(left <= x && x < right && top <= y && y < bottom) {
|
||||||
if x < left {
|
if x < left {
|
||||||
self.offset_x = -x;
|
self.offset_x = -x;
|
||||||
self.width = self.width + (-x + left) as usize;
|
self.width = self.width + (-x + left) as usize;
|
||||||
} else if x >= right {
|
} else if x >= right {
|
||||||
self.width = self.width + 1 + (x - right) as usize;
|
self.width = self.width + 1 + (x - right) as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if y < top {
|
if y < top {
|
||||||
self.offset_y = -y;
|
self.offset_y = -y;
|
||||||
self.height = self.height + (-y + top) as usize;
|
self.height = self.height + (-y + top) as usize;
|
||||||
} else if y >= bottom {
|
} else if y >= bottom {
|
||||||
self.height = self.height + 1 + (y - bottom) as usize;
|
self.height = self.height + 1 + (y - bottom) as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_data = Vec::new();
|
let mut new_data = Vec::new();
|
||||||
for y in self.y_range(0) {
|
for y in self.y_range(0) {
|
||||||
for x in self.x_range(0) {
|
for x in self.x_range(0) {
|
||||||
new_data.push(self.get(x, y));
|
new_data.push(self.get(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.data = new_data;
|
self.data = new_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = ((y + self.offset_y) * self.width as i32 + (x + self.offset_x)) as usize;
|
let index = ((y + self.offset_y) * self.width as i32 + (x + self.offset_x)) as usize;
|
||||||
self.data[index] = value;
|
self.data[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_bounds(&self, x: i32, y: i32) -> bool {
|
fn in_bounds(&self, x: i32, y: i32) -> bool {
|
||||||
let (left, top, right, bottom) = self.bounds();
|
let (left, top, right, bottom) = self.bounds();
|
||||||
left <= x && x < right && top <= y && y < bottom
|
left <= x && x < right && top <= y && y < bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count(&self, value: bool) -> usize {
|
fn count(&self, value: bool) -> usize {
|
||||||
if self.default_value == value {
|
if self.default_value == value {
|
||||||
usize::MAX
|
usize::MAX
|
||||||
} else {
|
} else {
|
||||||
self.data.iter().filter(|x| **x == value).count()
|
self.data.iter().filter(|x| **x == value).count()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x_range(&self, padding: u32) -> Range<i32> {
|
fn x_range(&self, padding: u32) -> Range<i32> {
|
||||||
let padding = padding as i32;
|
let padding = padding as i32;
|
||||||
Range {
|
Range {
|
||||||
start: -self.offset_x - padding,
|
start: -self.offset_x - padding,
|
||||||
end: self.width as i32 - self.offset_x + padding
|
end: self.width as i32 - self.offset_x + padding,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y_range(&self, padding: u32) -> Range<i32> {
|
fn y_range(&self, padding: u32) -> Range<i32> {
|
||||||
let padding = padding as i32;
|
let padding = padding as i32;
|
||||||
Range {
|
Range {
|
||||||
start: -self.offset_y - padding,
|
start: -self.offset_y - padding,
|
||||||
end: self.height as i32 - self.offset_y + padding
|
end: self.height as i32 - self.offset_y + padding,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// (i32, i32, i32, i32) => (left, top, right, bottom)
|
// (i32, i32, i32, i32) => (left, top, right, bottom)
|
||||||
fn bounds(&self) -> (i32, i32, i32, i32) {
|
fn bounds(&self) -> (i32, i32, i32, i32) {
|
||||||
return (
|
return (
|
||||||
-self.offset_x,
|
-self.offset_x,
|
||||||
-self.offset_y,
|
-self.offset_y,
|
||||||
self.width as i32 - self.offset_x,
|
self.width as i32 - self.offset_x,
|
||||||
self.height as i32 - self.offset_y
|
self.height as i32 - self.offset_y,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Image {
|
impl Display for Image {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut grid = String::new();
|
let mut grid = String::new();
|
||||||
for y in self.y_range(1) {
|
for y in self.y_range(1) {
|
||||||
for x in self.x_range(1) {
|
for x in self.x_range(1) {
|
||||||
let symbol = if self.get(x, y) { '#' } else { '.' };
|
let symbol = if self.get(x, y) { '#' } else { '.' };
|
||||||
grid.push(symbol);
|
grid.push(symbol);
|
||||||
}
|
}
|
||||||
grid.push('\n');
|
grid.push('\n');
|
||||||
}
|
}
|
||||||
write!(f, "{}", grid)
|
write!(f, "{}", grid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_image(input: &str) -> Image {
|
fn parse_image(input: &str) -> Image {
|
||||||
let mut image = Image::new(3, 3);
|
let mut image = Image::new(3, 3);
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
for line in input.lines() {
|
for line in input.lines() {
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
for c in line.chars() {
|
for c in line.chars() {
|
||||||
image.set(x, y, c == '#');
|
image.set(x, y, c == '#');
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
y += 1
|
y += 1
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_enchancer(input: &str) -> [bool; 512] {
|
fn parse_enchancer(input: &str) -> [bool; 512] {
|
||||||
let mut enhancer = [false; 512];
|
let mut enhancer = [false; 512];
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for c in input.chars() {
|
for c in input.chars() {
|
||||||
enhancer[i] = c == '#';
|
enhancer[i] = c == '#';
|
||||||
i+=1;
|
i += 1;
|
||||||
}
|
}
|
||||||
return enhancer
|
return enhancer;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> ([bool; 512], Image) {
|
pub fn parse_input(input: &str) -> ([bool; 512], Image) {
|
||||||
let (section1, section2) = input.split_once("\n\n").unwrap();
|
let (section1, section2) = input.split_once("\n\n").unwrap();
|
||||||
return (
|
return (parse_enchancer(section1), parse_image(section2));
|
||||||
parse_enchancer(section1),
|
|
||||||
parse_image(section2)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_enhancer(x: i32, y: i32, image: &Image, enhancer: &[bool; 512]) -> bool {
|
fn lookup_enhancer(x: i32, y: i32, image: &Image, enhancer: &[bool; 512]) -> bool {
|
||||||
let pixel_offsets = [
|
let pixel_offsets = [
|
||||||
(-1, -1), ( 0, -1), ( 1, -1),
|
(-1, -1),
|
||||||
(-1, 0), ( 0, 0), ( 1, 0),
|
(0, -1),
|
||||||
(-1, 1), ( 0, 1), ( 1, 1)
|
(1, -1),
|
||||||
];
|
(-1, 0),
|
||||||
|
(0, 0),
|
||||||
|
(1, 0),
|
||||||
|
(-1, 1),
|
||||||
|
(0, 1),
|
||||||
|
(1, 1),
|
||||||
|
];
|
||||||
|
|
||||||
let mut lookup_index = 0;
|
let mut lookup_index = 0;
|
||||||
for i in 0..9 {
|
for i in 0..9 {
|
||||||
let (ox, oy) = pixel_offsets[i];
|
let (ox, oy) = pixel_offsets[i];
|
||||||
let pixel = image.get(x + ox, y + oy);
|
let pixel = image.get(x + ox, y + oy);
|
||||||
if pixel {
|
if pixel {
|
||||||
lookup_index += 2usize.pow((8-i) as u32)
|
lookup_index += 2usize.pow((8 - i) as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enhancer[lookup_index]
|
enhancer[lookup_index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enhance(image: &Image, enhancer: &[bool; 512]) -> Image {
|
fn enhance(image: &Image, enhancer: &[bool; 512]) -> Image {
|
||||||
let mut enhanced = image.clone();
|
let mut enhanced = image.clone();
|
||||||
for y in image.y_range(1) {
|
for y in image.y_range(1) {
|
||||||
for x in image.x_range(1) {
|
for x in image.x_range(1) {
|
||||||
enhanced.set(x, y, lookup_enhancer(x, y, image, enhancer));
|
enhanced.set(x, y, lookup_enhancer(x, y, image, enhancer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if image.default_value {
|
if image.default_value {
|
||||||
enhanced.default_value = enhancer[511];
|
enhanced.default_value = enhancer[511];
|
||||||
} else {
|
} else {
|
||||||
enhanced.default_value = enhancer[0];
|
enhanced.default_value = enhancer[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
enhanced
|
enhanced
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(data: &([bool; 512], Image)) -> usize {
|
pub fn part1(data: &([bool; 512], Image)) -> usize {
|
||||||
let (enhancer, image) = data;
|
let (enhancer, image) = data;
|
||||||
let mut enhanced_image = enhance(&image, enhancer);
|
let mut enhanced_image = enhance(&image, enhancer);
|
||||||
enhanced_image = enhance(&enhanced_image, enhancer);
|
enhanced_image = enhance(&enhanced_image, enhancer);
|
||||||
enhanced_image.count(true)
|
enhanced_image.count(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(data: &([bool; 512], Image)) -> usize {
|
pub fn part2(data: &([bool; 512], Image)) -> usize {
|
||||||
let (enhancer, image) = data;
|
let (enhancer, image) = data;
|
||||||
let mut enhanced_image = enhance(&image, enhancer);
|
let mut enhanced_image = enhance(&image, enhancer);
|
||||||
for _ in 0..49 {
|
for _ in 0..49 {
|
||||||
enhanced_image = enhance(&enhanced_image, enhancer);
|
enhanced_image = enhance(&enhanced_image, enhancer);
|
||||||
}
|
}
|
||||||
enhanced_image.count(true)
|
enhanced_image.count(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = parse_input("..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
|
let input = parse_input("..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
|
||||||
|
|
||||||
#..#.
|
#..#.
|
||||||
#....
|
#....
|
||||||
##..#
|
##..#
|
||||||
..#..
|
..#..
|
||||||
..###");
|
..###");
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 35);
|
assert_eq!(result, 35);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = parse_input("..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
|
let input = parse_input("..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
|
||||||
|
|
||||||
#..#.
|
#..#.
|
||||||
#....
|
#....
|
||||||
##..#
|
##..#
|
||||||
..#..
|
..#..
|
||||||
..###");
|
..###");
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 3351);
|
assert_eq!(result, 3351);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
151
src/day21.rs
151
src/day21.rs
@ -1,97 +1,108 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> (u8, u8) {
|
pub fn parse_input(input: &str) -> (u8, u8) {
|
||||||
let players: Vec<u8> = input.lines()
|
let players: Vec<u8> = input
|
||||||
.map(|l| l.split_once(": ").unwrap().1.parse().unwrap())
|
.lines()
|
||||||
.collect();
|
.map(|l| l.split_once(": ").unwrap().1.parse().unwrap())
|
||||||
return (players[0], players[1]);
|
.collect();
|
||||||
|
return (players[0], players[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(starting_positions: &(u8, u8)) -> u32 {
|
pub fn part1(starting_positions: &(u8, u8)) -> u32 {
|
||||||
let mut player1_position = starting_positions.0 as u32;
|
let mut player1_position = starting_positions.0 as u32;
|
||||||
let mut player2_position = starting_positions.1 as u32;
|
let mut player2_position = starting_positions.1 as u32;
|
||||||
let mut player1_score: u32 = 0;
|
let mut player1_score: u32 = 0;
|
||||||
let mut player2_score: u32 = 0;
|
let mut player2_score: u32 = 0;
|
||||||
let mut rolled_count = 0;
|
let mut rolled_count = 0;
|
||||||
|
|
||||||
let mut is_player1_turn = true;
|
let mut is_player1_turn = true;
|
||||||
|
|
||||||
while player1_score < 1000 && player2_score < 1000 {
|
while player1_score < 1000 && player2_score < 1000 {
|
||||||
let position;
|
let position;
|
||||||
let score;
|
let score;
|
||||||
if is_player1_turn {
|
if is_player1_turn {
|
||||||
position = &mut player1_position;
|
position = &mut player1_position;
|
||||||
score = &mut player1_score;
|
score = &mut player1_score;
|
||||||
} else {
|
} else {
|
||||||
position = &mut player2_position;
|
position = &mut player2_position;
|
||||||
score = &mut player2_score;
|
score = &mut player2_score;
|
||||||
}
|
}
|
||||||
|
|
||||||
*position += (rolled_count + 1 - 1) % 100 + 1;
|
*position += (rolled_count + 1 - 1) % 100 + 1;
|
||||||
*position += (rolled_count + 2 - 1) % 100 + 1;
|
*position += (rolled_count + 2 - 1) % 100 + 1;
|
||||||
*position += (rolled_count + 3 - 1) % 100 + 1;
|
*position += (rolled_count + 3 - 1) % 100 + 1;
|
||||||
|
|
||||||
*position = (*position - 1) % 10 + 1;
|
*position = (*position - 1) % 10 + 1;
|
||||||
*score += *position as u32;
|
*score += *position as u32;
|
||||||
|
|
||||||
rolled_count += 3;
|
rolled_count += 3;
|
||||||
is_player1_turn = !is_player1_turn;
|
is_player1_turn = !is_player1_turn;
|
||||||
}
|
}
|
||||||
|
|
||||||
player1_score.min(player2_score) * rolled_count as u32
|
player1_score.min(player2_score) * rolled_count as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_wins_amount(starting_pos1: u32, starting_pos2: u32, starting_score1: u32, starting_score2: u32, memo: &mut HashMap<(u32, u32, u32, u32), (u64, u64)>) -> (u64, u64) {
|
fn get_wins_amount(
|
||||||
let memo_key = (starting_pos1, starting_pos2, starting_score1, starting_score2);
|
starting_pos1: u32,
|
||||||
if memo.contains_key(&memo_key) {
|
starting_pos2: u32,
|
||||||
return *memo.get(&memo_key).unwrap()
|
starting_score1: u32,
|
||||||
}
|
starting_score2: u32,
|
||||||
let mut total_wins1 = 0;
|
memo: &mut HashMap<(u32, u32, u32, u32), (u64, u64)>,
|
||||||
let mut total_wins2 = 0;
|
) -> (u64, u64) {
|
||||||
|
let memo_key = (
|
||||||
|
starting_pos1,
|
||||||
|
starting_pos2,
|
||||||
|
starting_score1,
|
||||||
|
starting_score2,
|
||||||
|
);
|
||||||
|
if memo.contains_key(&memo_key) {
|
||||||
|
return *memo.get(&memo_key).unwrap();
|
||||||
|
}
|
||||||
|
let mut total_wins1 = 0;
|
||||||
|
let mut total_wins2 = 0;
|
||||||
|
|
||||||
for dice1 in 1..=3 {
|
for dice1 in 1..=3 {
|
||||||
for dice2 in 1..=3 {
|
for dice2 in 1..=3 {
|
||||||
for dice3 in 1..=3 {
|
for dice3 in 1..=3 {
|
||||||
let pos1 = (starting_pos1 + dice1 + dice2 + dice3 - 1) % 10 + 1;
|
let pos1 = (starting_pos1 + dice1 + dice2 + dice3 - 1) % 10 + 1;
|
||||||
let score1 = starting_score1 + pos1;
|
let score1 = starting_score1 + pos1;
|
||||||
if score1 >= 21 {
|
if score1 >= 21 {
|
||||||
total_wins1 += 1
|
total_wins1 += 1
|
||||||
} else {
|
} else {
|
||||||
let (wins2, wins1) = get_wins_amount(starting_pos2, pos1, starting_score2, score1, memo);
|
let (wins2, wins1) = get_wins_amount(starting_pos2, pos1, starting_score2, score1, memo);
|
||||||
total_wins1 += wins1;
|
total_wins1 += wins1;
|
||||||
total_wins2 += wins2;
|
total_wins2 += wins2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memo.insert(memo_key, (total_wins1, total_wins2));
|
memo.insert(memo_key, (total_wins1, total_wins2));
|
||||||
|
|
||||||
(total_wins1, total_wins2)
|
(total_wins1, total_wins2)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(positions: &(u8, u8)) -> u64 {
|
pub fn part2(positions: &(u8, u8)) -> u64 {
|
||||||
let mut memo = HashMap::new();
|
let mut memo = HashMap::new();
|
||||||
let (wins1, wins2) = get_wins_amount(positions.0 as u32, positions.1 as u32, 0, 0, &mut memo);
|
let (wins1, wins2) = get_wins_amount(positions.0 as u32, positions.1 as u32, 0, 0, &mut memo);
|
||||||
wins1.max(wins2)
|
wins1.max(wins2)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = (4, 8);
|
let input = (4, 8);
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 739785);
|
assert_eq!(result, 739785);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = (4, 8);
|
let input = (4, 8);
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 444356092776315);
|
assert_eq!(result, 444356092776315);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
409
src/day22.rs
409
src/day22.rs
@ -1,17 +1,20 @@
|
|||||||
use std::{convert::{TryFrom, TryInto}, num::ParseIntError, collections::HashSet};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
num::ParseIntError,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cuboid {
|
pub struct Cuboid {
|
||||||
x: (i32, i32),
|
x: (i32, i32),
|
||||||
y: (i32, i32),
|
y: (i32, i32),
|
||||||
z: (i32, i32)
|
z: (i32, i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StepAction {
|
pub enum StepAction {
|
||||||
On,
|
On,
|
||||||
Off
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -19,210 +22,216 @@ pub struct RebootStep(StepAction, Cuboid);
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseRangeError {
|
pub enum ParseRangeError {
|
||||||
Empty,
|
Empty,
|
||||||
BadLen,
|
BadLen,
|
||||||
ParseInt(ParseIntError)
|
ParseInt(ParseIntError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseCuboidError {
|
pub enum ParseCuboidError {
|
||||||
Empty,
|
Empty,
|
||||||
BadLen,
|
BadLen,
|
||||||
ParseRange(ParseRangeError)
|
ParseRange(ParseRangeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseRebootStepError {
|
pub enum ParseRebootStepError {
|
||||||
Empty,
|
Empty,
|
||||||
BadLen,
|
BadLen,
|
||||||
BadAction,
|
BadAction,
|
||||||
ParseCuboid(ParseCuboidError)
|
ParseCuboid(ParseCuboidError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cuboid {
|
impl Cuboid {
|
||||||
fn contains(&self, point: &(i32, i32, i32)) -> bool {
|
fn contains(&self, point: &(i32, i32, i32)) -> bool {
|
||||||
self.x.0 <= point.0 && point.0 <= self.x.1 &&
|
self.x.0 <= point.0
|
||||||
self.y.0 <= point.1 && point.1 <= self.y.1 &&
|
&& point.0 <= self.x.1
|
||||||
self.z.0 <= point.2 && point.2 <= self.z.1
|
&& self.y.0 <= point.1
|
||||||
}
|
&& point.1 <= self.y.1
|
||||||
|
&& self.z.0 <= point.2
|
||||||
|
&& point.2 <= self.z.1
|
||||||
|
}
|
||||||
|
|
||||||
fn clamp(&self, other: &Cuboid) -> Cuboid {
|
fn clamp(&self, other: &Cuboid) -> Cuboid {
|
||||||
Cuboid {
|
Cuboid {
|
||||||
x: (self.x.0.max(other.x.0), self.x.1.min(other.x.1)),
|
x: (self.x.0.max(other.x.0), self.x.1.min(other.x.1)),
|
||||||
y: (self.y.0.max(other.y.0), self.y.1.min(other.y.1)),
|
y: (self.y.0.max(other.y.0), self.y.1.min(other.y.1)),
|
||||||
z: (self.z.0.max(other.z.0), self.z.1.min(other.z.1)),
|
z: (self.z.0.max(other.z.0), self.z.1.min(other.z.1)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersection(&self, other: &Cuboid) -> Option<Cuboid> {
|
fn intersection(&self, other: &Cuboid) -> Option<Cuboid> {
|
||||||
if self.z.0 > other.z.1 || other.z.0 > self.z.1 { return None; }
|
if self.z.0 > other.z.1 || other.z.0 > self.z.1 {
|
||||||
if self.y.0 > other.y.1 || other.y.0 > self.y.1 { return None; }
|
return None;
|
||||||
if self.x.0 > other.x.1 || other.x.0 > self.x.1 { return None; }
|
}
|
||||||
Some(self.clamp(other))
|
if self.y.0 > other.y.1 || other.y.0 > self.y.1 {
|
||||||
}
|
return None;
|
||||||
|
}
|
||||||
|
if self.x.0 > other.x.1 || other.x.0 > self.x.1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(self.clamp(other))
|
||||||
|
}
|
||||||
|
|
||||||
fn volume(&self) -> u64 {
|
fn volume(&self) -> u64 {
|
||||||
(self.x.1 - self.x.0 + 1) as u64 *
|
(self.x.1 - self.x.0 + 1) as u64
|
||||||
(self.y.1 - self.y.0 + 1) as u64 *
|
* (self.y.1 - self.y.0 + 1) as u64
|
||||||
(self.z.1 - self.z.0 + 1) as u64
|
* (self.z.1 - self.z.0 + 1) as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseIntError> for ParseRangeError {
|
impl From<ParseIntError> for ParseRangeError {
|
||||||
fn from(e: ParseIntError) -> Self {
|
fn from(e: ParseIntError) -> Self {
|
||||||
ParseRangeError::ParseInt(e)
|
ParseRangeError::ParseInt(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseRangeError> for ParseCuboidError {
|
impl From<ParseRangeError> for ParseCuboidError {
|
||||||
fn from(e: ParseRangeError) -> Self {
|
fn from(e: ParseRangeError) -> Self {
|
||||||
ParseCuboidError::ParseRange(e)
|
ParseCuboidError::ParseRange(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_range(value: &str) -> Result<(i32, i32), ParseRangeError> {
|
fn parse_range(value: &str) -> Result<(i32, i32), ParseRangeError> {
|
||||||
if value.is_empty() {
|
if value.is_empty() {
|
||||||
return Err(ParseRangeError::Empty);
|
return Err(ParseRangeError::Empty);
|
||||||
}
|
}
|
||||||
let (start, end) = value[2..]
|
let (start, end) = value[2..].split_once("..").ok_or(ParseRangeError::BadLen)?;
|
||||||
.split_once("..")
|
Ok((start.parse()?, end.parse()?))
|
||||||
.ok_or(ParseRangeError::BadLen)?;
|
|
||||||
Ok((start.parse()?, end.parse()?))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for Cuboid {
|
impl TryFrom<&str> for Cuboid {
|
||||||
type Error = ParseCuboidError;
|
type Error = ParseCuboidError;
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
if value.is_empty() {
|
if value.is_empty() {
|
||||||
return Err(Self::Error::Empty);
|
return Err(Self::Error::Empty);
|
||||||
}
|
}
|
||||||
let ranges: Vec<_> = value.split(',')
|
let ranges: Vec<_> = value
|
||||||
.map(parse_range)
|
.split(',')
|
||||||
.collect::<Result<_, _>>()?;
|
.map(parse_range)
|
||||||
if ranges.len() != 3 {
|
.collect::<Result<_, _>>()?;
|
||||||
return Err(Self::Error::BadLen);
|
if ranges.len() != 3 {
|
||||||
}
|
return Err(Self::Error::BadLen);
|
||||||
Ok(Cuboid {
|
}
|
||||||
x: ranges[0],
|
Ok(Cuboid {
|
||||||
y: ranges[1],
|
x: ranges[0],
|
||||||
z: ranges[2]
|
y: ranges[1],
|
||||||
})
|
z: ranges[2],
|
||||||
}
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseCuboidError> for ParseRebootStepError {
|
impl From<ParseCuboidError> for ParseRebootStepError {
|
||||||
fn from(e: ParseCuboidError) -> Self {
|
fn from(e: ParseCuboidError) -> Self {
|
||||||
ParseRebootStepError::ParseCuboid(e)
|
ParseRebootStepError::ParseCuboid(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for RebootStep {
|
impl TryFrom<&str> for RebootStep {
|
||||||
type Error = ParseRebootStepError;
|
type Error = ParseRebootStepError;
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
if value.is_empty() {
|
if value.is_empty() {
|
||||||
return Err(Self::Error::Empty);
|
return Err(Self::Error::Empty);
|
||||||
}
|
}
|
||||||
let (action_str, cuboid) = value.split_once(" ").ok_or(Self::Error::BadLen)?;
|
let (action_str, cuboid) = value.split_once(" ").ok_or(Self::Error::BadLen)?;
|
||||||
|
|
||||||
let action = match action_str {
|
let action = match action_str {
|
||||||
"on" => StepAction::On,
|
"on" => StepAction::On,
|
||||||
"off" => StepAction::Off,
|
"off" => StepAction::Off,
|
||||||
_ => return Err(Self::Error::BadAction)
|
_ => return Err(Self::Error::BadAction),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RebootStep(action, cuboid.try_into()?))
|
Ok(RebootStep(action, cuboid.try_into()?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Vec<RebootStep> {
|
pub fn parse_input(input: &str) -> Vec<RebootStep> {
|
||||||
input.lines()
|
input.lines().map(|l| l.try_into().unwrap()).collect()
|
||||||
.map(|l| l.try_into().unwrap())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_cubes_in_cuboid(steps: &[RebootStep], region: &Cuboid) -> u32 {
|
fn count_cubes_in_cuboid(steps: &[RebootStep], region: &Cuboid) -> u32 {
|
||||||
let mut cubes = HashSet::new();
|
let mut cubes = HashSet::new();
|
||||||
|
|
||||||
for step in steps {
|
for step in steps {
|
||||||
let clamped = step.1.clamp(region);
|
let clamped = step.1.clamp(region);
|
||||||
for x in clamped.x.0..=clamped.x.1 {
|
for x in clamped.x.0..=clamped.x.1 {
|
||||||
for y in clamped.y.0..=clamped.y.1 {
|
for y in clamped.y.0..=clamped.y.1 {
|
||||||
for z in clamped.z.0..=clamped.z.1 {
|
for z in clamped.z.0..=clamped.z.1 {
|
||||||
let cube = (x, y, z);
|
let cube = (x, y, z);
|
||||||
match step.0 {
|
match step.0 {
|
||||||
StepAction::On => cubes.insert(cube),
|
StepAction::On => cubes.insert(cube),
|
||||||
StepAction::Off => cubes.remove(&cube)
|
StepAction::Off => cubes.remove(&cube),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cubes.len() as u32
|
cubes.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
// From: http://twocentstudios.com/2016/08/16/calculating-the-area-of-multiple-intersecting-rectangles-with-swift
|
// From: http://twocentstudios.com/2016/08/16/calculating-the-area-of-multiple-intersecting-rectangles-with-swift
|
||||||
/*
|
/*
|
||||||
fn total_volume(cuboids: &Vec<Cuboid>) -> u64 {
|
fn total_volume(cuboids: &Vec<Cuboid>) -> u64 {
|
||||||
let mut unique_x = HashSet::new();
|
let mut unique_x = HashSet::new();
|
||||||
let mut unique_y = HashSet::new();
|
let mut unique_y = HashSet::new();
|
||||||
let mut unique_z = HashSet::new();
|
let mut unique_z = HashSet::new();
|
||||||
for cuboid in cuboids {
|
for cuboid in cuboids {
|
||||||
unique_x.insert(cuboid.x.0);
|
unique_x.insert(cuboid.x.0);
|
||||||
unique_x.insert(cuboid.x.1);
|
unique_x.insert(cuboid.x.1);
|
||||||
unique_y.insert(cuboid.y.0);
|
unique_y.insert(cuboid.y.0);
|
||||||
unique_y.insert(cuboid.y.1);
|
unique_y.insert(cuboid.y.1);
|
||||||
unique_z.insert(cuboid.z.0);
|
unique_z.insert(cuboid.z.0);
|
||||||
unique_z.insert(cuboid.z.1);
|
unique_z.insert(cuboid.z.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut unique_x: Vec<_> = unique_x.iter().collect();
|
let mut unique_x: Vec<_> = unique_x.iter().collect();
|
||||||
let mut unique_y: Vec<_> = unique_y.iter().collect();
|
let mut unique_y: Vec<_> = unique_y.iter().collect();
|
||||||
let mut unique_z: Vec<_> = unique_z.iter().collect();
|
let mut unique_z: Vec<_> = unique_z.iter().collect();
|
||||||
|
|
||||||
unique_x.sort();
|
unique_x.sort();
|
||||||
unique_y.sort();
|
unique_y.sort();
|
||||||
unique_z.sort();
|
unique_z.sort();
|
||||||
|
|
||||||
let mut volume = 0;
|
let mut volume = 0;
|
||||||
for (i, x) in unique_x.iter().enumerate().skip(1) {
|
for (i, x) in unique_x.iter().enumerate().skip(1) {
|
||||||
for (j, y) in unique_y.iter().enumerate().skip(1) {
|
for (j, y) in unique_y.iter().enumerate().skip(1) {
|
||||||
for (k, z) in unique_z.iter().enumerate().skip(1) {
|
for (k, z) in unique_z.iter().enumerate().skip(1) {
|
||||||
let cuboid = Cuboid {
|
let cuboid = Cuboid {
|
||||||
x: (*unique_x[i-1], **x),
|
x: (*unique_x[i-1], **x),
|
||||||
y: (*unique_y[j-1], **y),
|
y: (*unique_y[j-1], **y),
|
||||||
z: (*unique_z[k-1], **z)
|
z: (*unique_z[k-1], **z)
|
||||||
};
|
};
|
||||||
for c in cuboids {
|
for c in cuboids {
|
||||||
if c.has_overlap(&cuboid) {
|
if c.has_overlap(&cuboid) {
|
||||||
volume += cuboid.volume();
|
volume += cuboid.volume();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// let point = (
|
// let point = (
|
||||||
// (*unique_x[i-1] + **x)/2,
|
// (*unique_x[i-1] + **x)/2,
|
||||||
// (*unique_y[j-1] + **y)/2,
|
// (*unique_y[j-1] + **y)/2,
|
||||||
// (*unique_z[k-1] + **z)/2
|
// (*unique_z[k-1] + **z)/2
|
||||||
// );
|
// );
|
||||||
// for c in cuboids {
|
// for c in cuboids {
|
||||||
// if c.contains(&point) {
|
// if c.contains(&point) {
|
||||||
// let cuboid = Cuboid {
|
// let cuboid = Cuboid {
|
||||||
// x: (*unique_x[i-1], **x),
|
// x: (*unique_x[i-1], **x),
|
||||||
// y: (*unique_y[j-1], **y),
|
// y: (*unique_y[j-1], **y),
|
||||||
// z: (*unique_z[k-1], **z)
|
// z: (*unique_z[k-1], **z)
|
||||||
// };
|
// };
|
||||||
// volume += cuboid.volume();
|
// volume += cuboid.volume();
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
volume
|
volume
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -231,50 +240,51 @@ fn total_volume(cuboids: &Vec<Cuboid>) -> u64 {
|
|||||||
* Based on inclusion-exclusion principle. https://en.wikipedia.org/wiki/Inclusion%E2%80%93exclusion_principle
|
* Based on inclusion-exclusion principle. https://en.wikipedia.org/wiki/Inclusion%E2%80%93exclusion_principle
|
||||||
*/
|
*/
|
||||||
fn count_cubes(steps: Vec<RebootStep>) -> u64 {
|
fn count_cubes(steps: Vec<RebootStep>) -> u64 {
|
||||||
let mut cuboids: Vec<(Cuboid, bool)> = Vec::new();
|
let mut cuboids: Vec<(Cuboid, bool)> = Vec::new();
|
||||||
|
|
||||||
for step in steps {
|
for step in steps {
|
||||||
let mut extra_cuboids = Vec::new();
|
let mut extra_cuboids = Vec::new();
|
||||||
for c in cuboids.iter() {
|
for c in cuboids.iter() {
|
||||||
if let Some(intersect) = step.1.intersection(&c.0) {
|
if let Some(intersect) = step.1.intersection(&c.0) {
|
||||||
extra_cuboids.push((intersect, !c.1));
|
extra_cuboids.push((intersect, !c.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let StepAction::On = step.0 {
|
if let StepAction::On = step.0 {
|
||||||
cuboids.push((step.1, true));
|
cuboids.push((step.1, true));
|
||||||
}
|
}
|
||||||
cuboids.append(&mut extra_cuboids);
|
cuboids.append(&mut extra_cuboids);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut total_volume = 0;
|
let mut total_volume = 0;
|
||||||
for (cuboid, additive) in cuboids {
|
for (cuboid, additive) in cuboids {
|
||||||
let sign: i64 = if additive { 1 } else { -1 };
|
let sign: i64 = if additive { 1 } else { -1 };
|
||||||
total_volume += sign * cuboid.volume() as i64;
|
total_volume += sign * cuboid.volume() as i64;
|
||||||
}
|
}
|
||||||
total_volume as u64
|
total_volume as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(steps: &[RebootStep]) -> u32 {
|
pub fn part1(steps: &[RebootStep]) -> u32 {
|
||||||
let region = Cuboid{
|
let region = Cuboid {
|
||||||
x: (-50, 50),
|
x: (-50, 50),
|
||||||
y: (-50, 50),
|
y: (-50, 50),
|
||||||
z: (-50, 50)
|
z: (-50, 50),
|
||||||
};
|
};
|
||||||
count_cubes_in_cuboid(steps, ®ion)
|
count_cubes_in_cuboid(steps, ®ion)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(steps: Vec<RebootStep>) -> u64 {
|
pub fn part2(steps: Vec<RebootStep>) -> u64 {
|
||||||
count_cubes(steps)
|
count_cubes(steps)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let steps = parse_input("on x=-20..26,y=-36..17,z=-47..7
|
let steps = parse_input(
|
||||||
|
"on x=-20..26,y=-36..17,z=-47..7
|
||||||
on x=-20..33,y=-21..23,z=-26..28
|
on x=-20..33,y=-21..23,z=-26..28
|
||||||
on x=-22..28,y=-29..23,z=-38..16
|
on x=-22..28,y=-29..23,z=-38..16
|
||||||
on x=-46..7,y=-6..46,z=-50..-1
|
on x=-46..7,y=-6..46,z=-50..-1
|
||||||
@ -295,14 +305,16 @@ on x=-49..-5,y=-3..45,z=-29..18
|
|||||||
off x=18..30,y=-20..-8,z=-3..13
|
off x=18..30,y=-20..-8,z=-3..13
|
||||||
on x=-41..9,y=-7..43,z=-33..15
|
on x=-41..9,y=-7..43,z=-33..15
|
||||||
on x=-54112..-39298,y=-85059..-49293,z=-27449..7877
|
on x=-54112..-39298,y=-85059..-49293,z=-27449..7877
|
||||||
on x=967..23432,y=45373..81175,z=27513..53682");
|
on x=967..23432,y=45373..81175,z=27513..53682",
|
||||||
let result = part1(&steps);
|
);
|
||||||
assert_eq!(result, 590784);
|
let result = part1(&steps);
|
||||||
}
|
assert_eq!(result, 590784);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let steps = parse_input("on x=-5..47,y=-31..22,z=-19..33
|
let steps = parse_input(
|
||||||
|
"on x=-5..47,y=-31..22,z=-19..33
|
||||||
on x=-44..5,y=-27..21,z=-14..35
|
on x=-44..5,y=-27..21,z=-14..35
|
||||||
on x=-49..-1,y=-11..42,z=-10..38
|
on x=-49..-1,y=-11..42,z=-10..38
|
||||||
on x=-20..34,y=-40..6,z=-44..1
|
on x=-20..34,y=-40..6,z=-44..1
|
||||||
@ -361,8 +373,9 @@ off x=-37810..49457,y=-71013..-7894,z=-105357..-13188
|
|||||||
off x=-27365..46395,y=31009..98017,z=15428..76570
|
off x=-27365..46395,y=31009..98017,z=15428..76570
|
||||||
off x=-70369..-16548,y=22648..78696,z=-1892..86821
|
off x=-70369..-16548,y=22648..78696,z=-1892..86821
|
||||||
on x=-53470..21291,y=-120233..-33476,z=-44150..38147
|
on x=-53470..21291,y=-120233..-33476,z=-44150..38147
|
||||||
off x=-93533..-4276,y=-16170..68771,z=-104985..-24507");
|
off x=-93533..-4276,y=-16170..68771,z=-104985..-24507",
|
||||||
let result = part2(steps);
|
);
|
||||||
assert_eq!(result, 2758514936282235);
|
let result = part2(steps);
|
||||||
}
|
assert_eq!(result, 2758514936282235);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
166
src/day3.rs
166
src/day3.rs
@ -1,103 +1,123 @@
|
|||||||
use std::{num::ParseIntError, slice::Iter};
|
use std::{num::ParseIntError, slice::Iter};
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<Vec<i32>, ParseIntError> {
|
pub fn parse_input(input: &str) -> Result<Vec<i32>, ParseIntError> {
|
||||||
input.split_whitespace()
|
input
|
||||||
.map(|s| i32::from_str_radix(s, 2))
|
.split_whitespace()
|
||||||
.collect()
|
.map(|s| i32::from_str_radix(s, 2))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_max_bits(nums: &[i32]) -> u32 {
|
fn calculate_max_bits(nums: &[i32]) -> u32 {
|
||||||
let mut max_bits = 0;
|
let mut max_bits = 0;
|
||||||
for num in nums {
|
for num in nums {
|
||||||
max_bits = max_bits.max((*num as f32).log2().ceil() as u32);
|
max_bits = max_bits.max((*num as f32).log2().ceil() as u32);
|
||||||
}
|
}
|
||||||
return max_bits;
|
return max_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_bits(nums: Iter<i32>, power: &i32) -> u32 {
|
fn count_bits(nums: Iter<i32>, power: &i32) -> u32 {
|
||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
for num in nums {
|
for num in nums {
|
||||||
if num & power > 0 {
|
if num & power > 0 {
|
||||||
bits += 1;
|
bits += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(diagnostics: &[i32]) -> i32 {
|
pub fn part1(diagnostics: &[i32]) -> i32 {
|
||||||
let n = diagnostics.len() as u32;
|
let n = diagnostics.len() as u32;
|
||||||
let mut gamma = 0;
|
let mut gamma = 0;
|
||||||
let mut epsilon = 0;
|
let mut epsilon = 0;
|
||||||
|
|
||||||
let max_bits = calculate_max_bits(&diagnostics);
|
let max_bits = calculate_max_bits(&diagnostics);
|
||||||
let mut power = 1;
|
let mut power = 1;
|
||||||
for _ in 1..=max_bits {
|
for _ in 1..=max_bits {
|
||||||
let bits = count_bits(diagnostics.iter(), &power);
|
let bits = count_bits(diagnostics.iter(), &power);
|
||||||
if 2*bits >= n {
|
if 2 * bits >= n {
|
||||||
gamma += power;
|
gamma += power;
|
||||||
} else {
|
} else {
|
||||||
epsilon += power;
|
epsilon += power;
|
||||||
}
|
}
|
||||||
power *= 2;
|
power *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gamma * epsilon;
|
return gamma * epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(diagnostics: &[i32]) -> i32 {
|
pub fn part2(diagnostics: &[i32]) -> i32 {
|
||||||
let mut carbon_diagnostics = Vec::new();
|
let mut carbon_diagnostics = Vec::new();
|
||||||
let mut oxygen_diagnostics = Vec::new();
|
let mut oxygen_diagnostics = Vec::new();
|
||||||
|
|
||||||
carbon_diagnostics.extend_from_slice(diagnostics);
|
carbon_diagnostics.extend_from_slice(diagnostics);
|
||||||
oxygen_diagnostics.extend_from_slice(diagnostics);
|
oxygen_diagnostics.extend_from_slice(diagnostics);
|
||||||
|
|
||||||
let max_bits = calculate_max_bits(&diagnostics);
|
let max_bits = calculate_max_bits(&diagnostics);
|
||||||
let mut power = 2i32.pow(max_bits-1);
|
let mut power = 2i32.pow(max_bits - 1);
|
||||||
for _ in 1..=max_bits {
|
for _ in 1..=max_bits {
|
||||||
let oxygen_len = oxygen_diagnostics.len() as u32;
|
let oxygen_len = oxygen_diagnostics.len() as u32;
|
||||||
if oxygen_len > 1 {
|
if oxygen_len > 1 {
|
||||||
let bit_count = count_bits(oxygen_diagnostics.iter(), &power);
|
let bit_count = count_bits(oxygen_diagnostics.iter(), &power);
|
||||||
if 2*bit_count >= oxygen_len {
|
if 2 * bit_count >= oxygen_len {
|
||||||
oxygen_diagnostics = oxygen_diagnostics.into_iter().filter(|n| n & power > 0).collect();
|
oxygen_diagnostics = oxygen_diagnostics
|
||||||
} else {
|
.into_iter()
|
||||||
oxygen_diagnostics = oxygen_diagnostics.into_iter().filter(|n| n & power == 0).collect();
|
.filter(|n| n & power > 0)
|
||||||
}
|
.collect();
|
||||||
}
|
} else {
|
||||||
|
oxygen_diagnostics = oxygen_diagnostics
|
||||||
|
.into_iter()
|
||||||
|
.filter(|n| n & power == 0)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let carbon_len = carbon_diagnostics.len() as u32;
|
let carbon_len = carbon_diagnostics.len() as u32;
|
||||||
if carbon_len > 1 {
|
if carbon_len > 1 {
|
||||||
let bit_count = count_bits(carbon_diagnostics.iter(), &power);
|
let bit_count = count_bits(carbon_diagnostics.iter(), &power);
|
||||||
if 2*bit_count < carbon_len {
|
if 2 * bit_count < carbon_len {
|
||||||
carbon_diagnostics = carbon_diagnostics.into_iter().filter(|n| n & power > 0).collect();
|
carbon_diagnostics = carbon_diagnostics
|
||||||
} else {
|
.into_iter()
|
||||||
carbon_diagnostics = carbon_diagnostics.into_iter().filter(|n| n & power == 0).collect();
|
.filter(|n| n & power > 0)
|
||||||
}
|
.collect();
|
||||||
}
|
} else {
|
||||||
|
carbon_diagnostics = carbon_diagnostics
|
||||||
|
.into_iter()
|
||||||
|
.filter(|n| n & power == 0)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if oxygen_len == 1 && carbon_len == 1 { break; }
|
if oxygen_len == 1 && carbon_len == 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
power /= 2;
|
power /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return carbon_diagnostics[0] * oxygen_diagnostics[0];
|
return carbon_diagnostics[0] * oxygen_diagnostics[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let diagnostics = [0b00100, 0b11110, 0b10110, 0b10111, 0b10101, 0b01111, 0b00111, 0b11100, 0b10000, 0b11001, 0b00010, 0b01010];
|
let diagnostics = [
|
||||||
let result = part1(&diagnostics);
|
0b00100, 0b11110, 0b10110, 0b10111, 0b10101, 0b01111, 0b00111, 0b11100, 0b10000, 0b11001,
|
||||||
assert_eq!(result, 198);
|
0b00010, 0b01010,
|
||||||
}
|
];
|
||||||
|
let result = part1(&diagnostics);
|
||||||
|
assert_eq!(result, 198);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let diagnostics = [0b00100, 0b11110, 0b10110, 0b10111, 0b10101, 0b01111, 0b00111, 0b11100, 0b10000, 0b11001, 0b00010, 0b01010];
|
let diagnostics = [
|
||||||
let result = part2(&diagnostics);
|
0b00100, 0b11110, 0b10110, 0b10111, 0b10101, 0b01111, 0b00111, 0b11100, 0b10000, 0b11001,
|
||||||
assert_eq!(result, 230);
|
0b00010, 0b01010,
|
||||||
}
|
];
|
||||||
|
let result = part2(&diagnostics);
|
||||||
|
assert_eq!(result, 230);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
350
src/day4.rs
350
src/day4.rs
@ -1,229 +1,217 @@
|
|||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BingoGame {
|
pub struct BingoGame {
|
||||||
numbers: Vec<i32>,
|
numbers: Vec<i32>,
|
||||||
boards: Vec<[i32; 25]>
|
boards: Vec<[i32; 25]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseBingoGameError {
|
pub enum ParseBingoGameError {
|
||||||
NoNumbersError,
|
NoNumbersError,
|
||||||
ParseNumberError(ParseIntError),
|
ParseNumberError(ParseIntError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<BingoGame, ParseBingoGameError> {
|
pub fn parse_input(input: &str) -> Result<BingoGame, ParseBingoGameError> {
|
||||||
let mut sections = input.split_terminator("\n\n");
|
let mut sections = input.split_terminator("\n\n");
|
||||||
|
|
||||||
let numbers_section = sections
|
let numbers_section = sections.next().ok_or(ParseBingoGameError::NoNumbersError)?;
|
||||||
.next()
|
|
||||||
.ok_or(ParseBingoGameError::NoNumbersError)?;
|
|
||||||
|
|
||||||
let numbers = numbers_section
|
let numbers = numbers_section
|
||||||
.split_terminator(",")
|
.split_terminator(",")
|
||||||
.map(|s| s.parse::<i32>())
|
.map(|s| s.parse::<i32>())
|
||||||
.collect::<Result<Vec<i32>, ParseIntError>>()
|
.collect::<Result<Vec<i32>, ParseIntError>>()
|
||||||
.map_err(ParseBingoGameError::ParseNumberError)?;
|
.map_err(ParseBingoGameError::ParseNumberError)?;
|
||||||
|
|
||||||
let mut boards = Vec::new();
|
let mut boards = Vec::new();
|
||||||
for section in sections {
|
for section in sections {
|
||||||
let mut board: [i32; 25] = [0; 25];
|
let mut board: [i32; 25] = [0; 25];
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
for row in section.split_terminator("\n") {
|
for row in section.split_terminator("\n") {
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
for s in row.split_whitespace() {
|
for s in row.split_whitespace() {
|
||||||
let n = s.trim().parse::<i32>().map_err(ParseBingoGameError::ParseNumberError)?;
|
let n = s
|
||||||
board[5*y + x] = n;
|
.trim()
|
||||||
x += 1;
|
.parse::<i32>()
|
||||||
}
|
.map_err(ParseBingoGameError::ParseNumberError)?;
|
||||||
y += 1;
|
board[5 * y + x] = n;
|
||||||
}
|
x += 1;
|
||||||
boards.push(board);
|
}
|
||||||
}
|
y += 1;
|
||||||
|
}
|
||||||
|
boards.push(board);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(BingoGame { numbers, boards })
|
Ok(BingoGame { numbers, boards })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_number(board: &[i32], value: i32) -> Option<usize> {
|
fn find_number(board: &[i32], value: i32) -> Option<usize> {
|
||||||
for i in 0..board.len() {
|
for i in 0..board.len() {
|
||||||
if board[i] == value {
|
if board[i] == value {
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_number(board: &[i32], markings: &mut i32, value: i32) -> bool {
|
fn mark_number(board: &[i32], markings: &mut i32, value: i32) -> bool {
|
||||||
let pos: usize;
|
let pos: usize;
|
||||||
match find_number(board, value) {
|
match find_number(board, value) {
|
||||||
None => return false,
|
None => return false,
|
||||||
Some(n) => pos = n
|
Some(n) => pos = n,
|
||||||
};
|
};
|
||||||
*markings |= 1 << pos;
|
*markings |= 1 << pos;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_win(markings: i32) -> bool {
|
fn contains_win(markings: i32) -> bool {
|
||||||
const WINNING_COMBINATIONS: [i32; 10] = [
|
const WINNING_COMBINATIONS: [i32; 10] = [
|
||||||
0b11111 << 0, // First row
|
0b11111 << 0, // First row
|
||||||
0b11111 << 5, // Second row
|
0b11111 << 5, // Second row
|
||||||
0b11111 << 10, // Third row
|
0b11111 << 10, // Third row
|
||||||
0b11111 << 15, // Fourth row
|
0b11111 << 15, // Fourth row
|
||||||
0b11111 << 20, // Fifth row
|
0b11111 << 20, // Fifth row
|
||||||
0b0000100001000010000100001, // First column
|
0b0000100001000010000100001, // First column
|
||||||
0b0001000010000100001000010, // Second column
|
0b0001000010000100001000010, // Second column
|
||||||
0b0010000100001000010000100, // Third column
|
0b0010000100001000010000100, // Third column
|
||||||
0b0100001000010000100001000, // Fourth column
|
0b0100001000010000100001000, // Fourth column
|
||||||
0b1000010000100001000010000, // Fifth column
|
0b1000010000100001000010000, // Fifth column
|
||||||
];
|
];
|
||||||
|
|
||||||
for comb in WINNING_COMBINATIONS.iter() {
|
for comb in WINNING_COMBINATIONS.iter() {
|
||||||
if markings & comb == *comb {
|
if markings & comb == *comb {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum_unmarked_numbers(board: &[i32], markings: i32) -> i32 {
|
fn sum_unmarked_numbers(board: &[i32], markings: i32) -> i32 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for i in 0..25 {
|
for i in 0..25 {
|
||||||
if markings & (1 << i) == 0 {
|
if markings & (1 << i) == 0 {
|
||||||
sum += board[i];
|
sum += board[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(game: &BingoGame) -> i32 {
|
pub fn part1(game: &BingoGame) -> i32 {
|
||||||
let mut markings = vec![0; game.boards.len()];
|
let mut markings = vec![0; game.boards.len()];
|
||||||
|
|
||||||
for num in game.numbers.iter() {
|
for num in game.numbers.iter() {
|
||||||
for i in 0..game.boards.len() {
|
for i in 0..game.boards.len() {
|
||||||
if mark_number(&game.boards[i], &mut markings[i], *num) && contains_win(markings[i]) {
|
if mark_number(&game.boards[i], &mut markings[i], *num) && contains_win(markings[i]) {
|
||||||
return num * sum_unmarked_numbers(&game.boards[i], markings[i]);
|
return num * sum_unmarked_numbers(&game.boards[i], markings[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(game: &BingoGame) -> i32 {
|
pub fn part2(game: &BingoGame) -> i32 {
|
||||||
let mut markings = vec![0; game.boards.len()];
|
let mut markings = vec![0; game.boards.len()];
|
||||||
let mut winning_numbers: Vec<i32> = vec![0; game.boards.len()];
|
let mut winning_numbers: Vec<i32> = vec![0; game.boards.len()];
|
||||||
let mut last_winning_board = usize::MAX;
|
let mut last_winning_board = usize::MAX;
|
||||||
|
|
||||||
for num in game.numbers.iter() {
|
for num in game.numbers.iter() {
|
||||||
for i in 0..game.boards.len() {
|
for i in 0..game.boards.len() {
|
||||||
if winning_numbers[i] == 0 && mark_number(&game.boards[i], &mut markings[i], *num) && contains_win(markings[i]) {
|
if winning_numbers[i] == 0
|
||||||
winning_numbers[i] = *num;
|
&& mark_number(&game.boards[i], &mut markings[i], *num)
|
||||||
last_winning_board = i;
|
&& contains_win(markings[i])
|
||||||
}
|
{
|
||||||
}
|
winning_numbers[i] = *num;
|
||||||
}
|
last_winning_board = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return winning_numbers[last_winning_board] * sum_unmarked_numbers(&game.boards[last_winning_board], markings[last_winning_board]);
|
return winning_numbers[last_winning_board]
|
||||||
|
* sum_unmarked_numbers(
|
||||||
|
&game.boards[last_winning_board],
|
||||||
|
markings[last_winning_board],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn new_test_board() -> [i32; 25] {
|
fn new_test_board() -> [i32; 25] {
|
||||||
let mut board: [i32; 25] = [0; 25];
|
let mut board: [i32; 25] = [0; 25];
|
||||||
for i in 0..25 {
|
for i in 0..25 {
|
||||||
board[i] = i as i32;
|
board[i] = i as i32;
|
||||||
}
|
}
|
||||||
return board;
|
return board;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = BingoGame {
|
let input = BingoGame {
|
||||||
numbers: vec![7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1],
|
numbers: vec![
|
||||||
boards: vec![
|
7, 4, 9, 5, 11, 17, 23, 2, 0, 14, 21, 24, 10, 16, 13, 6, 15, 25, 12, 22, 18, 20, 8, 19, 3,
|
||||||
[
|
26, 1,
|
||||||
22, 13, 17, 11, 0,
|
],
|
||||||
8, 2, 23, 4, 24,
|
boards: vec![
|
||||||
21, 9, 14, 16, 7,
|
[
|
||||||
6, 10, 3, 18, 5,
|
22, 13, 17, 11, 0, 8, 2, 23, 4, 24, 21, 9, 14, 16, 7, 6, 10, 3, 18, 5, 1, 12, 20, 15, 19,
|
||||||
1, 12, 20, 15, 19
|
],
|
||||||
],
|
[
|
||||||
[
|
3, 15, 0, 2, 22, 9, 18, 13, 17, 5, 19, 8, 7, 25, 23, 20, 11, 10, 24, 4, 14, 21, 16, 12, 6,
|
||||||
3, 15, 0, 2, 22,
|
],
|
||||||
9, 18, 13, 17, 5,
|
[
|
||||||
19, 8, 7, 25, 23,
|
14, 21, 17, 24, 4, 10, 16, 15, 9, 19, 18, 8, 23, 26, 20, 22, 11, 13, 6, 5, 2, 0, 12, 3, 7,
|
||||||
20, 11, 10, 24, 4,
|
],
|
||||||
14, 21, 16, 12, 6
|
],
|
||||||
],
|
};
|
||||||
[
|
let result = part1(&input);
|
||||||
14, 21, 17, 24, 4,
|
assert_eq!(result, 4512);
|
||||||
10, 16, 15, 9, 19,
|
}
|
||||||
18, 8, 23, 26, 20,
|
|
||||||
22, 11, 13, 6, 5,
|
|
||||||
2, 0, 12, 3, 7
|
|
||||||
]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
let result = part1(&input);
|
|
||||||
assert_eq!(result, 4512);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = BingoGame {
|
let input = BingoGame {
|
||||||
numbers: vec![7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1],
|
numbers: vec![
|
||||||
boards: vec![
|
7, 4, 9, 5, 11, 17, 23, 2, 0, 14, 21, 24, 10, 16, 13, 6, 15, 25, 12, 22, 18, 20, 8, 19, 3,
|
||||||
[
|
26, 1,
|
||||||
22, 13, 17, 11, 0,
|
],
|
||||||
8, 2, 23, 4, 24,
|
boards: vec![
|
||||||
21, 9, 14, 16, 7,
|
[
|
||||||
6, 10, 3, 18, 5,
|
22, 13, 17, 11, 0, 8, 2, 23, 4, 24, 21, 9, 14, 16, 7, 6, 10, 3, 18, 5, 1, 12, 20, 15, 19,
|
||||||
1, 12, 20, 15, 19
|
],
|
||||||
],
|
[
|
||||||
[
|
3, 15, 0, 2, 22, 9, 18, 13, 17, 5, 19, 8, 7, 25, 23, 20, 11, 10, 24, 4, 14, 21, 16, 12, 6,
|
||||||
3, 15, 0, 2, 22,
|
],
|
||||||
9, 18, 13, 17, 5,
|
[
|
||||||
19, 8, 7, 25, 23,
|
14, 21, 17, 24, 4, 10, 16, 15, 9, 19, 18, 8, 23, 26, 20, 22, 11, 13, 6, 5, 2, 0, 12, 3, 7,
|
||||||
20, 11, 10, 24, 4,
|
],
|
||||||
14, 21, 16, 12, 6
|
],
|
||||||
],
|
};
|
||||||
[
|
let result = part2(&input);
|
||||||
14, 21, 17, 24, 4,
|
assert_eq!(result, 1924);
|
||||||
10, 16, 15, 9, 19,
|
}
|
||||||
18, 8, 23, 26, 20,
|
|
||||||
22, 11, 13, 6, 5,
|
|
||||||
2, 0, 12, 3, 7
|
|
||||||
]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
let result = part2(&input);
|
|
||||||
assert_eq!(result, 1924);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_mark_number() {
|
fn check_mark_number() {
|
||||||
let board = new_test_board();
|
let board = new_test_board();
|
||||||
let mut markings = 0b0;
|
let mut markings = 0b0;
|
||||||
mark_number(&board, &mut markings, 0);
|
mark_number(&board, &mut markings, 0);
|
||||||
assert_eq!(markings, 1);
|
assert_eq!(markings, 1);
|
||||||
|
|
||||||
mark_number(&board, &mut markings, 1);
|
mark_number(&board, &mut markings, 1);
|
||||||
assert_eq!(markings, 3);
|
assert_eq!(markings, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_contains_win() {
|
fn check_contains_win() {
|
||||||
let board = new_test_board();
|
let board = new_test_board();
|
||||||
let mut markings = 0b0;
|
let mut markings = 0b0;
|
||||||
mark_number(&board, &mut markings, 0);
|
mark_number(&board, &mut markings, 0);
|
||||||
mark_number(&board, &mut markings, 1);
|
mark_number(&board, &mut markings, 1);
|
||||||
mark_number(&board, &mut markings, 2);
|
mark_number(&board, &mut markings, 2);
|
||||||
mark_number(&board, &mut markings, 3);
|
mark_number(&board, &mut markings, 3);
|
||||||
mark_number(&board, &mut markings, 4);
|
mark_number(&board, &mut markings, 4);
|
||||||
mark_number(&board, &mut markings, 5);
|
mark_number(&board, &mut markings, 5);
|
||||||
assert!(contains_win(markings));
|
assert!(contains_win(markings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
359
src/day5.rs
359
src/day5.rs
@ -1,177 +1,282 @@
|
|||||||
use std::{num::ParseIntError, cmp::{min, max}};
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
num::ParseIntError,
|
||||||
|
};
|
||||||
|
|
||||||
type Grid = Vec<Vec<i32>>;
|
type Grid = Vec<Vec<i32>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Line {
|
pub struct Line {
|
||||||
x1: i32,
|
x1: i32,
|
||||||
y1: i32,
|
y1: i32,
|
||||||
x2: i32,
|
x2: i32,
|
||||||
y2: i32
|
y2: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseLineError {
|
pub enum ParseLineError {
|
||||||
ParseNumberError(ParseIntError),
|
ParseNumberError(ParseIntError),
|
||||||
ParsePointsError,
|
ParsePointsError,
|
||||||
ParsePointError
|
ParsePointError,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_point(input: &str) -> Result<(i32, i32), ParseLineError> {
|
pub fn parse_point(input: &str) -> Result<(i32, i32), ParseLineError> {
|
||||||
let mut parts = input.split(',');
|
let mut parts = input.split(',');
|
||||||
let x = parts.next()
|
let x = parts
|
||||||
.ok_or(ParseLineError::ParsePointError)?
|
.next()
|
||||||
.parse()
|
.ok_or(ParseLineError::ParsePointError)?
|
||||||
.map_err(ParseLineError::ParseNumberError)?;
|
.parse()
|
||||||
let y = parts.next()
|
.map_err(ParseLineError::ParseNumberError)?;
|
||||||
.ok_or(ParseLineError::ParsePointError)?
|
let y = parts
|
||||||
.parse()
|
.next()
|
||||||
.map_err(ParseLineError::ParseNumberError)?;
|
.ok_or(ParseLineError::ParsePointError)?
|
||||||
return Ok((x, y));
|
.parse()
|
||||||
|
.map_err(ParseLineError::ParseNumberError)?;
|
||||||
|
return Ok((x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<Vec<Line>, ParseLineError> {
|
pub fn parse_input(input: &str) -> Result<Vec<Line>, ParseLineError> {
|
||||||
let mut lines = Vec::new();
|
let mut lines = Vec::new();
|
||||||
for input_line in input.lines() {
|
for input_line in input.lines() {
|
||||||
let mut parts = input_line.split(" -> ");
|
let mut parts = input_line.split(" -> ");
|
||||||
|
|
||||||
let point1 = parts.next().ok_or(ParseLineError::ParsePointsError)?;
|
let point1 = parts.next().ok_or(ParseLineError::ParsePointsError)?;
|
||||||
let (x1, y1) = parse_point(point1)?;
|
let (x1, y1) = parse_point(point1)?;
|
||||||
|
|
||||||
let point2 = parts.next().ok_or(ParseLineError::ParsePointsError)?;
|
let point2 = parts.next().ok_or(ParseLineError::ParsePointsError)?;
|
||||||
let (x2, y2) = parse_point(point2)?;
|
let (x2, y2) = parse_point(point2)?;
|
||||||
|
|
||||||
lines.push(Line { x1, y1, x2, y2 })
|
lines.push(Line { x1, y1, x2, y2 })
|
||||||
}
|
}
|
||||||
return Ok(lines);
|
return Ok(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_bounds(lines: &[Line]) -> (i32, i32, i32, i32) {
|
fn determine_bounds(lines: &[Line]) -> (i32, i32, i32, i32) {
|
||||||
let mut x1 = i32::MAX;
|
let mut x1 = i32::MAX;
|
||||||
let mut y1 = i32::MAX;
|
let mut y1 = i32::MAX;
|
||||||
let mut x2 = i32::MIN;
|
let mut x2 = i32::MIN;
|
||||||
let mut y2 = i32::MIN;
|
let mut y2 = i32::MIN;
|
||||||
for line in lines {
|
for line in lines {
|
||||||
x1 = min(x1, min(line.x1, line.x2));
|
x1 = min(x1, min(line.x1, line.x2));
|
||||||
y1 = min(y1, min(line.y1, line.y2));
|
y1 = min(y1, min(line.y1, line.y2));
|
||||||
x2 = max(x2, max(line.x1, line.x2));
|
x2 = max(x2, max(line.x1, line.x2));
|
||||||
y2 = max(y2, max(line.y1, line.y2));
|
y2 = max(y2, max(line.y1, line.y2));
|
||||||
}
|
}
|
||||||
return (x1, y1, x2, y2);
|
return (x1, y1, x2, y2);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_grid(width: usize, height: usize) -> Grid {
|
fn new_grid(width: usize, height: usize) -> Grid {
|
||||||
let mut grid = Vec::new();
|
let mut grid = Vec::new();
|
||||||
for _ in 0..height {
|
for _ in 0..height {
|
||||||
grid.push(vec![0; width]);
|
grid.push(vec![0; width]);
|
||||||
}
|
}
|
||||||
grid
|
grid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(x: i32) -> i32 {
|
fn sign(x: i32) -> i32 {
|
||||||
if x > 0 {
|
if x > 0 {
|
||||||
1
|
1
|
||||||
} else if x < 0 {
|
} else if x < 0 {
|
||||||
-1
|
-1
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_line(grid: &mut Grid, line: &Line, ox: i32, oy: i32) {
|
fn mark_line(grid: &mut Grid, line: &Line, ox: i32, oy: i32) {
|
||||||
let dx = sign(line.x2 - line.x1);
|
let dx = sign(line.x2 - line.x1);
|
||||||
let dy = sign(line.y2 - line.y1);
|
let dy = sign(line.y2 - line.y1);
|
||||||
let mut x = line.x1;
|
let mut x = line.x1;
|
||||||
let mut y = line.y1;
|
let mut y = line.y1;
|
||||||
while x != line.x2 || y != line.y2 {
|
while x != line.x2 || y != line.y2 {
|
||||||
grid[(y - oy) as usize][(x - ox) as usize] += 1;
|
grid[(y - oy) as usize][(x - ox) as usize] += 1;
|
||||||
if x != line.x2 {
|
if x != line.x2 {
|
||||||
x += dx;
|
x += dx;
|
||||||
}
|
}
|
||||||
if y != line.y2 {
|
if y != line.y2 {
|
||||||
y += dy;
|
y += dy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
grid[(y - oy) as usize][(x - ox) as usize] += 1;
|
grid[(y - oy) as usize][(x - ox) as usize] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_dangerous_areas(grid: &Grid) -> u32 {
|
fn count_dangerous_areas(grid: &Grid) -> u32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for row in grid {
|
for row in grid {
|
||||||
for point in row {
|
for point in row {
|
||||||
if *point > 1 {
|
if *point > 1 {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(lines: &[Line]) -> u32 {
|
pub fn part1(lines: &[Line]) -> u32 {
|
||||||
let bounds = determine_bounds(lines);
|
let bounds = determine_bounds(lines);
|
||||||
let width = (bounds.2 - bounds.0 + 1) as usize;
|
let width = (bounds.2 - bounds.0 + 1) as usize;
|
||||||
let height = (bounds.3 - bounds.1 + 1) as usize;
|
let height = (bounds.3 - bounds.1 + 1) as usize;
|
||||||
let mut grid = new_grid(width, height);
|
let mut grid = new_grid(width, height);
|
||||||
|
|
||||||
for line in lines {
|
for line in lines {
|
||||||
if (line.x1 == line.x2) || (line.y1 == line.y2) {
|
if (line.x1 == line.x2) || (line.y1 == line.y2) {
|
||||||
mark_line(&mut grid, line, bounds.0, bounds.1);
|
mark_line(&mut grid, line, bounds.0, bounds.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count_dangerous_areas(&grid)
|
count_dangerous_areas(&grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(lines: &[Line]) -> u32 {
|
pub fn part2(lines: &[Line]) -> u32 {
|
||||||
let bounds = determine_bounds(lines);
|
let bounds = determine_bounds(lines);
|
||||||
let width = (bounds.2 - bounds.0 + 1) as usize;
|
let width = (bounds.2 - bounds.0 + 1) as usize;
|
||||||
let height = (bounds.3 - bounds.1 + 1) as usize;
|
let height = (bounds.3 - bounds.1 + 1) as usize;
|
||||||
let mut grid = new_grid(width, height);
|
let mut grid = new_grid(width, height);
|
||||||
|
|
||||||
for line in lines {
|
for line in lines {
|
||||||
mark_line(&mut grid, line, bounds.0, bounds.1);
|
mark_line(&mut grid, line, bounds.0, bounds.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
count_dangerous_areas(&grid)
|
count_dangerous_areas(&grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = [
|
let input = [
|
||||||
Line { x1: 0, y1: 9, x2: 5, y2: 9 },
|
Line {
|
||||||
Line { x1: 8, y1: 0, x2: 0, y2: 8 },
|
x1: 0,
|
||||||
Line { x1: 9, y1: 4, x2: 3, y2: 4 },
|
y1: 9,
|
||||||
Line { x1: 2, y1: 2, x2: 2, y2: 1 },
|
x2: 5,
|
||||||
Line { x1: 7, y1: 0, x2: 7, y2: 4 },
|
y2: 9,
|
||||||
Line { x1: 6, y1: 4, x2: 2, y2: 0 },
|
},
|
||||||
Line { x1: 0, y1: 9, x2: 2, y2: 9 },
|
Line {
|
||||||
Line { x1: 3, y1: 4, x2: 1, y2: 4 },
|
x1: 8,
|
||||||
Line { x1: 0, y1: 0, x2: 8, y2: 8 },
|
y1: 0,
|
||||||
Line { x1: 5, y1: 5, x2: 8, y2: 2 }
|
x2: 0,
|
||||||
];
|
y2: 8,
|
||||||
let result = part1(&input);
|
},
|
||||||
assert_eq!(result, 5);
|
Line {
|
||||||
}
|
x1: 9,
|
||||||
|
y1: 4,
|
||||||
|
x2: 3,
|
||||||
|
y2: 4,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 2,
|
||||||
|
y1: 2,
|
||||||
|
x2: 2,
|
||||||
|
y2: 1,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 7,
|
||||||
|
y1: 0,
|
||||||
|
x2: 7,
|
||||||
|
y2: 4,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 6,
|
||||||
|
y1: 4,
|
||||||
|
x2: 2,
|
||||||
|
y2: 0,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 0,
|
||||||
|
y1: 9,
|
||||||
|
x2: 2,
|
||||||
|
y2: 9,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 3,
|
||||||
|
y1: 4,
|
||||||
|
x2: 1,
|
||||||
|
y2: 4,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 8,
|
||||||
|
y2: 8,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 5,
|
||||||
|
y1: 5,
|
||||||
|
x2: 8,
|
||||||
|
y2: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let result = part1(&input);
|
||||||
|
assert_eq!(result, 5);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = [
|
let input = [
|
||||||
Line { x1: 0, y1: 9, x2: 5, y2: 9 },
|
Line {
|
||||||
Line { x1: 8, y1: 0, x2: 0, y2: 8 },
|
x1: 0,
|
||||||
Line { x1: 9, y1: 4, x2: 3, y2: 4 },
|
y1: 9,
|
||||||
Line { x1: 2, y1: 2, x2: 2, y2: 1 },
|
x2: 5,
|
||||||
Line { x1: 7, y1: 0, x2: 7, y2: 4 },
|
y2: 9,
|
||||||
Line { x1: 6, y1: 4, x2: 2, y2: 0 },
|
},
|
||||||
Line { x1: 0, y1: 9, x2: 2, y2: 9 },
|
Line {
|
||||||
Line { x1: 3, y1: 4, x2: 1, y2: 4 },
|
x1: 8,
|
||||||
Line { x1: 0, y1: 0, x2: 8, y2: 8 },
|
y1: 0,
|
||||||
Line { x1: 5, y1: 5, x2: 8, y2: 2 }
|
x2: 0,
|
||||||
];
|
y2: 8,
|
||||||
let result = part2(&input);
|
},
|
||||||
assert_eq!(result, 12);
|
Line {
|
||||||
}
|
x1: 9,
|
||||||
|
y1: 4,
|
||||||
|
x2: 3,
|
||||||
|
y2: 4,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 2,
|
||||||
|
y1: 2,
|
||||||
|
x2: 2,
|
||||||
|
y2: 1,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 7,
|
||||||
|
y1: 0,
|
||||||
|
x2: 7,
|
||||||
|
y2: 4,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 6,
|
||||||
|
y1: 4,
|
||||||
|
x2: 2,
|
||||||
|
y2: 0,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 0,
|
||||||
|
y1: 9,
|
||||||
|
x2: 2,
|
||||||
|
y2: 9,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 3,
|
||||||
|
y1: 4,
|
||||||
|
x2: 1,
|
||||||
|
y2: 4,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 8,
|
||||||
|
y2: 8,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
x1: 5,
|
||||||
|
y1: 5,
|
||||||
|
x2: 8,
|
||||||
|
y2: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let result = part2(&input);
|
||||||
|
assert_eq!(result, 12);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
103
src/day6.rs
103
src/day6.rs
@ -1,34 +1,36 @@
|
|||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<Vec<i32>, ParseIntError> {
|
pub fn parse_input(input: &str) -> Result<Vec<i32>, ParseIntError> {
|
||||||
input.trim_end().split_terminator(',')
|
input
|
||||||
.map(|s| s.parse())
|
.trim_end()
|
||||||
.collect()
|
.split_terminator(',')
|
||||||
|
.map(|s| s.parse())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simulate_step(fishes: &mut Vec<i32>) {
|
fn simulate_step(fishes: &mut Vec<i32>) {
|
||||||
for i in 0..fishes.len() {
|
for i in 0..fishes.len() {
|
||||||
if fishes[i] == 0 {
|
if fishes[i] == 0 {
|
||||||
fishes[i] = 7;
|
fishes[i] = 7;
|
||||||
fishes.push(9);
|
fishes.push(9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..fishes.len() {
|
for i in 0..fishes.len() {
|
||||||
if fishes[i] > 0 {
|
if fishes[i] > 0 {
|
||||||
fishes[i] -= 1;
|
fishes[i] -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: &[i32]) -> u32 {
|
pub fn part1(input: &[i32]) -> u32 {
|
||||||
let mut fishes = input.to_vec();
|
let mut fishes = input.to_vec();
|
||||||
|
|
||||||
for _ in 0..80 {
|
for _ in 0..80 {
|
||||||
simulate_step(&mut fishes)
|
simulate_step(&mut fishes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fishes.len() as u32
|
fishes.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instead of storing each fishes cycle as individual values group them up
|
// Instead of storing each fishes cycle as individual values group them up
|
||||||
@ -37,45 +39,44 @@ pub fn part1(input: &[i32]) -> u32 {
|
|||||||
// And one extra group, for accounting for the delay that when the timer is 0,
|
// And one extra group, for accounting for the delay that when the timer is 0,
|
||||||
// they produce a new fish only on the next turn.
|
// they produce a new fish only on the next turn.
|
||||||
pub fn part2(input: &[i32]) -> u64 {
|
pub fn part2(input: &[i32]) -> u64 {
|
||||||
let mut groups: [u64; 10] = [0; 10];
|
let mut groups: [u64; 10] = [0; 10];
|
||||||
|
|
||||||
for fish in input.iter() {
|
for fish in input.iter() {
|
||||||
groups[*fish as usize + 1] += 1;
|
groups[*fish as usize + 1] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..256 {
|
for _ in 0..256 {
|
||||||
for i in 1..10 {
|
for i in 1..10 {
|
||||||
groups[i-1] += groups[i];
|
groups[i - 1] += groups[i];
|
||||||
groups[i] = 0;
|
groups[i] = 0;
|
||||||
}
|
}
|
||||||
groups[7] += groups[0];
|
groups[7] += groups[0];
|
||||||
groups[9] += groups[0];
|
groups[9] += groups[0];
|
||||||
groups[0] = 0;
|
groups[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
for amount in groups.iter() {
|
for amount in groups.iter() {
|
||||||
count += amount;
|
count += amount;
|
||||||
}
|
}
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = [3,4,3,1,2];
|
let input = [3, 4, 3, 1, 2];
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 5934);
|
assert_eq!(result, 5934);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = [3,4,3,1,2];
|
let input = [3, 4, 3, 1, 2];
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 26984457539 as u64);
|
assert_eq!(result, 26984457539 as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
111
src/day7.rs
111
src/day7.rs
@ -1,85 +1,90 @@
|
|||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Result<Vec<i32>, ParseIntError> {
|
pub fn parse_input(input: &str) -> Result<Vec<i32>, ParseIntError> {
|
||||||
input.trim_end().split_terminator(',')
|
input
|
||||||
.map(|s| s.parse())
|
.trim_end()
|
||||||
.collect()
|
.split_terminator(',')
|
||||||
|
.map(|s| s.parse())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abs(x: i32) -> u32 {
|
fn abs(x: i32) -> u32 {
|
||||||
if x < 0 { -x as u32 } else { x as u32 }
|
if x < 0 {
|
||||||
|
-x as u32
|
||||||
|
} else {
|
||||||
|
x as u32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_total_cost_to1(crabs: &[i32], target: i32) -> u32 {
|
fn calculate_total_cost_to1(crabs: &[i32], target: i32) -> u32 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for crab in crabs {
|
for crab in crabs {
|
||||||
sum += abs(crab-target);
|
sum += abs(crab - target);
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(crabs: &[i32]) -> u32 {
|
pub fn part1(crabs: &[i32]) -> u32 {
|
||||||
let mut best_cost = calculate_total_cost_to1(crabs, crabs[0]);
|
let mut best_cost = calculate_total_cost_to1(crabs, crabs[0]);
|
||||||
|
|
||||||
for position in crabs.iter().skip(1) {
|
for position in crabs.iter().skip(1) {
|
||||||
let cost = calculate_total_cost_to1(crabs, *position);
|
let cost = calculate_total_cost_to1(crabs, *position);
|
||||||
if cost < best_cost {
|
if cost < best_cost {
|
||||||
best_cost = cost;
|
best_cost = cost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
best_cost
|
best_cost
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_total_cost_to2(crabs: &[i32], target: i32) -> u32 {
|
fn calculate_total_cost_to2(crabs: &[i32], target: i32) -> u32 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for crab in crabs {
|
for crab in crabs {
|
||||||
let distance = abs(crab - target);
|
let distance = abs(crab - target);
|
||||||
for i in 1..=distance {
|
for i in 1..=distance {
|
||||||
sum += i;
|
sum += i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_average(arr: &[i32]) -> f32 {
|
fn calculate_average(arr: &[i32]) -> f32 {
|
||||||
let mut sum = 0.0;
|
let mut sum = 0.0;
|
||||||
for a in arr {
|
for a in arr {
|
||||||
sum += *a as f32;
|
sum += *a as f32;
|
||||||
}
|
}
|
||||||
return sum / arr.len() as f32;
|
return sum / arr.len() as f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(crabs: &[i32]) -> u32 {
|
pub fn part2(crabs: &[i32]) -> u32 {
|
||||||
let average_position = calculate_average(crabs).round() as i32;
|
let average_position = calculate_average(crabs).round() as i32;
|
||||||
let mut best_cost = calculate_total_cost_to2(crabs, average_position);
|
let mut best_cost = calculate_total_cost_to2(crabs, average_position);
|
||||||
|
|
||||||
for position in average_position-5..=average_position+5 {
|
for position in average_position - 5..=average_position + 5 {
|
||||||
let cost = calculate_total_cost_to2(crabs, position);
|
let cost = calculate_total_cost_to2(crabs, position);
|
||||||
if cost < best_cost {
|
if cost < best_cost {
|
||||||
best_cost = cost;
|
best_cost = cost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
best_cost
|
best_cost
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = [16,1,2,0,4,2,7,1,2,14];
|
let input = [16, 1, 2, 0, 4, 2, 7, 1, 2, 14];
|
||||||
let result = part1(&input);
|
let result = part1(&input);
|
||||||
assert_eq!(result, 37);
|
assert_eq!(result, 37);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = [16,1,2,0,4,2,7,1,2,14];
|
let input = [16, 1, 2, 0, 4, 2, 7, 1, 2, 14];
|
||||||
let result = part2(&input);
|
let result = part2(&input);
|
||||||
assert_eq!(result, 168);
|
assert_eq!(result, 168);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
602
src/day8.rs
602
src/day8.rs
@ -1,179 +1,507 @@
|
|||||||
use std::{convert::TryInto, collections::HashMap};
|
use std::{collections::HashMap, convert::TryInto};
|
||||||
|
|
||||||
pub struct Entry([String; 10], [String; 4]);
|
pub struct Entry([String; 10], [String; 4]);
|
||||||
|
|
||||||
fn parse_line(line: &str) -> Entry {
|
fn parse_line(line: &str) -> Entry {
|
||||||
let parts: Vec<&str> = line.split(" | ").collect();
|
let parts: Vec<&str> = line.split(" | ").collect();
|
||||||
let unique_patterns = parts[0]
|
let unique_patterns = parts[0]
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.map(|s| String::from(s))
|
.map(|s| String::from(s))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let output_digits = parts[1]
|
let output_digits = parts[1]
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.map(|s| String::from(s))
|
.map(|s| String::from(s))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return Entry(unique_patterns, output_digits);
|
return Entry(unique_patterns, output_digits);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Vec<Entry> {
|
pub fn parse_input(input: &str) -> Vec<Entry> {
|
||||||
input.lines()
|
input.lines().map(parse_line).collect()
|
||||||
.map(parse_line)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(entries: &[Entry]) -> u32 {
|
pub fn part1(entries: &[Entry]) -> u32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
for digit in entry.1.iter() {
|
for digit in entry.1.iter() {
|
||||||
let len = digit.len();
|
let len = digit.len();
|
||||||
if len == 2 || len == 3 || len == 4 || len == 7 {
|
if len == 2 || len == 3 || len == 4 || len == 7 {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function which converts a string into a binary representation
|
// Helper function which converts a string into a binary representation
|
||||||
// I did this is so that the order of the letters won't matter
|
// I did this is so that the order of the letters won't matter
|
||||||
fn signal_to_bitmask(signal: &str) -> u32 {
|
fn signal_to_bitmask(signal: &str) -> u32 {
|
||||||
let mut bitmask = 0;
|
let mut bitmask = 0;
|
||||||
for c in signal.chars() {
|
for c in signal.chars() {
|
||||||
match c {
|
match c {
|
||||||
'a' => bitmask += 0b0000001,
|
'a' => bitmask += 0b0000001,
|
||||||
'b' => bitmask += 0b0000010,
|
'b' => bitmask += 0b0000010,
|
||||||
'c' => bitmask += 0b0000100,
|
'c' => bitmask += 0b0000100,
|
||||||
'd' => bitmask += 0b0001000,
|
'd' => bitmask += 0b0001000,
|
||||||
'e' => bitmask += 0b0010000,
|
'e' => bitmask += 0b0010000,
|
||||||
'f' => bitmask += 0b0100000,
|
'f' => bitmask += 0b0100000,
|
||||||
'g' => bitmask += 0b1000000,
|
'g' => bitmask += 0b1000000,
|
||||||
_ => bitmask += 0
|
_ => bitmask += 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bitmask;
|
return bitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_signal(signal: &str, wire_loopup: &HashMap<u32, u32>) -> Option<u32> {
|
fn decode_signal(signal: &str, wire_loopup: &HashMap<u32, u32>) -> Option<u32> {
|
||||||
wire_loopup.get(&signal_to_bitmask(signal)).map(|n| *n)
|
wire_loopup.get(&signal_to_bitmask(signal)).map(|n| *n)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_signals(signals: &[String], wire_loopup: &HashMap<u32, u32>) -> u32 {
|
fn decode_signals(signals: &[String], wire_loopup: &HashMap<u32, u32>) -> u32 {
|
||||||
let mut number = 0;
|
let mut number = 0;
|
||||||
let n = signals.len();
|
let n = signals.len();
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
let value = decode_signal(&signals[i], wire_loopup).unwrap();
|
let value = decode_signal(&signals[i], wire_loopup).unwrap();
|
||||||
number += value * u32::pow(10, (n-i-1) as u32);
|
number += value * u32::pow(10, (n - i - 1) as u32);
|
||||||
}
|
}
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_wire_lookup(signals: &[String; 10]) -> HashMap<u32, u32> {
|
fn decode_wire_lookup(signals: &[String; 10]) -> HashMap<u32, u32> {
|
||||||
let mut wire_lookup = HashMap::new();
|
let mut wire_lookup = HashMap::new();
|
||||||
let mut bitmasks: [u32; 10] = [0; 10];
|
let mut bitmasks: [u32; 10] = [0; 10];
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
bitmasks[i] = signal_to_bitmask(&signals[i]);
|
bitmasks[i] = signal_to_bitmask(&signals[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut one_bitmask = 0;
|
let mut one_bitmask = 0;
|
||||||
let mut four_bitmask = 0;
|
let mut four_bitmask = 0;
|
||||||
|
|
||||||
// Decode all signals which have unique number of wires
|
// Decode all signals which have unique number of wires
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let len = signals[i].len();
|
let len = signals[i].len();
|
||||||
if len == 2 {
|
if len == 2 {
|
||||||
one_bitmask = bitmasks[i];
|
one_bitmask = bitmasks[i];
|
||||||
wire_lookup.insert(one_bitmask, 1);
|
wire_lookup.insert(one_bitmask, 1);
|
||||||
} else if len == 3 {
|
} else if len == 3 {
|
||||||
wire_lookup.insert(bitmasks[i], 7);
|
wire_lookup.insert(bitmasks[i], 7);
|
||||||
} else if len == 4 {
|
} else if len == 4 {
|
||||||
four_bitmask = bitmasks[i];
|
four_bitmask = bitmasks[i];
|
||||||
wire_lookup.insert(four_bitmask, 4);
|
wire_lookup.insert(four_bitmask, 4);
|
||||||
} else if len == 7 {
|
} else if len == 7 {
|
||||||
wire_lookup.insert(bitmasks[i], 8);
|
wire_lookup.insert(bitmasks[i], 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fourdiff = four_bitmask ^ one_bitmask;
|
let fourdiff = four_bitmask ^ one_bitmask;
|
||||||
|
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let len = signals[i].len();
|
let len = signals[i].len();
|
||||||
if len == 5 {
|
if len == 5 {
|
||||||
if bitmasks[i] & one_bitmask == one_bitmask {
|
if bitmasks[i] & one_bitmask == one_bitmask {
|
||||||
wire_lookup.insert(bitmasks[i], 3);
|
wire_lookup.insert(bitmasks[i], 3);
|
||||||
} else if bitmasks[i] & fourdiff == fourdiff {
|
} else if bitmasks[i] & fourdiff == fourdiff {
|
||||||
wire_lookup.insert(bitmasks[i], 5);
|
wire_lookup.insert(bitmasks[i], 5);
|
||||||
} else {
|
} else {
|
||||||
wire_lookup.insert(bitmasks[i], 2);
|
wire_lookup.insert(bitmasks[i], 2);
|
||||||
}
|
}
|
||||||
} else if len == 6 {
|
} else if len == 6 {
|
||||||
if bitmasks[i] & four_bitmask == four_bitmask {
|
if bitmasks[i] & four_bitmask == four_bitmask {
|
||||||
wire_lookup.insert(bitmasks[i], 9);
|
wire_lookup.insert(bitmasks[i], 9);
|
||||||
} else if bitmasks[i] & fourdiff == fourdiff {
|
} else if bitmasks[i] & fourdiff == fourdiff {
|
||||||
wire_lookup.insert(bitmasks[i], 6);
|
wire_lookup.insert(bitmasks[i], 6);
|
||||||
} else {
|
} else {
|
||||||
wire_lookup.insert(bitmasks[i], 0);
|
wire_lookup.insert(bitmasks[i], 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return wire_lookup;
|
return wire_lookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_entry(entry: &Entry) -> u32 {
|
fn decode_entry(entry: &Entry) -> u32 {
|
||||||
let wire_lookup = decode_wire_lookup(&entry.0);
|
let wire_lookup = decode_wire_lookup(&entry.0);
|
||||||
return decode_signals(&entry.1, &wire_lookup);
|
return decode_signals(&entry.1, &wire_lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(entries: &[Entry]) -> u32 {
|
pub fn part2(entries: &[Entry]) -> u32 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
sum += decode_entry(entry);
|
sum += decode_entry(entry);
|
||||||
}
|
}
|
||||||
sum
|
sum
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// I know it's ugly
|
// I know it's ugly
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
Entry(["be" .into(), "cfbegad" .into(), "cbdgef" .into(), "fgaecd" .into(), "cgeb" .into(), "fdcge" .into(), "agebfd" .into(), "fecdb" .into(), "fabcd" .into(), "edb".into()], ["fdgacbe" .into(), "cefdb" .into(), "cefbgd" .into(), "gcbe".into()]),
|
Entry(
|
||||||
Entry(["edbfga" .into(), "begcd" .into(), "cbg" .into(), "gc" .into(), "gcadebf" .into(), "fbgde" .into(), "acbgfd" .into(), "abcde" .into(), "gfcbed" .into(), "gfec".into()], ["fcgedb" .into(), "cgb" .into(), "dgebacf" .into(), "gc".into()]),
|
[
|
||||||
Entry(["fgaebd" .into(), "cg" .into(), "bdaec" .into(), "gdafb" .into(), "agbcfd" .into(), "gdcbef" .into(), "bgcad" .into(), "gfac" .into(), "gcb" .into(), "cdgabef".into()], ["cg" .into(), "cg" .into(), "fdcagb" .into(), "cbg".into()]),
|
"be".into(),
|
||||||
Entry(["fbegcd" .into(), "cbd" .into(), "adcefb" .into(), "dageb" .into(), "afcb" .into(), "bc" .into(), "aefdc" .into(), "ecdab" .into(), "fgdeca" .into(), "fcdbega".into()], ["efabcd" .into(), "cedba" .into(), "gadfec" .into(), "cb".into()]),
|
"cfbegad".into(),
|
||||||
Entry(["aecbfdg" .into(), "fbg" .into(), "gf" .into(), "bafeg" .into(), "dbefa" .into(), "fcge" .into(), "gcbea" .into(), "fcaegb" .into(), "dgceab" .into(), "fcbdga".into()], ["gecf" .into(), "egdcabf" .into(), "bgf" .into(), "bfgea".into()]),
|
"cbdgef".into(),
|
||||||
Entry(["fgeab" .into(), "ca" .into(), "afcebg" .into(), "bdacfeg" .into(), "cfaedg" .into(), "gcfdb" .into(), "baec" .into(), "bfadeg" .into(), "bafgc" .into(), "acf".into()], ["gebdcfa" .into(), "ecba" .into(), "ca" .into(), "fadegcb".into()]),
|
"fgaecd".into(),
|
||||||
Entry(["dbcfg" .into(), "fgd" .into(), "bdegcaf" .into(), "fgec" .into(), "aegbdf" .into(), "ecdfab" .into(), "fbedc" .into(), "dacgb" .into(), "gdcebf" .into(), "gf".into()], ["cefg" .into(), "dcbef" .into(), "fcge" .into(), "gbcadfe".into()]),
|
"cgeb".into(),
|
||||||
Entry(["bdfegc" .into(), "cbegaf" .into(), "gecbf" .into(), "dfcage" .into(), "bdacg" .into(), "ed" .into(), "bedf" .into(), "ced" .into(), "adcbefg" .into(), "gebcd".into()], ["ed" .into(), "bcgafe" .into(), "cdgba" .into(), "cbgef".into()]),
|
"fdcge".into(),
|
||||||
Entry(["egadfb" .into(), "cdbfeg" .into(), "cegd" .into(), "fecab" .into(), "cgb" .into(), "gbdefca" .into(), "cg" .into(), "fgcdab" .into(), "egfdb" .into(), "bfceg".into()], ["gbdfcae" .into(), "bgc" .into(), "cg" .into(), "cgb".into()]),
|
"agebfd".into(),
|
||||||
Entry(["gcafb" .into(), "gcf" .into(), "dcaebfg" .into(), "ecagb" .into(), "gf" .into(), "abcdeg" .into(), "gaef" .into(), "cafbge" .into(), "fdbac" .into(), "fegbdc".into()], ["fgae" .into(), "cfgab" .into(), "fg" .into(), "bagce".into()]),
|
"fecdb".into(),
|
||||||
];
|
"fabcd".into(),
|
||||||
let result = part1(&input);
|
"edb".into(),
|
||||||
assert_eq!(result, 26);
|
],
|
||||||
}
|
[
|
||||||
|
"fdgacbe".into(),
|
||||||
|
"cefdb".into(),
|
||||||
|
"cefbgd".into(),
|
||||||
|
"gcbe".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"edbfga".into(),
|
||||||
|
"begcd".into(),
|
||||||
|
"cbg".into(),
|
||||||
|
"gc".into(),
|
||||||
|
"gcadebf".into(),
|
||||||
|
"fbgde".into(),
|
||||||
|
"acbgfd".into(),
|
||||||
|
"abcde".into(),
|
||||||
|
"gfcbed".into(),
|
||||||
|
"gfec".into(),
|
||||||
|
],
|
||||||
|
["fcgedb".into(), "cgb".into(), "dgebacf".into(), "gc".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"fgaebd".into(),
|
||||||
|
"cg".into(),
|
||||||
|
"bdaec".into(),
|
||||||
|
"gdafb".into(),
|
||||||
|
"agbcfd".into(),
|
||||||
|
"gdcbef".into(),
|
||||||
|
"bgcad".into(),
|
||||||
|
"gfac".into(),
|
||||||
|
"gcb".into(),
|
||||||
|
"cdgabef".into(),
|
||||||
|
],
|
||||||
|
["cg".into(), "cg".into(), "fdcagb".into(), "cbg".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"fbegcd".into(),
|
||||||
|
"cbd".into(),
|
||||||
|
"adcefb".into(),
|
||||||
|
"dageb".into(),
|
||||||
|
"afcb".into(),
|
||||||
|
"bc".into(),
|
||||||
|
"aefdc".into(),
|
||||||
|
"ecdab".into(),
|
||||||
|
"fgdeca".into(),
|
||||||
|
"fcdbega".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"efabcd".into(),
|
||||||
|
"cedba".into(),
|
||||||
|
"gadfec".into(),
|
||||||
|
"cb".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"aecbfdg".into(),
|
||||||
|
"fbg".into(),
|
||||||
|
"gf".into(),
|
||||||
|
"bafeg".into(),
|
||||||
|
"dbefa".into(),
|
||||||
|
"fcge".into(),
|
||||||
|
"gcbea".into(),
|
||||||
|
"fcaegb".into(),
|
||||||
|
"dgceab".into(),
|
||||||
|
"fcbdga".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"gecf".into(),
|
||||||
|
"egdcabf".into(),
|
||||||
|
"bgf".into(),
|
||||||
|
"bfgea".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"fgeab".into(),
|
||||||
|
"ca".into(),
|
||||||
|
"afcebg".into(),
|
||||||
|
"bdacfeg".into(),
|
||||||
|
"cfaedg".into(),
|
||||||
|
"gcfdb".into(),
|
||||||
|
"baec".into(),
|
||||||
|
"bfadeg".into(),
|
||||||
|
"bafgc".into(),
|
||||||
|
"acf".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"gebdcfa".into(),
|
||||||
|
"ecba".into(),
|
||||||
|
"ca".into(),
|
||||||
|
"fadegcb".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"dbcfg".into(),
|
||||||
|
"fgd".into(),
|
||||||
|
"bdegcaf".into(),
|
||||||
|
"fgec".into(),
|
||||||
|
"aegbdf".into(),
|
||||||
|
"ecdfab".into(),
|
||||||
|
"fbedc".into(),
|
||||||
|
"dacgb".into(),
|
||||||
|
"gdcebf".into(),
|
||||||
|
"gf".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"cefg".into(),
|
||||||
|
"dcbef".into(),
|
||||||
|
"fcge".into(),
|
||||||
|
"gbcadfe".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"bdfegc".into(),
|
||||||
|
"cbegaf".into(),
|
||||||
|
"gecbf".into(),
|
||||||
|
"dfcage".into(),
|
||||||
|
"bdacg".into(),
|
||||||
|
"ed".into(),
|
||||||
|
"bedf".into(),
|
||||||
|
"ced".into(),
|
||||||
|
"adcbefg".into(),
|
||||||
|
"gebcd".into(),
|
||||||
|
],
|
||||||
|
["ed".into(), "bcgafe".into(), "cdgba".into(), "cbgef".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"egadfb".into(),
|
||||||
|
"cdbfeg".into(),
|
||||||
|
"cegd".into(),
|
||||||
|
"fecab".into(),
|
||||||
|
"cgb".into(),
|
||||||
|
"gbdefca".into(),
|
||||||
|
"cg".into(),
|
||||||
|
"fgcdab".into(),
|
||||||
|
"egfdb".into(),
|
||||||
|
"bfceg".into(),
|
||||||
|
],
|
||||||
|
["gbdfcae".into(), "bgc".into(), "cg".into(), "cgb".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"gcafb".into(),
|
||||||
|
"gcf".into(),
|
||||||
|
"dcaebfg".into(),
|
||||||
|
"ecagb".into(),
|
||||||
|
"gf".into(),
|
||||||
|
"abcdeg".into(),
|
||||||
|
"gaef".into(),
|
||||||
|
"cafbge".into(),
|
||||||
|
"fdbac".into(),
|
||||||
|
"fegbdc".into(),
|
||||||
|
],
|
||||||
|
["fgae".into(), "cfgab".into(), "fg".into(), "bagce".into()],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let result = part1(&input);
|
||||||
|
assert_eq!(result, 26);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
Entry(["be" .into(), "cfbegad" .into(), "cbdgef" .into(), "fgaecd" .into(), "cgeb" .into(), "fdcge" .into(), "agebfd" .into(), "fecdb" .into(), "fabcd" .into(), "edb".into()], ["fdgacbe" .into(), "cefdb" .into(), "cefbgd" .into(), "gcbe".into()]),
|
Entry(
|
||||||
Entry(["edbfga" .into(), "begcd" .into(), "cbg" .into(), "gc" .into(), "gcadebf" .into(), "fbgde" .into(), "acbgfd" .into(), "abcde" .into(), "gfcbed" .into(), "gfec".into()], ["fcgedb" .into(), "cgb" .into(), "dgebacf" .into(), "gc".into()]),
|
[
|
||||||
Entry(["fgaebd" .into(), "cg" .into(), "bdaec" .into(), "gdafb" .into(), "agbcfd" .into(), "gdcbef" .into(), "bgcad" .into(), "gfac" .into(), "gcb" .into(), "cdgabef".into()], ["cg" .into(), "cg" .into(), "fdcagb" .into(), "cbg".into()]),
|
"be".into(),
|
||||||
Entry(["fbegcd" .into(), "cbd" .into(), "adcefb" .into(), "dageb" .into(), "afcb" .into(), "bc" .into(), "aefdc" .into(), "ecdab" .into(), "fgdeca" .into(), "fcdbega".into()], ["efabcd" .into(), "cedba" .into(), "gadfec" .into(), "cb".into()]),
|
"cfbegad".into(),
|
||||||
Entry(["aecbfdg" .into(), "fbg" .into(), "gf" .into(), "bafeg" .into(), "dbefa" .into(), "fcge" .into(), "gcbea" .into(), "fcaegb" .into(), "dgceab" .into(), "fcbdga".into()], ["gecf" .into(), "egdcabf" .into(), "bgf" .into(), "bfgea".into()]),
|
"cbdgef".into(),
|
||||||
Entry(["fgeab" .into(), "ca" .into(), "afcebg" .into(), "bdacfeg" .into(), "cfaedg" .into(), "gcfdb" .into(), "baec" .into(), "bfadeg" .into(), "bafgc" .into(), "acf".into()], ["gebdcfa" .into(), "ecba" .into(), "ca" .into(), "fadegcb".into()]),
|
"fgaecd".into(),
|
||||||
Entry(["dbcfg" .into(), "fgd" .into(), "bdegcaf" .into(), "fgec" .into(), "aegbdf" .into(), "ecdfab" .into(), "fbedc" .into(), "dacgb" .into(), "gdcebf" .into(), "gf".into()], ["cefg" .into(), "dcbef" .into(), "fcge" .into(), "gbcadfe".into()]),
|
"cgeb".into(),
|
||||||
Entry(["bdfegc" .into(), "cbegaf" .into(), "gecbf" .into(), "dfcage" .into(), "bdacg" .into(), "ed" .into(), "bedf" .into(), "ced" .into(), "adcbefg" .into(), "gebcd".into()], ["ed" .into(), "bcgafe" .into(), "cdgba" .into(), "cbgef".into()]),
|
"fdcge".into(),
|
||||||
Entry(["egadfb" .into(), "cdbfeg" .into(), "cegd" .into(), "fecab" .into(), "cgb" .into(), "gbdefca" .into(), "cg" .into(), "fgcdab" .into(), "egfdb" .into(), "bfceg".into()], ["gbdfcae" .into(), "bgc" .into(), "cg" .into(), "cgb".into()]),
|
"agebfd".into(),
|
||||||
Entry(["gcafb" .into(), "gcf" .into(), "dcaebfg" .into(), "ecagb" .into(), "gf" .into(), "abcdeg" .into(), "gaef" .into(), "cafbge" .into(), "fdbac" .into(), "fegbdc".into()], ["fgae" .into(), "cfgab" .into(), "fg" .into(), "bagce".into()]),
|
"fecdb".into(),
|
||||||
];
|
"fabcd".into(),
|
||||||
let result = part2(&input);
|
"edb".into(),
|
||||||
assert_eq!(result, 61229);
|
],
|
||||||
}
|
[
|
||||||
|
"fdgacbe".into(),
|
||||||
|
"cefdb".into(),
|
||||||
|
"cefbgd".into(),
|
||||||
|
"gcbe".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"edbfga".into(),
|
||||||
|
"begcd".into(),
|
||||||
|
"cbg".into(),
|
||||||
|
"gc".into(),
|
||||||
|
"gcadebf".into(),
|
||||||
|
"fbgde".into(),
|
||||||
|
"acbgfd".into(),
|
||||||
|
"abcde".into(),
|
||||||
|
"gfcbed".into(),
|
||||||
|
"gfec".into(),
|
||||||
|
],
|
||||||
|
["fcgedb".into(), "cgb".into(), "dgebacf".into(), "gc".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"fgaebd".into(),
|
||||||
|
"cg".into(),
|
||||||
|
"bdaec".into(),
|
||||||
|
"gdafb".into(),
|
||||||
|
"agbcfd".into(),
|
||||||
|
"gdcbef".into(),
|
||||||
|
"bgcad".into(),
|
||||||
|
"gfac".into(),
|
||||||
|
"gcb".into(),
|
||||||
|
"cdgabef".into(),
|
||||||
|
],
|
||||||
|
["cg".into(), "cg".into(), "fdcagb".into(), "cbg".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"fbegcd".into(),
|
||||||
|
"cbd".into(),
|
||||||
|
"adcefb".into(),
|
||||||
|
"dageb".into(),
|
||||||
|
"afcb".into(),
|
||||||
|
"bc".into(),
|
||||||
|
"aefdc".into(),
|
||||||
|
"ecdab".into(),
|
||||||
|
"fgdeca".into(),
|
||||||
|
"fcdbega".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"efabcd".into(),
|
||||||
|
"cedba".into(),
|
||||||
|
"gadfec".into(),
|
||||||
|
"cb".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"aecbfdg".into(),
|
||||||
|
"fbg".into(),
|
||||||
|
"gf".into(),
|
||||||
|
"bafeg".into(),
|
||||||
|
"dbefa".into(),
|
||||||
|
"fcge".into(),
|
||||||
|
"gcbea".into(),
|
||||||
|
"fcaegb".into(),
|
||||||
|
"dgceab".into(),
|
||||||
|
"fcbdga".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"gecf".into(),
|
||||||
|
"egdcabf".into(),
|
||||||
|
"bgf".into(),
|
||||||
|
"bfgea".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"fgeab".into(),
|
||||||
|
"ca".into(),
|
||||||
|
"afcebg".into(),
|
||||||
|
"bdacfeg".into(),
|
||||||
|
"cfaedg".into(),
|
||||||
|
"gcfdb".into(),
|
||||||
|
"baec".into(),
|
||||||
|
"bfadeg".into(),
|
||||||
|
"bafgc".into(),
|
||||||
|
"acf".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"gebdcfa".into(),
|
||||||
|
"ecba".into(),
|
||||||
|
"ca".into(),
|
||||||
|
"fadegcb".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"dbcfg".into(),
|
||||||
|
"fgd".into(),
|
||||||
|
"bdegcaf".into(),
|
||||||
|
"fgec".into(),
|
||||||
|
"aegbdf".into(),
|
||||||
|
"ecdfab".into(),
|
||||||
|
"fbedc".into(),
|
||||||
|
"dacgb".into(),
|
||||||
|
"gdcebf".into(),
|
||||||
|
"gf".into(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"cefg".into(),
|
||||||
|
"dcbef".into(),
|
||||||
|
"fcge".into(),
|
||||||
|
"gbcadfe".into(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"bdfegc".into(),
|
||||||
|
"cbegaf".into(),
|
||||||
|
"gecbf".into(),
|
||||||
|
"dfcage".into(),
|
||||||
|
"bdacg".into(),
|
||||||
|
"ed".into(),
|
||||||
|
"bedf".into(),
|
||||||
|
"ced".into(),
|
||||||
|
"adcbefg".into(),
|
||||||
|
"gebcd".into(),
|
||||||
|
],
|
||||||
|
["ed".into(), "bcgafe".into(), "cdgba".into(), "cbgef".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"egadfb".into(),
|
||||||
|
"cdbfeg".into(),
|
||||||
|
"cegd".into(),
|
||||||
|
"fecab".into(),
|
||||||
|
"cgb".into(),
|
||||||
|
"gbdefca".into(),
|
||||||
|
"cg".into(),
|
||||||
|
"fgcdab".into(),
|
||||||
|
"egfdb".into(),
|
||||||
|
"bfceg".into(),
|
||||||
|
],
|
||||||
|
["gbdfcae".into(), "bgc".into(), "cg".into(), "cgb".into()],
|
||||||
|
),
|
||||||
|
Entry(
|
||||||
|
[
|
||||||
|
"gcafb".into(),
|
||||||
|
"gcf".into(),
|
||||||
|
"dcaebfg".into(),
|
||||||
|
"ecagb".into(),
|
||||||
|
"gf".into(),
|
||||||
|
"abcdeg".into(),
|
||||||
|
"gaef".into(),
|
||||||
|
"cafbge".into(),
|
||||||
|
"fdbac".into(),
|
||||||
|
"fegbdc".into(),
|
||||||
|
],
|
||||||
|
["fgae".into(), "cfgab".into(), "fg".into(), "bagce".into()],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let result = part2(&input);
|
||||||
|
assert_eq!(result, 61229);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
152
src/day9.rs
152
src/day9.rs
@ -1,100 +1,108 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
|
||||||
pub fn parse_input(input: &str) -> Vec<Vec<u32>> {
|
pub fn parse_input(input: &str) -> Vec<Vec<u32>> {
|
||||||
input.lines()
|
input
|
||||||
.map(|s| s.chars().map(|c| c.to_digit(10).unwrap()).collect())
|
.lines()
|
||||||
.collect()
|
.map(|s| s.chars().map(|c| c.to_digit(10).unwrap()).collect())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_low_points(grid: &Vec<Vec<u32>>) -> Vec<(usize, usize)> {
|
fn find_low_points(grid: &Vec<Vec<u32>>) -> Vec<(usize, usize)> {
|
||||||
let mut low_points = Vec::new();
|
let mut low_points = Vec::new();
|
||||||
let height = grid.len();
|
let height = grid.len();
|
||||||
for i in 0..height {
|
for i in 0..height {
|
||||||
let width = grid[i].len();
|
let width = grid[i].len();
|
||||||
for j in 0..width {
|
for j in 0..width {
|
||||||
if (i == 0 || grid[i-1][j] > grid[i][j])
|
if (i == 0 || grid[i - 1][j] > grid[i][j])
|
||||||
&& (i == height-1 || grid[i+1][j] > grid[i][j])
|
&& (i == height - 1 || grid[i + 1][j] > grid[i][j])
|
||||||
&& (j == 0 || grid[i][j-1] > grid[i][j])
|
&& (j == 0 || grid[i][j - 1] > grid[i][j])
|
||||||
&& (j == width-1 || grid[i][j+1] > grid[i][j]) {
|
&& (j == width - 1 || grid[i][j + 1] > grid[i][j])
|
||||||
low_points.push((i, j));
|
{
|
||||||
}
|
low_points.push((i, j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return low_points;
|
}
|
||||||
|
return low_points;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(grid: Vec<Vec<u32>>) -> u32 {
|
pub fn part1(grid: Vec<Vec<u32>>) -> u32 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for low_point in find_low_points(&grid) {
|
for low_point in find_low_points(&grid) {
|
||||||
let depth = grid[low_point.0][low_point.1];
|
let depth = grid[low_point.0][low_point.1];
|
||||||
sum += depth + 1;
|
sum += depth + 1;
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_basin_size(grid: &Vec<Vec<u32>>, location: (usize, usize)) -> u32 {
|
fn find_basin_size(grid: &Vec<Vec<u32>>, location: (usize, usize)) -> u32 {
|
||||||
let mut explored_spots = HashSet::new();
|
let mut explored_spots = HashSet::new();
|
||||||
let mut leaf_nodes = vec![location];
|
let mut leaf_nodes = vec![location];
|
||||||
let height = grid.len();
|
let height = grid.len();
|
||||||
let width = grid[0].len();
|
let width = grid[0].len();
|
||||||
|
|
||||||
while leaf_nodes.len() > 0 {
|
while leaf_nodes.len() > 0 {
|
||||||
let leaf_node = leaf_nodes.pop().unwrap();
|
let leaf_node = leaf_nodes.pop().unwrap();
|
||||||
explored_spots.insert(leaf_node);
|
explored_spots.insert(leaf_node);
|
||||||
|
|
||||||
let (i, j) = leaf_node;
|
let (i, j) = leaf_node;
|
||||||
if i > 0 && !explored_spots.contains(&(i-1, j)) && grid[i-1][j] != 9 { leaf_nodes.push((i-1, j)); }
|
if i > 0 && !explored_spots.contains(&(i - 1, j)) && grid[i - 1][j] != 9 {
|
||||||
if i < height-1 && !explored_spots.contains(&(i+1, j)) && grid[i+1][j] != 9 { leaf_nodes.push((i+1, j)); }
|
leaf_nodes.push((i - 1, j));
|
||||||
if j > 0 && !explored_spots.contains(&(i, j-1)) && grid[i][j-1] != 9 { leaf_nodes.push((i, j-1)); }
|
}
|
||||||
if j < width-1 && !explored_spots.contains(&(i, j+1)) && grid[i][j+1] != 9 { leaf_nodes.push((i, j+1)); }
|
if i < height - 1 && !explored_spots.contains(&(i + 1, j)) && grid[i + 1][j] != 9 {
|
||||||
}
|
leaf_nodes.push((i + 1, j));
|
||||||
|
}
|
||||||
|
if j > 0 && !explored_spots.contains(&(i, j - 1)) && grid[i][j - 1] != 9 {
|
||||||
|
leaf_nodes.push((i, j - 1));
|
||||||
|
}
|
||||||
|
if j < width - 1 && !explored_spots.contains(&(i, j + 1)) && grid[i][j + 1] != 9 {
|
||||||
|
leaf_nodes.push((i, j + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return explored_spots.len() as u32;
|
return explored_spots.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_basin_sizes(grid: &Vec<Vec<u32>>) -> Vec<u32> {
|
fn find_basin_sizes(grid: &Vec<Vec<u32>>) -> Vec<u32> {
|
||||||
let mut sizes = Vec::new();
|
let mut sizes = Vec::new();
|
||||||
for low_point in find_low_points(&grid) {
|
for low_point in find_low_points(&grid) {
|
||||||
sizes.push(find_basin_size(grid, low_point))
|
sizes.push(find_basin_size(grid, low_point))
|
||||||
}
|
}
|
||||||
return sizes;
|
return sizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(grid: Vec<Vec<u32>>) -> u32 {
|
pub fn part2(grid: Vec<Vec<u32>>) -> u32 {
|
||||||
let mut basin_sizes = find_basin_sizes(&grid);
|
let mut basin_sizes = find_basin_sizes(&grid);
|
||||||
basin_sizes.sort_by(|a, b| b.cmp(a));
|
basin_sizes.sort_by(|a, b| b.cmp(a));
|
||||||
return basin_sizes[0] * basin_sizes[1] * basin_sizes[2];
|
return basin_sizes[0] * basin_sizes[1] * basin_sizes[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
vec![2, 1, 9, 9, 9, 4, 3, 2, 1, 0],
|
vec![2, 1, 9, 9, 9, 4, 3, 2, 1, 0],
|
||||||
vec![3, 9, 8, 7, 8, 9, 4, 9, 2, 1],
|
vec![3, 9, 8, 7, 8, 9, 4, 9, 2, 1],
|
||||||
vec![9, 8, 5, 6, 7, 8, 9, 8, 9, 2],
|
vec![9, 8, 5, 6, 7, 8, 9, 8, 9, 2],
|
||||||
vec![8, 7, 6, 7, 8, 9, 6, 7, 8, 9],
|
vec![8, 7, 6, 7, 8, 9, 6, 7, 8, 9],
|
||||||
vec![9, 8, 9, 9, 9, 6, 5, 6, 7, 8]
|
vec![9, 8, 9, 9, 9, 6, 5, 6, 7, 8],
|
||||||
];
|
];
|
||||||
let result = part1(input);
|
let result = part1(input);
|
||||||
assert_eq!(result, 15);
|
assert_eq!(result, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
vec![2, 1, 9, 9, 9, 4, 3, 2, 1, 0],
|
vec![2, 1, 9, 9, 9, 4, 3, 2, 1, 0],
|
||||||
vec![3, 9, 8, 7, 8, 9, 4, 9, 2, 1],
|
vec![3, 9, 8, 7, 8, 9, 4, 9, 2, 1],
|
||||||
vec![9, 8, 5, 6, 7, 8, 9, 8, 9, 2],
|
vec![9, 8, 5, 6, 7, 8, 9, 8, 9, 2],
|
||||||
vec![8, 7, 6, 7, 8, 9, 6, 7, 8, 9],
|
vec![8, 7, 6, 7, 8, 9, 6, 7, 8, 9],
|
||||||
vec![9, 8, 9, 9, 9, 6, 5, 6, 7, 8]
|
vec![9, 8, 9, 9, 9, 6, 5, 6, 7, 8],
|
||||||
];
|
];
|
||||||
let result = part2(input);
|
let result = part2(input);
|
||||||
assert_eq!(result, 1134);
|
assert_eq!(result, 1134);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ mod day19;
|
|||||||
mod day20;
|
mod day20;
|
||||||
mod day21;
|
mod day21;
|
||||||
mod day22;
|
mod day22;
|
||||||
|
mod day23;
|
||||||
|
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -80,6 +81,8 @@ fn run(day: i32, part: i32, input_filename: &str) {
|
|||||||
"21.2" => println!("{}", day21::part2(&day21::parse_input(&contents))),
|
"21.2" => println!("{}", day21::part2(&day21::parse_input(&contents))),
|
||||||
"22.1" => println!("{}", day22::part1(&day22::parse_input(&contents))),
|
"22.1" => println!("{}", day22::part1(&day22::parse_input(&contents))),
|
||||||
"22.2" => println!("{}", day22::part2(day22::parse_input(&contents))),
|
"22.2" => println!("{}", day22::part2(day22::parse_input(&contents))),
|
||||||
|
"23.1" => println!("{}", day23::part1(&day23::parse_input(&contents))),
|
||||||
|
"23.2" => println!("{}", day23::part2(&day23::parse_input(&contents))),
|
||||||
_ => println!("Day {} part {} not found", day, part)
|
_ => println!("Day {} part {} not found", day, part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user