Skip to content

Commit 24ea7ad

Browse files
committed
Add, solve Day 11
This was a fun one; I hadn't thought much about hex coordinate systems in the past, but it was good to see how they worked. I'm also pretty pleased with the efficiency of the solution I came up with; 18 ms isn't bad when the work is doubled! ... though I'm probably going to go back and fix that now, because it really isn't ideal to do everything twice when there's an obvious single-pass solution. It's just that I went to the trouble to implement iter::Sum for HexPosition, so I wanted to at least use that, even though after Part 2 it's smarter not to. $ cargo check && cargo build --release && time target/release/day11 Compiling day11 v0.1.0 (file:///mnt/d/Users/coriolinus/Documents/Projects/adventofcode.com/adventofcode-2017/day11) Finished dev [unoptimized + debuginfo] target(s) in 0.64 secs Compiling util v0.2.0 (file:///mnt/d/Users/coriolinus/Documents/Projects/adventofcode.com/adventofcode-2017/util) Compiling day11 v0.1.0 (file:///mnt/d/Users/coriolinus/Documents/Projects/adventofcode.com/adventofcode-2017/day11) Finished release [optimized] target(s) in 4.74 secs Dist to origin: 720 Max dist to origin: 1485 real 0m0.018s user 0m0.000s sys 0m0.016s $ wc -c input.txt 21533 input.txt
1 parent 185fc13 commit 24ea7ad

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed

day11/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "day11"
3+
version = "0.1.0"
4+
authors = ["Peter Goodspeed-Niklaus <[email protected]>"]
5+
6+
[dependencies]
7+
util = { path = "../util" }

day11/src/lib.rs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
use std::ascii::AsciiExt;
2+
use std::iter::Sum;
3+
use std::ops::Add;
4+
use std::str::FromStr;
5+
6+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7+
pub enum HexDirection {
8+
N,
9+
Ne,
10+
Nw,
11+
S,
12+
Se,
13+
Sw,
14+
}
15+
16+
impl FromStr for HexDirection {
17+
type Err = &'static str;
18+
fn from_str(s: &str) -> Result<HexDirection, Self::Err> {
19+
match &*s.to_ascii_lowercase() {
20+
"n" => Ok(HexDirection::N),
21+
"ne" => Ok(HexDirection::Ne),
22+
"nw" => Ok(HexDirection::Nw),
23+
"s" => Ok(HexDirection::S),
24+
"se" => Ok(HexDirection::Se),
25+
"sw" => Ok(HexDirection::Sw),
26+
_ => Err("Could not parse as HexDirection"),
27+
}
28+
}
29+
}
30+
31+
32+
/// Track a location in a hex grid
33+
///
34+
/// Every cell in a hex grid can be uniquely identified by a pair
35+
/// of coordinates in a two-axis system; there are at least four
36+
/// different types of two-axis systems available. However, it's
37+
/// more efficient to use a cubical system, so that's what we do here.
38+
///
39+
/// Cubical coordinate systems have a unique address for every hex
40+
/// if constrained such that the sum of axis values always equals 0.
41+
///
42+
/// See https://www.redblobgames.com/grids/hexagons/
43+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44+
pub struct HexPosition {
45+
east: isize,
46+
northwest: isize,
47+
southwest: isize,
48+
}
49+
50+
impl HexPosition {
51+
pub fn new() -> HexPosition {
52+
HexPosition {
53+
east: 0,
54+
northwest: 0,
55+
southwest: 0,
56+
}
57+
}
58+
59+
pub fn step(&self, direction: HexDirection) -> HexPosition {
60+
use HexDirection::*;
61+
match direction {
62+
N => {
63+
HexPosition {
64+
northwest: self.northwest + 1,
65+
southwest: self.southwest - 1,
66+
..*self
67+
}
68+
}
69+
S => {
70+
HexPosition {
71+
northwest: self.northwest - 1,
72+
southwest: self.southwest + 1,
73+
..*self
74+
}
75+
}
76+
Ne => {
77+
HexPosition {
78+
east: self.east + 1,
79+
southwest: self.southwest - 1,
80+
..*self
81+
}
82+
}
83+
Sw => {
84+
HexPosition {
85+
east: self.east - 1,
86+
southwest: self.southwest + 1,
87+
..*self
88+
}
89+
}
90+
Nw => {
91+
HexPosition {
92+
east: self.east - 1,
93+
northwest: self.northwest + 1,
94+
..*self
95+
}
96+
}
97+
Se => {
98+
HexPosition {
99+
east: self.east + 1,
100+
northwest: self.northwest - 1,
101+
..*self
102+
}
103+
}
104+
}
105+
}
106+
107+
/// Generate all possible axial coordintes from this position.
108+
///
109+
/// Axial coordinates are generated from cubical coordinates
110+
/// by dropping one of the cubical axes.
111+
///
112+
/// As there are three axes, there are three potential axial
113+
/// coordinate systems. This function drops axes in the sequence
114+
/// `east`, `northwest`, `southwest`. Therefore, the output represents
115+
/// the axial systems:
116+
///
117+
/// 1. `(northwest, southwest)`
118+
/// 2. `(east, southwest)`
119+
/// 3. `(east, northwest)`
120+
pub fn to_axial(&self) -> [(isize, isize); 3] {
121+
[
122+
(self.northwest, self.southwest),
123+
(self.east, self.southwest),
124+
(self.east, self.northwest),
125+
]
126+
}
127+
128+
/// Find the minimum number of steps required to navigate to the origin.
129+
///
130+
/// From any point in a hex grid, it's possible to navigate to the origin
131+
/// by repeatedly moving in at most two directions. Therefore, the minimal
132+
/// number of steps to the origin must always be the sum of the absolute values
133+
/// of the axes in one of the possible axial systems.
134+
pub fn min_steps_to_origin(&self) -> isize {
135+
self.to_axial()
136+
.iter()
137+
.map(|&(left, right)| left.abs() + right.abs())
138+
.min()
139+
// unwrap is safe because we know that to_axial always returns three values
140+
.unwrap()
141+
}
142+
}
143+
144+
impl Add<HexDirection> for HexPosition {
145+
type Output = HexPosition;
146+
fn add(self, other: HexDirection) -> HexPosition {
147+
self.step(other)
148+
}
149+
}
150+
151+
impl<'a> Add<&'a HexDirection> for HexPosition {
152+
type Output = HexPosition;
153+
fn add(self, other: &'a HexDirection) -> HexPosition {
154+
self.step(*other)
155+
}
156+
}
157+
158+
impl Sum<HexDirection> for HexPosition {
159+
fn sum<I>(iter: I) -> Self
160+
where
161+
I: Iterator<Item = HexDirection>,
162+
{
163+
let mut position = HexPosition::new();
164+
for direction in iter {
165+
position = position + direction;
166+
}
167+
position
168+
}
169+
}
170+
171+
impl<'a> Sum<&'a HexDirection> for HexPosition {
172+
fn sum<I>(iter: I) -> Self
173+
where
174+
I: Iterator<Item = &'a HexDirection>,
175+
{
176+
let mut position = HexPosition::new();
177+
for direction in iter {
178+
position = position + direction;
179+
}
180+
position
181+
}
182+
}

day11/src/main.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::cmp::max;
2+
3+
extern crate day11;
4+
use day11::{HexPosition, HexDirection};
5+
6+
extern crate util;
7+
use util::file_as_by;
8+
9+
fn main() {
10+
for directions in file_as_by::<HexDirection, _>("input.txt", |line| {
11+
line.split(',')
12+
.filter(|ref token| !token.is_empty())
13+
.collect()
14+
}).expect("Problem parsing input")
15+
{
16+
let position: HexPosition = directions.iter().sum();
17+
println!("Dist to origin: {}", position.min_steps_to_origin());
18+
19+
let mut position = HexPosition::new();
20+
let mut max_dist = 0;
21+
for direction in directions.iter() {
22+
position = position + direction;
23+
max_dist = max(max_dist, position.min_steps_to_origin());
24+
}
25+
println!("Max dist to origin: {}", max_dist);
26+
}
27+
}

0 commit comments

Comments
 (0)