Quest 14: The Game of Light

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

Link to participate: https://everybody.codes/

  • hadesOPM
    link
    fedilink
    arrow-up
    2
    ·
    2 months ago

    Rust

    Finally caught up with the puzzles.

    use std::collections::{HashMap, HashSet};
    
    use itertools::Itertools;
    
    pub fn solve_part_1(input: &str) -> String {
        let mut barrels: HashSet<(isize, isize)> = input
            .lines()
            .enumerate()
            .flat_map(|(i, line)| {
                line.chars()
                    .enumerate()
                    .filter(|&(_, ch)| ch == '#')
                    .map(move |(j, _)| (i as isize, j as isize))
            })
            .collect();
        let max_i = barrels.iter().map(|(i, _)| *i).max().unwrap();
        let max_j = barrels.iter().map(|(_, j)| *j).max().unwrap();
        let mut total_active = 0;
        for _ in 0..10 {
            let mut next_barrels = HashSet::new();
            for i in 0..=max_i {
                for j in 0..=max_j {
                    let active_neighbours = [(-1, -1), (1, 1), (-1, 1), (1, -1)]
                        .iter()
                        .filter(|(di, dj)| barrels.contains(&(i + di, j + dj)))
                        .count();
                    let will_be_active = if barrels.contains(&(i, j)) {
                        active_neighbours % 2 == 1
                    } else {
                        active_neighbours % 2 == 0
                    };
                    if will_be_active {
                        next_barrels.insert((i, j));
                    }
                }
            }
            barrels = next_barrels;
            total_active += barrels.len();
        }
        total_active.to_string()
    }
    
    pub fn solve_part_2(input: &str) -> String {
        let mut barrels: HashSet<(isize, isize)> = input
            .lines()
            .enumerate()
            .flat_map(|(i, line)| {
                line.chars()
                    .enumerate()
                    .filter(|&(_, ch)| ch == '#')
                    .map(move |(j, _)| (i as isize, j as isize))
            })
            .collect();
        let max_i = barrels.iter().map(|(i, _)| *i).max().unwrap();
        let max_j = barrels.iter().map(|(_, j)| *j).max().unwrap();
        let mut total_active = 0;
        for _ in 0..2025 {
            let mut next_barrels = HashSet::new();
            for i in 0..=max_i {
                for j in 0..=max_j {
                    let active_neighbours = [(-1, -1), (1, 1), (-1, 1), (1, -1)]
                        .iter()
                        .filter(|(di, dj)| barrels.contains(&(i + di, j + dj)))
                        .count();
                    let will_be_active = if barrels.contains(&(i, j)) {
                        active_neighbours % 2 == 1
                    } else {
                        active_neighbours % 2 == 0
                    };
                    if will_be_active {
                        next_barrels.insert((i, j));
                    }
                }
            }
            barrels = next_barrels;
            total_active += barrels.len();
        }
        total_active.to_string()
    }
    
    pub fn solve_part_3(input: &str) -> String {
        let pattern: HashSet<(isize, isize)> = input
            .lines()
            .enumerate()
            .flat_map(|(i, line)| {
                line.chars()
                    .enumerate()
                    .filter(|&(_, ch)| ch == '#')
                    .map(move |(j, _)| (i as isize, j as isize))
            })
            .collect();
        let pattern_max_i = pattern.iter().map(|(i, _)| *i).max().unwrap();
        let pattern_max_j = pattern.iter().map(|(_, j)| *j).max().unwrap();
        assert_eq!(7, pattern_max_i);
        assert_eq!(7, pattern_max_j);
        let mut barrels: HashSet<(isize, isize)> = HashSet::new();
        let max_i = 33;
        let max_j = 33;
        let mut state_map = HashMap::<Vec<(isize, isize)>, usize>::new();
        state_map.insert(vec![], 0);
        let mut current_round = 0;
        let rounds_to_simulate = 1000000000;
        let mut active_tiles_after_rounds = vec![0];
        while current_round < rounds_to_simulate {
            let mut next_barrels = HashSet::new();
            for i in 0..=max_i {
                for j in 0..=max_j {
                    let active_neighbours = [(-1, -1), (1, 1), (-1, 1), (1, -1)]
                        .iter()
                        .filter(|(di, dj)| barrels.contains(&(i + di, j + dj)))
                        .count();
                    let will_be_active = if barrels.contains(&(i, j)) {
                        active_neighbours % 2 == 1
                    } else {
                        active_neighbours % 2 == 0
                    };
                    if will_be_active {
                        next_barrels.insert((i, j));
                    }
                }
            }
            barrels = next_barrels;
            current_round += 1;
            let state_key: Vec<_> = barrels.iter().copied().collect();
            if let Some(&seen_before_after_round) = state_map.get(&state_key) {
                let loop_length = current_round - seen_before_after_round;
                let loops_remaining = (rounds_to_simulate - current_round) / loop_length;
                let iterations_remaining =
                    rounds_to_simulate - current_round - (loops_remaining * loop_length);
                return (active_tiles_after_rounds[0..current_round]
                    .iter()
                    .sum::<usize>()
                    + active_tiles_after_rounds[seen_before_after_round..current_round]
                        .iter()
                        .sum::<usize>()
                        * loops_remaining
                    + active_tiles_after_rounds
                        [seen_before_after_round..(seen_before_after_round + iterations_remaining)]
                        .iter()
                        .sum::<usize>())
                .to_string();
            }
            state_map.insert(state_key, current_round);
            let does_pattern_match = (13..=20)
                .cartesian_product(13..=20)
                .all(|(i, j)| barrels.contains(&(i, j)) == pattern.contains(&(i - 13, j - 13)));
            active_tiles_after_rounds.push(if does_pattern_match { barrels.len() } else { 0 });
        }
        active_tiles_after_rounds.iter().sum::<usize>().to_string()
    }