Topología ASCII pt 1: ¿Puedo contar con usted?

28

Tengo un serio problema. Tengo algunos archivos de texto donde guardo mis números muy importantes, ¡todos los importantes! Y dos y tres ...

Estos números eran tan importantes que no podía confiarlos a esos nuevos sistemas numéricos decimales o binarios. Mantuve cada número codificado en unario, así:

            +--+
            |  |
+---+  +----+  |
|   |  |       |
+---+  +-------+
   ~/two.txt

Simple y confiable: dos bucles ASCII para el número 2. Desafortunadamente, estas cosas tienden a enredarse con el tiempo y ahora me cuesta descubrir cuántos bucles hay en cada archivo. Aquí hay algunos ejemplos que trabajé a mano:

Uno:

   +---+
   |   |
+--+   |
|      |
+--+   |
   |   |
   |   |
   |   |
+--+   +--+
|         |
+---------+

Tres:

+---------+
| +-----+ |
| | +-+ | |
| | | | | |
| | +-+ | |
| +-----+ |
+---------+

Cuatro:

  +--------------+
  |  +--+  +--+  |
  |  |  |  |  |  |
+-|-----|-----|----+
| |  |  |  |  |  | |
| +--+  +--+  +--+ |
+------------------+

              +------------+
              |            |
        +-----+  +-----+   |
        |        |     |   |
  +-----|-----------+  |   |
  |     |  +--+  |  |  |   |
  +-+   +--|--|--+  +---------+
    |      |  +-+      |   |  |
    +------+    |      |   |  |
                +-------+  |  |
                       ||  |  |
                       |+-----+
                       |   |
                       +---+

Cinco:

+--------+  +--------+  +--------+
|        |  |        |  |        |
|     +--|-----+  +--|-----+     |
|     |  |  |  |  |  |  |  |     |
+-----|--+  +-----|--+  +--------+
      |        |  |        |
      +--------+  +--------+

¿Me pueden ayudar a contar mis bucles?

Estas son las reglas:

  • Como guardo todo en unario codificado en ASCII, la eficiencia del espacio es muy importante para mí. Por lo tanto, este es el código de golf. El programa más pequeño en bytes gana.
  • Los bucles se dibujan con los caracteres +, -, |. Cada esquina del bucle se dibuja sin ambigüedades: exactamente uno de los caracteres arriba y debajo del + será |, y exactamente uno a la derecha o izquierda será -. Dos marcas + nunca son adyacentes.
  • Las hebras pueden pasar una debajo de la otra. Cuando los hilos se crucen, podrá ver el hilo "debajo" inmediatamente a ambos lados del hilo "sobre".
  • Su programa debe tomar una representación de cadena del bucle (ya sea desde stdin o como un parámetro de función) y generar un número (ya sea stdout o como valor de retorno).
  • Las longitudes de línea pueden no ser uniformes en el dibujo del bucle y puede haber espacios finales en cada línea.
  • Puede suponer que hay al menos un bucle en la entrada.

¡Cuento con usted!

Matt Noonan
fuente
¿Puede uno de los lados de un 'under strand' ser un +?
feersum
@feersum: No, los dos bordes unidos al + siempre estarán visibles.
Matt Noonan
1
@ Martin: Me temo que no. Mi espacio de almacenamiento es realmente premium, por lo que no puedo ahorrar todos esos espacios adicionales.
Matt Noonan
Creo que debería agregar esto ( pastebin ) como un caso de prueba a menos que me falte algo y no sea una entrada válida. Hay 6 bucles; Solo lo probé en línea con la solución SnakeEx y produce 12.
blutorange
1
Tal vez debería prohibir o permitir explícitamente los bucles que se cruzan entre sí (por ejemplo, pastebin.com/5RLZuULG ) Actualmente, la solución rubí los detecta, pero no la solución SnakeEx.
blutorange

Respuestas:

19

SnakeEx - 98 bytes con Javascript, 44 sin

Esto parecía un buen problema para probar mi idioma desde el desafío quincenal en:

m:({e<>PE}\-[|\-]*<T>\+|[|\-]*<T>)+`\+
e:\+

El mejor lugar para probar esto es mi intérprete en línea .

SnakeEx hace coincidir patrones en el texto mediante el uso de "serpientes" que se mueven alrededor de las expresiones regulares de texto. El código se lee como una expresión regular, excepto:

  • La <T>instrucción Ese es un comando de dirección que ramifica a la serpiente de izquierda a derecha desde su dirección actual.
  • {e<>PE}es como una llamada de subrutina. Es el que genera una serpiente con definición eque avanza ( <>) y con parámetros P(piggyback - la serpiente de desove sigue a la nueva serpiente) y E(exclusivo - no coincide con nada que ya haya sido igualado). Esta verificación exclusiva es lo único que impide que la serpiente se repita infinitamente.
  • El prefijo `al final indica que lo que sigue debe coincidir solo si ya se ha comparado, lo que podemos usar para forzar el cierre del ciclo.

Debido a que SnakeEx es como regex y técnicamente no genera los resultados deseados por sí mismo, supongo que necesitamos envolverlo en algún Javascript que llame al intérprete:

function e(s){return snakeEx.run('m:({e<>PE}\\-[|\\-]*<T>\\+|[|\\-]*<T>)+`\\+\ne:\\+',s,1).length}

Editar : lo arregló para que funcione con los casos de prueba adicionales de blutorange

BMac
fuente
1
+1 Realmente me gusta esto, tal vez porque puedo probarlo en línea y resaltar los bucles. Pero es posible que desee verificar estos dos casos de prueba: 1 , 2
blutorange
@blutorange Buena captura. Agregué una solución ligeramente hacky para los bucles de autocruzamiento. Sin embargo, tendré que pensar un poco más en el caso de prueba 1.
BMac
Ese es el fácil de arreglar, simplemente reemplace [^ ]con [|\-];)
blutorange
Ja, me tomó mucho tiempo entender por qué ese era el caso. Buena llamada.
BMac
¡Esto es asombroso!
Ingo Bürk
10

C # - 338 388 433 bytes

Se guardó un montón de bytes al cambiar a una matriz unidimensional.

using C=System.Console;using System.Linq;class P{static void Main(){var D=C.In.ReadToEnd().Split('\n');int z,w=D.Max(m=>m.Length)+1,d,c=0;var E=D.SelectMany(l=>l.PadRight(w)).ToList();for(z=E.Count;z-->1;)if(E[z-1]==43)for(d=1,c++;E[z+=d%2<1?w*d-w:d-2]>32;)if(E[z]<44){E[z]=' ';d=d%2>0?z<w||E[z-w]<99?2:0:E[z+1]!=45?1:3;}C.WriteLine(c);}}

Primero lee en la entrada, y lo hace agradable y rectangular, con un borde "" para que no tengamos que hacer ningún control de límites en la horizontal (más barato hacer la verificación en la vertical que poner en la línea extra) . Luego mira hacia atrás a través del rectángulo, por lo que siempre toca una esquina inferior derecha. Cuando golpea uno de estos, se dirige hacia arriba, siguiendo los + s que encuentra y eliminándolos a medida que avanza (con un espacio). Deja de seguir cuando se encuentra con un espacio. Probado en los cinco ejemplos dados.

using C=System.Console;
using System.Linq;

class P
{
    static void Main()
    {
        // read in
        var D=C.In.ReadToEnd().Split('\n');

        int z, // z is position in E
        w=D.Max(m=>m.Length)+1, // w is width
        d, // d is direction of travel (1 == horizontal?, 2 == down/right?)
        c=0; // c is loop count

        // make all the lines the same length
        var E=D.SelectMany(l=>l.PadRight(w)).ToList(); // say no to horizontal bounds checking

        // consume +s
        for(z=E.Count;z-->1;)
            if(E[z-1]==43) // right-most lower-most +
                for(d=1,c++; // go left, increment counter
                    E[z+=d%2<1?w*d-w:d-2]>32 // move z, then check we havn't hit a space (when we do, z is basiclly z - 1)
                    ;)
                    if(E[z]<44) // +
                    {
                        E[z]=' '; // toodles

                        d=
                            d%2>0? // currently horizontal, must go vertical
                                z<w||E[z-w]<99?2 // can't go up, must go down
                                :0 // can go up, go up
                            : // currently vertical, must go horizontal
                                E[z+1]!=45?1 // can't go right, must go left
                                :3 // can go right, go right
                            ;
                    }

        // output result
        C.WriteLine(c);
    }
}
VisualMelon
fuente
Guau. Eso es impresionante: o
Metoniem
6

Slip , 51 41 + 2 = 43 bytes

$a(`+`-[^ +]*`+(<|>)`|[^ +]*`+#(<|>))+?$A

(Ahora actualizado para trabajar con el caso de prueba de @ blutorange a un costo mayor)

Dado que @BMac usó SnakeEx para este desafío, pensé en intentar usar mi envío de lenguaje de coincidencia de patrones 2D , Slip. Pero debido a que Slip no tenía las características necesarias para resolver este problema, las he agregado en los últimos días. En otras palabras, esta presentación no es elegible para ganar .

Corre con la nbandera por el número de partidos, por ejemplo

py -3 slip.py regex.txt input.txt n

Pruébalo en línea .


Explicación

Debido a la gran cantidad de nuevas características en esta presentación, esta es una buena oportunidad para mostrarlas.

$a                Set custom anchor at current position
(
  `+`-            Match '+-'
  [^ +]*          Match any number of '|' or '-'
  `+              Match a '+'
  (<|>)           Either turn left or turn right
  `|              Match a '|'
  [^ +]*          Match any number of '|' or '-'
  `+              Match a '+'
  #               Prevent next match from moving the match pointer (doubling up on '+')
  (<|>)           Either turn left or turn right
)
+?                Do the above at least once, matching lazily
$A                Make sure we're back where we started

Slip intenta hacer coincidir partidas desde cada posición, y solo devuelve partidas únicas. Tenga en cuenta que usamos [^ +]: mientras que usar [-|]teóricamente ahorraría dos bytes, sin escapes -al principio / final de las clases de caracteres aún no se implementa en Slip.

Sp3000
fuente
1
@blutorange threetambién tiene +s que no son uno -, uno |y 2 espacios, así que supongo que no es un error
Sp3000
5

Rubí 295

F=->i{l=i.lines
g={}
l.size.times{|y|i.size.times{|x|l[y][x]==?+&&g[[y,x]]=[[y,x]]}}
c=->a,b{w=g[b]+g[a];w.map{|x|g[x]=w}}
k=g.keys
k.product(k).map{|n,o|
r,p=n
s,q=o
((r==s&&p<q&&l[r][p...q]=~/^\+-[|-]*$/)||(p==q&&r<s&&l[r...s].map{|l|l[p]||c}.join=~/^\+\|[|-]*$/))&&c[n,o]}
g.values.uniq.size}

Pruébelo en línea: http://ideone.com/kIKELi ( agregué una #to_allamada en la primera línea, porque ideone.com usa Ruby 1.9.3, que no es compatible #sizecon Enumerables. En Ruby 2.1.5+ el código funciona bien . )

El enfoque es el siguiente:

  • haga una lista de todos los +signos en la entrada y considere cada uno de ellos una forma distinta
  • encuentra líneas horizontales y verticales que unen dos +signos y combinan sus formas en una
  • al final, el número de formas distintas coincide con el resultado

Aquí hay una versión más legible:

def ascii_topology_count(input)
  lines = input.lines
  max_length = lines.map(&:size).max

  # hash in which the keys are corners ("+"s), represented by their [y, x] coords
  # and the values are arrays of corners, representing all corners in that group
  corner_groups = {}

  lines.size.times { |y|
    max_length.times { |x|
      if lines[y][x] == ?+
        corner_groups[[y, x]] = [[y, x]]
      end
    }
  }

  # function that combines the groups of two different corners
  # into only one group
  combine_groups =-> c1, c2 {
    g1 = corner_groups[c1]
    g2 = corner_groups[c2]

    g2 += g1
    corner_groups[c1] = g2
    g2.map{|x| corner_groups[x] = g2}
  }

  corner_groups.keys.product(corner_groups.keys).map{|c1, c2|
    y1,x1=c1
    y2,x2=c2
    if y1 == y2 && x1 < x2
      # test horizontal edge
      t = lines[y1][x1...x2]
      if t =~ /^\+-[|-]*$/
        # p "#{c1}, #{c2}, [H] #{t}"
        combine_groups[c1, c2]

      end
    end

    if x1 == x2 && y1 < y2
      # test vertical edge
      t=lines[y1...y2].map{|l|l[x1]||' '}.join

      if t =~ /^\+\|[|-]*$/
        # p "#{c1}, #{c2}, [V] #{t}"
        combine_groups[c1, c2]
      end
    end
  }

  corner_groups.values.uniq.count
end
Cristian Lupascu
fuente
5

JavaScript (ES6) 190 197 202 215 235 289 570

Editar matriz de dimensión única en lugar de 2 dimensiones, tamaño máximo de fila 999 caracteres (pero puede ser más sin costo, por ejemplo, 1e5 en lugar de 999)

Editar fragmento de código animado agregado, ver más abajo

F=s=>[...s.replace(/.+/g,r=>r+Array(e-r.length),e=999)]
  .map((c,x,z,d=1,f='-',g='|')=>{
    if(c=='+')
      for(++n;z[x+=d]!='+'||([f,g,e,d]=[g,f,d,z[x-e]==g?-e:z[x+e]==g&&e],d);)
        z[x]=z[x]==g&&g
  },n=0)|n

Ungolfed primer intento

f=s=>
{
  cnt=0
  s = (1+s+1).split(/[1\n]/)

  for(;x=-1, y=s.findIndex(r=>~(x=r.indexOf('+-'))), x>=0;++cnt)
  {
    //console.log(y+' '+x+' '+s.join('\n'))
    dx = 1
    for(;dx;)
    {
      a=s[y-1],b=s[y+1],
      r=[...s[y]]
      for(r[x]=' ';(c=r[x+=dx])!='+';)
      {
        if (c=='-')
          if((a[x]||b[x])=='|')r[x]='|';
          else r[x]=' ';
      }  

      if(a[x]=='|')dy=-1;
      else if(b[x]=='|')dy=1;
      else dy=0
      r[x]=' '
      s[y]=r.join('')
      if (dy) {
        for(;y+=dy,r=[...s[y]],(c=r[x])!='+'&c!=' ';)
        {
          if (c=='|') 
            if((r[x-1]||r[x+1])=='-')r[x]='-';
            else r[x]=' ';
          s[y]=r.join('')
        }  
        if(r[x-1]=='-')dx=-1;
        else if(r[x+1]=='-')dx=1;
        else dx=0;
      }
    }  
  }
  return cnt
}

Fragmento animado

Prueba en la consola Firefox / FireBug

F('\
  +--------------+\n\
  |  +--+  +--+  |\n\
  |  |  |  |  |  |\n\
+-|-----|-----|----+\n\
| |  |  |  |  |  | |\n\
| +--+  +--+  +--+ |\n\
+------------------+\n\
\n\
              +------------+\n\
              |            |\n\
        +-----+  +-----+   |\n\
        |        |     |   |\n\
  +-----|-----------+  |   |\n\
  |     |  +--+  |  |  |   |\n\
  +-+   +--|--|--+  +---------+\n\
    |      |  +-+      |   |  |\n\
    +------+    |      |   |  |\n\
                +-------+  |  |\n\
                       ||  |  |\n\
                       |+-----+\n\
                       |   |\n\
                       +---+')

