Reworked part 1 to support part 2

This commit is contained in:
Dreaded_X 2022-12-22 23:56:15 +01:00
parent b1292193a4
commit 80da5eb67e
Signed by: Dreaded_X
GPG Key ID: 76BDEC4E165D8AD9

View File

@ -1,6 +1,6 @@
#![feature(test)] #![feature(test)]
use core::fmt; use core::fmt;
use std::str::FromStr; use std::rc::Rc;
use anyhow::Result; use anyhow::Result;
use aoc::Solver; use aoc::Solver;
@ -42,14 +42,18 @@ mod tests {
} }
} }
#[derive(PartialEq, Eq)] const PLANES: usize = 4;
type Transform = Rc<Box<dyn 'static + Fn(Player) -> Player>>;
#[derive(Copy, Clone, PartialEq, Eq)]
enum Tile { enum Tile {
Void, Void,
Open, Open,
Wall, Wall,
} }
#[derive(Debug)] #[derive(Debug, Copy, Clone)]
enum Direction { enum Direction {
Right, Right,
Down, Down,
@ -106,125 +110,263 @@ struct Vec2 {
y: usize, y: usize,
} }
struct Map { impl Vec2 {
map: Vec<Vec<Tile>>, fn new(x: usize, y: usize) -> Self {
pos: Vec2, Self { x, y }
}
}
#[derive(Copy, Clone)]
struct Player {
plane: Vec2,
position: Vec2,
direction: Direction, direction: Direction,
} }
impl FromStr for Map { #[derive(Clone)]
type Err = anyhow::Error; struct Plane {
grid: Vec<Vec<Tile>>,
neighbours: Vec<Option<(Vec2, Transform)>>,
}
fn from_str(s: &str) -> Result<Self, Self::Err> { impl Plane {
let map = s fn new(size: usize) -> Plane {
let grid = vec![vec![Tile::Void; size]; size];
let mut neighbours = Vec::with_capacity(4);
for _ in 0..4 {
neighbours.push(None);
}
Plane { grid, neighbours }
}
}
struct Map {
planes: Vec<Vec<Option<Plane>>>,
player: Player,
size: usize,
}
impl Map {
fn new(input: &str, size: usize) -> Self {
let mut planes = vec![vec![None; PLANES]; PLANES];
input
.lines() .lines()
.take_while(|line| !line.is_empty()) .enumerate()
.map(|line| { .take_while(|(_, line)| !line.is_empty())
.for_each(|(y, line)| {
line line
.chars() .chars()
.map(Tile::from) .enumerate()
.collect::<Vec<_>>() .for_each(|(x, c)| {
}) let tile = Tile::from(c);
.collect::<Vec<_>>(); if tile == Tile::Void {
return;
}
// Get the starting point let plane = &mut planes[y/size][x/size];
let x = map[0].iter().position(|t| *t == Tile::Open).unwrap(); if plane.is_none() {
let pos = Vec2 { *plane = Some(Plane::new(size));
x, }
y: 0,
if let Some(plane) = plane {
plane.grid[y % size][x % size] = tile;
}
});
});
let x = planes[0].iter().position(|plane| plane.is_some()).unwrap();
let player = Player {
plane: Vec2::new(x, 0),
position: Vec2::new(0, 0),
direction: Direction::Right,
}; };
Ok(Self { map, pos, direction: Direction::Right }) Self { planes, player, size }
}
fn _print(&self) {
for y in 0..self.size*PLANES {
for x in 0..self.size*PLANES {
if let Some(plane) = &self.planes[y/self.size][x/self.size] {
if self.player.plane.x == x/self.size && self.player.plane.y == y/self.size && self.player.position.x == x%self.size && self.player.position.y == y%self.size {
print!("$");
} else {
let tile = &plane.grid[y%self.size][x%self.size];
print!("{tile}");
}
} else {
print!(" ");
}
}
println!("");
}
} }
} }
impl Map { impl Map {
fn movement(&mut self, steps: usize) { fn movement(&mut self, moves: Vec<(usize, Option<bool>)>) {
for _ in 0..steps { for m in moves {
let mut np = self.pos; self.step(m.0);
match self.direction { if let Some(clockwise) = m.1 {
Direction::Right => { self.rotate(clockwise);
np.x += 1;
// Wrap around if we walk out of the map on the right
if np.x >= self.map[np.y].len() {
np.x = 0;
// Move over the void, we only do this when we wrap around, since there are
// no void tiles on the right
while self.map[np.y][np.x] == Tile::Void {
np.x += 1;
}
}
},
Direction::Left => {
// Make sure we do not underflow
if np.x == 0 {
np.x = self.map[np.y].len();
}
// Update our location
np.x -= 1;
// Jump over the void
if self.map[np.y][np.x] == Tile::Void {
np.x = self.map[np.y].len()-1;
}
},
Direction::Up => {
// Make sure we do not underflow
if np.y == 0 {
np.y = self.map.len();
}
// Update our location
np.y -= 1;
// Jump over the void
if np.x >= self.map[np.y].len() || self.map[np.y][np.x] == Tile::Void {
np.y = self.map.len()-1;
}
while np.x >= self.map[np.y].len() || self.map[np.y][np.x] == Tile::Void {
np.y -= 1;
}
},
Direction::Down => {
// Update our location
np.y += 1;
// Wrap around
if np.y >= self.map.len() {
np.y = 0;
}
// Jump over the void
if np.x >= self.map[np.y].len() || self.map[np.y][np.x] == Tile::Void {
np.y = 0;
}
while np.x >= self.map[np.y].len() || self.map[np.y][np.x] == Tile::Void {
np.y += 1;
}
}
}
// If the space is open we update our location, otherwise we stay where we are
if self.map[np.y][np.x] == Tile::Open {
self.pos = np;
} else {
// There is a wall in front of us, so no point in trying the rest of the
// steps
break;
} }
} }
} }
fn get_neighbour(&self, plane: &Vec2, direction: &Direction) -> &(Vec2, Transform) {
// Get the position of the neighbour
let plane = self.planes[plane.y][plane.x].as_ref().unwrap();
plane.neighbours[usize::from(direction)].as_ref().unwrap()
}
fn step(&mut self, steps: usize) {
for _ in 0..steps {
// Make a copy of the player
let mut np = self.player;
match self.player.direction {
Direction::Right => {
// Move the new player
np.position.x += 1;
// We walk out of the current plane
if np.position.x >= self.size {
// Get the position of the neighbour
let neighbour = self.get_neighbour(&np.plane, &np.direction);
np.plane = neighbour.0;
// Update the coordinates of the player
np = neighbour.1(np);
}
},
Direction::Left => {
// Move the new player
let temp = np.position.x as isize - 1;
// We walk out of the current plane
if temp < 0 {
// Get the position of the neighbour
let neighbour = self.get_neighbour(&np.plane, &np.direction);
np.plane = neighbour.0;
// Update the coordinates of the player
np = neighbour.1(np);
} else {
np.position.x -= 1;
}
},
Direction::Down => {
// Move the new player
np.position.y += 1;
// We walk out of the current plane
if np.position.y >= self.size {
// Get the position of the neighbour
let neighbour = self.get_neighbour(&np.plane, &np.direction);
np.plane = neighbour.0;
// Update the coordinates of the player
np = neighbour.1(np);
}
},
Direction::Up => {
// Move the new player
let temp = np.position.y as isize - 1;
// We walk out of the current plane
if temp < 0 {
// Get the position of the neighbour
let neighbour = self.get_neighbour(&np.plane, &np.direction);
np.plane = neighbour.0;
// Update the coordinates of the player
np = neighbour.1(np);
} else {
np.position.y -= 1;
}
},
}
// Get the current plane
let plane = self.planes[np.plane.y][np.plane.x].as_ref().unwrap();
// Make sure the new location is free
if plane.grid[np.position.y][np.position.x] == Tile::Wall {
// We are done moving
break;
}
// Update the player location
self.player = np;
}
}
fn rotate(&mut self, clockwise: bool) { fn rotate(&mut self, clockwise: bool) {
self.direction.rotate(clockwise); self.player.direction.rotate(clockwise);
} }
fn score(&self) -> usize { fn score(&self) -> usize {
1000 * (self.pos.y+1) + 4 * (self.pos.x+1) + (usize::from(&self.direction)) 1000 * (self.player.plane.y*self.size + self.player.position.y + 1) + 4 * (self.player.plane.x*self.size + self.player.position.x + 1) + (usize::from(&self.player.direction))
}
// Connect the planes together according to the rules in part 1
fn fill_neighbours_part1(&mut self) {
let size = self.size;
for y in 0..PLANES {
for x in 0..PLANES {
if self.planes[y][x].is_some() {
// Check up neighbour
{
let y_neighbour = (0..PLANES).rev().cycle().skip(PLANES-y).take(PLANES).find(|y| self.planes[*y][x].is_some()).unwrap();
self.planes[y][x].as_mut().unwrap().neighbours[usize::from(&Direction::Up)] = Some((Vec2::new(x, y_neighbour), Rc::new(Box::new(move |mut p: Player| {
p.position.y = size-1;
return p;
}))));
}
// Check down neighbour
{
let y_neighbour = (0..PLANES).cycle().skip(y+1).take(PLANES).find(|y| self.planes[*y][x].is_some()).unwrap();
self.planes[y][x].as_mut().unwrap().neighbours[usize::from(&Direction::Down)] = Some((Vec2::new(x, y_neighbour), Rc::new(Box::new(move |mut p: Player| {
// This happens post move
p.position.y = 0;
return p;
}))));
}
// Check left neighbour
{
let x_neighbour = (0..PLANES).rev().cycle().skip(PLANES-x).take(PLANES).find(|x| self.planes[y][*x].is_some()).unwrap();
self.planes[y][x].as_mut().unwrap().neighbours[usize::from(&Direction::Left)] = Some((Vec2::new(x_neighbour, y), Rc::new(Box::new(move |mut p: Player| {
p.position.x = size-1;
return p;
}))));
}
// Check right neighbour
{
let x_neighbour = (0..PLANES).cycle().skip(x+1).take(PLANES).find(|x| self.planes[y][*x].is_some()).unwrap();
self.planes[y][x].as_mut().unwrap().neighbours[usize::from(&Direction::Right)] = Some((Vec2::new(x_neighbour, y), Rc::new(Box::new(move |mut p: Player| {
// This happens post move
p.position.x = 0;
return p;
}))));
}
}
}
}
}
fn fill_neighbours_part2(&mut self) {
{
}
} }
} }
@ -246,6 +388,10 @@ fn parse_movement(input: &str) -> Vec<(usize, Option<bool>)> {
}).collect() }).collect()
} }
fn is_test(input: &str) -> bool {
input.len() == 189
}
// -- Solution -- // -- Solution --
pub struct Day; pub struct Day;
impl aoc::Solver for Day { impl aoc::Solver for Day {
@ -257,21 +403,36 @@ impl aoc::Solver for Day {
} }
fn part1(input: &str) -> Self::Output1 { fn part1(input: &str) -> Self::Output1 {
// Split the input into the two different parts
let (map, movement) = input.split_once("\n\n").unwrap(); let (map, movement) = input.split_once("\n\n").unwrap();
let mut map = Map::from_str(map).unwrap();
let movement = parse_movement(movement);
for mov in movement { // Create the map
map.movement(mov.0); let size = if is_test(input) { 4 } else { 50 };
if let Some(clockwise) = mov.1 { let mut map = Map::new(map, size);
map.rotate(clockwise);
} // Connect the planes together
} map.fill_neighbours_part1();
// Create the movement instructions and execute them
let moves = parse_movement(movement);
map.movement(moves);
map.score() map.score()
} }
fn part2(input: &str) -> Self::Output2 { fn part2(input: &str) -> Self::Output2 {
0 // Split the input into the two different parts
let (map, movement) = input.split_once("\n\n").unwrap();
// Create the map
let size = if is_test(input) { 4 } else { 50 };
let mut map = Map::new(map, size);
map.fill_neighbours_part2();
let moves = parse_movement(movement);
map.movement(moves);
map.score()
} }
} }