Day six
This commit is contained in:
1
day-06-rust/.gitignore
vendored
Normal file
1
day-06-rust/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target/
|
||||
7
day-06-rust/Cargo.lock
generated
Normal file
7
day-06-rust/Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "day-06-rust"
|
||||
version = "0.1.0"
|
||||
6
day-06-rust/Cargo.toml
Normal file
6
day-06-rust/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "day-06-rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
183
day-06-rust/src/common.rs
Normal file
183
day-06-rust/src/common.rs
Normal file
@@ -0,0 +1,183 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type Coord = i16;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Direction {
|
||||
North,
|
||||
East,
|
||||
South,
|
||||
West,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn next_clockwise(&self) -> Direction {
|
||||
match self {
|
||||
Direction::North => Direction::East,
|
||||
Direction::East => Direction::South,
|
||||
Direction::South => Direction::West,
|
||||
Direction::West => Direction::North,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_counter_clockwise(&self) -> Direction {
|
||||
match self {
|
||||
Direction::North => Direction::West,
|
||||
Direction::East => Direction::North,
|
||||
Direction::South => Direction::East,
|
||||
Direction::West => Direction::South,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn opposite(&self) -> Direction {
|
||||
match self {
|
||||
Direction::North => Direction::South,
|
||||
Direction::East => Direction::West,
|
||||
Direction::South => Direction::North,
|
||||
Direction::West => Direction::East,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct Position {
|
||||
pub x: Coord,
|
||||
pub y: Coord,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn offset(&self, direction: Direction) -> Position {
|
||||
match direction {
|
||||
Direction::North => Position { x: self.x, y: self.y - 1 },
|
||||
Direction::East => Position { x: self.x + 1, y: self.y },
|
||||
Direction::South => Position { x: self.x, y: self.y + 1 },
|
||||
Direction::West => Position { x: self.x - 1, y: self.y },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entity {
|
||||
pub direction: Direction,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Tile {
|
||||
Solid,
|
||||
Empty(DirectionInfo),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DirectionInfo {
|
||||
pub north: bool,
|
||||
pub east: bool,
|
||||
pub south: bool,
|
||||
pub west: bool,
|
||||
}
|
||||
|
||||
impl DirectionInfo {
|
||||
pub fn mark(&mut self, direction: Direction) {
|
||||
match direction {
|
||||
Direction::North => self.north = true,
|
||||
Direction::East => self.east = true,
|
||||
Direction::South => self.south = true,
|
||||
Direction::West => self.west = true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, direction: Direction) -> bool {
|
||||
match direction {
|
||||
Direction::North => self.north,
|
||||
Direction::East => self.east,
|
||||
Direction::South => self.south,
|
||||
Direction::West => self.west,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Map = HashMap<Position, Tile>;
|
||||
|
||||
pub struct Input {
|
||||
pub map: Map,
|
||||
pub guard: Option<Entity>,
|
||||
}
|
||||
|
||||
pub fn parse_input(input: &str) -> Input {
|
||||
let mut map = Map::new();
|
||||
let mut guard: Option<Entity> = None;
|
||||
|
||||
input.lines().enumerate().for_each(|(y, line)| {
|
||||
line.chars().enumerate().for_each(|(x, ch)| {
|
||||
let position = Position {
|
||||
x: x as Coord,
|
||||
y: y as Coord,
|
||||
};
|
||||
map.insert(
|
||||
position,
|
||||
match ch {
|
||||
'.' => Tile::Empty(Default::default()),
|
||||
'^' => {
|
||||
guard = Some(Entity {
|
||||
position,
|
||||
direction: Direction::North,
|
||||
});
|
||||
Tile::Empty(DirectionInfo {
|
||||
north: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
_ => Tile::Solid,
|
||||
},
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
Input { map, guard }
|
||||
}
|
||||
|
||||
pub fn print_map(map: &Map) {
|
||||
let max_pos = map.keys().fold(Position {x: 0, y: 0 }, |a, b| Position { x: a.x.max(b.x), y: a.y.max(b.y) });
|
||||
for y in 0..=max_pos.y {
|
||||
let line = (0..=max_pos.x).map(|x| map.get(&Position{x, y})).collect::<Vec<_>>();
|
||||
|
||||
for element in &line {
|
||||
if let Some(Tile::Empty(DirectionInfo{north: true, ..})) = element {
|
||||
print!(" ^ ")
|
||||
} else {
|
||||
print!(" ")
|
||||
}
|
||||
print!(" ")
|
||||
}
|
||||
print!("\n");
|
||||
for element in &line {
|
||||
if let Some(Tile::Empty(DirectionInfo{west: true, ..})) = element {
|
||||
print!("<")
|
||||
} else {
|
||||
print!(" ")
|
||||
}
|
||||
if let Some(Tile::Solid) = element {
|
||||
print!("#")
|
||||
} else {
|
||||
print!(".")
|
||||
}
|
||||
if let Some(Tile::Empty(DirectionInfo{east: true, ..})) = element {
|
||||
print!(">")
|
||||
} else {
|
||||
print!(" ")
|
||||
}
|
||||
print!(" ")
|
||||
}
|
||||
print!("\n");
|
||||
for element in &line {
|
||||
if let Some(Tile::Empty(DirectionInfo{south: true, ..})) = element {
|
||||
print!(" v ")
|
||||
} else {
|
||||
print!(" ")
|
||||
}
|
||||
print!(" ")
|
||||
}
|
||||
print!("\n");
|
||||
}
|
||||
}
|
||||
17
day-06-rust/src/main.rs
Normal file
17
day-06-rust/src/main.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
mod task1;
|
||||
mod common;
|
||||
mod task2;
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage: {} <1|2> <input file>", args[0]);
|
||||
}
|
||||
|
||||
match args[1].as_str() {
|
||||
"1" => task1::run(&*std::fs::read_to_string(&args[2]).expect("File not accessible")),
|
||||
"2" => task2::run(&*std::fs::read_to_string(&args[2]).expect("File not accessible")),
|
||||
_ => eprintln!("Unknown task: {}", args[1]),
|
||||
}
|
||||
}
|
||||
45
day-06-rust/src/task1.rs
Normal file
45
day-06-rust/src/task1.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::common::{parse_input, Entity, Input, Map, Tile};
|
||||
|
||||
pub fn run(input: &str) {
|
||||
let Input { mut map, guard } = parse_input(input);
|
||||
|
||||
let visited_count;
|
||||
if let Some(mut guard) = guard {
|
||||
wander_around(&mut map, &mut guard);
|
||||
|
||||
visited_count = map
|
||||
.values()
|
||||
.into_iter()
|
||||
.filter(|tile| {
|
||||
if let Tile::Empty(visited) = tile {
|
||||
visited.north || visited.east || visited.south || visited.west
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.count();
|
||||
} else {
|
||||
visited_count = 0;
|
||||
}
|
||||
println!("Guard visited {} tiles", visited_count);
|
||||
}
|
||||
|
||||
fn wander_around(map: &mut Map, guard: &mut Entity) {
|
||||
loop {
|
||||
let next_position = guard.position.offset(guard.direction);
|
||||
match map.get_mut(&next_position) {
|
||||
Some(Tile::Empty(ref mut visited)) => {
|
||||
guard.position = next_position;
|
||||
if visited.get(guard.direction) {
|
||||
// we're in a loop
|
||||
break;
|
||||
}
|
||||
visited.mark(guard.direction);
|
||||
}
|
||||
None => break,
|
||||
_ => {
|
||||
guard.direction = guard.direction.next_clockwise();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
144
day-06-rust/src/task2.rs
Normal file
144
day-06-rust/src/task2.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use crate::common::{parse_input, print_map, Direction, Entity, Input, Map, Position, Tile};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn run(input: &str) {
|
||||
let Input { map, guard } = parse_input(input);
|
||||
|
||||
if let Some(guard) = guard {
|
||||
let clever_result = solve_clever(map.clone(), guard.clone());
|
||||
println!("Clever solution result: {:?}", clever_result.len());
|
||||
let brute_force_result = solve_brute_force(map.clone(), guard.clone());
|
||||
println!(
|
||||
"Brute force solution result: {:?}",
|
||||
brute_force_result.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_clever(mut map: Map, mut guard: Entity) -> HashSet<Position> {
|
||||
let mut obstacle_positions = HashSet::<Position>::new();
|
||||
|
||||
cast_crumbs(&mut map, guard.position, guard.direction.opposite());
|
||||
loop {
|
||||
let next_position = guard.position.offset(guard.direction);
|
||||
let next_tile = map.get(&next_position);
|
||||
|
||||
if let Some(Tile::Empty(crumbs)) = map.get(&guard.position) {
|
||||
if crumbs.get(guard.direction.next_clockwise()) {
|
||||
if let Some(Tile::Empty(_)) = next_tile {
|
||||
obstacle_positions.insert(next_position);
|
||||
}
|
||||
} else if crumbs.get(guard.direction.opposite()) {
|
||||
if let Some(Tile::Solid) = next_tile {
|
||||
obstacle_positions.insert(guard.position.offset(guard.direction.next_clockwise()));
|
||||
} else if let Some(Tile::Solid) = map.get(&guard.position.offset(guard.direction.next_clockwise())) {
|
||||
obstacle_positions.insert(next_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match next_tile {
|
||||
None => break,
|
||||
Some(Tile::Empty(_)) => {
|
||||
if let Some(Tile::Empty(crumbs)) = map.get_mut(&guard.position) {
|
||||
crumbs.mark(guard.direction);
|
||||
}
|
||||
|
||||
guard.position = next_position;
|
||||
|
||||
if let Some(Tile::Solid) =
|
||||
map.get(&next_position.offset(guard.direction.next_counter_clockwise()))
|
||||
{
|
||||
cast_crumbs(&mut map, guard.position, guard.direction);
|
||||
}
|
||||
}
|
||||
Some(Tile::Solid) => {
|
||||
guard.direction = guard.direction.next_clockwise();
|
||||
cast_crumbs(&mut map, guard.position, guard.direction.opposite());
|
||||
}
|
||||
}
|
||||
}
|
||||
obstacle_positions
|
||||
}
|
||||
|
||||
fn cast_crumbs(map: &mut Map, mut position: Position, mut direction: Direction) {
|
||||
loop {
|
||||
let next_position = position.offset(direction);
|
||||
match map.get_mut(&next_position) {
|
||||
None => break,
|
||||
Some(Tile::Empty(crumbs)) => {
|
||||
if crumbs.get(direction.opposite()) {
|
||||
return;
|
||||
} else {
|
||||
crumbs.mark(direction.opposite())
|
||||
}
|
||||
position = next_position;
|
||||
}
|
||||
Some(Tile::Solid) => {
|
||||
direction = direction.next_counter_clockwise();
|
||||
if let Some(Tile::Empty(crumbs)) = map.get_mut(&position) {
|
||||
crumbs.mark(direction.opposite())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_brute_force(map: Map, mut guard: Entity) -> HashSet<Position> {
|
||||
let mut obstacle_positions = HashSet::<Position>::new();
|
||||
let mut tested = HashSet::<Position>::new();
|
||||
|
||||
loop {
|
||||
let next_position = guard.position.offset(guard.direction);
|
||||
let next_tile = map.get(&next_position);
|
||||
|
||||
if let Some(Tile::Empty(_)) = next_tile {
|
||||
if !tested.contains(&next_position) {
|
||||
tested.insert(next_position);
|
||||
let mut world = map.clone();
|
||||
world.insert(next_position, Tile::Solid);
|
||||
if check_for_loop(&mut world, guard.position, guard.direction) {
|
||||
//print!(" =========================== {} ===========================", obstacle_positions.len());
|
||||
//print_map(&world);
|
||||
obstacle_positions.insert(next_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match next_tile {
|
||||
None => break,
|
||||
Some(Tile::Empty(_)) => {
|
||||
guard.position = next_position;
|
||||
}
|
||||
Some(Tile::Solid) => {
|
||||
guard.direction = guard.direction.next_clockwise();
|
||||
}
|
||||
}
|
||||
}
|
||||
obstacle_positions
|
||||
}
|
||||
|
||||
fn check_for_loop(map: &mut Map, mut position: Position, mut direction: Direction) -> bool {
|
||||
loop {
|
||||
let next_position = position.offset(direction);
|
||||
match map.get_mut(&next_position) {
|
||||
None => return false,
|
||||
Some(Tile::Empty(crumbs)) => {
|
||||
if crumbs.get(direction) {
|
||||
return true;
|
||||
}
|
||||
crumbs.mark(direction);
|
||||
position = next_position;
|
||||
}
|
||||
Some(Tile::Solid) => {
|
||||
direction = direction.next_clockwise();
|
||||
if let Some(Tile::Empty(crumbs)) = map.get_mut(&position) {
|
||||
if crumbs.get(direction) {
|
||||
return true;
|
||||
}
|
||||
crumbs.mark(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user