Skip to content

Commit 506c5cd

Browse files
committed
add day6
1 parent 8caf012 commit 506c5cd

File tree

6 files changed

+217
-2
lines changed

6 files changed

+217
-2
lines changed

Diff for: 2024/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ members = [
66
"day3",
77
"day4",
88
"day5",
9-
# "day6",
9+
"day6",
1010
# "day7",
1111
# "day8",
1212
# "day9",

Diff for: 2024/day6/.gitignore

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
input.txt
2+
flamegraph.svg
3+
perf.data*
4+
### Rust
5+
# Generated by Cargo
6+
# will have compiled files and executables
7+
debug/
8+
target/
9+
10+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
11+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
12+
Cargo.lock
13+
14+
# These are backup files generated by rustfmt
15+
**/*.rs.bk
16+
17+
# MSVC Windows builds of rustc generate these, which store debugging information
18+
*.pdb
19+

Diff for: 2024/day6/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "day6"
3+
authors = ["mirsella <[email protected]>"]
4+
version = "0.1.0"
5+
edition = "2021"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
itertools = "0.13.0"
11+
pathfinding = "4.11.0"
12+
rayon = "1.10.0"

Diff for: 2024/day6/build.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use std::fs::File;
2+
use std::io::{self, Read};
3+
use std::path::PathBuf;
4+
use std::{env, fs};
5+
6+
fn replace_in_file(file_path: &PathBuf, old: &str, new: &str) -> io::Result<()> {
7+
let mut contents = String::new();
8+
File::open(file_path)?.read_to_string(&mut contents)?;
9+
let new_contents = contents.replace(old, new);
10+
if contents != new_contents {
11+
println!("Updating {}", file_path.display());
12+
fs::write(file_path, new_contents)?;
13+
}
14+
Ok(())
15+
}
16+
17+
fn main() -> io::Result<()> {
18+
let pkg_name = env::var("CARGO_PKG_NAME").unwrap();
19+
replace_in_file(
20+
&"../Cargo.toml".into(),
21+
&format!("# \"{pkg_name}\""),
22+
&format!("\"{pkg_name}\""),
23+
)?;
24+
25+
replace_in_file(&"./Cargo.toml".into(), "\n[workspace]", "")?;
26+
27+
Ok(())
28+
}

Diff for: 2024/day6/src/main.rs

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use itertools::Itertools;
2+
use pathfinding::matrix::{directions::*, Matrix};
3+
use rayon::prelude::*;
4+
use std::collections::HashSet;
5+
6+
fn part1(input: &str) -> usize {
7+
let directions = [(N, '^'), (E, '>'), (S, 'v'), (W, '<')];
8+
let get_direction = |char: char| directions.iter().find(|(_, c)| char == *c).unwrap().0;
9+
let get_next_direction =
10+
|char: char| directions[(directions.iter().position(|v| char == v.1).unwrap() + 1) % 4];
11+
let mut m = Matrix::from_rows(input.lines().map(str::chars)).unwrap();
12+
let mut pos = m
13+
.items()
14+
.find_map(|(p, c)| directions.iter().any(|dir| dir.1 == *c).then_some(p))
15+
.unwrap();
16+
loop {
17+
let Some((newpos, new)) = m
18+
.move_in_direction(pos, get_direction(m[pos]))
19+
.map(|p| (p, m[p]))
20+
else {
21+
break;
22+
};
23+
if new == '#' {
24+
m[pos] = get_next_direction(m[pos]).1
25+
} else {
26+
m[newpos] = m[pos];
27+
pos = newpos;
28+
}
29+
}
30+
m.values()
31+
.filter(|&&c| directions.iter().any(|dir| dir.1 == c))
32+
.count()
33+
}
34+
35+
// try to do it without bruteforce
36+
fn _part2_clever(input: &str) -> usize {
37+
let directions = [(N, '^'), (E, '>'), (S, 'v'), (W, '<')];
38+
let get_direction = |char: char| directions.iter().find(|(_, c)| char == *c).unwrap().0;
39+
let get_next_direction =
40+
|char: char| directions[(directions.iter().position(|v| char == v.1).unwrap() + 1) % 4];
41+
let mut m = Matrix::from_rows(input.lines().map(str::chars)).unwrap();
42+
let mut pos = m
43+
.items()
44+
.find_map(|(p, c)| directions.iter().any(|dir| dir.1 == *c).then_some(p))
45+
.unwrap();
46+
let start = pos;
47+
let mut possible_obstructions = HashSet::new();
48+
loop {
49+
let direction = get_direction(m[pos]);
50+
let Some((newpos, new)) = m.move_in_direction(pos, direction).map(|p| (p, m[p])) else {
51+
break;
52+
};
53+
let next_direction = get_next_direction(m[pos]);
54+
if new == '#' {
55+
m[pos] = next_direction.1;
56+
} else {
57+
m[newpos] = m[pos];
58+
pos = newpos;
59+
// if putting a obstruction at the new tile will make the guard join a path he already took
60+
let next_next_direction = get_next_direction(next_direction.1);
61+
for tile in m.in_direction(pos, get_next_direction(m[pos]).0) {
62+
if m[tile] == next_direction.1
63+
|| (m[tile] == next_next_direction.1
64+
&& m[m.move_in_direction(tile, next_direction.0).unwrap()] == '#')
65+
{
66+
let move_in_direction = m.move_in_direction(pos, direction).unwrap();
67+
if move_in_direction != start {
68+
possible_obstructions.insert(move_in_direction);
69+
}
70+
break;
71+
}
72+
}
73+
}
74+
}
75+
for pos in possible_obstructions.iter() {
76+
m[*pos] = 'O'
77+
}
78+
for row in m.iter() {
79+
println!("{}", row.iter().collect::<String>());
80+
}
81+
possible_obstructions.len()
82+
}
83+
84+
// bruteforce but thanks to rayon its still ~250ms to run
85+
fn part2(input: &str) -> usize {
86+
let directions = [(N, '^'), (E, '>'), (S, 'v'), (W, '<')];
87+
let get_direction = |char: char| directions.iter().find(|(_, c)| char == *c).unwrap().0;
88+
let get_next_direction =
89+
|char: char| directions[(directions.iter().position(|v| char == v.1).unwrap() + 1) % 4];
90+
let m = Matrix::from_rows(input.lines().map(str::chars)).unwrap();
91+
let pos = m
92+
.items()
93+
.find_map(|(p, c)| directions.iter().any(|dir| dir.1 == *c).then_some(p))
94+
.unwrap();
95+
let is_infinite = |mut m: Matrix<char>, mut pos: (usize, usize)| -> bool {
96+
let mut seen = HashSet::new();
97+
loop {
98+
let direction = get_direction(m[pos]);
99+
let Some((newpos, new)) = m.move_in_direction(pos, direction).map(|p| (p, m[p])) else {
100+
break;
101+
};
102+
let next_direction = get_next_direction(m[pos]);
103+
if new == '#' {
104+
m[pos] = next_direction.1;
105+
} else {
106+
m[newpos] = m[pos];
107+
pos = newpos;
108+
if !seen.insert((pos, m[pos])) {
109+
return true;
110+
}
111+
}
112+
}
113+
false
114+
};
115+
m.keys()
116+
.collect_vec()
117+
.par_iter()
118+
.filter(|&&tile| {
119+
if tile == pos {
120+
return false;
121+
}
122+
let mut m = m.clone();
123+
m[tile] = '#';
124+
is_infinite(m, pos)
125+
})
126+
.count()
127+
}
128+
129+
fn main() {
130+
let input = include_str!("../input.txt");
131+
println!("Part 1: {}", part1(input));
132+
println!("Part 2: {}", part2(input));
133+
}
134+
135+
#[cfg(test)]
136+
mod tests {
137+
const INPUT: &str = "....#.....
138+
.........#
139+
..........
140+
..#.......
141+
.......#..
142+
..........
143+
.#..^.....
144+
........#.
145+
#.........
146+
......#...
147+
";
148+
#[test]
149+
fn part1() {
150+
assert_eq!(super::part1(INPUT), 41);
151+
}
152+
#[test]
153+
fn part2() {
154+
assert_eq!(super::part2(INPUT), 6);
155+
}
156+
}

Diff for: template/cargo-generate.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ prompt = "example input equal to"
99
regex = "[0-9]+"
1010

1111
[placeholders.input]
12-
type = "text"
12+
type = "editor"
1313
prompt = "main puzzle input"
1414
regex = "[^ ]"

0 commit comments

Comments
 (0)