Solution for 2022, day 22, part one
This commit is contained in:
parent
da2bde0fdf
commit
113e9cf107
12
2022/day22-part1/Cargo.toml
Normal file
12
2022/day22-part1/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "day22-part1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rayon = "1"
|
||||
itertools = "0"
|
||||
id-arena = { version = "2", features = ["rayon"] }
|
||||
ndarray = { version = "0", features = ["rayon"] }
|
||||
259
2022/day22-part1/src/main.rs
Normal file
259
2022/day22-part1/src/main.rs
Normal file
@ -0,0 +1,259 @@
|
||||
use itertools::Itertools;
|
||||
use ndarray::prelude::*;
|
||||
|
||||
/// The state each cell in the map can be in.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum MapCell {
|
||||
/// Terrain where the player can be.
|
||||
Open,
|
||||
/// Terrain where the player cannot be.
|
||||
Wall,
|
||||
/// Terrain that is not part of the map.
|
||||
/// The player wraps around if they are on this tile.
|
||||
Void,
|
||||
}
|
||||
|
||||
type MonkeyMap = Array<MapCell, Dim<[usize; 2]>>;
|
||||
|
||||
/// A single command from the input's command-chain.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum Command {
|
||||
Movement(u32),
|
||||
TurnRight,
|
||||
TurnLeft,
|
||||
}
|
||||
|
||||
/// The four directions the player can face.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum Direction {
|
||||
Right = 0,
|
||||
Down,
|
||||
Left,
|
||||
Up,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
/// Returns the direction you'd get when turning right.
|
||||
fn turn_right(&self) -> Direction {
|
||||
match self {
|
||||
Direction::Up => Direction::Right,
|
||||
Direction::Down => Direction::Left,
|
||||
Direction::Left => Direction::Up,
|
||||
Direction::Right => Direction::Down,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the direction you'd get when turning left.
|
||||
fn turn_left(&self) -> Direction {
|
||||
match self {
|
||||
Direction::Up => Direction::Left,
|
||||
Direction::Down => Direction::Right,
|
||||
Direction::Left => Direction::Down,
|
||||
Direction::Right => Direction::Up,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single instance of a player existing on the map.
|
||||
#[derive(Debug, Clone)]
|
||||
struct Player<'a> {
|
||||
x_pos: usize,
|
||||
y_pos: usize,
|
||||
direction: Direction,
|
||||
monkey_map: &'a MonkeyMap,
|
||||
}
|
||||
|
||||
impl Player<'_> {
|
||||
/// Run the simulation for the given list of commands.
|
||||
fn run_simulation(&mut self, commands: &[Command]) {
|
||||
for com in commands {
|
||||
// self.print_map();
|
||||
match com {
|
||||
Command::TurnLeft => {
|
||||
self.direction = self.direction.turn_left();
|
||||
}
|
||||
Command::TurnRight => {
|
||||
self.direction = self.direction.turn_right();
|
||||
}
|
||||
Command::Movement(mv) => {
|
||||
for _ in 0..*mv {
|
||||
self.move_one_tile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the player by one tile along the direction they're facing.
|
||||
fn move_one_tile(&mut self) {
|
||||
let height = self.monkey_map.dim().0;
|
||||
let width = self.monkey_map.dim().1;
|
||||
|
||||
// Keep track of our original position.
|
||||
let og_x = self.x_pos;
|
||||
let og_y = self.y_pos;
|
||||
|
||||
loop {
|
||||
// Determine the theoretical new coordianates.
|
||||
let (new_x, new_y) = match self.direction {
|
||||
Direction::Up => (self.x_pos as i32, self.y_pos as i32 - 1),
|
||||
Direction::Down => (self.x_pos as i32, self.y_pos as i32 + 1),
|
||||
Direction::Left => (self.x_pos as i32 - 1, self.y_pos as i32),
|
||||
Direction::Right => (self.x_pos as i32 + 1, self.y_pos as i32),
|
||||
};
|
||||
|
||||
// Check if they're out-of-bounds and wrap them around, if so.
|
||||
let new_x = if new_x < 0 {
|
||||
width - 1
|
||||
} else if new_x >= width as i32 {
|
||||
0usize
|
||||
} else {
|
||||
new_x as usize
|
||||
};
|
||||
|
||||
let new_y = if new_y < 0 {
|
||||
height - 1
|
||||
} else if new_y >= height as i32 {
|
||||
0usize
|
||||
} else {
|
||||
new_y as usize
|
||||
};
|
||||
|
||||
// Next, check the kind of tile we've reached.
|
||||
match self.monkey_map[[new_y, new_x]] {
|
||||
MapCell::Open => {
|
||||
// Free space? Great, then move there and return.
|
||||
self.x_pos = new_x;
|
||||
self.y_pos = new_y;
|
||||
return;
|
||||
}
|
||||
MapCell::Wall => {
|
||||
// Wall? We can't move there.
|
||||
// Return to where we started (in case we moved through the void) and return.
|
||||
self.x_pos = og_x;
|
||||
self.y_pos = og_y;
|
||||
return;
|
||||
}
|
||||
MapCell::Void => {
|
||||
// If we're in the Void we need to wrap around, i.e. keep moving.
|
||||
// Update the position but don't return just yet.
|
||||
// This way we'll run another loop iteration.
|
||||
self.x_pos = new_x;
|
||||
self.y_pos = new_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the final solution as outlined in the task description.
|
||||
fn calculate_solution(&self) -> usize {
|
||||
((self.y_pos + 1) * 1000) + ((self.x_pos + 1) * 4) + (self.direction as usize)
|
||||
}
|
||||
|
||||
/// Print the current state of the map as well as the player's location for debugging.
|
||||
#[allow(dead_code)]
|
||||
fn print_map(&self) {
|
||||
println!();
|
||||
|
||||
let height = self.monkey_map.dim().0;
|
||||
let width = self.monkey_map.dim().1;
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
if x == self.x_pos && y == self.y_pos {
|
||||
match self.direction {
|
||||
Direction::Up => print!("^"),
|
||||
Direction::Down => print!("v"),
|
||||
Direction::Left => print!("<"),
|
||||
Direction::Right => print!(">"),
|
||||
}
|
||||
} else {
|
||||
match self.monkey_map[[y, x]] {
|
||||
MapCell::Void => print!(" "),
|
||||
MapCell::Open => print!("."),
|
||||
MapCell::Wall => print!("#"),
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Use command line arguments to specify the input filename.
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() < 2 {
|
||||
panic!("Usage: ./main <input-file>\nNo input file provided. Exiting.");
|
||||
}
|
||||
|
||||
// Next, read the contents of the input file into a string for easier processing.
|
||||
let input = std::fs::read_to_string(&args[1]).expect("Error opening file");
|
||||
|
||||
// --- TASK BEGIN ---
|
||||
let mut lines = input.lines().collect_vec();
|
||||
|
||||
// Parse the list of commands.
|
||||
let commands = lines
|
||||
.last()
|
||||
.unwrap()
|
||||
.chars()
|
||||
.chunk_by(|e| e.is_ascii_digit())
|
||||
.into_iter()
|
||||
.map(|(is_number, chunk)| {
|
||||
let chunk = chunk.collect::<String>();
|
||||
match (is_number, chunk.chars().next().unwrap()) {
|
||||
(true, _) => Command::Movement(chunk.parse::<u32>().unwrap()),
|
||||
(false, 'R') => Command::TurnRight,
|
||||
(false, 'L') => Command::TurnLeft,
|
||||
_ => panic!("could not parse series of movement commands"),
|
||||
}
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// Get rid of the last two lines, as they're the only ones not part of the map.
|
||||
lines.pop();
|
||||
lines.pop();
|
||||
|
||||
// Determine the height and width of the map.
|
||||
let width = lines.iter().map(|e| e.len()).max().unwrap();
|
||||
let height = lines.len();
|
||||
// Allocate it using ndarray.
|
||||
let mut monkey_map = Array::<MapCell, _>::from_elem((height, width), MapCell::Void);
|
||||
// Now populate the map with the map-data.
|
||||
for (y, line) in lines.iter().enumerate() {
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
monkey_map[[y, x]] = match c {
|
||||
' ' => MapCell::Void,
|
||||
'.' => MapCell::Open,
|
||||
'#' => MapCell::Wall,
|
||||
_ => panic!("unkown character detected in drawing of map"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the initial coordinates.
|
||||
let (initial_y, initial_x) = monkey_map
|
||||
.indexed_iter()
|
||||
.find_map(|((y, x), c)| {
|
||||
if *c != MapCell::Void {
|
||||
Some((y, x))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
// Construct a player out of this.
|
||||
let mut player = Player {
|
||||
y_pos: initial_y,
|
||||
x_pos: initial_x,
|
||||
direction: Direction::Right,
|
||||
monkey_map: &monkey_map,
|
||||
};
|
||||
|
||||
// And let the player run the simulation.
|
||||
player.run_simulation(&commands);
|
||||
|
||||
// Calculate and return the solution.
|
||||
println!("Result: {}", player.calculate_solution());
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user