การสร้างปริศนาค้นหาคำ


13

รับรายการสตริงค้นหาเมทริกซ์จตุรัสที่เล็กที่สุดที่มีสตริงเริ่มต้นแต่ละรายการ สตริงอาจปรากฏแนวนอนแนวตั้งหรือแนวทแยงและไปข้างหน้าหรือถอยหลังเหมือนในคำถามนี้คำค้นหาปริศนา

ควรวางคำในช่องสี่เหลี่ยมอย่างน้อยหนึ่งคำในแต่ละทิศทาง (แนวนอนแนวตั้งและแนวทแยงมุม) คำควรปรากฏขึ้นเพียงครั้งเดียว

ดังนั้นการป้อนข้อมูลเป็นเพียงรายการของคำ ตัวอย่างเช่นCAT, TRAIN, CUBE, BICYCLE. ทางออกหนึ่งที่เป็นไปได้คือ:

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

ฉันแทนที่ตัวอักษรด้วยเครื่องหมายดอกจันเพื่อความชัดเจน ผลลัพธ์ที่ต้องการควรมีตัวอักษรเติมแบบสุ่ม


ต้องพบคำแต่ละคำในตำแหน่งเดียวเท่านั้น (เช่นการค้นหาคำทั่วไป) ยกตัวอย่างเช่นตัวอักษรที่เหลือACในตัวอย่างของคุณจะทำให้คนอื่นถ้ามันCAT T
Geobits

ไม่ชัดเจนสำหรับฉันว่าคุณหมายถึงอะไรโดย " คำควรอยู่ในทุกทิศทาง " คำตอบจะตรงตามเกณฑ์นี้หรือไม่หากวางคำออกมาอย่างไม่แน่นอนมันจะสุ่มเลือกหนึ่งในแปดของสมมาตรของจัตุรัส หรือที่สุดขั้วอื่น ๆ เอาต์พุตควรถูกเลือกอย่างสม่ำเสมอจากสี่เหลี่ยมที่เล็กที่สุดที่เป็นไปได้ทั้งหมดซึ่งมีคำเหล่านั้นหรือไม่ หรือมีการลากเส้นที่ไหนสักแห่งระหว่างสุดขั้วเหล่านั้นหรือไม่?
Peter Taylor

ใช่ควรพบได้ในตำแหน่งเดียวเท่านั้น
Migue

1
ไม่จริงไม่ ฉันยังไม่ชัดเจนว่าช่องว่างที่คำตอบควรสุ่มอย่างนั้นคืออะไรและไม่อนุญาตให้มีความยืดหยุ่นในการถ่วงน้ำหนักองค์ประกอบของพื้นที่นั้น
Peter Taylor

1
ณ ตอนนี้คำถามมีอินพุตที่ไม่สามารถแก้ไขได้ แต่ไม่มีการกล่าวถึงว่าคุณควรจะจัดการกับเรื่องนั้นอย่างไร ตัวอย่างเช่นชุด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 Zไม่มีวิธีแก้ปัญหา
orlp

คำตอบ:


6

JavaScript (ES6), 595 628 680

แก้ไขการล้างและผสานบางส่วน:
- ฟังก์ชัน P ที่ผสานภายในฟังก์ชัน R
- calc x และ z ใน. map เดียวกัน
- เมื่อพบวิธีแก้ปัญหาให้ตั้งค่า x เป็น 0 เพื่อออกจากวงรอบนอก
- นิยามที่รวมกันและการเรียก W

Edit2เพิ่มเติมการเล่นกอล์ฟการสุ่มเติมให้สั้นลงการวนรอบนอกแก้ไข ... ดูประวัติสำหรับสิ่งที่อ่านง่ายขึ้น

ต่างจากคำตอบที่ยอมรับได้ซึ่งควรใช้กับอินพุตส่วนใหญ่ เพียงหลีกเลี่ยงคำตัวอักษรเดียว หากพบผลลัพธ์จะเป็นการดีที่สุดและใช้ทั้ง 3 ทิศทาง

