Fix Day 21
This commit is contained in:
60
Cargo.lock
generated
60
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
13
flake.nix
13
flake.nix
@@ -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
|
||||
}
|
||||
'';
|
||||
};
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
253
src/bin/day21.rs
253
src/bin/day21.rs
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user