Prédire les chutes de pierres

18

Dans ce défi, vous obtenez une carte d'un terrain à deux dimensions, vu de côté. Malheureusement, certaines parties du terrain flottent dans l'air, ce qui signifie qu'elles vont s'écraser. Votre travail consiste à prédire où ils atterriront.

L'entrée

Votre entrée est une ou plusieurs chaînes séparées par des retours à la ligne de longueurs égales, contenant uniquement les caractères #(un signe numérique, signifiant un rocher) ou .(un point, signifiant un espace vide).

Le résultat

Votre sortie a le même format que l'entrée, mais avec la modification suivante. Voyons la chaîne d'entrée comme une grille de roches à deux dimensions. Chaque roche dans l'entrée qui est reliée au bas de la grille par un chemin de roches adjacentes est ferme ; d'autres roches sont lâches . Les roches adjacentes en diagonale ne sont pas considérées comme adjacentes. Toutes les roches lâches tomberont directement vers le bas et finiront comme une pile au-dessus d'une roche ferme ou de la rangée inférieure. Les roches lâches ne sont pas attachées les unes aux autres, elles tombent donc individuellement, pas comme de grosses formations. La sortie est la grille résultante.

Exemples

  • L'entrée

    ..###.
    .##.#.
    .#....
    .##.#.
    

    ne contient pas de roches en vrac, donc la sortie est identique à celle-ci.

  • L'entrée

    ...#..
    .#..#.
    .#..##
    .#...#
    .#####
    .#...#
    

    contient une roche meuble en haut, qui tombe sur la roche ferme en dessous. La sortie est

    ......
    .#..#.
    .#..##
    .#.#.#
    .#####
    .#...#
    
  • L'entrée

    .#####....
    .#....####
    ###.###..#
    #.#...##..
    .####..#.#
    ......###.
    ..#...#..#
    ..#...#..#
    

    a un grand groupe de roches lâches sur la gauche. Le groupe tombe en panne lorsque les rochers tombent, donc la sortie est

    ..........
    ....######
    ..#.###..#
    . #...##..
    .##....#..
    .##...####
    ####..#..#
    #####.#..#
    

Clarifications

  • Vous pouvez soit prendre l'entrée de STDIN et la sortie vers STDOUT, soit écrire une fonction.
  • C'est le code-golf, donc le programme le plus court (en octets) est le gagnant.
  • Les failles standard ne sont pas autorisées.
Zgarb
la source

Réponses:

12

CJam, 180 ... 133 101 ... 94 90 87 octets

qN/~'#/S*_,):L;]N*_,_,*{:A1$='#={[W1LL~)]Af+{W>},1$f=S&,{ASct}*}*}/N/z{S/{$W%}%'#*}%zN*

Il y a certainement beaucoup de golf possible, mais je voulais le poster d'abord après l'avoir fait fonctionner complètement.

Regardez ma! Pas de barres de défilement!

Reprend la grille des roches (composée .ou #non d'une nouvelle ligne de fin) de STDIN et imprime la sortie dans STDOUT

MISE À JOUR : Utilisation d'un remblayage partiel inefficace mais plus court pour déterminer les roches fermes.

MISE À JOUR 2 : Modification de l'algorithme pour faire tomber les roches. Beaucoup plus court maintenant!

MISE À JOUR 3 : A fait plusieurs petites optimisations et à la fin j'ai pu ramener le nombre d'octets à la moitié du code d'origine!

Comment ça marche :

qN/~'#/S*_,):L;]N*             "Preparations";
qN/~                           "Read the input, split by new line and expand the array";
    '#/S*                      "In the last row, replace # by space";
         _,):L                 "Copy the last row and store length + 1 in L";
              ;]N*             "Pop the length, wrap everything in array and join by \n";

_,_,*{ ... }/                  "Flood fill";
_,                             "Copy the array and calculate its length";
  _,                           "Copy the length and calculate [0 - length] array";
    *                          "Repeat the above array, length times";
     { ... }/                  "Run the code block on each element of the array";

:A1$='#={ ... }*               "Process only #";
:A1$                           "Store the number in A and copy the input array to stack";
    =                          "Get Ath index element from input array";
     '#={ ... }*               "Run the code block if Ath element equals #";

[W1LL~)]Af+{W>},1$f=S&,{ASct}* "Flood fill spaces";
[W1LL~)]Af+                    "Get the indexes of the 4 elements on the cross formed by"
                               "the Ath index";
           {W>},               "Filter out the negative values";
                1$f=           "For each of the index, get the char from input string";
                    S&,        "Check if space is one of the 4 chars from above step";
                       {    }* "Run the code block if space is present";
                        ASct   "Make the Ath character of input string as space";

