Labirintos Infinitos

35

fundo

Você é aprendiz de um poderoso mago, e seu mestre está desenvolvendo um feitiço para criar um labirinto interdimensional para prender seus inimigos. Ele quer que você programe seu computador a vapor para analisar os possíveis layouts. A programação desta máquina diabólica é altamente perigosa, portanto, você deve manter o código o mais curto possível.

Entrada

Sua entrada é uma grade bidimensional de pontos .e hashes #, significando espaço e paredes vazios, dados como uma sequência delimitada por nova linha. Sempre haverá pelo menos um .e um #, e você pode decidir se há uma nova linha à direita ou não.

Essa grade é o modelo de um labirinto infinito, que é feito alinhando infinitamente muitas cópias da grade uma da outra. O labirinto é dividido em cavidades , que são componentes conectados de espaços vazios (espaços adjacentes na diagonal não são conectados). Por exemplo, a grade

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

resulta no seguinte labirinto (continuado infinitamente em todas as direções):

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

Este labirinto em particular contém uma cavidade de área infinita. Por outro lado, esse projeto resulta em um labirinto com apenas cavidades finitas:

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

Saída

Sua saída será um valor verdadeiro se o labirinto contiver uma cavidade infinita e um valor falso se não. Observe que o labirinto pode conter cavidades finitas e infinitas; nesse caso, a saída deve ser verdadeira.

Regras

Você pode escrever um programa completo ou uma função. A menor contagem de bytes vence e as brechas padrão não são permitidas.

Casos de teste adicionais

Cavidades infinitas:

.#

#.#
...
#.#

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

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

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

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

Cavidades finitas:

###
#.#
###

.#
#.

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

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

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

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

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


###########
........#..
#########.#
..........#
.##########
.#.........
##.########
...#.......
Zgarb
fonte
Existe um caractere de nova linha à direita?
FUZxxl
@FUZxxl Isso é com você.
Zgarb
O labirinto infinito pode ser uma linha reta que vai ao infinito?
11
@ Neil Eu não tenho certeza do que você quer dizer. O primeiro e o segundo exemplos infinitos têm linhas infinitas, mas há pelo menos um .e um #na entrada.
Zgarb 31/03
11
Bom desafio, mais difícil do que parece
edc65

Respostas:

2

JavaScript (ES6), 235253

O mesmo método usado pelo @mac. Para cada célula livre, tento um preenchimento recursivo, marcando as células usadas com a coordenada que estou usando (que pode estar fora do modelo original). Se durante o preenchimento eu chegar a uma célula já marcada com uma coordenada diferente, estou em um caminho infinito.

A maneira peculiar de lidar com o módulo em JS é bastante irritante.

L=g=>(
  g=g.split('\n').map(r=>[...r]),
  w=g[0].length,h=g.length,
  F=(x,y,t=((x%w)+w)%w,u=((y%h)+h)%h,v=g[u][t],k=0+[x,y])=>
    v<'.'?0:v>'.'?v!=k
    :[0,2,-3,5].some(n=>F(x+(n&3)-1,y+(n>>2)),g[u][t]=k),
  g.some((r,y)=>r.some((c,x)=>c=='.'&&F(x,y)))
)

Teste no console Firefox / FireBug

Infinito

['##.###\n#..###\n..##..\n###..#\n##..##'
,'#.#\n...\n#.#'
,'#.###.#.###.#\n#.#...#...#.#\n#.#.#####.#.#\n..#.#...#.#..\n###.#.#.#.###\n#...#.#.#...#\n#.###.#.###.#'
,'##.###\n#..###\n..##..\n###..#\n##..##'
,'#.####.###.###.####\n#...#..#...###..###\n###.#..#.######..##\n....####.#######...\n###..###...########\n##########.##....##\n..###......##.##...\n#.........##..#####\n###########..###..#\n#...........####..#\n#.###########.##..#\n#.##....##.....####\n#.####.###.###.####'
].forEach(g=>console.log(g,L(g)))

Saída

"##.###
#..###
..##..
###..#
##..##" true

"#.#
...
#.#" true

"#.###.#.###.#
#.#...#...#.#
#.#.#####.#.#
..#.#...#.#..
###.#.#.#.###
#...#.#.#...#
#.###.#.###.#" true

"##.###
#..###
..##..
###..#
##..##" true

