Gerar uma grade de palavras cruzadas reproduzível

8

Escreva um programa para produzir um arquivo contendo uma grade de palavras cruzadas na qual o usuário possa imprimir e trabalhar o quebra-cabeça.

Entrada

Um nome de arquivo representando um arquivo de grade de palavras cruzadas e, opcionalmente, um segundo nome de arquivo representando um arquivo de numeração de palavras cruzadas. A entrada deve ser aceita por um meio convencional para o seu ambiente de programação: argumentos de linha de comando, entrada padrão, formulários da web etc.

Você pode assumir que as palavras cruzadas foram validadas e se estiver usando um arquivo de numeração que corresponda à grade fornecida .

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.

Formato do arquivo de numeração As linhas que começam com '#' são ignoradas e podem ser usadas para comentários. Todas as outras linhas contêm uma guia separada tripleto i, m, nonde irepresenta um número a ser impresso sobre a grade, e me nrepresentam a linha e a coluna do quadrado onde deve ser impresso. O número de linhas e colunas começa em 1.

Resultado

A saída será um arquivo no qual o usuário poderá imprimir e trabalhar com palavras cruzadas. Serão aceitos ASCII, postscript, pdf, png e qualquer outro formato razoável, mas todos devem respeitar estas regras:

  1. Deve haver uma regra em torno de todo o quebra-cabeça e entre cada par de quadrados.
  2. Quadrados bloqueados devem ser preenchidos sombriamente.
  3. No quadrado de jogo que representa o início de uma pista numerada (do outro lado ou para baixo) deve ser fornecido com um número no canto superior esquerdo do quadrado, deixando a maior parte do quadrado em branco para a peça escrever. Observe que a grade típica publicado nos jornais terá muitas dezenas de pistas e poderá ter mais de 100.

A saída será apenas da grade, sem a lista de pistas.

A saída deve ser enviada para um destino convencional (um arquivo cujo nome é derivado do nome do arquivo de entrada, produzido como uma página da web, etc.)

Caso de teste

Dada uma contribuição de

5   5
#  ##
#    
  #  
    #
##  #

o canto inicial de uma saída ASCII aceitável pode se parecer com isso

+-----+-----+-----+---
|#####|1    |2    |###
|#####|     |     |###
|#####|     |     |###
+-----+-----+-----+---
|#####|3    |     |4  
|#####|     |     |   
|#####|     |     |   
+-----+-----+-----+---
|6    |     |#####|   
|     |     |#####|   

Quem usa formatos gráficos deve se inspirar nas fontes impressas usuais.

Esquema de numeração

Uma grade numerada corretamente possui as seguintes propriedades:

  1. A numeração começa em 1.
  2. Nenhuma coluna ou extensão de quadrados abertos é numerada.
  3. Os números serão encontrados em ordem de contagem, digitalizando da linha superior para a inferior, levando cada linha da esquerda para a direita.

a parte, de lado

Este é o terceiro 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.

Desafios anteriores desta série:

dmckee --- gatinho ex-moderador
fonte
Não posso comentar, mas o exemplo de saída viola seu próprio esquema de numeração.
Megan Walker
@Samuel: O mesmo acontece. É o que eu ganho por escrevê-lo à mão, em vez de olhar para o meu próprio trabalho. Obrigado. Corrigido.
dmckee --- ex-moderador gatinho

Respostas:

4

Python, 379 caracteres

import sys
A=sys.argv
f=open(A[1])
V,H=map(int,f.readline().split())
M={}
if A[2:]:
 for r in open(A[2]).readlines():n,y,x=map(int,r.split());M[y*H+y+x]=n
R='+-----'*H+'+'
n,v,s='\n| '
x=y=z=''
p=V+1
for c in n+''.join(f):
 if c==n:print x+n+y+n+z+n+R;x=y=z=''
 elif'@'>c:x+=5*c;y+=5*c;z+=5*c
 else:x+=5*s;y+=s+s+c+s+s;z+=5*s
 if p in M:x=x[:-5]+"%-5d"%M[p]
 x+=v;y+=v;z+=v;p+=1
Keith Randall
fonte
Funciona bem desde que não haja comentários no arquivo de numeração.
dmckee --- ex-moderador gatinho
Você pode usar em next(f)vez de f.readline(). Você não precisa .readlines()disso.
Gnibbler
10

Postscript 905 797 677 675 629 608 330 320 308

{G N}/Times-Roman .3 2 22 1 30/.{<920>dup 1 4 3 roll put cvx 
exec}def/${//. 73 .}def[/R{99<a51f3e7d75>$}/G{<1fab75>$ 
R(uN)${0 R{1(X)$ 0 1 -1 5 4 roll 35 eq{4<1980>$}if<81>$ 
1 add}(I)$(u)$ 0 -1<ad>$}<834d>$}/X{{exit}if}/N{-.9
.7<ad>${<1fab70>$ X}loop{{(>nk)$(  )<31a0>$}<a3>$
X}loop}>><0d38388b369bad8e3f>$

Este programa é escrito como um "protocolo de protocolo", para que você possa agrupá-lo juntamente com os arquivos de grade e número (nessa ordem, separados por linhas em branco) e canalizar toda a bagunça para o ghostscript, Distiller ou uma impressora PS. Anexado à versão de referência abaixo está um quebra-cabeça do NYT (de 5 de novembro de 2011) com números e uma resposta que eu tenho certeza (os sábados são difíceis!).

A nova revisão usa esses dois procedimentos para executar nomes de sistemas codificados em binários a partir de strings.

/.{
    <920>  % two-byte binary-encoded name template with 0x92 prefix
    dup 1 4 3 roll put  % insert number into string
    cvx exec  % and execute it
}def
/${
    //.   %the /. procedure body defined above
    73 .  %"forall" (by code number)
}def

Recuado e (um pouco) comentado.

