Skip to content

Commit

Permalink
feat: Day 5 2021, ⭐ ⭐
Browse files Browse the repository at this point in the history
  • Loading branch information
icyJoseph committed Dec 5, 2021
1 parent d530d11 commit 4186330
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 0 deletions.
9 changes: 9 additions & 0 deletions 2021/day-5/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-5"
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"}
135 changes: 135 additions & 0 deletions 2021/day-5/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use aoc;
use std::cmp::{max, min};
use std::collections::HashMap;

type Point = (usize, usize);

fn normal(x: usize, y: usize, width: usize) -> usize {
x + y * width
}

fn expand((x1, y1): Point, (x2, y2): Point, skip_diagonals: bool) -> Vec<Point> {
let min_x = min(x1, x2);
let max_x = max(x1, x2);

let min_y = min(y1, y2);
let max_y = max(y1, y2);

if x1 == x2 {
return (min_y..=max_y)
.collect::<Vec<usize>>()
.iter()
.map(|&y| (x1, y))
.collect::<Vec<Point>>();
}

if y1 == y2 {
return (min_x..=max_x)
.collect::<Vec<usize>>()
.iter()
.map(|&x| (x, y1))
.collect::<Vec<Point>>();
}

if skip_diagonals {
return vec![];
}

let start_y = if min_x == x1 { y1 } else { y2 };
let end_y = if max_x == x1 { y1 } else { y2 };

return (min_x..=max_x)
.collect::<Vec<usize>>()
.iter()
.scan(0usize, |state, &x| {
let current_step = *state;
*state += 1;

Some((
x,
if end_y > start_y {
start_y + current_step
} else {
start_y - current_step
},
))
})
.collect::<Vec<Point>>();
}

fn solve(raw: String) -> () {
let coords = raw
.trim()
.split("\n")
.map(|row| {
let spec = row.split(" -> ").collect::<Vec<&str>>();

let start = spec[0]
.split(",")
.map(|s| parse_num::<usize>(s))
.collect::<Vec<usize>>();
let end = spec[1]
.split(",")
.map(|s| parse_num::<usize>(s))
.collect::<Vec<usize>>();

return ((start[0], start[1]), (end[0], end[1]));
})
.collect::<Vec<(Point, Point)>>();

let width = 1 + coords
.iter()
.map(|(start, end)| max(start.0, end.0))
.max()
.unwrap_or(0);

let count_overlaps = move |skip_diagonals: bool| {
let mut table: HashMap<usize, usize> = HashMap::new();

for coord in coords.iter() {
let points = expand(coord.0, coord.1, skip_diagonals);

for point in points {
let norm = normal(point.0, point.1, width);
*table.entry(norm).or_insert(0) += 1;
}
}

table.iter().filter(|(_, &value)| value > 1).count()
};

println!("Part one: {}", count_overlaps(true));
println!("Part two: {}", count_overlaps(false));
}

fn main() {
let input = aoc::get_input(2021, 5);
// let input = std::fs::read_to_string("./input/day-5.in").expect("Error reading input");

solve(input);
}

// Utilities

#[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)
}
91 changes: 91 additions & 0 deletions 2021/deno/day-5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const input = await Deno.readTextFile("./input/day-5.in");

const expand = (
{ x1, y1, x2, y2 }: typeof coords[number],
skipDiagonals = true
) => {
const minY = Math.min(y1, y2);
const maxY = Math.max(y1, y2);

const minX = Math.min(x1, x2);
const maxX = Math.max(x1, x2);

if (x1 === x2) {
// horizontal
const length = maxY - minY;

return [...Array.from({ length }, (_, i) => [x1, minY + i]), [x1, maxY]];
}

if (y1 === y2) {
// vertical
const length = maxX - minX;

return [...Array.from({ length }, (_, i) => [minX + i, y1]), [maxX, y1]];
}

if (skipDiagonals) return [];

const lengthY = maxY - minY;
const lengthX = maxX - minX;

console.assert(lengthX === lengthY, "45 deg");

if (lengthY !== lengthX) return [];

const sign = Math.sign(minX === x1 ? y2 - y1 : y1 - y2);

const startY = minX === x1 ? y1 : y2;
const endY = maxX === x1 ? y1 : y2;

return [
...Array.from({ length: lengthX }, (_, i) => {
return [minX + i, startY + i * sign];
}),
[maxX, endY]
];
};

const normal = (x: number, y: number, width: number) => x + y * width;
const revNormal = (norm: number, width: number) => [
norm % width,
Math.floor(norm / width)
];

const coords = input.split("\n").map((row) => {
const [left, right] = row.split(" -> ");
const [x1, y1] = left.split(",").map(Number);
const [x2, y2] = right.split(",").map(Number);

return { x1, y1, x2, y2 };
});

const width = 1 + Math.max(...coords.map(({ x1, x2 }) => [x1, x2]).flat(1));

const countOverlaps = ({ skipDiagonals } = { skipDiagonals: true }) => {
const table = new Map<number, number>();

for (const coord of coords) {
const points = expand(coord, skipDiagonals);

for (const [x, y] of points) {
const norm = normal(x, y, width);
const count = table.get(norm) || 0;
table.set(norm, count + 1);
}
}

return [...table.values()].filter((count) => count > 1).length;
};

/**
* Part One
*/

console.log("Part One:", countOverlaps());

/**
* Part Two
*/

console.log("Part Two:", countOverlaps({ skipDiagonals: false }));

0 comments on commit 4186330

Please sign in to comment.