Skip to content

Instantly share code, notes, and snippets.

@jmikkola
Created December 6, 2024 17:21
Show Gist options
  • Save jmikkola/444fec25573b590454f2ec4b791b95e5 to your computer and use it in GitHub Desktop.
Save jmikkola/444fec25573b590454f2ec4b791b95e5 to your computer and use it in GitHub Desktop.
use std::env;
use std::fs;
use std::collections::HashSet;
fn main() {
let map = split_lines(get_content(get_arg()));
println!("Part 1: {}", part1(&map));
println!("Part 2: {}", part2(&map));
}
fn part2(map: &[Vec<char>]) -> usize {
let start = find_start(map);
get_visited(map)
.iter()
.filter(|point| **point != start && would_create_loop(map, start, **point))
.count()
}
fn would_create_loop(map: &[Vec<char>], guard: (usize, usize), obstruction: (usize, usize)) -> bool {
let mut turns = HashSet::new();
let mut position = guard;
let mut direction = (-1, 0);
'walk: loop {
// turn loop:
'turn: for _ in 0..2 {
let new_position = update(map, position, direction);
if let Some(p) = new_position {
if map[p.0][p.1] == '#' || p == obstruction {
let turned_at = (position, direction);
if turns.contains(&turned_at) {
return true;
}
turns.insert(turned_at);
direction = turn(direction);
continue 'turn;
} else {
position = p;
}
} else {
// Walked off an edge, stop
break 'walk;
}
}
}
false
}
fn part1(map: &[Vec<char>]) -> usize {
get_visited(map).len()
}
fn get_visited(map: &[Vec<char>]) -> HashSet<(usize, usize)> {
let mut visited: HashSet<(usize, usize)> = HashSet::new();
let start = find_start(map);
visited.insert(start);
let mut position = start;
let mut direction = (-1, 0);
'walk: loop {
// turn loop:
for _ in 0..2 {
let new_position = update(map, position, direction);
if let Some(p) = new_position {
if map[p.0][p.1] == '#' {
direction = turn(direction);
} else {
position = p;
visited.insert(p);
}
} else {
// Walked off an edge, stop
break 'walk;
}
}
}
visited
}
fn find_start(map: &[Vec<char>]) -> (usize, usize) {
for i in 0..map.len() {
for j in 0..map[0].len() {
if map[i][j] == '^' {
return (i, j);
}
}
}
unreachable!();
}
fn update(map: &[Vec<char>], position: (usize, usize), direction: (i32, i32)) -> Option<(usize, usize)> {
if position.0 == 0 && direction.0 < 0 {
return None;
}
if position.1 == 0 && direction.1 < 0 {
return None;
}
let new_position = (((position.0 as i32) + direction.0) as usize, ((position.1 as i32) + direction.1) as usize);
if new_position.0 >= map.len() {
return None;
}
if new_position.1 >= map[0].len() {
return None;
}
Some(new_position)
}
fn turn(direction: (i32, i32)) -> (i32, i32) {
(direction.1, -1 * direction.0)
}
fn get_arg() -> String {
let opt_filename = env::args().nth(1);
match opt_filename {
Some(f) => f,
None => "example".into(),
}
}
fn get_content(filename: String) -> String {
fs::read_to_string(filename)
.unwrap()
.trim()
.to_string()
}
fn split_lines(s: String) -> Vec<Vec<char>> {
s.split('\n')
.map(|line| line.chars().collect())
.collect()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment