I'm a Sr. Software Engineer at Flashpoint. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Took me an extra day to clean it up so I wasn't disgusted with it. I definitely followed the Advent of Code "Brute Force until proven otherwise" approach. I considered doing it by only considering each leg of wire as two endpoints and calculating the intersections, but this was easier and more straightforward.
I also tried out Rust's type aliases to make things read a bit easier, which I'm really happy with.
Part 2 almost got me with a sneaky "Off by Two" error. I forgot to count the step off of the origin on both wires, leading me to be short by 2 on my final answer. A quick test case based on the examples provided showed I landed at 608 instead of 610 and I got suspicious.
Altogether much happier with this than I was last night. :)
/// Day 3: Crossed Wires/// /// Figure out which directions wires go and where they crossusestd::collections::HashSet;usestd::iter::FromIterator;usestd::fs;usestd::ops::Add;/// A coordinate is a 2D location (or Vector change!) with X and Y components#[derive(Debug,PartialEq,Eq,Hash,Copy,Clone)]structCoordinate{x:isize,y:isize,}/// You can Add Coordinates together with + to get a new oneimplAddforCoordinate{typeOutput=Self;fnadd(self,other:Self)->Self{Self{x:self.x+other.x,y:self.y+other.y,}}}implCoordinate{pubfnnew(x:isize,y:isize)->Self{Self{x,y}}}/// A Wire is a chain of coordinates that are electrically connectedtypeWire=Vec<Coordinate>;/// Moves are an ordered list of delta moves to make to form a wiretypeMoves=Vec<Coordinate>;/// Expects two lines of comma separated move strings, one for each wire./// The move strings are of the pattern [UDLR][0-9]+/// [UDLR]: Specifies whether the wire is going up, down, left or right/// [0-9]+: Specifies how many spaces that leg of the wire covers/// /// Returns both processed wiresfnparse_input()->(Wire,Wire){lettext=fs::read_to_string("data/day3.txt").unwrap();letlines:Vec<&str>=text.split("\n").collect();letmutwires=lines.into_iter().map(build_moves).map(make_wire);(wires.next().unwrap(),wires.next().unwrap())}/// Builds a list of Moves out of an input comma separated string as described/// above.fnbuild_moves(text_moves:&str)->Moves{letmutresults:Vec<Coordinate>=Vec::new();forstepintext_moves.split(","){let(direction,count_text)=step.split_at(1);letcount:usize=count_text.parse().unwrap();letmove_coord=ifdirection=="U"{Coordinate::new(0,1)}elseifdirection=="D"{Coordinate::new(0,-1)}elseifdirection=="L"{Coordinate::new(-1,0)}elseifdirection=="R"{Coordinate::new(1,0)}else{panic!("Weird step {}",direction);};for_in0..count{results.push(move_coord);}}results}/// Build a Wire out of relative Movesfnmake_wire(moves:Moves)->Wire{letmutcurrent=Coordinate{x:0,y:0};letmutresults:Wire=Vec::new();forstepinmoves{current=step+current;results.push(current);}results}/// Calculate a Coordinate's absolute distance from the originfnorigin_manhattan_distance(coord:&Coordinate)->usize{(coord.x.abs()+coord.y.abs())asusize}/// Given two Wires, find the location where they cross that is closest to the/// origin (which is where both wires start, and doesn't count as a cross)fnfind_closest_cross(a:&Wire,b:&Wire)->Coordinate{leta_set:HashSet<&Coordinate>=HashSet::from_iter(a.iter());letb_set:HashSet<&Coordinate>=HashSet::from_iter(b.iter());**a_set.intersection(&b_set).min_by_key(|c|origin_manhattan_distance(c)).unwrap()}/// Find the first occurrence of a Coordinate in a Wire (index-wise, but 1-based)fnfind_in_wire(wire:&Wire,target:&Coordinate)->usize{wire.iter().position(|e|e==target).unwrap()+1}/// Find the shortest distance you can travel on each wire (summed) before you/// hit a cross.fnshortest_cross_distance(a:&Wire,b:&Wire)->usize{leta_set:HashSet<&Coordinate>=HashSet::from_iter(a.iter());letb_set:HashSet<&Coordinate>=HashSet::from_iter(b.iter());a_set.intersection(&b_set).map(|c|find_in_wire(a,c)+find_in_wire(b,c)).min().unwrap()}/// Main Day 3 logic to solve the puzzlespubfnrun(){let(a,b)=parse_input();letclosest_cross=find_closest_cross(&a,&b);println!("Closest cross distance is {}",origin_manhattan_distance(&closest_cross));println!("Fewest combined steps: {}",shortest_cross_distance(&a,&b));}
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Took me an extra day to clean it up so I wasn't disgusted with it. I definitely followed the Advent of Code "Brute Force until proven otherwise" approach. I considered doing it by only considering each leg of wire as two endpoints and calculating the intersections, but this was easier and more straightforward.
I also tried out Rust's type aliases to make things read a bit easier, which I'm really happy with.
Part 2 almost got me with a sneaky "Off by Two" error. I forgot to count the step off of the origin on both wires, leading me to be short by 2 on my final answer. A quick test case based on the examples provided showed I landed at 608 instead of 610 and I got suspicious.
Altogether much happier with this than I was last night. :)