-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Day 9 2021 ⭐ 🌟 Rust and TypeScript solution
- Loading branch information
Showing
3 changed files
with
322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
authors = ["Joseph Chamochumbi <[email protected]>"] | ||
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"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
use aoc; | ||
|
||
fn solve(raw: String) -> () { | ||
let rows = raw.trim().split("\n").collect::<Vec<&str>>(); | ||
|
||
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::<usize>(c)) | ||
}) | ||
.collect::<Vec<usize>>(); | ||
|
||
let adj = calc_adj(height, width); | ||
|
||
let mut basins_origins: Vec<usize> = 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::<usize>() | ||
); | ||
|
||
let mut basin_sizes: Vec<usize> = 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<T: std::str::FromStr>(str: &str) -> T { | ||
match str.trim().parse::<T>() { | ||
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<T: std::string::ToString>(vec: &Vec<T>, separator: &str) -> String { | ||
vec.iter() | ||
.map(|x| x.to_string()) | ||
.collect::<Vec<String>>() | ||
.join(separator) | ||
} | ||
|
||
type Adj = Vec<Vec<usize>>; | ||
|
||
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<usize>) -> Vec<usize> { | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<T>(size: number, init: () => T): T[] { | ||
return Array.from({ length: size }, init); | ||
} | ||
|
||
function flatten2dMatrix<T>(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<number[]>(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; | ||
} |