Encontre uma configuração de espelho para corresponder aos destinos do laser

13

PONTUAÇÃO ATUALIZADA : Como esse desafio é mais difícil do que eu previa, ajustei a pontuação. Um programa que pode resolver uma única entrada de espelho é uma resposta válida. Programas mais sofisticados recebem um bônus em sua pontuação.

Houve vários quebra-cabeças no PPCG para encontrar um caminho a laser em uma caixa de espelhos. Neste quebra-cabeça, você precisa criar uma caixa de espelhos para combinar com vários destinos de laser.

Você recebe uma caixa e especificações onde os lasers devem entrar e sair. Seu programa precisa colocar exatamente N espelhos de dupla face na caixa para atender às especificações. Os espelhos devem estar angulados a 45 graus, mas podem ser inclinados para a frente ou para trás.

Entrada

Seu programa deve aceitar uma grade de caixa via STDIN, argumento de linha de comando ou arquivo nos seguintes exemplos de formato:

+--G--+     +abcde+
G     |     f/////d
|    /|     a//   c
+-----+     f     |
            +-b-e-+

Os pares de letras ([a-zA-Z] podem ser usados) indicam a entrada / saída de até 52 lasers. Dentro da caixa haverá N /espelhos. As dimensões da caixa serão 3 <= W, H <= 200. A caixa é feita de +|-caracteres. Pode haver qualquer número de espelhos na caixa, incluindo zero.

Resultado

A saída deve corresponder à entrada, exceto que os /caracteres podem ser movidos e / ou alterados para \caracteres. Seu programa deve enviar uma seqüência de caixa de espelho correta para STDOUT ou um arquivo, seguindo a nova linha opcional. Se nenhum posicionamento de espelhos atender à especificação de entrada, faça a saída Impossible\n. Exemplos de possíveis soluções:

+--G--+     +abcde+
G  /  |     f \ \ d
|     |     a/ \  c
+-----+     f / //|
            +-b-e-+

Exemplo de teste

Entrada:

+abcdefghijklmnopqrstuvwxyA-+
|///////////////            |
|///////////////            |
|                           |
+-Abcdefghijklmnopqrstuvwxya+

Exemplo de saída:

+abcdefghijklmnopqrstuvwxyA-+
|\                         \|
|/                        / |
|\\\\\\\\\\\\\\\\\\\\\\\\\\ |
+-Abcdefghijklmnopqrstuvwxya+

Pontuação (ATUALIZADO)

Isso é código-golfe com bônus. Você deve indicar com sua resposta quantos espelhos seu programa pode resolver (N). Sua pontuação é o tamanho do seu programa em bytes dividido por N. Isso permite que as pessoas entrem com um programa simples, mas recompensa mais programadores de ambição com um bônus.

Lacunas padrão não permitidas.

Cavaleiro Lógico
fonte
3
Isso soa como um problema difícil, independentemente do golfe.
orlp
2
Dica: força bruta não é uma opção ; você terá três idades do universo em 10 mil opções por segundo para o exemplo maior.
Sanchises
@sanchises eu acho que vai demorar muito mais tempo, como qualquer espelho pode ser invertida, então eu acho que você precisa de um * 2^30componente de lá também
VisualMelon
Dica extra: você precisará explorar as propriedades do quebra-cabeça para remover seu espaço de pesquisa. Você também pode usar combinações de soluções parciais ou subidas a partir de soluções parciais próximas de uma solução completa. Agora é válido responder com soluções mais simples, para que programas que resolvam um ou dois quebra-cabeças espelhados também sejam bem-vindos.
Logic Knight

Respostas:

2

C # - 897 862 bytes

Foi encontrado um bug sério ao colocar espelhos em lugares que não podiam estar. Agora funciona, espero! Também jogava golfe leve, não podia deixar o loop while lá ... vergonhoso.

Programa completo, recebe entrada de STDIN, sai para STDOUT.

Foi muito divertido, ele lida bem com o problema 7 por 5 (e quando você remove um dos espelhos, tornando-o impossível), demorava cerca de 1 hora para resolver os 30 por 5.

