use std::fs::read_to_string; #[derive(Debug, PartialEq, Eq, Clone)] struct Map { min_x: usize, width: usize, height: usize, map: Vec, } impl Map { fn new() -> Self { Self { min_x: 500, width: 0, height: 0, map: vec![], } } fn print(&self) { for y in 0..self.height { for x in 0..self.width { print!( "{}", if self.map[x + y * self.width] { '#' } else { '.' } ); } println!(); } } fn at(&self, position: (usize, usize)) -> bool { if !self.is_inside(position) { return false; } let x = position.0 - self.min_x; self.map[x + position.1 * self.width] } fn is_inside(&self, position: (usize, usize)) -> bool { position.0 >= self.min_x && position.0 < self.min_x + self.width && position.1 < self.height } fn add_obstacle(&mut self, position: (usize, usize)) { while self.min_x > position.0 { self.extend_left(); } while self.min_x + self.width <= position.0 { self.extend_right(); } while self.height <= position.1 { self.extend_down(); } let x = position.0 - self.min_x; self.map[x + position.1 * self.width] = true; } fn extend_down(&mut self) { self.map.append(&mut vec![false; self.width]); self.height += 1; } fn extend_left(&mut self) { self.map.append(&mut vec![false; self.height]); for y in (0..self.height).rev() { for x in (0..self.width).rev() { self.map[(x + 1) + y * (self.width + 1)] = self.map[x + y * self.width]; } } self.width += 1; self.min_x -= 1; for y in 0..self.height { self.map[y * self.width] = false; } } fn extend_right(&mut self) { self.map.append(&mut vec![false; self.height]); for y in (0..self.height).rev() { for x in (0..self.width).rev() { self.map[x + y * (self.width + 1)] = self.map[x + y * self.width]; } } self.width += 1; for y in 0..self.height { self.map[self.width - 1 + y * self.width] = false; } } } fn add_sand(map: &Map) -> Option<(usize, usize)> { let mut sand = (500, 0); while map.is_inside(sand) { let x = sand.0; let y = sand.1; if !map.at((x, y + 1)) { sand.1 += 1; continue; } if !map.at((x - 1, y + 1)) { sand.1 += 1; sand.0 -= 1; continue; } if !map.at((x + 1, y + 1)) { sand.1 += 1; sand.0 += 1; continue; } return Some(sand); } None } fn add_sand_floor(map: &Map, floor: usize) -> Option<(usize, usize)> { let mut sand = (500, 0); while !map.at((500, 0)) { let x = sand.0; let y = sand.1; if y + 1 == floor { return Some(sand); } if !map.at((x, y + 1)) { sand.1 += 1; continue; } if !map.at((x - 1, y + 1)) { sand.1 += 1; sand.0 -= 1; continue; } if !map.at((x + 1, y + 1)) { sand.1 += 1; sand.0 += 1; continue; } return Some(sand); } None } fn main() { let input = "498,4 -> 498,6 -> 496,6 503,4 -> 502,4 -> 502,9 -> 494,9" .to_owned(); let input = read_to_string("input.txt").unwrap_or(input); let mut map = Map::new(); for line in input.lines() { let points = line.split(" -> ").map(|coord| { let [x, y] = coord.split(',').map(|n| n.parse::().unwrap()).collect::>()[..] else {panic!()}; (x, y) }).collect::>(); for line in points.windows(2) { let (x1, y1) = line[0]; let (x2, y2) = line[1]; let x_min = x1.min(x2); let x_max = x1.max(x2); let y_min = y1.min(y2); let y_max = y1.max(y2); for x in x_min..=x_max { for y in y_min..=y_max { map.add_obstacle((x, y)); } } } } println!("printing map:"); map.print(); let mut num_sand = 0; while let Some(sand) = add_sand(&map) { map.add_obstacle(sand); num_sand += 1; } println!(); map.print(); println!("needed {num_sand} sand particles"); let floor = map.height + 1; println!("floor: {floor}"); while let Some(sand) = add_sand_floor(&map, floor) { map.add_obstacle(sand); num_sand += 1; } println!(); map.print(); println!("needed {num_sand} sand particles"); }