-
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.
- Loading branch information
Showing
3 changed files
with
235 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-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"} |
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,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) | ||
} |
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,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 })); |