using Q=System.Console;class P{static int w,L;static string S(char[]M,int t,int r,int i,int d,int[]B){var s="";if(r<0)return s;M=(char[])M.Clone();B=(int[])B.Clone();B[i]=1;for(i+=d;M[t]<48|t==i;i=t+(d=t<w?w:t>L-w?-w:t%w<1?1:-1))if(++t>=L){for(i=0;++i<L&r>0;)if(B[i]<1&M[i]<33){M[i]='.';r--;}return r<1?new string(M):s;}int c=M[i];if(c>32)s=c>47|c<46?s=c==M[t]?S(M,t,r,t,0,B):s:S(M,t,r,i,c<47?w/d:-w/d,B);else if((s=S(M,t,r,i,d,B))==""&B[i]<1){M[i]='.';s=S(M,t,r-1,i,w/d,B);if(s==""){M[i]='/';s=S(M,t,r-1,i,-w/d,B);}}return s;}static void Main(){string a,A="",R=A;for(;(a=Q.ReadLine())!=null;A+=a)L+=(w=a.Length);var G=A.ToCharArray();int r=0,i=L;for(;i>0;G[i]=G[i]=='|'?',':G[i])if(G[--i]==47|G[i]==92){r++;G[i]=' ';}a=S(G,0,r,1,w,new int[L]);if(a=="")R="Impossible\n";else for(;i<L;i+=w)R+=a.Substring(i,w)+"\n";Q.Write(R.Replace(".","\\").Replace(",","|"));}}

Exemplo 7 por 5:

+abcde+
f/////d
a//   c
f     |
+-b-e-+

+abcde+
f   \ d
a/  //c
f/ \ /|
+-b-e-+

Versão impossível:

+abcde+
f ////d
a//   c
f     |
+-b-e-+

Impossible

Algo diferente (o programa não olha para o layout do espelho original):

+a----+
|//// |
|/////|
|/////|
+----a+

+a----+
| /\\\|
|\\\\\|
|\\/\\|
+----a+

Solução 30 por 5:

+abcdefghijklmnopqrstuvwxyA-+
| \\\\\\\\\\\\\\\\\\\\\\\\ \|
| /                       //|
|\                         \|
+-Abcdefghijklmnopqrstuvwxya+

Ele olha para cada fonte de laser, por sua vez, e cria uma rota válida para ela (se puder), e depois passa para a próxima. É uma pesquisa bem simples e aprofundada, que precisa saber para qual fonte de laser (alvo) ela está olhando, quantos espelhos ela resta para colocar, a célula atual em que está "na", a direção em que está se movendo e cada célula já foi visitado (para não colocar um espelho em algum lugar que já esteve). Os últimos 3 são usados ​​para montar o caminho para o destino atual e redefinir quando o destino for alterado. Uma vez que todos os lasers estão conectados, ele segue em frente e preenche todas as lacunas que não precisam ser deixadas em branco (outra razão pela qual precisa saber em todos os lugares que é visitado).

Quando está construindo rotas, favorece o avanço da inserção de um espelho e, quando o faz, favorece o espelho "\" - isso é melhor visto no exemplo "algo diferente" acima, onde ignora a primeira célula abaixo da no topo da lista 'a', em seguida, preenche continuamente um "\" se ele puder encontrar uma solução com um, caso contrário, um "/" (naturalmente, se pular a primeira célula resultou na impossibilidade de encontrar uma solução, seria retroceda e tente colocar um espelho lá).

using Q=System.Console;

class P
{
    static int w,L;

