Advent of Code 2022: Days 8 and 9

Published December 12, 2022 on Chandler Swift's Blog Source


Today I was out of the house most of the day. This was a good chance to check out TRAMP: emacs' Transparent Remote Access: Multiple Protocols, which satisfied my remote-editing needs pretty effectively.

For context, previously my remote editing generally consists of one of the following:

  • rsync down the project, edit the project, rsync up the project, though this doesn’t let me use the environment I have set up on my desktop (compilers, cached builds, configs, …)
  • A fresh git clone on the laptop, independent of the project files elsewhere, though this doesn’t give me any changes I haven’t committed and pushed
  • vim over ssh, though this isn’t terribly resilient to high-latency connections or packet loss, both of which are common in rural Minnesota, and isn’t perfect with things like clipboard integration
  • VSCode’s remote SSH extension, though this is a Microsoft proprietary extension and doesn’t work with Arch’s open source build of vscode

TRAMP does pretty well with all the concerns I’ve previously run into:

  • I can use the remote environment automatically with M-x shell
  • I’m editing my primary copy of the files, so I don’t have to worry about clobbering uncomitted work
  • Since the file is copied locally, it’s generally fairly tolerant of higher-latency connections; saving may just take a while
  • And, it’s open-source, and built into Emacs!

That’s not to say everything is perfect; I had some issues with interrupted sessions and autosaves when my VPN connection into my home network dropped, though I was able to figure all of these out eventually. (M-x recover-this-file goes a long way!)

Day 8

Part 1 source code
use std::fs;

fn is_visible(forest: &Vec<Vec<u8>>, row: usize, column: usize) -> bool {
    let height = forest[row][column];
    let mut hidden_from_north = false;
    for i in 0..row {
        if forest[i][column] >= height {
            hidden_from_north = true;
            break;
        }
    }
    let mut hidden_from_south = false;
    for i in row + 1..forest.len() {
        if forest[i][column] >= height {
            hidden_from_south = true;
            break;
        }
    }
    let mut hidden_from_west = false;
    for i in 0..column {
        if forest[row][i] >= height {
            hidden_from_west = true;
            break;
        }
    }
    let mut hidden_from_east = false;
    for i in column + 1..forest[0].len() {
        if forest[row][i] >= height {
            hidden_from_east = true;
            break;
        }
    }
    !hidden_from_north || !hidden_from_south || !hidden_from_west || !hidden_from_east
}

fn process(data: &str) -> u32 {
    let mut forest: Vec<Vec<u8>> = Vec::new();
    for line in data.split("\n") {
        let mut tree_row = Vec::new();
        for c in line.chars() {
            tree_row.push(c.to_digit(10).unwrap() as u8);
        }
        forest.push(tree_row);
    }
    let mut visible_count = 0;
    for row in 0..forest.len() {
        for column in 0..forest[0].len() {
            if is_visible(&forest, row, column) {
                visible_count += 1;
            }
        }
    }
    visible_count
}

fn main() {
    let data = fs::read_to_string("input.txt").unwrap();
    let data = data.trim();

    println!("{}", process(data));
}

#[cfg(test)]
mod test {
    use super::*;

    static DATA: &str = "30373
25512
65332
33549
35390";

    #[test]
    fn test() {
        assert!(process(DATA) == 21);
    }
}
Part 2 source code
use std::fs;

fn scenic_score(forest: &Vec<Vec<u8>>, row: usize, column: usize) -> u32 {
    let height = forest[row][column];
    let mut visible_to_north = 0;
    for i in 1..=row {
        visible_to_north += 1;
        if forest[row - i][column] >= height {
            break;
        }
    }
    let mut visible_to_south = 0;
    for i in row + 1..forest.len() {
        visible_to_south += 1;
        if forest[i][column] >= height {
            break;
        }
    }
    let mut visible_to_west = 0;
    for i in 1..=column {
        visible_to_west += 1;
        if forest[row][column - i] >= height {
            break;
        }
    }
    let mut visible_to_east = 0;
    for i in column + 1..forest[0].len() {
        visible_to_east += 1;
        if forest[row][i] >= height {
            break;
        }
    }
    visible_to_north * visible_to_south * visible_to_west * visible_to_east
}

