1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::{ops::Neg, str::FromStr};

use advent_of_code::errors::Error;

use super::point::Point;

#[derive(Debug, Clone, Copy)]
pub enum Push {
    Left,
    Right,
    Down,
    Up,
}

impl Push {
    pub fn shift_amount(&self) -> Point {
        match self {
            Push::Left => (-1, 0),
            Push::Right => (1, 0),
            Push::Down => (0, -1),
            Push::Up => (0, 1),
        }
        .into()
    }
}

impl TryFrom<char> for Push {
    type Error = Error;

    fn try_from(value: char) -> Result<Self, Self::Error> {
        Ok(match value {
            '<' => Push::Left,
            '>' => Push::Right,
            _ => return Err(Error::InvalidParseError("not a correct move".to_owned())),
        })
    }
}

impl Neg for Push {
    type Output = Push;

    fn neg(self) -> Self::Output {
        match self {
            Push::Left => Push::Right,
            Push::Right => Push::Left,
            Push::Up => Push::Down,
            Push::Down => Push::Up,
        }
    }
}

pub struct MoveIter {
    cur: usize,
    is_down: bool,
    moves: Vec<Push>,
}

impl Iterator for MoveIter {
    type Item = Push;

    fn next(&mut self) -> Option<Self::Item> {
        if self.cur >= self.moves.len() {
            self.cur = 0;
        }
        if self.is_down {
            self.is_down = false;
            return Some(Push::Down);
        }
        if let Some(p) = self.moves.get(self.cur) {
            self.cur += 1;
            self.is_down = true;
            return Some(*p);
        }
        None
    }
}

impl FromStr for MoveIter {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let moves = s
            .trim()
            .chars()
            .map(|c| c.try_into())
            .collect::<Result<_, _>>()?;

        Ok(MoveIter {
            cur: 0,
            is_down: false,
            moves,
        })
    }
}