|
| 1 | +use std::{ |
| 2 | + cmp::{max, min}, |
| 3 | + iter::once, |
| 4 | +}; |
| 5 | + |
| 6 | +use colored::{Colorize, CustomColor}; |
| 7 | +use grid::*; |
| 8 | +use hex_color::HexColor; |
| 9 | + |
| 10 | +mod grid; |
| 11 | +mod macros; |
| 12 | + |
| 13 | +type Int = i32; |
| 14 | + |
| 15 | +#[derive(Debug)] |
| 16 | +struct Point { |
| 17 | + point: grid::Point, |
| 18 | + terrain: Terrain, |
| 19 | +} |
| 20 | + |
| 21 | +impl Point { |
| 22 | + fn new(p: grid::Point, c: String) -> Point { |
| 23 | + Point { |
| 24 | + point: p, |
| 25 | + terrain: Terrain::Trench(c), |
| 26 | + } |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +#[derive(Debug, Clone)] |
| 31 | +enum Terrain { |
| 32 | + Ground, |
| 33 | + Trench(String), |
| 34 | +} |
| 35 | + |
| 36 | +impl Terrain { |
| 37 | + fn to_string(&self) -> String { |
| 38 | + use Terrain::*; |
| 39 | + match self { |
| 40 | + Ground => ".".to_string(), |
| 41 | + Trench(color) => { |
| 42 | + let c = HexColor::parse(&color).unwrap(); |
| 43 | + "#".custom_color(CustomColor::new(c.r, c.g, c.b)) |
| 44 | + .to_string() |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +#[derive(Debug)] |
| 51 | +struct Polygon { |
| 52 | + points: Vec<Point>, |
| 53 | +} |
| 54 | + |
| 55 | +impl Polygon { |
| 56 | + fn new() -> Polygon { |
| 57 | + Polygon { points: Vec::new() } |
| 58 | + } |
| 59 | + |
| 60 | + fn translate(mut self, x: Int, y: Int) -> Polygon { |
| 61 | + Polygon { |
| 62 | + points: self |
| 63 | + .points |
| 64 | + .iter() |
| 65 | + .map(|p| Point { |
| 66 | + point: p.point.translate(x, y), |
| 67 | + terrain: p.terrain.clone(), |
| 68 | + }) |
| 69 | + .collect(), |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + fn translate_to_px_py(self) -> Polygon { |
| 74 | + let mut min_x = Int::MAX; |
| 75 | + let mut min_y = Int::MAX; |
| 76 | + for p in &self.points { |
| 77 | + min_x = min(min_x, p.point.x); |
| 78 | + min_y = min(min_y, p.point.y); |
| 79 | + } |
| 80 | + self.translate(-min_x, -min_y) |
| 81 | + } |
| 82 | + |
| 83 | + fn print(&self) { |
| 84 | + let mut max_x = 0; |
| 85 | + let mut max_y = 0; |
| 86 | + for p in &self.points { |
| 87 | + max_x = max(max_x, p.point.x); |
| 88 | + max_y = max(max_y, p.point.y); |
| 89 | + } |
| 90 | + for y in 0..=max_y { |
| 91 | + for x in 0..=max_x { |
| 92 | + if let Some(p) = self |
| 93 | + .points |
| 94 | + .iter() |
| 95 | + .find(|p| p.point.x == x && p.point.y == y) |
| 96 | + { |
| 97 | + print!("{}", p.terrain.to_string()); |
| 98 | + } else { |
| 99 | + print!("{}", Terrain::Ground.to_string()) |
| 100 | + } |
| 101 | + } |
| 102 | + println!(); |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + fn calc_area(&self) -> Int { |
| 107 | + //Shoelace formule to calculate area of polygon |
| 108 | + if self.points.is_empty() { |
| 109 | + 0 |
| 110 | + } else { |
| 111 | + let b = self.points.len() as Int; |
| 112 | + let points: Vec<_> = self.points.iter().chain(once(&self.points[0])).collect(); |
| 113 | + let mut area = 0; |
| 114 | + for i in 0..self.points.len() { |
| 115 | + let (p_0, p_1) = (points[i].point, points[i + 1].point); |
| 116 | + area += p_0.x * p_1.y - p_1.x * p_0.y; |
| 117 | + } |
| 118 | + area = (area / 2).abs(); |
| 119 | + // A = i + b/2 - 1 |
| 120 | + // i = A-b/2+1 |
| 121 | + // Filled polygon = i + b |
| 122 | + b + area - b / 2 + 1 |
| 123 | + } |
| 124 | + } |
| 125 | +} |
| 126 | + |
1 | 127 | fn main() {
|
2 |
| - println!("Hello, World! from src/day18.rs!"); |
| 128 | + println!("Hello, World! from src/day18.rs!"); |
| 129 | + // Part 1 - Example |
| 130 | + let dig_plan = vec_of_strings![ |
| 131 | + "R 6 (#70c710)", |
| 132 | + "D 5 (#0dc571)", |
| 133 | + "L 2 (#5713f0)", |
| 134 | + "D 2 (#d2c081)", |
| 135 | + "R 2 (#59c680)", |
| 136 | + "D 2 (#411b91)", |
| 137 | + "L 5 (#8ceee2)", |
| 138 | + "U 2 (#caa173)", |
| 139 | + "L 1 (#1b58a2)", |
| 140 | + "U 2 (#caa171)", |
| 141 | + "R 2 (#7807d2)", |
| 142 | + "U 3 (#a77fa3)", |
| 143 | + "L 2 (#015232)", |
| 144 | + "U 2 (#7a21e3)", |
| 145 | + ]; |
| 146 | + let mut polygon: Polygon = Polygon::new(); |
| 147 | + let mut p = grid::Point::new(0, 0); |
| 148 | + for op in dig_plan { |
| 149 | + let (d, l, c) = { |
| 150 | + let mut i = op.split_whitespace(); |
| 151 | + ( |
| 152 | + i.next().unwrap(), |
| 153 | + i.next().unwrap().parse::<Int>().unwrap(), |
| 154 | + i.next().unwrap(), |
| 155 | + ) |
| 156 | + }; |
| 157 | + let c = &c[1..c.len() - 1].to_string(); |
| 158 | + let direction = match d { |
| 159 | + "R" => East, |
| 160 | + "D" => South, |
| 161 | + "U" => North, |
| 162 | + "L" => West, |
| 163 | + _ => panic!("Invalid direction: '{}'", d), |
| 164 | + }; |
| 165 | + for i in 0..l { |
| 166 | + polygon.points.push(Point::new(p, c.clone())); |
| 167 | + p = p.move_to(&direction); |
| 168 | + } |
| 169 | + } |
| 170 | + dbg!(&polygon); |
| 171 | + polygon = polygon.translate_to_px_py(); |
| 172 | + polygon.print(); |
| 173 | + let a = polygon.calc_area(); |
| 174 | + test!(62, a); |
3 | 175 | }
|
0 commit comments