fn process(data: &str) -> u32 {
    let mut forest: Vec<Vec<u8>> = Vec::new();
    for line in data.split("\n") {
        let mut tree_row = Vec::new();
        for c in line.chars() {
            tree_row.push(c.to_digit(10).unwrap() as u8);
        }
        forest.push(tree_row);
    }
    let mut max = 0;
    for row in 0..forest.len() {
        for column in 0..forest[0].len() {
            if scenic_score(&forest, row, column) > max {
                max = scenic_score(&forest, row, column);
            }
        }
    }
    max
}

fn main() {
    let data = fs::read_to_string("input.txt").unwrap();
    let data = data.trim();

    println!("{}", process(data));
}

#[cfg(test)]
mod test {
    use super::*;

    static DATA: &str = "30373
25512
65332
33549
35390";

    #[test]
    fn test() {
        assert!(process(DATA) == 8);
    }
}

Day 9

Source code
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fs;

fn do_move(direction: &str, positions: &mut Vec<(i32, i32)>) {
    match direction {
        "U" => positions[0].1 += 1,
        "D" => positions[0].1 -= 1,
        "L" => positions[0].0 -= 1,
        "R" => positions[0].0 += 1,
        _ => panic!("Illegal direction {}", direction),
    }

    for i in 1..positions.len() {
        if (positions[i].0 - positions[i - 1].0).abs() > 1
            || (positions[i].1 - positions[i - 1].1).abs() > 1
        {
            positions[i].0 += match positions[i].0.cmp(&positions[i - 1].0) {
                Ordering::Less => 1, // TODO: There _must_ be a better way to do this
                Ordering::Equal => 0,
                Ordering::Greater => -1,
            };
            positions[i].1 += match positions[i].1.cmp(&positions[i - 1].1) {
                Ordering::Less => 1,
                Ordering::Equal => 0,
                Ordering::Greater => -1,
            };
        }
    }
}

fn process(data: &str, knot_count: usize) -> usize {
    let mut positions = Vec::new();
    for _ in 0..knot_count {
        positions.push((0, 0));
    }
    let mut visited_positions = HashMap::new();
    for row in data.split("\n") {
        let command: Vec<&str> = row.split(" ").collect();
        let direction = command[0];
        for _ in 0..command[1].parse().unwrap() {
            do_move(direction, &mut positions);
            visited_positions.insert(positions[positions.len() - 1], true);
        }
    }
    visited_positions.len()
}

fn main() {
    let data = fs::read_to_string("input.txt").unwrap();
    let data = data.trim();
    println!("{}", process(data, 10)); // 2 for part 1
}

#[cfg(test)]
mod test {
    use super::*;

    const DATA: &str = "R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2";

    #[test]
    fn test_move() {
        let cases = vec![
            // positions, dir, new_positions
            (vec![(1, 0), (0, 0)], "R", vec![(2, 0), (1, 0)]),
            (vec![(1, -2), (1, -1)], "D", vec![(1, -3), (1, -2)]),
            (vec![(2, -2), (1, -3)], "R", vec![(3, -2), (2, -2)]),
        ];
        for (original_positions, dir, new_positions) in cases {
            let mut positions = original_positions.clone();
            do_move(dir, &mut positions);
            println!("{:?} == {:?}", positions, original_positions);
            assert!(positions == new_positions);
        }
    }

    #[test]
    fn test_full() {
        assert!(process(DATA, 2) == 13);
        assert!(process(DATA, 10) == 1);
    }

    #[test]
    fn test_fuller() {
        let data = "R 5
U 8
L 8
D 3
R 17
D 10
L 25
U 20";
        assert!(process(data, 10) == 36);
    }
}

Soundtrack

Christmas, by Mannheim Steamroller Messiah, by Polyphony, Britten Sinfonia, Stephen Layton Merry Christmas, by Mariah Carey


I don't have a formal commenting system set up. If you have questions or comments about anything I've written, send me an email and I'd be delighted to hear what you have to say!