4 4

F('\
+--------+  +--------+  +--------+\n\
|        |  |        |  |        |\n\
|     +--|-----+  +--|-----+     |\n\
|     |  |  |  |  |  |  |  |     |\n\
+-----|--+  +-----|--+  +--------+\n\
      |        |  |        |\n\
      +--------+  +--------+')

5 5

edc65
fuente
¿Seguramente ahorrarías algunos bytes al alinear la versión de golf? También puede crear una función anónima (creo que está dentro de las reglas de codegolf)
theonlygusti
@theonlygusti, la versión de golf se cuenta como una sola línea (no se cuentan líneas nuevas ni espacios de sangría). Con una función anónima ahorro 2 bytes, ahorro insignificante ...
edc65 05 de
4

Rubí, 178 187 199 212

Mejor versión ruby, crea una función F. Ahora con advertencias más deliciosas del intérprete constantemente.

b=->n{Q[I]=?~
d=Q[I+n]==(n>1??|:?-)?1:-1
loop{I+=d*n
b[n>1?1:N]if Q[I]==?+
Q[I]<?~?4:break}}
F=->s{N=s.size;Q=s+?\n
Q.gsub!(/^.*$/){$&.ljust N-1}
(0..N).find{!(I=Q=~/\+/)||b[1]}}

Pruébelo en línea: ideone

Básicamente, la función bcomienza en cualquiera +, pasa recursivamente por el ciclo, configurando todo +en u. Así, un ciclo se elimina cada vez que bse llama. La función Fsolo intenta con qué frecuencia necesitamos llamar bhasta que no quede ningún bucle.

blutorange
fuente
2

Python 2 - 390

Toma una cadena con líneas nuevas de stdin. Es un método bastante simple de golf, pero estoy seguro de que no tanto como podría estar considerando cuánto tiempo es.

s=raw_input().split('\n');L=len
def R(x,y):
 b=p,q=x,y;u=v=f=0
 while b!=(p,q)or not f:
    p+=u;q+=v;f=u+v;c=s[q][p]
    if'+'==c:u,v=[(i,j)for i,j in{(-1,0),(1,0),(0,-1),(0,1)}-{(-u,-v)}if 0<=q+j<L(s)and 0<=p+i<L(s[q+j])and s[q+j][p+i]in['-+','|+'][j]][0];s[q]=s[q][:p]+' '+s[q][p+1:]
    if c+s[q+v][p+u]in'-|-':p+=u;q+=v
print L([R(x,y)for y in range(L(s))for x in range(L(s[y]))if'+'==s[y][x]])
KSab
fuente
1

Python 2 - 346 bytes

Implementado como una función cque toma los datos del archivo como entrada y devuelve el número de bucles.

Primero, la función descompone los datos en un mapeo de ubicaciones de elementos de bucle al tipo de elemento en esa ubicación (por ejemplo {(0,0): '+'}). Luego, utiliza dos funciones internas recursivas mutuamente. El primero elimina un segmento de bucle del mapeo y decide qué ubicaciones verificar para el segmento posterior. El segundo verifica qué tipo de elemento de bucle está en las ubicaciones seleccionadas y, si es compatible con lo que se espera, llama al primero para eliminar la sección recién encontrada.

e=enumerate
def c(d):
 D={(i,j):k for i,l in e(d.split('\n'))for j,k in e(l)if k in'+-|'}
 def f(r,c,R,C,t):
  if D.get((r,c),t)!=t:g(r,c)
  elif D.get((R,C),t)!=t:g(R,C)
 def g(r,c):
  t=D.pop((r,c))
  if t!='|':f(r,c-1,r,c-2,'|');f(r,c+1,r,c+2,'|')
  if t!='-':f(r-1,c,r-2,c,'-');f(r+1,c,r+2,c,'-')
 n=0
 while D:g(*D.keys()[0]);n+=1
 return n
Mac
fuente