Skip to content

Commit a134ceb

Browse files
committed
feat: Day 9 2021 ⭐ 🌟 Rust and TypeScript solution
1 parent 48e214c commit a134ceb

File tree

3 files changed

+322
-0
lines changed

3 files changed

+322
-0
lines changed

2021/day-9/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
authors = ["Joseph Chamochumbi <[email protected]>"]
3+
edition = "2018"
4+
name = "day-9"
5+
version = "0.1.0"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
[dependencies]
9+
aoc = {git = "https://github.com/icyJoseph/advent-of-code.git"}

2021/day-9/src/main.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use aoc;
2+
3+
fn solve(raw: String) -> () {
4+
let rows = raw.trim().split("\n").collect::<Vec<&str>>();
5+
6+
let height = rows.len();
7+
let width = rows[0].len();
8+
9+
let grid = rows
10+
.iter()
11+
.flat_map(|row| {
12+
row.split("")
13+
.filter(|x| !x.is_empty())
14+
.map(|c| parse_num::<usize>(c))
15+
})
16+
.collect::<Vec<usize>>();
17+
18+
let adj = calc_adj(height, width);
19+
20+
let mut basins_origins: Vec<usize> = vec![];
21+
22+
for (index, value) in grid.iter().enumerate() {
23+
if adj[index].iter().map(|&m| grid[m]).all(|m| m > *value) {
24+
basins_origins.push(index);
25+
}
26+
}
27+
28+
println!(
29+
"Part One: {}",
30+
basins_origins
31+
.iter()
32+
.map(|&origin| grid[origin] + 1)
33+
.sum::<usize>()
34+
);
35+
36+
let mut basin_sizes: Vec<usize> = vec![];
37+
38+
for origin in basins_origins.iter() {
39+
let distances = bfs(&adj, height, width, *origin, &grid);
40+
let size = distances.iter().filter(|&x| *x > 0).count() + 1;
41+
42+
basin_sizes.push(size);
43+
}
44+
45+
basin_sizes.sort_by(|a, b| b.cmp(&a));
46+
47+
println!(
48+
"Part Two: {}",
49+
basin_sizes[..3].iter().fold(1, |prev, curr| prev * curr)
50+
);
51+
}
52+
53+
fn main() {
54+
let input = aoc::get_input(2021, 9);
55+
56+
// let input = std::fs::read_to_string("").expect("Error reading input");
57+
58+
solve(input);
59+
}
60+
61+
// Utilities
62+
#[allow(dead_code)]
63+
fn normal(x: usize, y: usize, width: usize) -> usize {
64+
x + y * width
65+
}
66+
67+
#[allow(dead_code)]
68+
fn rev_normal(norm: usize, width: usize) -> (usize, usize) {
69+
(norm % width, norm / width)
70+
}
71+
72+
#[allow(dead_code)]
73+
fn parse_num<T: std::str::FromStr>(str: &str) -> T {
74+
match str.trim().parse::<T>() {
75+
Ok(n) => n,
76+
_ => panic!("Error parsing"),
77+
}
78+
}
79+
#[allow(dead_code)]
80+
fn to_int(bin: &str) -> u32 {
81+
match u32::from_str_radix(bin, 2) {
82+
Ok(n) => n,
83+
_ => panic!("Error parsing binary to integer"),
84+
}
85+
}
86+
87+
#[allow(dead_code)]
88+
fn string_vec<T: std::string::ToString>(vec: &Vec<T>, separator: &str) -> String {
89+
vec.iter()
90+
.map(|x| x.to_string())
91+
.collect::<Vec<String>>()
92+
.join(separator)
93+
}
94+
95+
type Adj = Vec<Vec<usize>>;
96+
97+
fn norm(x: usize, y: usize, width: usize) -> usize {
98+
x + y * width
99+
}
100+
101+
fn calc_adj(height: usize, width: usize) -> Adj {
102+
let mut adj = vec![vec![]; height * width];
103+
104+
for y in 0..height {
105+
for x in 0..width {
106+
let index = norm(x, y, width);
107+
if x > 0 {
108+
adj[index].push(norm(x - 1, y, width));
109+
}
110+
if y + 1 < height {
111+
adj[index].push(norm(x, y + 1, width));
112+
}
113+
if x + 1 < width {
114+
adj[index].push(norm(x + 1, y, width));
115+
}
116+
if y > 0 {
117+
adj[index].push(norm(x, y - 1, width));
118+
}
119+
}
120+
}
121+
122+
adj
123+
}
124+
125+
fn bfs(adj: &Adj, height: usize, width: usize, root: usize, nodes: &Vec<usize>) -> Vec<usize> {
126+
let mut distances = vec![0; height * width];
127+
128+
let mut visited = vec![false; height * width];
129+
130+
use std::collections::VecDeque;
131+
let mut q = VecDeque::new();
132+
133+
visited[root] = true;
134+
135+
q.push_back(root);
136+
// distance to self is zero
137+
distances[root] = 0;
138+
139+
loop {
140+
let next = q.pop_front();
141+
142+
match next {
143+
None => {
144+
break;
145+
}
146+
Some(elem) => {
147+
for &vec in adj[elem].iter() {
148+
if visited[vec] {
149+
continue;
150+
}
151+
152+
visited[vec] = true;
153+
154+
if nodes[vec] == 9usize {
155+
continue;
156+
}
157+
158+
distances[vec] = distances[elem] + 1;
159+
q.push_back(vec);
160+
}
161+
}
162+
}
163+
}
164+
165+
distances
166+
}

2021/deno/day-9.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
const input = await Deno.readTextFile("./input/day-9.in");
2+
3+
// const input = await Deno.readTextFile("./input/example.in");
4+
5+
const rows = input.split("\n");
6+
7+
const height = rows.length;
8+
const width = rows[0].length;
9+
10+
const grid = rows.map((row) => row.split("").map(Number));
11+
12+
const adj = calcAdj(width, height);
13+
14+
const origins = [];
15+
16+
for (let y = 0; y < height; y++) {
17+
for (let x = 0; x < width; x++) {
18+
const value = grid[y][x];
19+
20+
const adjacent = adj[norm(x, y, width)].map((v) => {
21+
const { x, y } = invNorm(v, width);
22+
return grid[y][x];
23+
});
24+
25+
const isLow = adjacent.every((v) => v > value);
26+
27+
if (isLow) {
28+
origins.push({ x, y });
29+
}
30+
}
31+
}
32+
33+
/**
34+
* Part One
35+
*/
36+
console.log(
37+
"Part One:",
38+
origins.map(({ x, y }) => grid[y][x] + 1).reduce((a, b) => a + b, 0)
39+
);
40+
41+
/**
42+
* Part Two
43+
*/
44+
45+
const flatGrid = flatten2dMatrix(grid);
46+
47+
const basins = [];
48+
49+
for (const { x, y } of origins) {
50+
const distances = bfs(norm(x, y, width), adj, flatGrid, width * height);
51+
const size = distances.filter((x) => x > 0).length + 1;
52+
53+
basins.push(size);
54+
}
55+
56+
basins.sort((a, b) => b - a);
57+
58+
console.log(
59+
"Part Two:",
60+
basins.slice(0, 3).reduce((a, b) => a * b, 1)
61+
);
62+
63+
function norm(x: number, y: number, width: number) {
64+
return x + y * width;
65+
}
66+
67+
function invNorm(normal: number, width: number) {
68+
return { x: normal % width, y: Math.floor(normal / width) };
69+
}
70+
71+
function createMatrix<T>(size: number, init: () => T): T[] {
72+
return Array.from({ length: size }, init);
73+
}
74+
75+
function flatten2dMatrix<T>(matrix: T[][]): T[] {
76+
const result: T[] = [];
77+
78+
for (let y = 0; y < matrix.length; y++) {
79+
const row = matrix[y];
80+
result.push(...row);
81+
}
82+
83+
return result;
84+
}
85+
86+
function calcAdj(width: number, height: number) {
87+
const adj = createMatrix<number[]>(width * height, () => []);
88+
89+
for (let y = 0; y < height; y++) {
90+
for (let x = 0; x < width; x++) {
91+
let index = norm(x, y, width);
92+
if (x > 0) {
93+
// down
94+
adj[index].push(norm(x - 1, y, width));
95+
}
96+
if (y + 1 < height) {
97+
// right
98+
adj[index].push(norm(x, y + 1, width));
99+
}
100+
if (x + 1 < width) {
101+
// left
102+
adj[index].push(norm(x + 1, y, width));
103+
}
104+
if (y > 0) {
105+
// up
106+
adj[index].push(norm(x, y - 1, width));
107+
}
108+
}
109+
}
110+
111+
return adj;
112+
}
113+
114+
export function bfs(
115+
start: number,
116+
adj: number[][],
117+
grid: number[],
118+
size: number
119+
) {
120+
const q: number[] = [];
121+
const visited = createMatrix(size, () => false);
122+
const distance = createMatrix(size, () => 0);
123+
124+
visited[start] = true;
125+
distance[start] = 0;
126+
q.push(start);
127+
128+
while (true) {
129+
const current = q.shift();
130+
131+
if (current == null) break;
132+
133+
for (const vec of adj[current]) {
134+
if (visited[vec]) continue;
135+
136+
visited[vec] = true;
137+
138+
if (grid[vec] === 9) continue;
139+
140+
distance[vec] = distance[current] + 1;
141+
142+
q.push(vec);
143+
}
144+
}
145+
146+
return distance;
147+
}

0 commit comments

Comments
 (0)