Andrew Swistak 6 vuotta sitten
vanhempi
commit
05fcc3f76f
5 muutettua tiedostoa jossa 429 lisäystä ja 6 poistoa
  1. 2 0
      src/day_3/mod.rs
  2. 204 0
      src/day_3/puzzle_1.rs
  3. 2 0
      src/day_3/puzzle_1.txt
  4. 209 0
      src/day_3/puzzle_2.rs
  5. 12 6
      src/main.rs

+ 2 - 0
src/day_3/mod.rs

@@ -0,0 +1,2 @@
+pub mod puzzle_1;
+pub mod puzzle_2;

+ 204 - 0
src/day_3/puzzle_1.rs

@@ -0,0 +1,204 @@
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+
+/*
+--- Day 3: Crossed Wires ---
+
+The gravity assist was successful, and you're well on your way to the Venus refuelling station.
+During the rush back on Earth, the fuel management system wasn't completely installed, so that's
+next on the priority list.
+
+Opening the front panel reveals a jumble of wires. Specifically, two wires are connected to a
+central port and extend outward on a grid. You trace the path each wire takes as it leaves the
+central port, one wire per line of text (your puzzle input).
+
+The wires twist and turn, but the two wires occasionally cross paths. To fix the circuit, you need
+to find the intersection point closest to the central port. Because the wires are on a grid, use
+the Manhattan distance for this measurement. While the wires do technically cross right at the
+central port where they both start, this point does not count, nor does a wire count as crossing
+with itself.
+
+For example, if the first wire's path is R8,U5,L5,D3, then starting from the central port (o), it
+goes right 8, up 5, left 5, and finally down 3:
+
+...........
+...........
+...........
+....+----+.
+....|....|.
+....|....|.
+....|....|.
+.........|.
+.o-------+.
+...........
+
+Then, if the second wire's path is U7,R6,D4,L4, it goes up 7, right 6, down 4, and left 4:
+
+...........
+.+-----+...
+.|.....|...
+.|..+--X-+.
+.|..|..|.|.
+.|.-X--+.|.
+.|..|....|.
+.|.......|.
+.o-------+.
+...........
+
+These wires cross at two locations (marked X), but the lower-left one is closer to the central
+port: its distance is 3 + 3 = 6.
+
+Here are a few more examples:
+
+    R75,D30,R83,U83,L12,D49,R71,U7,L72
+    U62,R66,U55,R34,D71,R55,D58,R83 = distance 159
+    R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
+    U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 = distance 135
+
+What is the Manhattan distance from the central port to the closest intersection?
+*/
+
+#[derive(Copy, Clone)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+impl Point {
+    pub fn manhattan_dist_to(&self, other: Point) -> i32 {
+        let x_dist = (self.x - other.x).abs();
+        let y_dist = (self.y - other.y).abs();
+
+        x_dist + y_dist
+    }
+}
+
+struct Segment {
+    x_min: i32,
+    x_max: i32,
+    y_min: i32,
+    y_max: i32,
+}
+
+impl Segment {
+    pub fn new(p1: Point, p2: Point) -> Segment {
+        let x_min = if p1.x < p2.x { p1.x } else { p2.x };
+        let x_max = if p1.x > p2.x { p1.x } else { p2.x };
+        let y_min = if p1.y < p2.y { p1.y } else { p2.y };
+        let y_max = if p1.y > p2.y { p1.y } else { p2.y };
+
+        Segment {
+            x_min: x_min,
+            x_max: x_max,
+            y_min: y_min,
+            y_max: y_max,
+        }
+    }
+
+    pub fn intersects(&self, other: &Segment) -> Option<Point> {
+        // make an assumption that segments never overlap, e.g. intersections are always
+        // perpendicular
+
+        // self is horizontal, other is vertical
+        if self.x_min <= other.x_min
+            && self.x_max >= other.x_max
+            && other.y_min <= self.y_min
+            && other.y_max >= self.y_max
+        {
+            Some(Point {
+                x: other.x_min,
+                y: self.y_min,
+            })
+        // self is vertical, other is horizontal
+        } else if other.x_min <= self.x_min
+            && other.x_max >= self.x_max
+            && self.y_min <= other.y_min
+            && self.y_max >= other.y_max
+        {
+            Some(Point {
+                x: self.x_min,
+                y: other.y_min,
+            })
+        } else {
+            None
+        }
+    }
+}
+
+type RawWire = Vec<String>;
+type Wire = Vec<Segment>;
+
+fn get_raw_wires() -> (RawWire, RawWire) {
+    let file = File::open("./src/day_3/puzzle_1.txt").expect("no such file");
+    let mut buf = BufReader::new(file);
+
+    let mut raw_wire1 = String::new();
+    let _ = buf.read_line(&mut raw_wire1);
+
+    let mut raw_wire2 = String::new();
+    let _ = buf.read_line(&mut raw_wire2);
+
+    (
+        raw_wire1
+            .trim()
+            .split(',')
+            .map(|seg| seg.to_owned())
+            .collect(),
+        raw_wire2
+            .trim()
+            .split(',')
+            .map(|seg| seg.to_owned())
+            .collect(),
+    )
+}
+
+fn parse_raw_wire(raw: RawWire) -> Wire {
+    let mut wire = Wire::new();
+
+    let mut point = Point { x: 0, y: 0 };
+
+    for seg in raw {
+        let (dir, str_amt) = seg.split_at(1);
+        let amt = str_amt.parse::<i32>().expect("could not parse segment");
+
+        let mut new_point = point;
+
+        match dir {
+            "U" => new_point.x += amt,
+            "D" => new_point.x -= amt,
+            "L" => new_point.y -= amt,
+            "R" => new_point.y += amt,
+            _ => panic!(),
+        }
+
+        wire.push(Segment::new(point, new_point));
+        point = new_point;
+    }
+
+    wire
+}
+
+pub fn solve() {
+    let (raw_wire1, raw_wire2) = get_raw_wires();
+    let wire1 = parse_raw_wire(raw_wire1);
+    let wire2 = parse_raw_wire(raw_wire2);
+
+    let mut min_dist = i32::max_value();
+    let origin = Point { x: 0, y: 0 };
+
+    for seg1 in wire1 {
+        for seg2 in &wire2 {
+            match seg2.intersects(&seg1) {
+                Some(point) => {
+                    let dist = point.manhattan_dist_to(origin);
+                    if dist < min_dist && dist != 0 {
+                        min_dist = dist;
+                    }
+                }
+                None => (),
+            }
+        }
+    }
+
+    println!("min manhattan dist: {}", min_dist);
+}

+ 2 - 0
src/day_3/puzzle_1.txt

@@ -0,0 +1,2 @@
+R990,U408,L583,U275,R483,U684,R437,U828,R108,U709,R378,U97,R252,D248,R413,U750,R428,D545,R570,D795,L204,D975,L557,U160,L861,U106,R436,U934,R81,D237,R660,U704,L451,U135,R282,D391,R39,D109,R125,U918,R214,U481,R853,U825,L91,D763,R335,U868,R42,U218,R152,D429,R414,D607,R28,U436,R7,U770,L215,D373,R209,U440,L536,U120,R900,D46,R635,D75,R58,U267,L581,U474,L858,U172,R725,U54,R291,D274,L583,D743,L130,U563,R137,U524,R659,D997,R131,D364,R883,D222,R628,U579,R801,D890,L519,D749,L620,U60,L759,D759,R376,U769,L910,D570,L814,U954,L153,D42,L784,D66,L844,U29,L794,D342,L924,U825,R447,U828,R404,D52,L330,D876,R125,U203,R245,U936,R866,D804,L186,U693,L620,D722,L32,D735,L191,D217,R68,U209,L736,U365,R280,U608,L450,D240,L282,U434,R589,U94,R470,D5,R49,U407,R552,D651,L69,U518,L358,D130,L710,D929,L315,U345,L511,D229,L557,U44,L890,D702,L181,D61,L208,U553,R878,U354,R787,U624,L961,D92,L891,U70,R203,U255,R532,U154,R299,U934,L609,D985,R115,U757,L13,D368,R936,D742,L412,U346,R56,D67,R371,D175,R868,U107,R806,D530,L40,U153,R374,D223,R517,D481,L194,U545,L356,U906,L999,D885,R967,U407,L141,U927,L489,U959,L992,U638,R332,U51,R256,U901,L891,U803,L885,U804,L242,U180,R277,U693,R935,D253,L68,D153,L614,D596,L999,D633,R995,D803,R17,U303,L569,U231,R737,D970,L45,D860,L225,D65,R41,D313,R698,D340,R599,D531,R55,D568,L911,D547,R196,D228,R868,D227,R262,U525,R104,D625,R570,U968,L276,D586,R690,D73,L336,U287,R294,U148,R781,D395,R478,D804,L429,U872,L351,D910,L597,U726,L320,D964,R928,U2,R540,D325,L222
+L998,U662,R342,U104,R140,U92,R67,D102,L225,U265,R641,U592,L295,D77,R415,U908,L640,D381,R312,U44,R424,D847,R892,D625,L337,D344,L917,D914,R127,D273,L627,U812,L200,D262,R226,U273,R911,U597,L888,U28,R921,U464,R254,U771,R818,D808,L239,D225,L280,U785,R322,D831,L622,U506,R139,U12,L491,D572,L172,U685,R54,U747,L812,D717,R874,U428,L867,U174,R360,D36,R217,D539,R210,D791,L82,D665,L190,D313,R649,U849,R63,U385,R105,U806,L207,U697,L823,D272,R830,D952,L386,U987,R775,U517,R139,D756,R545,D973,L743,D286,R261,U448,R946,U884,L903,D142,R28,D374,R259,U403,R689,D245,L302,D134,R710,U762,L67,D561,R801,D140,L887,U346,L227,U682,L350,D218,L711,U755,R226,D277,R114,D61,R992,U602,L191,U640,R733,D329,R862,U242,R754,D161,L52,D974,L251,D444,L552,U977,R174,U483,R869,D955,R925,U693,R610,D353,L843,U148,L866,D167,R412,D31,L847,D979,L282,D797,L837,U473,L402,U193,L332,D603,R48,D589,L760,D673,L843,U428,R779,D592,L688,D141,R851,D642,R559,U939,R999,D64,L297,U817,R670,U322,L768,D936,L39,U95,L342,U849,L692,U714,L732,D734,L373,U66,L577,D453,R336,U760,L217,U542,R920,U24,R529,D594,L34,D79,R877,D965,R932,U460,R879,U26,R803,U876,L780,U956,L235,D270,L315,D577,R835,U750,R414,D584,L828,U335,L563,U238,L815,U780,L550,U18,R743,D54,L816,U344,L806,D197,L518,D682,L835,U255,L666,U442,L286,D543,R102,D52,L570,D787,L763,D223,R279,D892,L828,D111,L554,D452,R575,D299,R932,D187,L439,U616,L278,D701,L360,D524,L891,U953,L896,U788,R776,U782,L71,D741,L652,U121,R669,D809,L662,U319,R392,D313,R870,U794,R937,D469,R571,D761,R947

