366 lines
9.8 KiB
Rust
366 lines
9.8 KiB
Rust
use std::{
|
|
error::Error,
|
|
fmt::{self, Formatter, Display},
|
|
io::{self, Read},
|
|
mem,
|
|
str::FromStr,
|
|
};
|
|
|
|
type Result<T> = std::result::Result<T, Box<dyn Error>>;
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct ErrorMessage(String);
|
|
|
|
impl From<String> for ErrorMessage {
|
|
fn from(other: String) -> Self {
|
|
ErrorMessage(other)
|
|
}
|
|
}
|
|
|
|
impl ErrorMessage {
|
|
fn message(&self) -> &String {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl Display for ErrorMessage {
|
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
|
write!(fmt, "{}", self.message())
|
|
}
|
|
}
|
|
|
|
impl Error for ErrorMessage {
|
|
fn description(&self) -> &str {
|
|
self.message()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum Wire {
|
|
Up(usize),
|
|
Down(usize),
|
|
Left(usize),
|
|
Right(usize),
|
|
}
|
|
|
|
impl Wire {
|
|
fn magnitude(&self) -> usize {
|
|
match self {
|
|
Wire::Up(m)
|
|
| Wire::Down(m)
|
|
| Wire::Left(m)
|
|
| Wire::Right(m) => *m
|
|
}
|
|
}
|
|
|
|
fn add_pos(&self, (x, y): (isize, isize)) -> (isize, isize) {
|
|
let (x0, y0) = match self {
|
|
Wire::Up(y0) => (0, -(*y0 as isize)),
|
|
Wire::Down(y0) => (0, *y0 as isize),
|
|
Wire::Left(x0) => (-(*x0 as isize), 0),
|
|
Wire::Right(x0) => (*x0 as isize, 0),
|
|
};
|
|
(x + x0, y + y0)
|
|
}
|
|
}
|
|
|
|
impl FromStr for Wire {
|
|
type Err = ErrorMessage;
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
let num = s[1..].parse::<usize>()
|
|
.map_err(|e| ErrorMessage(e.description().to_string()))?;
|
|
let c = s.chars().nth(0)
|
|
.ok_or_else(|| ErrorMessage("expected U, D, L, or R followed by a number".to_string()))?;
|
|
match c {
|
|
'U' => Ok(Wire::Up(num)),
|
|
'D' => Ok(Wire::Down(num)),
|
|
'L' => Ok(Wire::Left(num)),
|
|
'R' => Ok(Wire::Right(num)),
|
|
_ => Err(ErrorMessage("expected U, D, L, or R followed by a number".to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
struct Cell {
|
|
pass: usize,
|
|
steps: [usize; 2],
|
|
}
|
|
|
|
const FIRST_PASS: usize = 1;
|
|
const SECOND_PASS: usize = 2;
|
|
const BOTH_PASS: usize = FIRST_PASS | SECOND_PASS;
|
|
|
|
#[derive(Debug)]
|
|
struct Board {
|
|
board: Vec<Vec<Cell>>,
|
|
pos: (isize, isize),
|
|
origin: (isize, isize),
|
|
steps: usize,
|
|
}
|
|
|
|
impl Default for Board {
|
|
fn default() -> Self {
|
|
Board {
|
|
board: vec![vec![Default::default()]],
|
|
pos: Default::default(),
|
|
origin: Default::default(),
|
|
steps: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for Board {
|
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
|
for (y, row) in self.board.iter().enumerate() {
|
|
let y = y as isize;
|
|
for (x, col) in row.iter().enumerate() {
|
|
let x = x as isize;
|
|
let c = if (x, y) == self.origin {
|
|
'o'
|
|
} else if (x, y) == self.pos {
|
|
'*'
|
|
} else if col.pass > 0 {
|
|
'+'
|
|
} else {
|
|
'.'
|
|
};
|
|
write!(fmt, "{}", c)?;
|
|
}
|
|
writeln!(fmt)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Board {
|
|
fn x(&self) -> isize {
|
|
self.pos.0
|
|
}
|
|
|
|
fn y(&self) -> isize {
|
|
self.pos.1
|
|
}
|
|
|
|
fn origin_x(&self) -> isize {
|
|
self.origin.0
|
|
}
|
|
|
|
fn origin_y(&self) -> isize {
|
|
self.origin.1
|
|
}
|
|
|
|
fn min_x(&self) -> isize {
|
|
-self.origin_x()
|
|
}
|
|
|
|
fn min_y(&self) -> isize {
|
|
-self.origin_y()
|
|
}
|
|
|
|
fn max_x(&self) -> isize {
|
|
self.board_w() - self.origin_x()
|
|
}
|
|
|
|
fn max_y(&self) -> isize {
|
|
self.board_h() - self.origin_y()
|
|
}
|
|
|
|
fn board_w(&self) -> isize {
|
|
if self.board.is_empty() {
|
|
0
|
|
} else {
|
|
self.board[0].len() as isize
|
|
}
|
|
}
|
|
|
|
fn board_h(&self) -> isize {
|
|
self.board.len() as isize
|
|
}
|
|
|
|
fn ensure_pos(&mut self, (pos_x, pos_y): (isize, isize)) {
|
|
self.ensure_y_pos(pos_y);
|
|
self.ensure_x_pos(pos_x);
|
|
}
|
|
|
|
fn ensure_x_pos(&mut self, pos_x: isize) {
|
|
if pos_x >= self.max_x() {
|
|
let amount = pos_x - self.max_x() + 1;
|
|
self.grow_right(amount as usize);
|
|
} else if pos_x <= self.min_x() {
|
|
let amount = (self.min_x() - pos_x).abs() + 1;
|
|
self.grow_left(amount as usize);
|
|
}
|
|
}
|
|
|
|
fn ensure_y_pos(&mut self, pos_y: isize) {
|
|
if pos_y >= self.max_y() {
|
|
let amount = pos_y - self.max_y() + 1;
|
|
self.grow_down(amount as usize);
|
|
} else if pos_y <= self.min_y() {
|
|
let amount = (self.min_y() - pos_y).abs() + 1;
|
|
self.grow_up(amount as usize);
|
|
}
|
|
}
|
|
|
|
fn grow_down(&mut self, amount: usize) {
|
|
let rows = vec!(vec!(Default::default(); self.board_w() as usize); amount);
|
|
self.board.extend(rows);
|
|
}
|
|
|
|
fn grow_up(&mut self, amount: usize) {
|
|
let w = self.board_w();
|
|
let rows = mem::replace(&mut self.board, vec!(vec!(Default::default(); w as usize); amount));
|
|
self.board.extend(rows);
|
|
self.origin.1 += amount as isize;
|
|
}
|
|
|
|
fn grow_left(&mut self, amount: usize) {
|
|
for row in self.board.iter_mut() {
|
|
let row_remainder = mem::replace(row, vec!(Default::default(); amount));
|
|
row.extend(row_remainder);
|
|
}
|
|
self.origin.0 += amount as isize;
|
|
}
|
|
|
|
fn grow_right(&mut self, amount: usize) {
|
|
for row in self.board.iter_mut() {
|
|
row.extend(vec!(Cell::default(); amount));
|
|
}
|
|
}
|
|
|
|
fn apply_wire(&mut self, wire: Wire, which_pass: usize) -> Vec<(Cell, isize, isize)> {
|
|
let end_pos = wire.add_pos(self.pos);
|
|
self.ensure_pos(end_pos);
|
|
let x = (self.x() + self.origin_x()) as usize;
|
|
let y = (self.y() + self.origin_y()) as usize;
|
|
|
|
/*
|
|
println!("applying wire {:?}", wire);
|
|
println!("pos: {:?}", self.pos);
|
|
println!("end pos: {:?}", end_pos);
|
|
println!("origin: {:?}", self.origin);
|
|
println!("start pos: {:?}", (x, y));
|
|
println!("size: {:?}", self.board_size());
|
|
*/
|
|
|
|
let mut crossings = Vec::new();
|
|
let step_index = which_pass - 1;
|
|
match wire {
|
|
Wire::Up(amount) => for offset in 0..amount {
|
|
let y0 = y - offset;
|
|
self.board[y0][x].pass |= which_pass;
|
|
|
|
if self.board[y0][x].steps[step_index] == 0 {
|
|
self.board[y0][x].steps[step_index] = self.steps + offset;
|
|
}
|
|
|
|
if self.board[y0][x].pass == BOTH_PASS {
|
|
let cell = self.board[y0][x];
|
|
crossings.push((cell, self.x(), self.y() - (offset as isize)));
|
|
}
|
|
}
|
|
Wire::Down(amount) => for offset in 0..amount {
|
|
let y0 = y + offset;
|
|
self.board[y0][x].pass |= which_pass;
|
|
|
|
if self.board[y0][x].steps[step_index] == 0 {
|
|
self.board[y0][x].steps[step_index] = self.steps + offset;
|
|
}
|
|
|
|
if self.board[y0][x].pass == BOTH_PASS {
|
|
let cell = self.board[y0][x];
|
|
crossings.push((cell, self.x(), self.y() + (offset as isize)));
|
|
}
|
|
}
|
|
Wire::Left(amount) => for offset in 0..amount {
|
|
let x0 = x - offset;
|
|
self.board[y][x0].pass |= which_pass;
|
|
|
|
if self.board[y][x0].steps[step_index] == 0 {
|
|
self.board[y][x0].steps[step_index] = self.steps + offset;
|
|
}
|
|
|
|
if self.board[y][x0].pass == BOTH_PASS {
|
|
let cell = self.board[y][x0];
|
|
crossings.push((cell, self.x() - (offset as isize), self.y()));
|
|
}
|
|
}
|
|
Wire::Right(amount) => for offset in 0..amount {
|
|
let x0 = x + offset;
|
|
self.board[y][x0].pass |= which_pass;
|
|
|
|
if self.board[y][x0].steps[step_index] == 0 {
|
|
self.board[y][x0].steps[step_index] = self.steps + offset;
|
|
}
|
|
|
|
if self.board[y][x0].pass == BOTH_PASS {
|
|
let cell = self.board[y][x0];
|
|
crossings.push((cell, self.x() + (offset as isize), self.y()));
|
|
}
|
|
}
|
|
}
|
|
self.steps += wire.magnitude();
|
|
self.pos = end_pos;
|
|
crossings.retain(|(_, x, y)| (*x, *y) != (0, 0));
|
|
crossings
|
|
}
|
|
|
|
fn reset_pos(&mut self) {
|
|
self.steps = 0;
|
|
self.pos = (0, 0);
|
|
}
|
|
}
|
|
|
|
fn get_input_string() -> io::Result<String> {
|
|
let mut buffer = String::new();
|
|
io::stdin().read_to_string(&mut buffer)?;
|
|
Ok(buffer)
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
let input = get_input_string()?;
|
|
let mut lines = input
|
|
.trim()
|
|
.lines()
|
|
.map(|line| line.trim()
|
|
.split(",")
|
|
.map(|wire| wire.parse::<Wire>().map_err(From::from))
|
|
.collect::<Result<Vec<_>>>());
|
|
let wires1 = lines.next()
|
|
.expect("two lines of input")?;
|
|
let wires2 = lines.next()
|
|
.expect("two lines of input")?;
|
|
|
|
day3(&wires1, &wires2);
|
|
Ok(())
|
|
}
|
|
|
|
fn day3(wires1: &Vec<Wire>, wires2: &Vec<Wire>) {
|
|
let mut board = Board::default();
|
|
|
|
for wire in wires1.iter() {
|
|
board.apply_wire(*wire, FIRST_PASS);
|
|
}
|
|
board.reset_pos();
|
|
|
|
let mut crossings = Vec::new();
|
|
for wire in wires2.iter() {
|
|
crossings.extend(board.apply_wire(*wire, SECOND_PASS));
|
|
}
|
|
//println!("{}", board);
|
|
//println!("{:#?}", crossings);
|
|
let shortest: isize = crossings.iter()
|
|
.map(|(_, x, y)| x.abs() + y.abs())
|
|
.min()
|
|
.unwrap();
|
|
println!("Part 1: {}", shortest);
|
|
|
|
let fewest: usize = crossings.iter()
|
|
.map(|(cell, _, _)| cell.steps[0] + cell.steps[1])
|
|
.min()
|
|
.unwrap();
|
|
println!("Part 2: {}", fewest);
|
|
}
|