Fix Day 21

This commit is contained in:
Christian
2023-12-28 13:47:30 +01:00
parent ebaba04774
commit af8413cba8
4 changed files with 214 additions and 113 deletions

60
Cargo.lock generated
View File

@@ -45,6 +45,7 @@ dependencies = [
"itertools",
"ndarray",
"num",
"rand",
"regex",
]
@@ -72,6 +73,17 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "getrandom"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
@@ -91,6 +103,12 @@ dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "matrixmultiply"
version = "0.3.8"
@@ -202,6 +220,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.70"
@@ -220,6 +244,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
@@ -278,6 +332,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "zerocopy"
version = "0.7.31"

View File

@@ -12,4 +12,5 @@ hashbrown = "0.14.3"
itertools = "0.12.0"
ndarray = "0.15.6"
num = "0.4.1"
rand = "0.8.5"
regex = "1.10.2"

View File

@@ -19,11 +19,18 @@
devShells = forAllSystems (pkgs: with pkgs; {
default = mkShell {
nativeBuildInputs = [ rustc cargo ];
buildInputs = [ hyperfine ];
nativeBuildInputs = [ rustc cargo clippy ];
buildInputs = [ hyperfine glow libiconv ];
RUST_SRC_PATH = "${rustPlatform.rustLibSrc}";
shellHook = ''
bench() {
cargo build --release
DAY=$((($(date +%s)-$(date +%s --date "2023-11-30"))/(3600*24)))
hyperfine --parameter-list day $(seq --format "%02.f" 1 $DAY | paste -sd ',' -) "./target/release/day{day} ./data/{day}.in" --runs 20 --warmup 10 --shell=none --export-markdown ./target/bench-output.md 2> /dev/null
glow ./target/bench-output.md
}
'';
};
});
};
}

View File

