Macil's recent activity

  1. Comment on Senate Democrats block mammoth coronavirus stimulus package in ~finance

    Macil
    Link
    This article really worked to avoid mentioning what the Democrats' problems with the bill were. I saw previous articles suggesting that the Republicans were trying to put anti-abortion stuff into...

    This article really worked to avoid mentioning what the Democrats' problems with the bill were.

    I saw previous articles suggesting that the Republicans were trying to put anti-abortion stuff into the bill, so I first assumed the issue now was just that and the article was deliberately avoiding talking about that to unfairly demonize the Democrats. But then I googled more for information about the bill (https://www.commondreams.org/news/2020/03/18/refusal-pelosi-consider-universal-cash-payments-response-coronavirus-pandemic) and now I'm just baffled by what's going on.

    16 votes
  2. Comment on U.S. Department of Justice seeks new emergency powers amid coronavirus pandemic in ~health.coronavirus

    Macil
    Link
    Are there not existing laws/procedures for enforcing quarantine? Surely the government already has the ability to quarantine people in pandemics? Or is this a necessary step to that?

    Are there not existing laws/procedures for enforcing quarantine? Surely the government already has the ability to quarantine people in pandemics? Or is this a necessary step to that?

    1 vote
  3. Comment on Bloomberg drops out of presidential race, endorses Biden in ~news

    Macil
    Link Parent
    I think his point is that Bernie could beat Trump, but Biden can't and Biden is more likely to win the primary now that Bloomberg has dropped out and endorsed him. That's my belief anyway.

    I think his point is that Bernie could beat Trump, but Biden can't and Biden is more likely to win the primary now that Bloomberg has dropped out and endorsed him. That's my belief anyway.

    16 votes
  4. Comment on Super Tuesday: who did you end up voting for and why? in ~talk

    Macil
    Link
    Sanders. Universal healthcare and beating Trump are my two biggest concerns that I think the candidates differ on. Biden's policies aren't great in this and others, and I don't believe at all that...

    Sanders. Universal healthcare and beating Trump are my two biggest concerns that I think the candidates differ on. Biden's policies aren't great in this and others, and I don't believe at all that he can beat Trump.

    Warren seems okay, but with her numbers, a vote for her is just a protest vote that risks giving it to Biden, so I'm baffled by her current support. Her policies seem so much closer to Sanders than Biden, so I assume her supporters are evaluating Sanders very differently than I am (maybe they think he'll flub it all or lose to Trump and that even Biden would do better), or they just aren't into her for her policies or even a real chance at winning. A lot of support I've seen for her seems to just focus on some relatable quality in her personality that people want to see rewarded (in votes even if not a win) like a kind of self validation. (This tweet thread put it into words for me.) (I think every candidate gets this kind of vapid support, but in Warren's case it makes up a larger fraction of her support because people into her policies already hopped over to Sanders for a more realistic shot at getting something like them done.)

    Part of what makes me accept this explanation is that the converse of it explains one of the reasons why I dislike that Trump won so much: he's a lying asshole, and I don't like the idea of lying assholes being rewarded. (Though the reasoning is more valid in this case since those qualities are closely tied to bad policies.)

    11 votes
  5. Comment on What tasks on your computer have you automated? in ~comp

    Macil
    Link Parent
    Setting up Prettier to auto-format code on every commit in my work's javascript/typescript codebase has been a huge hit. I feel like it's changed me to consider code more as an abstract syntax...

    Setting up Prettier to auto-format code on every commit in my work's javascript/typescript codebase has been a huge hit. I feel like it's changed me to consider code more as an abstract syntax tree than as written text that can be arbitrarily formatted and happens to be machine-executable. It makes it easier for me to consider the meaning of unfamiliar code rather than wasting cycles thinking about how it's formatted. I barely think about formatting any more; it's somebody else's problem (the auto-formatter's) now.

    2 votes
  6. Comment on Do you run your own blog for personal use? in ~comp

    Macil
    Link
    I use a static site generator, so my blog is just a set of static files that I publish. I use the free tier of https://pinata.cloud/ to pin the files so they're accessible via IPFS, and then I use...

    Is it self-hosted, or do you rent server space?

    I use a static site generator, so my blog is just a set of static files that I publish. I use the free tier of https://pinata.cloud/ to pin the files so they're accessible via IPFS, and then I use CloudFlare's free IPFS gateway to make them accessible through my domain name. Completely free besides the domain name, and it means that in addition to being accessible as a normal site, people can access it through IPFS and can help re-host it or archive a copy of it.

    Besides being free hosting, it's super convenient that I don't have to manage anything, and I really value that IPFS provides an easy way for others to keep up my site themselves if anything goes wrong and I'm no longer around to fix things (like if the domain expires or the specific IPFS pinning service I used ends). It's something that could possibly outlast me, without just relying on the goodwill of a specific company. I hope more of the internet uses IPFS or something like it in the future, because it's pretty depressing how many websites fall off the internet completely after their creators go away.

    Do you use Wordpress or another blog platform like that, publish through other means like a flat-file CMS, or did you build it from scratch?

    I use Gatsby, which is a static site generator that uses React components for page templates. This is super convenient for sites that want interactive bits. (I'm a big fan of the interactivity in Going Critical; I want to make pages like that on my site.)

    Click for an aside about what makes Gatsby and React good for this

    In standard site generators or plain pages, if you want a specific page to have an interactive widget, then you need to:

    1. Make a template file used by the site generator containing the HTML of the widget as it originally appears on the page.
    2. Write some javascript which runs on page load and finds the widget HTML on the page and binds some event listeners to it to make it interactive.
    3. If other page javascript can ever cause your widget HTML to be added to the page, then you need to explicitly hook into the recognizer code of step 2 so it activates your widget in this case too.
    4. Whenever your code needs to update the widget's HTML, you often end up needing to re-create parts of the widget's HTML with other values substituted in. It's super awkward that your javascript can easily end up containing a near-copy of your HTML template.
    5. The specific page you want the widget in has to include this javascript. If you're using a javascript bundler, then you'll need to configure it to make another javascript bundle for this specific page. (Or you don't do that, and you just make one javascript bundle for your whole site, causing every page to load the code for every widget used on every other page.)

    It's all doable, but it's a discouraging amount of effort and repetition. You're probably not going to want to do that all any more than necessary. Gatsby and React removes the need for all of those steps since your components describe the initial HTML and the runtime javascript, and Gatsby automatically creates javascript bundles specifically for each page.

    This post has quickly become me just preaching about IPFS and React, hasn't it. Hmm, maybe I can recycle this text into a few blog posts on that.

    What topics do you write about?

    Software development.

    How consistently do you post; or alternatively, why don't you post as often as you would like to?

    My site just has like 5 posts I wrote years ago. I was too busy with other stuff so I didn't write much, but now I want to make more.

    I reworked my site in the last few days and started drafting some new posts. I hadn't really been happy with my site's tech stack before now. I'm really excited that my current tech stack makes it really easy for me to make pages with arbitrary templates and interactive bits. It motivates me knowing that I can make my site have things that aren't possible or easy on popular platforms.

    Do you keep analytics, or do you write regardless of how many clicks you get?

    I've got Google Analytics on my site. I'm not entirely sure what the intersection is of stuff I like to make and stuff people like to see, and I'm really interested in figuring that out. I'm curious whether people who come to my site when a post is linked on an aggregator tend to explore other pages on it.

    Is your site monetized with ads or otherwise?

    No. Even pretending I had traffic, I think the most I hope to get out of my writing is personal or professional connections. Maybe one day I might make little games and sell them or other software on my site, but that would be pretty far off.

    How popular is your blog on average?

    A couple of my old posts got discussed on HN and Reddit when I first posted them. That's probably nearly all the traffic I've gotten.

    How do you keep up with other writers' posts?

    I find a lot of software tech posts on Hacker News. I don't follow too many specific writers from that though. Sometimes I follow people's Twitter accounts, though that generally just exposes me to their favorite stuff rather than more of their content, which has been neat for most, though it's not been efficient for the task of keeping up with their content.

    I set up RSS on my site so people could use it to follow me, but I don't use RSS to follow anyone. I think I support it mainly just in case RSS (or a service consuming it) suddenly becomes popular in the future.

    I'm surprised how many others here mention using RSS. I wonder if it just happens to be popular with the sort of people that run their own blogs. tbh maybe that's the audience I'm aiming for, so it's probably a good thing I have it.

    3 votes
  7. Comment on What games have you been playing, and what's your opinion on them? in ~games

    Macil
    Link Parent
    I'm in the same boat as you on these. I've had VR for years (Vive and now Index), and controller locomotion is still automatic nausea for me. And then Boneworks has a ton of mechanics that are...

    Pavlov/Boneworks

    I'm in the same boat as you on these. I've had VR for years (Vive and now Index), and controller locomotion is still automatic nausea for me. And then Boneworks has a ton of mechanics that are perfect nausea triggers. The fact that you can push yourself off of other objects means if you push an object down onto a surface, or even just set your hands to your side and bump something out of your sight, then the whole world in your vision unexpectedly lurches. It feels like my real life hitpoints just got hit, which might be kinda neat as a punishment in a game, but it's a completely absurd consequence for bumping your hand into something. It's hard for me to play it longer than 15 minutes, and I never managed to make it to the first save point. Maybe I could make it through the game if I paced myself and stood still a lot, but at least in the early game, there's nothing to entertain yourself with while standing still. I haven't tried Boneworks again.

    Pavlov is okay in small doses for me, especially if I play in a less aggressive style where I camp a lot. It helps that it has a quicker gameplay loop, so playing it for just 5 minutes can actually feel rewarding. (5 minutes of Boneworks and you're barely into the tutorial area.) Its gunplay is neat but I don't think there's much more to it than that.

    Generally I avoid any games with controller locomotion. Games without controller locomotion do not cause nausea for me at all. Anyway, some recommendations if you want: Out of Ammo, Valve's The Lab (especially archery and Xortex), Superhot VR, SteamVR Home (it's really neat to explore all the custom environments. My favorites: Super Mario 64, Mars, Talos Principle, some others 1 2 3), Beat Saber, Space Pirate Trainer, Arizona Sunshine, Job Simulator, Budget Cuts

    3 votes
  8. Comment on How do I combat the "women need safe spaces" argument? in ~lgbt

    Macil
    Link
    A lot of anti-trans arguments seem like recycled anti-gay arguments. I remember similar arguments against gay men and women predators sharing bathrooms with straight people, though I don't...

    A lot of anti-trans arguments seem like recycled anti-gay arguments. I remember similar arguments against gay men and women predators sharing bathrooms with straight people, though I don't remember any catchy counter-arguments besides the observation that present gay acceptance hasn't lead to an epidemic of cases like that.

    7 votes
  9. Comment on Self promotion vs. Original content vs. Own content vs. User created vs. ...? in ~tildes

    Macil
    Link Parent
    I've seen the phrase a lot of times before and I didn't even realize I've been misunderstanding it. I just thought it meant something was both new and specifically related to the place it was...

    I've seen the phrase a lot of times before and I didn't even realize I've been misunderstanding it. I just thought it meant something was both new and specifically related to the place it was being linked, not that it was made by the person posting it.

    2 votes
  10. Comment on Altered Carbon - Season 2 trailer in ~tv

    Macil
    Link Parent
    They previously announced that season 2 isn't following the second book. I'm a bit disappointed, though I bet they'll take elements from books 2 and 3 even if they don't use them directly.

    They previously announced that season 2 isn't following the second book. I'm a bit disappointed, though I bet they'll take elements from books 2 and 3 even if they don't use them directly.

    4 votes
  11. Comment on United Nations guidelines for gender-inclusive language in English in ~humanities

    Macil
    Link Parent
    As a native english speaker who has always been a fan of singular "they" and thought that it's perfectly natural, I've always interpreted the word "they" as being more about referring to...

    As a native english speaker who has always been a fan of singular "they" and thought that it's perfectly natural, I've always interpreted the word "they" as being more about referring to unspecified people (zero, one, or more) rather than being about plurality. My personal intuition is it just happens to be used for plurality because there's no way to specify multiple at once in one word, so you have to use the word for unspecified people in that situation, but it's not the only situation you would use the word for unspecified people. The word does not make me think definitely of plurality in the same way that "many", "several", or plural words that end in -s like "things" do. It feels like the word "audience" in the sentence "Know your audience when speaking": the word fits for a situation where you're talking to a group or a situation where you're talking to one person.

    It's interesting to see that the closest equivalent of "they" in Portuguese does end in -s like a plural word. I wonder if you might be mentally translating the word "they" to "eles" in your head and getting the "ends with -s (so it definitely refers to several things)" quality associated with "they", which wouldn't happen for native english speakers.

    3 votes
  12. Comment on How do we stop the polarization/toxicity filling the web? in ~tech

    Macil
    Link Parent
    I've seen that attitude a lot too on HN and often on Reddit. Even ignoring HN's moderators, I find this attitude really funny there because it totally misses how post voting systems that power HN...

    "moderation is bad and everyone should decide for themselves"

    I've seen that attitude a lot too on HN and often on Reddit. Even ignoring HN's moderators, I find this attitude really funny there because it totally misses how post voting systems that power HN and Reddit are essentially moderation systems themselves. There's several posts in the OP HN thread that go like "There's no problem to solve. Trying to solve toxicity would be intruding on free speech!", yet they miss that a voting system is one (imperfect) way of trying to solve the problem of low-value posts including toxic ones.

    I think a lot of people online operate on the unexamined myth that there's some true neutral and objective way for a site to choose what posts to show, that the system they're familiar with (Reddit/HN post voting, or "reply bumps thread to the top" systems on many classic forums and imageboards) is that true neutral system, and that the objectivity of this true neutral system makes it categorically/morally better than having trusted moderators no matter the negative consequences to it. These "objective" kind of systems come with different pros and cons, so the choice of which to use is hardly objective itself.

    5 votes
  13. Comment on Overwatch Developer Update - Starting in March, "Hero Pools" will be introduced to competitive play, where different heroes will be disabled every week to encourage players to change strategies in ~games

    Macil
    Link
    I like it in games where people are a bit unfamiliar with the system, and success comes from being able to adapt quickly rather than having memorized some meta, so I think this sounds neat. I...

    I like it in games where people are a bit unfamiliar with the system, and success comes from being able to adapt quickly rather than having memorized some meta, so I think this sounds neat. I don't really ever play competitive mode though.

    One of my favorite things in multiplayer games is playing custom gametypes+maps (especially in Halo) where the gametype+map is unfamiliar to everyone and it's a chaotic rush to figure out what's going on and get a foothold in it.

    2 votes
  14. Comment on Why “rape games” are worse than violent games in ~games

    Macil
    (edited )
    Link
    I like the article's argument. I've seen the argument that slurs/racism/sexism etc are fine to encourage in games and don't matter at all because killing is already popular in games, and I...

    I like the article's argument. I've seen the argument that slurs/racism/sexism etc are fine to encourage in games and don't matter at all because killing is already popular in games, and I absolutely detest that argument.

    Killing in most games is generally an abstract game mechanic compared to what it's like in regular life, and people are less sensitive to it in media because society already does a good job of stigmatizing it. Regular people generally don't run into people that openly think killing is low-key okay, get emboldened by media portrayal of it, and then wantonly commit it while their victims have no recourse and have trouble convincing anyone there's even an issue. That's not so true about sexual assault or bigotry.

    I don't think I'd be for making games like Rape Day illegal (without some specific evidence of the harm it does at large to society), but I think it's good to push back as a consumer and advocate that storefronts and other companies involved with games shouldn't help enable these kinds of games. Someone can make a rape simulator on their own, but I don't want to do business with anyone enabling it so I'd rather Steam didn't.

    3 votes
  15. Comment on Star Trek: Picard S01E01 - Remembrance in ~tv

    Macil
    Link Parent
    Agreed. The scenes focused on Picard were great, but most scenes containing Dahj felt like something from a generic sci-fi/superhero thriller. I want less superhero action scenes and more of...

    Agreed. The scenes focused on Picard were great, but most scenes containing Dahj felt like something from a generic sci-fi/superhero thriller. I want less superhero action scenes and more of Picard convincing or arguing with people about weighty stuff.

    5 votes
  16. Comment on Stack Exchange, the parent company of Stack Overflow, has been taking aggressive stances against the Stack Exchange community in ~comp

    Macil
    Link
    I've found it hard to care about the drama around the Monica firing since it had to do with her trying to play word games to avoid following the spirit of a rule about respecting trans people's...

    I've found it hard to care about the drama around the Monica firing since it had to do with her trying to play word games to avoid following the spirit of a rule about respecting trans people's pronouns. I don't think it's too much for a site to select for moderators that will enthusiastically enforce and support their rules. A moderator position is not owed to anyone.

    2 votes
  17. Comment on There is such a thing as too much technology in ~tech

    Macil
    Link
    It sounds like a cheap implementation of something like what Amazon Go stores have. At an Amazon Go store, you scan a QR code on an app on your phone to get in, and then you can pick up anything...

    It sounds like a cheap implementation of something like what Amazon Go stores have. At an Amazon Go store, you scan a QR code on an app on your phone to get in, and then you can pick up anything you want and just walk out the door when you're done. Cameras track you and what you grab in the store.

    I love it. No more waiting in checkout lines. I guess the benefit is that it's dependable and hard to mess up using. You can't get in until you've done the thing right, and there's no complex pairing process between your phone and the scanner: a visual QR code that you can see yourself is pretty reliable and easy to confirm you've got your end working before you scan it.

  18. Comment on Sort By Controversial in ~tech

    Macil
    Link
    I love how this story emphasizes that humans are predictably emotional and tribal, and that our current media optimizes to find and propagate divisive statements. Even without an optimized scissor...

    I love how this story emphasizes that humans are predictably emotional and tribal, and that our current media optimizes to find and propagate divisive statements. Even without an optimized scissor statement generator, scissor statements have no trouble arising by chance, and our society is already optimized to beat itself with scissor statements. The story doesn't have any answers but is good at framing an issue.

    I think one big problem is that news companies and social media sites are encouraged to increase traffic at any cost, and needlessly divisive statements do a good job at increasing traffic. If news and social media sites didn't have to worry so much about increasing their numbers (to stay profitable, and to keep a userbase), then maybe this problem could go away, but it's hard to imagine a setup where those aren't issues or a setup that solves those without introducing other issues.

    Maybe there's a way to push for a culture shift to help people want to be resistant to divisive outrage news items. It seems hard to balance that idea against the need to be outraged and take action sometimes though.

    5 votes
  19. Comment on Day 24: Planet of Discord in ~comp

    Macil
    Link
    I've really enjoyed how this problem and day 20's (the donut maze one) added some unusual geometry to the problem. It really emphasizes thinking about problems as arbitrary graphs rather than just...

    I've really enjoyed how this problem and day 20's (the donut maze one) added some unusual geometry to the problem. It really emphasizes thinking about problems as arbitrary graphs rather than just grids. Also, it keeps the problem unique; it means it's less likely that there's a pre-existing ASCII maze-solver or cellular automata engine that the problem could be plugged into.

    Day 24, both parts in Rust
    use clap::{App, Arg};
    use std::collections::{HashSet, VecDeque};
    use std::error::Error;
    use std::fmt;
    
    fn main() -> Result<(), Box<dyn Error>> {
        let matches = App::new("day24")
            .arg(
                Arg::with_name("INPUT")
                    .help("Sets the input file to use")
                    .required(true),
            )
            .get_matches();
    
        let input_file = matches.value_of("INPUT").unwrap();
        let input = std::fs::read_to_string(input_file)?;
    
        let mut world = SimpleWorld::new_from_string(&input);
        world.run_until_repeat();
        println!("part 1: {}", world.biodiversity_rating());
    
        let mut rworld = RecursiveWorld::new(Level::new_from_string(&input));
        for _ in 0..200 {
            rworld.step();
        }
        println!("part 2: {}", rworld.count_live());
    
        Ok(())
    }
    
    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
    enum Tile {
        Live,
        Dead,
    }
    
    impl fmt::Display for Tile {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Tile::Live => write!(f, "#"),
                Tile::Dead => write!(f, "."),
            }
        }
    }
    
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    struct SimpleWorld {
        tiles: Vec<Tile>,
        width: usize,
    }
    
    impl SimpleWorld {
        fn new_from_string(input: &str) -> SimpleWorld {
            let mut tiles = Vec::with_capacity(25);
            for c in input.chars() {
                let tile = match c {
                    '#' => Tile::Live,
                    '.' => Tile::Dead,
                    ' ' | '\n' | '\r' => continue,
                    _ => panic!("Unexpected character: {}", c),
                };
                tiles.push(tile);
            }
            assert_eq!(tiles.len(), 25);
            SimpleWorld { tiles, width: 5 }
        }
    
        fn step(&mut self) {
            let copy = self.tiles.clone();
            let copy_rows = copy.chunks(self.width).collect::<Vec<_>>();
            let mut rows = self.tiles.chunks_mut(self.width).collect::<Vec<_>>();
    
            for (y, &copy_row) in copy_rows.iter().enumerate() {
                for (x, tile) in copy_row.iter().enumerate() {
                    let n = SimpleWorld::count_neighbors(x, y, &copy_rows);
                    match tile {
                        Tile::Live => {
                            if n != 1 {
                                rows[y][x] = Tile::Dead;
                            }
                        }
                        Tile::Dead => {
                            if n == 1 || n == 2 {
                                rows[y][x] = Tile::Live;
                            }
                        }
                    }
                }
            }
        }
    
        fn count_neighbors(x: usize, y: usize, copy_rows: &[&[Tile]]) -> u8 {
            let mut result = 0;
            if x != 0 && copy_rows[y][x - 1] == Tile::Live {
                result += 1;
            }
            if y != 0 && copy_rows[y - 1][x] == Tile::Live {
                result += 1;
            }
            if x != copy_rows[y].len() - 1 && copy_rows[y][x + 1] == Tile::Live {
                result += 1;
            }
            if y != copy_rows.len() - 1 && copy_rows[y + 1][x] == Tile::Live {
                result += 1;
            }
            result
        }
    
        fn run_until_repeat(&mut self) {
            let mut seen_states = HashSet::new();
            loop {
                if !seen_states.insert(self.clone()) {
                    break;
                }
                self.step();
            }
        }
    
        fn biodiversity_rating(&self) -> u64 {
            self.tiles
                .iter()
                .enumerate()
                .filter(|(_, &tile)| tile == Tile::Live)
                .map(|(i, _)| 2_u64.pow(i as u32))
                .sum()
        }
    }
    
    impl fmt::Display for SimpleWorld {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            for line in self.tiles.chunks(self.width) {
                for tile in line {
                    tile.fmt(f)?;
                }
                writeln!(f, "")?;
            }
            Ok(())
        }
    }
    
    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
    struct Level {
        tiles: [Tile; 24],
    }
    
    impl Level {
        fn new() -> Level {
            Level {
                tiles: [Tile::Dead; 24],
            }
        }
    
        fn new_from_string(input: &str) -> Level {
            let mut level = Level::new();
            for (y, line) in input.trim().lines().enumerate() {
                for (x, c) in line.trim().chars().enumerate() {
                    if (x, y) == (2, 2) {
                        continue;
                    }
                    level[(x as u8, y as u8)] = match c {
                        '#' => Tile::Live,
                        '.' => Tile::Dead,
                        _ => panic!("Unexpected character: '{}'", c),
                    };
                }
            }
            level
        }
    
        fn is_empty(&self) -> bool {
            self.tiles == [Tile::Dead; 24]
        }
    
        fn coord_to_slot(loc: (u8, u8)) -> usize {
            let r = (loc.1 * 5 + loc.0) as usize;
            assert_ne!(r, 12);
            if r >= 12 {
                r - 1
            } else {
                r
            }
        }
    
        fn count_top(&self) -> u8 {
            self.tiles[0..5]
                .iter()
                .filter(|&&t| t == Tile::Live)
                .count() as u8
        }
    
        fn count_bottom(&self) -> u8 {
            self.tiles[19..24]
                .iter()
                .filter(|&&t| t == Tile::Live)
                .count() as u8
        }
    
        fn count_left(&self) -> u8 {
            [
                self.tiles[0],
                self.tiles[5],
                self.tiles[10],
                self.tiles[14],
                self.tiles[19],
            ]
            .iter()
            .filter(|&&t| t == Tile::Live)
            .count() as u8
        }
    
        fn count_right(&self) -> u8 {
            [
                self.tiles[4],
                self.tiles[9],
                self.tiles[13],
                self.tiles[18],
                self.tiles[23],
            ]
            .iter()
            .filter(|&&t| t == Tile::Live)
            .count() as u8
        }
    }
    
    impl std::ops::Index<(u8, u8)> for Level {
        type Output = Tile;
    
        fn index(&self, loc: (u8, u8)) -> &Self::Output {
            &self.tiles[Level::coord_to_slot(loc)]
        }
    }
    
    impl std::ops::IndexMut<(u8, u8)> for Level {
        fn index_mut(&mut self, loc: (u8, u8)) -> &mut Self::Output {
            &mut self.tiles[Level::coord_to_slot(loc)]
        }
    }
    
    impl fmt::Display for Level {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            for y in 0..5 {
                for x in 0..5 {
                    if (x, y) == (2, 2) {
                        write!(f, "?")?;
                    } else {
                        self[(x, y)].fmt(f)?;
                    }
                }
                writeln!(f, "")?;
            }
            Ok(())
        }
    }
    
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    struct RecursiveWorld {
        outermost_depth: i32,
        levels: VecDeque<Level>,
    }
    
    impl RecursiveWorld {
        fn new(initial_level: Level) -> RecursiveWorld {
            let mut levels = VecDeque::new();
            levels.push_back(initial_level);
            RecursiveWorld {
                outermost_depth: 0,
                levels,
            }
        }
    
        fn step(&mut self) {
            let snapshot = self.clone();
    
            fn is_one_or_two(value: u8) -> bool {
                value == 1 || value == 2
            }
    
            for (i, level) in snapshot.levels.iter().enumerate() {
                let outer_level = if i == 0 {
                    None
                } else {
                    Some(&snapshot.levels[i-1])
                };
                let inner_level = snapshot.levels.get(i + 1);
    
                let new_level = &mut self.levels[i];
    
                for y in 0..5 {
                    for x in 0..5 {
                        if (x, y) == (2, 2) {
                            continue;
                        }
                        let tile = level[(x, y)];
                        let n = RecursiveWorld::count_neighbors(level, (x, y), inner_level, outer_level);
                        match tile {
                            Tile::Live => {
                                if n != 1 {
                                    new_level[(x, y)] = Tile::Dead;
                                }
                            }
                            Tile::Dead => {
                                if n == 1 || n == 2 {
                                    new_level[(x, y)] = Tile::Live;
                                }
                            }
                        }
                    }
                }
            }
    
            // check if we need to add new levels on the ends
            {
                let outermost_level = &snapshot.levels[0];
                let mut new_level = Level::new();
                if is_one_or_two(outermost_level.count_top()) {
                    new_level[(2, 1)] = Tile::Live;
                }
                if is_one_or_two(outermost_level.count_bottom()) {
                    new_level[(2, 3)] = Tile::Live;
                }
                if is_one_or_two(outermost_level.count_left()) {
                    new_level[(1, 2)] = Tile::Live;
                }
                if is_one_or_two(outermost_level.count_right()) {
                    new_level[(3, 2)] = Tile::Live;
                }
                if !new_level.is_empty() {
                    self.outermost_depth -= 1;
                    self.levels.push_front(new_level);
                }
            }
    
            {
                let innermost_level = &snapshot.levels[snapshot.levels.len() - 1];
                let mut new_level = Level::new();
                if innermost_level[(2, 1)] == Tile::Live {
                    new_level[(0, 0)] = Tile::Live;
                    new_level[(1, 0)] = Tile::Live;
                    new_level[(2, 0)] = Tile::Live;
                    new_level[(3, 0)] = Tile::Live;
                    new_level[(4, 0)] = Tile::Live;
                }
                if innermost_level[(1, 2)] == Tile::Live {
                    new_level[(0, 0)] = Tile::Live;
                    new_level[(0, 1)] = Tile::Live;
                    new_level[(0, 2)] = Tile::Live;
                    new_level[(0, 3)] = Tile::Live;
                    new_level[(0, 4)] = Tile::Live;
                }
                if innermost_level[(3, 1)] == Tile::Live {
                    new_level[(4, 0)] = Tile::Live;
                    new_level[(4, 1)] = Tile::Live;
                    new_level[(4, 2)] = Tile::Live;
                    new_level[(4, 3)] = Tile::Live;
                    new_level[(4, 4)] = Tile::Live;
                }
                if innermost_level[(2, 3)] == Tile::Live {
                    new_level[(0, 4)] = Tile::Live;
                    new_level[(1, 4)] = Tile::Live;
                    new_level[(2, 4)] = Tile::Live;
                    new_level[(3, 4)] = Tile::Live;
                    new_level[(4, 4)] = Tile::Live;
                }
                if !new_level.is_empty() {
                    self.levels.push_back(new_level);
                }
            }
        }
    
        fn count_neighbors(
            level: &Level,
            loc: (u8, u8),
            inner_level: Option<&Level>,
            outer_level: Option<&Level>,
        ) -> u8 {
            let (x, y) = loc;
            let mut result = 0;
            // check left
            if x != 0 {
                if (x, y) == (3, 2) {
                    if let Some(inner_level) = inner_level {
                        result += inner_level.count_right();
                    }
                } else if level[(x - 1, y)] == Tile::Live {
                    result += 1;
                }
            } else if let Some(outer_level) = outer_level {
                if outer_level[(1, 2)] == Tile::Live {
                    result += 1;
                }
            }
            // check up
            if y != 0 {
                if (x, y) == (2, 3) {
                    if let Some(inner_level) = inner_level {
                        result += inner_level.count_bottom();
                    }
                } else if level[(x, y - 1)] == Tile::Live {
                    result += 1;
                }
            } else if let Some(outer_level) = outer_level {
                if outer_level[(2, 1)] == Tile::Live {
                    result += 1;
                }
            }
            // check right
            if x != 4 {
                if (x, y) == (1, 2) {
                    if let Some(inner_level) = inner_level {
                        result += inner_level.count_left();
                    }
                } else if level[(x + 1, y)] == Tile::Live {
                    result += 1;
                }
            } else if let Some(outer_level) = outer_level {
                if outer_level[(3, 2)] == Tile::Live {
                    result += 1;
                }
            }
            // check down
            if y != 4 {
                if (x, y) == (2, 1) {
                    if let Some(inner_level) = inner_level {
                        result += inner_level.count_top();
                    }
                } else if level[(x, y + 1)] == Tile::Live {
                    result += 1;
                }
            } else if let Some(outer_level) = outer_level {
                if outer_level[(2, 3)] == Tile::Live {
                    result += 1;
                }
            }
            result
        }
    
        fn count_live(&self) -> u64 {
            self.levels
                .iter()
                .map(|l| l.tiles.iter().filter(|&&t| t == Tile::Live).count() as u64)
                .sum()
        }
    }
    
    impl fmt::Display for RecursiveWorld {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            for (i, level) in self.levels.iter().enumerate() {
                let depth = i as i32 + self.outermost_depth;
                writeln!(f, "Depth {}:\n{}", depth, level)?;
            }
            Ok(())
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_simple_steps() -> Result<(), Box<dyn Error>> {
            let mut world = SimpleWorld::new_from_string(
                "
                ....#
                #..#.
                #..##
                ..#..
                #....
            ",
            );
            world.step();
            assert_eq!(
                world,
                SimpleWorld::new_from_string(
                    "
                    #..#.
                    ####.
                    ###.#
                    ##.##
                    .##..
                "
                )
            );
            world.step();
            assert_eq!(
                world,
                SimpleWorld::new_from_string(
                    "
                    #####
                    ....#
                    ....#
                    ...#.
                    #.###
                "
                )
            );
            world.step();
            assert_eq!(
                world,
                SimpleWorld::new_from_string(
                    "
                    #....
                    ####.
                    ...##
                    #.##.
                    .##.#
                "
                )
            );
            world.step();
            assert_eq!(
                world,
                SimpleWorld::new_from_string(
                    "
                    ####.
                    ....#
                    ##..#
                    .....
                    ##...
                "
                )
            );
            Ok(())
        }
    
        #[test]
        fn test_simple_loop() -> Result<(), Box<dyn Error>> {
            let mut world = SimpleWorld::new_from_string(
                "
                ....#
                #..#.
                #..##
                ..#..
                #....
            ",
            );
            world.run_until_repeat();
            assert_eq!(
                world,
                SimpleWorld::new_from_string(
                    "
                    .....
                    .....
                    .....
                    #....
                    .#...
                "
                )
            );
            assert_eq!(world.biodiversity_rating(), 2129920);
            Ok(())
        }
    
        #[test]
        fn test_recursive_world() -> Result<(), Box<dyn Error>> {
            let mut rworld = RecursiveWorld::new(Level::new_from_string(
                "
                ....#
                #..#.
                #.?##
                ..#..
                #....
            ",
            ));
            for _ in 0..10 {
                rworld.step();
            }
            assert_eq!(rworld.count_live(), 99);
            Ok(())
        }
    }
    
    1 vote
  20. Comment on Day 18: Many-Worlds Interpretation in ~comp

    Macil
    (edited )
    Link Parent
    Woot, I got my solution improved a lot. Both parts together now run in under half a second. During pathfinding, I now cull the neighbor map down (using Dijkstra's algorithm again, to calculate the...

    Woot, I got my solution improved a lot. Both parts together now run in under half a second. During pathfinding, I now cull the neighbor map down (using Dijkstra's algorithm again, to calculate the new distances between the remaining nodes) as I pick up keys to remove the picked-up keys and opened doors. This means the algorithm no longer explores to and from and between keys and doors that no longer matter. Apparently the algorithm was spending nearly all of its time doing that before. I think this solution is now an efficient algorithm for this kind of problem.

    3 votes