• 20 Posts
  • 26 Comments
Joined 1 year ago
cake
Cake day: June 21st, 2023

help-circle

  • bugsmith@programming.devtoFeddit UK@feddit.ukWhat's happening?
    ·
    edit-2
    6 months ago

    Well TIL. Didn't think to search for it. I am in my thirties and have lived in the UK my whole life, and I have never encountered or heard of such a device until now!
    Given that, I think it's really creative name to use for such a purpose 👍








  • Today's problems felt really refreshing after yesterday.

    Solution in Rust 🦀

    View formatted code on GitLab

    Code
    use std::{
        collections::HashSet,
        env, fs,
        io::{self, BufRead, BufReader, Read},
    };
    
    fn main() -> io::Result<()> {
        let args: Vec = env::args().collect();
        let filename = &args[1];
        let file1 = fs::File::open(filename)?;
        let file2 = fs::File::open(filename)?;
        let reader1 = BufReader::new(file1);
        let reader2 = BufReader::new(file2);
    
        println!("Part one: {}", process_part_one(reader1));
        println!("Part two: {}", process_part_two(reader2));
        Ok(())
    }
    
    fn parse_data(reader: BufReader) -> Vec> {
        let lines = reader.lines().flatten();
        let data: Vec<_> = lines
            .map(|line| {
                line.split(':')
                    .last()
                    .expect("text after colon")
                    .split_whitespace()
                    .map(|s| s.parse::().expect("numbers"))
                    .collect::>()
            })
            .collect();
        data
    }
    
    fn calculate_ways_to_win(time: u64, dist: u64) -> HashSet {
        let mut wins = HashSet::::new();
        for t in 1..time {
            let d = t * (time - t);
            if d > dist {
                wins.insert(t);
            }
        }
        wins
    }
    
    fn process_part_one(reader: BufReader) -> u64 {
        let data = parse_data(reader);
        let results: Vec<_> = data[0].iter().zip(data[1].iter()).collect();
        let mut win_method_qty: Vec = Vec::new();
        for r in results {
            win_method_qty.push(calculate_ways_to_win(*r.0, *r.1).len() as u64);
        }
        win_method_qty.iter().product()
    }
    
    fn process_part_two(reader: BufReader) -> u64 {
        let data = parse_data(reader);
        let joined_data: Vec<_> = data
            .iter()
            .map(|v| {
                v.iter()
                    .map(|d| d.to_string())
                    .collect::>()
                    .join("")
                    .parse::()
                    .expect("all digits")
            })
            .collect();
    
        calculate_ways_to_win(joined_data[0], joined_data[1]).len() as u64
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        const INPUT: &str = "Time:      7  15   30
    Distance:  9  40  200";
    
        #[test]
        fn test_process_part_one() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(288, process_part_one(BufReader::new(input_bytes)));
        }
    
        #[test]
        fn test_process_part_two() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(71503, process_part_two(BufReader::new(input_bytes)));
        }
    }
    

  • Like many others, I really didn't enjoy this one. I particularly struggled with part 02, which ended up with me just brute forcing it and checking each seed. On my system it took over 15 minutes to run, which is truly awful. I'm open to pointers on how I could better have solved part two.

    Solution in Rust 🦀

    Formatted Solution on GitLab

    Code
    use std::{
        env, fs,
        io::{self, BufReader, Read},
    };
    
    fn main() -> io::Result<()> {
        let args: Vec = env::args().collect();
        let filename = &args[1];
        let file1 = fs::File::open(filename)?;
        let file2 = fs::File::open(filename)?;
        let mut reader1 = BufReader::new(file1);
        let mut reader2 = BufReader::new(file2);
    
        println!("Part one: {}", process_part_one(&mut reader1));
        println!("Part two: {}", process_part_two(&mut reader2));
        Ok(())
    }
    
    #[derive(Debug)]
    struct Map {
        lines: Vec,
    }
    
    impl Map {
        fn map_to_lines(&self, key: u32) -> u32 {
            for line in &self.lines {
                if line.in_range(key) {
                    return line.map(key);
                }
            }
            key
        }
    }
    
    #[derive(Debug)]
    struct MapLine {
        dest_range: u32,
        source_range: u32,
        range_length: u32,
    }
    
    impl MapLine {
        fn map(&self, key: u32) -> u32 {
            let diff = key - self.source_range;
            if self.dest_range as i64 + diff as i64 > 0 {
                return (self.dest_range as i64 + diff as i64) as u32;
            }
            key
        }
    
        fn in_range(&self, key: u32) -> bool {
            self.source_range <= key
                && (key as i64) < self.source_range as i64 + self.range_length as i64
        }
    }
    
    fn parse_input(reader: &amp;mut BufReader) -> (Vec, Vec<map>) {
        let mut almanac = String::new();
        reader
            .read_to_string(&amp;mut almanac)
            .expect("read successful");
        let parts: Vec&lt;&amp;str> = almanac.split("\n\n").collect();
        let (seeds, others) = parts.split_first().expect("at least one part");
        let seeds: Vec&lt;_> = seeds
            .split(": ")
            .last()
            .expect("at least one")
            .split_whitespace()
            .map(|s| s.to_string())
            .collect();
        let maps: Vec&lt;_> = others
            .iter()
            .map(|item| {
                let lines_iter = item
                    .split(':')
                    .last()
                    .expect("exists")
                    .trim()
                    .split('\n')
                    .map(|nums| {
                        let nums_split = nums.split_whitespace().collect::>();
                        MapLine {
                            dest_range: nums_split[0].parse().expect("is digit"),
                            source_range: nums_split[1].parse().expect("is digit"),
                            range_length: nums_split[2].parse().expect("is digit"),
                        }
                    });
                Map {
                    lines: lines_iter.collect(),
                }
            })
            .collect();
        (seeds, maps)
    }
    
    fn process_part_one(reader: &amp;mut BufReader) -> u32 {
        let (seeds, maps) = parse_input(reader);
        let mut res = u32::MAX;
        for seed in &amp;seeds {
            let mut val = seed.parse::().expect("is digits");
            for map in &amp;maps {
                val = map.map_to_lines(val);
            }
            res = u32::min(res, val);
        }
        res
    }
    
    fn process_part_two(reader: &amp;mut BufReader) -> u32 {
        let (seeds, maps) = parse_input(reader);
        let seed_chunks: Vec&lt;_> = seeds.chunks(2).collect();
        let mut res = u32::MAX;
        for chunk in seed_chunks {
            let range_start: u32 = chunk[0].parse().expect("is digits");
            let range_length: u32 = chunk[1].parse().expect("is digits");
            let range_end: u32 = range_start + range_length;
            for seed in range_start..range_end {
                let mut val = seed;
                for map in &amp;maps {
                    val = map.map_to_lines(val);
                }
                res = u32::min(res, val);
            }
        }
        res
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        const INPUT: &amp;str = "seeds: 79 14 55 13
    
    seed-to-soil map:
    50 98 2
    52 50 48
    
    soil-to-fertilizer map:
    0 15 37
    37 52 2
    39 0 15
    
    fertilizer-to-water map:
    49 53 8
    0 11 42
    42 0 7
    57 7 4
    
    water-to-light map:
    88 18 7
    18 25 70
    
    light-to-temperature map:
    45 77 23
    81 45 19
    68 64 13
    
    temperature-to-humidity map:
    0 69 1
    1 0 69
    
    humidity-to-location map:
    60 56 37
    56 93 4";
    
        #[test]
        fn test_process_part_one() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(35, process_part_one(&amp;mut BufReader::new(input_bytes)));
        }
    
        #[test]
        fn test_process_part_two() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(46, process_part_two(&amp;mut BufReader::new(input_bytes)));
        }
    }
    

    :::</map>


  • Late as always (actually a day late by UK time).

    My solution to this one runs slow, but it gets the job done. I didn't actually need the CardInfo struct by the time I was done, but couldn't be bothered to remove it. Previously, it held more than just count.

    Day 04 in Rust 🦀

    View formatted on GitLab

    use std::{
        collections::BTreeMap,
        env, fs,
        io::{self, BufRead, BufReader, Read},
    };
    
    fn main() -> io::Result&lt;()> {
        let args: Vec = env::args().collect();
        let filename = &amp;args[1];
        let file1 = fs::File::open(filename)?;
        let file2 = fs::File::open(filename)?;
        let reader1 = BufReader::new(file1);
        let reader2 = BufReader::new(file2);
    
        println!("Part one: {}", process_part_one(reader1));
        println!("Part two: {}", process_part_two(reader2));
        Ok(())
    }
    
    fn process_part_one(reader: BufReader) -> u32 {
        let mut sum = 0;
        for line in reader.lines().flatten() {
            let card_data: Vec&lt;_> = line.split(": ").collect();
            let all_numbers = card_data[1];
            let number_parts: Vec> = all_numbers
                .split('|')
                .map(|x| {
                    x.replace("  ", " ")
                        .split_whitespace()
                        .map(|val| val.to_string())
                        .collect()
                })
                .collect();
            let (winning_nums, owned_nums) = (&amp;number_parts[0], &amp;number_parts[1]);
            let matches = owned_nums
                .iter()
                .filter(|num| winning_nums.contains(num))
                .count();
            if matches > 0 {
                sum += 2_u32.pow((matches - 1) as u32);
            }
        }
        sum
    }
    
    #[derive(Debug)]
    struct CardInfo {
        count: u32,
    }
    
    fn process_part_two(reader: BufReader) -> u32 {
        let mut cards: BTreeMap = BTreeMap::new();
        for line in reader.lines().flatten() {
            let card_data: Vec&lt;_> = line.split(": ").collect();
            let card_id: u32 = card_data[0]
                .replace("Card", "")
                .trim()
                .parse()
                .expect("is digit");
            let all_numbers = card_data[1];
            let number_parts: Vec> = all_numbers
                .split('|')
                .map(|x| {
                    x.replace("  ", " ")
                        .split_whitespace()
                        .map(|val| val.to_string())
                        .collect()
                })
                .collect();
            let (winning_nums, owned_nums) = (&amp;number_parts[0], &amp;number_parts[1]);
            let matches = owned_nums
                .iter()
                .filter(|num| winning_nums.contains(num))
                .count();
            let card_details = CardInfo { count: 1 };
            if let Some(old_card_info) = cards.insert(card_id, card_details) {
                let card_entry = cards.get_mut(&amp;card_id);
                card_entry.expect("card exists").count += old_card_info.count;
            };
            let current_card = cards.get(&amp;card_id).expect("card exists");
            if matches > 0 {
                for _ in 0..current_card.count {
                    for i in (card_id + 1)..=(matches as u32) + card_id {
                        let new_card_info = CardInfo { count: 1 };
                        if let Some(old_card_info) = cards.insert(i, new_card_info) {
                            let card_entry = cards.get_mut(&amp;i).expect("card exists");
                            card_entry.count += old_card_info.count;
                        }
                    }
                }
            }
        }
        let sum = cards.iter().fold(0, |acc, c| acc + c.1.count);
        sum
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        const INPUT: &amp;str = "Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
    Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
    Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
    Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
    Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
    Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11";
    
        #[test]
        fn test_process_part_one() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(13, process_part_one(BufReader::new(input_bytes)));
        }
    
        #[test]
        fn test_process_part_two() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(30, process_part_two(BufReader::new(input_bytes)));
        }
    }
    
    

  • Edit: Updated now with part 2.

    Managed to have a crack at this a bit earlier today, I've only done Part 01 so far. I'll update with Part 02 later.

    I tackled this with the personal challenge of not loading the entire puzzle input into memory, which would have made this a bit easier.

    Solution in Rust 🦀

    View formatted on GitLab

    use std::{
        env, fs,
        io::{self, BufRead, BufReader, Read},
    };
    
    fn main() -> io::Result&lt;()> {
        let args: Vec = env::args().collect();
        let filename = &amp;args[1];
        let file1 = fs::File::open(filename)?;
        let file2 = fs::File::open(filename)?;
        let reader1 = BufReader::new(file1);
        let reader2 = BufReader::new(file2);
    
        println!("Part one: {}", process_part_one(reader1));
        println!("Part two: {}", process_part_two(reader2));
        Ok(())
    }
    
    fn process_part_one(reader: BufReader) -> u32 {
        let mut lines = reader.lines().peekable();
        let mut prev_line: Option = None;
        let mut sum = 0;
        while let Some(line) = lines.next() {
            let current_line = line.expect("line exists");
            let next_line = match lines.peek() {
                Some(Ok(line)) => Some(line),
                Some(Err(_)) => None,
                None => None,
            };
            match (prev_line, next_line) {
                (None, Some(next)) => {
                    let lines = vec![¤t_line, next];
                    sum += parse_lines(lines, true);
                }
                (Some(prev), Some(next)) => {
                    let lines = vec![&amp;prev, ¤t_line, next];
                    sum += parse_lines(lines, false);
                }
                (Some(prev), None) => {
                    let lines = vec![&amp;prev, ¤t_line];
                    sum += parse_lines(lines, false);
                }
                (None, None) => {}
            }
    
            prev_line = Some(current_line);
        }
        sum
    }
    
    fn process_part_two(reader: BufReader) -> u32 {
        let mut lines = reader.lines().peekable();
        let mut prev_line: Option = None;
        let mut sum = 0;
        while let Some(line) = lines.next() {
            let current_line = line.expect("line exists");
            let next_line = match lines.peek() {
                Some(Ok(line)) => Some(line),
                Some(Err(_)) => None,
                None => None,
            };
            match (prev_line, next_line) {
                (None, Some(next)) => {
                    let lines = vec![¤t_line, next];
                    sum += parse_lines_for_gears(lines, true);
                }
                (Some(prev), Some(next)) => {
                    let lines = vec![&amp;prev, ¤t_line, next];
                    sum += parse_lines_for_gears(lines, false);
                }
                (Some(prev), None) => {
                    let lines = vec![&amp;prev, ¤t_line];
                    sum += parse_lines_for_gears(lines, false);
                }
                (None, None) => {}
            }
    
            prev_line = Some(current_line);
        }
    
        sum
    }
    
    fn parse_lines(lines: Vec&lt;&amp;String>, first_line: bool) -> u32 {
        let mut sum = 0;
        let mut num = 0;
        let mut valid = false;
        let mut char_vec: Vec> = Vec::new();
        for line in lines {
            char_vec.push(line.chars().collect());
        }
        let chars = match first_line {
            true => &amp;char_vec[0],
            false => &amp;char_vec[1],
        };
        for i in 0..chars.len() {
            if chars[i].is_digit(10) {
                // Add the digit to the number
                num = num * 10 + chars[i].to_digit(10).expect("is digit");
    
                // Check the surrounding character for non-period symbols
                for &amp;x in &amp;[-1, 0, 1] {
                    for chars in &amp;char_vec {
                        if (i as isize + x).is_positive() &amp;&amp; ((i as isize + x) as usize) &lt; chars.len() {
                            let index = (i as isize + x) as usize;
                            if !chars[index].is_digit(10) &amp;&amp; chars[index] != '.' {
                                valid = true;
                            }
                        }
                    }
                }
            } else {
                if valid {
                    sum += num;
                }
                valid = false;
                num = 0;
            }
        }
        if valid {
            sum += num;
        }
        sum
    }
    
    fn parse_lines_for_gears(lines: Vec&lt;&amp;String>, first_line: bool) -> u32 {
        let mut sum = 0;
        let mut char_vec: Vec> = Vec::new();
        for line in &amp;lines {
            char_vec.push(line.chars().collect());
        }
        let chars = match first_line {
            true => &amp;char_vec[0],
            false => &amp;char_vec[1],
        };
        for i in 0..chars.len() {
            if chars[i] == '*' {
                let surrounding_nums = get_surrounding_numbers(&amp;lines, i);
                let product = match surrounding_nums.len() {
                    0 | 1 => 0,
                    _ => surrounding_nums.iter().product(),
                };
                sum += product;
            }
        }
        sum
    }
    
    fn get_surrounding_numbers(lines: &amp;Vec&lt;&amp;String>, gear_pos: usize) -> Vec {
        let mut nums: Vec = Vec::new();
        let mut num: u32 = 0;
        let mut valid = false;
        for line in lines {
            for (i, char) in line.chars().enumerate() {
                if char.is_digit(10) {
                    num = num * 10 + char.to_digit(10).expect("is digit");
                    if [gear_pos - 1, gear_pos, gear_pos + 1].contains(&amp;i) {
                        valid = true;
                    }
                } else if num > 0 &amp;&amp; valid {
                    nums.push(num);
                    num = 0;
                    valid = false;
                } else {
                    num = 0;
                    valid = false;
                }
            }
            if num > 0 &amp;&amp; valid {
                nums.push(num);
            }
            num = 0;
            valid = false;
        }
        nums
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        const INPUT: &amp;str = "467..114..
    ...*......
    ..35..633.
    ......#...
    617*......
    .....+.58.
    ..592.....
    ......755.
    ...$.*....
    .664.598..";
    
        #[test]
        fn test_process_part_one() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(4361, process_part_one(BufReader::new(input_bytes)));
        }
    
        #[test]
        fn test_process_part_two() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(467835, process_part_two(BufReader::new(input_bytes)));
        }
    }
    

  • Late as always, as I'm on UK time and can't work on these until late evening.

    Part 01 and Part 02 in Rust 🦀 :

    use std::{
        env, fs,
        io::{self, BufRead, BufReader},
    };
    
    #[derive(Debug)]
    struct Sample {
        r: u32,
        g: u32,
        b: u32,
    }
    
    fn split_cube_set(set: &amp;[&amp;str], colour: &amp;str) -> Option {
        match set.iter().find(|x| x.ends_with(colour)) {
            Some(item) => item
                .trim()
                .split(' ')
                .next()
                .expect("Found item is present")
                .parse::()
                .ok(),
            None => None,
        }
    }
    
    fn main() -> io::Result&lt;()> {
        let args: Vec = env::args().collect();
        let filename = &amp;args[1];
        let file = fs::File::open(filename)?;
        let reader = BufReader::new(file);
        let mut valid_game_ids_sum = 0;
        let mut game_power_sum = 0;
        let max_r = 12;
        let max_g = 13;
        let max_b = 14;
        for line_result in reader.lines() {
            let mut valid_game = true;
            let line = line_result.unwrap();
            let line_split: Vec&lt;_> = line.split(':').collect();
            let game_id = line_split[0]
                .split(' ')
                .collect::>()
                .last()
                .expect("item exists")
                .parse::()
                .expect("is a number");
            let rest = line_split[1];
            let cube_sets = rest.split(';');
            let samples: Vec = cube_sets
                .map(|set| {
                    let set_split: Vec&lt;_> = set.split(',').collect();
                    let r = split_cube_set(&amp;set_split, "red").unwrap_or(0);
                    let g = split_cube_set(&amp;set_split, "green").unwrap_or(0);
                    let b = split_cube_set(&amp;set_split, "blue").unwrap_or(0);
                    Sample { r, g, b }
                })
                .collect();
            let mut highest_r = 0;
            let mut highest_g = 0;
            let mut highest_b = 0;
            for sample in &amp;samples {
                if !(sample.r &lt;= max_r &amp;&amp; sample.g &lt;= max_g &amp;&amp; sample.b &lt;= max_b) {
                    valid_game = false;
                }
                highest_r = u32::max(highest_r, sample.r);
                highest_g = u32::max(highest_g, sample.g);
                highest_b = u32::max(highest_b, sample.b);
            }
            if valid_game {
                valid_game_ids_sum += game_id;
            }
            game_power_sum += highest_r * highest_g * highest_b;
        }
        println!("Sum of game ids: {valid_game_ids_sum}");
        println!("Sum of game powers: {game_power_sum}");
        Ok(())
    }
    

  • Part 02 in Rust 🦀 :

    use std::{
        collections::HashMap,
        env, fs,
        io::{self, BufRead, BufReader},
    };
    
    fn main() -> io::Result&lt;()> {
        let args: Vec = env::args().collect();
        let filename = &amp;args[1];
        let file = fs::File::open(filename)?;
        let reader = BufReader::new(file);
    
        let number_map = HashMap::from([
            ("one", "1"),
            ("two", "2"),
            ("three", "3"),
            ("four", "4"),
            ("five", "5"),
            ("six", "6"),
            ("seven", "7"),
            ("eight", "8"),
            ("nine", "9"),
        ]);
    
        let mut total = 0;
        for _line in reader.lines() {
            let digits = get_text_numbers(_line.unwrap(), &amp;number_map);
            if !digits.is_empty() {
                let digit_first = digits.first().unwrap();
                let digit_last = digits.last().unwrap();
                let mut cat = String::new();
                cat.push(*digit_first);
                cat.push(*digit_last);
                let cat: i32 = cat.parse().unwrap();
                total += cat;
            }
        }
        println!("{total}");
        Ok(())
    }
    
    fn get_text_numbers(text: String, number_map: &amp;HashMap&lt;&amp;str, &amp;str>) -> Vec {
        let mut digits: Vec = Vec::new();
        if text.is_empty() {
            return digits;
        }
        let mut sample = String::new();
        let chars: Vec = text.chars().collect();
        let mut ptr1: usize = 0;
        let mut ptr2: usize;
        while ptr1 &lt; chars.len() {
            sample.clear();
            ptr2 = ptr1 + 1;
            if chars[ptr1].is_digit(10) {
                digits.push(chars[ptr1]);
                sample.clear();
                ptr1 += 1;
                continue;
            }
            sample.push(chars[ptr1]);
            while ptr2 &lt; chars.len() {
                if chars[ptr2].is_digit(10) {
                    sample.clear();
                    break;
                }
                sample.push(chars[ptr2]);
                if number_map.contains_key(&amp;sample.as_str()) {
                    let str_digit: char = number_map.get(&amp;sample.as_str()).unwrap().parse().unwrap();
                    digits.push(str_digit);
                    sample.clear();
                    break;
                }
                ptr2 += 1;
            }
            ptr1 += 1;
        }
    
        digits
    }
    

















  • Absolutely loved this. Never heard of the artist before this (though clearly she is very popular!). She seemed to have a lot of fun making the video.

    The only thing that disappointed me was learning that a bunch of people had to volunteer their time to make this. Surely this made lot of money for the artist and video producers, could it really be that the margins were too thin to compensate all the people working on this?






  • Like many others here, at the company I work for you get nothing.

    I do one on-call shift as primary per week and one as secondary. I then also cover a week every six weeks or so.

    If shit really hits the fan, them work is pretty cool about taking some time back, but we're far from micromanaged as it is, so we can just kind of make it work.

    I'd say an incidency probably occurs on around half of my primary shifts (and I've yet to ever do anything as secondary), and nearly always it was something I could resolve within one hour.

    Every dev at the company is on the rota once they've got a few month's experience.

    Based in the UK.