Skip to main content
part one
Link

Advent of Code 2016: Day 1, Part 1

add note about `feature(field_init_shorthand)`
Source Link

I would have used #![feature(field_init_shorthand)] to replace

Instruction {
    turn: turn,
    blocks: blocks,
}

with Instruction { turn, blocks }, but the current version of rustfmt doesn't support that feature yet.

I would have used #![feature(field_init_shorthand)] to replace

Instruction {
    turn: turn,
    blocks: blocks,
}

with Instruction { turn, blocks }, but the current version of rustfmt doesn't support that feature yet.

Source Link

Advent of Code 2016 Day 1

I'm working through Advent of Code 2016 in Rust. The prizes go to the swift, but I'm aiming for well-written code. This is Day 1.

To summarize the problem statement, given a instruction string of turns and distances (e.g. "R5, L5, R5, R3"), return the Manhattan distance from the origin (in this case 12).

I am using Rust version 1.15.0-nightly (71c06a56a 2016-12-18).

#lib.rs

#![feature(try_from)]
#![feature(more_struct_aliases)]

mod day_01;

pub fn day_01() {
    let day_01_answer =
        day_01::blocks_away("L4, L1, R4, R1, R1, L3, R5, L5, L2, L3, R2, R1, L4, R5, R4, L2, R1, \
                             R3, L5, R1, L3, L2, R5, L4, L5, R1, R2, L1, R5, L3, R2, R2, L1, R5, \
                             R2, L1, L1, R2, L1, R1, L2, L2, R4, R3, R2, L3, L188, L3, R2, R54, \
                             R1, R1, L2, L4, L3, L2, R3, L1, L1, R3, R5, L1, R5, L1, L1, R2, R4, \
                             R4, L5, L4, L1, R2, R4, R5, L2, L3, R5, L5, R1, R5, L2, R4, L2, L1, \
                             R4, R3, R4, L4, R3, L4, R78, R2, L3, R188, R2, R3, L2, R2, R3, R1, \
                             R5, R1, L1, L1, R4, R2, R1, R5, L1, R4, L4, R2, R5, L2, L5, R4, L3, \
                             L2, R1, R1, L5, L4, R1, L5, L1, L5, L1, L4, L3, L5, R4, R5, R2, L5, \
                             R5, R5, R4, R2, L1, L2, R3, R5, R5, R5, L2, L1, R4, R3, R1, L4, L2, \
                             L3, R2, L3, L5, L2, L2, L1, L2, R5, L2, L2, L3, L1, R1, L4, R2, L4, \
                             R3, R5, R3, R4, R1, R5, L3, L5, L5, L3, L2, L1, R3, L4, R3, R2, L1, \
                             R3, R1, L2, R4, L3, L3, L3, L1, L2");
    assert_eq!(day_01_answer, 279);
}

#day_01.rs

use std::convert::TryFrom;

pub fn blocks_away(instructions: &str) -> i16 {
    let mut x = 0;
    let mut y = 0;
    let mut direction = Direction::North;
    for instruction in Instruction::try_many_from(instructions).unwrap() {
        direction.turn(instruction.turn);
        match direction {
            Direction::North => y += instruction.blocks,
            Direction::East => x += instruction.blocks,
            Direction::South => y -= instruction.blocks,
            Direction::West => x -= instruction.blocks,
        }
    }
    x.abs() + y.abs()
}

#[derive(Debug, PartialEq, Eq)]
enum Direction {
    North,
    East,
    South,
    West,
}

impl Direction {
    fn turn(&mut self, turn: Turn) {
        *self = match *self {
            Direction::North => {
                match turn {
                    Turn::Left => Direction::West,
                    Turn::Right => Direction::East,
                }
            }
            Direction::East => {
                match turn {
                    Turn::Left => Direction::North,
                    Turn::Right => Direction::South,
                }
            }
            Direction::South => {
                match turn {
                    Turn::Left => Direction::East,
                    Turn::Right => Direction::West,
                }
            }
            Direction::West => {
                match turn {
                    Turn::Left => Direction::South,
                    Turn::Right => Direction::North,
                }
            }
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum Turn {
    Left,
    Right,
}

#[derive(Debug, PartialEq, Eq)]
struct Instruction {
    turn: Turn,
    blocks: i16,
}

impl<'a> Instruction {
    fn try_many_from(s: &'a str) -> Result<Vec<Self>, &'a str> {
        s.split(", ").map(Self::try_from).collect()
    }
}

impl<'a> TryFrom<&'a str> for Instruction {
    type Err = &'a str;

    fn try_from(s: &'a str) -> Result<Self, &'a str> {
        let mut chars = s.chars();
        let turn = match chars.next() {
            Some(turn_char) => {
                match turn_char {
                    'L' => Turn::Left,
                    'R' => Turn::Right,
                    _ => return Err("Turn character invalid"),
                }
            }
            None => return Err("Instruction string is empty"),
        };
        let blocks = try!(chars.as_str().parse::<i16>().map_err(|_| "Could not parse blocks"));
        Ok(Instruction {
            turn: turn,
            blocks: blocks,
        })
    }
}

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

    #[test]
    fn test_parse_instruction_success() {
        let instruction = Instruction::try_from("L1").unwrap();
        assert_eq!(instruction,
                   Instruction {
                       turn: Turn::Left,
                       blocks: 1,
                   });
        let instruction = Instruction::try_from("R2").unwrap();
        assert_eq!(instruction,
                   Instruction {
                       turn: Turn::Right,
                       blocks: 2,
                   });
    }

    #[test]
    fn test_parse_instruction_invalid_turn_character() {
        let instruction = Instruction::try_from("S1");
        assert_eq!(instruction, Err("Turn character invalid"));
    }

    #[test]
    fn test_parse_instruction_empty_string() {
        let instruction = Instruction::try_from("");
        assert_eq!(instruction, Err("Instruction string is empty"));
    }

    #[test]
    fn test_parse_instruction_missing_blocks_digit() {
        let instruction = Instruction::try_from("L");
        assert_eq!(instruction, Err("Could not parse blocks"));
    }

    #[test]
    fn test_parse_instruction_invalid_blocks_digit() {
        let instruction = Instruction::try_from("LL");
        assert_eq!(instruction, Err("Could not parse blocks"));
    }

    #[test]
    fn test_parse_instructions() {
        let instructions = Instruction::try_many_from("L1, R2").unwrap();
        assert_eq!(instructions,
                   vec![Instruction {
                            turn: Turn::Left,
                            blocks: 1,
                        },
                        Instruction {
                            turn: Turn::Right,
                            blocks: 2,
                        }]);
    }

    // Assert that parsing failure returns the first error
    #[test]
    fn test_parse_instructions_with_error() {
        let instructions = Instruction::try_many_from("L1, , R2, S2");
        assert_eq!(instructions, Err("Instruction string is empty"));
    }

    #[test]
    fn test_blocks_away() {
        let blocks_away = blocks_away("L4, L1, L1");
        assert_eq!(blocks_away, 4);
    }
}

I'm particularly concerned with the implementation of day_01::Direction::turn. It's quite explicit and symmetric. Is there a more elegant way of implementing it – perhaps with a circular buffer of [North, East, South, West]?