Created
December 4, 2024 18:09
-
-
Save jmikkola/f606ef85ad7e3900d45cd7583aa89fc1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::env; | |
use std::fs; | |
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() | |
} | |
#[derive(Debug, Clone, Copy)] | |
enum Direction { | |
NW, | |
N, | |
NE, | |
E, | |
SE, | |
S, | |
SW, | |
W | |
} | |
const DIRECTIONS: [Direction; 8] = [ | |
Direction::NW, | |
Direction::N, | |
Direction::NE, | |
Direction::E, | |
Direction::SE, | |
Direction::S, | |
Direction::SW, | |
Direction::W, | |
]; | |
fn part1(lines: &[Vec<char>]) -> i64 { | |
let mut total = 0; | |
for start_y in 0..lines.len() { | |
for start_x in 0..lines[0].len() { | |
let point: Point = (start_x as i32, start_y as i32); | |
for direction in DIRECTIONS.iter() { | |
total += search(lines, point, *direction, 0); | |
} | |
} | |
} | |
total | |
} | |
fn search(lines: &[Vec<char>], point: Point, direction: Direction, chars_seen: usize) -> i64 { | |
if !in_bounds(point, lines) { | |
return 0; | |
} | |
let current_char = lines[point.1 as usize][point.0 as usize]; | |
let expected_char: char = "XMAS".chars().nth(chars_seen).unwrap(); | |
if current_char != expected_char { | |
return 0; | |
} | |
if current_char == 'S' { | |
return 1; | |
} | |
search(lines, step(point, direction), direction, chars_seen + 1) | |
} | |
type Point = (i32, i32); | |
fn in_bounds(p: Point, lines: &[Vec<char>]) -> bool { | |
let (x, y) = p; | |
if x < 0 || y < 0 { | |
return false; | |
} | |
if y as usize >= lines.len() { | |
return false; | |
} | |
if x as usize >= lines[0].len() { | |
return false; | |
} | |
true | |
} | |
fn step(p: Point, direction: Direction) -> Point { | |
use Direction::*; | |
let y_offset = match direction { | |
NW | N | NE => -1, | |
W | E => 0, | |
SW | S | SE => 1, | |
}; | |
let x_offset = match direction { | |
NW | W | SW => -1, | |
N | S => 0, | |
NE | E | SE => 1, | |
}; | |
let (x, y) = p; | |
(x + x_offset, y + y_offset) | |
} | |
fn part2(lines: &[Vec<char>]) -> i64 { | |
use Direction::*; | |
let mut total = 0; | |
for start_y in 0..lines.len() { | |
for start_x in 0..lines[0].len() { | |
if lines[start_y][start_x] != 'A' { | |
continue; | |
} | |
let point: Point = (start_x as i32, start_y as i32); | |
if has_mas(lines, point, [NW, SE]) && has_mas(lines, point, [NE, SW]) { | |
total += 1; | |
} | |
} | |
} | |
total | |
} | |
fn has_mas(lines: &[Vec<char>], point: Point, directions: [Direction; 2]) -> bool { | |
let p1 = step(point, directions[0]); | |
let p2 = step(point, directions[1]); | |
if !in_bounds(p1, lines) || !in_bounds(p2, lines) { | |
return false; | |
} | |
let c1 = lines[p1.1 as usize][p1.0 as usize]; | |
let c2 = lines[p2.1 as usize][p2.0 as usize]; | |
(c1 == 'M' && c2 == 'S') || (c1 == 'S' && c2 == 'M') | |
} | |
fn main() { | |
let lines = split_lines(get_content(get_arg())); | |
println!("part 1: {}", part1(&lines)); | |
println!("part 2: {}", part2(&lines)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment