day14: rust

This commit is contained in:
matthias@arch 2023-12-14 17:34:40 +01:00
parent 5dfbe7c237
commit 3d5270034b
3 changed files with 130 additions and 0 deletions

22
14/README.md Normal file
View File

@ -0,0 +1,22 @@
# [Day 14](https://adventofcode.com/2023/day/14)
:gift::gift::gift::gift::gift::gift::gift::gift::gift::gift::gift::gift::gift::gift:
Today's language: **Rust**
Lines of code: **84**
Execution time: **0,171 s**
This solution uses a single function for the tilting procedure.
Different tilting directions are achieved by "remapping" `x` and `y` through captures.
For task 2, a cycle is searched by comparing the rocks after each nwse-tilt with all previous results.
After finding two equal states, only `(1.000.000.000 - cycle_start) % cycle_length` cycles have to be performed to get the correct result.
This approach requires making copies of the 2D array in a loop, which involves expensive heap allocations.
It is still pretty fast.
```shell
rustc day14.rs
./day14
```
<!-- no bruteforce in < 90 lines -->

107
14/day14.rs Normal file
View File

@ -0,0 +1,107 @@
use std::io::{self, BufRead};
type Rocks = Vec<Vec<u8>>;
// tilt so that all 'O' characters want to DECREMENT their Y index
// using custom `at` function that "remaps" x and y, this function can be used for all directions
fn tilt<F>(rocks: &mut Rocks, at: F, x_len: usize, y_len: usize)
where F: Fn(&mut Rocks, usize, usize) -> &mut u8 {
for x in 0..x_len {
let mut next_y: usize = 0; // lowest reachable position
for y in 0..y_len {
match *at(rocks, x, y) as char {
'#' => next_y = y + 1,
'O' => {
if next_y != y {
*at(rocks, x, next_y) = 'O' as u8;
*at(rocks, x, y) = '.' as u8;
}
while next_y < y_len - 1 {
next_y += 1;
if *at(rocks, x, next_y) != '#' as u8 { break }
}
},
_ => {},
}
}
}
}
// --> y
// |
// V
// x
// West is the orientation that looks like the input
// When printing with one of these, the rocks want to roll to the left side
fn west_at(rocks: &mut Rocks, x: usize, y: usize) -> &mut u8 { &mut rocks[x][y] }
fn east_at(rocks: &mut Rocks, x: usize, y: usize) -> &mut u8 { let ylen = rocks[0].len(); &mut rocks[x][ylen - y - 1] }
fn south_at (rocks: &mut Rocks, x: usize, y: usize) -> &mut u8 { let xlen = rocks.len(); let ylen = rocks[0].len(); &mut rocks[xlen - y - 1][ylen - x - 1] }
fn north_at (rocks: &mut Rocks, x: usize, y: usize) -> &mut u8 { let ylen = rocks[0].len(); &mut rocks[y][ylen - x - 1] }
fn print<F>(rocks: &mut Rocks, at: F, x_len: usize, y_len: usize)
where F: Fn(&mut Rocks, usize, usize) -> &mut u8 {
(0..x_len).for_each(|x| { (0..y_len).for_each(|y| print!("{}", *at(rocks, x, y) as char)); println!() });
}
fn weigh<F>(rocks: &mut Rocks, at: F, x_len: usize, y_len: usize) -> usize
where F: Fn(&mut Rocks, usize, usize) -> &mut u8 {
let mut weight: usize = 0;
(0..x_len).for_each(|x| (0..y_len).for_each(|y| if *at(rocks, x, y) == 'O' as u8 { weight += y_len - y }) );
return weight;
}
fn main() {
// let input = "example.txt";
let input = "input.txt";
let lines = read_lines(&input);
let mut original: Rocks = Vec::new();
for line in lines.map(|r| r.ok().unwrap()) {
original.push(line.chars().map(|c| c as u8).collect());
}
let ew_xlen = original.len();
let ew_ylen = original[0].len();
let ns_xlen = ew_ylen;
let ns_ylen = ew_xlen;
// TASK 1
let mut rocks = original.clone();
tilt(&mut rocks, north_at, ns_xlen, ns_ylen);
println!("Load on north support beams (1): {}", weigh(&mut rocks, north_at, ns_xlen, ns_ylen));
// print(&mut rocks, west_at, ew_xlen, ew_ylen);
// TASK 2
rocks = original.clone();
// find a cycle
let mut rocks_store: Vec<Rocks> = Vec::new();
let mut cycle_start: usize = 0;
let mut cycle_length: usize = 0;
for i in 0..1_000_000_000 as usize {
tilt(&mut rocks, north_at, ns_xlen, ns_ylen);
tilt(&mut rocks, west_at, ew_xlen, ew_ylen);
tilt(&mut rocks, south_at, ns_xlen, ns_ylen);
tilt(&mut rocks, east_at, ew_xlen, ew_ylen);
if let Some(j) = rocks_store.iter().position(|r| r.eq(&rocks)) {
cycle_start = j;
cycle_length = i - j;
println!("Cycle found! rocks at {} == rocks at {}", i, j);
break;
}
rocks_store.push(rocks.clone());
}
// do the cycles that remain after the ... Cycle
for _ in 0..(1_000_000_000 as usize - cycle_start) % cycle_length - 1 {
tilt(&mut rocks, north_at, ns_xlen, ns_ylen);
tilt(&mut rocks, west_at, ew_xlen, ew_ylen);
tilt(&mut rocks, south_at, ns_xlen, ns_ylen);
tilt(&mut rocks, east_at, ew_xlen, ew_ylen);
}
// tilt(&mut rocks, north_at, ns_xlen, ns_ylen);
println!("Load on north support beams (2): {}", weigh(&mut rocks, north_at, ns_xlen, ns_ylen));
}
fn read_lines<P>(filename: P) -> io::Lines<io::BufReader<std::fs::File>>
where P: AsRef<std::path::Path>, {
return match std::fs::File::open(filename) {
Err(why) => panic!("Could not open file. {}", why),
Ok(file) => std::io::BufReader::new(file).lines()
};
}

View File

@ -16,5 +16,6 @@ This is a repository for my solutions for the [advent of code 2023](https://adve
| [10](10) | Rust | 136 | 0,003 s | lots of bitwise operations |
| [11](11) | Rust | 66 | 0,005 s | |
| [13](13) | Rust | 84 | 0,002 s | binary encoding ftw |
| [14](14) | Rust | 84 | 0,171 s | no bruteforce in < 90 lines |
Lines of code are without blank lines and comments