Reworked part 1 to support part 2
This commit is contained in:
parent
b1292193a4
commit
80da5eb67e
|
@ -1,6 +1,6 @@
|
|||
#![feature(test)]
|
||||
use core::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Result;
|
||||
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 {
|
||||
Void,
|
||||
Open,
|
||||
Wall,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Direction {
|
||||
Right,
|
||||
Down,
|
||||
|
@ -106,125 +110,263 @@ struct Vec2 {
|
|||
y: usize,
|
||||
}
|
||||
|
||||
struct Map {
|
||||
map: Vec<Vec<Tile>>,
|
||||
pos: Vec2,
|
||||
impl Vec2 {
|
||||
fn new(x: usize, y: usize) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Player {
|
||||
plane: Vec2,
|
||||
position: Vec2,
|
||||
direction: Direction,
|
||||
}
|
||||
|
||||
impl FromStr for Map {
|
||||
type Err = anyhow::Error;
|
||||
#[derive(Clone)]
|
||||
struct Plane {
|
||||
grid: Vec<Vec<Tile>>,
|
||||
neighbours: Vec<Option<(Vec2, Transform)>>,
|
||||
}
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let map = s
|
||||
impl Plane {
|
||||
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()
|
||||
.take_while(|line| !line.is_empty())
|
||||
.map(|line| {
|
||||
.enumerate()
|
||||
.take_while(|(_, line)| !line.is_empty())
|
||||
.for_each(|(y, line)| {
|
||||
line
|
||||
.chars()
|
||||
.map(Tile::from)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.enumerate()
|
||||
.for_each(|(x, c)| {
|
||||
let tile = Tile::from(c);
|
||||
if tile == Tile::Void {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the starting point
|
||||
let x = map[0].iter().position(|t| *t == Tile::Open).unwrap();
|
||||
let pos = Vec2 {
|
||||
x,
|
||||
y: 0,
|
||||
let plane = &mut planes[y/size][x/size];
|
||||
if plane.is_none() {
|
||||
*plane = Some(Plane::new(size));
|
||||
}
|
||||
|
||||
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 {
|
||||
fn movement(&mut self, steps: usize) {
|
||||
for _ in 0..steps {
|
||||
let mut np = self.pos;
|
||||
match self.direction {
|
||||
Direction::Right => {
|
||||
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 movement(&mut self, moves: Vec<(usize, Option<bool>)>) {
|
||||
for m in moves {
|
||||
self.step(m.0);
|
||||
if let Some(clockwise) = m.1 {
|
||||
self.rotate(clockwise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
self.direction.rotate(clockwise);
|
||||
self.player.direction.rotate(clockwise);
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
fn is_test(input: &str) -> bool {
|
||||
input.len() == 189
|
||||
}
|
||||
|
||||
// -- Solution --
|
||||
pub struct Day;
|
||||
impl aoc::Solver for Day {
|
||||
|
@ -257,21 +403,36 @@ impl aoc::Solver for Day {
|
|||
}
|
||||
|
||||
fn part1(input: &str) -> Self::Output1 {
|
||||
// Split the input into the two different parts
|
||||
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 {
|
||||
map.movement(mov.0);
|
||||
if let Some(clockwise) = mov.1 {
|
||||
map.rotate(clockwise);
|
||||
}
|
||||
}
|
||||
// Create the map
|
||||
let size = if is_test(input) { 4 } else { 50 };
|
||||
let mut map = Map::new(map, size);
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user