@@ -1,11 +1,88 @@
use anyhow::{Context, Result};
fn bfs(grid: &Vec<u8>, height: usize, width: usize, start: (i64, i64)) -> Vec<u64> {
let mut distances = vec![u64::MAX; grid.len()];
distances[start.0 as usize * width + start.1 as usize] = 0;
const STEPS_P1: u64 = 64;
const STEPS_P2: usize = 202300 * 131 + 65;
let height = height as i64;
let width = width as i64;
struct Plot {
start: (usize, usize),
height: usize,
width: usize,
scale: usize,
grid: Vec<u8>,
}
impl Plot {
#[rustfmt::skip]
fn new(grid: Vec<u8>, width: usize) -> Self {
let height = grid.len() / width;
let mut start = (height / 2, width / 2);
for x in 0..height {
for y in 0..width {
if grid[x * width + y] == b'S' {
start = (x, y);
}
}
}
Self { start, height, width, scale: 0, grid }
}
fn scale_up(&mut self) {
self.scale_to(self.scale + 1);
}
fn scale_to(&mut self, new_scale: usize) {
let old_factor = 2 * self.scale + 1;
let factor = 2 * new_scale + 1;
let height = self.height;
let width = self.width;
self.grid.resize(factor * factor * height * width, 0);
for x in (0..height).rev() {
for y in 0..width {
self.grid[x * width * factor + y] = self.grid[x * old_factor * width + y];
}
}
for xf in 0..factor {
for x in 0..height {
let row = x * width * factor + xf * factor * height * width;
for yf in 0..factor {
let col_start = row + yf * width;
for y in 0..width {
self.grid[col_start + y] = self.grid[x * width * factor + y];
}
}
}
}
self.scale = new_scale;
}
fn scale_height(&self) -> usize {
(2 * self.scale + 1) * self.height
}
fn scale_width(&self) -> usize {
(2 * self.scale + 1) * self.width
}
fn scale_start(&self) -> (usize, usize) {
(
self.start.0 + self.scale * self.height,
self.start.1 + self.scale * self.width,
)
}
}
fn bfs(plot: &Plot) -> Vec<u64> {
let mut distances = vec![u64::MAX; plot.grid.len()];
let start = plot.scale_start();
distances[start.0 * plot.scale_width() + start.1] = 0;
let height = plot.scale_height() as i32;
let width = plot.scale_width() as i32;
let start = (start.0 as i32, start.1 as i32);
let mut todo = Vec::from([start]);
let mut next = Vec::new();
@@ -16,7 +93,7 @@ fn bfs(grid: &Vec<u8>, height: usize, width: usize, start: (i64, i64)) -> Vec<u6
let (nx, ny) = (x + dx, y + dy);
if 0 <= nx && nx < height && 0 <= ny && ny < width {
let index = (nx * width + ny) as usize;
if grid[index] != b'#' && distance + 1 < distances[index] {
if plot.grid[index] != b'#' && distance + 1 < distances[index] {
distances[index] = distance + 1;
next.push((nx, ny));
}
@@ -32,20 +109,24 @@ fn bfs(grid: &Vec<u8>, height: usize, width: usize, start: (i64, i64)) -> Vec<u6
return distances;
}
fn repeat_grid(factor: usize, grid: &Vec<u8>, height: usize, width: usize) -> Vec<u8> {
let mut result = vec![0; height * width * factor * factor];
fn interpolate(x0: usize, mut ys: [i64; 4], x: usize) -> Option<i64> {
assert!(x >= x0);
if x <= x0 + 3 {
Some(ys[(x - x0) as usize])
} else {
let mut f = [0; 4];
for xf in 0..factor {
for yf in 0..factor {
let base = xf * height * width * factor + yf * width;
for x in 0..height {
for y in 0..width {
result[base + x * width * factor + y] = grid[x * width + y];
}
for i in 0..4 {
f[i] = ys[0];
for i in 0..ys.len() - 1 {
ys[i] = ys[i + 1] - ys[i];
}
}
let xa = (x - x0) as i64;
let xb = xa * (x - x0 - 1) as i64;
(f[3] == 0).then_some(f[0] + f[1] * xa as i64 + f[2] * xb / 2)
}
return result;
}
fn main() -> Result<()> {
@@ -55,103 +136,55 @@ fn main() -> Result<()> {
let input = std::fs::read_to_string(filename)?;
let width = input.lines().next().unwrap().len();
let grid: Vec<_> = input.lines().flat_map(|line| line.bytes()).collect();
let height = grid.len() / width;
let mut start1 = (0, 0);
for x in 0..height {
for y in 0..width {
if grid[x * width + y] == b'S' {
start1 = (x as i64, y as i64);
let mut plot = Plot::new(grid, width);
let positions = |distances: &Vec<u64>, step_count: u64| -> u64 {
let mut positions = 0;
for distance in distances.iter().copied() {
positions += ((distance <= step_count) & (distance % 2 == step_count % 2)) as u64
}
positions
};
let distances = bfs(&plot);
let part1 = positions(&distances, STEPS_P1);
assert!(plot.height == plot.width);
let side_length = width;
let required_scale =
|step_count| (step_count - plot.start.0 as usize + side_length) / side_length;
let offset = STEPS_P2 % side_length;
let max_scale = required_scale(STEPS_P2);
let part2 = if max_scale < 4 {
plot.scale_to(max_scale);
let distances = bfs(&plot);
positions(&distances, STEPS_P2 as u64)
} else {
let scale0 = required_scale(offset);
let mut sequence = [0; 4];
plot.scale_to(scale0 + 3);
let distances = bfs(&plot);
for i in 0..4 {
sequence[i] = positions(&distances, (offset + i * side_length) as u64) as i64;
}
let mut n = 0;
loop {
if let Some(v) = interpolate(n, sequence, max_scale - scale0) {
break v as u64;
}
plot.scale_up();
let distances = bfs(&plot);
sequence.rotate_left(1);
n += 1;
sequence[3] = positions(&distances, (offset + (n + 3) * side_length) as u64) as i64;
}
}
let distances = bfs(&grid, height, width, start1);
let steps_p1 = 64;
let mut part1 = 0;
for x in 0..height {
for y in 0..width {
let distance = distances[x * width + y];
if distance <= steps_p1 && distance % 2 == steps_p1 % 2 {
part1 += 1;
}
}
}
let center = (height as i64 / 2, width as i64 / 2);
let scale = 2usize;
let repeats = scale * 2 + 1;
let scale_center = (
center.0 + (scale * height) as i64,
center.1 + (scale * width) as i64,
);
let scale_grid = repeat_grid(repeats, &grid, height, width);
let distances = bfs(&scale_grid, height * repeats, width * repeats, scale_center);
let mut counts = vec![vec![0; repeats]; repeats];
let limit = (scale * height + height / 2) as u64;
for qx in 0..repeats {
for qy in 0..repeats {
let base = qx * height * width * repeats + qy * width;
for x in 0..height {
for y in 0..width {
let distance = distances[base + x * width * repeats + y];
if distance <= limit as u64 && limit as u64 % 2 == distance % 2 {
counts[qx][qy] += 1;
}
}
}
}
}
let part_2_steps = 26501365;
let part_2_scale: u64 = ((part_2_steps - height / 2) / height) as u64;
let mut part2 = 0;
for x in 0..repeats {
for y in 0..repeats {
print!("{: >6} ", counts[x][y]);
}
println!();
}
// ..*#*..
// .*'o'*.
// *'o+o'*
// #o+o+o#
// *'o+o'*
// .*'o'*.
// ..*#*..
// o Even centers
let inner_even_count = ((part_2_scale - 1) | 0x1) * ((part_2_scale - 1) | 0x1);
part2 += inner_even_count * counts[scale][scale];
let inner_odd_count = (part_2_scale & (!0x1)) * (part_2_scale & (!0x1));
// + Odd centers
part2 += inner_odd_count * counts[scale][scale + 1];
// # Pointy ends
part2 += counts[0][scale];
part2 += counts[repeats - 1][scale];
part2 += counts[scale][0];
part2 += counts[scale][repeats - 1];
// * Outer edges
part2 += part_2_scale * counts[0][scale - 1];
part2 += part_2_scale * counts[0][scale + 1];
part2 += part_2_scale * counts[repeats - 1][scale - 1];
part2 += part_2_scale * counts[repeats - 1][scale + 1];
// ' Inner edges
part2 += (part_2_scale - 1) * counts[1][scale - 1];
part2 += (part_2_scale - 1) * counts[1][scale + 1];
part2 += (part_2_scale - 1) * counts[repeats - 2][scale - 1];
part2 += (part_2_scale - 1) * counts[repeats - 2][scale + 1];
};
println!("1) {}", part1);
println!("2) {}", part2);