Skip to content

Commit

Permalink
feat: Day 9 2021 ⭐ 🌟 Rust and TypeScript solution
Browse files Browse the repository at this point in the history
  • Loading branch information
icyJoseph committed Dec 9, 2021
1 parent 48e214c commit a134ceb
Show file tree
Hide file tree
Showing 3 changed files with 322 additions and 0 deletions.
9 changes: 9 additions & 0 deletions 2021/day-9/Cargo.toml
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"}
166 changes: 166 additions & 0 deletions 2021/day-9/src/main.rs
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
}
147 changes: 147 additions & 0 deletions 2021/deno/day-9.ts
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;
}

0 comments on commit a134ceb

Please sign in to comment.