Generierung von Wortsuchrätseln


13

Suchen Sie anhand einer Liste von Zeichenfolgen die kleinste quadratische Matrix, die die einzelnen Anfangszeichenfolgen enthält. Die Zeichenfolgen können horizontal, vertikal oder diagonal und vorwärts oder rückwärts wie in dieser Frage Wortsuchrätsel angezeigt werden .

Wörter sollten in das Quadrat gesetzt werden, mit mindestens einem Wort in jeder Richtung (horizontal, vertikal und diagonal). Wörter sollten nur einmal vorkommen.

Die Eingabe ist also nur eine Liste von Wörtern. Zum Beispiel: CAT, TRAIN, CUBE, BICYCLE. Eine mögliche Lösung ist:

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

Aus Gründen der Übersichtlichkeit habe ich das Ausfüllen von Buchstaben durch Sternchen ersetzt. Die gewünschte Ausgabe sollte zufällige Füllbuchstaben enthalten.


Muss jedes Wort nur an einer Stelle gefunden werden (wie bei einer typischen Wortsuche)? ACBeispielsweise würde der in Ihrem Beispiel verbleibende Buchstabe einen anderen ergeben, CATwenn dies der Fall ist T.
Geobits

Mir ist nicht klar, was genau Sie unter " Wörter sollten zufällig in alle Richtungen platziert werden" verstehen . Würde eine Antwort dieses Kriterium erfüllen, wenn sie, nachdem sie die Wörter deterministisch angeordnet hat, zufällig eine der acht Symmetrien des Quadrats auswählt? Oder sollte im anderen Extremfall die Ausgabe einheitlich aus allen möglichen kleinsten Quadraten ausgewählt werden, die die Wörter enthalten? Oder liegt die Grenze irgendwo zwischen diesen Extremen?
Peter Taylor

Ja, es sollte nur in einer Position gefunden werden.
Migue

1
Nicht wirklich, nein. Mir ist immer noch nicht klar, aus welchem ​​Raum eine Antwort zufällig ausgewählt werden soll und wie viel Flexibilität bei der Gewichtung der Elemente dieses Raums zulässig ist.
Peter Taylor

1
Die Frage enthält derzeit Eingaben, die nicht gelöst werden können, aber es wird nicht erwähnt, wie Sie damit umgehen sollen. Zum Beispiel hat das 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 Zkeine Lösung.
Orlp

Antworten:


6

JavaScript (ES6), 595 628 680

Bearbeiten Einige Bereinigung und Zusammenführung:
- Funktion P fusioniert innerhalb Funktion R
- Calc x und z in der gleichen .Map
- wenn Lösung gefunden, Set x auf 0 äußere Schleife verlassen
- und fusionierten definiton Anruf von W

Edit2 mehr Golf, zufällige Füllung verkürzt, äußere Schleife überarbeitet ... siehe Verlauf für etwas lesbarer

Im Gegensatz zu der akzeptierten Antwort, dies für die meisten Eingaben funktionieren. Vermeiden Sie einfach einzelne Buchstaben. Wenn eine Ausgabe gefunden wird, ist sie optimal und verwendet alle drei Richtungen.

Die Einschränkung, das Wiederholen von Wörtern zu vermeiden, ist sehr schwierig. Ich musste bei jedem Schritt, bei dem ich ein Wort zum Gitter hinzufügte, und bei jedem zufälligen Füllzeichen nach wiederholten Wörtern suchen.

Hauptunterfunktionen:

  • P (w) wahr, wenn das Wort palindrom ist. Ein Palindrom-Wort wird zweimal gefunden, wenn nach wiederholten Wörtern gesucht wird.

  • R (s) prüft die Wiederholung von Wörtern in Gitter s

  • Q (s) füllen das Gitter s mit zufälligen Zeichen - es ist rekursiv und rückgängig zu machen, wenn sich ein Wort wiederholt - und können fehlschlagen.

  • W () rekursiv, versuchen Sie, wenn möglich, ein Raster mit der angegebenen Größe zu füllen.

Die Hauptfunktion verwendet W (), um ein Ausgabegitter zu finden. Dabei wird versucht, das längste Wort in der Eingabe bis zur Summe der Länge aller Wörter zu finden.

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 und erklärt (unvollständig, sorry Leute, es ist eine Menge Arbeit)

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 in der Firefox / FireBug-Konsole

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,  

nicht gefüllt

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

F)

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

Ausgabe nicht gefüllt - @nathan: Jetzt können Sie kein weiteres A x ohne Wiederholungen hinzufügen . Du brauchst ein größeres Gitter.

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

Ist das in Ihrem letzten Testfall in einem 3x3-Raster nicht möglich?
Nathan Merrill

@ NathanMerrill Nr. Weitere Details im Antworttext
edc65

völlig unlesbarer Code :) aber schön, das ist der Nachteil von Byte / Punkt "Auszeichnung" Sei kein menschlicher Compiler
Firefil

1
@Firephil versucht, eine Erklärung hinzuzufügen, es ist nicht einfach ...
Edc65

1

C #

Hier ist eine einfache Implementierung mit noch zu erledigender Arbeit. Es gibt sehr viele Kombinationen, um die kleinste Größe zu erhalten. Also einfach den einfachsten Algorithmus verwenden, an den man denken könnte.

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

}

Prüfung

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

es funktioniert aber es ist nicht optimal: Beispiel-String-Wörter = "CAT, DOG, HR, RUN, CMD";
Firephil

Prüfen Sie, ob die zufälligen Füllzeichen die Wiederholung eines Wortes im Raster verursachen?
Edc65

-1 Versuchte es. Entspricht nicht den Spezifikationen at least one word in each direction (horizontal, vertical and diagonal). Ausführen des Testprogramms, kein horizontales Wort (3 vertikale, 1 diag)
edc65

3
Bei dieser Frage handelt es sich um Code-Golf . Sie sollten also angeben, wie viele Bytes der Titel enthält, und Ihr Programm wahrscheinlich um einiges kürzen. Vielen Dank.
mbomb007

@ edc65 Es macht eine vertikale, eine diagonale und alle anderen horizontal. Wie ich bereits bemerkte, sind zur Prüfung der perfekten Lösung eine enorme Anzahl von Kombinationen sowie die Spezifikationen der Frage erforderlich.
Bacchusbeale
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.