Geração de quebra-cabeças de busca de palavras


13

Dada uma lista de cadeias, encontre a menor matriz quadrada que contém cada uma das cadeias iniciais. As strings podem aparecer na horizontal, na vertical ou na diagonal e para a frente ou para trás, como nesta pergunta Word Search Puzzle .

As palavras devem ser colocadas no quadrado, com pelo menos uma palavra em cada direção (horizontal, vertical e diagonal). As palavras devem aparecer apenas uma vez.

Portanto, a entrada é apenas uma lista de palavras. Por exemplo: CAT, TRAIN, CUBE, BICYCLE. Uma solução possível é:

B N * * * * *
* I * * C A T
* A C * * * *
* R * Y * * C
* T * * C * U
* * * * * L B
* * * * * * E

Substituí o preenchimento de cartas por asteriscos apenas para maior clareza. A saída desejada deve incluir letras de preenchimento aleatórias.


Cada palavra deve ser encontrada em apenas uma posição (como pesquisas de palavras comuns)? Por exemplo, a letra deixada ACno seu exemplo faria outra, CATse for T.
Geobits

Não está claro para mim o que exatamente você quer dizer com " Palavras devem ser colocadas aleatoriamente em todas as direções ". Uma resposta atenderia a esse critério se, ao definir as palavras deterministicamente, selecionasse aleatoriamente uma das oito simetrias do quadrado? Ou, no outro extremo, a saída deve ser selecionada uniformemente entre todos os menores quadrados possíveis que contenham as palavras? Ou a linha é traçada em algum lugar entre esses extremos?
Peter Taylor

Sim, deve ser encontrado apenas em uma posição.
Migue

1
Não, realmente não. Ainda não estou claro qual é o espaço do qual uma resposta deve amostrar aleatoriamente, nem quanta flexibilidade é permitida na ponderação dos elementos desse espaço.
Peter Taylor

1
A partir de agora, a questão tem entradas que não podem ser resolvidas, mas nenhuma menção é feita sobre como você deve lidar com isso. Por exemplo, o conjunto A B C D E F G H I J K L M N O P Q R S T U V W X Y Znão tem solução.
Ou orp

Respostas:


6

JavaScript (ES6), 595628680

Editar Alguma limpeza e mesclagem:
- função P mesclada dentro da função R
- calc x e z no mesmo .map
- quando a solução for encontrada, defina x como 0 para sair do loop externo
- definição e chamada mescladas de W

Edit2 mais golfe, preenchimento aleatório reduzido, loop externo revisado ... veja a história para algo mais legível

Diferentemente da resposta aceita, isso deve funcionar para a maioria das entradas. Apenas evite palavras de letra única. Se uma saída for encontrada, é ideal e use todas as três direções.

A restrição de evitar repetir palavras é muito difícil. Eu tive que procurar por palavras repetidas em cada etapa, adicionando palavras à grade e em cada caractere de preenchimento aleatório.

Subfunções principais:

  • P (w) verdadeiro se palavra palíndromo. Uma palavra palindrom será encontrada duas vezes ao procurar por palavras repetidas.

  • R (s) verifica palavras repetidas na grade s

  • Q (s) preenchem a grade s com caracteres aleatórios - são recursivos e retornam em caso de repetição de palavras - e podem falhar.

  • W () recursivo, tente preencher uma grade de determinado tamanho, se possível.

A função principal usa W () para encontrar uma grade de saída, tentando desde o tamanho da palavra mais longa na entrada até a soma do comprimento de todas as palavras.

F=l=>{
  for(z=Math.max(...l.map(w=>(w=w.length,x+=w,w),x=0));
      ++z<=x;
      (W=(k,s,m,w=l[k])=>w?s.some((a,p)=>!!a&&
            D.some((d,j,_,r=[...s],q=p-d)=>
              [...w].every(c=>r[q+=d]==c?c:r[q]==1?r[q]=c:0)
              &&R(r)&&W(k+1,r,m|1<<(j/2))
            )
          )
        :m>12&&Q(s)&&(console.log(''+s),z=x) 
      )(0,[...Array(z*z-z)+99].map((c,i)=>i%z?1:'\n'))
    )
    D=[~z,-~z,1-z,z-1,z,-z,1,-1]
    ,R=u=>!l.some(w=>u.map((a,p)=>a==w[0]&&D.map(d=>n+=[...w].every(c=>u[q+=d]==c,q=p-d)),
      n=~([...w]+''==[...w].reverse()))&&n>0)
    ,Q=(u,p=u.indexOf(1),r=[...'ABCDEFGHIJHLMNOPQRSTUVWXYZ'])=>
      ~p?r.some((v,c)=>(r[u[p]=r[j=0|c+Math.random()*(26-c)],j]=v,R(u)&&Q(u)))||(u[p]=1):1
    //,Q=u=>u.map((c,i,u)=>u[i]=c!=1?c:' ') // uncomment to avoid random fill
}

