1
0
Fork 0
advent-of-code/2023/src/bin/14.rs

206 lines
5.2 KiB
Rust

advent_of_code::solution!(14);
use std::collections::HashSet;
fn parse(input: &str) -> Vec<String> {
input
.lines()
.filter_map(|line| {
if !line.is_empty() {
Some(String::from(line))
} else {
None
}
})
.collect::<Vec<_>>()
}
fn map_to_string(map: &[[char; 100]; 100]) -> String {
let mut s = String::with_capacity(100 * 100);
for row in map.iter() {
let mut has_char = false;
for &c in row.iter() {
if c == ' ' {
break;
}
has_char = true;
s.push(c);
}
if has_char {
s.push('\n');
}
}
s
}
fn score(input: &str) -> u32 {
let lines = input
.split("\n")
.filter_map(|t| {
if t.is_empty() {
None
} else {
Some(String::from(t))
}
})
.collect::<Vec<String>>();
let mut score = 0;
let total_lines = lines.len();
for (index, line) in lines.iter().enumerate() {
score += line.matches("O").collect::<Vec<_>>().len() * (total_lines - index);
}
score.try_into().unwrap()
}
pub fn part_one(input: &str) -> Option<u32> {
let mut map: [[char; 100]; 100] = [[' '; 100]; 100];
for (row, line) in parse(input).iter().enumerate() {
for (col, c) in line.chars().enumerate() {
map[row][col] = c;
}
}
tilt_north(&mut map);
Some(score(&map_to_string(&map)))
}
fn tilt_north(map: &mut [[char; 100]; 100]) -> String {
for col in 0..(map[0]).len() {
let mut r = 0;
for row in (0..(map.len())).rev() {
if map[row][col] == ' ' {
continue;
} else if map[row][col] == '#' {
for rock in 1..=r {
map[row + rock][col] = 'O';
}
r = 0;
} else if map[row][col] == 'O' {
r += 1;
map[row][col] = '.';
}
}
for rock in 1..=r {
map[rock - 1][col] = 'O';
}
}
map_to_string(&map)
}
fn tilt_west(map: &mut [[char; 100]; 100]) -> String {
for row in 0..map.len() {
let mut r = 0;
for col in (0..map[0].len()).rev() {
if map[row][col] == ' ' {
continue;
} else if map[row][col] == '#' {
for rock in 1..=r {
map[row][col + rock] = 'O';
}
r = 0;
} else if map[row][col] == 'O' {
r += 1;
map[row][col] = '.';
}
}
for rock in 1..=r {
map[row][rock - 1] = 'O';
}
}
map_to_string(&map)
}
fn tilt_south(map: &mut [[char; 100]; 100]) -> String {
for col in 0..map[0].len() {
let mut r = 0;
let mut last_row = 100;
for row in 0..map.len() {
if map[row][col] == ' ' {
last_row = row;
break;
} else if map[row][col] == '#' {
for rock in 1..=r {
map[row - rock][col] = 'O';
}
r = 0;
} else if map[row][col] == 'O' {
r += 1;
map[row][col] = '.';
}
}
for rock in 1..=r {
map[last_row - rock][col] = 'O';
}
}
map_to_string(&map)
}
fn tilt_east(map: &mut [[char; 100]; 100]) -> String {
for row in 0..map.len() {
let mut r = 0;
let mut last_col = 100;
for col in 0..map[row].len() {
if map[row][col] == ' ' {
last_col = col;
break;
} else if map[row][col] == '#' {
for rock in 1..=r {
map[row][col - rock] = 'O';
}
r = 0;
} else if map[row][col] == 'O' {
r += 1;
map[row][col] = '.';
}
}
for rock in 1..=r {
map[row][last_col - rock] = 'O';
}
}
map_to_string(&map)
}
pub fn part_two(input: &str) -> Option<u32> {
let mut map: [[char; 100]; 100] = [[' '; 100]; 100];
for (row, line) in parse(input).iter().enumerate() {
for (col, c) in line.chars().enumerate() {
map[row][col] = c;
}
}
let mut patterns: HashSet<String> = HashSet::new();
let mut seq: Vec<String> = vec![];
let mut iters = 0;
loop {
if iters == 1000 {
break;
}
tilt_north(&mut map);
tilt_west(&mut map);
tilt_south(&mut map);
tilt_east(&mut map);
let s = map_to_string(&map);
iters += 1;
seq.push(s.clone());
patterns.insert(s);
}
let score = score(&seq[999]);
Some(score)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(136));
}
#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(64));
}
}