"#.####.###.###.####
#...#..#...###..###
###.#..#.######..##
....####.#######...
###..###...########
##########.##....##
..###......##.##...
#.........##..#####
###########..###..#
#...........####..#
#.###########.##..#
#.##....##.....####
#.####.###.###.####" true

Finito

['###\n#.#\n###', '.#\n#.', '####\n.#..\n####'
,'#.#.#\n..#..\n#####\n..#..\n#.#.#'
,'#.#.#.#.#.#\n..#...#.#..\n###.###.###\n..#.#......\n#.#.#######\n#.#.......#\n#.#######.#\n#.#.....#.#\n#.#.#.#.#.#'
,'##....#####\n.#..#...##.\n.##.#..#...\n..###.###..\n#..##.#####\n#...##....#\n#.#.#####.#\n###..####.#\n....####...\n###...#####'
,'###....##.#########\n####...##....#...##\n..####.#######.###.\n....##..........##.\n###..#####.#..##...\n####..#..#....#..##\n..###.####.#.#..##.\n..###...#....#.#...\n..####..##.###...##\n#.####.##..#####.##\n####...##.#####..##'
].forEach(g=>console.log(g,L(g)))

Saída

"###
#.#
###" false

".#
#." false

"####
.#..
####" false

"#.#.#
..#..
#####
..#..
#.#.#" false

"#.#.#.#.#.#
..#...#.#..
###.###.###
..#.#......
#.#.#######
#.#.......#
#.#######.#
#.#.....#.#
#.#.#.#.#.#" false

"##....#####
.#..#...##.
.##.#..#...
..###.###..
#..##.#####
#...##....#
#.#.#####.#
###..####.#
....####...
###...#####" false

"###....##.#########
####...##....#...##
..####.#######.###.
....##..........##.
###..#####.#..##...
####..#..#....#..##
..###.####.#.#..##.
..###...#....#.#...
..####..##.###...##
#.####.##..#####.##
####...##.#####..##" false
edc65
fonte
Sim, daft modulo também era problemático em C #, mas acho que encontrei uma maneira de fazer bom uso dele em minha cópia de trabalho com o código direcional (só voltarei a postar se conseguir 10% redução ou melhor): (j%4-1)%2fornece um bom padrão de repetição.
VisualValon:
Acredito que funções não nomeadas são permitidas e, portanto, a menos que a função inclua uma chamada para si mesma (parece que não), é permitido não contar a L=contagem de bytes.
SuperJedi224
@ SuperJedi224 provavelmente você está certo, mas é curto o suficiente como é, afinal,
edc65
21

C # - 423 375 bytes

O programa C # completo, aceita entrada via STDIN, gera "True" ou "False" para STDOUT conforme apropriado.

Eu não podia deixar esse Linq lá ... felizmente, sua remoção valeu a pena! Agora, ele controla as células visitadas e visitadas em uma matriz (já que, de qualquer maneira, apenas olha para um número finito delas). Também reescrevi o código direcional, removendo a necessidade de um Lambda e geralmente tornando o código mais impossível de entender (mas com economia substancial de bytes).

using C=System.Console;struct P{int x,y;static void Main(){int w=0,W,k=0,o,i,j;P t;string D="",L;for(;(L=C.ReadLine())!=null;D+=L)w=L.Length;for(i=W=D.Length;i-->0&k<W;){k=1;P[]F=new P[W];for(F[j=0].x=i%w+W*W,F[0].y=i/w+W*W;D[i]>35&j<k;)for(t=F[j++],o=1;o<5&k<W;t.y+=(o++&2)-1){t.x+=o&2;if(D[--t.x%w+t.y%(W/w)*w]>35&System.Array.IndexOf(F,t)<0)F[k++]=t;}}C.WriteLine(k>=W);}}

Trata-se de uma pesquisa de amplitude (não que isso importe) que continue até ficar presa em uma caverna finita ou decidir que a caverna é grande o suficiente para ser infinitamente grande (quando tiver tantas células quanto a célula). Retângulo original, isso significa que deve haver um caminho de uma célula para outra em algum outro lugar, que possamos seguir sempre).

Código não cortado:

using C=System.Console;

struct P
{
    int x,y;

