This commit is contained in:
2024-12-06 22:45:12 +01:00
parent 14a90754c2
commit 80b3a907bc
7 changed files with 403 additions and 0 deletions

183
day-06-rust/src/common.rs Normal file
View 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
View 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
View 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
View 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);
}
}
}
}
}