Ungolfed e explicou (incompleto, desculpe pessoal, é muito trabalho)

F=l=>
{
  var x, z, s, q, D, R, Q, W;
  // length of longest word in z
  z = Math.max( ... l.map(w => w.length))
  // sum of all words length in x
  x = 0;
  l.forEach(w => x += w.length);

  for(; ++z <= x; ) // test square size from z to x
  {
    // grid in s[], each row of len z + 1 newline as separator, plus leading and trailing newline
    // given z==offset between rows, total length of s is z*(z-1)+1
    // gridsize: 2, z:3, s.length: 7 
    // gridsize: 3, z:4, s.length: 13
    // ...
    // All empty, nonseparator cells, filled with 1, so
    // - valid cells have a truthy value (1 or string)
    // - invalid cells have falsy value ('\n' or undefined)
    s = Array(z*z-z+1).fill(1) 
    s = s.map((v,i) => i % z != 0 ? 1 : '\n');

    // offset for 8 directions 
    D = [z+1, -z-1, 1-z, z-1, z, -z, 1, -1]; // 4 diags, then 2 vertical, then 2 horizontal 

    // Function to check repeating words
    R = u => // return true if no repetition
      ! l.some( w => // for each word (exit early when true)
      {
          n = -1 -([...w]+''==[...w].reverse()); // counter starts at -1 or -2 if palindrome word
          u.forEach( (a, p) => // for each cell if grid 
          {
            if (a == [0]) // do check if cell == first letter of word, else next word
               D.forEach( d => // check all directions 
                 n += // word counter
                   [...w].every( c => // for each char in word, exit early if not equal
                     u[q += d] == c, // if word char == cell, continue to next cell using current offset
                     q = p-d  // starting position for cell
                   )
               ) // end for each direction
          } ) // end for each cell
          return n > 0 // if n>0 the word was found more than once
      } ) // end for each word

    // Recursive function to fill empty space with random chars
    // each call add a single char
    Q = 
    ( u, 
      p = u.indexOf(1), // position of first remaining empty cell 
      r = [...'ABCDEFGHIJHLMNOPQRSTUVWXYZ'] // char array to be random shuffled
    ) => {
      if (~p) // proceed if p >= 0
        return r.some((v,c)=>(r[u[p]=r[j=0|c+Math.random()*(26-c)],j]=v,R(u)&&Q(u)))||(u[p]=1)
      else 
        return 1; // when p < 0, no more empty cells, return 1 as true
    }
    // Main working function, recursive fill of grid          
    W = 
    ( k, // current word position in list
      s, // grid
      m, // bitmask with all directions used so far (8 H, 4V, 2 or 1 diag)
      w = l[k] // get current word
    ) => {
      var res = false
      if (w) { // if current word exists
        res = s.some((a,p)=>!!a&&
            D.some((d,j,_,r=[...s],q=p-d)=>
              [...w].every(c=>r[q+=d]==c?c:r[q]==1?r[q]=c:0)
              &&R(r)&&W(k+1,r,m|1<<(j/2))
            )
          )
      } 
      else 
      { // word list completed, check additional constraints
        if (m > 12 // m == 13, 14 or 15, means all directions used
            && Q(s) ) // try to fill with random, proceed if ok
        { // solution found !!
          console.log(''+s) // output grid
          z = x // z = x to stop outer loop
          res = x//return value non zero to stop recursion
        }
      }
      return res
    };
    W(0,s)
  }    
}

Teste no console Firefox / FireBug

F (['TREM', 'CUBO', 'CAIXA', 'BICICLETA'])

,T,C,B,O,X,B,H,  
,H,R,U,H,L,I,H,  
,Y,A,A,B,E,C,B,  
,D,H,S,I,E,Y,I,  
,H,E,R,L,N,C,T,  
,G,S,T,Y,F,L,U,  
,H,U,Y,F,O,E,H,  

não preenchido

,T,C,B,O,X,B, ,
, ,R,U, , ,I, ,
, , ,A,B, ,C, ,
, , , ,I,E,Y, ,
, , , , ,N,C, ,
, , , , , ,L, ,
, , , , , ,E, ,

F (['TREM', 'ARTES', 'RAT', 'CUBO', 'CAIXA', 'BICICLETA', 'TEMPESTADE', 'CÉREBRO', 'PROFUNDIDADE', 'BOCA', 'TRABALHO'])

,T,A,R,C,S,T,H,
,S,R,R,L,U,D,T,
,T,B,A,T,N,B,P,
,O,B,O,I,S,A,E,
,R,B,A,X,N,H,D,
,M,R,M,O,U,T,H,
,B,I,C,Y,C,L,E,

