y2024d18
This commit is contained in:
		
							
								
								
									
										3450
									
								
								y2024/resources/18_input.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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
									
								
							
							
						
						
									
										27
									
								
								y2024/src/bin/d18.rs
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										294
									
								
								y2024/src/days/d18.rs
									
									
									
									
									
										Normal 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());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,3 +23,5 @@ pub mod d15;
 | 
			
		||||
pub mod d16;
 | 
			
		||||
 | 
			
		||||
pub mod d17;
 | 
			
		||||
 | 
			
		||||
pub mod d18;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user