Quest 9: Encoded in the Scales

  • 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/

  • hades@programming.devOPM
    link
    fedilink
    arrow-up
    1
    ·
    14 days ago

    Rust

    use std::collections::HashMap;
    
    use bit_set::BitSet;
    use itertools::{Itertools, izip};
    
    type BitSetBase = u32;
    
    fn is_child(child: &str, parent1: &str, parent2: &str) -> bool {
        izip!(child.chars(), parent1.chars(), parent2.chars()).all(|(c, a, b)| c == a || c == b)
    }
    
    fn similarity(a: &str, b: &str) -> usize {
        izip!(a.chars(), b.chars()).filter(|(a, b)| a == b).count()
    }
    
    fn unequality_bitset(a: &str, b: &str) -> BitSet<BitSetBase> {
        izip!(a.chars(), b.chars())
            .enumerate()
            .filter_map(|(i, (a_ch, b_ch))| if a_ch == b_ch { None } else { Some(i) })
            .collect()
    }
    
    pub fn solve_part_1(input: &str) -> String {
        let dnas = input
            .lines()
            .map(|l| {
                let (_, dna) = l.split_once(":").unwrap();
                dna
            })
            .collect::<Vec<_>>();
        let (child, parenta, parentb) = if is_child(dnas[0], dnas[1], dnas[2]) {
            (dnas[0], dnas[1], dnas[2])
        } else if is_child(dnas[1], dnas[0], dnas[2]) {
            (dnas[1], dnas[0], dnas[2])
        } else {
            (dnas[2], dnas[0], dnas[1])
        };
        (similarity(child, parenta) * similarity(child, parentb)).to_string()
    }
    
    pub fn solve_part_2(input: &str) -> String {
        let dnas = input
            .lines()
            .map(|l| {
                let (_, dna) = l.split_once(":").unwrap();
                dna
            })
            .collect::<Vec<_>>();
        let unequalities = dnas
            .iter()
            .enumerate()
            .cartesian_product(dnas.iter().enumerate())
            .map(|((a_id, a), (b_id, b))| ((a_id, b_id), unequality_bitset(a, b)))
            .collect::<HashMap<_, _>>();
        let mut total_similarities = 0;
        'outer: for (child_id, &child_string) in dnas.iter().enumerate() {
            for (parent_a_id, &parent_a_string) in dnas.iter().enumerate() {
                if parent_a_id == child_id {
                    continue;
                }
                for (parent_b_id, &parent_b_string) in dnas.iter().enumerate() {
                    if parent_b_id == child_id || parent_b_id == parent_a_id {
                        continue;
                    }
                    if unequalities[&(child_id, parent_a_id)]
                        .intersection(&unequalities[&(child_id, parent_b_id)])
                        .count()
                        == 0
                    {
                        total_similarities += similarity(child_string, parent_a_string)
                            * similarity(child_string, parent_b_string);
                        continue 'outer;
                    }
                }
            }
        }
        total_similarities.to_string()
    }
    
    pub fn solve_part_3(input: &str) -> String {
        let dnas = input
            .lines()
            .map(|l| {
                let (_, dna) = l.split_once(":").unwrap();
                dna
            })
            .collect::<Vec<_>>();
        let unequalities = dnas
            .iter()
            .enumerate()
            .cartesian_product(dnas.iter().enumerate())
            .map(|((a_id, a), (b_id, b))| ((a_id, b_id), unequality_bitset(a, b)))
            .collect::<HashMap<_, _>>();
        let mut parents: HashMap<usize, (usize, usize)> = HashMap::new();
        'outer: for (child_id, _) in dnas.iter().enumerate() {
            for (parent_a_id, _) in dnas.iter().enumerate() {
                if parent_a_id == child_id {
                    continue;
                }
                for (parent_b_id, _) in dnas.iter().enumerate() {
                    if parent_b_id == child_id || parent_b_id == parent_a_id {
                        continue;
                    }
                    if unequalities[&(child_id, parent_a_id)]
                        .intersection(&unequalities[&(child_id, parent_b_id)])
                        .count()
                        == 0
                    {
                        parents.insert(child_id, (parent_a_id, parent_b_id));
                        continue 'outer;
                    }
                }
            }
        }
        let mut family_id = (0usize..dnas.len()).collect::<Vec<_>>();
        for (child, (parent_a, parent_b)) in parents.drain() {
            let destination_family_id = family_id[child];
            let merge_families = (family_id[parent_a], family_id[parent_b]);
            for candidate_family in family_id.iter_mut() {
                if *candidate_family == merge_families.0 || *candidate_family == merge_families.1 {
                    *candidate_family = destination_family_id;
                }
            }
        }
        let families = family_id
            .iter()
            .enumerate()
            .map(|(member_idx, &family_idx)| (family_idx, member_idx))
            .into_group_map();
        families
            .values()
            .max_by_key(|f| f.len())
            .unwrap()
            .iter()
            .map(|member_id| member_id + 1)
            .sum::<usize>()
            .to_string()
    }