ข้อ จำกัด ในการหลีกเลี่ยงคำซ้ำ ๆ เป็นเรื่องยากมาก ฉันต้องมองหาคำที่ซ้ำกันในแต่ละขั้นตอนเพิ่มคำลงในตารางและที่ตัวละครเติมแต่ละแบบสุ่ม

ฟังก์ชั่นหลัก:

  • P (w) จริงถ้าคำ palindrome คำ palindrom จะพบสองครั้งเมื่อตรวจสอบคำที่ซ้ำกัน

  • R (s) ตรวจสอบคำที่ซ้ำกันในกริด s

  • Q เติม s กริดด้วยอักขระสุ่ม - มันวนซ้ำและย้อนกลับในกรณีที่มีคำซ้ำ - และอาจล้มเหลว

  • W () วนซ้ำพยายามเติมกริดที่มีขนาดที่กำหนดถ้าเป็นไปได้

ฟังก์ชั่นหลักใช้ W () เพื่อค้นหากริดเอาต์พุตโดยลองจากขนาดของคำที่ยาวที่สุดในอินพุตจนถึงผลรวมของความยาวของคำทั้งหมด

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และอธิบาย (ไม่สมบูรณ์ขอโทษพวกมันทำงานมาก)

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

ทดสอบในคอนโซล 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,  

ไม่เต็ม

,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', 'โฆษณา', 'AE', 'เอเอฟ', 'เอจี'])

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

F ([ 'AA', 'AB', 'AC', 'โฆษณา', 'AE', 'เอเอฟ'])

ไม่ได้กรอกข้อมูลเอาท์พุท - @nathan: ตอนนี้คุณไม่สามารถเพิ่ม A xอีกครั้งโดยไม่มีการทำซ้ำ คุณจะต้องมีกริดที่ใหญ่กว่า

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

ในกรณีทดสอบครั้งสุดท้ายของคุณมันเป็นไปไม่ได้ในตาราง 3x3 หรือไม่?
นาธานเมอร์ริล

@NathanMerrill no รายละเอียดเพิ่มเติมในข้อความคำตอบ
edc65

รหัสที่อ่านไม่ได้โดยสิ้นเชิง :) แต่ก็ดีข้อเสียของไบต์ / จุด "รางวัล" ไม่ได้เป็นคอมไพเลอร์ของมนุษย์
firephil

1
@ firephil กำลังพยายามเพิ่มคำอธิบายมันไม่ง่ายเลย ...
edc65

1

ค#

นี่คือการดำเนินการอย่างง่าย ๆ ที่ยังคงต้องทำ มีชุดค่าผสมจำนวนมากเพื่อให้ได้ขนาดที่เล็กที่สุด ดังนั้นเพียงแค่ใช้อัลกอริทึมที่ง่ายที่สุดในการคิด

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

}

ทดสอบ

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

มันใช้งานได้ แต่มันไม่เหมาะสม: sample string words = "CAT, DOG, HR, RUN, CMD";
firephil

คุณตรวจสอบว่าตัวละครเติมสุ่มทำให้เกิดการซ้ำคำในตารางหรือไม่?
edc65

-1 ลองดู at least one word in each direction (horizontal, vertical and diagonal)ไม่เป็นไปตามรายละเอียดที่ เรียกใช้โปรแกรมทดสอบไม่มีคำแนวนอน (แนวตั้ง 3, 1 diag)
edc65

3
คำถามนี้คือcode-golfดังนั้นคุณควรโพสต์จำนวนไบต์ในชื่อเรื่องและอาจทำให้โปรแกรมของคุณสั้นลง ขอบคุณ
mbomb007

@ edc65 มันเป็นแนวตั้งหนึ่งแนวทแยงและแนวนอนทั้งหมด ในขณะที่ฉันแสดงความคิดเห็นเพื่อรับโซลูชั่นที่สมบูรณ์แบบจะต้องมีชุดค่าผสมจำนวนมากเพื่อตรวจสอบเช่นเดียวกับข้อกำหนดของคำถาม
bacchusbeale
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.