Work on day22

This commit is contained in:
Malte Tammena 2022-12-22 14:21:18 +01:00
parent db7cfc6b49
commit 9af717658a
4 changed files with 497 additions and 2 deletions

202
day22/input Normal file

File diff suppressed because one or more lines are too long

14
day22/input.test Normal file
View file

@ -0,0 +1,14 @@
...#
.#..
#...
....
...#.......#
........#...
..#....#....
..........#.
...#....
.....#..
.#......
......#.
10R5L5R10L4R5L5

View file

@ -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
View 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]
}
}