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
overssh
, 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);
}
}