aoc-2021/d05/src/main.rs

216 lines
5.7 KiB
Rust

use std::cmp::Ordering;
use std::fmt;
use std::fs;
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd, Eq)]
struct Point(i32, i32);
impl Point {
fn from_str(input: &str) -> Self {
let coords: Vec<i32> = input
.split(",")
// TODO: propogate this error
.map(|n| n.parse::<i32>().unwrap())
.collect();
// TODO: Check length
Point(coords[0], coords[1])
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.0, self.1)
}
}
impl Ord for Point {
fn cmp(&self, other: &Self) -> Ordering {
let x_ordering = self.0.cmp(&other.0);
if x_ordering == Ordering::Equal {
self.1.cmp(&other.1)
} else {
x_ordering
}
}
}
#[derive(Debug, Default, Clone, Copy)]
struct Line(Point, Point);
impl Line {
fn from_str(input: &str) -> Self {
// TODO: propagate error
let parts: Vec<Point> = input.split(" -> ").map(|p| Point::from_str(p)).collect();
// TODO: Check length
Line(parts[0], parts[1])
}
fn is_horizontal(&self) -> bool {
self.0 .1 == self.1 .1
}
fn is_vertical(&self) -> bool {
self.0 .0 == self.1 .0
}
fn calc_x(&self, y: i32) -> i32 {
if self.is_vertical() {
return self.0 .0;
}
// (y1 - x1) / (y2 - x2)
let slope: f64 = (f64::from(self.1 .1) - f64::from(self.0 .1))
/ (f64::from(self.1 .0) - f64::from(self.0 .0));
let offset = f64::from(self.1 .1) - (f64::from(self.1 .0) * slope);
((f64::from(y) - offset) / slope) as i32
}
fn calc_y(&self, x: i32) -> i32 {
// (y1 - x1) / (y2 - x2)
let slope: f64 = (f64::from(self.1 .1) - f64::from(self.0 .1))
/ (f64::from(self.1 .0) - f64::from(self.0 .0));
let offset = f64::from(self.1 .1) - (f64::from(self.1 .0) * slope);
((f64::from(x) * slope) + offset) as i32
}
fn interpolate_line(&self) -> Vec<Point> {
// Walk along x
let x_walk = (self.0 .0.min(self.1 .0)..(self.0 .0.max(self.1 .0) + 1))
.map(|x| Point(x, self.calc_y(x)));
// Walk along y
let y_walk = (self.0 .1.min(self.1 .1)..(self.0 .1.max(self.1 .1) + 1))
.map(|y| Point(self.calc_x(y), y));
if self.is_horizontal() {
x_walk.collect()
} else if self.is_vertical() {
y_walk.collect()
} else {
let mut points: Vec<Point> = x_walk.chain(y_walk).collect();
points.sort();
points.dedup();
points
}
}
}
impl fmt::Display for Line {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{} -> {}>", self.0, self.1)
}
}
fn join_points(points: &Vec<Point>) -> String {
points
.iter()
.map(|p| format!("{}", p))
.collect::<Vec<String>>()
.join(" -> ")
}
fn join_vec<T: std::fmt::Display>(join_str: &str, v: &Vec<T>) -> String {
v.iter()
.map(|p| format!("{}", p))
.collect::<Vec<String>>()
.join(join_str)
}
/// Returns the maximum point in a grid bound by the input vector
///
/// # Arguments
///
/// * `points` - A vector of points that should fit in the grid
fn max_point(points: &Vec<Point>) -> Point {
points.iter().fold(Point(0, 0), |max, point| {
Point(max.0.max(point.0), max.1.max(point.1))
})
}
fn part1() {
let file_contents = fs::read_to_string("input.txt").expect("No input found");
let lines: Vec<Line> = file_contents
.lines()
.map(|l| Line::from_str(l))
.filter(|l| l.is_horizontal() || l.is_vertical())
.collect();
for line in &lines {
println!("Line is {}", line);
let line_points: Vec<Point> = line.interpolate_line();
println!("Full line: {}", join_points(&line_points));
}
let all_points = lines
.iter()
.map(|line| line.interpolate_line())
.flatten()
.collect();
let grid_max = max_point(&all_points);
let mut grid: Vec<Vec<i32>> = (0..(grid_max.1 + 1))
.map(|_| (0..(grid_max.0 + 1)).map(|_| 0).collect::<Vec<i32>>())
.collect();
for point in &all_points {
grid[point.1 as usize][point.0 as usize] += 1;
}
for row in &grid {
println!("{}", join_vec(" ", row));
}
let result: i32 = grid
.iter()
.flatten()
.map(|v| if *v > 1 { 1 } else { 0 })
.sum();
println!("Part 1 result is {}", result)
}
fn part2() {
let file_contents = fs::read_to_string("input.txt").expect("No input found");
let lines: Vec<Line> = file_contents
.lines()
.map(|l| Line::from_str(l))
// .filter(|l| l.is_horizontal() || l.is_vertical())
.collect();
for line in &lines {
println!("Line is {}", line);
let line_points: Vec<Point> = line.interpolate_line();
println!("Full line: {}", join_points(&line_points));
}
let all_points = lines
.iter()
.map(|line| line.interpolate_line())
.flatten()
.collect();
let grid_max = max_point(&all_points);
let mut grid: Vec<Vec<i32>> = (0..(grid_max.1 + 1))
.map(|_| (0..(grid_max.0 + 1)).map(|_| 0).collect::<Vec<i32>>())
.collect();
for point in &all_points {
grid[point.1 as usize][point.0 as usize] += 1;
}
for row in &grid {
println!("{}", join_vec(" ", row));
}
let result: i32 = grid
.iter()
.flatten()
.map(|v| if *v > 1 { 1 } else { 0 })
.sum();
println!("Part 1 result is {}", result)
}
fn main() {
part1();
part2();
}