use std::env;
use std::iter::Peekable;
#[derive(Clone)]
#[derive(Copy)]
#[derive(Debug)]
struct Point {
x: i64,
y: i64,
}
#[derive(Clone)]
#[derive(Copy)]
#[derive(Debug)]
enum Direction {
U,
R,
D,
L
}
#[derive(Clone)]
#[derive(Copy)]
#[derive(Debug)]
struct Segment {
direction: Direction,
length: i64,
}
impl core::ops::Add<&Segment> for Point {
type Output = Self;
fn add(self, other: &Segment) -> Self {
match other.direction {
Direction::U => Point { x: self.x, y: self.y - other.length },
Direction::R => Point { x: self.x + other.length, y: self.y },
Direction::D => Point { x: self.x, y: self.y + other.length },
Direction::L => Point { x: self.x - other.length, y: self.y },
}
}
}
struct Pairwise<I: Iterator> {
last: Option<I::Item>,
inner: I,
}
impl<I: Iterator> Pairwise<I> {
fn new(inner: I) -> Self {
let mut inner = inner;
Pairwise {
last: inner.next(),
inner: inner,
}
}
}
impl<I: Iterator> Iterator for Pairwise<I>
where
I::Item: Copy
{
type Item = (I::Item,I::Item);
fn next(&mut self) -> Option<Self::Item> {
self.last.and_then(|last|
self.inner.next().and_then(|fresh| {
self.last = Some(fresh);
Some((last,fresh))
}).or_else(|| {
self.last = None;
None
})
)
}
}
struct Accumulator<I, R> {
sum: Option<R>,
inner: I,
}
impl<I, R> Accumulator<I,R> {
pub fn new(initial: R, inner: I) -> Self {
Accumulator{
sum: Some(initial),
inner: inner,
}
}
}
impl<I,R> Iterator for Accumulator<I,R> where
I: Iterator,
R: core::ops::Add<I::Item, Output=R> + Copy,
{
type Item = R;
fn next(&mut self) -> Option<R> {
match self.sum {
None => None,
Some(s) => {
self.sum = match self.inner.next() {
Some(a) => Some(s + a),
None => None,
};
Some(s)
},
}
}
}
fn next_direction<I: Iterator<Item=char>>(i: &mut Peekable<I>) -> Option<Direction> {
i.peek().and_then(|c| match c {
'U' => Some(Direction::U),
'R' => Some(Direction::R),
'D' => Some(Direction::D),
'L' => Some(Direction::L),
_ => None,
}).map(|d| {
i.next();
d
})
}
struct SegmentIter<I: Iterator<Item=char>> { inner: Peekable<I> }
impl<I: Iterator<Item=char>> SegmentIter<I> {
fn new(original: I) -> Self {
SegmentIter { inner: original.peekable(), }
}
fn next_line(&mut self) {
if self.inner.peek().map_or(false,|c| *c == '\n') {
self.inner.next();
}
}
}
impl<I: Iterator<Item=char>> Iterator for SegmentIter<I> {
type Item = Segment;
fn next(&mut self) -> Option<Segment> {
let r = next_direction(&mut self.inner)
.and_then(|d| next_number(&mut self.inner).map(|s| Segment {
direction: d,
length: s,
}));
if r.is_some() && self.inner.peek().map_or(false, |c| *c == ',') {
self.inner.next();
}
r
}
}
fn next_number<I: Iterator<Item=char>>(i: &mut Peekable<I>) -> Option<i64> {
i.peek()
.and_then(|c| c.to_digit(10))
.and_then(|d| {
i.next();
let mut d = d as i64;
loop {
match i.peek().and_then(|c| c.to_digit(10)) {
Some(d2) => {
d = d * 10 + d2 as i64;
i.next();
},
None => {
break Some(d);
},
}
}
})
}
fn intersection(a: (Point,Point), b: (Point,Point)) -> Option<Point> {
let mut a = a;
let mut b = b;
if a.0.x > a.1.x || a.0.y > a.1.y {
a = (a.1, a.0);
}
if b.0.x > b.1.x || b.0.y > b.1.y {
b = (b.1, b.0);
}
if a.0.x != a.1.x {
if b.0.x == b.1.x {
let t = b;
b = a;
a = t;
} else {
return None;
}
}
if b.0.y != b.1.y ||
b.0.y < a.0.y ||
b.0.y > a.1.y ||
a.0.x < b.0.x ||
a.0.x > b.1.x {
None
} else {
Some(Point{x: a.0.x, y: b.0.y})
}
}
fn main() {
let args: Vec<_> = env::args().collect();
let file = std::fs::read_to_string(&args[1])
.expect("Could not read the file");
let mut stream = SegmentIter::new(file.chars());
let mut wires = [Vec::new(), Vec::new()];
for i in 0 ..= 1 {
while let Some(segment) = stream.next() {
wires[i].push(segment);
}
stream.next_line();
}
let wires: [_; 2] = {
let mut w: [Vec<(Point,Point)>; 2] = [Vec::new(), Vec::new()];
for (dst, wl) in w.iter_mut().zip(wires.iter()) {
dst.extend(Pairwise::new(Accumulator::new(Point{x:0,y:0},wl.into_iter())));
}
w
};
let mut nd = i64::MAX;
let mut ai = wires[0].iter().enumerate();
while let Some((aix,a)) = ai.next() {
let mut bi = wires[1].iter();
if aix == 0 { bi.next(); }
while let Some(b) = bi.next() {
match intersection(*a,*b) {
None => {},
Some(p) => {
let d = p.x.abs() + p.y.abs();
if d < nd {
nd = d;
}
}
}
}
}
println!("{}", nd);
}