We are going to bring down an imaginary building with a series of explosions. Our building is a 5x5 matrix of integers ranging 1-9, each representing a single brick.

The challenge is to set of a series of explosions to bring down our building as much as we can, why not! Each brick that is connected (horizontally, vertically, diagonally) to a copy of itself will represent a payload that is going to explode in the next detonation. The bricks above those that are blown to bits will fall down. We will continue these steps untill we cannot demolish the building any further:

Look at the 'building' to find connected copies of integers for an explosion, set of the explosion to let bricks above the explosion fall down to create a new array of digits. If an explosion occured make sure to backfill your 'building' with a zero to represent rising smoke from the ashes. Though, extra imaginary digital kudos if you used actual clouds, e.g:

enter image description here > enter image description here > enter image description here > enter image description here

Your final output, the result of this challenge, should represent the ruins after the last possible detonation with the aforementioned smoke rising from the ashes.

In the case of no possible explosions, sadly output == input. In the occurence of a single big explosion (or multiple explosions for that matter) to bring the whole building down, the result is simply an array of clouds.


enter image description here > enter image description here

enter image description here > enter image description here

enter image description here > enter image description here

enter image description here > enter image description here

enter image description here > enter image description here

  • 2
    \$\begingroup\$ Could you clarify what's the input and what's the output? Do you need to generate the building yourself? Is it required to output it? What is the required distribution? Do you choose the size? \$\endgroup\$ Commented Mar 8, 2023 at 12:35
  • 1
    \$\begingroup\$ Please just give a building as input, don't ask 2 separate challenges at once. Then also provide test cases \$\endgroup\$
    – mousetail
    Commented Mar 8, 2023 at 13:15
  • 7
    \$\begingroup\$ Rewritten the task to take a matrix of integers (5x5) and take out the part where I'd ask the user to take a randomized input as part of the challenge. Hopefully the reworked question is more straightforward and enjoyable. \$\endgroup\$
    – JvdV
    Commented Mar 8, 2023 at 13:39
  • 1
    \$\begingroup\$ May we use something else in place of the cloud, such as 0? \$\endgroup\$
    – chunes
    Commented Mar 8, 2023 at 13:51
  • 1
    \$\begingroup\$ @LuisMendo, thanks, and thanks for your answer too. It actually wasn't inspired by that game, but looking at it, that could be a nice sequel \$\endgroup\$
    – JvdV
    Commented Mar 8, 2023 at 18:13

8 Answers 8


MATL, 31 bytes


Input and output are 2D numerical arrays, with 0 for smoke in the output.

Try it online! Or verify all test cases.

How it works

You can see a convolution coming, can't you?

12:"     % For each n in [1 2 ... 12]. 12 is a bound on the number of explosions
  9:"    %   For each k in [1 2 ... 9]
    t    %     Duplicate the numerical 2D array (A). Triggers implicit input the
          %    first time
    @=   %     Compare element-wise with k. Gives a zero-one 2D array (B)
    t    %     Duplicate
    3Y6  %     Push [1 1 1; 1 0 1; 1 1 1]. This defines the neighbourhood
    Z+   %     2D convolution, maintaining size. This counts, for each entry in
         %     (B), how many ones there are in its neighbourhood
    *    %     Multiply, element-wise. This counts, only for each entry in (B)
         %     equal to one, how many ones it has around
    ~    %     Negate. This gives zero for entries that are going to disappear,
         %     one otherwise
    *    %     Multiply by (A), element-wise. This sets the required entries to
         %     zero 
  ]      %   End
  tg     %   Duplicate. Convert to logical: non-zero values become one
  &S     %   Sort each column, and output the indices that correspond to the
         %   sorting. Gives a 2D array containing a permutation of [1 2 ... 5]
         %   in each column (C). This sorting corresponds to sending the zeros
         %   to the top of each column
  5:     %   Push [1 2 ... 5]
  q5*    %   Subtract 1, multiply element-wise by 5. Gives [0 5 10 15 20]
  +      %   Add to (C) with implicit expansion. This gives the linear indices 
         %   that will send zeros to the top of each column
  )      %   Reference indexing
         % End (implicit)
         % Display (implicit)
  • 2
    \$\begingroup\$ 3Y6Z+, what a surprise. \$\endgroup\$
    – Giuseppe
    Commented Mar 8, 2023 at 19:19

