whispersilk's recent activity

  1. Comment on Which web browser do you use? in ~tech

    whispersilk
    Link Parent
    Chrome doesn't let you assign keywords to bookmarks in the same way, but there is a workaround that might work for you, and that workaround is site search engines. Go to...

    Chrome doesn't let you assign keywords to bookmarks in the same way, but there is a workaround that might work for you, and that workaround is site search engines.

    Go to chrome://settings/searchEngines; scroll down to site search and hit "Add"; and then give the custom search engine the name of your bookmark, whatever keyword you want as its "shortcut", and your desired URL. While the hint text says "URL with %s in place of query", it doesn't actually force you to include %s anywhere, so you can give it your bookmark URL as is and everything will work as expected.

    5 votes
  2. Comment on Day 8: Treetop Tree House in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Like yesterday I didn't know how to avoid allocation on this one, but at least it's all up front today! Part 1 use...

    I'm using Rust this year, and trying to keep it std-only throughout the month. Like yesterday I didn't know how to avoid allocation on this one, but at least it's all up front today!

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
        let mut file = File::open("input_8.txt")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let map: Vec<Vec<char>> = contents.lines().map(|l| l.chars().collect()).collect();
        let visible_count = (0..map.len()).fold(0, |acc, h| {
            acc + (0..map[h].len()).filter(|w| visible(&map, h, *w)).count()
        });
        println!("{visible_count}");
        Ok(())
    }
    
    fn visible(map: &Vec<Vec<char>>, h: usize, w: usize) -> bool {
        let (height, tree) = (map.len(), map[h][w]);
        let from_left = map[h].iter().take(w).all(|c| c < &tree);
        let from_right = map[h].iter().skip(w + 1).all(|c| c < &tree);
        let from_top = (0..h).all(|h| map[h][w] < tree);
        let from_bottom = ((h + 1)..height).all(|h| map[h][w] < tree);
        from_left || from_right || from_top || from_bottom
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
        let mut file = File::open("input_8.txt")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let map: Vec<Vec<char>> = contents.lines().map(|l| l.chars().collect()).collect();
        let max_view_score = (0..map.len())
            .map(|h| (0..map[h].len()).map(|w| view_score(&map, h, w)).max())
            .max()
            .unwrap()
            .unwrap();
        println!("{max_view_score}");
        Ok(())
    }
    
    fn view_score(map: &Vec<Vec<char>>, h: usize, w: usize) -> usize {
        let (height, width, tree) = (map.len(), map[0].len(), map[h][w]);
        let lview = (0..w).rev().take_while(|w| map[h][*w] < tree).count();
        let rview = ((w + 1)..width).take_while(|w| map[h][*w] < tree).count();
        let tview = (0..h).rev().take_while(|h| map[*h][w] < tree).count();
        let bview = ((h + 1)..height).take_while(|h| map[*h][w] < tree).count();
        (lview + if lview == w { 0 } else { 1 })
            * (rview + if rview == width - 1 - w { 0 } else { 1 })
            * (tview + if tview == h { 0 } else { 1 })
            * (bview + if bview == height - 1 - h { 0 } else { 1 })
    }
    
    2 votes
  3. Comment on Day 7: No Space Left On Device in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. I've been doing a pretty good job of avoiding allocation so far, I think, but not today. Part 1 use...

    I'm using Rust this year, and trying to keep it std-only throughout the month. I've been doing a pretty good job of avoiding allocation so far, I think, but not today.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    use std::path::PathBuf;
    
    type Entry = (PathBuf, u64, Type);
    
    #[derive(PartialEq)]
    enum Type {
        Dir,
        File,
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
        let mut file = File::open("input_7.txt")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let mut entries: Vec<Entry> = vec![("/".into(), 0, Type::Dir)];
        let mut current_entry = 0;
        for line in contents.lines() {
            if line.starts_with("$ cd ..") {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.pop();
                current_entry = add_dir(&mut entries, new_path);
            } else if line.starts_with("$ cd ") {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.push(line.split(" ").nth(2).unwrap());
                current_entry = add_dir(&mut entries, new_path);
            } else if line.starts_with("$ ls") {
                // do nothing
            } else if line.starts_with("dir ") {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.push(line.split(" ").nth(1).unwrap());
                add_dir(&mut entries, new_path);
            } else {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.push(line.split(" ").nth(1).unwrap());
                let size = line.split(" ").next().unwrap().parse::<u64>().unwrap();
                add_file(&mut entries, new_path, size);
            }
        }
        let total_size = entries
            .iter()
            .filter(|e| e.1 <= 100_000 && e.2 == Type::Dir)
            .fold(0, |acc, e| acc + e.1);
        println!("{total_size}");
        Ok(())
    }
    
    fn add_dir(entries: &mut Vec<Entry>, path: PathBuf) -> usize {
        let exists = entries.iter().position(|e| e.0 == path);
        if exists.is_some() {
            exists.unwrap()
        } else {
            entries.push((path, 0, Type::Dir));
            entries.len() - 1
        }
    }
    
    fn add_file(entries: &mut Vec<Entry>, path: PathBuf, size: u64) -> usize {
        let exists = entries.iter().position(|e| e.0 == path);
        if exists.is_some() {
            exists.unwrap()
        } else {
            entries
                .iter_mut()
                .filter(|e| path.starts_with(&e.0))
                .for_each(|e| e.1 += size);
            entries.push((path, size, Type::File));
            entries.len() - 1
        }
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    use std::path::PathBuf;
    
    type Entry = (PathBuf, u64, Type);
    
    #[derive(PartialEq)]
    enum Type {
        Dir,
        File,
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
        let mut file = File::open("input_7.txt")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let mut entries: Vec<Entry> = vec![("/".into(), 0, Type::Dir)];
        let mut current_entry = 0;
        for line in contents.lines() {
            if line.starts_with("$ cd ..") {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.pop();
                current_entry = add_dir(&mut entries, new_path);
            } else if line.starts_with("$ cd ") {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.push(line.split(" ").nth(2).unwrap());
                current_entry = add_dir(&mut entries, new_path);
            } else if line.starts_with("$ ls") {
                // do nothing
            } else if line.starts_with("dir ") {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.push(line.split(" ").nth(1).unwrap());
                add_dir(&mut entries, new_path);
            } else {
                let mut new_path = entries.get(current_entry).unwrap().0.clone();
                new_path.push(line.split(" ").nth(1).unwrap());
                let size = line.split(" ").next().unwrap().parse::<u64>().unwrap();
                add_file(&mut entries, new_path, size);
            }
        }
        let total_size = entries
            .iter()
            .filter(|e| e.2 == Type::File)
            .fold(0, |acc, e| acc + e.1);
        let need_to_clear = 30_000_000 - (70_000_000 - total_size);
        let smallest_suitable_dir_size = entries
            .iter()
            .filter(|e| e.1 >= need_to_clear)
            .min_by(|e1, e2| e1.1.cmp(&e2.1))
            .unwrap()
            .1;
        println!("{smallest_suitable_dir_size}");
        Ok(())
    }
    
    fn add_dir(entries: &mut Vec<Entry>, path: PathBuf) -> usize {
        let exists = entries.iter().position(|e| e.0 == path);
        if exists.is_some() {
            exists.unwrap()
        } else {
            entries.push((path, 0, Type::Dir));
            entries.len() - 1
        }
    }
    
    fn add_file(entries: &mut Vec<Entry>, path: PathBuf, size: u64) -> usize {
        let exists = entries.iter().position(|e| e.0 == path);
        if exists.is_some() {
            exists.unwrap()
        } else {
            entries
                .iter_mut()
                .filter(|e| path.starts_with(&e.0))
                .for_each(|e| e.1 += size);
            entries.push((path, size, Type::File));
            entries.len() - 1
        }
    }
    
    3 votes
  4. Comment on Day 6: Tuning Trouble in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; struct Buffer { inner: [(usize, char); 4], } impl...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    struct Buffer {
    	inner: [(usize, char); 4],
    }
    
    impl Buffer {
    	fn new() -> Buffer {
    		Buffer { inner: [(0, '\n'); 4] }
    	}
    
    	fn add(&mut self, new_elem: (usize, char)) {
    		let least_recent = self.inner.iter().enumerate().min_by(|x, y| x.1.0.cmp(&y.1.0)).unwrap();
    		self.inner[least_recent.0] = new_elem;
    	}
    
    	fn unique(&self) -> bool {
    		self.inner.iter().all(|e| e.1 != '\n' && self.inner.iter().filter(|e2| e.1 == e2.1).count() == 1)
    	}
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_6.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut recents: Buffer = Buffer::new();
    	let pos = contents.chars().enumerate().take_while(|(idx, c)| {
    		recents.add((idx + 1, *c));
    		!recents.unique()
    	}).count() + 1;
    	println!("{pos}");
    	Ok(())
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    struct Buffer {
    	inner: [(usize, char); 14],
    }
    
    impl Buffer {
    	fn new() -> Buffer {
    		Buffer { inner: [(0, '\n'); 14] }
    	}
    
    	fn add(&mut self, new_elem: (usize, char)) {
    		let least_recent = self.inner.iter().enumerate().min_by(|x, y| x.1.0.cmp(&y.1.0)).unwrap();
    		self.inner[least_recent.0] = new_elem;
    	}
    
    	fn unique(&self) -> bool {
    		self.inner.iter().all(|e| e.1 != '\n' && self.inner.iter().filter(|e2| e.1 == e2.1).count() == 1)
    	}
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_6.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut recents: Buffer = Buffer::new();
    	let pos = contents.chars().enumerate().take_while(|(idx, c)| {
    		recents.add((idx + 1, *c));
    		!recents.unique()
    	}).count() + 1;
    	println!("{pos}");
    	Ok(())
    }
    
    2 votes
  5. Comment on Day 5: Supply Stacks in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; fn main() -> Result<(), Box<dyn Error>> { let mut...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_5.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut sections = contents.splitn(2, "\n\n");
    	let stacks = sections.next().unwrap();
     	let instructions = sections.next().unwrap();
    	let mut stacks = get_stacks(stacks);
    	move_boxes(&mut stacks, instructions);
    	let mut top_boxes = String::with_capacity(stacks.len());
    	for stack in stacks.iter() {
    		top_boxes.push(*stack.last().unwrap());
    	}
    	println!("{top_boxes}");
    	Ok(())
    }
    
    fn get_stacks(stack_section: &str) -> Vec<Vec<char>> {
    	let num_stacks = (stack_section.lines().last().unwrap().len() + 1) / 4;
    	let mut stacks = Vec::with_capacity(num_stacks);
    	for _ in 0..num_stacks {
    		stacks.push(Vec::new());
    	}
    	for line in stack_section.lines().rev().skip(1) {
    		for idx in 0..num_stacks {
    			let c = line.chars().nth(1 + 4 * idx).unwrap();
    			if c != ' ' {
    				stacks[idx].push(c);
    			}
    		}
    	};
    	stacks
    }
    
    fn move_boxes(stacks: &mut Vec<Vec<char>>, instructions: &str) {
    	for line in instructions.lines() {
    		let mut pieces = line.split(' ').skip(1).step_by(2);
    		let iterations = pieces.next().unwrap().parse::<usize>().unwrap();
    		let from_stack = pieces.next().unwrap().parse::<usize>().unwrap() - 1;
    		let to_stack = pieces.next().unwrap().parse::<usize>().unwrap() - 1;
    		for _ in 0..iterations {
    			let c = stacks[from_stack].pop().unwrap();
    			stacks[to_stack].push(c);
    		}
    	}
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_5.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut sections = contents.splitn(2, "\n\n");
    	let stacks = sections.next().unwrap();
     	let instructions = sections.next().unwrap();
    	let mut stacks = get_stacks(stacks);
    	move_boxes(&mut stacks, instructions);
    	let mut top_boxes = String::with_capacity(stacks.len());
    	for stack in stacks.iter() {
    		top_boxes.push(*stack.last().unwrap());
    	}
    	println!("{top_boxes}");
    	Ok(())
    }
    
    fn get_stacks(stack_section: &str) -> Vec<Vec<char>> {
    	let num_stacks = (stack_section.lines().last().unwrap().len() + 1) / 4;
    	let mut stacks = Vec::with_capacity(num_stacks);
    	for _ in 0..num_stacks {
    		stacks.push(Vec::new());
    	}
    	for line in stack_section.lines().rev().skip(1) {
    		for idx in 0..num_stacks {
    			let c = line.chars().nth(1 + 4 * idx).unwrap();
    			if c != ' ' {
    				stacks[idx].push(c);
    			}
    		}
    	};
    	stacks
    }
    
    fn move_boxes(stacks: &mut Vec<Vec<char>>, instructions: &str) {
    	for line in instructions.lines() {
    		let mut pieces = line.split(' ').skip(1).step_by(2);
    		let iterations = pieces.next().unwrap().parse::<usize>().unwrap();
    		let from_stack = pieces.next().unwrap().parse::<usize>().unwrap() - 1;
    		let to_stack = pieces.next().unwrap().parse::<usize>().unwrap() - 1;
    		let start_index = stacks[from_stack].len() - iterations;
    		for _ in 0..iterations {
    			let c = stacks[from_stack].remove(start_index);
    			stacks[to_stack].push(c);
    		}
    	}
    }
    
    1 vote
  6. Comment on Day 4: Camp Cleanup in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; fn main() -> Result<(), Box<dyn Error>> { let mut...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_4.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let number_containing = contents.lines().fold(0, |acc, line| {
    		let mut pieces = line.splitn(2, ',').map(Range::from_str);
    		let first = pieces.next().unwrap();
    		let second = pieces.next().unwrap();
    		if Range::contains(&first, &second) {
    			acc + 1
    		} else {
    			acc
    		}
    	});
    	println!("{number_containing}");
    	Ok(())
    }
    
    struct Range {
    	start: u32,
    	end: u32,
    }
    
    impl Range {
    	// Creates a new Range from a string of the form "<start>-<end>"
    	fn from_str(s: &str) -> Range {
    		let mut pieces = s.splitn(2, '-');
    		let start = pieces.next().unwrap().parse::<u32>().unwrap();
    		let end = pieces.next().unwrap().parse::<u32>().unwrap();
    		Range {
    			start,
    			end
    		}
    	}
    
    	// Returns true if x contains y or y contains x
    	fn contains(x: &Range, y: &Range) -> bool {
    		(x.start <= y.start && x.end >= y.end) || (y.start <= x.start && y.end >= x.end)
    	}
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_4.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let number_containing = contents.lines().fold(0, |acc, line| {
    		let mut pieces = line.splitn(2, ',').map(Range::from_str);
    		let first = pieces.next().unwrap();
    		let second = pieces.next().unwrap();
    		if Range::overlaps(&first, &second) {
    			acc + 1
    		} else {
    			acc
    		}
    	});
    	println!("{number_containing}");
    	Ok(())
    }
    
    struct Range {
    	start: u32,
    	end: u32,
    }
    
    impl Range {
    	// Creates a new Range from a string of the form "<start>-<end>"
    	fn from_str(s: &str) -> Range {
    		let mut pieces = s.splitn(2, '-');
    		let start = pieces.next().unwrap().parse::<u32>().unwrap();
    		let end = pieces.next().unwrap().parse::<u32>().unwrap();
    		Range {
    			start,
    			end
    		}
    	}
    
    	// Returns true if x contains y or y contains x
    	#[allow(dead_code)]
    	fn contains(x: &Range, y: &Range) -> bool {
    		(x.start <= y.start && x.end >= y.end) || (y.start <= x.start && y.end >= x.end)
    	}
    
    	// Returns true if x and y overlap
    	fn overlaps(x:&Range, y: &Range) -> bool {
    		(x.start >= y.start && x.end <= y.end)
    		|| (x.end >= y.start && x.end <= y.end)
    		|| (y.start >= x.start && y.end <= x.end)
    		|| (y.end >= x.start && y.end <= x.end)
    	}
    }
    
    1 vote
  7. Comment on Signal’s president Meredith Whittaker on what’s next for the private messaging app in ~tech

    whispersilk
    Link Parent
    Signal on Android has SMS support currently and is dropping it. I think Loire's point is that they wish Signal would keep it around.

    Signal on Android has SMS support currently and is dropping it. I think Loire's point is that they wish Signal would keep it around.

    7 votes
  8. Comment on Day 3: Rucksack Reorganization in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. I had a lot of fun using a bitset for this one. Part 1 use std::error::Error; use std::fs::File; use std::io::Read;...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    I had a lot of fun using a bitset for this one.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_3.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let total_overlap_priority = contents.lines().fold(0, |acc, line| {
    		let line_len = line.len();
    		let first = &line[0..line_len / 2];
    		let second = &line[line_len / 2..];
    		let first_set = first.chars().fold(0, |acc, x| acc | priority(x));
    		let second_set = second.chars().fold(0, |acc, x| acc | priority(x));
    		let overlap = first_set & second_set;
    		acc + overlap_priority(overlap)
    	});
    	println!("{total_overlap_priority}");
    	Ok(())
    }
    
    // Returns a u64 with the Xth bit set, where X is the char's priority
    fn priority(c: char) -> u64 {
    	if c >= 'A' && c <= 'Z' {
    		1 << (26 + (c as u8 - 'A' as u8))
    	} else if c >= 'a' && c <= 'z' {
    		 1 << (c as u8 - 'a' as u8)
    	} else {
    		0
    	}
    }
    
    fn overlap_priority(overlap: u64) -> u64 {
    	assert!(overlap & overlap - 1 == 0);
    	let mut n = overlap;
    	let mut priority = 1;
    	while n > 1 {
    		priority += 1;
    		n = n >> 1;
    	}
    	priority
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_3.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut inventory_sets = contents.lines()
    		.map(|line| line.chars().fold(0, |acc, x| acc | priority(x)))
    		.peekable();
    	let mut badge_sum = 0;
    	while inventory_sets.peek().is_some() {
    		let first = inventory_sets.next().unwrap();
    		let second = inventory_sets.next().unwrap();
    		let third = inventory_sets.next().unwrap();
    		let overlap = first & second & third;
    		badge_sum += overlap_priority(overlap);
    	}
    	println!("{badge_sum}");
    	Ok(())
    }
    
    // Returns a u64 with the Xth bit set, where X is the char's priority
    fn priority(c: char) -> u64 {
    	if c >= 'A' && c <= 'Z' {
    		1 << (26 + (c as u8 - 'A' as u8))
    	} else if c >= 'a' && c <= 'z' {
    		 1 << (c as u8 - 'a' as u8)
    	} else {
    		0
    	}
    }
    
    fn overlap_priority(overlap: u64) -> u64 {
    	assert!(overlap & overlap - 1 == 0);
    	let mut n = overlap;
    	let mut priority = 1;
    	while n > 1 {
    		priority += 1;
    		n = n >> 1;
    	}
    	priority
    }
    
    1 vote
  9. Comment on Day 2: Rock Paper Scissors in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; fn main() -> Result<(), Box<dyn Error>> { let mut...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_2.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let total_score = contents.lines().fold(0, |acc, line| {
    		assert!(line.len() >= 3);
    		let me = line.chars().nth(2).unwrap();
    		let opponent = line.chars().next().unwrap();
    		acc + Move::score(Move::of(me), Move::of(opponent))
    	});
    	println!("{total_score}");
    	Ok(())
    }
    
    enum Move {
    	Rock,
    	Paper,
    	Scissors,
    }
    
    impl Move {
    	fn of(s: char) -> Move {
    		match s {
    			'A' | 'X' => Move::Rock,
    			'B' | 'Y' => Move::Paper,
    			'C' | 'Z' => Move::Scissors,
    			_ => unreachable!(),
    		}
    	}
    
    	fn score(me: Move, opponent: Move) -> u64 {
    		match (me, opponent) {
    			(Self::Rock, Self::Rock) => 4, // 1 for rock, 3 because rock ties rock
    			(Self::Rock, Self::Paper) => 1, // 1 for rock, 0 because rock loses to paper
    			(Self::Rock, Self::Scissors) => 7, // 1 for rock, 6 because rock beats scissors 
    			(Self::Paper, Self::Rock) => 8, // 2 for paper, 6 because paper beats rock
    			(Self::Paper, Self::Paper) => 5, // 2 for paper, 3 because paper ties paper
    			(Self::Paper, Self::Scissors) => 2, // 2 for paper, 0 because paper loses to scissors
    			(Self::Scissors, Self::Rock) => 3, // 3 for scissors, 0 because scissors loses to rock
    			(Self::Scissors, Self::Paper) => 9, // 3 for scissors, 6 because scissors beats paper
    			(Self::Scissors, Self::Scissors) => 6, // 3 for scissors, 3 because scissors ties scissors
    		}
    	}
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_2.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let total_score = contents.lines().fold(0, |acc, line| {
    		assert!(line.len() >= 3);
    		let me = line.chars().nth(2).unwrap();
    		let opponent = Move::of(line.chars().next().unwrap());
    		acc + Move::score(Outcome::of(me).to_move(opponent.clone()), opponent)
    	});
    	println!("{total_score}");
    	Ok(())
    }
    
    #[derive(Clone, PartialEq, Eq)]
    enum Move {
    	Rock,
    	Paper,
    	Scissors,
    }
    
    impl Move {
    	fn of(s: char) -> Move {
    		match s {
    			'A' | 'X' => Move::Rock,
    			'B' | 'Y' => Move::Paper,
    			'C' | 'Z' => Move::Scissors,
    			_ => unreachable!(),
    		}
    	}
    
    	fn beats(&self) -> Move {
    		match self {
    			Move::Rock => Move::Scissors,
    			Move::Paper => Move::Rock,
    			Move::Scissors => Move::Paper,
    		}
    	}
    
    	fn loses_to(&self) -> Move {
    		match self {
    			Move::Rock => Move::Paper,
    			Move::Paper => Move::Scissors,
    			Move::Scissors => Move::Rock,
    		}
    	}
    
    	fn ties(&self) -> Move {
    		return self.clone()
    	}
    
    	fn intrinsic_score(&self) -> u64 {
    		match self {
    			Move::Rock => 1,
    			Move::Paper => 2,
    			Move::Scissors => 3,
    		}
    	}
    
    	fn score(me: Move, opponent: Move) -> u64 {
    		me.intrinsic_score() + if me.loses_to() == opponent {
    			0
    		} else if me.ties() == opponent {
    			3
    		} else {
    			6
    		}
    	}
    }
    
    enum Outcome {
    	Win,
    	Lose,
    	Tie,
    }
    
    impl Outcome {
    	fn of(s: char) -> Outcome {
    		match s {
    			'X' => Outcome::Lose,
    			'Y' => Outcome::Tie,
    			'Z' => Outcome::Win,
    			_ => unreachable!(),
    		}
    	}
    
    	fn to_move(&self, other: Move) -> Move {
    		match self {
    			Outcome::Win => other.loses_to(),
    			Outcome::Lose => other.beats(),
    			Outcome::Tie => other.ties(),
    		}
    	}
    }
    
    1 vote
  10. Comment on Day 1: Calorie Counting in ~comp.advent_of_code

    whispersilk
    Link
    I'm using Rust this year, and hoping to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; fn main() -> Result<(), Box<dyn Error>> { let mut...

    I'm using Rust this year, and hoping to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_1.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let max_elf: u64 = contents
    		.trim()
    		.split("\n\n")
    		.map(|one_elf|
    			one_elf.split('\n')
    				.map(|line| u64::from_str_radix(line, 10).unwrap())
    				.fold(0, |acc, x| acc + x))
    		.max()
    		.unwrap();
    	println!("{max_elf}");
    	Ok(())
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_1.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let elves: (u64, u64, u64) = contents
    		.trim()
    		.split("\n\n")
    		.map(|one_elf|
    			one_elf.split('\n')
    				.map(|line| u64::from_str_radix(line, 10).unwrap())
    				.fold(0, |acc, x| acc + x))
    		.fold((0, 0, 0), |acc, x| {
    			if x > acc.0 {
    				(x, acc.0, acc.1)
    			} else if x > acc.1 {
    				(acc.0, x, acc.1)
    			} else if x > acc.2 {
    				(acc.0, acc.1, x)
    			} else {
    				acc
    			}
    		});
    	println!("{}", elves.0 + elves.1 + elves.2);
    	Ok(())
    }
    
    1 vote
  11. Comment on What programming/technical projects have you been working on? in ~comp

    whispersilk
    Link
    I can't say that I made all that much progress on it this week, but I did in the end manage to get my personal fanfic archive project out onto GitHub for the perusal of anyone interested....

    I can't say that I made all that much progress on it this week, but I did in the end manage to get my personal fanfic archive project out onto GitHub for the perusal of anyone interested.

    @cinereus and anyone else, you can find it here.

    4 votes
  12. Comment on The best Twitter alternatives in ~tech

    whispersilk
    Link Parent
    Oh, absolutely! I think there are two big differences between the two right now. First, the email situation isn't quite as absolute — it's all heuristics, checking IP ranges and compliance with...

    Oh, absolutely!

    I think there are two big differences between the two right now. First, the email situation isn't quite as absolute — it's all heuristics, checking IP ranges and compliance with various standards and whatnot, and can be worked around or reversed — while de-federation is effectively just putting a domain on a blocklist and being done with it. Second, the email situation is one-way: GMail users (or users of other large, generally trusted servers) can always send emails to whatever small server, they just may not be able to receive emails in turn. De-federation is bidirectional.

    There are definitely parallels, though, and I do wonder if Mastodon will every grow/evolve to the point where it has to do the same kind of heuristic spam filtering that email does.

    3 votes
  13. Comment on The best Twitter alternatives in ~tech

    whispersilk
    Link Parent
    No, you're right, 90% is a very exaggerated number, but mastodon.social is still the largest single instance by far. According to this it's almost eight times as large as the next largest...

    No, you're right, 90% is a very exaggerated number, but mastodon.social is still the largest single instance by far. According to this it's almost eight times as large as the next largest English-speaking server, and only has a single close rival in any language (pawoo.net, which is a Japanese server).

    2 votes
  14. Comment on The best Twitter alternatives in ~tech

    whispersilk
    Link Parent
    Regarding you not fully understanding what NaraVara said, here's my attempt to make it clearer: Jester said that he blacklisted all IPs from Russia, Iran, and a few other countries. In response,...
    • Exemplary

    Regarding you not fully understanding what NaraVara said, here's my attempt to make it clearer:

    Jester said that he blacklisted all IPs from Russia, Iran, and a few other countries. In response, the person1 in charge of mastodon.social de-federated sounter.social, which means that nobody with an account on mastodon.social would see anything posted by people with an account on counter.social. As an analogy, imagine if GMail said "we are no longer allowing people who have gmail.com accounts to communicate with people who have protonmail.com accounts. If you're a GMail user and want to send emails to or receive emails from protonmail.com, tough luck." The person in charge of mastodon.social then convinced some other big instances to do the same thing, so counter.social said "okay, screw this, we'll go off and be our own self-contained thing and not worry about being able to send messages to and from other servers at all."

    The whole things demonstrates an issue with Mastodon adoption, which is that — since mastodon.social is home to 90%2 of all Mastodon accounts — if mastodon.social de-federates you then you lose access to 90% of your audience even if you have an account on some other server. This is better than Twitter, where if the person in charge decides to ban you then you lose 100% of your audience, but not by much. In some ways it's worse, because mastodon.social makes de-federation decisions on a server-by-server basis, so your server may be de-federated when you haven't personally done anything wrong. Hopefully some other servers come along and get big enough that mastodon.social is only home to 50% of all accounts, or 30%, or 10%. If any single server is a smaller part of the ecosystem at large, that server's decisions have less influence over the other servers in the ecosystem.


    1: I say "person" but in reality this may be a team, or a foundation, or whatever else. Person is just shorthand for whatever governing body makes decisions at mastodon.social.

    2: 90% is a made-up number. The point is that mastodon.social is home to a very large percentage of all Mastodon accounts in existence.

    8 votes
  15. Comment on What programming/technical projects have you been working on? in ~comp

    whispersilk
    Link Parent
    Thank you for the kind words! Right now it handles AO3, RoyalRoad, and Xenforo threadmarks (currently only SpaceBattles and SufficientVelocity, but the parser should work for any Xenforo site once...

    Thank you for the kind words! Right now it handles AO3, RoyalRoad, and Xenforo threadmarks (currently only SpaceBattles and SufficientVelocity, but the parser should work for any Xenforo site once I make the stuff around it smart enough to realize that a URL is, in fact, running Xenforo). I would love to add FFnet support, but I have some work left to do there — I haven't figured out how to get around its CloudFlare protections yet. As soon as I do, though, it's going in!

    Right now the source code is sitting on a personal git server (shout out to soft serve for making that easy) but I can stick it out on GitHub and make it publicly available, sure! It's not ready like I would like it to be, but it's ready enough that I think other people could maybe get some value out of it. I probably won't get around to it this weekend because I'm super busy, but maybe next week. Pester me about it if I don't chime in on next week's thread. :)

    2 votes
  16. Comment on What programming/technical projects have you been working on? in ~comp

    whispersilk
    Link Parent
    Oh! Yes, it is, and I think I forgot that most boorus aren't. Web based is also very cool, and I guess makes your leaning toward Elixir/Phoenix for it make more sense.

    Oh! Yes, it is, and I think I forgot that most boorus aren't. Web based is also very cool, and I guess makes your leaning toward Elixir/Phoenix for it make more sense.

  17. Comment on What programming/technical projects have you been working on? in ~comp

    whispersilk
    Link Parent
    I found this from your comment in the most recent technical projects thread and just wanted to say that personal booru software is neat and it would be really cool to see some competition for hydrus.

    I found this from your comment in the most recent technical projects thread and just wanted to say that personal booru software is neat and it would be really cool to see some competition for hydrus.

  18. Comment on What programming/technical projects have you been working on? in ~comp

    whispersilk
    Link
    I'm working on a personal library tool, specifically designed around fanfiction and other works that are published serially rather than all at once. I follow a lot of stories that aren't yet...

    I'm working on a personal library tool, specifically designed around fanfiction and other works that are published serially rather than all at once. I follow a lot of stories that aren't yet marked complete, and I find that it can be really hard to keep what I'm following in my mind so that I remember to check up on them. I wanted a solution that:

    • wouldn't result in my getting even more emails than I already do;
    • would store the things I'm following locally on my computer;
    • would give me insight into what chapters I have and have not read; and
    • would be fast, easy to run, and easy to back up.

    I did some looking for a tool that fit all of those marks and didn't find one, so I'm doing it myself. I'm writing it in Rust, because a good concurrency story is helpful when much of your program's work is "download a lot of things from the internet" and also just because I like it, and I've actually made a lot of progress over the past few weeks! I can download and store stories from the handful of websites that I use most often, so my next task is tracking read status of chapters and implementing a UI of some sort — possibly two, because I'm thinking I might go in the direction of having a choice between TUI and web-based interfaces.

    5 votes
  19. Comment on Netflix with ads will cost $7 per month at launch in November in ~tv

    whispersilk
    Link Parent
    At least this time around the shows would be on demand?

    At least this time around the shows would be on demand?

    1 vote
  20. Comment on Twitter accepts buyout, giving Elon Musk total control of the company in ~tech