day21: problem 2 solution
not proud of this one either, completely cheated and it does not get exactly the correct error (it was off by one for my input), but the quadratic solver on Wolfram Alpha was able to do it.
This commit is contained in:
142
21/src/main.rs
142
21/src/main.rs
@ -1,9 +1,7 @@
|
||||
use core::panic;
|
||||
use std::collections::HashSet;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use petgraph::algo::k_shortest_path;
|
||||
use petgraph::prelude::*;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Lines};
|
||||
use std::time::Instant;
|
||||
@ -31,46 +29,33 @@ fn main() {
|
||||
|
||||
// PARSE
|
||||
|
||||
type Position = (usize, usize);
|
||||
type Position = (isize, isize);
|
||||
type WrappedPosition = (usize, usize);
|
||||
type Offset = (isize, isize);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MapTile {
|
||||
c: char,
|
||||
idx: NodeIndex,
|
||||
}
|
||||
|
||||
impl MapTile {
|
||||
fn new(c: char, idx: NodeIndex) -> Self {
|
||||
Self { c, idx }
|
||||
fn new(c: char) -> Self {
|
||||
Self { c }
|
||||
}
|
||||
}
|
||||
|
||||
type GraphType = DiGraph<char, ()>;
|
||||
struct GardenMap {
|
||||
map: Vec<Vec<MapTile>>,
|
||||
graph: GraphType,
|
||||
start: Position,
|
||||
}
|
||||
|
||||
impl<T: BufRead> From<Lines<T>> for GardenMap {
|
||||
fn from(lines: Lines<T>) -> Self {
|
||||
let mut graph = DiGraph::new();
|
||||
let map = lines
|
||||
.map(|line| {
|
||||
line.unwrap()
|
||||
.chars()
|
||||
.map(|c| MapTile::new(c, graph.add_node(c)))
|
||||
.collect()
|
||||
})
|
||||
.map(|line| line.unwrap().chars().map(|c| MapTile::new(c)).collect())
|
||||
.collect();
|
||||
let mut new = Self {
|
||||
map,
|
||||
start: (0, 0),
|
||||
graph,
|
||||
};
|
||||
let mut new = Self { map, start: (0, 0) };
|
||||
new.find_start();
|
||||
new.build_graph();
|
||||
new
|
||||
}
|
||||
}
|
||||
@ -78,6 +63,21 @@ impl<T: BufRead> From<Lines<T>> for GardenMap {
|
||||
const ADJACENCIES: [Offset; 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
|
||||
|
||||
impl GardenMap {
|
||||
fn wrap_pos(&self, pos: &Position) -> WrappedPosition {
|
||||
let (width, height) = (self.width() as isize, self.height() as isize);
|
||||
(
|
||||
if pos.0 < 0 {
|
||||
(pos.0 + (-pos.0 / width + 1) * width) as usize % self.width()
|
||||
} else {
|
||||
pos.0 as usize % self.width()
|
||||
},
|
||||
if pos.1 < 0 {
|
||||
(pos.1 + (-pos.1 / height + 1) * height) as usize % self.height()
|
||||
} else {
|
||||
pos.1 as usize % self.height()
|
||||
},
|
||||
)
|
||||
}
|
||||
fn width(&self) -> usize {
|
||||
self.map[0].len()
|
||||
}
|
||||
@ -85,80 +85,103 @@ impl GardenMap {
|
||||
self.map.len()
|
||||
}
|
||||
fn at(&self, pos: &Position) -> &MapTile {
|
||||
let pos = self.wrap_pos(pos);
|
||||
&self.map[pos.1][pos.0]
|
||||
}
|
||||
fn at_mut(&mut self, pos: &Position) -> &mut MapTile {
|
||||
&mut self.map[pos.1][pos.0]
|
||||
}
|
||||
// return the valid 'moves' from pos
|
||||
fn adjacent_to(&self, pos: &Position) -> Vec<Position> {
|
||||
ADJACENCIES
|
||||
.iter()
|
||||
.filter_map(|ofs| self.offset_pos(pos, ofs))
|
||||
.filter(|pos| self.at(pos).c == '.' || self.at(pos).c == 'S')
|
||||
.filter(|pos| self.at(pos).c != '#')
|
||||
.collect()
|
||||
}
|
||||
fn offset_pos(&self, pos: &Position, ofs: &Offset) -> Option<Position> {
|
||||
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
|
||||
if new_pos.0 < 0 || new_pos.1 < 0 || new_pos.0 >= self.width() as isize || new_pos.1 >= self.height() as isize {
|
||||
return None;
|
||||
}
|
||||
return Some((new_pos.0 as usize, new_pos.1 as usize));
|
||||
return Some((new_pos.0, new_pos.1));
|
||||
}
|
||||
fn find_start(&mut self) {
|
||||
for (y, row) in self.map.iter().enumerate() {
|
||||
for (x, tile) in row.iter().enumerate() {
|
||||
if tile.c == 'S' {
|
||||
self.start = (x, y);
|
||||
self.start = (x as isize, y as isize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("didn't find the start square!");
|
||||
}
|
||||
fn build_graph(&mut self) {
|
||||
for y in 0..self.height() {
|
||||
for x in 0..self.width() {
|
||||
for (x2, y2) in self.adjacent_to(&(x, y)) {
|
||||
self.graph.add_edge(self.at(&(x, y)).idx, self.at(&(x2, y2)).idx, ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn reachable_after(&self, from: &Position, n: usize) -> HashSet<Position> {
|
||||
fn reachable_after(&self, from: &Position, n: usize) -> u64 {
|
||||
let bar = ProgressBar::new(n as u64).with_style(
|
||||
ProgressStyle::with_template(
|
||||
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let mut visited_after: Vec<HashSet<Position>> = Vec::new();
|
||||
visited_after.push(HashSet::from([*from]));
|
||||
for i in 1..n + 1 {
|
||||
for i in 1..n+1 {
|
||||
visited_after.push(
|
||||
visited_after[i - 1]
|
||||
.iter()
|
||||
.flat_map(|last| self.adjacent_to(last))
|
||||
.collect(),
|
||||
);
|
||||
bar.inc(1);
|
||||
// if primes::is_prime(i as u64) {
|
||||
// println!("count after {} steps: {}", i, visited_after[i].len());
|
||||
// }
|
||||
}
|
||||
|
||||
visited_after[n].clone()
|
||||
visited_after[n].len() as u64
|
||||
}
|
||||
fn reachable_count_after(&self, from: &Position, n: usize) -> u64 {
|
||||
self.reachable_after(from, n).len() as u64
|
||||
let dim = self.width() as f64;
|
||||
let target_mod = (n % self.width()) as f64;
|
||||
let x_values:Vec<f64> = vec![target_mod, target_mod + dim, target_mod + 2.*dim];
|
||||
let y_values:Vec<f64> = x_values.iter().map(|n| self.reachable_after(from, *n as usize) as f64).collect();
|
||||
|
||||
let coeffs = polyfit_rs::polyfit_rs::polyfit(
|
||||
&x_values,
|
||||
&y_values,
|
||||
2,
|
||||
).unwrap();
|
||||
println!("values: x: {:?} y: {:?}", x_values, y_values);
|
||||
println!("coefficients: {:?}", coeffs);
|
||||
|
||||
let f_n= n as f64;
|
||||
let result = coeffs[0] + coeffs[1] * f_n + coeffs[2] * f_n.powf(2.0);
|
||||
|
||||
result.round() as u64
|
||||
}
|
||||
fn draw_with_bounds(&self, from: &Position, to: &Position) {
|
||||
for row in from.1..to.1 + 1 {
|
||||
for col in from.0..to.0 + 1 {
|
||||
print!("{}", self.at(&(col, row)).c);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_visited(map: &GardenMap, visited: &Vec<Vec<bool>>) {
|
||||
for (y, row) in visited.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c });
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
// fn print_visited(map: &GardenMap, visited: &Vec<Vec<bool>>) {
|
||||
// for (y, row) in visited.iter().enumerate() {
|
||||
// for (x, cell) in row.iter().enumerate() {
|
||||
// print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c });
|
||||
// }
|
||||
// println!();
|
||||
// }
|
||||
// }
|
||||
|
||||
// PROBLEM 1 solution
|
||||
|
||||
fn problem1_impl<T: BufRead>(input: Lines<T>, n: usize) -> u64 {
|
||||
let map = GardenMap::from(input);
|
||||
// println!("map: {:?} start: {:?}", map.map, &map.start);
|
||||
|
||||
// map.draw_with_bounds(
|
||||
// &(-(map.width() as isize), -(map.height() as isize)),
|
||||
// &(map.width() as isize * 2 + 1, map.height() as isize * 2 + 1),
|
||||
// );
|
||||
map.reachable_count_after(&map.start, n)
|
||||
}
|
||||
|
||||
@ -168,13 +191,14 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
|
||||
// PROBLEM 2 solution
|
||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
0
|
||||
problem1_impl(input, 26501365)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
use std::io::Cursor;
|
||||
use test_case::test_case;
|
||||
|
||||
const EXAMPLE: &str = &"...........
|
||||
.....###.#.
|
||||
@ -194,9 +218,13 @@ mod tests {
|
||||
assert_eq!(problem1_impl(c.lines(), 6), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn problem2_example() {
|
||||
#[test_case(6, 16)]
|
||||
#[test_case(10, 50)]
|
||||
#[test_case(50, 1594)]
|
||||
#[test_case(100, 6536)]
|
||||
#[test_case(500, 167004)]
|
||||
fn problem2_example(n: usize, expect: u64) {
|
||||
let c = Cursor::new(EXAMPLE);
|
||||
assert_eq!(problem2(c.lines()), 0);
|
||||
assert_eq!(problem1_impl(c.lines(), n), expect);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user