Hacker Timesnew | past | comments | ask | show | jobs | submitlogin

Nice

I see the final result here has a left bias, just press and hold to see it stacking towards left before right. So the next fix could be a random choice.



It does indeed! It's a great note. As you say, randomly choosing between left and right if they both exist would fix the bias.

Another issue is that it forms a perfect diagonal, which isn't the most pleasing to look at. It can be addressed by randomly allowing for stacks of two granules and allowing spreading further than just one position to the left or right.

I figured where I left it was appropriate for this post, but I'm happy to write follow-ups for other particle types and the kind of improvement you mentioned!


Choosing between left and right if they're both open fixes the bias when there is only a single pixel falling, but not when a circle is used. This is because the update function runs from right to left across the screen, so the falling pillar of sand behaves like a solid wall as the update has not had the chance to move the other side of the circle yet. The best example of this is dropping a pillar on the right slope of an existing cone, a solid line will form on the right side of the falling segment, but a slope will form against the left side of the pillar. When you try this with the left side of a cone, the behavior is correct.

One fix I found is relatively straightforward:

        class FallingGrid2 extends FallingGrid {
            // Draw from the end of the list to the beginning
            update() {
    +          const direction = Math.random() > 0.5 ? 'ltr' : 'rtl';
               for (let i = this.grid.length - this.width - 1; i > 0; i--) {
    -            this.updatePixel(i);
    +            if (direction === 'rtl') {
    +              this.updatePixel(i);
    +            } else {
    +              const offset = i % this.width;
    +              const flipped = this.width - offset;
    +              this.updatePixel(i - offset + flipped);
    +            }
              }
            }
          }
Cheers for making the source so easily hackable :)


Interesting, I solved it differently, by just randomly selecting the order in which belowLeft and belowRight are checked, inside the updatePixel function:

    updatePixel(i) {
        const below = i + this.width;
        const belowLeft = below - 1;
        const belowRight = below + 1;
        // flag to indicate if we checked the left side already
        let checkedLeft = false;

        if (this.isEmpty(below)) {
            swap(i, below);
        // check the left side first with 50 % prob (ltr)
        } else if (Math.random() <= 0.5 && this.isEmpty(belowLeft)) {
            swap(i, belowLeft);
            // indicate we checked the left side already
            checkedLeft = true;
        // check the right side first with 50 % prob
        } else if (this.isEmpty(belowRight)) {
            swap(i, belowRight);
        // if we haven't checked the left side earlier, check it now (rtl)
        } else if (!checkedLeft && window.sandgrid[belowLeft] == 1)
            swap(i, belowLeft);
    }


I may be testing wrong, but I believe that approach causes dropping a steady stream of sand on the right side of a cone to produce very strange falling behaviour as compared to the left, because the stream is more than one pixel wide. Looks like you changed some other bits around so you may have addressed this some other way.


Whoops I just noticed a copy-paste error I did, which might be the reason for what you describe in case you copied my code above

3rd last line the "window.sandgrid[belowLeft] == 1" needs to be "this.isEmpty(belowLeft)" instead

Other than that I'm not sure what would be causing the weird behaviour. I use this and it seems to work just fine.


Ah! I see now! Yes, we should alternate the direction of the rowwise pass, or pick randomly, as you demonstrate. This would be a great step to start a next post with.


Also wraps around if the pile gets too big to fit in the frame.


It's true! This has to do with how I wrote the "belowLeft" and "belowRight" variables. I don't ensure they are in the same row. You can do this by taking a pair of indices, dividing by width and flooring and checking for equality.


I also observe this bias, I imagine it is an artifact of > versus >= on the pixel boundary.


It's because the left pixel is checked before the right pixel (the settling behaviour section). This is common in games where you handle movement input (left, right arrow keys) - if you press both of them you'll always go one way, because that way is checked first for movement.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: