Topologia ASCII pt 1: Posso contar com você?

28

Eu tenho um problema sério Eu tenho alguns arquivos de texto onde guardo meus números muito importantes - todos os importantes! E dois e três ..

Esses números eram tão importantes que eu não os podia confiar nos novos sistemas de números decimais ou binários. Eu mantive cada número codificado em unário, da seguinte forma:

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

Simples e confiável: dois loops ASCII para o número 2. Infelizmente, essas coisas tendem a se complicar ao longo do tempo e agora tenho dificuldade em descobrir quantos loops existem em cada arquivo. Aqui estão alguns exemplos que trabalhei à mão:

1:

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

Três:

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

Quatro:

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

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

Cinco:

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

Você pode me ajudar a contar meus loops?

Aqui estão as regras:

  • Como guardo tudo no unário codificado em ASCII, a eficiência do espaço é muito importante para mim. Portanto, isso é código de golfe. O menor programa em bytes vence.
  • Os loops são desenhados com os caracteres +, -, |. Todos os cantos do loop são desenhados sem ambiguidade: exatamente um dos caracteres acima e abaixo do + será |, e exatamente um à direita ou à esquerda será -. Duas marcas + nunca são adjacentes.
  • Os fios podem passar um por cima do outro. Quando as mechas se cruzam, você pode ver a mecha "under" imediatamente em ambos os lados da mecha "over".
  • Seu programa deve usar uma representação de string do loop (a partir de stdin ou como um parâmetro de função) e produzir um número (para stdout ou como um valor de retorno).
  • Os comprimentos das linhas podem não ser uniformes no desenho do loop e pode haver espaços à direita em cada linha.
  • Você pode assumir que há pelo menos um loop na entrada.

Estou contando com você!

Matt Noonan
fonte
Pode um dos lados de um 'under strand' ser um +?
feersum
@feersum: Não, as duas arestas anexadas ao + sempre estarão visíveis.
Matt Noonan
1
@ Martin: Receio que não. Meu espaço de armazenamento é realmente muito caro, então não posso poupar todos esses espaços extras.
Matt Noonan
Eu acho que você deve adicionar isso ( pastebin ) como um caso de teste, a menos que esteja faltando alguma coisa e não seja uma entrada válida. Existem 6 loops; Eu só testei on-line com a solução SnakeEx e ela produz 12. #
blutorange
1
Talvez você deva proibir explicitamente ou permitir loops que se cruzam (por exemplo, pastebin.com/5RLZuULG ). Atualmente, eles são detectados pela solução ruby, mas não pela solução SnakeEx.
blutorange

Respostas:

19

SnakeEx - 98 bytes com Javascript, 44 sem

Parecia um bom problema tentar o meu idioma a partir do Desafio Quinzenal :

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

O melhor lugar para experimentar isso é o meu intérprete online .

O SnakeEx faz a correspondência de padrões no texto usando "serpentes" que se movem pelas expressões correspondentes ao texto. O código parece um regex, exceto:

  • A <T>instrução Esse é um comando de direção que ramifica a cobra para a esquerda e para a direita em relação à direção atual.
  • {e<>PE}é como uma chamada de sub-rotina. Ele cria uma cobra com a definição eavançando ( <>) e com parâmetros P(nas costas - a cobra reprodutora segue a nova cobra) e E(exclusivo - não corresponde a nada que já foi correspondido). Essa verificação exclusiva é a única coisa que impede a cobra de fazer um loop infinito.
  • O prefixo `no final indica que o que segue deve ser correspondido apenas se já tiver sido correspondido, o que podemos usar para forçar o fechamento do loop.

Como o SnakeEx é como regex e tecnicamente não produz os resultados desejados, acho que precisamos envolvê-lo em algum Javascript chamando o intérprete:

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

Edit : corrigido para trabalhar com os casos de teste adicionais do blutorange

BMac
fonte
1
+1 Eu realmente gosto disso, talvez porque eu possa experimentar on-line e destacar os loops. Mas você pode querer verificar esses dois casos de teste: 1 , 2
blutorange
@blutorange Boa captura. Eu adicionei uma correção ligeiramente hacky para loops de cruzamento automático. Vou ter que pensar um pouco mais no caso de teste 1.
BMAC
Isso é o mais fácil de corrigir, basta substituir [^ ]com [|\-];)
blutorange
Hah, demorei muito tempo para descobrir por que era esse o caso. Boa decisão.
BMAC
Isso é incrível!
Ingo Bürk 03/04/2015
10

C # - 338 388 433 bytes

Economizou um monte de bytes alterando para uma 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);}}

Primeiro, ele lê a entrada e a torna agradável e retangular, com uma borda "" para que não tenhamos que verificar os limites na horizontal (é mais barato verificar na vertical do que colocar na linha extra) . Em seguida, ele olha para trás através do retângulo, para sempre bater no canto inferior direito. Quando atinge um deles, ele se direciona, seguindo todos os + s que encontrar, e limpando-os à medida que avança (com um espaço). Para de seguir quando encontra um espaço. Testado nos cinco exemplos 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
fonte
Uau. Isso é impressionante: o
Metoniem 15/02
6

Deslizamento , 51 41 + 2 = 43 bytes

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

(Agora atualizado para funcionar com o caso de teste do @ blutorange a um custo maior)

Como o @BMac usou o SnakeEx para esse desafio, pensei em usar o meu envio de linguagem de correspondência de padrões 2D , Slip. Mas como o Slip não tinha os recursos necessários para resolver esse problema, eu os adicionei nos últimos dias. Em outras palavras, este envio não é elegível para ganhar .

Corra com a nbandeira para o número de correspondências, por exemplo

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

Experimente online .


Explicação

Devido à infinidade de novos recursos neste envio, esta é uma boa oportunidade para mostrá-los.

$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

O deslizamento tenta combinar a partir de todas as posições e retorna apenas correspondências únicas. Observe que usamos [^ +]- enquanto o uso [-|]economizaria dois bytes, teoricamente, o escape sem escape -no início / fim das classes de caracteres ainda não foi implementado no Slip.

Sp3000
fonte
1
@blutorange threetambém tem +s que não são um -, um |e 2 lugares, então estou assumindo que não é um erro
SP3000
5

Ruby 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}

Experimente on-line: http://ideone.com/kIKELi ( eu adicionei uma #to_achamada na primeira linha, porque ideone.com usa o Ruby 1.9.3, que não suporta #sizepara Enumerables Em Ruby 2.1.5+ o código é executado OK. . )

A abordagem é a seguinte:

  • faça uma lista de todos os +sinais na entrada e considere cada um deles com uma forma distinta
  • encontre linhas horizontais e verticais que ligam dois +sinais e combinam suas formas em um
  • no final, o número de formas distintas corresponde ao resultado

Aqui está uma versão mais legível:

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
fonte
5

JavaScript (ES6) 190 197 202 215 235 289 570

Editar matriz de dimensão única em vez de 2 dimensões, caracteres de tamanho máximo de linha 999 (mas pode ser mais sem custo, por exemplo, 1e5 em vez de 999)

Editar snippet de código animado adicionado, veja abaixo

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

Primeira tentativa ungolfed

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
}

Snippet animado

Teste no console 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

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

5

edc65
fonte
Certamente você salvaria alguns bytes alinhando a versão em golf com um único alinhamento? Você também pode simplesmente criar uma função anônima (eu acho que é dentro das regras do codegolf)
theonlygusti
@theonlygusti a versão golfed é contado como uma única linha (sem novas linhas e espaços de recuo são contados) .Com uma função anônima I salvar 2 bytes, a poupança insignificante ...
edc65
4

Ruby, 178 187 199 212

Melhor versão ruby, cria a função F. Agora, com mais deliciosos avisos para intérpretes 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]}}

Teste on-line: ideone

Basicamente, a função bcomeça em qualquer +, recursivamente passa pelo loop, configurando all +para u. Assim, um loop é removido a cada vez que bé chamado. A função Fapenas tenta quantas vezes precisamos chamar baté que não haja nenhum loop restante.

blutorange
fonte
2

Python 2 - 390

Pega uma string com novas linhas de stdin. É um método bem simples de jogar um pouco, mas tenho certeza que não tanto quanto poderia estar considerando quanto tempo leva.

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
fonte
1

Python 2 - 346 bytes

Implementado como uma função cque recebe os dados do arquivo como entrada e retorna o número de loops.

Primeiro, a função decompõe os dados em um mapeamento dos locais dos elementos do loop para o tipo de elemento nesse local (por exemplo {(0,0): '+'}). Em seguida, ele usa duas funções internas mutuamente recursivas. O primeiro remove um segmento de loop do mapeamento e decide quais locais verificar o segmento subsequente. O segundo verifica que tipo de elemento de loop está nos locais selecionados e, se for compatível com o esperado, chama o primeiro para remover a seção recém-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
fonte