• 2 Posts
  • 87 Comments
Joined 2 years ago
cake
Cake day: July 5th, 2023

help-circle






  • They’re always so confidently incorrect. People seem to want everything that happens to fit in a box to make it comprehensible. Change is scary. The idea that everything is just chaos and the miracle of our existence is just random, and that there is no “they” pulling the strings is scary. Better to believe that there is some kind of plan or shadowy people behind the scenes to give everything a sense of order than to come to grips with facts. Believing you are smarter than all the sheeple and have it all figured out is a nice warm blanket.

    We may be all alone in the universe. If we are, then all we’ve got is each other, and the rock in space we happen to live on. I wish the conspiracy theorists and the selfish, greedy folks with all the resources who are plunging us toward a bleak future could have a little perspective and empathy. This life is a shared experience and always has the same ending, so why not try to learn about each other and make someone else’s existence a little more pleasant while we’re here, rather than cave to fear and embracing ignorance?





  • I did run your code as-is in an ipython REPL to check. These were the results:

    REPL session
    # With unmodified `main` function & `import string` not shown
    In [4]: with open("inputs/day13.txt", "r") as f:
       ...:     input_data = f.read().strip().replace(',', '').split('\n\n')
       ...:
    
    In [5]: part_one, part_two = main(input_data)
    
    In [6]: part_one
    Out[6]: 39748
    
    In [7]: part_two
    Out[7]: 74926346266863
    
    # Then I modified the function to check if x is fractional
    In [8]: def main(input_data):
       ...:     part1_total_cost = 0
       ...:     part2_total_cost = 0
       ...:     for machine in input_data:
       ...:         Ax,Ay,Bx,By,Px,Py = [ int(l[2:]) for l in machine.split() if l[-1] in string.digits ]
       ...:         y,r = divmod((Ay * Px - Ax * Py), (Ay * Bx - Ax * By))
       ...:         if r == 0:
       ...:             x = (Px - Bx * y) / Ax
       ...:             if x % 1 == 0:
       ...:                 part1_total_cost += 3*x + y
       ...:         y,r = divmod((Ay * (Px+10000000000000) - Ax * (Py+10000000000000)), (Ay * Bx - Ax * By))
       ...:         if r == 0:
       ...:             x = ((Px+10000000000000) - Bx * y) / Ax
       ...:             if x % 1 == 0:
       ...:                 part2_total_cost += 3*x + y
       ...:
       ...:     return part1_total_cost,part2_total_cost
       ...:
    
    In [9]: part_one, part_two = main(input_data)
    
    In [10]: part_one
    Out[10]: 39748.0
    
    In [11]: part_two
    Out[11]: 74478585072604.0  # Correct answer for pt 2 of my input
    

    If you’re curious to check against my puzzle input, it’s here

    Thank you again for the back & forth, and for sharing your solution!





  • Rust

    Real thinker. Messed around with a couple solutions before this one. The gist is to take all the pairwise comparisons given and record them for easy access in a ranking matrix.

    For the sample input, this grid would look like this (I left out all the non-present integers, but it would be a 98 x 98 grid where all the empty spaces are filled with Ordering::Equal):

       13 29 47 53 61 75 97
    13  =  >  >  >  >  >  >
    29  <  =  >  >  >  >  >
    47  <  <  =  <  <  >  >
    53  <  <  >  =  >  >  >
    61  <  <  >  <  =  >  >
    75  <  <  <  <  <  =  >
    97  <  <  <  <  <  <  =
    

    I discovered this can’t be used for a total order on the actual puzzle input because there were cycles in the pairs given (see how rust changed sort implementations as of 1.81). I used usize for convenience (I did it with u8 for all the pair values originally, but kept having to cast over and over as usize). Didn’t notice a performance difference, but I’m sure uses a bit more memory.

    Also I Liked the simple_grid crate a little better than the grid one. Will have to refactor that out at some point.

    solution
    use std::{cmp::Ordering, fs::read_to_string};
    
    use simple_grid::Grid;
    
    type Idx = (usize, usize);
    type Matrix = Grid<Ordering>;
    type Page = Vec<usize>;
    
    fn parse_input(input: &str) -> (Vec<Idx>, Vec<Page>) {
        let split: Vec<&str> = input.split("\n\n").collect();
        let (pair_str, page_str) = (split[0], split[1]);
        let pairs = parse_pairs(pair_str);
        let pages = parse_pages(page_str);
        (pairs, pages)
    }
    
    fn parse_pairs(input: &str) -> Vec<Idx> {
        input
            .lines()
            .map(|l| {
                let (a, b) = l.split_once('|').unwrap();
                (a.parse().unwrap(), b.parse().unwrap())
            })
            .collect()
    }
    
    fn parse_pages(input: &str) -> Vec<Page> {
        input
            .lines()
            .map(|l| -> Page {
                l.split(",")
                    .map(|d| d.parse::<usize>().expect("invalid digit"))
                    .collect()
            })
            .collect()
    }
    
    fn create_matrix(pairs: &[Idx]) -> Matrix {
        let max = *pairs
            .iter()
            .flat_map(|(a, b)| [a, b])
            .max()
            .expect("iterator is non-empty")
            + 1;
        let mut matrix = Grid::new(max, max, vec![Ordering::Equal; max * max]);
        for (a, b) in pairs {
            matrix.replace_cell((*a, *b), Ordering::Less);
            matrix.replace_cell((*b, *a), Ordering::Greater);
        }
        matrix
    }
    
    fn valid_pages(pages: &[Page], matrix: &Matrix) -> usize {
        pages
            .iter()
            .filter_map(|p| {
                if check_order(p, matrix) {
                    Some(p[p.len() / 2])
                } else {
                    None
                }
            })
            .sum()
    }
    
    fn fix_invalid_pages(pages: &mut [Page], matrix: &Matrix) -> usize {
        pages
            .iter_mut()
            .filter(|p| !check_order(p, matrix))
            .map(|v| {
                v.sort_by(|a, b| *matrix.get((*a, *b)).unwrap());
                v[v.len() / 2]
            })
            .sum()
    }
    
    fn check_order(page: &[usize], matrix: &Matrix) -> bool {
        page.is_sorted_by(|a, b| *matrix.get((*a, *b)).unwrap() == Ordering::Less)
    }
    
    pub fn solve() {
        let input = read_to_string("inputs/day05.txt").expect("read file");
        let (pairs, mut pages) = parse_input(&input);
        let matrix = create_matrix(&pairs);
        println!("Part 1: {}", valid_pages(&pages, &matrix));
        println!("Part 2: {}", fix_invalid_pages(&mut pages, &matrix));
    }
    

    On github

    *Edit: I did try switching to just using std::collections::HashMap, but it was 0.1 ms slower on average than using the simple_grid::GridVec[idx] access is faster maybe?


  • Rust

    Ugh. Spent way too long on today’s. Should have just used my own grid structure from last year. I will likely refactor to use that. Even though it’s likely a super slow implementation, the convenience of dealing with it is better than shoehorning in the grid::Grid<T> from that crate.

    solution (no supporting code)
    use grid::Grid;
    
    use crate::shared::{
        grid2d::{iter_diag_nesw, iter_diag_nwse, Point},
        util::read_lines,
    };
    
    fn parse_grid(input: &[String]) -> Grid<u8> {
        let cols = input.first().unwrap().len();
        Grid::from_vec(
            input
                .iter()
                .flat_map(|row| row.chars().map(|c| c as u8).collect::<Vec<u8>>())
                .collect(),
            cols,
        )
    }
    
    fn part1(grid: &Grid<u8>) -> usize {
        let mut xmas_count = 0;
        let rows = grid
            .iter_rows()
            .map(|d| String::from_utf8(d.copied().collect()).unwrap());
        let cols = grid
            .iter_cols()
            .map(|d| String::from_utf8(d.copied().collect()).unwrap());
        for diag in iter_diag_nesw(grid)
            .chain(iter_diag_nwse(grid))
            .filter_map(|d| {
                if d.len() >= 4 {
                    Some(String::from_utf8(d.clone()).unwrap())
                } else {
                    None
                }
            })
            .chain(rows)
            .chain(cols)
        {
            xmas_count += diag.matches("XMAS").count() + diag.matches("SAMX").count()
        }
        xmas_count
    }
    
    fn part2(grid: &Grid<u8>) -> usize {
        let mut xmas_count = 0;
        let valid = [
            [b'M', b'M', b'S', b'S'],
            [b'M', b'S', b'S', b'M'],
            [b'S', b'M', b'M', b'S'],
            [b'S', b'S', b'M', b'M'],
        ];
        for x in 1..grid.cols() - 1 {
            for y in 1..grid.rows() - 1 {
                if grid.get(y, x) == Some(&b'A')
                    && valid.contains(
                        &(Point::new(x as isize, y as isize)
                            .diagonal_neighbors(grid)
                            .map(|i| i.unwrap_or(0))),
                    )
                {
                    xmas_count += 1;
                }
            }
        }
        xmas_count
    }
    
    pub fn solve() {
        let input = read_lines("inputs/day04.txt");
        let grid = parse_grid(&input);
        println!("Part 1: {}", part1(&grid));
        println!("Part 2: {}", part2(&grid));
    }
    

    And here’s a link to the Github if you care to see the gross supporting code :D


  • Rust

    Didn’t do anything crazy here – ended up using regex like a bunch of other folks.

    solution
    use regex::Regex;
    
    use crate::shared::util::read_lines;
    
    fn parse_mul(input: &[String]) -> (u32, u32) {
        // Lazy, but rejoin after having removed `\n`ewlines.
        let joined = input.concat();
        let re = Regex::new(r"mul\((\d+,\d+)\)|(do\(\))|(don't\(\))").expect("invalid regex");
    
        // part1
        let mut total1 = 0u32;
        // part2 -- adds `do()`s and `don't()`s
        let mut total2 = 0u32;
        let mut enabled = 1u32;
    
        re.captures_iter(&joined).for_each(|c| {
            let (_, [m]) = c.extract();
            match m {
                "do()" => enabled = 1,
                "don't()" => enabled = 0,
                _ => {
                    let product: u32 = m.split(",").map(|s| s.parse::<u32>().unwrap()).product();
                    total1 += product;
                    total2 += product * enabled;
                }
            }
        });
        (total1, total2)
    }
    
    pub fn solve() {
        let input = read_lines("inputs/day03.txt");
        let (part1_res, part2_res) = parse_mul(&input);
        println!("Part 1: {}", part1_res);
        println!("Part 2: {}", part2_res);
    }
    
    #[cfg(test)]
    mod test {
        use super::*;
    
        #[test]
        fn test_solution() {
            let test_input = vec![
                "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))".to_string(),
            ];
            let (p1, p2) = parse_mul(&test_input);
            eprintln!("P1: {p1}, P2: {p2}");
            assert_eq!(161, p1);
            assert_eq!(48, p2);
        }
    }
    
    

    Solution on my github (Made it public now)