Our sandpiles are based on a 3 by 3 rectangular grid giving nine areas that
contain a number from 0 to 3 inclusive. (The numbers are said to represent
grains of sand in each area of the sandpile).

E.g. s1 =

    
    1 2 0
    2 1 1
    0 1 3

and s2 =

    2 1 3
    1 0 1
    0 1 0

Addition on sandpiles is done by adding numbers in corresponding grid areas,
so for the above:

              1 2 0     2 1 3     3 3 3
    s1 + s2 = 2 1 1  +  1 0 1  =  3 1 2
              0 1 3     0 1 0     0 2 3

If the addition would result in more than 3 "grains of sand" in any area then
those areas cause the whole sandpile to become "unstable" and the sandpile
areas are "toppled" in an "avalanche" until the "stable" result is obtained.

Any unstable area (with a number >= 4), is "toppled" by loosing one grain of
sand to each of its four horizontal or vertical neighbours. Grains are lost
at the edge of the grid, but otherwise increase the number in neighbouring
cells by one, whilst decreasing the count in the toppled cell by four in each
toppling.

A toppling may give an adjacent area more than four grains of sand leading to
a chain of topplings called an "avalanche".
E.g.

    
    4 3 3     0 4 3     1 0 4     1 1 0     2 1 0
    3 1 2 ==> 4 1 2 ==> 4 2 2 ==> 4 2 3 ==> 0 3 3
    0 2 3     0 2 3     0 2 3     0 2 3     1 2 3

The final result is the stable sandpile on the right.

Note: The order in which cells are toppled does not affect the final result.

Task

 Create a class or datastructure and functions to represent and operate on sandpiles. 
 Confirm the result of the avalanche of topplings shown above
 Confirm that s1 + s2 == s2 + s1 # Show the stable results
 If s3 is the sandpile with number 3 in every grid area, and s3_id is the following sandpile:
    2 1 2  
    1 0 1  
    2 1 2

 Show that s3 + s3_id == s3
 Show that s3_id + s3_id == s3_id


Show confirming output here, with your examples.



References

 https://www.youtube.com/watch?v=1MtEUErz7Gg
 https://en.wikipedia.org/wiki/Abelian_sandpile_model


/*REXX program demonstrates a 3x3 sandpile model by addition with toppling & avalanches.*/
@.= 0;   size= 3                                 /*assign 0 to all grid cells; grid size*/
call init   1,      1 2 0    2 1 1    0 1 3      /*   "   grains of sand──► sandpile 1. */
call init   2,      2 1 3    1 0 1    0 1 0      /*   "      "    "   "  "     "     2  */
call init   3,      3 3 3    3 3 3    3 3 3      /*   "      "    "   "  "     "     3  */
call init 's3_id',  2 1 2    1 0 1    2 1 2      /*   "      "    "   "  "     "    3_id*/
call show   1                                    /*display sandpile  1  to the terminal.*/
call show   2                                    /*   "        "     2   "  "      "    */
call  add   1, 2, 'sum1',           'adding sandpile  s1  and  s2  yields:'
call show 'sum1'
call  add   2, 1, 'sum2',           'adding sandpile  s2  and  s1  yields:'
call show 'sum2'
call  eq? 'sum1', 'sum2'                         /*is  sum1  the same as  sum2 ?        */
call show   3
call show 's3_id'
call  add   3,     's3_id', 'sum3', 'adding sandpile  s3  and  s3_id  yields:'
call show 'sum3'
call  add 's3_id', 's3_id', 'sum4', 'adding sandpile  s3_id  and  s3_id  yields:'
call show 'sum4'
call  eq? 'sum4',  's3_id'                       /*is  sum4  the same as  s3_id ?       */
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
@get: procedure expose @.; parse arg grid,r,c    ;                      return @.grid.r.c
@set: procedure expose @.; parse arg grid,r,c,val;  @.grid.r.c= val;    return
tran: procedure; parse arg a;  if datatype(a,'W')  then a='s'a;         return a
/*──────────────────────────────────────────────────────────────────────────────────────*/
add:  parse arg x, y, t;    if t==''  then t= 'sum';  xx= tran(x);  yy= tran(y)
        do r=1  for size;   do c=1  for size;   @.t.r.c= @.xx.r.c  +  @.yy.r.c
                            end   /*c*/
        end   /*r*/;    say arg(4);             call norm t;            return
/*──────────────────────────────────────────────────────────────────────────────────────*/
eq?:  parse arg x, y;   xx= tran(x);      yy= tran(y);      ?= 1
        do r=1  for size;         do c=1  for size;         ?= ?  &  (@.xx.r.c==@.yy.r.c)
                                  end   /*c*/
        end   /*r*/
      if ?  then say  'comparison of '      xx       " and "       yy':  same.'
            else say  'comparison of '      xx       " and "       yy':  not the same.'
      return
/*──────────────────────────────────────────────────────────────────────────────────────*/
init: parse arg x, $;   xx= tran(x);   #= 0;      pad= left('', 8);   ind= left('', 45)
        do r=1  for size;   do c=1  for size;     #= # + 1;           @.xx.r.c= word($, #)
                            end   /*c*/
        end   /*r*/;                              shows= 0;             return
/*──────────────────────────────────────────────────────────────────────────────────────*/
norm: procedure expose @. size; parse arg x;        xx= tran(x);            recurse= 0
        do r=1  for size;     do c=1  for size;     if @.xx.r.c<=size  then iterate
                              recurse= 1;              @.xx.r.c= @.xx.r.c - 4
                              call @set  xx, r-1, c  , @get(xx, r-1, c  )   +   1
                              call @set  xx, r+1, c  , @get(xx, r+1, c  )   +   1
                              call @set  xx, r  , c-1, @get(xx, r  , c-1)   +   1
                              call @set  xx, r  , c+1, @get(xx, r  , c+1)   +   1
                              end   /*c*/
        end   /*r*/;    if recurse  then call norm xx;                  return
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: parse arg x;  xx= tran(x);  say ind center("sandpile" xx,25,'─')  /*show the title*/
        do r=1  for size;  $=;      do c=1  for size;   $= $  @.xx.r.c  /*build a row.  */
                                    end   /*c*/
        say ind pad $                                                   /*display a row.*/
        end   /*r*/;     shows= shows + 1;     if shows==1  then say;   return