05AB1E, 52 48 bytes


Uses 0 for smoke.

Try it online or verify all test cases.


Δ               # Loop until the result no longer changes:
 9E             #  Loop `N` in the range [1,9]:
   D            #   Duplicate the current matrix
                #   (which will be the implicit input-matrix in the first iteration)
    NQ          #   Check which values are equal to `N` in the copy
      ©         #   Store this matrix of 1s/0s in variable `®` (without popping)
       ˜        #   Flatten it to a list
        ƶ       #   Multiply each value by its 1-based index
         5ä     #   Convert the list back to a 5x5 matrix
   Δ            #   Loop until it no longer changes to flood-fill the positive values:
    2Fø0δ.ø}    #    Add a border of 0s around the matrix:
    2F     }    #     Loop 2 times:
      ø         #      Zip/transpose; swapping rows/columns
        δ       #      Map over each row:
       0 .ø     #       Add a leading/trailing 0
    2Fø€ü3}     #    Convert it into overlapping 3x3 blocks: 
    2F    }     #     Loop 2 times again:
      ø         #      Zip/transpose; swapping rows/columns
       €        #      Map over each inner list:
        ü3      #       Convert it to a list of overlapping triplets
    ®*          #    Multiply each 3x3 block by the value in matrix `®`
                #    (so the 0s remain 0s)
    €€à         #    Keep the largest value from each 3x3 block:
    €€          #     Nested map over each 3x3 block:
      à         #       Pop and push its flattened maximum
   }            #   Close the flood-fill loop
    Иs¢2@*_*   #   Remove all islands of 2 or more cells from the matrix:
    Ð           #    Triplicate the resulting matrix of 0s and positive islands
     ˜          #    Flatten the top copy
      s         #    Swap so the other copy of the matrix is at the top
       ¢        #    For each value in the matrix, count its occurrences in the list
        2@      #    Check for each count whether it's >= 2
          *     #    Multiply these checks to the matrix of 0s and positive islands
           _    #    Check for each value whether it's equal to 0
                #    (the falsey results are the islands we'll remove)
            *   #    Multiply it to the values of the matrix to 'explode' the islands
  }             #  Close the [1,9]-loop
   ø0δ†ø        #  Collapse the bricks of the building down after the explosions:
   ø            #   Zip the matrix; swapping rows/columns
     δ          #   Map over each inner list (each column):
    0 †         #    Filter all 0s to the front
       ø        #   Zip/transpose it back
                # (after which the result is output implicitly)

JavaScript (ES7), 167 bytes

Expects a matrix of integers. Returns another matrix of integers, with 0 for smoke.


Try it online!


We use the same recursive function to process two distinct steps:

  1. Fill an array of strings representing the updated columns of the matrix without removed cells. Then convert this array back to a matrix.
  2. Test whether a given cell should be removed.

Step 1

The variable \$V\$ is initially set to an empty array and the variables \$X\$ and \$Y\$ are undefined.

