diff --git a/2021/day-5/Cargo.toml b/2021/day-5/Cargo.toml new file mode 100644 index 0000000..1ba1361 --- /dev/null +++ b/2021/day-5/Cargo.toml @@ -0,0 +1,9 @@ +[package] +authors = ["Joseph Chamochumbi "] +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"} diff --git a/2021/day-5/src/main.rs b/2021/day-5/src/main.rs new file mode 100644 index 0000000..445fd49 --- /dev/null +++ b/2021/day-5/src/main.rs @@ -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 { + 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::>() + .iter() + .map(|&y| (x1, y)) + .collect::>(); + } + + if y1 == y2 { + return (min_x..=max_x) + .collect::>() + .iter() + .map(|&x| (x, y1)) + .collect::>(); + } + + 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::>() + .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::>(); +} + +fn solve(raw: String) -> () { + let coords = raw + .trim() + .split("\n") + .map(|row| { + let spec = row.split(" -> ").collect::>(); + + let start = spec[0] + .split(",") + .map(|s| parse_num::(s)) + .collect::>(); + let end = spec[1] + .split(",") + .map(|s| parse_num::(s)) + .collect::>(); + + return ((start[0], start[1]), (end[0], end[1])); + }) + .collect::>(); + + 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 = 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(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) +} diff --git a/2021/deno/day-5.ts b/2021/deno/day-5.ts new file mode 100644 index 0000000..18c221b --- /dev/null +++ b/2021/deno/day-5.ts @@ -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(); + + 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 }));