    static void Main()
    {
        int w=0, // w is the width
        W, // W is the length of the whole thing
        k=0, // k is visited count
        o, // o is offset, or something (gives -1,0 0,-1 +1,0 0,+1 t offset pattern)
        i, // i is the start cell we are checking currently
        j; // j is the F index of the cell we are looking at

        P t; // t is the cell at offset from the cell we are looking at

        string D="", // D is the map
        L;

        for(;(L=C.ReadLine())!=null; // read a line, while we can
            D+=L) // add the line to the map
            w=L.Length; // record the width

        for(i=W=D.Length;i-->0&k<W;) // for each cell
        {
            k=1;

            P[]F=new P[W]; // F is the list of visited cells,

            for(F[j=0].x=i%w+W*W,F[0].y=i/w+W*W; // there are reasons (broken modulo)
                D[i]>35&j<k;) // for each cell we've visited, until we've run out
                for(t=F[j++], // get the current cell
                    o=1; // o is just a counter which we use to kick t about
                    o<5& // 4 counts
                    k<W; // make sure we havn't filled F
                    t.y+=(o++&2)-1) // kick and nudge y, inc o
                {
                    t.x+=o&2; // kick x
                    if(D[--t.x%w+t.y%(W/w)*w]>35 // nudge x, it's a dot
                       &System.Array.IndexOf(F,t)<0) // and we've not seen it before
                        F[k++]=t; // then add it
                }
        }

        C.WriteLine(k>=W); // result is whether we visited lots of cells
    }
}
VisualMelon
fonte
11
Provavelmente, a primeira vez que vi uma C#resposta como a melhor forma de obter votos aqui.
Michael McGriff
11
Main () em struct, agora isso é fofo.
PTwr
10

Python 2 - 258 210 244 bytes

Verifique recursivamente os caminhos, se o estouro da pilha retornar 1 (verdade) senão retornar None (falsey).

import sys
def k(s):
 a=len(s);m=[[c=='.'for c in b]*999for b in s.split('\n')]*999;sys.setrecursionlimit(a)
 for x in range(a*a):
  try:p(m,x/a,x%a)
  except:return 1
def p(m,x,y):
 if m[x][y]:m[x][y]=0;map(p,[m]*4,[x,x,x+1,x-1],[y+1,y-1,y,y])
Kyle Gullion
fonte
11
Você pode salvar alguns bytes usando ;para as linhas p, pois você os obterá na mesma linha que o if.
PurkkaKoodari
11
"Se Stack Overflow return true" - I como que a condição final recursão :)
schnaader
3
Não estou convencido de que essa seja uma abordagem válida. Usar estouros de pilha para detectar uma região "infinita" produzirá falsos positivos. A especificação do problema não indica nenhuma limitação nos intervalos de entrada, mas algo como um labirinto de 300 x 300 não parece irracional e pode incluir caminhos finitos muito longos.
Johne
4
Quase todos os labirintos finitos também causariam um estouro de pilha. Este não é um programa válido.
PyRulez 31/03
@johne Atualizado para que o limite de recursão seja da ordem do tamanho dos labirintos. Infelizmente, 34 bytes adicionados infelizmente, mas devem estar corretos agora (pelo menos o mais corretos possível como um hack).
Kyle Gullion
5

Python 2 - 297 286 275 bytes

Seleciona uma célula "aberta" arbitrária para iniciar um preenchimento de inundação. O labirinto é infinito se, durante o preenchimento, visitarmos novamente uma célula que já visitamos, mas ela tem uma coordenada diferente da visita anterior. Se o preenchimento de inundação preencher toda a região sem encontrar uma célula, tente uma região diferente. Se não for possível encontrar uma região, o labirinto é finito.

Leva o arquivo para processar na linha de comando, retorna o código de saída 1para infinito e 0finito.

Retorna resultados corretos para todos os casos de teste.

import sys
e=enumerate
C=dict([((i,j),1)for i,l in e(open(sys.argv[1]))for j,k in e(l)if'.'==k])
while C:
 d={}
 def f(r,c):
  n=(r%(i+1),c%j)
  if n in d:return(r,c)!=d[n]
  if C.pop(n,0):d[n]=(r,c);return any(map(f,[r-1,r,r+1,r],[c,c+1,c,c-1]))
 if f(*C.keys()[0]):exit(1)
Mac
fonte
11
Você não pode supor que alguma célula seja membro de uma caverna infinita; você pode facilmente ter cavernas infinitas e finitas.
VisualMelon
2
@VisualMelon: desculpe, a descrição não está totalmente correta. O código realmente verifica todas as regiões possíveis de células interconectadas, não apenas uma (como está atualmente implícito). É para isso que serve o loop while final - selecionar regiões para verificar enquanto ainda restam células não verificadas.
Mac