Skip to content

Commit

Permalink
add day6
Browse files Browse the repository at this point in the history
  • Loading branch information
mirsella committed Dec 6, 2024
1 parent 8caf012 commit 506c5cd
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 2 deletions.
2 changes: 1 addition & 1 deletion 2024/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ members = [
"day3",
"day4",
"day5",
# "day6",
"day6",
# "day7",
# "day8",
# "day9",
Expand Down
19 changes: 19 additions & 0 deletions 2024/day6/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
input.txt
flamegraph.svg
perf.data*
### Rust
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

12 changes: 12 additions & 0 deletions 2024/day6/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "day6"
authors = ["mirsella <mirsella@protonmail.com>"]
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
itertools = "0.13.0"
pathfinding = "4.11.0"
rayon = "1.10.0"
28 changes: 28 additions & 0 deletions 2024/day6/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::fs::File;
use std::io::{self, Read};
use std::path::PathBuf;
use std::{env, fs};

fn replace_in_file(file_path: &PathBuf, old: &str, new: &str) -> io::Result<()> {
let mut contents = String::new();
File::open(file_path)?.read_to_string(&mut contents)?;
let new_contents = contents.replace(old, new);
if contents != new_contents {
println!("Updating {}", file_path.display());
fs::write(file_path, new_contents)?;
}
Ok(())
}

fn main() -> io::Result<()> {
let pkg_name = env::var("CARGO_PKG_NAME").unwrap();
replace_in_file(
&"../Cargo.toml".into(),
&format!("# \"{pkg_name}\""),
&format!("\"{pkg_name}\""),
)?;

replace_in_file(&"./Cargo.toml".into(), "\n[workspace]", "")?;

Ok(())
}
156 changes: 156 additions & 0 deletions 2024/day6/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
use itertools::Itertools;
use pathfinding::matrix::{directions::*, Matrix};
use rayon::prelude::*;
use std::collections::HashSet;

fn part1(input: &str) -> usize {
let directions = [(N, '^'), (E, '>'), (S, 'v'), (W, '<')];
let get_direction = |char: char| directions.iter().find(|(_, c)| char == *c).unwrap().0;
let get_next_direction =
|char: char| directions[(directions.iter().position(|v| char == v.1).unwrap() + 1) % 4];
let mut m = Matrix::from_rows(input.lines().map(str::chars)).unwrap();
let mut pos = m
.items()
.find_map(|(p, c)| directions.iter().any(|dir| dir.1 == *c).then_some(p))
.unwrap();
loop {
let Some((newpos, new)) = m
.move_in_direction(pos, get_direction(m[pos]))
.map(|p| (p, m[p]))
else {
break;
};
if new == '#' {
m[pos] = get_next_direction(m[pos]).1
} else {
m[newpos] = m[pos];
pos = newpos;
}
}
m.values()
.filter(|&&c| directions.iter().any(|dir| dir.1 == c))
.count()
}

// try to do it without bruteforce
fn _part2_clever(input: &str) -> usize {
let directions = [(N, '^'), (E, '>'), (S, 'v'), (W, '<')];
let get_direction = |char: char| directions.iter().find(|(_, c)| char == *c).unwrap().0;
let get_next_direction =
|char: char| directions[(directions.iter().position(|v| char == v.1).unwrap() + 1) % 4];
let mut m = Matrix::from_rows(input.lines().map(str::chars)).unwrap();
let mut pos = m
.items()
.find_map(|(p, c)| directions.iter().any(|dir| dir.1 == *c).then_some(p))
.unwrap();
let start = pos;
let mut possible_obstructions = HashSet::new();
loop {
let direction = get_direction(m[pos]);
let Some((newpos, new)) = m.move_in_direction(pos, direction).map(|p| (p, m[p])) else {
break;
};
let next_direction = get_next_direction(m[pos]);
if new == '#' {
m[pos] = next_direction.1;
} else {
m[newpos] = m[pos];
pos = newpos;
// if putting a obstruction at the new tile will make the guard join a path he already took
let next_next_direction = get_next_direction(next_direction.1);
for tile in m.in_direction(pos, get_next_direction(m[pos]).0) {
if m[tile] == next_direction.1
|| (m[tile] == next_next_direction.1
&& m[m.move_in_direction(tile, next_direction.0).unwrap()] == '#')
{
let move_in_direction = m.move_in_direction(pos, direction).unwrap();
if move_in_direction != start {
possible_obstructions.insert(move_in_direction);
}
break;
}
}
}
}
for pos in possible_obstructions.iter() {
m[*pos] = 'O'
}
for row in m.iter() {
println!("{}", row.iter().collect::<String>());
}
possible_obstructions.len()
}

// bruteforce but thanks to rayon its still ~250ms to run
fn part2(input: &str) -> usize {
let directions = [(N, '^'), (E, '>'), (S, 'v'), (W, '<')];
let get_direction = |char: char| directions.iter().find(|(_, c)| char == *c).unwrap().0;
let get_next_direction =
|char: char| directions[(directions.iter().position(|v| char == v.1).unwrap() + 1) % 4];
let m = Matrix::from_rows(input.lines().map(str::chars)).unwrap();
let pos = m
.items()
.find_map(|(p, c)| directions.iter().any(|dir| dir.1 == *c).then_some(p))
.unwrap();
let is_infinite = |mut m: Matrix<char>, mut pos: (usize, usize)| -> bool {
let mut seen = HashSet::new();
loop {
let direction = get_direction(m[pos]);
let Some((newpos, new)) = m.move_in_direction(pos, direction).map(|p| (p, m[p])) else {
break;
};
let next_direction = get_next_direction(m[pos]);
if new == '#' {
m[pos] = next_direction.1;
} else {
m[newpos] = m[pos];
pos = newpos;
if !seen.insert((pos, m[pos])) {
return true;
}
}
}
false
};
m.keys()
.collect_vec()
.par_iter()
.filter(|&&tile| {
if tile == pos {
return false;
}
let mut m = m.clone();
m[tile] = '#';
is_infinite(m, pos)
})
.count()
}

fn main() {
let input = include_str!("../input.txt");
println!("Part 1: {}", part1(input));
println!("Part 2: {}", part2(input));
}

#[cfg(test)]
mod tests {
const INPUT: &str = "....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
";
#[test]
fn part1() {
assert_eq!(super::part1(INPUT), 41);
}
#[test]
fn part2() {
assert_eq!(super::part2(INPUT), 6);
}
}
2 changes: 1 addition & 1 deletion template/cargo-generate.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ prompt = "example input equal to"
regex = "[0-9]+"

[placeholders.input]
type = "text"
type = "editor"
prompt = "main puzzle input"
regex = "[^ ]"

0 comments on commit 506c5cd

Please sign in to comment.