For each cell at \$(x,y)\$ with value \$v\$, we insert the result of the second step at the beginning of \$V[x]\$ (coerced to an empty string if it's still undefined):

V[x] = f(m, v, x, y) + [V[x]]

Once all cells have been processed, we transpose the strings stored in \$V\$ to build the new matrix with each column filled with leading 0's wherever needed:

V.map((s, j) =>
  m.map((r, i) => r[j] = ~~s[4 - i])

If the matrix is unchanged, we stop and return it. Otherwise, we do a recursive call at step 1.

Step 2

The variable \$V\$ is set to the value of the cell being tested and the variables \$X\$ and \$Y\$ hold its coordinates.

We walk through each cell at \$(x,y)\$ and set \$V\$ to an empty string as soon as a neighbor cell with the same value \$v\$ is detected. A neighbor cell is a cell whose squared Euclidean distance with the reference cell is either \$1\$ or \$2\$. This is tested with a bitmask:

6 >> (X - x) ** 2 + (Y - y) ** 2 & V == v

NB: Because the matrix is \$5\times5\$, the maximum squared Euclidean distance is \$32\$, in which case the shift amount will wrap to \$0\$. Fortunately, this will still work as expected.

Once all cells have been processed, we simply return \$V\$.


Charcoal, 55 bytes


Try it online! Link is to verbose version of code. Explanation:


Copy the input to the canvas.


Repeat until two iterations produce the same result.


Save the result of the previous iteration.


Start collecting lonely bricks for this iteration.


Loop over the columns from left to right and the rows from bottom to top.


If this brick is lonely then collect it.


Clear the canvas.


Output a background of clouds, leaving the cursor at the bottom left.


Output the lonely bricks from left to right and bottom to top.


Julia 1.8, 144 bytes

!o=(k=keys(o)).|>_->(o.=[(2>count(o[i].==get.([o],i-k[7].+k[1:3,1:3],0)))o[i] for i=k];[o[j,i]<1&&(o[j-1:j,i]=o[[j,j-1],i]) for i=1:5,j=5:-1:2])

Try it online!

mutates the input. uses zeros for ashes


Python3, 507 bytes:

def S(b):
 while q:
  while Q:
   for X,Y in[(1,0),(0,1),(0,-1),(-1,0),(-1,-1),(1,-1),(-1,1),(1,1)]:
    if C in b and b[C]==b[(x,y)] and C not in R:Q+=[C];F=0;R+=[C];q.remove(C)
   if F and len(R)>1:yield R
def f(b):
 while 1:
  for i in S({(x,y):v for x,r in E(b)for y,v in E(r)if v}):
   for x,y in i:b[x][y]=0
  if F:return b
  b=[*map(list,zip(*[[j for j in i if 0==j]+[*filter(None,i)]for i in zip(*b)]))]

Try it online!


Jelly, 27 24 bytes


Try it online!

-2 with ¬¬$ -> , -1 with dyadic chaining via extreme chain separator abuse

The neighborhood logic feels a bit... unhinged, but I'm not mentally present enough to devise or steal anything better at the moment.

      )                     For each row:
ŻṖ                          Prepend 0 and remove the last element,
  ;"                        concatenate with corresponding original elements,
    ⁺                       and do the same
     Ḋ                      with the first element removed.
       Z                    Transpose,
        ð⁺                  and do it again.
          ċ""               How many times is the middle in each neighborhood?
             Ị×             Zero out elements with counts greater than 1.
               Z  €Z        For each column,
                 Þ          sort its elements by
                Ṡ           their signs.
                    µ$ƬṪ    Repeat all of that while unique.

Python 3.8, 231 227 bytes

def f(a,X=range(5)):
 while(b:=[[(i:=a[r][c])-i*any(all([r-2<R<r+2,c-2<C<c+2,(r,c)!=(R,C),a[R][C]==i])for R in X for C in X)for c in X]for r in X])!=a:a=[[([0]*5+[R[c]for R in b if R[c]])[-5:][r]for c in X]for r in X]
 return a

Try it online!

Ungolfed and commented

def f(a):
 for _ in range(25):a=h(g(a))
 return a

# Replace connected integers with 0s
def g(a):
 b=[[*i]for i in a]
 for r in range(5):
  for c in range(5):
   if I(a,r,c,i):b[r][c]=0
 return b

# Check surrounding cells
def I(a,r,c,i):
 for R in range(5):
  for C in range(5):
   if r-2<R<r+2and c-2<C<c+2and(r,c)!=(R,C)and a[R][C]==i:return 1
 return 0

# Drop numbers down based on positions of 0
def h(a):
 b=[[0]*5for i in a]
 for c in range(5):
  C=([0]*5+[R[c]for R in a if R[c]])[-5:]
  for r in range(5):b[r][c]=C[r]
 return b

Try it online!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.