diff --git a/2021/day-9/Cargo.toml b/2021/day-9/Cargo.toml new file mode 100644 index 0000000..c59d666 --- /dev/null +++ b/2021/day-9/Cargo.toml @@ -0,0 +1,9 @@ +[package] +authors = ["Joseph Chamochumbi "] +edition = "2018" +name = "day-9" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +aoc = {git = "https://github.com/icyJoseph/advent-of-code.git"} diff --git a/2021/day-9/src/main.rs b/2021/day-9/src/main.rs new file mode 100644 index 0000000..6a70c69 --- /dev/null +++ b/2021/day-9/src/main.rs @@ -0,0 +1,166 @@ +use aoc; + +fn solve(raw: String) -> () { + let rows = raw.trim().split("\n").collect::>(); + + let height = rows.len(); + let width = rows[0].len(); + + let grid = rows + .iter() + .flat_map(|row| { + row.split("") + .filter(|x| !x.is_empty()) + .map(|c| parse_num::(c)) + }) + .collect::>(); + + let adj = calc_adj(height, width); + + let mut basins_origins: Vec = vec![]; + + for (index, value) in grid.iter().enumerate() { + if adj[index].iter().map(|&m| grid[m]).all(|m| m > *value) { + basins_origins.push(index); + } + } + + println!( + "Part One: {}", + basins_origins + .iter() + .map(|&origin| grid[origin] + 1) + .sum::() + ); + + let mut basin_sizes: Vec = vec![]; + + for origin in basins_origins.iter() { + let distances = bfs(&adj, height, width, *origin, &grid); + let size = distances.iter().filter(|&x| *x > 0).count() + 1; + + basin_sizes.push(size); + } + + basin_sizes.sort_by(|a, b| b.cmp(&a)); + + println!( + "Part Two: {}", + basin_sizes[..3].iter().fold(1, |prev, curr| prev * curr) + ); +} + +fn main() { + let input = aoc::get_input(2021, 9); + + // let input = std::fs::read_to_string("").expect("Error reading input"); + + solve(input); +} + +// Utilities +#[allow(dead_code)] +fn normal(x: usize, y: usize, width: usize) -> usize { + x + y * width +} + +#[allow(dead_code)] +fn rev_normal(norm: usize, width: usize) -> (usize, usize) { + (norm % width, norm / width) +} + +#[allow(dead_code)] +fn parse_num(str: &str) -> T { + match str.trim().parse::() { + Ok(n) => n, + _ => panic!("Error parsing"), + } +} +#[allow(dead_code)] +fn to_int(bin: &str) -> u32 { + match u32::from_str_radix(bin, 2) { + Ok(n) => n, + _ => panic!("Error parsing binary to integer"), + } +} + +#[allow(dead_code)] +fn string_vec(vec: &Vec, separator: &str) -> String { + vec.iter() + .map(|x| x.to_string()) + .collect::>() + .join(separator) +} + +type Adj = Vec>; + +fn norm(x: usize, y: usize, width: usize) -> usize { + x + y * width +} + +fn calc_adj(height: usize, width: usize) -> Adj { + let mut adj = vec![vec![]; height * width]; + + for y in 0..height { + for x in 0..width { + let index = norm(x, y, width); + if x > 0 { + adj[index].push(norm(x - 1, y, width)); + } + if y + 1 < height { + adj[index].push(norm(x, y + 1, width)); + } + if x + 1 < width { + adj[index].push(norm(x + 1, y, width)); + } + if y > 0 { + adj[index].push(norm(x, y - 1, width)); + } + } + } + + adj +} + +fn bfs(adj: &Adj, height: usize, width: usize, root: usize, nodes: &Vec) -> Vec { + let mut distances = vec![0; height * width]; + + let mut visited = vec![false; height * width]; + + use std::collections::VecDeque; + let mut q = VecDeque::new(); + + visited[root] = true; + + q.push_back(root); + // distance to self is zero + distances[root] = 0; + + loop { + let next = q.pop_front(); + + match next { + None => { + break; + } + Some(elem) => { + for &vec in adj[elem].iter() { + if visited[vec] { + continue; + } + + visited[vec] = true; + + if nodes[vec] == 9usize { + continue; + } + + distances[vec] = distances[elem] + 1; + q.push_back(vec); + } + } + } + } + + distances +} diff --git a/2021/deno/day-9.ts b/2021/deno/day-9.ts new file mode 100644 index 0000000..d7b19fc --- /dev/null +++ b/2021/deno/day-9.ts @@ -0,0 +1,147 @@ +const input = await Deno.readTextFile("./input/day-9.in"); + +// const input = await Deno.readTextFile("./input/example.in"); + +const rows = input.split("\n"); + +const height = rows.length; +const width = rows[0].length; + +const grid = rows.map((row) => row.split("").map(Number)); + +const adj = calcAdj(width, height); + +const origins = []; + +for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const value = grid[y][x]; + + const adjacent = adj[norm(x, y, width)].map((v) => { + const { x, y } = invNorm(v, width); + return grid[y][x]; + }); + + const isLow = adjacent.every((v) => v > value); + + if (isLow) { + origins.push({ x, y }); + } + } +} + +/** + * Part One + */ +console.log( + "Part One:", + origins.map(({ x, y }) => grid[y][x] + 1).reduce((a, b) => a + b, 0) +); + +/** + * Part Two + */ + +const flatGrid = flatten2dMatrix(grid); + +const basins = []; + +for (const { x, y } of origins) { + const distances = bfs(norm(x, y, width), adj, flatGrid, width * height); + const size = distances.filter((x) => x > 0).length + 1; + + basins.push(size); +} + +basins.sort((a, b) => b - a); + +console.log( + "Part Two:", + basins.slice(0, 3).reduce((a, b) => a * b, 1) +); + +function norm(x: number, y: number, width: number) { + return x + y * width; +} + +function invNorm(normal: number, width: number) { + return { x: normal % width, y: Math.floor(normal / width) }; +} + +function createMatrix(size: number, init: () => T): T[] { + return Array.from({ length: size }, init); +} + +function flatten2dMatrix(matrix: T[][]): T[] { + const result: T[] = []; + + for (let y = 0; y < matrix.length; y++) { + const row = matrix[y]; + result.push(...row); + } + + return result; +} + +function calcAdj(width: number, height: number) { + const adj = createMatrix(width * height, () => []); + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + let index = norm(x, y, width); + if (x > 0) { + // down + adj[index].push(norm(x - 1, y, width)); + } + if (y + 1 < height) { + // right + adj[index].push(norm(x, y + 1, width)); + } + if (x + 1 < width) { + // left + adj[index].push(norm(x + 1, y, width)); + } + if (y > 0) { + // up + adj[index].push(norm(x, y - 1, width)); + } + } + } + + return adj; +} + +export function bfs( + start: number, + adj: number[][], + grid: number[], + size: number +) { + const q: number[] = []; + const visited = createMatrix(size, () => false); + const distance = createMatrix(size, () => 0); + + visited[start] = true; + distance[start] = 0; + q.push(start); + + while (true) { + const current = q.shift(); + + if (current == null) break; + + for (const vec of adj[current]) { + if (visited[vec]) continue; + + visited[vec] = true; + + if (grid[vec] === 9) continue; + + distance[vec] = distance[current] + 1; + + q.push(vec); + } + } + + return distance; +}