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
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
use std::fmt;
use std::iter;
use std::ops;
use std::str;

/// A two-dimensional matrix of characters
#[derive(Debug)]
pub struct Playfield {
    field: Vec<u8>,
    width: usize,
    height: usize,
}

impl Playfield {
    /// Create a new playfield from the given input string.
    ///
    /// Each line in the input is padded with spaces to the length of the longest line.
    /// Width and height are defined as the length of the longest line and the number of lines in
    /// the input string.
    pub fn new(input: &str) -> Self {
        let lines: Vec<&str> = input.lines().collect();
        let width = lines.iter().map(|s| s.bytes().count()).max().unwrap();
        let height = lines.len();

        let mut field = Vec::with_capacity(width * height);

        for l in lines {
            field.extend(l.bytes().chain(iter::repeat(b' ')).take(width));
        }

        Self {
            field,
            width,
            height,
        }
    }

    /// Return the width of this playfield.
    pub fn width(&self) -> usize {
        self.width
    }

    /// Return the height of this playfield.
    pub fn height(&self) -> usize {
        self.height
    }

    /// Return the dimensions of this playfield.
    pub fn dimensions(&self) -> (usize, usize) {
        (self.width, self.height)
    }

    /// Return an iterator over the lines of this playfield.
    pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
        self.field.chunks(self.width)
    }
}

impl ops::Index<(usize, usize)> for Playfield {
    type Output = u8;

    fn index(&self, index: (usize, usize)) -> &Self::Output {
        &self.field[index.0 + self.width * index.1]
    }
}

impl ops::IndexMut<(usize, usize)> for Playfield {
    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
        &mut self.field[index.0 + self.width * index.1]
    }
}

impl fmt::Display for Playfield {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for l in self.lines() {
            writeln!(f, "{}", str::from_utf8(l).unwrap())?;
        }

        Ok(())
    }
}

/// The four movement directions
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction {
    Up,
    Down,
    Left,
    Right,
}

/// A navigator through the playfield
///
/// The navigator stores the current position and the direction at which we are looking.
pub struct PlayfieldNavigator {
    dim: (usize, usize),
    pos: (usize, usize),
    dir: Direction,
}

impl PlayfieldNavigator {
    /// Create a new navigator for the given dimensions.
    ///
    /// Initially, we are looking right from position `(0, 0)`.
    pub fn new(dim: (usize, usize)) -> Self {
        Self {
            dim,
            pos: (0, 0),
            dir: Direction::Right,
        }
    }

    /// Move one step in the field.
    ///
    /// When the border of the field is reached, the navigator wraps around and continues at the
    /// opposite side of the field.
    pub fn step(&mut self) {
        match self.dir {
            Direction::Up => {
                if self.pos.1 > 0 {
                    self.pos.1 -= 1
                } else {
                    self.pos.1 = self.dim.1 - 1
                }
            }
            Direction::Down => {
                if self.pos.1 < self.dim.1 - 1 {
                    self.pos.1 += 1
                } else {
                    self.pos.1 = 0
                }
            }
            Direction::Left => {
                if self.pos.0 > 0 {
                    self.pos.0 -= 1
                } else {
                    self.pos.0 = self.dim.0 - 1
                }
            }
            Direction::Right => {
                if self.pos.0 < self.dim.0 - 1 {
                    self.pos.0 += 1
                } else {
                    self.pos.0 = 0
                }
            }
        }
    }

    /// Turn into the given direction.
    pub fn turn(&mut self, dir: Direction) {
        self.dir = dir
    }

    /// Return the current position of the navigator.
    pub fn pos(&self) -> (usize, usize) {
        self.pos
    }

    /// Return the current direction the navigator is looking in.
    pub fn dir(&self) -> Direction {
        self.dir
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn playfield() {
        let mut playfield = Playfield::new("abc\nde\nx yz\n");

        assert_eq!((4, 3), playfield.dimensions());
        assert_eq!("abc \nde  \nx yz\n", playfield.to_string());

        assert_eq!('a', playfield[(0, 0)] as char);
        assert_eq!(' ', playfield[(3, 1)] as char);

        playfield[(3, 1)] = 0x62;

        assert_eq!('b', playfield[(3, 1)] as char);
    }

    #[test]
    fn playfield_navigator() {
        let mut navigator = PlayfieldNavigator::new((4, 3));

        assert_eq!(Direction::Right, navigator.dir());
        assert_eq!((0, 0), navigator.pos());

        navigator.step();

        assert_eq!((1, 0), navigator.pos());

        navigator.step();

        assert_eq!((2, 0), navigator.pos());

        navigator.step();

        assert_eq!((3, 0), navigator.pos());

        navigator.step();

        assert_eq!((0, 0), navigator.pos());

        navigator.turn(Direction::Down);

        assert_eq!(Direction::Down, navigator.dir());
        assert_eq!((0, 0), navigator.pos());

        navigator.step();

        assert_eq!((0, 1), navigator.pos());

        navigator.step();

        assert_eq!((0, 2), navigator.pos());

        navigator.step();

        assert_eq!((0, 0), navigator.pos());

        navigator.turn(Direction::Left);

        assert_eq!(Direction::Left, navigator.dir());
        assert_eq!((0, 0), navigator.pos());

        navigator.step();

        assert_eq!((3, 0), navigator.pos());

        navigator.turn(Direction::Up);

        assert_eq!(Direction::Up, navigator.dir());
        assert_eq!((3, 0), navigator.pos());

        navigator.step();

        assert_eq!((3, 2), navigator.pos());
    }
}