Work on day22
This commit is contained in:
parent
db7cfc6b49
commit
9af717658a
202
day22/input
Normal file
202
day22/input
Normal file
File diff suppressed because one or more lines are too long
14
day22/input.test
Normal file
14
day22/input.test
Normal file
|
@ -0,0 +1,14 @@
|
|||
...#
|
||||
.#..
|
||||
#...
|
||||
....
|
||||
...#.......#
|
||||
........#...
|
||||
..#....#....
|
||||
..........#.
|
||||
...#....
|
||||
.....#..
|
||||
.#......
|
||||
......#.
|
||||
|
||||
10R5L5R10L4R5L5
|
|
@ -1,3 +1,62 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
|
||||
use map::Map;
|
||||
|
||||
mod map;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
enum SolvePuzzle {
|
||||
First,
|
||||
#[default]
|
||||
Second,
|
||||
}
|
||||
|
||||
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
|
||||
let map = Map::parse(reader);
|
||||
map.display();
|
||||
map.crack_password()
|
||||
}
|
||||
|
||||
fn solve_second_puzzle<R: BufRead>(reader: R) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = ::std::env::args().skip(1);
|
||||
let file = args.next().unwrap_or_else(|| String::from("./input"));
|
||||
let solve = args
|
||||
.next()
|
||||
.map(|arg| match arg.as_str() {
|
||||
"first" => SolvePuzzle::First,
|
||||
"second" => SolvePuzzle::Second,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let file = BufReader::new(File::open(file).expect("Opening file"));
|
||||
match solve {
|
||||
SolvePuzzle::First => println!("{}", solve_first_puzzle(file)),
|
||||
SolvePuzzle::Second => println!("{}", solve_second_puzzle(file)),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn example_on_first() {
|
||||
let reader = Cursor::new(include_str!("../input.test"));
|
||||
assert_eq!(solve_first_puzzle(reader), 152);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example_on_second() {
|
||||
let reader = Cursor::new(include_str!("../input.test"));
|
||||
assert_eq!(solve_second_puzzle(reader), 301);
|
||||
}
|
||||
}
|
||||
|
|
220
day22/src/map.rs
Normal file
220
day22/src/map.rs
Normal file
|
@ -0,0 +1,220 @@
|
|||
use std::{io::BufRead, ops::Index};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Facing {
|
||||
North,
|
||||
East,
|
||||
South,
|
||||
West,
|
||||
}
|
||||
impl Facing {
|
||||
fn turn_left(&mut self) {
|
||||
*self = match self {
|
||||
Facing::North => Facing::West,
|
||||
Facing::East => Facing::North,
|
||||
Facing::South => Facing::East,
|
||||
Facing::West => Facing::South,
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_right(&mut self) {
|
||||
*self = match self {
|
||||
Facing::North => Facing::East,
|
||||
Facing::East => Facing::South,
|
||||
Facing::South => Facing::West,
|
||||
Facing::West => Facing::North,
|
||||
}
|
||||
}
|
||||
|
||||
fn opposite(&self) -> Self {
|
||||
match self {
|
||||
Facing::North => Facing::South,
|
||||
Facing::East => Facing::West,
|
||||
Facing::South => Facing::North,
|
||||
Facing::West => Facing::East,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_number(&self) -> usize {
|
||||
match self {
|
||||
Facing::North => 3,
|
||||
Facing::East => 0,
|
||||
Facing::South => 1,
|
||||
Facing::West => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub enum Tile {
|
||||
#[default]
|
||||
Void,
|
||||
Open,
|
||||
Solid,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
fn from_byte(byte: u8) -> Self {
|
||||
match byte {
|
||||
b' ' => Self::Void,
|
||||
b'.' => Self::Open,
|
||||
b'#' => Self::Solid,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Action {
|
||||
Move(usize),
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
fn from_line(line: String) -> Vec<Self> {
|
||||
line.split_inclusive(&['L', 'R'])
|
||||
.flat_map(|matched| {
|
||||
if matched.len() == 1 {
|
||||
// Only a single char
|
||||
vec![matched]
|
||||
} else {
|
||||
// Split last char
|
||||
let (first, second) = matched.split_at(matched.len() - 1);
|
||||
vec![first, second]
|
||||
}
|
||||
})
|
||||
.map(Self::parse_range)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_range<S: AsRef<str>>(s: S) -> Self {
|
||||
println!("RANGE: {}", s.as_ref());
|
||||
match s.as_ref() {
|
||||
"L" => Self::Left,
|
||||
"R" => Self::Right,
|
||||
number => Self::Move(number.parse().expect("Is a number: {}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Map {
|
||||
tiles: Vec<Tile>,
|
||||
width: usize,
|
||||
actions: Vec<Action>,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
pub fn parse<R: BufRead>(reader: R) -> Self {
|
||||
let mut lines: Vec<String> = reader.lines().map(Result::unwrap).collect();
|
||||
let instructions = lines.pop().expect("Instructions on the last line");
|
||||
let actions = Action::from_line(instructions);
|
||||
let _blank = lines.pop();
|
||||
let width = lines
|
||||
.iter()
|
||||
.map(|line| line.len())
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
let tiles = lines
|
||||
.into_iter()
|
||||
.flat_map(|line| {
|
||||
let mut bytes = line.into_bytes();
|
||||
// Make sure every line has the max width
|
||||
bytes.resize(width, b' ');
|
||||
bytes
|
||||
})
|
||||
.map(|byte| Tile::from_byte(byte))
|
||||
.collect();
|
||||
Self {
|
||||
tiles,
|
||||
actions,
|
||||
width,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self) {
|
||||
let height = self.tiles.len() / self.width;
|
||||
for y in 0..height {
|
||||
for x in 0..self.width {
|
||||
print!(
|
||||
"{}",
|
||||
match self[(x, y)] {
|
||||
Tile::Void => " ",
|
||||
Tile::Open => ".",
|
||||
Tile::Solid => "#",
|
||||
}
|
||||
);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn crack_password(&self) -> usize {
|
||||
let (mut x, mut y) = self.first_position();
|
||||
let mut facing = Facing::East;
|
||||
'label: for action in &self.actions {
|
||||
match action {
|
||||
Action::Move(amount) => {
|
||||
for _step in 0..*amount {
|
||||
let raw_potential_new_position = match facing {
|
||||
Facing::North => (x, y - 1),
|
||||
Facing::East => (x + 1, y),
|
||||
Facing::South => (x, y + 1),
|
||||
Facing::West => (x - 1, y),
|
||||
};
|
||||
let potential_new_position =
|
||||
self.wrap_position(raw_potential_new_position, facing);
|
||||
if self[potential_new_position] == Tile::Solid {
|
||||
break 'label;
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::Left => facing.turn_left(),
|
||||
Action::Right => facing.turn_right(),
|
||||
}
|
||||
}
|
||||
1000 * (y + 1) + 4 * (x + 1) + facing.to_number()
|
||||
}
|
||||
|
||||
fn first_position(&self) -> (usize, usize) {
|
||||
let x = self
|
||||
.tiles
|
||||
.iter()
|
||||
.position(|tile| matches!(tile, Tile::Open))
|
||||
.expect("A first position :o");
|
||||
(x, 0)
|
||||
}
|
||||
|
||||
fn wrap_position(&self, (x, y): (usize, usize), facing: Facing) -> (usize, usize) {
|
||||
let height = self.tiles.len() / self.width;
|
||||
let wrap_array = |x: usize, y: usize| (x % self.width, y % height);
|
||||
let (x, y) = wrap_array(x, y);
|
||||
if self[(x, y)] == Tile::Void {
|
||||
loop {
|
||||
let next = match facing {
|
||||
Facing::North => wrap_array(x, if y == 0 { height - 1 } else { y - 1 }),
|
||||
Facing::East => wrap_array(x + 1, y),
|
||||
Facing::South => wrap_array(x, y + 1),
|
||||
Facing::West => wrap_array(if x == 0 { self.width - 1 } else { x - 1 }, y),
|
||||
};
|
||||
if self[next] != Tile::Void {
|
||||
break next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<(usize, usize)> for Map {
|
||||
type Output = Tile;
|
||||
|
||||
fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
|
||||
debug_assert!(x < self.width);
|
||||
debug_assert!(y < self.tiles.len() / self.width);
|
||||
&self.tiles[x + y * self.width]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue