Author: spidurmelon

  • Rubber Duck Debugging via Blog

    Rubber duck debugging never worked for me, at least not when using actual rubber ducks or other inanimate objects.

    Turns out this blog helps wonderfully, because the added pressure of someone reading this someday makes me articulate my ideas better and therefore develop a better understanding myself.

  • Grid Islands

    I am running into a performance issue in my new game, so instead of optimizing it directly I thought it’d be fun to write about it.

    Imagine you have a grid of cells, which can either be filled ([X]) or empty ([ ]):

       A  B  C  D  E  F  G
    1 [X][ ][X][X][ ][X][X]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]

    This grid has 3 “islands”. An island is defined as a set of cells all filled, separated by empty cells. I need a way for each cell to know what island they are a part off.

    My original implementation was a recursive approach, effectively flood-filling from whichever cell queried their island. The problem here is that although this isn’t super slow, for a grid of 7*7, =I’d have to do this 49 times. And in the worst case scenario – a grid completely filled – this would result in visiting 49*49=2401 cells.

    The first idea is to calculate the islands once for the whole grid, instead of doing it for every cell. This should definitely give me the speed up I desire, as I would only have to visit 98 cells in the worst case scenario. It would do this as follows:

    It would go over every cell in reading order, starting with A1:

       A  B  C  D  E  F  G
    1 [O][ ][X][X][ ][X][X]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]

    It would use the same flood-fill algorithm to find the whole island.

       A  B  C  D  E  F  G
    1 [O][ ][X][X][ ][X][X]
    2 [O][ ][X][X][ ][X][X]
    3 [O][ ][ ][ ][ ][ ][X]
    4 [O][O][O][ ][ ][ ][X]
    5 [O][O][O][ ][X][X][X]
    6 [O][O][O][ ][X][X][X]

    It would then continue in reading order from A1, skipping B1 because it’s already empty. It would also skip any cells that are already part of an island. So secondly it would land on C1, and flood-fill that one.

       A  B  C  D  E  F  G
    1 [O][ ][O][O][ ][X][X]
    2 [O][ ][O][O][ ][X][X]
    3 [O][ ][ ][ ][ ][ ][X]
    4 [O][O][O][ ][ ][ ][X]
    5 [O][O][O][ ][X][X][X]
    6 [O][O][O][ ][X][X][X]

    And finally get to F1.

       A  B  C  D  E  F  G
    1 [O][ ][O][O][ ][O][O]
    2 [O][ ][O][O][ ][O][O]
    3 [O][ ][ ][ ][ ][ ][O]
    4 [O][O][O][ ][ ][ ][O]
    5 [O][O][O][ ][O][O][O]
    6 [O][O][O][ ][O][O][O]

    It would continue over the entire grid finding that no other cells are island-less, and terminate there.

    But I am curious if there is an algorithm that would intelligently and dynamically update the islands. Visiting no other cells than the one changed. Like what would happen if we were to fill D6?

       A  B  C  D  E  F  G
    1 [O][ ][O][O][ ][O][O]
    2 [O][ ][O][O][ ][O][O]
    3 [O][ ][ ][ ][ ][ ][O]
    4 [O][O][O][ ][ ][ ][O]
    5 [O][O][O][ ][O][O][O]
    6 [O][O][O][X][O][O][O]

    I feel like it should be possible to directly update join the left island with the right, without having to go over each and every cell. I do not have a solution for this at present time, but maybe I’ll come back here if I ever stumble across it.

    EDIT: Turns out using recursion is just not the way to go, it is still rather slow. Instead I’m opting for the following iterative approach:

    • Go over every cell,
      • If the cell has a neighbour with an “island” value, set the value of this cell to that value.
      • If the cell has no neighbour with an “island” value, set it to highest value +1

    In practice (skipping empty cells):

       A  B  C  D  E  F  G
    1 [1][ ][X][X][ ][X][X]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]
    
       A  B  C  D  E  F  G
    1 [1][ ][2][X][ ][X][X]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]
    
       A  B  C  D  E  F  G
    1 [1][ ][2][2][ ][X][X]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]
    
       A  B  C  D  E  F  G
    1 [1][ ][2][2][ ][3][X]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]
    
       A  B  C  D  E  F  G
    1 [1][ ][2][2][ ][3][3]
    2 [X][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]
    
       A  B  C  D  E  F  G
    1 [1][ ][2][2][ ][3][3]
    2 [1][ ][X][X][ ][X][X]
    3 [X][ ][ ][ ][ ][ ][X]
    4 [X][X][X][ ][ ][ ][X]
    5 [X][X][X][ ][X][X][X]
    6 [X][X][X][ ][X][X][X]
    
    etc...
    
       A  B  C  D  E  F  G
    1 [1][ ][2][2][ ][3][3]
    2 [1][ ][2][2][ ][3][3]
    3 [1][ ][ ][ ][ ][ ][3]
    4 [1][1][1][ ][ ][ ][3]
    5 [1][1][1][ ][3][3][3]
    6 [1][1][1][ ][3][3][3]

    EDIT 2: I’ve noticed a problem with this approach. Imagine the following scenario:

       A  B  C 
    1 [X][ ][X]
    2 [X][ ][X]
    3 [X][X][X]

    How should this be resolved? Because they’re part of the same island, but following the steps as described before will result in the following:

       A  B  C 
    1 [1][ ][2]
    2 [1][ ][2]
    3 [1][1][?]

    So we need to revise the rules based on the amount of neighbours already assigned to an island:

    • 0 neighbours with a value: Set to MAX + 1
    • 1 neighbour with a value, or 2 neighbours with the same value: Set to neighbours value
    • 2 neighbours with a unique value: Pick one of the two values, then replace all instances of the other value with the picked value

    EDIT 3: It seems this last approach has its flaws too, so I’m reverting back to approach number 2.

  • Choice Paralysis


    Choosing is hard

    What to write about first?

    What about the game I’m currently making?

    Maybe the struggles with my autism?

    That one cool procedural generation algorithm I saw a while back?

    Maybe the duality of wanting people to see my creations… but also please don’t observe anything I make.

    Have you ever felt unsure about whether to rate something a 6/10, a 7/10 or an 8/10? It’s hard because there is no correct answer, there’s many ways to reason about it and there’s just too many numbers.

    The more options you give a brain, the harder it gets to pick between them. In the case of ratings we only have 10 options. What happens if we have to pick between 20, 100 or even 1000?

    That’s the problem I am running into with my Steam library right now. Approaching 500 games, it becomes a huge task to make a choice each time I open Steam.

    In fact my brain refuses to consider all of them and only ever considers my most recent purchases. It does this fully automatically. To pick something older I need to force myself, even if it’s one of my favorite games of all time.

    This trait of my brain reduces the choices to 10-20, but just like with rating something, this is not enough to make a choice. I need some way of reducing the choices further, and the way I’m going to do this is inspired by Steam’s review system.

    Steam’s review system is purely a binary choice: Did I like this? Yes/No. Most games in my library easily fall into one of those 2 categories. It loses some nuance but it makes the choice incredibly simple. If you want to add nuance you can add some text to your review.

    So what if I could apply this to every choice in life? Not every choice has just 2 options, but if the options can be split up into groups, it can be reduced to a series of yes/no questions. My brain already does this subconsciously for rating something:

    So my current way of trying to combat choice paralysis is trying to split every choice into a series of yes/no questions. For games this isn’t too hard, but for more difficult open-ended questions this is a lot tougher.