Reworked part 1 to support part 2
This commit is contained in:
parent
b1292193a4
commit
80da5eb67e
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user