/Times-Roman .3 2 22 1 30
/.{<920>dup 1 4 3 roll put cvx exec}def/${//. 73 .}def
[
/R{99<a51f3e7d75>$}    %currentfile 99 string readline pop 
/G{<1fab75>$ %currentfile token pop 
    R (uN)$ %<754e>$ %pop gsave
    {   
        0 R { 
            1 (X)$ %index
            0 1 -1 5 4 roll
            35 eq{ 
                4<1980>$ %copy rectfill
            }if 
            <81>$ %rectstroke
            1 add 
        }(I)$
        (u)$ % 73 . %forall pop 
        0 -1<ad>$ %translate
    }<834d>$ %repeat grestore
}
/X{{exit}if}
/N{
    -.9 .7<ad>$ %translate
    %{ currentfile token not {exit} if } loop
    {<1fab70>$
        X %{exit}if
    }loop
    {   
        %dup type/integertype ne{exit}if
        {
            (>nk)$ %<3e6e6b>$ %exch neg moveto
            (  )<31a0>$ %cvs show
        }<a3>$ %stopped
        X %{exit}if
    }loop
}
>>
<0d38388b369bad8e>$
%begin dup dup scale div setlinewidth translate selectfont
{G N}exec

Arquivos de dados.

15 15
     #   #     


       #     ##
##   #   #     
    #     #    
   #    #      
       #       
      #    #   
    #     #    
    K#   #   ##
##  I  #       
    L          
    N          
    S#   #     

#i m n   figure(number), row, col
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
6 1 7
7 1 8
8 1 9
9 1 11
10 1 12
11 1 13
12 1 14
13 1 15
14 2 1
15 2 6
16 2 10
17 3 1
18 4 1
19 4 9
20 5 3
21 5 7
22 5 8
23 5 11
24 5 14
25 5 15
26 6 1
27 6 2
28 6 6
29 6 10
30 6 12
31 7 1
32 7 5
33 7 10
34 7 11
35 8 1
36 8 4
37 8 9
38 9 1
39 9 8
40 9 13
41 10 1
42 10 6
43 10 7
44 10 12
45 11 1
46 11 5
47 11 7
48 11 11
49 12 3
50 12 6
51 12 9
52 12 10
53 12 14
54 12 15
55 13 1
56 13 2
57 13 8
58 14 1
59 15 1
60 15 7
60 15 11

A impressora deve parecer boa, mas na tela ela precisa de uma pequena ajuda. Esse procedimento de 19 caracteres e 9 caracteres para invocá-lo em todos os pontos de espaço do usuário ajuda a tornar as linhas com espaçamento uniforme mais uniformes. Então 308 + 19 + 9 = 337, usado para gerar esta imagem.

/F{<ac893e893e5f>$} % transform round exch round exch itransform

saída de palavras cruzadas

Postscript 608

Esta versão anterior (da revisão 8 ) usa uma abordagem completamente diferente, reutilizando o código da linha principal como um "léxico" do qual tokens mais longos podem ser indexados usando seqüências de caracteres.

<<-1{{30 700 translate 0 0 moveto
currentfile 2{(@A1*)*}repeat exch string
3 2 roll{('BO1)*{( )(@#)*
4(,.N:;<%)*<<( ){p}/#{(1:;>%)*}0{(;,'.)*
6 -26(CE%)*}>>(IJ'B)* known not{p
0}if(LG #C)*}forall(#;*1 D%)*}repeat
p/Times-Roman 8 selectfont 99
string{('BO)*{(@)* length(#=)*{p}{(@#L)*
35(=)*{p}{cvx(GID ?'H*ID ?KHF%)*(   )cvs(E)*}e}e}{showpage
exit}e}loop exit{( @#M# FMF#M)*
closepath}currentpoint stroke eq fill
mul dup token copy rmoveto sub show
neg exec add 1 index 9 get rlineto
put readline}loop}0 1 index 0 get{1
index 1 add}forall pop/*{{32 sub load
exec}forall}/e{ifelse}/p{pop}>>begin(23,?4)*<1f>*

Foi escrito usando esta versão comentada que ilustra a codificação do léxico. O primeiro token 30é comentado, spaceportanto, ( )*é sinônimo de 30. Não é muito benéfico para 30, mas para tokens mais longos essa é (foi) uma grande vitória (até que possibilidades mais profundas de codificação sejam (foram) descobertas).

<<-1{{
%space  !    "         # $ %      &           '(        )     %*    +      , - .   %/
 30     700  translate 0 0 moveto currentfile 2{(@A1*)*}repeat exch string 3 2 roll
{('BO1)*{( )(@#)* 4(,.N:;<%)*<<( ){p}/#{(1:;>%)*}0{(;,'.)* 6 -26(CE%)*}>>(IJ'B)*
known not{p 0}if(LG #C)*}forall(#;*1 D%)*}
%0      1   2          3 4          5  6     7
 repeat p /Times-Roman 8 selectfont 99 string{('BO)*{(@)* length(#=)*{p}{
(@#L)* 35(=)*{p}{cvx(GID ?'H*ID ?KHF%)*(   )cvs(E)*}e}e}{showpage clear exit}e}
%8    9   :                         %;            <      =    >    ?
 loop exit{( @#M# FMF#M)* closepath} currentpoint stroke eq fill mul
%@   A     B    C       D   E    F   G    H   I J     K L   M       N   O
 dup token copy rmoveto sub show neg exec add 1 index 9 get rlineto put readline
}loop}0 1 index 0 get{1 index 1 add}forall
pop/*{{32 sub load exec}forall}/e{ifelse}/p{pop}>>begin(23,?4)*<1f>*
luser droog
fonte
1
Você não precisa manter todas as versões antigas, porque as pessoas podem acessar o histórico da postagem, se estiverem realmente interessadas.
Peter Taylor #
Entendido. Vou aparar um pouco de gordura quando atualizar.
Luser droog 7/11
Publiquei algumas versões alternativas neste tópico .
luser Droog
Invocação funciona como?
usuário desconhecido
1
Parece que a sobrecarga para o PostScript no estilo de golfe ainda é maior que minha solução "tradicional" ;-).
Thomas W.
4

C (saída para SVG), 553 caracteres

Eu sei, o código é enorme, mas esse problema está implorando por uma resposta SVG.

char*f,b[99];h,w;main(i){fscanf(f=fopen(gets(b),"r"),"%d%d%*[\n]",&h,&w);for(
printf("<svg xmlns='http://www.w3.org/2000/svg' viewBox='.9 .9 %d.2 %d.2'><path d='M1 1",
i=w,h);i--;)printf("v%dv-%dh1",h,h);for(;h--;)printf("v1h-%dh%d",w,w);for(
puts("' style='fill:none;stroke:#000;stroke-width:.04'/><path d='");
fgets(b,99,f);++h)for(i=0;i<w;)b[i++]-35||printf("M%d %dh1v1h-1Z",i,h+2);puts("'/>");
for(f=fopen(gets(b),"r");fgets(b,99,f);)sscanf(b,"%d%d%d",&i,&h,&w)>2&&
printf("<text x='%d.1' y='%d.3' style='font-size:.3px'>%d</text>",w,h,i);puts("</svg>");}

Quando executado, obtém os dois nomes de arquivos em duas linhas separadas de entrada padrão; primeiro o arquivo de grade, depois o arquivo de números.

A lógica desta é realmente bastante simples. O formato do SVG permite criar todos os elementos em qualquer ordem (em vez de ir de cima para baixo, como na solução de saída ASCII). O tamanho é devido quase inteiramente ao clichê SVG.

Mas a imagem resultante está ótima!

Editado para adicionar: aqui está uma versão mais curta (517 caracteres) que gera uma resolução específica. Isso permite que o código use mais configurações padrão, mas ao custo proibitivo (a meu ver) que o SVG não redimensiona mais automaticamente no seu navegador da web.

char*f,b[99];h,w;main(i){fscanf(f=fopen(gets(b),"r"),"%d%d%*[\n]",&h,&w);for(
printf("<svg xmlns='http://www.w3.org/2000/svg'><path d='M1 1",i=w,h);i--;)
printf("v%d0v-%d0h50",h*5,h*5);for(;h--;)printf("v50h-%d0h%d0",w*5,w*5);for(
puts("' style='fill:none;stroke:#000'/><path d='");fgets(b,99,f);++h)
for(i=-1;++i<w;)b[i]-35||printf("M%d1 %d1h50v50h-50Z",i*5,h*5+5);puts("'/>");
for(f=fopen(gets(b),"r");fgets(b,99,f);)sscanf(b,"%d%d%d",&i,&h,&w)>2&&
printf("<text x='%d3' y='%d5'>%d</text>",w*5-5,h*5-4,i);puts("</svg>");}
caixa de pão
fonte
O SVG está ótimo!
usuário desconhecido
3

Haskell, 328 caracteres

import System
main=do(g:d)<-mapM(fmap lines.readFile)=<<getArgs;mapM_ putStrLn$g% \i k->[t|x<-d,y@(c:_)<-x,c/='#',(t,q)<-lex y,w q==i,k<1]
(s:g)%n=[q|(i,x)<-e g,q<-b s:[c['|':f#n[i,j]k|(j,f)<-e x]++"|"|k<-[0..2]]]++[b s]
'#'#_="#####";_#n=take 5$c n++"     ";b n='+':([1..w n!!1]>>"-----+")
e=zip[1..];c=concat;w=map read.words
hammar
fonte
2

C, 375 caracteres

char b[99];g[999],*r=g,*f,i,j,w;main(n){
for(fscanf(f=fopen(gets(b),"r"),"%*d%d%*[\n]",&w);fgets(b,99,f);)
for(i=0;i<w;)*r++=-(b[i++]==35);
for(f=fopen(gets(b),"r");fgets(b,99,f);)
sscanf(b,"%d%d%d",&n,&j,&i)?g[j*w-w+i-1]=n:0;
for(f=g;f<=r;f+=w){for(i=w;i--;)printf(" ----");puts("");
if(f<r)for(j=3;j--;puts("|"))
for(i=0;i<w;printf(~n?n&&j>1?"|%-4d":"|    ":"|////",n))n=f[i++];}}

Os dois nomes de arquivos de entrada são inseridos na entrada padrão, cada um em uma linha separada. A grade é renderizada em ASCII na saída padrão. Sim, é uma interface de usuário ruim, mas qualquer coisa melhor custa caracteres. Comecei a invocá-lo assim:

printf "%s\n%s" grid.txt numbering.txt | ./crosswd-render > render.txt

O programa deve lidar corretamente com coisas como linhas comentadas no arquivo de numeração.

caixa de pão
fonte
*r++-=b[i++]==35( gé inicializado com zeros).
Ugoren
for(j=3*(f<r);j--;puts("|"))salva if.
Ugoren
n&&j>1->j/2*n
ugoren
2

Scala 463, formato de saída: html

object H extends App{val z=readLine.split("[ ]+")map(_.toInt-1)
val d="\t\t<td width='50' height='50'"
println("<html><table border='1'><tr>")
val b=(0 to z(0)).map{r=>readLine}
var c=0
(0 to z(0)).map{
y=>(0 to z(1)).map{
x=>if(b(y)(x)==' '&&((x==0||b(y)(x-1)==35)||(y==0||b(y-1)(x)==35))){
c+=1
println(d+"valign='top'>"+c+"</td>")}
else println(d+{if(b(y)(x)!=' ')"bgcolor='#0'>"else">&nbsp;"}+"</td>")}
println("\t</tr>\n\t<tr>")}
println("</table></html>")
}

Saída de amostra

Usuário desconhecido
fonte
Muito bonito. A saída parece boa .
dmckee --- gatinho ex-moderador
2

PostScript (435) (434)

[/r{currentfile 999 string readline pop}/p{pop}/x{exch}/T{translate}/S{scale}/G{gsave}/R{grestore}/_( )>>begin/Courier 1 selectfont 
20 20 S
.05 setlinewidth{r token
p x p
dup 3 x 3 add
T
G G{R
0 -1 T
G
_ 0
r{0 0 1 1
4 index 35 eq{rectfill p}{rectstroke
put
.3 .1 moveto
_ show}ifelse
1 0 T _ 0}forall}repeat
R R
1 -1 S -.9 -.7 T{{r
dup 0 get 35 ne{( )search
p 3 2 roll
cvx exec
G
x
T
.4 -.4 S
0 0 moveto show
p
R}if}loop}stopped}exec

Sem jogar golfe com dados:

%!
<<
  /r{currentfile 999 string readline pop}
  /p{pop}
  /x{exch}
  /T{translate}
  /S{scale}
  /G{gsave}
  /R{grestore}
  /_( )
>>begin
/Courier 1 selectfont
% In theory, 20 20 scale isn't needed, 
% but it would make the whole thing very tiny when printed
% (on screen it doesn't matter too much, it can be zoomed)
20 20 S
.05 setlinewidth
{ % exec
% Read number of lines
r token                          % restString numberOfLines true
% Discard rest of line (Contains number of columns.
% It becomes clear implicitly from number of letters in a line of grid definition)
p x p                            % numberOfLines
% Move to where the top line starts
dup 3 x 3 add                    % numberOfLines x y
T                                % numberOfLines
G G
{ %repeat
  R
  % Move to next line
  0 -1 T
  G
  _ 0
  r                              % ( ) 0 line
  { %forall                      % ( ) 0 char
    0 0 1 1                      % ( ) 0 char 0 0 x y
    % Check whether char is a hash
    4 index 35 eq{ %ifelse
      4 copy rectfill
    }if
    rectstroke                   % ( ) 0 char
    put                          % -/-
    .3 .1 moveto
    _ show
    1 0 T
    _ 0                          % ( ) 0
  }forall                        % 
}repeat
R R
% Now, render the numbers according to the numbering definitions
1 -1 S -.9 -.7 T
{{
  r
  %Check whether this is a comment
  dup 0 get 35 ne{               % line
    % Split at the first tab
    %TODO: Ust tab instead of space
    ( )search                    % (y x) tab number true
    p 3 2 roll                   % tab number (y x)
    cvx exec                     % tab number y x
    G
    x                            % tab number x y
    T                            % tab number
    .4 -.4 S
    0 0 moveto show              % tab
    % This pop can be eliminated in theory to save two characters,
    % but the operand stack will continue to grow
    p
    R
  }if
}loop}stopped
}exec
15 15
     #   #     


       #     ##
##   #   #     
    #     #    
   #    #      
       #       
      #    #   
    #     #    
    K#   #   ##
##  I  #       
    L          
    N          
    S#   #     
#i m n   figure(number), row, col
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
6 1 7
7 1 8
8 1 9
9 1 11
10 1 12
11 1 13
12 1 14
13 1 15
14 2 1
15 2 6
16 2 10
17 3 1
18 4 1
19 4 9
20 5 3
21 5 7
22 5 8
23 5 11
24 5 14
25 5 15
26 6 1
27 6 2
28 6 6
29 6 10
30 6 12
31 7 1
32 7 5
33 7 10
34 7 11
35 8 1
36 8 4
37 8 9
38 9 1
39 9 8
40 9 13
41 10 1
42 10 6
43 10 7
44 10 12
45 11 1
46 11 5
47 11 7
48 11 11
49 12 3
50 12 6
51 12 9
52 12 10
53 12 14
54 12 15
55 13 1
56 13 2
57 13 8
58 14 1
59 15 1
60 15 7
60 15 11
Thomas W.
fonte
Impressionante. Eu realmente preciso fazer melhor uso stopped. ... Vou deixar a recompensa em aberto por um dia ou mais para atrair atenção.
Luser droog
1

Postscript, não combatente.

Inspirado (mais uma vez) por sua pergunta relacionada ao SO , criei uma versão de referência no Postscript usando o arquivo IO. Ele também cria uma fonte derivada de largura fixa para que os dados da grade sejam simplesmente transmitidos show. é uma caixa vazia e #é uma caixa cheia. Qualquer outro caractere ascii é desenhado como um pequeno glifo Times-Romano cercado por uma caixa.

Este programa utiliza um recurso ghostscript que pode não estar presente em todos os intérpretes Postscript. Se o ghostscript for chamado com a --opção, ele passará argumentos da linha de comando para o programa postscript em uma matriz de cadeias denominada / ARGUMENTS. Então, você precisa invocar o programa assim gs -- xw-io.ps grid-file number-file.

%!

ARGUMENTS{}forall
/numfile exch (r) file def
/gridfile exch (r) file def

/q{0 0 moveto 1 0 lineto 1 1 lineto 0 1 lineto closepath}def
/X<</FontType 3/FontBBox[0 0 1 1]/FontMatrix[1 0 0 1 0 0]
    /Encoding StandardEncoding
    /BuildChar{
        1 0 0 0 1 1 setcachedevice
        .001 setlinewidth
        q stroke
        dup 35 eq { pop
            q fill
        }{
            .3 .1 moveto
            .1 .1 scale
            /Times-Roman 8 selectfont
            (?)dup 0 4 3 roll put show
        }ifelse pop}
>>definefont pop /X 30 selectfont
40 700 moveto {
    gridfile 2{dup token pop exch}repeat pop pop
    {
        gridfile =string readline{
            dup length 0 ne{
                show currentpoint exch pop 30 sub 40 exch moveto
            }{clear exit}ifelse
        }{clear exit}ifelse
    }loop
    /Times-Roman 8 selectfont
    {
        40 700 moveto
        numfile =string readline{
            dup length 0 ne{
                dup 0 get 35 ne{
                    cvx exec
                    1 sub 30 mul 2 add exch
                    1 sub -30 mul 22 add rmoveto
                    (   )cvs show
                }{clear}ifelse
            }{clear}ifelse
        }{clear exit}ifelse
    }loop showpage
}exec
luser droog
fonte
Parece ótimo. Quão grande é isso? Conto 1247 caracteres. Mas pode ser jogado de golfe - não é? Uma tradução simples de 4 espaços em branco => 1 guia leva a 980 caracteres, eliminando todas as guias para 891. #
user unknown
Devido a todas as palavras especiais necessárias para criar uma fonte, esta não pode ser menor que minha outra resposta pós-escrita.
luser Droog