• 27 Posts
  • 103 Comments
Joined 2 years ago
cake
Cake day: July 3rd, 2023

help-circle
  • I believe learning languages is generally a net good. But to answer your question, it would help to know: why do you want to learn Russian?

    If you just find the idea of the language interesting, then yes! Start leaning it. If you have motivation, that will help.

    Is there specific media you’re looking to consume in its original language, Russian? Then yes, absolutely :).

    Are you just trying to learn “any Slavic language”, to extend the language families you have knowledge of? You already have some Polish, so what is it about Russian that attracts you? Is there another language that might have more resonance or utility for you?

    As far as I am aware, mostly sue to Soviet influence, Russian is probably the most-widely-understood Slavic language, so this does offer some advantages. I have spoken with Ukranians and Georgians who now don’t like speaking Russian, for obvious reasons, though I don’t know how widespread this feeling really is. And at least here in Germany, I feel like Croatian, Czech, or Slovakian would be a more useful day-to-day or holiday language, but itball depends on your goals.

    And, as a dentist once told me in regards to dental floss, but it applies here too: The best language to learn is the one that you will actually learn. If there’s a language you’ll actually stick with, that’s good.




  • Rust

    Part 1 was very straight forward. I overcomplicated it a bit by using an actual graph library, but I’m used to using it.

    I originally tried to brute force Part 2, which of course failed. I then reverted to dynamic programming, which worked well.

    use std::collections::{HashMap, VecDeque};
    
    use graphrs::{Graph, GraphSpecs};
    
    use crate::solver::DaySolver;
    
    pub struct Day11Solver;
    
    impl DaySolver for Day11Solver {
        fn part1(&self, input: String) -> String {
            let mut graph: Graph<String, ()> = Graph::new(GraphSpecs::directed_create_missing());
            let edges = input.lines()
                .flat_map(|line| {
                    let (start, end_str) = line.split_once(": ").unwrap();
                    end_str.split_whitespace()
                        .map(|end| (start.to_string(), end.to_string()))
                })
                .collect();
            graph.add_edge_tuples(edges).unwrap();
    
            let mut frontier = VecDeque::from([vec!["you".to_string()]]);
    
            let mut path_count = 0;
    
            while let Some(path) = frontier.pop_front() {
                let last = path.last().unwrap();
    
                if last == "out" {
                    path_count += 1;
                } else {
                    graph
                        .get_successor_node_names(last.to_string())
                        .unwrap()
                        .into_iter()
                        .filter(|next| !path.contains(next))
                        .map(|next| {
                            let mut new_path = path.clone();
                            new_path.push(next.clone());
                            new_path
                        })
                        .for_each(|new_path| frontier.push_back(new_path));
                }
            }
    
            path_count.to_string()
        }
    
        fn part2(&self, input: String) -> String {
            let mut graph: Graph<String, ()> = Graph::new(GraphSpecs::directed_create_missing());
            let edges = input.lines()
                .flat_map(|line| {
                    let (start, end_str) = line.split_once(": ").unwrap();
                    end_str.split_whitespace()
                        .map(|end| (start.to_string(), end.to_string()))
                })
                .collect();
            graph.add_edge_tuples(edges).unwrap();
    
            how_many_paths(
                &mut HashMap::new(),
                &graph,
                ("svr".to_string(), false, false),
            )
                .to_string()
    
        }
    }
    
    fn how_many_paths(
        cache: &mut HashMap<(String, bool, bool), usize>,
        graph: &Graph<String, ()>,
        current: (String, bool, bool),
    ) -> usize {
        if let Some(&c) = cache.get(&current) {
            c
        } else {
            let (node, has_dac, has_fft) = &current;
            if node == "out" {
                let count = if *has_dac && *has_fft { 1 } else { 0 };
                cache.insert(current, count);
                count
            } else {
                let count = graph
                    .get_successor_node_names(node.clone())
                    .unwrap()
                    .into_iter()
                    .map(|next| {
                        let next_state = (next.to_string(), *has_dac || next == "dac", *has_fft || next == "fft");
                        how_many_paths(cache, graph, next_state)
                    })
                    .sum();
                cache.insert(current, count);
                count
            }
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn part1() {
            let input = include_str!("../../inputs/test/11");
    
            let solver = Day11Solver {};
            assert_eq!("5", solver.part1(input.to_string()));
        }
    
        #[test]
        fn part2() {
            let input = include_str!("../../inputs/test/11-2");
    
            let solver = Day11Solver {};
            assert_eq!("2", solver.part2(input.to_string()));
        }
    }
    




  • Rust

    Part 1 was quite straightforward: just keep track of a frontier of every beam, and keep following it, adding splitter locations to a set. Easy.

    I had to give some thought to Part 2 and ended up doing dynamic programming: for every position, keep track of how many timelines go through it, solving recursively where necesary.

    use std::collections::{HashMap, HashSet, VecDeque};
    
    use crate::{grid::{Coordinate, Direction, Grid}, solver::DaySolver};
    
    pub struct Day07Solver;
    
    impl DaySolver for Day07Solver {
        fn part1(&self, input: String) -> String {
            let grid = Grid::from_grid_string(
                &input,
                |c| match c {
                    'S' => Location::Start,
                    '^' => Location::Splitter,
                    '.' => Location::Empty,
                    _ => panic!("Invalid location type"),
                }
            );
    
            let mut reached_splitters: HashSet<Coordinate> = HashSet::new();
    
            let mut beam_coordinates: VecDeque<Coordinate> = grid.iter()
                .filter_map(|(c, l)| match l {
                    Location::Start => Some(c),
                    _ => None,
                })
                .collect();
    
            while let Some(next) = beam_coordinates.pop_front() {
                if let Some(next_coord) = grid.neighbor_in_direction(next, Direction::Down) {
                    match grid[next_coord] {
                        Location::Start => { panic!("Encountered a second start!"); },
                        Location::Splitter => {
                            if !reached_splitters.contains(&next_coord) {
                                reached_splitters.insert(next_coord);
                                [
                                    grid.neighbor_in_direction(next_coord, Direction::Left),
                                    grid.neighbor_in_direction(next_coord, Direction::Right),
                                ]
                                    .into_iter()
                                    .flatten()
                                    .for_each(|c| beam_coordinates.push_back(c));
                            }
                        },
                        Location::Empty => { beam_coordinates.push_back(next_coord); }
                    }
                }
            }
    
            reached_splitters
                .len()
                .to_string()
        }
    
        fn part2(&self, input: String) -> String {
            let grid = Grid::from_grid_string(
                &input,
                |c| match c {
                    'S' => Location::Start,
                    '^' => Location::Splitter,
                    '.' => Location::Empty,
                    _ => panic!("Invalid location type"),
                }
            );
    
            let start_coord = grid.iter().find(|(_, l)| **l == Location::Start).unwrap().0;
    
            let mut num_timelines: HashMap<Coordinate, usize> = HashMap::new();
            count_timelines(&mut num_timelines, &grid, 1, start_coord);
    
            num_timelines[&start_coord].to_string()
        }
    }
    
    fn count_timelines(
        num_timelines: &mut HashMap<Coordinate, usize>,
        grid: &Grid<Location>,
        timelines_so_far: usize,
        coordinate: Coordinate,
    ) {
        if num_timelines.contains_key(&coordinate) {
            return
        }
    
        match grid[coordinate] {
            Location::Splitter => {
                let neighbors: Vec<Coordinate> = [
                    grid.neighbor_in_direction(coordinate, Direction::Left),
                    grid.neighbor_in_direction(coordinate, Direction::Right),
                ]
                    .into_iter()
                    .flatten()
                    .collect();
    
                neighbors.iter()
                    .for_each(|next| {
                        count_timelines(num_timelines, grid, timelines_so_far, *next);
                    });
    
                let timelines_from_here = neighbors.iter().map(|c| num_timelines[c]).sum();
                num_timelines.insert(coordinate, timelines_from_here);
            },
            _ => {
                let timelines_from_here = if let Some(next) = grid.neighbor_in_direction(coordinate, Direction::Down) {
                    count_timelines(num_timelines, grid, timelines_so_far, next);
                    num_timelines[&next]
                } else {
                    timelines_so_far
                };
    
                num_timelines.insert(coordinate, timelines_from_here);
            },
        };
    }
    
    #[derive(PartialEq, Eq, Copy, Clone)]
    enum Location {
        Start,
        Splitter,
        Empty,
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn part1() {
            let input = include_str!("../../inputs/test/07");
    
            let solver = Day07Solver {};
            assert_eq!("21", solver.part1(input.to_string()));
        }
    
    
        #[test]
        fn part2() {
            let input = include_str!("../../inputs/test/07");
    
            let solver = Day07Solver {};
            assert_eq!("40", solver.part2(input.to_string()));
        }
    }
    




  • Cool that you’re trying something new out! I also often find prefix arguments a bit unintuitive in my flow.

    First of all, you’re right that you could do (eat nil 4) to simulate the prefix argument. I do this in a number of my helpers.

    For my advice on your code, I think it would be easier to understand if you used a let* block to assign meaningful variable names. I’m writing this from my phone, so it’s hard to give a detailed example, but:

    (let* ((eat-buffers (match-buffers "\*eat\*"))
           (last-buffer (last (sort eat-buffers))))
     ;; Body
    )
    

    And so on. This would let you reuse some values, and would also reduce the amount of nesting in your code.

    Edit: Missed a few parentheses, fixing syntax highlighting