+ 209 - 0
src/day_3/puzzle_2.rs

@@ -0,0 +1,209 @@
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+
+/*
+--- Part Two ---
+
+It turns out that this circuit is very timing-sensitive; you actually need to minimize the signal
+delay.
+
+To do this, calculate the number of steps each wire takes to reach each intersection; choose the
+intersection where the sum of both wires' steps is lowest. If a wire visits a position on the grid
+multiple times, use the steps value from the first time it visits that position when calculating
+the total value of a specific intersection.
+
+The number of steps a wire takes is the total number of grid squares the wire has entered to get to
+that location, including the intersection being considered. Again consider the example from above:
+
+...........
+.+-----+...
+.|.....|...
+.|..+--X-+.
+.|..|..|.|.
+.|.-X--+.|.
+.|..|....|.
+.|.......|.
+.o-------+.
+...........
+
+In the above example, the intersection closest to the central port is reached after 8+5+5+2 = 20
+steps by the first wire and 7+6+4+3 = 20 steps by the second wire for a total of 20+20 = 40 steps.
+
+However, the top-right intersection is better: the first wire takes only 8+5+2 = 15 and the second
+wire takes only 7+6+2 = 15, a total of 15+15 = 30 steps.
+
+Here are the best steps for the extra examples from above:
+
+    R75,D30,R83,U83,L12,D49,R71,U7,L72
+    U62,R66,U55,R34,D71,R55,D58,R83 = 610 steps
+    R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
+    U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 = 410 steps
+
+What is the fewest combined steps the wires must take to reach an intersection?
+*/
+
+#[derive(Copy, Clone)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+impl Point {
+    pub fn manhattan_dist_to(&self, other: Point) -> i32 {
+        let x_dist = (self.x - other.x).abs();
+        let y_dist = (self.y - other.y).abs();
+
+        x_dist + y_dist
+    }
+
+    pub fn equals(&self, other: Point) -> bool {
+        self.x == other.x && self.y == other.y
+    }
+}
+
+struct Segment {
+    p1: Point,
+    p2: Point,
+    x_min: i32,
+    x_max: i32,
+    y_min: i32,
+    y_max: i32,
+    steps: i32,
+}
+
+impl Segment {
+    pub fn new(p1: Point, p2: Point) -> Segment {
+        let x_min = if p1.x < p2.x { p1.x } else { p2.x };
+        let x_max = if p1.x > p2.x { p1.x } else { p2.x };
+        let y_min = if p1.y < p2.y { p1.y } else { p2.y };
+        let y_max = if p1.y > p2.y { p1.y } else { p2.y };
+
+        Segment {
+            p1: p1,
+            p2: p2,
+            x_min: x_min,
+            x_max: x_max,
+            y_min: y_min,
+            y_max: y_max,
+            steps: (p1.x - p2.x).abs() + (p1.y - p2.y).abs(),
+        }
+    }
+
+    pub fn intersects(&self, other: &Segment) -> Option<Point> {
+        // make an assumption that segments never overlap, e.g. intersections are always
+        // perpendicular
+
+        // self is horizontal, other is vertical
+        if self.x_min <= other.x_min
+            && self.x_max >= other.x_max
+            && other.y_min <= self.y_min
+            && other.y_max >= self.y_max
+        {
+            Some(Point {
+                x: other.x_min,
+                y: self.y_min,
+            })
+        // self is vertical, other is horizontal
+        } else if other.x_min <= self.x_min
+            && other.x_max >= self.x_max
+            && self.y_min <= other.y_min
+            && self.y_max >= other.y_max
+        {
+            Some(Point {
+                x: self.x_min,
+                y: other.y_min,
+            })
+        } else {
+            None
+        }
+    }
+
+    pub fn steps_to_point(&self, point: Point) -> i32 {
+        (self.p1.x - point.x).abs() + (self.p1.y - point.y).abs()
+    }
+}
+
+type RawWire = Vec<String>;
+type Wire = Vec<Segment>;
+
+fn get_raw_wires() -> (RawWire, RawWire) {
+    let file = File::open("./src/day_3/puzzle_1.txt").expect("no such file");
+    let mut buf = BufReader::new(file);
+
+    let mut raw_wire1 = String::new();
+    let _ = buf.read_line(&mut raw_wire1);
+
+    let mut raw_wire2 = String::new();
+    let _ = buf.read_line(&mut raw_wire2);
+
+    (
+        raw_wire1
+            .trim()
+            .split(',')
+            .map(|seg| seg.to_owned())
+            .collect(),
+        raw_wire2
+            .trim()
+            .split(',')
+            .map(|seg| seg.to_owned())
+            .collect(),
+    )
+}
+
+fn parse_raw_wire(raw: RawWire) -> Wire {
+    let mut wire = Wire::new();
+
+    let mut point = Point { x: 0, y: 0 };
+
+    for seg in raw {
+        let (dir, str_amt) = seg.split_at(1);
+        let amt = str_amt.parse::<i32>().expect("could not parse segment");
+
+        let mut new_point = point;
+
+        match dir {
+            "U" => new_point.x += amt,
+            "D" => new_point.x -= amt,
+            "L" => new_point.y -= amt,
+            "R" => new_point.y += amt,
+            _ => panic!(),
+        }
+
+        wire.push(Segment::new(point, new_point));
+        point = new_point;
+    }
+
+    wire
+}
+
+pub fn solve() {
+    let (raw_wire1, raw_wire2) = get_raw_wires();
+    let wire1 = parse_raw_wire(raw_wire1);
+    let wire2 = parse_raw_wire(raw_wire2);
+
+    let mut min_steps = i32::max_value();
+    let origin = Point { x: 0, y: 0 };
+
+    let mut steps: i32 = 0;
+    for seg1 in wire1 {
+        let mut seg2_steps = 0;
+
+        for seg2 in &wire2 {
+            match seg2.intersects(&seg1) {
+                Some(p) => {
+                    let total =
+                        steps + seg2_steps + seg1.steps_to_point(p) + seg2.steps_to_point(p);
+
+                    if total < min_steps && !p.equals(origin) {
+                        min_steps = total;
+                    }
+                }
+                None => (),
+            }
+            seg2_steps += seg2.steps;
+        }
+        steps += seg1.steps;
+    }
+
+    println!("min steps: {}", min_steps);
+}

