Verificação da grade de palavras cruzadas


8

Valide uma grade de palavras cruzadas proposta.

As inscrições devem ser programas completos que simplesmente testam uma grade proposta para determinar se ela atende a um conjunto de condições para tornar felizes os solucionadores de palavras cruzadas.

Entrada

A entrada será o nome de um arquivo que representa a grade de palavras cruzadas. O nome do arquivo de entrada pode ser passado como argumento, na entrada padrão ou por outros meios convencionais que não sejam codificados.

Formato de arquivo de grade: A primeira linha consiste em duas constantes inteiras separadas por espaços em branco M e N. A seguir, há linhas M, cada uma composta por N caracteres (mais uma nova linha) selecionados [#A-Z ]. Esses caracteres são interpretados de forma a '#' indicar um quadrado bloqueado, ' 'um quadrado aberto no quebra-cabeça sem conteúdo conhecido e qualquer letra um quadrado aberto que contenha essa letra.

Resultado

O programa não deve produzir saída em grades válidas e sair com o estado de terminação normal. Se a grade proposta falhar, o programa deverá produzir uma mensagem de erro de diagnóstico e sair com um estado de finalização anormal (ou seja, não 0 no unix) se isso for suportado pelo seu ambiente de execução. A mensagem de erro deve indicar qual condição de validade é violada e a localização do quadrado incorreto; você é livre para escolher os meios de transmitir esses fatos.

Condições de validade

As grades válidas não terão respostas (cruzadas ou inativas) com apenas 1 caractere (crédito extra por tornar o tamanho mínimo um parâmetro de entrada) e exibirão a simetria usual. A simetria usual significa que as palavras cruzadas permanecem as mesmas depois (três descrições equivalentes da mesma operação):

  • reflexão através do seu próprio centro
  • reflexão vertical e horizontalmente
  • Rotação de 180 graus

Entrada de teste e saída esperada

Passes:

5   5
#  ##
#    
  #  
    #
##  #

Falha na resposta curta:

5   5
## ##
#    
  #  
    #
## ##

Falha na simetria:

5   5
#  ##
#    
  #  
#   #
##  #

a parte, de lado

Este é o segundo de vários desafios relacionados às palavras cruzadas. Pretendo usar um conjunto consistente de formatos de arquivo e criar um conjunto respeitável de utilitários relacionados a palavras cruzadas no processo. Por exemplo, um quebra-cabeça subsequente exigirá a impressão de uma versão ASCII das palavras cruzadas com base na entrada e saída desse quebra-cabeça.

Desafios anteriores desta série:


1
O requisito de simetria também se aplica ao conteúdo conhecido da grade ou apenas à estrutura (# ou não #)?
JB

Somente para a estrutura de quadrados bloqueados e em jogo.
dmckee --- ex-moderador gatinho

Oh, este já tinha uma recompensa. Vadio. Ainda assim, acho que duas respostas são poucas.
Joey

Simetria central e rotação de 180 ° são a mesma coisa - não são? Mas não vejo simetria vertical nem horizontal. Mas rotação de 90 °.
usuário desconhecido

Respostas:


4

Ruby - 215 207

t,*d=$<.map &:chop;n,d,e=t.size+1,(d*S=?#).gsub(/[^#]/,W=" "),->c,i{puts [c,i/n+1,i%n+1]*" ";exit 1}
0.upto(d.size-1){|i|d[i]==d[-i-1]||e[?R,i];d[i]==W&&(d[i-1]!=W&&d[i+1]!=W||d[i-n]!=W&&d[i+n]!=W)&&e[?L,i]}

Ungolfed:

h, *g = $<.map &:chop
w = h.size+1
g = (g*?#).gsub(/[^#]/," ")
error = ->c,i{ puts [c,i/w+1,i% w+1]*" "; exit 1 }
0.upto(size-1) { |i|
        error[?R, i] if g[i] != g[-i-1]
        error[?L,i] if g[i]==" " && (g[i-1]!=" " && g[i+1]!=" " || g[i-w]!=" " && g[i+w] != " ")
}

.

h, *g = $<.map &:chop

Isso basicamente remove o último caractere (quebra de linha) de cada linha de entrada chamando o chopmétodo neles e retornando uma matriz dos resultados.

hpega o primeiro elemento dessa matriz e *gpega o resto. Então, terminamos com a primeira linha he as linhas de grade de palavras cruzadas g.

g = (g*?#).gsub(/[^#]/," ")

g*?#junta-se ( *) à matriz gcom "#"( ?#é um caractere literal). É o mesmo que g.*("#"), ou g.join("#"). Então todo não #é substituído por um espaço.

Para a verificação de simetria, basta verificar se o caractere em cada índice é igual ao caractere no índice oposto na string:

0.upto(g.size-1) { |i| if g[i] != g[g.size - i - 1]; error() }

No Ruby, podemos indexar strings a partir do final usando índices negativos (começando em -1vez de 0), de modo que g[-i-1]é o oposto de g[i]na string. Isso economiza alguns caracteres:

0.upto(g.size-1) { |i| if g[i] != g[-i-1]; error() }

Podemos salvar um ;usando uma declaração condicional:

0.upto(g.size-1) { |i| error() if g[i] != g[-i-1] }

Na versão golfed, podemos salvar mais alguns caracteres:

0.upto(g.size-1){|i|g[i]==g[-i-1]||error()}

Em uma versão anterior, usei recursão para iterar sobre a string:

(f=->i{g[i]&&f[i+1]&&g[i]!=g[-i-1]&&error()})[0]

Um acesso fora do limite para gretornos nulos; portanto, uma vez g[i]retornado nil, isso interrompe a iteração.

Formato de saída:

{ L | R } line-number column-number

L para erros de comprimento e R para erro de rotação ( L 1 2significa erro de comprimento na linha 1, coluna 2)


Gostaria de explicar um pouco para aqueles de nós que não falam rubi? Posso ver como você removeu os não-negros dos quadrados em jogo e como a verificação do tamanho da resposta funciona (bom, BTW), mas não estou recebendo a verificação de simetria.
dmckee --- ex-moderador gatinho

Vejo aqui um problema de como a largura da grade é determinada - assumindo o comprimento da 1ª linha. Isso está incorreto, ele funcionará apenas no exemplo em que essa linha é "5-5" (três espaços no meio). Se fosse "5 5", não funcionaria!
Nas Banov 5/03

Também acho que há um problema com o "contorno vertical", ao passar pela 1ª linha e olhar uma linha para baixo (+ n) e uma linha para cima (-n) - a última procurará a última linha e poderá erroneamente pegar espaço a partir daí!
Nas Banov 5/03

Bem, você está certo quanto à largura da grade; Eu assumi que na primeira linha, o segundo número está sempre alinhado ao final da linha.
Arnaud Le Blanc

1

Implementação de referência

c99

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void readgrid(FILE *f, int m, int n, char grid[m][n]){
  int i = 0;
  int j = 0;
  int c = 0;
  while ( (c = fgetc(f)) != EOF) {
    if (c == '\n') {
      if (j != n) fprintf(stderr,"Short input line (%d)\n",i);
      i++;
      j=0;
    } else {
      grid[i][j++] = c;
    }
  }
}

int isSymmetric(int m, int n, char g[m][n]){
  for (int i=0; i<m; ++i)
    for (int j=0; j<n; ++j)
      if ( (g[i][j]=='#') != (g[m-i-1][n-j-1]=='#') )
    return j*m+i;
  return -1;
}

int isLongEnough(int m, int n, char g[m][n], int min){
  /* Check the rows */
  for (int i=0; i<m; ++i) {
    int lo=-(min+1); /* last open square */
    int lb=-1;       /* last blocked square */
    for (int j=0; j<n; ++j) {
      if ( g[i][j] == '#' ) {
    /* blocked square */
    if ( (lo == j-1) && (j-lb <= min+1) ) return lo*m+i;
    lb=j;
      } else {
    /* In-play square */
    lo=j;
      }
    }
  }

  /* Check the columns */
  for (int j=0; j<n; ++j) {
    int lo=-(min+1); /* last open square */
    int lb=-1;       /* last blocked square */
    for (int i=0; i<m; ++i) {
      if ( g[i][j] == '#' ) {
    /* blocked square */
    if ( (lo == i-1) && (i-lb <= min+1) ) return lo*m+i;
    lb=i;
      } else {
    /* In-play square */
    lo=i;
      }
    }
  }

  /* Passed */
  return -1;
}

int main(int argc, char** argv){
  const char *infname;
  FILE *inf=NULL;
  FILE *outf=stdout;
  int min=1;

  /* deal with the command line */
  switch (argc) {
  case 3: /* two or more arguments. Take the second to be the minium
         answer length */
    if ( (min=atoi(argv[2]))<1 ) {
      fprintf(stderr,"%s: Minimum length '%s' too short. Exiting.",
          argv[0],argv[2]);
      return 2;
    }
    /* FALLTHROUGH */
  case 2: /* exactly one argument */
    infname = argv[1];
    if (!(inf = fopen(infname,"r"))) {
      fprintf(stderr,"%s: Couldn't open file '%s'. Exiting.",
          argv[0],argv[1]);
      return 1;
    };
    break;
  default:
    printf("%s: Verify crossword grid.\n\t%s <grid file> [<minimum length>]\n",
       argv[0],argv[0]);
    return 0;
  }

  /* Read the grid size from the first line */
  int m=0,n=0,e=-1;
  char lbuf[81];
  fgets(lbuf,81,inf);
  sscanf(lbuf,"%d %d",&m,&n);

  /* Intialize the grid */
  char grid[m][n];
  for(int i=0; i<m; ++i) {
    for(int j=0; j<n; ++j) {
      grid[i][j]='#';
    }
  }

  readgrid(inf,m,n,grid);

  if ((e=isSymmetric(m,n,grid))>=0) {
    fprintf(stderr,"Symmetry violation at %d,%d.\n",e/m+1,e%m+1);
    return 4;
  } else if ((e=isLongEnough(m,n,grid,min))>=0) {
    fprintf(stderr,"Short answer at %d,%d.\n",e/m+1,e%m+1);
    return 8;
  }
  return 0;
}

1

C, 278 caracteres

char*f,g[999],*r=g;i,j,h,w;main(e){
for(fscanf(f=fopen(gets(g),"r"),"%*d%d%*[\n]",&w);fgets(g+h*w,99,f);++h);
for(;j++<h;)for(i=0;i++<w;++r)if(e=*r==35^g[g+w*h-r-1]==35?83:
*r==35||i>1&r[-1]!=35|i<w&r[1]!=35&&j>1&r[-w]!=35|j<h&r[w]!=35?0:76)
exit(printf("%d%c%d\n",j,e,i));exit(0);}

Como seria de esperar, as próprias mensagens de erro foram jogadas no golfe. Eles assumem a seguinte forma:

11L8 - indica um erro de comprimento na linha 11, coluna 8

4S10 - indica um erro de simetria na linha 4, coluna 10


1

APL (115)

{∨/,k←⍵≠⌽⊖⍵:'⍉',(,k×⍳⍴k)~⊂2/0⋄×⍴d←(,⊃,/(g(⍉g←⍳⍴⍵))×{k↑1⌽1⊖0 1 0⍷¯1⌽¯1⊖⍵↑⍨2+k←⍴⍵}¨⍵(⍉⍵))~⊂2/0:'∘',d}d↑↑{'#'≠⍞}¨⍳⊃d←⎕

Se a grade não for simétrica, ela sai seguida pelas coordenadas, ou seja, por exemplo, fornece

2 5 4 1
Se a grade tiver respostas curtas, ela sai seguida pelas coordenadas, ou seja, por exemplo, fornece
1 2 5 2

Explicação:

  • d↑↑{'#'≠⍞}¨⍳⊃d←⎕: leia a primeira linha como uma lista de números e armazene-a d, depois leia quantas linhas como o primeiro número e reformule como uma matriz de tamanho d. Os quadrados 'fechados' são armazenados como 0 e os quadrados 'abertos' como 1.
  • ∨/,k←⍵≠⌽⊖⍵: armazene nos klocais onde a grade é assimétrica. Se existe um lugar assim ...
  • '⍉',(,k×⍳⍴k)~⊂2/0: produza a ⍉ seguido pelas coordenadas incorretas
  • : de outra forma...
  • ~⊂2/0: remova as coordenadas zero da seguinte lista:
  • ¨⍵(⍉⍵): para a grade e sua transposição ...
  • ¯1⌽¯1⊖⍵↑⍨2+k←⍴⍵: rode a grade com zeros (quadrados fechados)
  • 0 1 0⍷: veja onde ele contém um quadrado 'aberto' delimitado por dois quadrados 'fechados' (= muito curto)
  • k↑1⌽1⊖: remova o anel de zeros extras novamente
  • ,⊃,/(g(⍉g←⍳⍴⍵))×: multiplique por coordenadas e coordenadas transpostas e junte-se para formar uma lista de coordenadas incorretas (e muitos zeros que removemos).
  • ×⍴d←: armazene as coordenadas incorretas de, se houver alguma ...
  • :'∘',d: produza a ∘ seguido pelas coordenadas.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.