F (['AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG'])

,A,U,B,C,
,T,A,E,Z,
,C,D,O,F,
,Q,C,G,A,

F (['AA', 'AB', 'AC', 'AD', 'AE', 'AF'])

saída não preenchida - @nathan: agora você não pode adicionar outro A x sem repetições. Você precisará de uma grade maior.

,A, ,C,
, ,A,F,
,D,E,B,

No seu último caso de teste, não é possível em uma grade 3x3?
Nathan Merrill

@NathanMerrill no. Mais detalhes no texto de resposta
edc65 23/03

código totalmente ilegível :) mas agradável que é a desvantagem de byte / ponto de "prêmio" não seja um compilador humana
firephil

1
@firephil tentando adicionar uma explicação, não é fácil ...
edc65

1

C #

Aqui está uma implementação simples, com ainda o trabalho a ser feito. Existem muitas combinações para obter o menor tamanho. Então, só usei o algoritmo mais simples possível.

class Tile
{
    public char C;
    public int X, Y;
}

class Grid
{
    List<Tile> tiles;

    public Grid()
    {
        tiles = new List<Tile>();
    }
    public int MaxX()
    {
        return tiles.Max(x => x.X);
    }
    public int MaxY()
    {
        return tiles.Max(x => x.Y);
    }
    public void AddWords(List<string> list)
    {
        int n = list.Count;
        for (int i = 0; i < n; i++)
        {
            string s = list[i];
            if(i==0)
            {
                Vert(s, 0, 0);
            }
            else if(i==n-1)
            {
                int my = MaxY();
                Diag(s, 0, my+1);
            }
            else
            {
                Horiz(s, 0, i);
            }
        }

    }
    private void Vert(string s, int x, int y)
    {
        for (int i = 0; i < s.Length; i++)
        {
            Tile t = new Tile();
            t.C = s[i];
            t.X = x+i;
            t.Y = y;
            tiles.Add(t);
        }
    }
    private void Horiz(string s, int x, int y)
    {
        for (int i = 0; i < s.Length; i++)
        {
            Tile t = new Tile();
            t.C = s[i];
            t.X = x+i;
            t.Y = y;
            tiles.Add(t);
        }
    }
    private void Diag(string s, int x, int y)
    {
        for (int i = 0; i < s.Length; i++)
        {
            Tile t = new Tile();
            t.C = s[i];
            t.X = x++;
            t.Y = y++;
            tiles.Add(t);
        }
    }
    public void Print()
    {
        int mx = this.MaxX();
        int my = this.MaxY();
        int S = Math.Max(mx, my) + 1;
        char[,] grid = new char[S, S];
        Random r = new Random(DateTime.Now.Millisecond);
        //fill random chars
        for (int i = 0; i < S; i++)
        {
            for (int j = 0; j < S; j++)
            {
                grid[i, j] = (char)(r.Next() % 26 + 'A');
            }
        }
        //fill words
        tiles.ForEach(t => grid[t.X, t.Y] = t.C);
        //print
        for (int i = 0; i < S; i++)
        {
            for (int j = 0; j < S; j++)
            {
                Console.Write("{0} ", grid[i,j]);
            }
            Console.WriteLine();
        }
    }
}

class WordSearch
{
    public static void Generate(List<string>list)
    {
        list.Sort((x, y) =>
        { int s = 0; if (x.Length < y.Length)s = -1; else if (y.Length < x.Length)s = 1; return s; });
        list.Reverse();
        Grid g = new Grid();
        g.AddWords(list);
        g.Print();
    }

}

Teste

class Program
{
    static void Main(string[] args)
    {
        string words = "CAT, TRAIN, CUBE, BICYCLE";
        string comma=",";
        List<string> w = words.Split(comma.ToArray()).ToList();
        List<string> t = new List<string>();
        foreach(string s in w)
        {
           t.Add(s.Trim());
        }
        WordSearch.Generate(t);

        Console.ReadKey();
    }
}

funciona, mas não é o ideal: exemplo de palavras de seqüência de caracteres = "CAT, DOG, HR, RUN, CMD";
firephil

Você verifica se os caracteres de preenchimento aleatório causam a repetição de uma palavra na grade?
edc65

-1 Tentei. Não segue as especificações at least one word in each direction (horizontal, vertical and diagonal). Executando o programa de teste, nenhuma palavra horizontal (3 vertical, 1 diag)
edc65

3
Esta questão é code-golf , portanto, você deve postar quantos bytes no título e provavelmente diminuir o seu programa. Obrigado.
mbomb007

@ edc65 Faz uma vertical, uma diagonal e todas as outras na horizontal. Como eu comentei para obter uma solução perfeita, será necessário um número enorme de combinações para verificar, bem como as especificações da pergunta.
24515 bacchusbeale
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.