192 lines
4.9 KiB
Rust
192 lines
4.9 KiB
Rust
use std::fs::read_to_string;
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
struct Map {
|
|
min_x: usize,
|
|
width: usize,
|
|
height: usize,
|
|
map: Vec<bool>,
|
|
}
|
|
|
|
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::<usize>().unwrap()).collect::<Vec<_>>()[..] else {panic!()};
|
|
(x, y)
|
|
}).collect::<Vec<_>>();
|
|
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");
|
|
}
|