+ 12 - 6
src/main.rs

@@ -3,6 +3,7 @@ extern crate clap;
 
 mod day_1;
 mod day_2;
+mod day_3;
 
 fn main() {
     use clap::App;
@@ -14,14 +15,19 @@ fn main() {
     let puzzle = opt_str_to_int(matches.value_of("puzzle"));
 
     match day {
-        d if d == 1 => match puzzle {
-            p if p == 1 => day_1::puzzle_1::solve(),
-            p if p == 2 => day_1::puzzle_2::solve(),
+        1 => match puzzle {
+            1 => day_1::puzzle_1::solve(),
+            2 => day_1::puzzle_2::solve(),
             _ => println!("unavailable puzzle given!"),
         },
-        d if d == 2 => match puzzle {
-            p if p == 1 => day_2::puzzle_1::solve(),
-            p if p == 2 => day_2::puzzle_2::solve(),
+        2 => match puzzle {
+            1 => day_2::puzzle_1::solve(),
+            2 => day_2::puzzle_2::solve(),
+            _ => println!("unavailable puzzle given!"),
+        },
+        3 => match puzzle {
+            1 => day_3::puzzle_1::solve(),
+            2 => day_3::puzzle_2::solve(),
             _ => println!("unavailable puzzle given!"),
         },
         _ => println!("unavailable day given!"),