N/z{S/{$W%}%'#*}%zN*           "Let the rocks fall";
N/z                            "Split the resultant string by newlines and"
                               "transpose the matrix";
   {           }%              "Run the code block for each row (column of original)";
    S/{   }%                   "Split by space and run the code block for each part";
       $W%                     "Sort and reverse. This makes # come down and . to go up";
            '#*                "Join by 3, effectively replacing back spaces with #";
                 zN*           "Transpose to get back final matrix and join by newline";

Pour le remblai, nous itérons sur toute la longueur de la grille (grille) fois. À chaque itération, nous sommes garantis de convertir au moins 1 #qui touche directement un espace en (espace). L'espace représente ici un groupe de rock ferme. Ainsi, à la fin des itérations de longueur (grille), nous sommes garantis que toutes les roches fermes sont représentées par des espaces.

Essayez-le en ligne ici

Optimiseur
la source
15

Perl 5: 98

98 comprenant 2 drapeaux de ligne de commande.

#!perl -p0
1while/
/,($x="($`)")=~y!#!.!,s/#(.*
$)/%$1/+s/#$x?%|%$x?#/%$1$2%/s;1while s/#$x\./.$1#/s;y!%!#!

Explication:

#!perl -p0 #read entire input to $_ and print at the end
/\n/;($x="($`)")=~y!#!.!; #calculate pattern matching space
                          #between two characters in the same column
                          #looks like "(......)" 
1 while s/#(.*\n$)/%$1/+s/#$x?%|%$x?#/%$1$2%/s;
                          #flood fill solid rock with %
1 while s/#$x\./.$1#/s;   #drop loose rock
y!%!#!                    #change % back to #
nutki
la source
@Optimizer Je compte sur la fin de la dernière ligne d'entrée voir: ideone.com/7E3gQh Sans cette dépendance, ce serait un seul solitaire (ou un plus court en s'appuyant sur le contraire - manque de fin de vie finale).
nutki
1
Battre CJam de près de 30%? Incroyable. Je vous félicite.
DLosc
@DLosc Plus: P
Optimizer
Battre les autres langues impératives de 100 à 300%? Incroyable. Je vous félicite. ;)
DLosc
@DLosc En regardant la réponse ci-dessus, je n'inclurai plus Perl dans la liste des langues impératives: P
Optimizer
5

JavaScript (ES6) 232

s=>{for(s=[...s+'1'.repeat(r=1+s.search('\n'))];s=s.map((c,p)=>c=='#'&(s[p+1]|s[p-1]|s[p-r]|s[p+r])?f=1:c,f=0),f;);for(;s.map((c,p)=>c=='#'&s[p+r]=='.'&&(s[p]='.',f=s[p+r]=c),f=0),f;);return s.join('').replace(/1/g,rok).slice(0,-r)}

En tant que fonction avec un paramètre de chaîne et renvoyant une chaîne.