    // M is cur grid
    // t is target edge thing (0->L)
    // r is mirrors remaining
    // i is pos
    // d is dir
    static string S(char[]M,int t,int r,int i,int d,int[]B)
    {
        var s="";

        if(r<0) // no mirrors left
            return s;

        // clone everything
        M=(char[])M.Clone();
        B=(int[])B.Clone();

        B[i]=1; // can't write to this

        for(i+=d; // move i
            M[t]<48|t==i; // only if target is something sensible (increment if i==t)
            i=t+(d=t<w?w:t>L-w?-w:t%w<1?1:-1)) // reflect, should be fine for w=3
            if(++t>=L) // run off the end
            {
                for(i=0;++i<L&r>0;) // don't need I any more (count through everything)
                    if(B[i]<1&M[i]<33) // not been here & it's open space
                    {
                        M[i]='.'; // doesn't matter
                        r--;
                    }
                return r<1?new string(M):s; // none remaining ? victory : defeat
            }

        int c=M[i];
        if(c>32) // not boring
            s=c>47|c<46? // hit edge
                s=c==M[t]? // hit the correct thing
                    S(M,t,r,t,0,B): // i+0=t, tells it to increment t
                    s
            :S(M,t,r,i,c<47?w/d:-w/d,B); // mirror
        else // boring
            if((s=S(M,t,r,i,d,B))==""&B[i]<1) // fwd
            {
                M[i]='.'; // use . instead of \
                s=S(M,t,r-1,i,w/d,B); // \
                if(s=="")
                {
                    M[i]='/';
                    s=S(M,t,r-1,i,-w/d,B); // /
                }
            }

        return s;
    }

    static void Main()
    {
        string a,A="",R=A; // R is free
        for(;(a=Q.ReadLine())!=null;A+=a) // read input
            L+=(w=a.Length); // note width, accumulate length

        var G=A.ToCharArray();

        int r=0,i=L; // count mirrors (I refuse to make these static)
        for(;i>0; // end on i=0
            G[i]=G[i]=='|'?',':G[i]) // replace | with ,
            if(G[--i]==47|G[i]==92) // remove and count mirrors
            {
                r++;
                G[i]=' '; // storing G[i] doesn't seem to save anything
            }

        // search
        a=S(G,0,r,1,w,new int[L]);

        if(a=="") // defeat
            R="Impossible\n";
        else // victory
            for(;i<L;i+=w) // for each line
                R+=a.Substring(i,w)+"\n";

        Q.Write(R.Replace(".","\\").Replace(",","|")); // swap back | and \
    }
}
VisualMelon
fonte
Ótima solução. De acordo com o novo sistema de pontuação que você marcar pelo menos 917/7 = 131.
Logic Cavaleiro
2

Python, 671 654 bytes

Não é uma solução, mas uma tentativa, leia abaixo.

import random as R
def V(F):
 for S,_x,_y in (F[0],0,1),(F[-1],0,-1),([L[0] for L in F],1,0),([L[-1] for L in F],-1,0):
  for i,C in enumerate(S):
   if not C in '+-|':
    x=_x;y=_y
    if not x: X=i;Y=y
    elif not y: Y=i;X=x
    while F[Y][X] != C:
     if F[Y][X]=='\\':x,y=y,x
     if F[Y][X]=='/':a=x+y;x,y=x-a,y-a
     X+=x;Y+=y
     try:
      if F[Y][X] in '+-|':return False
     except:
      return False
 return True
F=open(input()).read().split('\n')
while 1:
 _=[F[0]]+['\n'.join([L[0]+''.join([R.choice(' \\/')for i in range(len(F[0])-2)])+L[-1] for L in F[1:-1]])]+[F[-1]]
 if V(_):
  for l in _: print l
  break

Não joguei isso ao máximo, pois não estou satisfeito com a solução. Vvalida uma determinada solução percorrendo o campo Fpara cada caractere Cencontrado na linha lateral. As soluções são geradas aleatoriamente. É feio, funciona para a entrada1, mas leva muito tempo para as outras entradas. Como ele tenta soluções aleatoriamente, não considero que seja uma solução real para o problema em questão; mas pode ajudar os outros.

Corre: echo "entry1.txt" | python script.py

sentiao
fonte
1
Com o novo sistema de pontuação, essa é uma solução válida, mas não obtém um bônus de divisor (a menos que possa resolver problemas com 2 ou mais espelhos). Eu acho que você poderia otimizar isso removendo configurações inválidas anteriormente (por exemplo: cada coluna ou linha com uma letra na borda deve ter pelo menos um espelho - a menos que as letras correspondentes sejam opostas).
Logic Knight