diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/day-12/Cargo.lock b/day-12/Cargo.lock new file mode 100644 index 0000000..b7e1235 --- /dev/null +++ b/day-12/Cargo.lock @@ -0,0 +1,78 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "day-12" +version = "0.1.0" +dependencies = [ + "indoc", +] + +[[package]] +name = "indoc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" diff --git a/day-12/Cargo.toml b/day-12/Cargo.toml new file mode 100644 index 0000000..35cad43 --- /dev/null +++ b/day-12/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day-12" +version = "0.1.0" +authors = ["Tristan Daniƫl Maat "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indoc = "0.3" diff --git a/day-12/input b/day-12/input new file mode 100644 index 0000000..bedc29a --- /dev/null +++ b/day-12/input @@ -0,0 +1,771 @@ +F8 +N2 +F32 +F17 +E4 +N4 +R90 +S2 +R90 +E3 +L90 +N5 +E2 +N2 +W5 +F78 +L180 +F19 +R90 +S1 +E2 +L180 +E1 +S5 +E4 +F62 +R180 +F16 +S2 +F8 +R180 +S1 +L90 +E4 +R90 +S3 +E5 +R180 +F87 +N2 +E2 +R90 +N2 +F2 +R90 +N5 +W4 +L90 +F42 +N1 +F93 +F87 +E2 +S4 +F73 +L270 +S2 +W3 +F48 +W5 +L180 +N1 +F53 +R90 +S2 +R90 +N2 +E2 +S5 +W3 +R90 +E2 +R90 +W1 +L180 +F29 +W1 +F56 +R90 +F34 +F74 +S1 +R90 +L90 +W4 +L90 +W5 +L90 +W1 +L90 +N5 +E2 +S2 +F58 +N5 +L90 +S4 +L90 +R270 +W4 +S4 +E3 +R180 +S4 +W3 +R90 +F36 +R90 +W1 +F73 +S4 +E1 +L90 +S4 +W5 +L90 +F20 +W3 +L180 +E3 +S1 +R90 +S5 +W3 +L90 +E5 +W2 +F21 +N4 +F83 +W4 +F48 +W3 +F4 +L90 +N5 +R270 +E1 +S5 +L180 +F44 +W5 +R180 +S3 +F30 +N5 +F87 +L90 +F69 +S5 +E1 +R90 +E2 +S3 +F40 +W4 +F97 +W5 +F20 +L180 +N5 +L90 +E5 +N3 +L90 +F13 +N2 +F38 +S5 +F27 +E5 +L180 +F59 +N3 +F2 +R90 +N2 +R90 +F56 +L90 +N4 +R90 +F12 +F34 +N3 +F93 +L270 +W3 +F74 +W4 +R90 +E2 +L180 +W3 +F12 +N5 +W1 +F98 +E4 +R180 +S1 +W5 +R90 +F96 +N2 +L90 +F36 +S1 +F3 +W3 +F100 +N5 +R90 +F33 +W3 +N5 +E3 +R90 +F33 +N5 +E2 +N1 +L90 +F84 +L270 +E1 +F28 +R180 +W3 +L90 +S2 +F88 +L90 +W2 +N1 +F3 +R90 +F56 +N1 +N4 +L90 +R90 +F97 +E5 +N4 +F38 +N1 +R90 +W1 +F60 +W3 +N1 +F59 +E1 +N3 +E3 +L180 +N1 +F53 +S1 +E2 +R90 +E2 +F6 +R180 +F36 +R180 +W2 +F81 +R90 +E4 +R90 +F97 +L90 +W1 +S1 +E5 +L180 +F34 +R180 +F64 +E2 +R180 +W3 +S5 +L90 +E4 +F12 +F58 +W3 +N3 +F77 +N4 +F32 +R90 +N2 +E4 +L90 +S5 +E1 +N5 +F44 +R90 +F5 +E4 +R90 +N5 +E4 +R180 +W3 +L90 +N1 +F1 +S3 +E5 +R180 +S3 +F86 +S5 +F61 +W3 +R270 +W5 +R90 +F26 +R180 +F92 +S5 +L90 +E5 +N5 +F82 +R90 +F22 +R90 +F23 +S1 +F42 +N4 +F76 +E1 +S1 +W3 +S2 +L90 +F19 +E4 +F41 +E2 +N2 +L90 +F34 +S4 +F20 +W3 +F18 +S1 +R90 +N3 +F38 +W3 +R90 +W4 +R90 +E2 +R90 +F10 +L90 +N4 +F94 +S1 +W3 +R180 +W5 +F74 +R90 +S4 +L180 +S3 +F74 +N5 +S4 +L90 +F34 +S2 +E5 +N5 +F28 +L90 +E1 +F31 +N1 +L90 +L90 +W2 +N5 +R90 +F1 +N5 +F48 +W2 +F50 +N2 +F62 +S4 +L90 +W5 +N1 +F12 +W3 +R90 +R90 +F75 +N5 +F69 +E3 +F19 +N2 +F77 +E1 +N4 +R180 +E3 +N2 +L90 +N1 +W1 +S4 +F85 +W1 +R90 +F74 +E5 +F73 +E4 +S3 +W4 +S5 +L90 +F49 +S5 +E5 +F5 +W2 +F58 +R90 +W5 +F53 +S4 +F86 +N2 +F88 +E5 +F59 +E1 +F56 +W2 +N4 +W4 +R180 +F16 +F25 +R180 +N3 +F4 +W4 +S4 +F98 +E5 +L90 +W4 +S1 +E2 +R90 +F96 +L270 +E1 +N1 +F55 +S1 +F10 +R90 +W2 +L90 +N5 +R90 +N4 +E4 +L90 +F52 +S3 +F43 +E2 +R90 +S3 +R90 +N4 +E1 +N4 +F15 +E3 +R270 +L180 +N2 +F43 +L90 +W2 +F19 +L90 +S5 +F58 +E4 +S4 +L90 +W1 +F9 +N4 +F38 +S5 +L90 +W1 +F39 +W5 +F83 +L180 +F99 +L90 +E3 +S2 +R90 +N3 +F35 +N1 +N3 +L90 +N4 +W5 +F26 +R270 +N2 +F7 +N1 +F16 +S4 +L90 +S5 +L180 +F5 +W1 +F32 +S2 +N3 +F82 +N4 +R90 +F27 +R180 +F20 +S1 +E3 +L90 +W3 +F23 +L180 +N3 +F34 +W1 +N3 +S2 +F80 +E5 +F65 +L90 +E5 +N1 +F80 +R90 +W3 +L90 +N1 +L180 +S1 +F65 +E3 +S1 +W3 +F89 +S1 +F24 +E5 +F85 +W1 +F87 +S1 +R90 +S4 +F3 +S3 +F23 +N4 +L90 +N5 +R90 +N2 +R90 +S2 +W4 +S2 +F95 +L90 +F52 +W1 +N5 +L90 +N4 +S3 +E3 +R90 +N2 +E1 +R180 +W4 +F82 +L180 +E5 +L90 +E4 +F65 +W5 +R90 +W5 +N5 +L180 +N4 +F22 +W3 +S4 +F60 +R90 +E5 +N3 +F32 +S2 +F80 +R90 +F18 +S3 +L90 +F90 +E3 +L90 +N3 +E5 +F79 +N5 +W4 +S5 +F100 +N1 +E3 +S3 +F49 +R180 +S3 +E2 +F1 +W1 +F5 +R180 +S5 +W3 +S3 +F67 +R270 +N3 +W3 +N1 +W3 +F37 +L90 +N3 +L90 +F68 +N3 +W4 +W2 +F26 +N3 +L90 +W3 +S2 +F7 +W3 +E3 +L270 +F64 +R90 +E4 +R90 +W3 +N1 +W1 +F98 +R270 +W5 +F45 +R90 +F49 +E4 +S2 +F58 +F56 +W3 +F57 +E3 +S5 +R180 +E3 +F82 +F57 +S3 +W2 +R90 +E2 +R90 +F95 +W4 +F85 +E3 +N3 +R90 +E5 +F31 +R90 +F20 +R90 +N5 +E3 +S4 +R180 +W1 +N5 +F72 +L90 +E3 +F46 +R180 +F18 +E3 +F48 +S2 +F84 +W3 +F88 +F44 +S2 +E4 +F77 +L90 +N4 +L90 +E2 +F22 +E5 +L90 +F79 +W1 +R90 +F41 +R180 +F54 diff --git a/day-12/src/main.rs b/day-12/src/main.rs new file mode 100644 index 0000000..6212c23 --- /dev/null +++ b/day-12/src/main.rs @@ -0,0 +1,225 @@ +use std::fs; + +fn main() -> Result<(), Box> { + let input = fs::read_to_string("input")?; + let actions = parse_actions(&input)?; + + // Part 1 + let location = simulate_ship_movement(&actions); + println!("{}", location.0.abs() + location.1.abs()); + + // Part 2 + let location = simulate_waypoint_movement(&actions); + println!("{}", location.0.abs() + location.1.abs()); + + Ok(()) +} + +#[derive(Copy, Clone, Debug)] +enum Action { + North(u32), + South(u32), + East(u32), + West(u32), + Left(u32), + Right(u32), + Forward(u32), +} + +impl Action { + fn new(input: &str) -> Result { + if input.len() < 2 { + Err(format!("Invalud input line: {}", input))?; + } + + let (action, value) = input.split_at(1); + let value = value + .parse() + .map_err(|e| format!("Could not parse value: {}", e))?; + + Ok(match action { + "N" => Self::North(value), + "S" => Self::South(value), + "E" => Self::East(value), + "W" => Self::West(value), + "L" => Self::Left(value), + "R" => Self::Right(value), + "F" => Self::Forward(value), + _ => Err(format!("Could not parse action: {}", action))?, + }) + } + + fn opposite_direction(self) -> Self { + match self { + Action::North(x) => Action::South(x), + Action::South(x) => Action::North(x), + Action::East(x) => Action::West(x), + Action::West(x) => Action::East(x), + _ => panic!("Invalid direction"), + } + } + + fn right_direction(self) -> Self { + match self { + Action::North(x) => Action::East(x), + Action::South(x) => Action::West(x), + Action::East(x) => Action::South(x), + Action::West(x) => Action::North(x), + _ => panic!("Invalid direction"), + } + } + + fn left_direction(self) -> Self { + match self { + Action::North(x) => Action::West(x), + Action::South(x) => Action::East(x), + Action::East(x) => Action::North(x), + Action::West(x) => Action::South(x), + _ => panic!("Invalid direction"), + } + } +} + +fn parse_actions(input: &str) -> Result, String> { + input.lines().map(Action::new).collect() +} + +fn simulate_ship_movement(actions: &Vec) -> (i64, i64) { + let mut coords: (i64, i64) = (0, 0); + let mut direction = Action::East(0); + + for action in actions { + // If the action is Action::Forward, we instead want to move + // in a direction determined by our current angle. + // + // So we convert Action::Forward to a concrete direction. + + let converted_action = if let &Action::Forward(amount) = action { + match direction { + Action::North(_) => Action::North(amount), + Action::South(_) => Action::South(amount), + Action::East(_) => Action::East(amount), + Action::West(_) => Action::West(amount), + _ => unreachable!("Direction can only be NSEW."), + } + } else { + *action + }; + + match converted_action { + Action::North(amount) => { + coords.1 += amount as i64; + } + Action::South(amount) => { + coords.1 -= amount as i64; + } + Action::East(amount) => { + coords.0 += amount as i64; + } + Action::West(amount) => { + coords.0 -= amount as i64; + } + Action::Left(amount) => match amount % 360 { + 0 => direction = direction, + 90 => direction = direction.left_direction(), + 180 => direction = direction.opposite_direction(), + 270 => direction = direction.right_direction(), + _ => panic!("Only 90 degree turns allowed"), + }, + Action::Right(amount) => match amount % 360 { + 0 => direction = direction, + 90 => direction = direction.right_direction(), + 180 => direction = direction.opposite_direction(), + 270 => direction = direction.left_direction(), + _ => panic!("Only 90 degree turns allowed"), + }, + Action::Forward(_) => unreachable!("Converted previously"), + } + } + + coords +} + +fn simulate_waypoint_movement(actions: &Vec) -> (i64, i64) { + let mut waypoint = (1, 10); + let mut ship = (0, 0); + + for action in actions { + match *action { + Action::North(amount) => { + waypoint.0 += amount as i64; + } + Action::South(amount) => { + waypoint.0 -= amount as i64; + } + Action::East(amount) => { + waypoint.1 += amount as i64; + } + Action::West(amount) => { + waypoint.1 -= amount as i64; + } + Action::Left(amount) => match amount % 360 { + 0 => {} + 90 => waypoint = (waypoint.1, -waypoint.0), + 180 => waypoint = (-waypoint.0, -waypoint.1), + 270 => waypoint = (-waypoint.1, waypoint.0), + _ => panic!("Only 90 degree turns allowed"), + }, + Action::Right(amount) => match amount % 360 { + 0 => {} + 90 => waypoint = (-waypoint.1, waypoint.0), + 180 => waypoint = (-waypoint.0, -waypoint.1), + 270 => waypoint = (waypoint.1, -waypoint.0), + _ => panic!("Only 90 degree turns allowed"), + }, + Action::Forward(amount) => { + ship.0 += waypoint.0 * amount as i64; + ship.1 += waypoint.1 * amount as i64; + } + } + } + + ship +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + + #[test] + fn test_simple() -> Result<(), Box> { + let input = indoc!( + "F10 + N3 + F7 + R90 + F11 + " + ); + + let actions = parse_actions(&input)?; + let location = simulate_ship_movement(&actions); + assert_eq!(location.0.abs() + location.1.abs(), 25); + + Ok(()) + } + + #[test] + fn test_simple2() -> Result<(), Box> { + let input = indoc!( + "F10 + N3 + F7 + R90 + F11 + " + ); + + let actions = parse_actions(&input)?; + let location = simulate_waypoint_movement(&actions); + assert_eq!(location.0.abs() + location.1.abs(), 286); + + Ok(()) + } +}