Au début, ajoutez une rangée inférieure de «1» pour identifier la ligne de fond.
La première boucle recherche les roches fixes (qui sont proches d'un «1») et les marque également comme «1». La recherche est répétée jusqu'à ce qu'aucune autre pierre ferme ne soit trouvée.
La deuxième boucle déplace les caractères «#» restants vers la ligne du bas. Encore une fois, cela se répète jusqu'à ce qu'aucun rocher ne puisse être déplacé.
Enfin, remplacez le «1» par «#» et coupez la rangée du bas.

Moins golfé

s=>{
  r = 1+s.search('\n');
  s = [...s+'1'.repeat(r)];
  for (; s = s.map((c,p) => c=='#' & (s[p+1]|s[p-1]|s[p-r]|s[p+r])?f=1:c,f=0),f; );
  for (; s.map((c,p) => c=='#' & s[p+r]=='.'&& (s[p] ='.', s[p+r]=c, f=1),f=0),f; );
  return s.join('')
    .replace(/1/g,'#')
    .slice(0,-r)
}

Test (Vous pouvez avoir des preuves de ce que les roches sont fermes et ce qui est tombé)

F=
s=>{for(s=[...s+'1'.repeat(r=1+s.search('\n'))];s=s.map((c,p)=>c=='#'&(s[p+1]|s[p-1]|s[p-r]|s[p+r])?f=1:c,f=0),f;);for(;s.map((c,p)=>c=='#'&s[p+r]=='.'&&(s[p]='.',f=s[p+r]=c),f=0),f;);return s.join('').replace(/1/g,rok).slice(0,-r)}

var rok // using rok that is 3 chars like '#'

function update() {
  rok = C.checked ? '@' : '#';
  O.textContent=F(I.textContent)
}

update()
td { padding: 5px }
pre { border: 1px solid #000; margin:0 }
<table><tr><td>Input</td><td>Output</td></tr>
<tr><td><pre id=I>.#####....
.#....####
###.###..#
#.#...##..
.####..#.#
......###.
..#...#..#
..#...#..#</pre></td>
<td><pre id=O></pre>
</td></tr></table>
<input type='checkbox' id=C oninput='update()'>Show firm rocks

edc65
la source
3

APL, 130 119

'.##'[1+⊖1↓⍉↑↑{,/{⍵[⍒⍵]}¨x⊂⍨2=x←2,⍵}¨↓ ⍉⊃⌈/(1,¨⍳⍴⊃↓x){x←⍵⋄(⍺⌷x)∧←2⋄x≡⍵:x⋄⊃⌈/((⊂⍴⍵)⌊¨1⌈(,∘-⍨↓∘.=⍨⍳2)+⊂⍺)∇¨⊂x}¨⊂⊖'#'=x←⎕]

Puisqu'il n'est pas possible (pour autant que je sache) d'entrer des retours à la ligne lorsque la saisie est demandée, ce programme prend une matrice de caractères en entrée.

L'algorithme utilisé est d'abord la conversion en une matrice binaire ( 0est de l'air et 1est de la roche) puis le remplissage par inondation de la rangée du bas pour marquer les roches fermes comme 2. Ensuite, partitionnez chaque colonne en "espaces entre les roches fermes" et triez chaque partition pour faire "tomber" la roche meuble dans l'air.

Edit1: Golfé certains en utilisant un algorithme de remplissage d'inondation différent


Essais

Exécuter 1

Définissez une matrice de caractères Aet imprimez-la:

      A←↑('.#####....') ('.#....####') ('###.###..#') ('#.#...##..') ('.####..#.#') ('......###.') ('..#...#..#') ('..#...#..#')
      A
.#####....
.#....####
###.###..#
#.#...##..
.####..#.#
......###.
..#...#..#
..#...#..#

Ensuite, alimentez Ale programme:

      '.##'[1+⊖1↓⍉↑↑{,/{⍵[⍒⍵]}¨x⊂⍨2=x←2,⍵}¨↓⍉(1,¨⍳⊃⌽⍴x){⍵≡y←⊃⌈/x←⍺{x←⍵⋄(⍺⌷x)∧←2⋄x}¨⊂⍵:y⋄((⊂⍴⍵)⌊¨1⌈,(,∘-⍨↓∘.=⍨⍳2)∘.+⍺/⍨x≢¨⊂⍵)∇y}⊖'#'=x←⎕]
⎕:
      A
..........
....######
..#.###..#
..#...##..
.##....#..
.##...####
####..#..#
#####.#..#

Exécuter 2

      A←↑('#######')('#.....#')('#.#.#.#')('#.....#')('#######')
      A
#######
#.....#
#.#.#.#
#.....#
#######
      '.##'[1+⊖1↓⍉↑↑{,/{⍵[⍒⍵]}¨x⊂⍨2=x←2,⍵}¨↓⍉(1,¨⍳⊃⌽⍴x){⍵≡y←⊃⌈/x←⍺{x←⍵⋄(⍺⌷x)∧←2⋄x}¨⊂⍵:y⋄((⊂⍴⍵)⌊¨1⌈,(,∘-⍨↓∘.=⍨⍳2)∘.+⍺/⍨x≢¨⊂⍵)∇y}⊖'#'=x←⎕]
⎕:
      A
#######
#.....#
#.....#
#.#.#.#
#######
TwiNight
la source
2

JS - 443 octets

function g(b){function f(b,c,e){return b.substr(0,c)+e+b.substr(c+1)}function e(d,c){"#"==b[c][d]&&(b[c]=f(b[c],d,"F"),1<d&&e(d-1,c),d<w-1&&e(d+1,c),1<c&&e(d,c-1),c<h-1&&e(d,c+1))}b=b.split("\n");w=b[0].length;h=b.length;for(i=0;i<w;i++)"#"==b[h-1][i]&&e(i,h-1);for(j=h-2;-1<j;j--)for(i=0;i<w;i++)if(k=j+1,"#"==b[j][i]){for(;k<h&&"F"!=b[k][i]&&"#"!=b[k][i];)k++;k--;b[j]=f(b[j],i,".");b[k]=f(b[k],i,"#")}return b.join("\n").replace(/F/g,"#")};

L'inondation remplit les roches par le bas, puis fait descendre les roches non inondées. Utilise beaucoup de récursivité avec le remplissage d'inondation, ce qui peut retarder un peu votre navigateur.

C'est une fonction - appelez-la avec g("input")

JSFiddle: http://jsfiddle.net/mh66xge6/1/

JSFiddle non golfé: http://jsfiddle.net/mh66xge6/

DankMemes
la source
1

Python 3, 364 octets

Je suis sûr que plus pourrait être évincé de cela ... mais ça ne va jamais rivaliser avec CJam et Perl.

z="%";R=range
def F(r,c,g):
 if z>g[r][c]:g[r][c]=z;[F(r+d%2*(d-2),c+(d%2-1)*(d-1),g)for d in R(4)]
def P(s):
 t=s.split()[::-1];w=len(t[0]);g=[list(r+".")for r in t+["."*w]];[F(0,c,g)for c in R(w)]
 for c in R(w):
  for r in R(len(g)):
   while g[r][c]<z<g[r-1][c]and r:g[r][c],g[r-1][c]=".#";r-=1
 return"\n".join(''.join(r[:w])for r in g[-2::-1]).replace(z,"#")

Similaire à d'autres réponses. Une bizarrerie est qu'elle tourne la grille à l'envers en premier (pour rendre les indices de boucle plus pratiques) et ajoute une ligne et une colonne supplémentaires de .(pour éviter les problèmes avec les -1indices de bouclage ). Exécutez en appelant P(string).

DLosc
la source