This commit is contained in:
Fabian Schmidt 2024-12-18 14:30:44 +01:00
parent 00ae1c3f7d
commit 6a204e5f59
4 changed files with 3773 additions and 0 deletions

3450
y2024/resources/18_input.txt Normal file

File diff suppressed because it is too large Load Diff

27
y2024/src/bin/d18.rs Normal file
View File

@ -0,0 +1,27 @@
use std::{fs, time::Instant};
use utils::time::get_elapsed_string;
use y2024::days::d18;
fn main() {
let now = Instant::now();
println!("Part 1:");
part1();
println!("Ran in {}", get_elapsed_string(now.elapsed()));
let now = Instant::now();
println!("Part 2:");
part2();
println!("Ran in {}", get_elapsed_string(now.elapsed()));
}
fn part1() {
let root = env!("CARGO_MANIFEST_DIR");
let content = fs::read_to_string(format!("{root}/resources/18_input.txt")).unwrap();
println!("{}", d18::process_part1(&content));
}
fn part2() {
let root = env!("CARGO_MANIFEST_DIR");
let content = fs::read_to_string(format!("{root}/resources/18_input.txt")).unwrap();
println!("{}", d18::process_part2(&content));
}

294
y2024/src/days/d18.rs Normal file
View File

@ -0,0 +1,294 @@
use std::{collections::HashSet, error::Error};
use itertools::Itertools;
pub fn process_part1(input: &str) -> u32 {
let falling_bytes = &input
.lines()
.map(|line| {
let (x, y) = line.split_once(",").unwrap();
(x.parse().unwrap(), y.parse().unwrap())
})
.collect_vec()[..1024];
let y_walls = vec![GridTile::Wall; 73];
let mut inner_rows = vec![GridTile::Path; 71];
inner_rows.insert(0, GridTile::Wall);
inner_rows.push(GridTile::Wall);
let mut grid = vec![inner_rows; 71];
grid.insert(0, y_walls.clone());
grid.push(y_walls);
simulate(grid, falling_bytes).unwrap()
}
fn simulate(mut grid: Vec<Vec<GridTile>>, falling_bytes: &[(usize, usize)]) -> Option<u32> {
grid[1][1] = GridTile::Start;
let height = grid.len();
let width = grid[0].len();
grid[height - 2][width - 2] = GridTile::End;
falling_bytes.iter().for_each(|(x, y)| {
grid[*y + 1][*x + 1] = GridTile::Wall;
});
let mut visited = HashSet::new();
visited.insert((1, 1));
let mut next_paths = vec![MazeRunner {
coords: (1, 1),
visited: visited.clone(),
..Default::default()
}];
let mut arrived: Vec<MazeRunner> = Vec::new();
while !next_paths.is_empty() {
next_paths = next_paths
.iter()
.map(|maze_runner| {
let mut paths = Vec::new();
if let Some(path) = maze_runner.get_next(&grid, Direction::Up) {
paths.push(path);
}
if let Some(path) = maze_runner.get_next(&grid, Direction::Down) {
paths.push(path);
}
if let Some(path) = maze_runner.get_next(&grid, Direction::Left) {
paths.push(path);
}
if let Some(path) = maze_runner.get_next(&grid, Direction::Right) {
paths.push(path);
}
paths
})
.collect_vec()
.concat();
for (idx, maze_runner) in next_paths.clone().iter().enumerate().rev() {
if maze_runner.visited.contains(&maze_runner.coords)
|| visited.contains(&maze_runner.coords)
{
next_paths.remove(idx);
continue;
}
visited.insert(maze_runner.coords);
next_paths[idx].visited.insert(maze_runner.coords);
if maze_runner.state == State::Arrived {
let arrived_reindeer = next_paths.remove(idx);
arrived.push(arrived_reindeer);
}
}
}
//let visited = arrived
// .iter()
// .map(|reindeer| {
// reindeer
// .visited
// .iter()
// .map(|(coords, _)| coords)
// .collect_vec()
// })
// .collect_vec()
// .concat();
//visited.iter().unique().count()
arrived.sort_by(|a_runner, b_runner| a_runner.visited.len().cmp(&b_runner.visited.len()));
if let Some(arrived) = arrived.first() {
log_maze(&grid, arrived);
Some(arrived.visited.len() as u32 - 1)
} else {
None
}
}
pub fn process_part2(input: &str) -> String {
let falling_bytes = &input
.lines()
.map(|line| {
let (x, y) = line.split_once(",").unwrap();
(x.parse::<usize>().unwrap(), y.parse::<usize>().unwrap())
})
.collect_vec();
let y_walls = vec![GridTile::Wall; 73];
let mut inner_rows = vec![GridTile::Path; 71];
inner_rows.insert(0, GridTile::Wall);
inner_rows.push(GridTile::Wall);
let mut grid = vec![inner_rows; 71];
grid.insert(0, y_walls.clone());
grid.push(y_walls);
let mut num_bytes = 1025;
while simulate(grid.clone(), &falling_bytes[..num_bytes]).is_some() {
num_bytes += 1;
}
let (x, y) = falling_bytes[num_bytes - 1];
format!("{x},{y}")
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Direction {
Up,
Down,
Left,
Right,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
enum State {
#[default]
Going,
Arrived,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
struct MazeRunner {
coords: (usize, usize),
visited: HashSet<(usize, usize)>,
state: State,
}
impl MazeRunner {
fn get_next(&self, grid: &[Vec<GridTile>], direction: Direction) -> Option<MazeRunner> {
let tile = match direction {
Direction::Up => grid[self.coords.1 - 1][self.coords.0],
Direction::Down => grid[self.coords.1 + 1][self.coords.0],
Direction::Right => grid[self.coords.1][self.coords.0 + 1],
Direction::Left => grid[self.coords.1][self.coords.0 - 1],
};
let coords = match direction {
Direction::Up => (self.coords.0, self.coords.1 - 1),
Direction::Down => (self.coords.0, self.coords.1 + 1),
Direction::Left => (self.coords.0 - 1, self.coords.1),
Direction::Right => (self.coords.0 + 1, self.coords.1),
};
if tile == GridTile::Wall {
None
} else if tile == GridTile::End {
Some(MazeRunner {
state: State::Arrived,
coords,
..self.clone()
})
} else {
Some(MazeRunner {
coords,
..self.clone()
})
}
}
}
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
enum GridTile {
Wall,
#[default]
Path,
Start,
End,
}
impl TryFrom<char> for GridTile {
type Error = Box<dyn Error>;
fn try_from(value: char) -> std::result::Result<GridTile, Box<dyn Error>> {
match value {
'#' => Ok(Self::Wall),
'.' => Ok(Self::Path),
'S' => Ok(Self::Start),
'E' => Ok(Self::End),
_ => Err(Box::from(format!("{value} is not a valid tile"))),
}
}
}
fn log_maze(grid: &[Vec<GridTile>], maze_runner: &MazeRunner) {
for (yidx, row) in grid.iter().enumerate() {
for (xidx, tile) in row.iter().enumerate() {
let contains = {
maze_runner.visited.contains(&(xidx, yidx))
|| maze_runner.visited.contains(&(xidx, yidx))
|| maze_runner.visited.contains(&(xidx, yidx))
|| maze_runner.visited.contains(&(xidx, yidx))
};
if contains && !(*tile == GridTile::Start || *tile == GridTile::End) {
print!("O");
} else if *tile == GridTile::Wall {
print!("#");
} else if *tile == GridTile::Path {
print!(".");
} else if *tile == GridTile::Start {
print!("S");
} else if *tile == GridTile::End {
print!("E");
}
}
println!();
}
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = "5,4
4,2
4,5
3,0
2,1
6,3
2,4
1,5
0,6
3,3
2,6
5,1
1,2
5,5
2,5
6,5
1,4
0,4
6,4
1,1
6,1
1,0
0,5
1,6
2,0";
#[test]
fn part1() {
let falling_bytes = &INPUT
.lines()
.map(|line| {
let (x, y) = line.split_once(",").unwrap();
(x.parse().unwrap(), y.parse().unwrap())
})
.collect_vec()[..12];
let y_walls = vec![GridTile::Wall; 9];
let mut inner_rows = vec![GridTile::Path; 7];
inner_rows.insert(0, GridTile::Wall);
inner_rows.push(GridTile::Wall);
let mut grid = vec![inner_rows; 7];
grid.insert(0, y_walls.clone());
grid.push(y_walls);
let result = simulate(grid, falling_bytes);
assert_eq!(result, Some(22));
}
#[test]
fn part2() {
let falling_bytes = INPUT
.lines()
.map(|line| {
let (x, y) = line.split_once(",").unwrap();
(x.parse::<usize>().unwrap(), y.parse::<usize>().unwrap())
})
.collect_vec();
let y_walls = vec![GridTile::Wall; 9];
let mut inner_rows = vec![GridTile::Path; 7];
inner_rows.insert(0, GridTile::Wall);
inner_rows.push(GridTile::Wall);
let mut grid = vec![inner_rows; 7];
grid.insert(0, y_walls.clone());
grid.push(y_walls);
let mut num_bytes = 12;
while simulate(grid.clone(), &falling_bytes[..num_bytes]).is_some() {
num_bytes += 1;
}
let (x, y) = falling_bytes[num_bytes - 1];
let result = format!("{x},{y}");
assert_eq!(result, "6,1".to_string());
}
}

View File

@ -23,3 +23,5 @@ pub mod d15;
pub mod d16;
pub mod d17;
pub mod d18;