Forum Discussion
Patrick2788
Jul 14, 2022Silver Contributor
A LAMBDA Word Search Puzzle
I've created a 15x10 Word Search. I've included a word list (dynamic range) containing words which might be found in the word search. The goal is to then extract those words when they appear in the...
- Jul 16, 2022
Here is one more variant
//------ variant without TEXTJOIN() reverseColumns= LAMBDA( data, LET( n, COLUMNS( data ), CHOOSECOLS( data, SEQUENCE(n,,n,-1) ) )); reversedText= LAMBDA( str, LET( head, LEFT(str), tail, RIGHT(str, LEN(str) - 1), IF(str = "", "", reversedText(tail) & head) )); isPolindrom= LAMBDA( text, text = reversedText( text ) ); checkSingleWord= LAMBDA( str, vector, LET( getWord, REDUCE("", vector, LAMBDA( a,v, IF( LEFT(str, LEN(a&v)) = a&v, a&v, IF( LEFT(str) = a, v, IF( a = str, a, "" ) ) ) ) ), IF( getWord = str, str, "") )); checkListOfWords= LAMBDA( wordsVector, vector, LET( getWords, REDUCE("", wordsVector, LAMBDA(a,v, VSTACK(a, checkSingleWord( v, vector) ) ) ), IFERROR( FILTER( getWords, getWords <> ""), "" ) )); wordsInMatrix= LAMBDA( data, wordsVector, LET( k, SEQUENCE( ROWS(data) ), scanData, REDUCE(1, k, LAMBDA(a,v, CHOOSECOLS( IF( v < SEQUENCE(,v,v,-1), a, VSTACK(a, checkListOfWords( Words, CHOOSEROWS(data,v) ) ) ), 1 ) )), removeFirst, DROP( scanData, 1 ), FILTER( removeFirst, removeFirst <> "") )); wordsInPuzzle= LAMBDA( data, wordsVector, LET( allWords, SORT( VSTACK( wordsInMatrix( data, wordsVector ), wordsInMatrix( reverseColumns( data ), wordsVector ), wordsInMatrix( TRANSPOSE( data ), wordsVector ), wordsInMatrix( reverseColumns( TRANSPOSE( data ) ), wordsVector ) )), ifPolindroms, MAP(allWords, LAMBDA(v, isPolindrom(v) ) ), polindroms, UNIQUE( FILTER(allWords, ifPolindroms)), notPolindroms, FILTER(allWords, ifPolindroms -1), stack, IF( ISERR(polindroms), notPolindroms, VSTACK( polindroms, notPolindroms ) ), SORT( stack ) ));
Patrick2788
Jul 24, 2022Silver Contributor
For this exercise, I didn't do overlapping words or diagonals (Things might really get out of hand if those were included!). I think SCAN can locate the words, but it seems easier to go by the row (Less letters to discard and no need to account for the row changing) or convert the matrix to a vector to pull the words.
mtarler
Jul 25, 2022Silver Contributor
yes I saw the no diagonals but didn't see any exclusion for overlap. Besides, I don't think overlap adds a lot of complexity, just prevents some tricks that make it more efficient.
- Patrick2788Aug 01, 2023Silver Contributor
I didn't pull duplicates because I figure if a word appears more than once in the puzzle the formula should return each instance.
The big difference between the solutions is I have the expensive operation of reversing the dictionary entries. My first thought was to move ReverseWords from Solve to a new named item.
The result:
Next, I swapped out MAP (e.g. MAP(dictionary, LAMBDA(e,Reverse(e))) for the other Lambda helper functions to see the difference.
The results:
For grins, I even tested REDUCE:
Is there a more efficient way to reverse each element in a vector? The main obstacle being that 'exploding' an array is scalar-based so Lambda helpers are needed to deploy 'Explode' on each element.
- mtarlerAug 01, 2023Silver Contributor
Patrick2788 so I finally got back to this and did my version. Love to hear about the performance testing you do on them ....
=LET( h, ROWS(puzzleRange), w, COLUMNS(puzzleRange), space, " ", spaceLine, EXPAND(space, 1, w, space), PuzzleReverseH, CHOOSECOLS(puzzleRange, SEQUENCE(, w, w, -1)), PuzzleStack1, VSTACK(puzzleRange, spaceLine, PuzzleReverseH), PuzzleReverseV, CHOOSEROWS(PuzzleStack1, SEQUENCE(, 2 * h + 1, 2 * h + 1, -1)), PuzzleStack2, HSTACK(TRANSPOSE(puzzleRange), TRANSPOSE(TAKE(PuzzleReverseV, -h - 1, ))), PuzzleDiags, LAMBDA(stack, LET( nr, ROWS(stack), nc, COLUMNS(stack), REDUCE( EXPAND(INDEX(stack, , 1), nr + nc - 1, 1, space), SEQUENCE(nc - 1), LAMBDA(p, q, HSTACK( p, EXPAND( VSTACK(EXPAND(space, q, 1, space), INDEX(stack, , q + 1)), nr + nc - 1, 1, space ) ) ) ) ) ), PuzzleStacksHalf1, VSTACK( PuzzleStack1, spaceLine, PuzzleDiags(VSTACK(PuzzleStack1, spaceLine, PuzzleReverseV)) ), PuzzleStacksHalf2, PuzzleStack2, findWord, LAMBDA(puzzlestack, REDUCE( "", SEQUENCE(ROWS(puzzlestack)), LAMBDA(p, r, VSTACK( p, FILTER( dictionary, ISNUMBER(SEARCH(dictionary, CONCAT(INDEX(puzzlestack, r, )))), "" ) ) ) ) ), findWords, VSTACK(findWord(PuzzleStacksHalf1), findWord(PuzzleStacksHalf2)), out, SORT(FILTER(findWords, findWords <> "")), out )
ok so I did a quick test and mine seems to fair pretty well. (*** note I have made a few edits as I found my diagonals were not as I thought and needed to adjust my sets to get all 4 diagonals correctly. Note, I also found that your solution is also missing some)
btw my concept was to take the puzzle and create a stack of:
orig puzzle,
puzzle reversed by rows, columns, and both
the above 2 shifted by 1 row for each column (i.e. creating the diagonals)
and then find any words found in any row
and repeat for the transpose of the orig and rev by rows to get vertical up/down words.
so I originally output the UNIQUE list but noted you showed all. As noted above I orig had some issues with missing 1 or 2 diagonals so my list had some you didn't you had some i didn't. I fixed it and now have every word in your list but when I removed UNIQUE i notice you have more copies in a couple cases. I don't know why that is the case but submit this as is for now)
- Patrick2788Jul 23, 2023Silver Contributor
I thought it would be interesting to re-visit this task with a fresh set of eyes. I scrapped my solution from 2022 and made things more interesting.
Functionality added:
- Diagonals are now supported
- Multiple dictionary support
There's a bit of calculation crunch when using the dictionary with 9,500+ words. I was going to add an Oxford dictionary that contained 100,000 words but the CSV needed extensive cleanup (Probably for the best as larger word lists contain a lot of made-up words).
=LET( height, ROWS(Puzzle), width, COLUMNS(Puzzle), AccumulateText, LAMBDA(a, letter, a & letter), Letter2Array, LAMBDA(arr, SCAN("", arr, AccumulateText)), WordsReversed, MAP(dictionary, LAMBDA(e, Reverse(e))), results, REDUCE( "", Puzzle, LAMBDA(a, letter, LET( r, ROW(letter), c, COLUMN(letter), across, INDEX(Puzzle, r, SEQUENCE(, width - c + 1, c, 1)), down, INDEX(Puzzle, SEQUENCE(height - r + 1, , r), c), diagonal_c, SEQUENCE(width - c + 1, , c), diagonal_r, SEQUENCE(width - c + 1, , r), diagonal, INDEX(Puzzle, diagonal_r, diagonal_c), neg_diagonal_c, SEQUENCE(c, , c, -1), neg_diagonal_r, SEQUENCE(width - c, , r), neg_diagonal, INDEX(Puzzle, neg_diagonal_r, neg_diagonal_c), arrAcross, TOCOL(Letter2Array(across)), arrDown, Letter2Array(down), arrDiagonal, Letter2Array(diagonal), arrDiagonalNeg, TOCOL(Letter2Array(neg_diagonal)), WordStack, IF( c = width, VSTACK(arrDown, arrDiagonalNeg), IF( r = height, VSTACK(arrAcross, arrDiagonal, arrDiagonalNeg), IF( c = 1, VSTACK(arrAcross, arrDown, arrDiagonal), VSTACK(arrAcross, arrDown, arrDiagonal, arrDiagonalNeg) ) ) ), WordBank, VSTACK(dictionary, WordsReversed), FoundWords, VSTACK(dictionary, dictionary), GetWords, TOCOL(XLOOKUP(WordStack, WordBank, FoundWords, ""), 2), VSTACK(a, GetWords) ) ) ), SORT(FILTER(results, results <> "", "no words found")) )