1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
const AIR: char = '.';
const ROCK: char = '#';
//const SANDSPAWN: char = '+';
//const MOVING_SAND: char = '*';
const STUCK_SAND: char = 'o';
pub fn run(input: String) {
// line segments
let ls: Vec<Vec<(i32,i32)>> = input.lines().map(|line| {
let cornersstr = line.split("->");
cornersstr.map(|cornerstr| {
let (xs,ys) = cornerstr.split_once(",").expect("coordinate didn't have a , in it!");
(xs.trim().parse::<i32>().expect("xs wasn't a i32!"), ys.trim().parse::<i32>().expect("ys wasn't a i32!"))
}).collect()
}).collect();
// Find our grid bounds,no need to have a 590 cell wide grid... or maybe you just should? idk.
// yeah you'll need a buffer to the left for the sand to fall off of, otherwise it hits the side and that's no good.
// let ((max_x, max_y),(min_x,min_y)) = ls.iter().flatten().fold(((0,0),(usize::MAX,usize::MAX)), |((max_x,max_y),(min_x,min_y)),(x,y)| {
// ((x.max(*max_x), y.max(max_y)),
// (x.min(min_x), y.min(min_y)))
// });
let (max_x, max_y) = ls.iter().flatten().fold((0 as i32,0 as i32), |(max_x,max_y),(x,y)| {
(*x.max(&max_x),*y.max(&max_y))
});
// +2, give a row/col of buffer
// Actually x2, in case we have a pyramid of sand...
let mut grid: Vec<Vec<char>> = (0..=(max_y+2)).map(|_| {
//(0..(max_x+2)).map(|_| {
(0..(max_x*2+2)).map(|_| {
AIR
}).collect()
}).collect();
// pt2; add the floor
let floor: usize = (max_y+2).try_into().unwrap();
for i in 0..(max_x*2+2) {
grid[floor][i as usize] = '#';
}
for l in ls {
for cs in l[..].windows(2) {
if cs.len() != 2 { panic!("window function failed???") }
let (c1, c2) = (cs[0], cs[1]);
let xd = c1.0 - c2.0;
let yd = c1.1 - c2.1;
if xd != 0 && yd != 0 { panic!("diagonal line segment???") }
if xd != 0 {
let y = c1.1 as usize;
for x in (c1.0.min(c2.0))..=(c1.0.max(c2.0)) {
grid[y][x as usize] = ROCK;
}
} else if yd != 0 {
let x = c1.0 as usize;
for y in (c1.1.min(c2.1))..=(c1.1.max(c2.1)) {
grid[y as usize][x] = ROCK;
}
} else {
panic!("0 length line segment???")
}
}
}
let sandspawn = (500,0);
let mut sand = sandspawn;
let mut ix = 0;
'lp: loop {
if let Some(np) = mov(&grid,&sand) {
// if np.1 >= max_y.try_into().expect("max_y couldn't fit a usize???") {
// println!("sand reached the end!");
// break 'lp;
// }
sand = np;
} else {
// Convert the sand to a stuck sand
grid[sand.1][sand.0] = STUCK_SAND;
if sand == sandspawn {
// If we couldn't move, and we're still on the spawn, then we're done!
break 'lp;
} else {
// Otherwise, spawn a new sand
sand = sandspawn;
}
}
ix += 1;
if ix > 1_000_000_000 {
eprint!("not getting out of here!");
break 'lp;
}
}
// Count the stuck sand
//print(&grid, &sandspawn, &sand);
let c_stuck_sand = grid.into_iter().flatten().filter(|ch| {*ch == STUCK_SAND}).count();
println!("Day 14: {}", c_stuck_sand);
}
fn mov(grid: &Vec<Vec<char>>, sand: &(usize,usize)) -> Option<(usize,usize)> {
if grid[sand.1+1][sand.0] == AIR {
Some((sand.0,sand.1+1))
} else if grid[sand.1+1][sand.0-1] == AIR {
Some((sand.0-1,sand.1+1))
} else if grid[sand.1+1][sand.0+1] == AIR {
Some((sand.0+1,sand.1+1))
} else {
None
}
}
// fn print(grid: &Vec<Vec<char>>, sandspawn: &(usize,usize), sand: &(usize,usize)) {
// for (y, line) in grid.into_iter().enumerate() {
// for (x, ch) in line.into_iter().enumerate().skip(480) {
// let pt = (x,y);
// if sand == &pt {
// print!("{}", MOVING_SAND);
// } else if sandspawn == &pt {
// print!("{}", SANDSPAWN);
// } else {
// print!("{}", ch);
// }
// }
// print!("\n");
// }
// }
|