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:
parent
97e901ee19
commit
f26ca8924c
7 changed files with 524 additions and 27 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -21,6 +21,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||
name = "advent"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clru",
|
||||
"getrandom",
|
||||
"itertools",
|
||||
"num-bigint",
|
||||
|
@ -232,6 +233,12 @@ version = "0.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "clru"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
|
|
|
@ -11,6 +11,7 @@ regex = "1.10.2"
|
|||
scraper = "0.18.1"
|
||||
reqwest = "0.11.22"
|
||||
itertools = "0.12.0"
|
||||
clru = "0.6.1"
|
||||
|
||||
[dependencies.getrandom]
|
||||
version = "0.2.11"
|
||||
|
|
|
@ -38,9 +38,13 @@ impl Day for Day11 {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(j, &c)| if c == '#' { Some((i as isize, j as isize)) } else { None })
|
||||
line.iter().enumerate().filter_map(move |(j, &c)| {
|
||||
if c == '#' {
|
||||
Some((i as isize, j as isize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -50,10 +54,10 @@ impl Day for Day11 {
|
|||
.map(|c| (c[0], c[1]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = combinations.into_iter().map(
|
||||
|(start, end)|
|
||||
(end.0 - start.0).abs() + (end.1 - start.1).abs()
|
||||
).sum::<isize>();
|
||||
let result = combinations
|
||||
.into_iter()
|
||||
.map(|(start, end)| (end.0 - start.0).abs() + (end.1 - start.1).abs())
|
||||
.sum::<isize>();
|
||||
|
||||
result.to_string()
|
||||
}
|
||||
|
@ -82,18 +86,26 @@ impl Day for Day11 {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.filter_map({
|
||||
line.iter().enumerate().filter_map({
|
||||
let rows_expanded = &self.rows_expanded;
|
||||
let columns_expanded = &self.columns_expanded;
|
||||
move |(j, &c)| {
|
||||
if c == '#' {
|
||||
Some((
|
||||
i as i128 + (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)
|
||||
i as i128
|
||||
+ (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]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = combinations.into_iter().map(
|
||||
|(start, end)|
|
||||
(end.0 - start.0).abs() + (end.1 - start.1).abs()
|
||||
).sum::<i128>();
|
||||
let result = combinations
|
||||
.into_iter()
|
||||
.map(|(start, end)| (end.0 - start.0).abs() + (end.1 - start.1).abs())
|
||||
.sum::<i128>();
|
||||
|
||||
result.to_string()
|
||||
}
|
||||
|
|
170
lib/src/day12/mod.rs
Normal file
170
lib/src/day12/mod.rs
Normal 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
293
lib/src/day13/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, num::NonZeroUsize};
|
||||
|
||||
use clru::CLruCache;
|
||||
use day1::Day1;
|
||||
use day10::Day10;
|
||||
use day11::Day11;
|
||||
use day12::Day12;
|
||||
use day13::Day13;
|
||||
use day2::Day2;
|
||||
use day3::Day3;
|
||||
use day4::Day4;
|
||||
|
@ -19,6 +22,8 @@ pub mod utils;
|
|||
pub mod day1;
|
||||
pub mod day10;
|
||||
pub mod day11;
|
||||
pub mod day12;
|
||||
pub mod day13;
|
||||
pub mod day2;
|
||||
pub mod day3;
|
||||
pub mod day4;
|
||||
|
@ -53,7 +58,16 @@ pub fn get_day(day: u8, input: String) -> Box<dyn Day> {
|
|||
input,
|
||||
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 #"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue