Generazione di puzzle di ricerca di parole


13

Dato un elenco di stringhe, trova la matrice quadrata più piccola che contiene ciascuna delle stringhe iniziali. Le stringhe possono apparire in orizzontale, in verticale o in diagonale e in avanti o all'indietro come in questa domanda Puzzle di ricerca di parole .

Le parole devono essere posizionate nel quadrato, con almeno una parola in ciascuna direzione (orizzontale, verticale e diagonale). Le parole dovrebbero apparire solo una volta.

Quindi, l'input è solo un elenco di parole. Ad esempio: CAT, TRAIN, CUBE, BICYCLE. Una possibile soluzione è:

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

Ho sostituito le lettere di riempimento con asterischi solo per chiarezza. L'output desiderato dovrebbe includere lettere di riempimento casuali.


Ogni parola deve essere trovata in una sola posizione (come le ricerche di parole tipiche)? Ad esempio, la lettera rimasta ACnel tuo esempio ne farebbe un'altra CATse lo è T.
Geobits

Non mi è chiaro cosa intendi esattamente con "Le parole dovrebbero essere posizionate in modo casuale in tutte le direzioni ". Una risposta soddisferebbe questo criterio se, dopo aver definito le parole in modo deterministico, selezionasse in modo casuale una delle otto simmetrie del quadrato? Oppure, all'altro estremo, l'output dovrebbe essere selezionato in modo uniforme da tutti i quadrati più piccoli possibili che contengono le parole? O la linea è tracciata da qualche parte tra quegli estremi?
Peter Taylor,

Sì, dovrebbe essere trovato solo in una posizione.
Migue

1
Non proprio no. Non sono ancora chiaro quale sia lo spazio dal quale una risposta dovrebbe campionare casualmente, né quanta flessibilità è permessa nel ponderare gli elementi di quello spazio.
Peter Taylor,

1
A partire da ora, la domanda ha input che non possono essere risolti, ma non si fa menzione di come dovresti affrontarlo. Ad esempio il set 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 Znon ha soluzione.
orlp

Risposte:


6

JavaScript (ES6), 595 628 680

Modifica Alcune operazioni di pulizia e unione:
- funzione P unita all'interno della funzione R
- calc x e z nella stessa .map
- una volta trovata la soluzione, imposta x su 0 per uscire dal ciclo esterno
- definiton uniti e chiamata di W

Modifica2 più golf, riempimento casuale accorciato, anello esterno rivisto ... vedi la storia per qualcosa di più leggibile

A differenza della risposta accettata, questo dovrebbe funzionare per la maggior parte degli input. Basta evitare le parole di una sola lettera. Se viene trovato un output, è ottimale e utilizza tutte e 3 le direzioni.

Il vincolo di evitare di ripetere le parole è molto difficile. Ho dovuto cercare parole ripetute ad ogni passaggio aggiungendo parole alla griglia e ad ogni carattere di riempimento casuale.

Principali funzioni secondarie:

  • P (w) vero se la parola palindromo. Una parola palindrom verrà trovata due volte quando si controllano le parole ripetute.

  • R (s) controlla le parole ripetute sulla griglia

  • Q (s) riempiono la griglia di caratteri casuali - è ricorsivo e torna indietro in caso di ripetizione di parole - e può fallire.

  • W () ricorsivo, prova a riempire una griglia di dimensioni determinate, se possibile.

La funzione principale usa W () per trovare una griglia di output, provando da una dimensione della parola più lunga in input fino alla somma della lunghezza di tutte le parole.

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 spiegato (incompleto, scusate ragazzi è un sacco di lavoro)

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)
  }    
}

Test nella console Firefox / FireBug

F (['TRAIN', 'CUBE', 'BOX', 'BICYCLE'])

,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,  

non riempito

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

F (['TRAIN', 'ARTS', 'RAT', 'CUBE', 'BOX', 'BICYCLE', 'STORM', 'BRAIN', 'DEPTH', 'MOUTH', 'SLAB'])

,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'])

output non compilato - @nathan: ora non puoi aggiungere un altro A x senza ripetizioni. Avrai bisogno di una griglia più grande.

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

Nel tuo ultimo caso di test, non è possibile in una griglia 3x3?
Nathan Merrill,

@NathanMerrill no. Maggiori dettagli nel testo della risposta
edc65,

codice totalmente illeggibile :) ma bello che è il rovescio della medaglia byte / point non essere un compilatore umano
firephil

1
@firephil cercando di aggiungere una spiegazione, non è facile ...
edc65

1

C #

Ecco una semplice implementazione con ancora del lavoro da fare. Esistono moltissime combinazioni per ottenere le dimensioni più piccole. Quindi solo l'algoritmo più semplice usato potrebbe pensare.

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();
    }

}

Test

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();
    }
}

funziona ma non è ottimale: parole stringa di esempio = "CAT, DOG, HR, RUN, CMD";
firephil,

Verifichi se i caratteri di riempimento casuali causano la ripetizione di una parola nella griglia?
edc65,

-1 Ho provato. Non segue le specifiche at least one word in each direction (horizontal, vertical and diagonal). Esecuzione del programma di test, nessuna parola orizzontale (3 verticali, 1 diag)
edc65

3
Questa domanda è code-golf , quindi dovresti pubblicare quanti byte nel titolo e probabilmente accorciare il tuo programma. Grazie.
mbomb007,

@ edc65 Fa una verticale, una diagonale e tutte le altre orizzontali. Come ho commentato per ottenere una soluzione perfetta richiederà un numero enorme di combinazioni da controllare, nonché le specifiche della domanda.
bacchusbeale,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.