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 = input .split(",") // TODO: propogate this error .map(|n| n.parse::().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 = 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 { // 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 = 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) -> String { points .iter() .map(|p| format!("{}", p)) .collect::>() .join(" -> ") } fn join_vec(join_str: &str, v: &Vec) -> String { v.iter() .map(|p| format!("{}", p)) .collect::>() .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 { 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 = 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 = 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> = (0..(grid_max.1 + 1)) .map(|_| (0..(grid_max.0 + 1)).map(|_| 0).collect::>()) .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 = 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 = 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> = (0..(grid_max.1 + 1)) .map(|_| (0..(grid_max.0 + 1)).map(|_| 0).collect::>()) .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(); }