216 lines
5.7 KiB
Rust
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();
|
|
}
|