Skip Navigation

Posts
14
Comments
261
Joined
2 yr. ago

  • I'm not sure that's really what's happening. The plan is specifically for when Russia collapses. I fear that this might make the job of any more democratic successor to Putin much harder, because the worst way to start a new government is by having a large part of your country taken away.

  • Young people leaving the countryside is definitely a problem for rural areas in general, but a "wilting" wine industry is not what personally worries me, instead I would really like to see the culture of alcoholism get reduced.

  • It's not that I'm expecting Chinese to be commutative, but the original image makes it look as such, with the upper and lower triangles of the matrix having the same symbols. In your 2D example this would be like having ab on the top right as well (I would give an example of the characters, but I cannot write Chinese).

  • Because the matrix in the original post is symmetric.

  • Uhh, why is it not symmetric?

  • That's a lot of money. At this point they should have bought something like this, would actually help in an apocalypse:

  • Generell: wenn der Absender unbekannt ist und sich nicht als zumindest halbwegs respektables Unternehmen identifizieren lässt, besser überhaupt keine Links öffnen sondern direkt als Spam markieren und löschen.

  • In my opinion yes, Debian is the best choice for server machines, especially on the homelab scale.

  • Holy shit, stadiums are expensive.

  • Well I'm glad they're rejoining the programme, because Brexit not only affected British students, but all EU students. The Erasmus partnerships between British and other European universities were suspended, which makes it much harder for EU students to study in the UK.

  • How does this even work? I get the redirection part, but how is the command executed in a detached state?

  • I do run vim on the GPU, through a GPU-rendered terminal emulator like kitty. And yes, it scrolls noticeably smoother than on other terminals.

  • Rust

    Only took four days, but I made it, and without any maths libraries. I tried lots of unsuccessful searches (including A*) before realizing part 2 is a linear equation system. Then I tried to find all solutions by just going row by row, guessing all but one unknown variable and finding the smallest solution, but this was still way too slow due to the high amount of variables guessed.

    By looking around on Wikipedia I found that the problem could be simplified by turning the matrix into Smith Normal Form or Hermitian Normal Form, but couldn't find any Rust library implementing these. Their algorithms looked just a bit too complicated to implement myself. Maybe I would have used Python, because sagemath has everything, but the problem of ultimately finding the smallest integer solution still remained, and I already had the search code in Rust without simplifying the matrix.

    So put the matrix into echelon form by implementing Gaussian elimination, which wasn't too bad, and it significantly reduced the number of variables to guess. Now part 2 runs in 70ms.

    View code on github

  • I think this is a very useful license to require in new software procurements commissioned by public entities in the EU. The FSFE calls this "public money – public code" (publiccode.eu). Having a specific EUPL over just the GPL might make this look more proper, even if there aren't many technical differences. However for personal use I would prefer using good old GPLv3.

  • Rust

    This one broke me, I couldn't do it without looking at some of the solutions in this thread. In the end, basically all that was necessary for part 2 is to check for every candidate rectangle if:

    1. No corners (red tiles) are inside the rectangle, and
    2. No lines intersect the rectangle.

    Plus a bunch shifting numbers around by 1. The code is not pretty at all, but at least it turned very efficient, solving part 2 in just 4.3ms. I have no idea how generalized the solution is. It definitely assumes that any larger rectangle is spanned inside the area and that the path doesn't cross over itself. Also of course that there are no two corners next to each other forming a 180 degree turn.

    View on github

     rust
        
    use std::ops::{Range, RangeInclusive};
    
    fn parse_input(input: &str) -> Vec<(u32, u32)> {
        input
            .lines()
            .map(|l| {
                let (a, b) = l.split_once(',').unwrap();
                (a.parse::<u32>().unwrap(), b.parse::<u32>().unwrap())
            })
            .collect()
    }
    
    #[inline]
    fn area(a: (u32, u32), b: (u32, u32)) -> u64 {
        (a.0.abs_diff(b.0) as u64 + 1) * (a.1.abs_diff(b.1) as u64 + 1)
    }
    
    fn part1(input: String) {
        let tiles = parse_input(&input);
        let mut largest = 0;
        for t1 in &tiles {
            for t2 in &tiles {
                let a = area(*t1, *t2);
                if a > largest {
                    largest = a;
                }
            }
        }
        println!("{largest}");
    }
    
    // Returns true only if t is not inside of the rectangle
    #[inline]
    fn red_allowed(t: (u32, u32), x_range: Range<u32>, y_range: Range<u32>) -> bool {
        !(t.0 > x_range.start && t.0 + 1 < x_range.end && t.1 > y_range.start && t.1 + 1 < y_range.end)
    }
    
    fn is_contained(
        a: (u32, u32),
        b: (u32, u32),
        tiles_x: &[(u32, u32)],
        tiles_y: &[(u32, u32)],
        vert_lines: &[(u32, RangeInclusive<u32>)],
        hori_lines: &[(u32, RangeInclusive<u32>)],
    ) -> bool {
        let x_range = a.0.min(b.0)..(a.0.max(b.0) + 1);
        let y_range = a.1.min(b.1)..(a.1.max(b.1) + 1);
    
        // Check that no corners (red tiles) are inside the rectangle
        let corners_ok = if x_range.end - x_range.start <= y_range.end - y_range.start {
            // Use tiles_x
            let start = match tiles_x.binary_search(&(x_range.start, 0)) {
                Ok(e) => e,
                Err(e) => e,
            };
            tiles_x
                .iter()
                .skip(start)
                .take_while(|t| t.0 < x_range.end)
                .filter(|&&t| t != a && t != b)
                .all(|t| red_allowed(*t, x_range.clone(), y_range.clone()))
        } else {
            // Use tiles_y
            let start = match tiles_y.binary_search_by_key(&(y_range.start, 0), |(x, y)| (*y, *x)) {
                Ok(e) => e,
                Err(e) => e,
            };
            tiles_y
                .iter()
                .skip(start)
                .take_while(|t| t.1 < y_range.end)
                .filter(|&&t| t != a && t != b)
                .all(|t| red_allowed(*t, x_range.clone(), y_range.clone()))
        };
        if !corners_ok {
            return false;
        }
    
        // Check that no line intersects the inside of the rectangle
        let start = match vert_lines.binary_search_by_key(&x_range.start, |(x, _)| *x) {
            Ok(e) => e,
            Err(e) => e,
        };
        for (x, line) in vert_lines
            .iter()
            .skip(start)
            .take_while(|(x, _)| *x < x_range.end)
        {
            if x_range.start < *x
                && x_range.end > *x + 1
                && (y_range.start + 1).max(*line.start()) < (y_range.end - 1).min(line.end() + 1)
            {
                return false;
            }
        }
        let start = match hori_lines.binary_search_by_key(&y_range.start, |(y, _)| *y) {
            Ok(e) => e,
            Err(e) => e,
        };
        for (y, line) in hori_lines
            .iter()
            .skip(start)
            .take_while(|(y, _)| *y < y_range.end)
        {
            if y_range.start < *y
                && y_range.end > *y + 1
                && (x_range.start + 1).max(*line.start()) < (x_range.end - 1).min(line.end() + 1)
            {
                return false;
            }
        }
        true
    }
    
    fn part2(input: String) {
        let tiles = parse_input(&input);
    
        let mut vert_lines = Vec::new();
        let mut hori_lines = Vec::new();
        let mut prev = *tiles.last().unwrap();
        for &t in &tiles {
            if t.0 == prev.0 {
                vert_lines.push((t.0, t.1.min(prev.1)..=t.1.max(prev.1)));
            } else {
                debug_assert_eq!(t.1, prev.1);
                hori_lines.push((t.1, t.0.min(prev.0)..=t.0.max(prev.0)));
            }
            prev = t;
        }
        vert_lines.sort_by_key(|(x, _)| *x);
        hori_lines.sort_by_key(|(y, _)| *y);
    
        let mut tiles_x = tiles.clone();
        tiles_x.sort();
        let mut tiles_y = tiles.clone();
        tiles_y.sort_by_key(|(x, y)| (*y, *x));
        let mut largest = 0;
        for (idx, t1) in tiles.iter().enumerate() {
            for t2 in tiles.iter().take(idx) {
                let a = area(*t1, *t2);
                if a > largest && is_contained(*t1, *t2, &tiles_x, &tiles_y, &vert_lines, &hori_lines) {
                    largest = a;
                }
            }
        }
        println!("{largest}");
    }
    
    util::aoc_main!();
    
      
  • Rust

    It's getting spicier, luckily part 2 wasn't really much additional complexity this time. There exists a pretty fancy union-find data structure which would have made representing the subcircuits much faster, but I went with a lazy approach.

    View on github

     rust
        
    use euclid::default::Point3D;
    use euclid::point3;
    
    fn parse_input(input: &str) -> Vec<Point3D<i64>> {
        input
            .lines()
            .map(|l| {
                let mut parts = l.split(',').map(|p| p.parse::<i64>().unwrap());
                let (x, y, z) = (
                    parts.next().unwrap(),
                    parts.next().unwrap(),
                    parts.next().unwrap(),
                );
                point3(x, y, z)
            })
            .collect()
    }
    
    // Distances between all points. Reflexive and symmetric pairs are skipped,
    // so the Vec's have increasing size, starting at 0.
    fn dists(points: &[Point3D<i64>]) -> Vec<Vec<i64>> {
        points
            .iter()
            .enumerate()
            .map(|(idx, &p1)| {
                points
                    .iter()
                    .take(idx)
                    .map(|&p2| (p2 - p1).square_length())
                    .collect::<Vec<i64>>()
            })
            .collect()
    }
    
    fn sorted_distances(dists: &[Vec<i64>]) -> Vec<(usize, usize, i64)> {
        let mut sorted: Vec<(usize, usize, i64)> = dists
            .iter()
            .enumerate()
            .flat_map(|(i, row)| row.iter().enumerate().map(move |(j, d)| (i, j, *d)))
            .collect();
        sorted.sort_by_key(|(_, _, d)| *d);
        sorted
    }
    
    fn part1(input: String) {
        let points = parse_input(&input);
        let d = dists(&points);
        let sorted = sorted_distances(&d);
    
        let mut circuits: Vec<u32> = (0..points.len() as u32).collect();
        for (i, j, _) in sorted.into_iter().take(1000) {
            let new_circuit = circuits[i];
            let old_circuit = circuits[j];
            if new_circuit != old_circuit {
                for c in circuits.iter_mut() {
                    if *c == old_circuit {
                        *c = new_circuit;
                    }
                }
            }
        }
        let mut sizes: Vec<u32> = vec![0; points.len()];
        for c in circuits {
            sizes[c as usize] += 1
        }
        sizes.sort_unstable();
        let result = sizes.iter().rev().take(3).product::<u32>();
        println!("{result}");
    }
    
    fn part2(input: String) {
        let points = parse_input(&input);
        let d = dists(&points);
        let sorted = sorted_distances(&d);
    
        let mut circuits: Vec<u32> = (0..points.len() as u32).collect();
        for (i, j, _) in sorted.into_iter() {
            let new_circuit = circuits[i];
            let old_circuit = circuits[j];
            if new_circuit != old_circuit {
                let mut all_connected = true;
                for c in circuits.iter_mut() {
                    if *c == old_circuit {
                        *c = new_circuit;
                    }
                    if *c != new_circuit {
                        all_connected = false;
                    }
                }
                if all_connected {
                    let result = points[i].x * points[j].x;
                    println!("{result}");
                    return;
                }
            }
        }
    }
    
    util::aoc_main!();
    
      
  • The gaps on the bottom and the top serve the important purpose of ventilation. It's a really effective design allowing vertical airflow. So yes, I do prefer air gaps over stinky boxes, and I have personally never seen a creep sticking their head under the gap.

  • Rust

    Dynamic programming? Nah, just keep track of the number of overlapping beams and part 2 becomes no different to part 1.

    View on github

     rust
        
    use std::collections::VecDeque;
    
    fn parse_input(input: &str) -> (Vec<Vec<bool>>, (usize, usize)) {
        let splits = input
            .lines()
            .map(|l| l.chars().map(|c| c == '^').collect())
            .collect();
        // Assume start is on first row
        let start = (input.chars().position(|c| c == 'S').unwrap(), 0);
        (splits, start)
    }
    
    fn solve(input: String) {
        let (splits, start) = parse_input(&input);
        let mut nsplits = 0u32;
        let mut timelines = 1u64;
        let mut frontier = VecDeque::from([(start, 1)]);
        while let Some((pos, multiplicity)) = frontier.pop_front() {
            let (x, y) = (pos.0, pos.1 + 1);
            if y == splits.len() {
                // Falls out of bottom
                continue;
            }
            if splits[y][x] {
                nsplits += 1;
                timelines += multiplicity;
                if let Some((b, m2)) = frontier.back_mut()
                    && *b == (x - 1, y)
                {
                    *m2 += multiplicity;
                } else {
                    frontier.push_back(((x - 1, y), multiplicity));
                }
                frontier.push_back(((x + 1, y), multiplicity));
            } else if let Some((b, m2)) = frontier.back_mut()
                && *b == (x, y)
            {
                *m2 += multiplicity;
            } else {
                frontier.push_back(((x, y), multiplicity));
            }
        }
        println!("Part 1: {nsplits}");
        println!("Part 2: {timelines}");
    }
    
    fn main() -> std::io::Result<()> {
        let (input, _) = util::get_input("day7.txt")?;
        solve(input);
        Ok(())
    }
    
      
  • That's absolutely hilarious! Can somebody from the UK confirm if this is actually true?