[Question]
  [
In Minecraft, pistons are blocks that can move other blocks. However, they can only move at most twelve blocks, and won't work at all if any obsidian is in the way. Also, slime blocks are sticky and stick to other blocks, except for obsidian.
# Your challenge
Take as input a 5x5 matrix or similar, containing one piston at the bottom, and output the result of the single piston pushing upwards, as 5x6. Input format should have five distinct values for obsidian, slime, other block, piston and empty space, here represented by `O`, `S`,`B`,`P`, and  respectively.
For example, say you start with
```
  B  
  B  
  B  
  B  
  P  
```
Output the result, if the piston was activated, as 5x6:
```
  B  
  B  
  B  
  B  
  P  
     
```
Note that the whole configuration with the piston is moved up, as long as it is legal
# Testcases:
```
BBBBB
BBBBB
SSSSS
  S   
  P  
=> 
BBBBB
BBBBB
SSSSS
  S  
  P  
Because 16 is too many blocks.
     
 SBS 
 SBS 
 SBS 
 SBS 
  P  
=>
  B  
 SBS 
 SBS 
 SBS 
 SPS 
Because slime sticks to blocks, but blocks don't stick to slime.
O    
SSSSS
    S
    S
    P
=>
O    
SSSSS
    S
    S
    P
     
Because the obsidian is in the way, the whole thing can't move.
S S S
     
S S S
     
S P S
=>
     
S S S
     
S S S
  P  
S   S
The stray blocks stay where they are.
SSSSS
B S B
B S B
B S B
B P B
=>
SSSSS
B S B
  S  
B S B
B P B
B   B
The slime pulls the first two blocks, but the rest stay in place. 
 OSO 
 OSO 
 OSO 
 OSO 
 OPO 
=>
  S  
 OSO 
 OSO 
 OSO 
 OPO 
 O O 
Slime doesn't stick to obsidian, so it's free to move. 
```
# Scoring
This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"), the shorter the better!
  
[Answer]
# [J](http://jsoftware.com/), 121 98 bytes
Takes in  `POSB` as digits `01234`. Probably some microgolfs left, these long tacit definitions tend to get confusing. :-)
```
((0,~*)+0,(*-.))[(]*(2 e.*&,)<14>+/@,@])[(]*@+(2&<@[*[:+/((,-)=i.2)t]*3=[)+*@[*1(t=:|.!.0)])^:_=&1
```
[Try it online!](https://tio.run/##bY4xb8MgEIV3fsW1A3AYE9vtREJlUalTJVAZkZvBSuJ06ZKx6l93z0mrSgkn8XHi3jvex3xvxB6cBQEaGrB0agPPb68vs5SN/lZYNVqq2iBmOSjZwc4ornHTPj5Vq173w/m9r2THN31W2VYrKXWN7mg6PA3qwWWsFE1aeXL2y9yZBgd8t1vH2xkZG50VEEPygh8NO1CSPTcjm6jJFqQAwfX6gJDXZtsxthunT5hAnqMi8wDgWSImlpZiIcQQGN4ql/rlRUke8tEVibcGSD5BiRAL6gDLrr/FS5x/lvSJPr9M4aqP1Bf0582elP6KkVhIH1KAIiMR5x8 "J – Try It Online")
### How it works
* `=&1` Start a will-be-pushed bitmap with the piston
* `^:_` expand the bitmap until it does not expand further:
* `1(t=:|.!.0)]` Push the bitmap upwards (non-wrapping, thus `!.0`)
* `*@[*` but only if a non-space was pushed.
* `t]*3=[` Extend push of the will-be-pushed slimes towards
* `((,-)=i.2)` all 4 direction
* `2&<@[*[:+` but only push slimes and blocks.
* `]*@+…+` OR old bitmap, slime pushes, and up pushes for the next iteration.
* `[(]*…)` Set the final bitmap to 0 if …
* `(2 e.#~&,)` There is an obsidian block in it
* `<14>+/@,@]` or the sum (including the piston) is not less 14.
* `0,(*-.)` Original map \* not-pushed bitmap and an empty row above.
* `(0,~*)` Original map \* pushed bitmap and an empty row under.
* `+` Add both maps – as empty spaces are 0 they don't overlap.
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), ~~144~~ 120 bytes
```
ＷＳ⊞υι≔⪫υ¶θＰθ…θ⌕θP≔⟦⟦ⅈⅉ⟧⟧ηＦη«Ｊ§ι⁰⊖§ι¹Ｆ‹@ＫＫ⊞η⟦ⅈⅉ⟧↓Ｆ⁼SＫＫＦ³«Ｍ✳⊗⁺²λＦ№⪪BS¹ＫＫ⊞η⟦ⅈⅉ⟧Ｍ✳⊗λ» »Ｆη«Ｊ§ι⁰⁻§ι¹›‹Ｌη¹⁴⊙η⁼O§§υ§ι¹§ι⁰§§υ⊟ι⊟ι
```
[Try it online!](https://tio.run/##hVLPT8IwGD2zv6Lp6WtSE1BPcpExNRAIS@ZBgxxwVNZY2rG1IDH87bNdmUCioYe234/3@r6Xptm8SNVcVNU244IhGMjc6EQXXC6BEBSbMgNDESfdoFeWfClhqLh0KfwmMaFobStjIzTPLUaDC@P61t@lgvUzlcOaokcuF@7EMSbkyDWdvoDleAUym1GU2cKHKhBkBH0HraFZ5c8KenogF@wLOEVt2xuxtGArJjVbnJY6NW2rho9YWQK@xxTFjH3aMQ5zZBQd33PdY7VhcBeprfzFPqzNXFh0coquKze1KI@JeMFSzZWESJl3YaXEwpRwTZEgXohn6ytjnUhywTXg0HF2yEVV/z0hPPM@aHmDMcI23l@2bMylFXdmFkVPBZtrVnizRkwutZVi851bu/XkzulqzJhY4Q28Oc0x5e0/i9vEr24j9g94bL8Gd7jDxQ1TVYlbQThJJuH5jmIUBtXVRvwA "Charcoal – Try It Online") Link is to verbose version of code. Explanation:
```
ＷＳ⊞υι
```
Input the layout. (I've decided to take input as newline-terminated lines; this allows you to experiment with layouts of different numbers of lines if you so wish.)
```
≔⪫υ¶θＰθ…θ⌕θP
```
Output the layout and move the cursor to the position of the piston.
```
≔⟦⟦ⅈⅉ⟧⟧ηＦη«
```
Perform a breadth-first search starting with the position of the piston.
```
Ｊ§ι⁰⊖§ι¹
```
Jump to the square above the current position.
```
Ｆ‹@ＫＫ⊞η⟦ⅈⅉ⟧
```
Add this position to the list unless it's empty.
```
↓Ｆ⁼SＫＫＦ³«
```
If the current position is a sticky block, then check the other three sides too.
```
Ｍ✳⊗⁺²λＦ№⪪BS¹ＫＫ⊞η⟦ⅈⅉ⟧Ｍ✳⊗λ
```
If there is a (sticky) block on that side then add this position to the list.
```
» »
```
Erase the block, assuming it will be moved. This also avoids considering the same block twice.
```
Ｆη«
```
Loop over each block.
```
Ｊ§ι⁰⁻§ι¹›‹Ｌη¹⁴⊙η⁼O§§υ§ι¹§ι⁰
```
Jump to its old or new position depending on whether there weren't too many blocks to push or any obsidian in the way.
```
§§υ⊟ι⊟ι
```
Print the block.
]