Refactor day 4 & 5
							parent
							
								
									551a322cc2
								
							
						
					
					
						commit
						acbb0123ac
					
				| 
						 | 
				
			
			@ -1,26 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
pub fn answer(lines : &str) -> u32 {
 | 
			
		||||
    let ( mut left, mut right ) : ( Vec<u32>, Vec<u32> ) = lines
 | 
			
		||||
        .split("\n")
 | 
			
		||||
        .filter_map(parse_line)
 | 
			
		||||
        .unzip();
 | 
			
		||||
    
 | 
			
		||||
    left.sort_unstable();
 | 
			
		||||
    right.sort_unstable();
 | 
			
		||||
    
 | 
			
		||||
    left.iter()
 | 
			
		||||
        .zip(right.iter())
 | 
			
		||||
        .map(|(x, y)| abs_diff(&x, &y))
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn abs_diff(l : &u32, r : &u32) -> u32 {
 | 
			
		||||
    if l >= r { l - r } else { r - l }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_line(s : &str) -> Option<( u32, u32 )> {
 | 
			
		||||
    let (left, right) = s.trim().split_once("   ")?;
 | 
			
		||||
 | 
			
		||||
    Some(( utils::str_to_u32(left)?, utils::str_to_u32(right)? ))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
pub fn answer(s : &str) -> u32 {
 | 
			
		||||
    let ( left, right ) : ( Vec<u32>, Vec<u32> ) = s
 | 
			
		||||
        .split("\n")
 | 
			
		||||
        .filter_map(parse_line)
 | 
			
		||||
        .unzip();
 | 
			
		||||
    
 | 
			
		||||
    left.iter()
 | 
			
		||||
        .map(|x| right.iter().filter(|y| x == *y).count() as u32 * x)
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_line(s : &str) -> Option<( u32, u32 )> {
 | 
			
		||||
    let (left, right) = s.trim().split_once("   ")?;
 | 
			
		||||
 | 
			
		||||
    Some(( utils::str_to_u32(left)?, utils::str_to_u32(right)? ))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,26 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
pub fn answer(text : &str) -> usize {
 | 
			
		||||
    text.split("\n")
 | 
			
		||||
        .map(parse_line)
 | 
			
		||||
        .filter(is_safe_line)
 | 
			
		||||
        .count()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_safe_line(v : &Vec<i32>) -> bool {
 | 
			
		||||
    let diff : Vec<i32> = v
 | 
			
		||||
        .windows(2)
 | 
			
		||||
        .filter_map(|v| Some(v.get(0)? - v.get(1)?))
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    let all_increasing : bool = diff.iter().all(|&x| x < 0);
 | 
			
		||||
    let all_decreasing : bool = diff.iter().all(|&x| x > 0);
 | 
			
		||||
    let all_bounded_ok : bool = diff.iter().all(|&x| x.abs() <= 3);
 | 
			
		||||
 | 
			
		||||
    all_bounded_ok && (all_increasing || all_decreasing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_line(s : &str) -> Vec<i32> {
 | 
			
		||||
    s.split(" ").filter_map(utils::str_to_i32).collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,43 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
pub fn answer(text : &str) -> usize {
 | 
			
		||||
    text.split("\n")
 | 
			
		||||
        .map(parse_line)
 | 
			
		||||
        .filter(is_dampened_safe_line)
 | 
			
		||||
        .count()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_dampened_safe_line(v : &Vec<i32>) -> bool {
 | 
			
		||||
    if is_safe_line(v) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for i in 0..v.len() {
 | 
			
		||||
        let mut cv = v.to_vec();
 | 
			
		||||
        cv.remove(i);
 | 
			
		||||
 | 
			
		||||
        if is_safe_line(&cv) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_safe_line(v : &Vec<i32>) -> bool {
 | 
			
		||||
    let diff : Vec<i32> = v
 | 
			
		||||
        .windows(2)
 | 
			
		||||
        .filter_map(|v| Some(v.get(0)? - v.get(1)?))
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    let all_increasing : bool = diff.iter().all(|&x| x < 0);
 | 
			
		||||
    let all_decreasing : bool = diff.iter().all(|&x| x > 0);
 | 
			
		||||
    let all_bounded_ok : bool = diff.iter().all(|&x| x.abs() <= 3);
 | 
			
		||||
 | 
			
		||||
    all_bounded_ok && (all_increasing || all_decreasing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_line(s : &str) -> Vec<i32> {
 | 
			
		||||
    s.split(" ").filter_map(utils::str_to_i32).collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
 | 
			
		||||
pub fn answer(text : &str) -> u32 {
 | 
			
		||||
    Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)")
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .captures_iter(text)
 | 
			
		||||
        .map(|c| c.extract())
 | 
			
		||||
        .filter_map(|(_, [l1, l2])| Some((utils::str_to_u32(l1)?, utils::str_to_u32(l2)?)))
 | 
			
		||||
        .map(|(x, y)| x * y)
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,30 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
 | 
			
		||||
pub fn answer(text : &str) -> u32 {
 | 
			
		||||
    match text.split_once("don't()") {
 | 
			
		||||
        Some((head, tail)) => {
 | 
			
		||||
            multiplication_sum(head) + tail.split("don't()").map(take_valid).sum::<u32>()
 | 
			
		||||
        },
 | 
			
		||||
        None => {
 | 
			
		||||
            multiplication_sum(text)
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn take_valid(s : &str) -> u32 {
 | 
			
		||||
    match s.split_once("do()") {
 | 
			
		||||
        Some((_, valid)) => multiplication_sum(valid),
 | 
			
		||||
        None => 0,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn multiplication_sum(s : &str) -> u32 {
 | 
			
		||||
    let re = Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)").unwrap();
 | 
			
		||||
    
 | 
			
		||||
    re.captures_iter(s)
 | 
			
		||||
        .map(|c| c.extract())
 | 
			
		||||
        .filter_map(|(_, [l1, l2])| Some((utils::str_to_u32(l1)?, utils::str_to_u32(l2)?)))
 | 
			
		||||
        .map(|(x, y)| x * y)
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,2 +1,87 @@
 | 
			
		|||
pub mod part_1;
 | 
			
		||||
pub mod part_2;
 | 
			
		||||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
pub fn answer(text : String) -> ( u16, u16 ) {
 | 
			
		||||
    let grid : Vec<Vec<char>> = from_string(text);
 | 
			
		||||
 | 
			
		||||
    let rows = grid.len();
 | 
			
		||||
    let cols = grid[0].len();
 | 
			
		||||
 | 
			
		||||
    ( find_xmas(&grid, rows as isize, cols as isize), find_x_mas(&grid, rows, cols) )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn find_x_mas(grid : &Vec<Vec<char>>, rows : usize, cols : usize) -> u16 {
 | 
			
		||||
    let mut total : u16 = 0;
 | 
			
		||||
 | 
			
		||||
    for y in 1..rows-1 {
 | 
			
		||||
        for x in 1..cols-1 {
 | 
			
		||||
            if grid[y][x] == 'A' {
 | 
			
		||||
                total = total + inspect_x_mas_location(&grid, x, y)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    total
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn find_xmas(grid : &Vec<Vec<char>>, rows : isize, cols : isize) -> u16 {
 | 
			
		||||
    let mut total : u16 = 0;
 | 
			
		||||
 | 
			
		||||
    for y in 0..rows {
 | 
			
		||||
        for x in 0..cols {
 | 
			
		||||
            if grid[y as usize][x as usize] == 'X' {
 | 
			
		||||
                total = total + inspect_xmas_location(&grid, x, y, rows, cols)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    total
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn from_string(s : String) -> Vec<Vec<char>> {
 | 
			
		||||
    s.split("\n").map(|s| s.chars().collect()).collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn inspect_x_mas_location(grid : &Vec<Vec<char>>, x : usize, y : usize) -> u16 {
 | 
			
		||||
    let c1 = grid[y-1][x-1];
 | 
			
		||||
    let c2 = grid[y-1][x+1];
 | 
			
		||||
    let c3 = grid[y+1][x-1];
 | 
			
		||||
    let c4 = grid[y+1][x+1];
 | 
			
		||||
 | 
			
		||||
    let horizontal = c1 == c2 && c3 == c4 && c1 != c3;
 | 
			
		||||
    let vertical = c1 == c3 && c2 == c4 && c1 != c2;
 | 
			
		||||
    let letters_match = (c1 == 'M' && c4 == 'S') || (c1 == 'S' && c4 == 'M');
 | 
			
		||||
 | 
			
		||||
    if letters_match && (horizontal || vertical) {
 | 
			
		||||
        1
 | 
			
		||||
    } else {
 | 
			
		||||
        0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn inspect_xmas_location(grid : &Vec<Vec<char>>, x : isize, y : isize, rows : isize, cols : isize) -> u16 {
 | 
			
		||||
    let mut total : u16 = 0;
 | 
			
		||||
 | 
			
		||||
    for dx in -1..=1 {
 | 
			
		||||
        for dy in -1..=1 {
 | 
			
		||||
            if dx == 0 && dy == 0 {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let (s_x, s_y) = ( x + 3 * dx, y + 3 * dy );
 | 
			
		||||
 | 
			
		||||
            if s_x < 0 || s_x >= rows || s_y < 0 || s_y >= cols {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let m = grid[(y + 1 * dy) as usize][(x + 1 * dx) as usize];
 | 
			
		||||
            let a = grid[(y + 2 * dy) as usize][(x + 2 * dx) as usize];
 | 
			
		||||
            let s = grid[(y + 3 * dy) as usize][(x + 3 * dx) as usize];
 | 
			
		||||
 | 
			
		||||
            if m == 'M' && a == 'A'  && s == 'S' {
 | 
			
		||||
                total = total + 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    total
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,47 +0,0 @@
 | 
			
		|||
pub fn answer(s : &str) -> u32 {
 | 
			
		||||
    let grid : Vec<Vec<char>> = s.split("\n")
 | 
			
		||||
        .map(|s| s.chars().collect())
 | 
			
		||||
        .collect();
 | 
			
		||||
    
 | 
			
		||||
    let rows : i32 = grid.len().try_into().unwrap();
 | 
			
		||||
    let cols : i32 = grid[0].len().try_into().unwrap();
 | 
			
		||||
    let mut total : u32 = 0;
 | 
			
		||||
 | 
			
		||||
    for y in 0..rows {
 | 
			
		||||
        for x in 0..cols {
 | 
			
		||||
            if grid[y as usize][x as usize] == 'X' {
 | 
			
		||||
                total = total + inspect_location(&grid, x, y, rows, cols)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    total
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn inspect_location(grid : &Vec<Vec<char>>, x : i32, y : i32, rows : i32, cols : i32) -> u32 {
 | 
			
		||||
    let mut total = 0;
 | 
			
		||||
 | 
			
		||||
    for dx in -1..=1 {
 | 
			
		||||
        for dy in -1..=1 {
 | 
			
		||||
            if dx == 0 && dy == 0 {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let (s_x, s_y) = ( x + 3 * dx, y + 3 * dy );
 | 
			
		||||
 | 
			
		||||
            if s_x < 0 || s_x >= rows || s_y < 0 || s_y >= cols {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let m = grid[(y + 1 * dy) as usize][(x + 1 * dx) as usize];
 | 
			
		||||
            let a = grid[(y + 2 * dy) as usize][(x + 2 * dx) as usize];
 | 
			
		||||
            let s = grid[(y + 3 * dy) as usize][(x + 3 * dx) as usize];
 | 
			
		||||
 | 
			
		||||
            if m == 'M' && a == 'A'  && s == 'S' {
 | 
			
		||||
                total = total + 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    total
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,36 +0,0 @@
 | 
			
		|||
pub fn answer(s : &str) -> u32 {
 | 
			
		||||
    let grid : Vec<Vec<char>> = s.split("\n")
 | 
			
		||||
        .map(|s| s.chars().collect())
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    let rows = grid.len();
 | 
			
		||||
    let cols = grid[0].len();
 | 
			
		||||
    let mut total : u32 = 0;
 | 
			
		||||
 | 
			
		||||
    for y in 1..rows-1 {
 | 
			
		||||
        for x in 1..cols-1 {
 | 
			
		||||
            if grid[y as usize][x as usize] == 'A' {
 | 
			
		||||
                total = total + inspect_location(&grid, x, y)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    total
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn inspect_location(grid : &Vec<Vec<char>>, x : usize, y : usize) -> u32 {
 | 
			
		||||
    let c1 = grid[y-1][x-1];
 | 
			
		||||
    let c2 = grid[y-1][x+1];
 | 
			
		||||
    let c3 = grid[y+1][x-1];
 | 
			
		||||
    let c4 = grid[y+1][x+1];
 | 
			
		||||
 | 
			
		||||
    let horizontal = c1 == c2 && c3 == c4 && c1 != c3;
 | 
			
		||||
    let vertical = c1 == c3 && c2 == c4 && c1 != c2;
 | 
			
		||||
    let letters_match = (c1 == 'M' && c4 == 'S') || (c1 == 'S' && c4 == 'M');
 | 
			
		||||
 | 
			
		||||
    if letters_match && (horizontal || vertical) {
 | 
			
		||||
        1
 | 
			
		||||
    } else {
 | 
			
		||||
        0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,2 +1,93 @@
 | 
			
		|||
pub mod part_1;
 | 
			
		||||
pub mod part_2;
 | 
			
		||||
use crate::utils;
 | 
			
		||||
use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
type HandBook = Vec<u8>;
 | 
			
		||||
type RuleSet = HashSet<(u8, u8)>;
 | 
			
		||||
 | 
			
		||||
pub fn answer(text : String) -> ( u16, u16 ) {
 | 
			
		||||
    let ( rules, handbooks ) : ( RuleSet, Vec<HandBook> ) = parse_input(text);
 | 
			
		||||
    let ( sorted, unsorted ) : ( Vec<_>, Vec<_> ) = handbooks
 | 
			
		||||
        .iter()
 | 
			
		||||
        .partition(|hb| follows_rules(hb, &rules));
 | 
			
		||||
    
 | 
			
		||||
    ( sorted.iter().map(|hb| sorted_middle_value(hb) as u16).sum()
 | 
			
		||||
    , unsorted.iter().map(|hb| unsorted_middle_value(hb, &rules)).sum()
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn follows_rules(handbook : &HandBook, rules : &RuleSet) -> bool {
 | 
			
		||||
    handbook.windows(2).all(|rule| {
 | 
			
		||||
        match (rule.get(0), rule.get(1)) {
 | 
			
		||||
            (Some(a), Some(b)) => {
 | 
			
		||||
                rules.contains(&(*a, *b))
 | 
			
		||||
            },
 | 
			
		||||
            // Invalid situations
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_hand_line(s : &str) -> Option<Vec<u8>> {
 | 
			
		||||
    let v : Vec<u8> = s.split(",").filter_map(utils::str_to_u8).collect();
 | 
			
		||||
    
 | 
			
		||||
    if v.len() == 0 { None } else { Some(v) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_input(s : String) -> (RuleSet, Vec<HandBook>) {
 | 
			
		||||
    let (r, h) = s.split_once("\n\n").unwrap_or(("", ""));
 | 
			
		||||
    
 | 
			
		||||
    ( r.split("\n").filter_map(parse_rule_line).collect()
 | 
			
		||||
    , h.split("\n").filter_map(parse_hand_line).collect()
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_rule_line(s : &str) -> Option<(u8, u8)> {
 | 
			
		||||
    let (l, r) = s.split_once("|")?;
 | 
			
		||||
 | 
			
		||||
    Some(( utils::str_to_u8(l)?, utils::str_to_u8(r)? ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn unsorted_middle_value(handbook : &HandBook, rules : &RuleSet) -> u16 {
 | 
			
		||||
    let (head, tail) = handbook.split_first().unwrap();
 | 
			
		||||
    
 | 
			
		||||
    let mut cursor : u8 = *head;
 | 
			
		||||
    let mut num_lower : u8 = 0;
 | 
			
		||||
    let mut num_higher : u8 = 0;
 | 
			
		||||
    let mut lower : Vec<u8> = Vec::new();
 | 
			
		||||
    let mut higher : Vec<u8> = Vec::new();
 | 
			
		||||
    let mut stack : Vec<u8> = Vec::from(tail);
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        // Classify numbers based on their order
 | 
			
		||||
        while let Some(n) = stack.pop() {
 | 
			
		||||
            if rules.contains(&( cursor, n )) {
 | 
			
		||||
                higher.push(n);
 | 
			
		||||
            } else {
 | 
			
		||||
                lower.push(n);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if we've found the correct number
 | 
			
		||||
        let low = num_lower + lower.len() as u8;
 | 
			
		||||
        let high = num_higher + higher.len() as u8;
 | 
			
		||||
        if low == high {
 | 
			
		||||
            return cursor.into();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Otherwise, rearrange the variables for the next step
 | 
			
		||||
        if low < high {
 | 
			
		||||
            (num_lower, lower) = (low + 1, Vec::new());
 | 
			
		||||
            (stack, higher) = (higher, Vec::new());
 | 
			
		||||
        } else {
 | 
			
		||||
            (num_higher, higher) = (high + 1, Vec::new());
 | 
			
		||||
            (stack, lower) = (lower, Vec::new());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Pick the next value
 | 
			
		||||
        cursor = stack.pop().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn sorted_middle_value(handbook : &HandBook) -> u8 {
 | 
			
		||||
    handbook[handbook.len() / 2]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,55 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
type HandBook = Vec<u32>;
 | 
			
		||||
type RuleSet = Vec<(u32, u32)>;
 | 
			
		||||
 | 
			
		||||
pub fn answer(s : &str) -> u32 {
 | 
			
		||||
    let ( rules, handbooks ) = parse_input(s);
 | 
			
		||||
 | 
			
		||||
    handbooks.iter()
 | 
			
		||||
        .filter(|hb| follows_rules(hb, &rules))
 | 
			
		||||
        .map(middle_value)
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn follows_rules(handbook : &HandBook, rules : &RuleSet) -> bool {
 | 
			
		||||
    rules.iter().all(|rule| follows_rule(handbook, rule))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn follows_rule(handbook : &HandBook, (first, second) : &(u32, u32)) -> bool {
 | 
			
		||||
    let mut encountered_second : bool = false;
 | 
			
		||||
 | 
			
		||||
    for num in handbook.iter() {
 | 
			
		||||
        if num == first {
 | 
			
		||||
            return !encountered_second;
 | 
			
		||||
        } else if num == second {
 | 
			
		||||
            encountered_second = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn middle_value(handbook : &HandBook) -> u32 {
 | 
			
		||||
    handbook[handbook.len() / 2]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_hand_line(s : &str) -> Option<Vec<u32>> {
 | 
			
		||||
    let v : Vec<u32> = s.split(",").filter_map(utils::str_to_u32).collect();
 | 
			
		||||
 | 
			
		||||
    if v.len() == 0 { None } else { Some(v) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_input(s : &str) -> (RuleSet, Vec<HandBook>) {
 | 
			
		||||
    let (r, h) = s.split_once("\n\n").unwrap_or(("", ""));
 | 
			
		||||
 | 
			
		||||
    ( r.split("\n").filter_map(parse_rule_line).collect()
 | 
			
		||||
    , h.split("\n").filter_map(parse_hand_line).collect()
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_rule_line(s : &str) -> Option<(u32, u32)> {
 | 
			
		||||
    let (l, r) = s.split_once("|")?;
 | 
			
		||||
 | 
			
		||||
    Some(( utils::str_to_u32(l)?, utils::str_to_u32(r)? ))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,72 +0,0 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
use std::cmp::Ordering;
 | 
			
		||||
 | 
			
		||||
type HandBook = Vec<u32>;
 | 
			
		||||
type RuleSet = Vec<(u32, u32)>;
 | 
			
		||||
 | 
			
		||||
pub fn answer(s : &str) -> u32 {
 | 
			
		||||
    let ( rules, mut handbooks ) = parse_input(s);
 | 
			
		||||
 | 
			
		||||
    handbooks.iter_mut()
 | 
			
		||||
        .filter(|hb| !follows_rules(hb, &rules))
 | 
			
		||||
        .map(|hb| { reorder_pages(hb, &rules); middle_value(hb) })
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn page_cmp(a : &u32, b : &u32, rules : &RuleSet) -> Ordering {
 | 
			
		||||
    for (r1, r2) in rules.iter() {
 | 
			
		||||
        if r1 == a && r2 == b {
 | 
			
		||||
            return Ordering::Less;
 | 
			
		||||
        } else if r1 == b && r2 == a {
 | 
			
		||||
            return Ordering::Greater;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ordering::Equal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn follows_rules(handbook : &HandBook, rules : &RuleSet) -> bool {
 | 
			
		||||
    rules.iter().all(|rule| follows_rule(handbook, rule))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn follows_rule(handbook : &HandBook, (first, second) : &(u32, u32)) -> bool {
 | 
			
		||||
    let mut encountered_second : bool = false;
 | 
			
		||||
 | 
			
		||||
    for num in handbook.iter() {
 | 
			
		||||
        if num == first {
 | 
			
		||||
            return !encountered_second;
 | 
			
		||||
        } else if num == second {
 | 
			
		||||
            encountered_second = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn middle_value(handbook : &HandBook) -> u32 {
 | 
			
		||||
    handbook[handbook.len() / 2]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_hand_line(s : &str) -> Option<Vec<u32>> {
 | 
			
		||||
    let v : Vec<u32> = s.split(",").filter_map(utils::str_to_u32).collect();
 | 
			
		||||
 | 
			
		||||
    if v.len() == 0 { None } else { Some(v) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_input(s : &str) -> (RuleSet, Vec<HandBook>) {
 | 
			
		||||
    let (r, h) = s.split_once("\n\n").unwrap_or(("", ""));
 | 
			
		||||
 | 
			
		||||
    ( r.split("\n").filter_map(parse_rule_line).collect()
 | 
			
		||||
    , h.split("\n").filter_map(parse_hand_line).collect()
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_rule_line(s : &str) -> Option<(u32, u32)> {
 | 
			
		||||
    let (l, r) = s.split_once("|")?;
 | 
			
		||||
 | 
			
		||||
    Some(( utils::str_to_u32(l)?, utils::str_to_u32(r)? ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn reorder_pages(handbook : &mut HandBook, rules : &RuleSet) {
 | 
			
		||||
    handbook.sort_unstable_by(|a, b| page_cmp(a, b, rules));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/lib.rs
								
								
								
								
							
							
						
						
									
										16
									
								
								src/lib.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
mod day_01;
 | 
			
		||||
mod day_02;
 | 
			
		||||
mod day_03;
 | 
			
		||||
mod day_04;
 | 
			
		||||
mod day_05;
 | 
			
		||||
mod utils;
 | 
			
		||||
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
| 
						 | 
				
			
			@ -29,3 +31,17 @@ pub fn day_03() -> DailyOutput {
 | 
			
		|||
 | 
			
		||||
    ( p1 as u128, p2 as u128, d )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn day_04() -> DailyOutput {
 | 
			
		||||
    let s : String = read_from_file("inputs/04.txt");
 | 
			
		||||
    let (p1, p2, d) = diagnostics::benchmark(s, day_04::answer);
 | 
			
		||||
 | 
			
		||||
    ( p1 as u128, p2 as u128, d )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn day_05() -> DailyOutput {
 | 
			
		||||
    let s : String = read_from_file("inputs/05.txt");
 | 
			
		||||
    let (p1, p2, d) = diagnostics::benchmark(s, day_05::answer);
 | 
			
		||||
 | 
			
		||||
    ( p1 as u128, p2 as u128, d )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,8 @@ fn main() {
 | 
			
		|||
    aoc.insert(1, aoc_2024::day_01());
 | 
			
		||||
    aoc.insert(2, aoc_2024::day_02());
 | 
			
		||||
    aoc.insert(3, aoc_2024::day_03());
 | 
			
		||||
    aoc.insert(4, aoc_2024::day_04());
 | 
			
		||||
    aoc.insert(5, aoc_2024::day_05());
 | 
			
		||||
 | 
			
		||||
    aoc.show_diagnostics();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,11 +39,11 @@ impl AdventOfCode {
 | 
			
		|||
            .map(|(day, result)| {
 | 
			
		||||
                match result {
 | 
			
		||||
                    Some(r) => {
 | 
			
		||||
                        println!("{}", r.to_string(day, MAX_MS_ON_SCREEN));
 | 
			
		||||
                        println!("{}", r.to_string(day + 1, MAX_MS_ON_SCREEN));
 | 
			
		||||
                        r.duration.as_millis()
 | 
			
		||||
                    },
 | 
			
		||||
                    None => {
 | 
			
		||||
                        println!("{}", empty_diagnostic(day));
 | 
			
		||||
                        println!("{}", empty_diagnostic(day + 1));
 | 
			
		||||
                        0
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue