Compare commits
2 Commits
96ea3c317d
...
a431c4b75f
Author | SHA1 | Date | |
---|---|---|---|
a431c4b75f | |||
20e2a9f7be |
106
12/Cargo.lock
generated
Normal file
106
12/Cargo.lock
generated
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"memoffset",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day12"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
8
12/Cargo.toml
Normal file
8
12/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "day12"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itertools = "0.12.0"
|
||||||
|
rayon = "1.8.0"
|
219
12/src/main.rs
Normal file
219
12/src/main.rs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufRead, BufReader, Lines};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
// BOILERPLATE
|
||||||
|
type InputIter = Lines<BufReader<File>>;
|
||||||
|
|
||||||
|
fn get_input() -> InputIter {
|
||||||
|
let f = File::open("input").unwrap();
|
||||||
|
let br = BufReader::new(f);
|
||||||
|
br.lines()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let start = Instant::now();
|
||||||
|
let ans1 = problem1(get_input());
|
||||||
|
let duration = start.elapsed();
|
||||||
|
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let ans2 = problem2(get_input());
|
||||||
|
let duration = start.elapsed();
|
||||||
|
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
|
||||||
|
}
|
||||||
|
|
||||||
|
// PARSE
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SpringRow<'a> {
|
||||||
|
springs: Vec<char>,
|
||||||
|
spans: Vec<usize>,
|
||||||
|
cache: RefCell<HashMap<(usize, &'a [usize], usize), u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&str> for SpringRow<'a> {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
let (springs_s, spans_s) = s.split_once(' ').unwrap();
|
||||||
|
SpringRow {
|
||||||
|
springs: springs_s.chars().collect(),
|
||||||
|
spans: spans_s.split(',').map(|x| x.parse().unwrap()).collect(),
|
||||||
|
cache: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SpringRow<'a> {
|
||||||
|
fn possible_arrangements(&self) -> u64 {
|
||||||
|
let mut permutations = vec![self.springs.clone()];
|
||||||
|
while let Some(cur) = permutations.iter().position(|perm| perm.contains(&'?')) {
|
||||||
|
let perm = permutations.remove(cur);
|
||||||
|
let rep_pos = perm.iter().position(|c| *c == '?').unwrap();
|
||||||
|
|
||||||
|
let mut new_elem = perm.clone();
|
||||||
|
|
||||||
|
new_elem[rep_pos] = '.';
|
||||||
|
permutations.push(new_elem.clone());
|
||||||
|
new_elem[rep_pos] = '#';
|
||||||
|
permutations.push(new_elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
permutations
|
||||||
|
.iter()
|
||||||
|
.filter_map(|perm| {
|
||||||
|
let groups: Vec<usize> = perm
|
||||||
|
.iter()
|
||||||
|
.group_by(|c| **c == '#')
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(key, group)| if key { Some(group.count()) } else { None })
|
||||||
|
.collect();
|
||||||
|
if groups == self.spans {
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.count() as u64
|
||||||
|
}
|
||||||
|
fn memoize_arrangements_r(
|
||||||
|
&self,
|
||||||
|
idx: usize,
|
||||||
|
spans_left: &'a [usize],
|
||||||
|
cur_span_left: usize,
|
||||||
|
) -> u64 {
|
||||||
|
if let Some(value) = self.cache.borrow().get(&(idx, spans_left, cur_span_left)) {
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
let result = self.possible_arrangements_r(idx, spans_left, cur_span_left);
|
||||||
|
self.cache
|
||||||
|
.borrow_mut()
|
||||||
|
.insert((idx, spans_left, cur_span_left), result);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
fn possible_arrangements_r(
|
||||||
|
&self,
|
||||||
|
idx: usize,
|
||||||
|
spans_left: &'a [usize],
|
||||||
|
cur_span_left: usize,
|
||||||
|
) -> u64 {
|
||||||
|
if cur_span_left == 0 && spans_left.len() == 1 {
|
||||||
|
// everything is consumed. if we're at the end, we good
|
||||||
|
// if we have stuff left, as long as it doesn't contain a fixed broken spring we good
|
||||||
|
if idx == self.springs.len() || !self.springs[idx..].contains(&'#') {
|
||||||
|
// println!("{:?} valid", self.springs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// something invalid
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if idx >= self.springs.len() {
|
||||||
|
// reached the end of the string without consuming all our spans
|
||||||
|
// println!(
|
||||||
|
// "{:?} invalid idx: {} spans_left: {:?}",
|
||||||
|
// self.springs, idx, spans_left
|
||||||
|
// );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// println!(
|
||||||
|
// "call idx: {} str: {:?} spans_left: {:?} cur_span_left: {}",
|
||||||
|
// idx,
|
||||||
|
// &self.springs[idx..],
|
||||||
|
// spans_left,
|
||||||
|
// cur_span_left
|
||||||
|
// );
|
||||||
|
return if cur_span_left == 0 {
|
||||||
|
match self.springs[idx] {
|
||||||
|
'#' => 0, // a broken spring after we have completed our span is invalid
|
||||||
|
// a space or a ? (becomes a space) starts a new span
|
||||||
|
'.' | '?' => self.memoize_arrangements_r(idx + 1, &spans_left[1..], spans_left[1]),
|
||||||
|
_ => panic!("invalid spring"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are mid span
|
||||||
|
match self.springs[idx] {
|
||||||
|
// cur_span_left != 0, so we still have work to do with broken springs
|
||||||
|
'#' => self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left - 1),
|
||||||
|
'.' => {
|
||||||
|
if spans_left[0] == cur_span_left {
|
||||||
|
self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'?' => {
|
||||||
|
if spans_left[0] == cur_span_left {
|
||||||
|
self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left - 1)
|
||||||
|
+ self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left)
|
||||||
|
} else {
|
||||||
|
self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("invalid spring"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 1 solution
|
||||||
|
|
||||||
|
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
|
let rows: Vec<SpringRow> = input
|
||||||
|
.map(|row| SpringRow::from(row.unwrap().as_str()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
rows.iter()
|
||||||
|
.map(|row| row.possible_arrangements_r(0, &row.spans, row.spans[0]))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 2 solution
|
||||||
|
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
|
let mut rows: Vec<SpringRow> = input
|
||||||
|
.map(|row| SpringRow::from(row.unwrap().as_str()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for row in &mut rows {
|
||||||
|
let orig_slice = &row.springs.clone();
|
||||||
|
for _i in 0..4 {
|
||||||
|
row.springs.push('?');
|
||||||
|
row.springs.extend_from_slice(orig_slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
let orig_span_slice = &row.spans.clone();
|
||||||
|
for _i in 0..4 {
|
||||||
|
row.spans.extend_from_slice(orig_span_slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rows.iter()
|
||||||
|
.map(|row| row.possible_arrangements_r(0, &row.spans, row.spans[0]))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::*;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
const EXAMPLE: &str = &"???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn problem1_example() {
|
||||||
|
let c = Cursor::new(EXAMPLE);
|
||||||
|
assert_eq!(problem1(c.lines()), 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn problem2_example() {
|
||||||
|
let c = Cursor::new(EXAMPLE);
|
||||||
|
assert_eq!(problem2(c.lines()), 525152);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user