Day 12+13

I may or may not have delayed p2s till today

Also don't judge my bad code, I just wanted it done
This commit is contained in:
Tyler Beckman 2023-12-13 22:00:34 -07:00
parent 97e901ee19
commit f26ca8924c
Signed by: Ty
GPG key ID: 2813440C772555A4
7 changed files with 524 additions and 27 deletions

7
Cargo.lock generated
View file

@ -21,6 +21,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
name = "advent" name = "advent"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clru",
"getrandom", "getrandom",
"itertools", "itertools",
"num-bigint", "num-bigint",
@ -232,6 +233,12 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "clru"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.0"

View file

@ -11,6 +11,7 @@ regex = "1.10.2"
scraper = "0.18.1" scraper = "0.18.1"
reqwest = "0.11.22" reqwest = "0.11.22"
itertools = "0.12.0" itertools = "0.12.0"
clru = "0.6.1"
[dependencies.getrandom] [dependencies.getrandom]
version = "0.2.11" version = "0.2.11"

View file

@ -38,9 +38,13 @@ impl Day for Day11 {
.iter() .iter()
.enumerate() .enumerate()
.flat_map(|(i, line)| { .flat_map(|(i, line)| {
line.iter() line.iter().enumerate().filter_map(move |(j, &c)| {
.enumerate() if c == '#' {
.filter_map(move |(j, &c)| if c == '#' { Some((i as isize, j as isize)) } else { None }) Some((i as isize, j as isize))
} else {
None
}
})
}) })
.collect(); .collect();
@ -50,10 +54,10 @@ impl Day for Day11 {
.map(|c| (c[0], c[1])) .map(|c| (c[0], c[1]))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let result = combinations.into_iter().map( let result = combinations
|(start, end)| .into_iter()
(end.0 - start.0).abs() + (end.1 - start.1).abs() .map(|(start, end)| (end.0 - start.0).abs() + (end.1 - start.1).abs())
).sum::<isize>(); .sum::<isize>();
result.to_string() result.to_string()
} }
@ -82,18 +86,26 @@ impl Day for Day11 {
.iter() .iter()
.enumerate() .enumerate()
.flat_map(|(i, line)| { .flat_map(|(i, line)| {
line.iter() line.iter().enumerate().filter_map({
.enumerate()
.filter_map({
let rows_expanded = &self.rows_expanded; let rows_expanded = &self.rows_expanded;
let columns_expanded = &self.columns_expanded; let columns_expanded = &self.columns_expanded;
move |(j, &c)| { move |(j, &c)| {
if c == '#' { if c == '#' {
Some(( Some((
i as i128 + (rows_expanded.iter().filter(|&&row| row < i).count() as i128 * 999999i128), i as i128
j as i128 + (columns_expanded.iter().filter(|&&column| column < j).count() as i128 * 999999i128) + (rows_expanded.iter().filter(|&&row| row < i).count()
as i128
* 999999i128),
j as i128
+ (columns_expanded
.iter()
.filter(|&&column| column < j)
.count() as i128
* 999999i128),
)) ))
} else { None } } else {
None
}
} }
}) })
}) })
@ -105,10 +117,10 @@ impl Day for Day11 {
.map(|c| (c[0], c[1])) .map(|c| (c[0], c[1]))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let result = combinations.into_iter().map( let result = combinations
|(start, end)| .into_iter()
(end.0 - start.0).abs() + (end.1 - start.1).abs() .map(|(start, end)| (end.0 - start.0).abs() + (end.1 - start.1).abs())
).sum::<i128>(); .sum::<i128>();
result.to_string() result.to_string()
} }

170
lib/src/day12/mod.rs Normal file
View file

@ -0,0 +1,170 @@
use clru::CLruCache;
use itertools::{repeat_n, Itertools};
use crate::utils::Day;
#[derive(Debug)]
pub struct Day12 {
pub input: String,
pub cache: CLruCache<(Vec<char>, usize, Vec<usize>), usize>,
}
impl Day12 {
fn calculate(
&mut self,
chars_remaining: &[char],
accumulated: usize,
groups: &[usize],
) -> usize {
if let Some(&cached) =
self.cache
.get(&(chars_remaining.to_vec(), accumulated, groups.to_vec()))
{
return cached;
}
let result = if chars_remaining.len() == 0 {
// We have reached the end, determine success
if groups.len() == 0 {
// All groups are gone, success!
1
} else {
// Otherwise, we didn't get all the groups, failure :(
0
}
} else {
// Continue accumulating and checking groups, adding up both directions when we have a ?
(if chars_remaining[0] != '.' {
// The char on the stack is a #, accumulate
self.calculate(&chars_remaining[1..], accumulated + 1, groups)
} else {
0
}) + (if chars_remaining[0] != '#' {
// The char on the stack is a ., either close a group or NOOP
if accumulated != 0 {
// There is a current accumulation, attempt to close the group
if groups.get(0).is_some_and(|&g| g == accumulated) {
// There is a group to close and it is equal to the amount of accumulation, close it and reset accumulation!
self.calculate(&chars_remaining[1..], 0, &groups[1..])
} else {
// There is no group to close or the accumulation is too low/high, fail this batch
0
}
} else {
// There is no current accumulation, NOOP this char
self.calculate(&chars_remaining[1..], 0, groups)
}
} else {
0
})
};
self.cache.put(
(chars_remaining.to_vec(), accumulated, groups.to_vec()),
result,
);
result
}
}
impl Day for Day12 {
fn part1(&mut self) -> String {
let parsed = self
.input
.lines()
.map(|line| line.split_once(' ').unwrap())
.map(|(springs, groups)| {
(
springs.chars().collect::<Vec<_>>(),
groups
.split(',')
.map(|s| s.parse::<usize>().unwrap())
.collect::<Vec<_>>(),
)
})
.collect::<Vec<_>>();
let mut result = 0u128;
for (springs, groups) in parsed {
let unknowns = springs
.iter()
.enumerate()
.filter_map(|(i, &c)| if c == '?' { Some((c, i)) } else { None })
.count();
let possible = repeat_n(vec!['#', '.'].into_iter(), unknowns).multi_cartesian_product();
for permutation in possible {
let mut permutation = permutation.into_iter();
// For each possible combination,
let springs = springs.iter().map(|char| {
if char == &'?' {
permutation.next().unwrap()
} else {
*char
}
});
let grouping = springs.group_by(|&char| char);
let grouped = grouping
.into_iter()
.filter_map(|(c, group)| if c == '#' { Some(group.count()) } else { None })
.collect::<Vec<_>>();
if grouped == groups {
result += 1;
}
}
}
result.to_string()
}
fn part2(&mut self) -> String {
let parsed = self
.input
.lines()
.map(|line| line.split_once(' ').unwrap())
.map(|(springs, groups)| {
(
repeat_n(springs, 5).join("?").chars().collect::<Vec<_>>(),
repeat_n(groups, 5)
.join(",")
.split(",")
.map(|s| s.parse::<usize>().unwrap())
.collect::<Vec<_>>(),
)
})
.map(|(mut springs, groups)| {
(
{
springs.push('.');
springs
},
groups,
)
})
.collect::<Vec<_>>();
// Hey at least I can say I tried the mathy way, but idk enough math for this lol
// let mut result = 0u128;
// for (springs, groups) in parsed {
// let n = (springs.len() - groups.iter().sum::<usize>() + 1) as u128;
// let r = groups.len() as u128;
// let nonogram_solution_possibilities = (1..=n).product::<BigInt>() / ((1..=(n-r)).product::<BigInt>() * (1..=r).product::<BigInt>());
// let permutations = (0..(springs.iter().filter(|&&c| c == '?').count())).map(|_| 2 as u128).product::<BigInt>();
// dbg!(nonogram_solution_possibilities, 2u128.pow(springs.len() as u32), permutations);
// }
let mut acc = 0;
for (springs, groups) in parsed.into_iter() {
acc += self.calculate(springs.as_slice(), 0, groups.as_slice());
}
acc.to_string()
}
}

293
lib/src/day13/mod.rs Normal file
View file

@ -0,0 +1,293 @@
use itertools::Itertools;
use crate::utils::Day;
#[derive(Debug, Default)]
pub struct Day13 {
pub input: String,
}
impl Day for Day13 {
fn part1(&mut self) -> String {
let pictures = self.input.split("\n\n");
pictures
.map(|picture| {
let picture = picture.trim();
let mut cache = String::new();
let rows = picture
.lines()
.map(|s| s.to_string())
.enumerate()
.collect_vec();
let columns = (0..picture.split_once('\n').unwrap().0.len())
.map(|i| {
(
i,
picture
.lines()
.map(|line| line.chars().nth(i).unwrap())
.join(""),
)
})
.collect_vec();
// Find all row (horizontal) reflections
let mut reflects_rows = vec![(0usize, 0usize); 0];
for (i, line) in rows.iter() {
if line == &cache {
reflects_rows.push((i - 1, *i));
} else {
cache = line.to_string();
}
}
cache = String::new();
// Find all column (vertical) reflections
let mut reflects_columns = vec![(0usize, 0usize); 0];
for (i, column) in columns.iter() {
if column == &cache {
reflects_columns.push((i - 1, *i));
} else {
cache = column.clone();
}
}
// Expand all row reflections
let rows = reflects_rows
.iter_mut()
.filter_map(|reflection| {
loop {
if reflection.0 != 0
&& reflection.1 != rows.len() - 1
&& rows[reflection.0 - 1].1 == rows[reflection.1 + 1].1
{
*reflection = (reflection.0 - 1, reflection.1 + 1);
} else {
break;
}
}
if reflection.0 == 0 || reflection.1 == rows.len() - 1 {
Some(((reflection.0 + reflection.1) / 2) + 1)
} else {
None
}
})
.sum::<usize>();
// Expand all column reflections
let columns = reflects_columns
.iter_mut()
.filter_map(|reflection| {
loop {
if reflection.0 != 0
&& reflection.1 != columns.len() - 1
&& columns[reflection.0 - 1].1 == columns[reflection.1 + 1].1
{
*reflection = (reflection.0 - 1, reflection.1 + 1);
} else {
break;
}
}
if reflection.0 == 0 || reflection.1 == columns.len() - 1 {
Some(((reflection.0 + reflection.1) / 2) + 1)
} else {
None
}
})
.sum::<usize>();
columns + (rows * 100)
})
.sum::<usize>()
.to_string()
}
fn part2(&mut self) -> String {
let pictures = self.input.split("\n\n");
// NOTE: do not short circuit, make sure to actually check if the one-off reflection is valid first
pictures
.map(|picture| {
let picture = picture.trim();
let rows = picture
.lines()
.map(|s| s.to_string())
.enumerate()
.collect_vec();
let columns = (0..picture.split_once('\n').unwrap().0.len())
.map(|i| {
(
i,
picture
.lines()
.map(|line| line.chars().nth(i).unwrap())
.join(""),
)
})
.collect_vec();
// Find all row (horizontal) reflections
let mut cache = String::from(" ".repeat(columns.len()));
let mut reflects_rows = vec![(0usize, 0usize, false); 0];
for (i, line) in rows.iter() {
let mut iter = cache.chars();
let intersection = line
.chars()
.map(|c| if c == iter.next().unwrap() { c } else { '!' })
.collect_vec();
match intersection.iter().filter(|&&c| c == '!').count() {
0 => reflects_rows.push((i - 1, *i, false)),
1 => reflects_rows.push((i - 1, *i, true)),
_ => (),
}
cache = line.to_string();
}
let mut cache = String::from(" ".repeat(rows.len()));
// Find all column (vertical) reflections
let mut reflects_columns = vec![(0usize, 0usize, false); 0];
for (i, column) in columns.iter() {
let mut iter = cache.chars();
let intersection = column
.chars()
.map(|c| if c == iter.next().unwrap() { c } else { '!' })
.collect_vec();
match intersection.iter().filter(|&&c| c == '!').count() {
0 => reflects_columns.push((i - 1, *i, false)),
1 => reflects_columns.push((i - 1, *i, true)),
_ => (),
}
cache = column.to_string();
}
// Expand all row reflections
reflects_rows = reflects_rows
.into_iter()
.filter_map(|mut reflection| {
loop {
if reflection.0 == 0 || reflection.1 == rows.len() - 1 {
break;
}
let mut lower_iter = rows[reflection.0 - 1].1.chars();
let intersection = rows[reflection.1 + 1]
.1
.chars()
.map(|c| {
if c == lower_iter.next().unwrap() {
c
} else {
'!'
}
})
.collect_vec();
match (
intersection.iter().filter(|&&c| c == '!').count(),
reflection.2,
) {
(1, false) => {
reflection = (reflection.0 - 1, reflection.1 + 1, true)
}
(0, _) => {
reflection = (reflection.0 - 1, reflection.1 + 1, reflection.2)
}
_ => break,
}
}
if reflection.0 == 0 || reflection.1 == rows.len() - 1 {
Some((
(reflection.0 + reflection.1) / 2,
((reflection.0 + reflection.1) / 2) + 1,
reflection.2,
))
} else {
None
}
})
.collect();
// Expand all column reflections
reflects_columns = reflects_columns
.into_iter()
.filter_map(|mut reflection| {
loop {
if reflection.0 == 0 || reflection.1 == columns.len() - 1 {
break;
}
let mut lower_iter = columns[reflection.0 - 1].1.chars();
let intersection = columns[reflection.1 + 1]
.1
.chars()
.map(|c| {
if c == lower_iter.next().unwrap() {
c
} else {
'!'
}
})
.collect_vec();
match (
intersection.iter().filter(|&&c| c == '!').count(),
reflection.2,
) {
(1, false) => {
reflection = (reflection.0 - 1, reflection.1 + 1, true)
}
(0, _) => {
reflection = (reflection.0 - 1, reflection.1 + 1, reflection.2)
}
_ => break,
}
}
if reflection.0 == 0 || reflection.1 == columns.len() - 1 {
Some((
(reflection.0 + reflection.1) / 2,
((reflection.0 + reflection.1) / 2) + 1,
reflection.2,
))
} else {
None
}
})
.collect();
reflects_rows
.into_iter()
.find_map(|reflection| {
if reflection.2 == true {
Some(reflection.1 * 100)
} else {
None
}
})
.unwrap_or_else(|| {
reflects_columns
.into_iter()
.find_map(|reflection| {
if reflection.2 == true {
Some(reflection.1)
} else {
None
}
})
.expect("unable to find reflection wtf")
})
})
.sum::<usize>()
.to_string()
}
}

View file

@ -1,8 +1,11 @@
use std::collections::HashMap; use std::{collections::HashMap, num::NonZeroUsize};
use clru::CLruCache;
use day1::Day1; use day1::Day1;
use day10::Day10; use day10::Day10;
use day11::Day11; use day11::Day11;
use day12::Day12;
use day13::Day13;
use day2::Day2; use day2::Day2;
use day3::Day3; use day3::Day3;
use day4::Day4; use day4::Day4;
@ -19,6 +22,8 @@ pub mod utils;
pub mod day1; pub mod day1;
pub mod day10; pub mod day10;
pub mod day11; pub mod day11;
pub mod day12;
pub mod day13;
pub mod day2; pub mod day2;
pub mod day3; pub mod day3;
pub mod day4; pub mod day4;
@ -53,7 +58,16 @@ pub fn get_day(day: u8, input: String) -> Box<dyn Day> {
input, input,
parsed: vec![], parsed: vec![],
}), }),
11 => Box::new(Day11 { input, rows_expanded: vec![], columns_expanded: vec![] }), 11 => Box::new(Day11 {
input,
rows_expanded: vec![],
columns_expanded: vec![],
}),
12 => Box::new(Day12 {
input,
cache: CLruCache::new(NonZeroUsize::new(10000).unwrap()),
}),
13 => Box::new(Day13 { input }),
_ => panic!("Invalid day #"), _ => panic!("Invalid day #"),
} }
} }