[Question]
  [
The New York Times has a daily online game called [Letter Boxed](https://www.nytimes.com/puzzles/letter-boxed) (the link is behind a paywall; the game is also described [here](https://www.polygon.com/2019/2/1/18205854/new-york-times-crossword-letter-boxed-puzzle-game)), presented on a square as follows:
[![Letter Boxed example from the New York Times](https://i.stack.imgur.com/9g0e2m.png)](https://i.stack.imgur.com/9g0e2m.png)
You are given 4 groups of 3 letters (each group corresponds to one side on the picture); no letter appears twice. The aim of the game is to find words made of those 12 letters (and those letters only) such that:
* Each word is at least 3 letters long;
* Consecutive letters cannot be from the same side;
* The last letter of a word becomes the first letter of the next word;
* All letters are used at least once (letters can be reused).
In this challenge, you are given the letters and a list of words. The goal is to check whether the list of words is a valid Letter Boxed solution.
## Input
Input consists of (1) 4 groups of 3 letters and (2) a list of words. It can be in any suitable format.
## Output
A truthy value if the list of words is a valid solution to the Letter Boxed challenge for those 4×3 letters, and a falsey value otherwise.
## Test cases
Groups of letters=`{{I,C,O}, {M,R,E}, {G,N,S}, {A,P,L}}`.
### Truthy values
* PILGRIMAGE, ENCLOSE
* CROPS, SAIL, LEAN, NOPE, ENIGMA
### Falsey values
* PILGRIMAGE, ECONOMIES (can't have CO since they are on the same side)
* CROPS, SAIL, LEAN, NOPE (G and M have not been used)
* PILGRIMAGE, ENCLOSURE (U is not one of the 12 letters)
* ENCLOSE, PILGRIMAGE (last letter of 1st word is not first letter of 2nd word)
* SCAMS, SO, ORGANISE, ELOPE (all words must be at least 3 letters long).
Note that in this challenge, we do not care whether the words are valid (part of a dictionary).
## Scoring:
This [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"), lowest score in bytes wins!
  
[Answer]
# JavaScript (ES6), ~~130~~ 126 bytes
Takes input as `(letters)(words)`. Returns \$0\$ or \$1\$.
```
L=>W=>L.every(a=>a.every(x=>(W+'').match(x,a.map(y=>s+='|'+x+y))),p=s=1)&W.every(w=>w[2]&&p|w[0]==p&!w.match(s,p=w.slice(-1)))
```
[Try it online!](https://tio.run/##nY9Pa4MwGMbv/RTuYhK0dt11vEIQkUA0ogwP4iE4u3a4Ko2rFfrdXZwOdmhh7PAkzwvP7/3zLs9SladD262PzWs17mDk4Gbgcqc6V6cBS3DlYi/g4sxCiDgfsiv3@GJL7Vo8gKssQFdkXayBEGK3oGBLzGzhenD7/Kkwzfba548FQGs@9EsPpcO9o@pDWeH1VsMjN8DIc8SQjTwtgQrbyFGobaLlz2WgbaSVziXVNtbiqCieV6vNxuhOn91@WJXNUTV15dTNG95hTnCOYsaDhIU08JFtID/yuEh1V0Juhb1ExOmUSynj0899Gk1/JOKFZ0FIv/Fp7E7W6i9TPRGJkPnp/@begW5d9pLcjf@crrO/yDvh1KPhvJCYXpEENGIz7PNlp/EL "JavaScript (Node.js) – Try It Online")
### Step 1
We first iterate over \$L\$ to build a pipe-separated string \$s\$ consisting of all invalid pairs of letters. While doing so, we also make sure that each letter appears at least once in some word.
```
L.every(a =>              // for each group of letter a[] in L[]:
  a.every(x =>            //   for each letter x in a[]:
    (W + '')              //     coerce W[] to a string
    .match(               //     and test whether ...
      x,                  //       ... x can be found in it
      a.map(y =>          //       for each letter y in a[]:
        s += '|' + x + y  //         append '|' + x + y to s
      )                   //       end of map()
    )                     //     end of match()
  ),                      //   end of inner every()
  p = s = 1               //   start with p = s = 1
)                         // end of outer every()
```
### Step 2
We now iterate over \$W\$ to test each word.
```
W.every(w =>              // for each word w in W[]:
  w[2] &&                 //   is this word at least 3 characters long?
  p |                     //   is it the first word? (p = 1)
  w[0] == p &             //   or does it start with the last letter of the previous word?
  !w.match(               //   and finally make sure that ...
    s,                    //     ... it doesn't contain any invalid pair of letters
    p = w.slice(-1)       //     and update p to the last letter of w
  )                       //   end of match()
)                         // end of every()
```
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~30~~ 29 bytes
```
FQṢ=Ṣ},i@€€’:3Iʋ,Ẉ>2ɗ,U=ḢɗƝ{Ȧ
```
[Try it online!](https://tio.run/##y0rNyan8/98t8OHORbZAXKuT6fCoaQ0INcy0MvY81a3zcFeHndHJ6Tqhtg93LDo5/djc6hPL/nvrPGqYo6Brp/CoYa7O4eVch5cDtUT@/x8drR7g6eMe5Onr6O6qrqOg7urn7OMf7Koey6UTre4c5B8QDBINdvT0AdE@ro5@INrPPwCq2tPd1xGiGM0cZ38/f19P12BiTMKmH@yO0CCoJMxZQBkkdWCpYGdHX4jR/iDSP8jd0c8TotTVB2x67H8lT2d/3yBXd79gxwAfJQA "Jelly – Try It Online")
A dyadic link that takes the list of words as left argument and the flattened list of letters in the box as the right argument. It returns `1` for true and `0` for false. 
### Explanation
```
F                               | Flatten the word list
 Q                              | Unique
  Ṣ                             | Sort
   =                            | Is equal to
    Ṣ}                          |   The sorted letterbox letters
      ,        ʋ                | Pair this with the following:
       i@€€                     |   The index of each letter of each word in the letterbox            
           ’                    |   Decrease by 1
            :3                  |   Integer divide by 3
              I                 |   Differences between consecutive ones (will be zero if any two consecutive letters in a word from same side of box)
                ,   ɗ           | Pair everything so far with the following:
                 Ẉ>2            |   Whether length of each input word is greater than 2
                     ,   ɗƝ{    | Pair everything so far with the following, applied to each neighbouring pair of the input word list
                      U         |   Upend (reverse) first word
                       =        | Compare characters to second
                        Ḣ       |   Take first (i.e. last character of first word equals first character of second)
                            Ȧ   | Flatten all of the above and check there are no false values
```
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E/wiki/Commands), ~~37~~ ~~35~~ ~~33~~ ~~32~~ ~~31~~ ~~29~~ 28 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage)
```
εk3÷üÊ}DO2@¹ü«εüQO}²{¹˜êQ)˜P
```
-2 bytes by taking inspiration of the `ê` approach [*@Emigna* used in his 05AB1E answer](https://codegolf.stackexchange.com/a/183219/52210).  
-3 bytes thanks to *@Grimy*.
Takes a list of list of characters for the words as first input, and the flattened list of twelve letters as second input.
[Try it online](https://tio.run/##yy9OTMpM/f//3NZs48PbD@853FXr4m/kcGjn4T2HVp/benhPoH/toU3Vh3aennN4VaDm6TkB//9HRysFKOkoeQKxDxC7A3EQlO8LxI5QMVelWJ1oIKmj5AfEzlDV/kAcDJGN5YoGa3KGCvtCDXKFGuAHVQoyMACsPRYA) or [verify all test cases](https://tio.run/##yy9OTMpM/e8ZWlb5qGGejsLhvfZKCo/aJiko2Vf@P7c1ojI42/jw9sN7DnfVuvgbOVQe3nNo9bmth/cE@tdGVFcGH14VqHl6TsB/nf/RSp5KOkrOQOwPxL5AHATErkDsDsR@QBwMxI5AHADEPkqxXNHRSgGePu5Bnr6O7iB1rn7OPv7BrkqxOgrRSs5B/gEgDcGOnj4g9a6OICP8/AMgKj3dfR0hClGNcPb38/f1dA0maAgWvWDrQ4OgcjDX6CCrAssEOzv6gk0FedQ/yN3RzxOsztUHbHAsAA).
**Explanation:**
```
ε         # Map over the character-lists `y` of the (implicit) input-list of words:
 k        #  Get the index of each character in the (implicit) input-list of letters
  3÷      #  Integer-divide each index by 3
    üÊ    #  Check for each overlapping pair of integers that they are NOT equal
}D        # After the map: duplicate the resulting list
  O       #  Get the sum of each inner list of truthy/falsey values
   2@     #  And check that each is larger than 2 (so all words had at least 3 letters)
¹ü        # Get all overlapping pairs of character-lists from the input-list of words:
  «       #  And merge them together to a flattened list of characters
   ε   }  # Map over those merged character lists:
    üQ    #  Check for each overlapping pair of characters in the list that they are equal
      O   #  And take the sum of this (where we'd expect 1/truthy if the last character of
          #  the first word and the first character of the second word are equal)
          #  (NOTE: This could fail for inputs with identical adjacent characters,
          #   but the earlier check of `εk3÷üÊ}` already covers for this)
²{        # Push the input-list of letters, and sort them
  ¹˜      # Push the input-list of list of word-letters, flattened,
    ê     # and then uniquified and sorted as well
     Q    # And check if both lists of characters are the same
)˜        # Then wrap everything on the stack into a list, and deep flatten it
  P       # And check if everything is truthy by taking the product
          # (which is output implicitly as result)
```
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E), 42 bytes
```
εg2›}P¹εεUIεXå}ƶO}üÊP}P¹ü‚ε`нsθQ}P¹Jê²JêQP
```
[Try it online!](https://tio.run/##yy9OTMpM/f//3NZ0o0cNu2oDDu08t/Xc1lDPc1sjDi@tPbbNv/bwnsNdASCJw3seNcw6tzXhwt7iczsCQSJeh1cd2gQkAgP@/49Wcg7yDwhW0lFQCnb09AHRPq6OfiDazz/AFUS7@nm6@zoqxXJFK3k6@4NEfIPAEu5@YH2OAT5Ksf91dfPydXMSqyoB "05AB1E – Try It Online")
[Answer]
# [Python 2](https://docs.python.org/2/), 171 bytes
```
lambda l,w:(set(sum(l,[]))==set(''.join(w)))*all(a[-1]==b[0]for a,b in zip(w,w[1:]))*all((a in g)+(b in g)<2for x in w for a,b in zip(x,x[1:])for g in l)*min(map(len,w))>2
```
[Try it online!](https://tio.run/##lVJLj5swEL7zK0bKwfYuXTX0tiqVEKIIiUcE3RPlYBaTUBkTYbNk@@dTG5J2N4dWPYw9Y8/3GMvHV3UYhHNu3e9nTvu6ocDt@RFLprCcesztsiLEdU2N0MOPoRN4JoTcUc4xLT9sK9ety49VO4xA7Ro6AT@7I57tudw@Vpc@TM35ntzjek0@O6b/ZIoZbqAn@7RAzfHeHHJy12vVnh4xZ8LW6l@cc@yWJYqQjXwdGapsKFGi01xHsJahTlMdxVp6Ot3piFFVWdbm2zipwyu8UD4xaVnHsRMKWhyb1l0Uh3mUeGGAbEBB6sdZoUnJ@y4/z3aFaSi8KDZ7HHip2dNsdwFGYeIZnLX5Srlkv@UWnr9q@lmaJVGgvRPYAH6mAik40BcGfgayE88M1EET0pHBIEwOkvZ66Rr2Xz5X/hCoaCBZFcSgoGZMwCRZQ/79NE/5leYJOrnAB6FttYutrQOcKcVGeUN1fVfN84Z1JeJUqgvM8Gx1NQ9jc6Vvu/HdvaPNm/sbhcL3knXyzKxZHnpptCoG8Z/h9Rdd0BL6SZrRgRpyY@HT1TvwQezJg3X@BQ "Python 2 – Try It Online")
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), 34 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
```
Ṫ=¥/ƝḢ€Ạȧ⁸Fe€ⱮZḄ;IẠƊȧF}fƑF{
ẈṂ>2ȧç
```
A dyadic Link accepting the words on the left and the letter groups on the right which yields `1` if valid and `0` if not.
**[Try it online!](https://tio.run/##y0rNyan8///hzlW2h5bqH5v7cMeiR01rHu5acGL5o8YdbqlAzqON66Ie7mix9gSKHus6sdytNu3YRLdqroe7Oh7ubLIzOrH88PL/h5cDVf7/Hx2tHuDp4x7k6evo7qquo6Du6ufs4x/sqh7LpROt7hzkHxAMEg129PQB0T6ujn4g2s8/AKra093XEaIYzRxnfz9/X0/XYGJMwqYf7I7QIKgkzFlAGSR1YKlgZ0dfiNH@INI/yN3RzxOi1NUHbHrsf3VPZ7CkbxBY3N0PrMExwEcdAA "Jelly – Try It Online")** Or see the [test-suite](https://tio.run/##y0rNyan8///hzlW2h5bqH5v7cMeiR01rHu5acGL5o8YdbqlAzqON66Ie7mix9gSKHus6sdytNu3YRLdqroe7Oh7ubLIzOrH88PL/h5cDVf7/H62goKwQUlRaklGpEJJaXKLgnFicWmzFpRCtHuDp4x7k6evo7qquo6Du6ufs4x/sqh7LpROt7hzkHxAMEg129PQB0T6ujn4g2s8/AKra093XEahYAWS@W2JOcSqq@ToY5jv7@/n7eroGE2NDLBb9YPeFBkElYc4FyiCpA0sFOzv6Qoz2B5H@Qe6Ofp4Qpa4@YNNj/6t7OoMlfYPA4u5@YA2OAT7qAA "Jelly – Try It Online").
[Answer]
# [Haskell](https://www.haskell.org/), 231 bytes
```
import Data.List
l&w=all((>2).length)w&&c w&&all(l!)w&&(h l)%(h w)
h=concat
l%w=null[x|x<-l,x`notElem`w]
l!(a:b:c)=a#l?(b#l)&&l!(b:c)
l!_=1>0
Just a?Just b=a/=b
_?_=1<0
c#l=findIndex(elem c)l
c(a:b:t)=last a==head b&&c(b:t)
c _=1>0
```
[Try it online!](https://tio.run/##pZBPT8JAEMXv@ymGRZo2QUSPhIU0tTY1/UNaPREC27Ja4tASuqQc/O64i5jgBaNe3uzOzPvlZQpevwnEw2G13lRbCfdc8l6wqiVBo2Ec0TRHd1YPRfkqC6sxjByU6D629NcsAK2O0sYiBcurMufK2mlYuUOc7t/3w2vs7hdlJV0U60UzI9gy@SAb5BbjbRybWRstw1BN3VLDObsd9cnjrpbAx8eSMX7DMjIfq9GwT/I2spdVufTLpdibQlEht5DkR6q0GHJtZawQfAmZCmzqNsnhSD6gkFJsa2Awpb4T0y4NE1epF6VK7UlAZ2TNV6VaWFYEYLOTqdwGJdCn7U6AFLWsB/Rs0KuLqoErOIGNKZ34gZf4oe0pLlA3coI4dRX2ksdJ4kmq11PbD3QNXDvSNYonJ4zvhfY3CtAHjvWfMjlxFIe@m/4r1ezXV3hOfjB9Hevsbup5hrpsTx07/Mwba40Tz478Ey44Rj58AA "Haskell – Try It Online")
Not the best score. Some Haskell guru will probably be able to get this under 100 bytes.
### Usage
```
["ICO","MRE","GNS","APL"]&["CROPS", "SAIL", "LEAN", "NOPE", "ENIGMA"]
```
### Explanation
```
import Data.List
l&w = all((>2).length)w &&      -- Every word has length > 2
      c w &&                    -- Every word ends with the same letter as the next one starts with
      all(l!)w &&               -- For every word: Consecutive letters are on different sides (and must exist on a side)
      (h l)%(h w)               -- All letters are used
h=concat                        -- Just a shorthand
l%w=null[x|x<-l,x`notElem`w]    -- The letters of l, with all letters of w removed, is empty
l!(a:b:c)=a#l?(b#l)&&l!(b:c)    -- Sides of the first two letters are different, recurse from second letter
l!_=1>0                         -- Until fewer than 2 letters remain
Just a?Just b=a/=b              -- Both sides must be different
_?_=1<0                         -- And must exist
c#l=findIndex(elem c)l          -- Find the side of letter c
c(a:b:t)=last a==head b&&c(b:t) -- Last letter of the first word must be same as first letter of second word, recurse starting from second word
c _=1>0                         -- Until there are fewer than 2 words
```
[Answer]
# [Haskell](https://www.haskell.org/), 231 bytes
A different Haskell variation, exactly the same size as [@Paul Mutser's](https://codegolf.stackexchange.com/a/183232/86524) :)
```
import Data.List
f x=filter(\a->length a>1)$concatMap subsequences x
g=nub.concat.f
p l(x:y)=foldl(\(m,n)c->(c,n&&length c>2&&(not$any(`isInfixOf`c)(f l))&&last m==head c))(x,True)y
z l w=null(g l\\g w)&&null(g w\\g l)&&(snd$p l w)
```
[Try it online!](https://tio.run/##LY3RaoNAEEXf/YpBRHbBCO1jQUHSEASjIelbDWSz2dWl42rdlZj8vNU0T4c798xMzcyPQJwm1XRtb@GTWRZmylhHwhhJhVb0pGSrGIWubA0sfqMebzVndsc6MMPFiN9BaC4MjE4V6eES/tehdDpAMn7caSRbvCIpSRNoylcx4YH2/ddFHr/7PtGt9Zi@k7MyqZZqLOSZUyIBKZ1NZiw0UVQLdgVOKRmDr34Q9O48AOE2P0UkFWBZVnCb/Ve@LRnnTIy@et2i0qlhSkMEXa@0BQ8e8O2m68INwN0dNgu2@XFBss/cE8zt@lDsn5NjkmYLs02SL8yL/XNhk6fbXeKepj8 "Haskell – Try It Online")
### Ungolfed
```
-- generate all invalid substrings
f :: [String] -> [String] 
f xs = filter (\x -> length x > 1) $ concatMap subsequences xs
-- utility function to flatten and remove duplicates
g :: [String] -> String
g  = nub $ concat $ f
-- verify that all conditions are satisfied along the list
p :: [String] -> [String] -> (String, Bool)
p l (x:xs) = foldl (\(m,n) c -> (c , n && length c > 2 && (not $ any (`isInfixOf` c)(f l)) && last m == head c)) (x, True) xs
-- put all the pieces together and consume input
z :: [String] -> [String] -> Bool
z l w = null (g l \\ g w) && null (g w \\ g l) && (snd $ p l w)
```
[Answer]
# [Ruby](https://www.ruby-lang.org/), 126 bytes
```
->l,w{(/(_|^)..(_|$)/!~s=w*?_)&&!!s.chars.uniq[12]&&/__|^_|_$|(_.*)\1/!~s.gsub(/(.)_\1/,'\1').chars.map{|x|l.grep(/#{x}/)}*?_}
```
[Try it online!](https://tio.run/##jZDRSsMwFIbv9xQWR9OMmlLvq4QQSiBNSovedDVs4qZQpTaWTZr66l26KWhR8Ob8EL7vP4c07fp92ETDxVXl7zov8JS5gwjZmMPA@dDRbnGtoOs6jkb3j6tGo/bl6bUIL0vXDZSFlVFz4ym0gMtwFNBWt2vbg6CyDz5YhgB@ms@rujN7U6Ft81B7wXm37wPY2/5@qKICMCKBD5KM2hmL3E6cclDOZnX7ps82ReUXIGU8zliC4xGignCZU1CW3xGSyXSUc8y4DU6xsCFkelJYnODR@LuVSCETRvP/9/4Ef7nxJptCX7f7ExzfTsCc4OS4dvwcmcVYsKNG@WnzcAA "Ruby – Try It Online")
[Answer]
# [Java (JDK)](http://jdk.java.net/), 188 bytes
```
g->w->{var v=0<1;int x=0,l,i=0,j,p,z,y=w[0][0];for(;i<w.length;i++)for(l=w[i].length,v&=y==w[i][0]&l>2,j=0,p=-9;j<l;v&=z>=0&z/3!=p/3,x|=2<<(p=z))z=g.indexOf(y=w[i][j++]);return v&x==8190;}
```
[Try it online!](https://tio.run/##nZJRj5pAEMff/RRTHwxbVw7vXnouS0IINSQCRtInw8NW0VuKQGBF0fOz2wXx2qbpNW0C7PKf/c3MzkzMKjaK19@ufJdnhYBY/qt7wRP1I@n9pm326UrwLG2Mq4SVJbiMp3DuAeT7rwlfQSmYkEuV8TXspE0JRMHT7TIEVmxL1B4F@Nz50W9WPC@iNV8xEemrF1Ysw2VoGLABet2OjMPIOFesgIpq@pjwVMCRajjBXH5jnOMTrulhqYXyIZusUAjXD2oSpVvxQvhwiBotkSd42Km4GtCatopkBonxiGPpK6ejZxLrCZHmk0G1wenh6QPNH57w8ZU@6rqS0xNCJ7pVebqOjv5GqW8@4uEwRKSIxL5IoRocKf00ftbI5Urau94LIEsgolKUQLsiAJyh71i@u7CnXmDOZ30M/bkzmy4c15zaGGzPmvmB3YcL/jNgLfx5gCEwnRmGmW16GDx/3tLO1DXfh3@JZvme7zp28F/x/iFOe6svi78g3eUx/GDfBwLLdJvEfAz@Ymp6TgPbs59zu5BeuzYj0YxU04/JrSvorSn3CZQDW7Badku5KwjMRirVUhQR2ykNuByHapknXChNDgipO5Z3Mz@ZiMySaAshVWTt5s3bZJJGB0S6qEFdiminZnuh5hIWSapsVJbnSX0Lo4XSg9wobVao4y695r1cvwM "Java (JDK) – Try It Online")
## Explanations
```
g->w->{     // Lambda accepting letter groups as a string and a list of words, in the form of an array of char arrays.
 var v=0<1;     // Validity variable
 int x=0,       // The letter coverage (rule 4)
     l,         // The length of w[i]
     i=0,       // The w iterator
     j,         // The w[i] iterator
     p,         // The previous group
     z,         // The current group
     y=w[0][0]; // The previous character
 for(;i<w.length;i++) // For each word...
  for(
     l=w[i].length,     // make a shortcut for the length
     v&=y==w[i][0]&l>2, // check if the last character of the previous word is the same as the first of the current.
                        // Also, check if the length is at least 3
     j=0,               // Reset the iteration
     p=-9               // Set p to an impossible value.
    ;
     j<l                // 
    ;
     v&=z>=0&z/3!=p/3,  // Check that each letter of the word is in the letter pool,
                        //  and that the current letter group isn't the same as the previous one.
     x|=2<<(p=z)      // After the checks, assign z to p,
                        //  and mark the letter of the pool as used.
   )
   z=g.indexOf(y=w[i][j++]); // Assign the current letter to y so that it contains the last at the end of the loop.
                             //  and fetch the position of the letter in the pool.
 return v&x==8190; // Return true if all matched
                   //  and if the rule 4 is enforced.
}
```
## Credits
* -2 bytes thanks to ceilingcat
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), 63 bytes
```
⌊⁺⁺⁺⭆η›Ｌι²⭆⪫ηω№⪫θωι⭆⪫θω№⪫ηωι⭆η⭆ι⎇μ¬⁼Φθ№νλΦθ№ν§ι⊖μ∨¬κ⁼§ι⁰§§η⊖κ±¹
```
[Try it online!](https://tio.run/##bZBNa4QwEIbv/RUhpxFS6Pbak1grFr9Yj@JBNGhoEtdsbPvv7cRiV7bOYYZ58s5X2qEx7djIZSmM0BZSoYWaFRRyvt670qKkT5sLDIxEhjeWG0i47u0AwmPk2UN3E72PQjvlF9JgnLH5SqZfIrwD9fRPPRyrEecGstHCB/Jwmht5Bd/GuuPfIBh5QrqlW8SaV94arri2vMNC1GS8xyvg5K12N@GWYEc3y9cdqL9xb0K6D5i2fTUj0vU44LvN9isob2cvy1JVNA5yyghNz6ELUVa64BcJrdkDqWgRJ9E5Tv1ofQ6zIMnLkNb18vgpfwA "Charcoal – Try It Online") Link is to verbose version of code. Explanation:
```
⌊⁺⁺⁺
```
Concatenate the below expressions and output `0` if any of them includes a `0` otherwise `1`.
```
⭆η›Ｌι²
```
For each word in the solution output whether its length is at least 3.
```
⭆⪫ηω№⪫θωι
```
For each letter in the solution output whether it appears in the puzzle.
```
⭆⪫θω№⪫ηωι
```
For each letter in the puzzle output whether it appears in the solution.
```
⭆η⭆ι⎇μ¬⁼Φθ№νλΦθ№ν§ι⊖μ∨¬κ⁼§ι⁰§§η⊖κ±¹
```
For each letter in the solution check that the previous letter is not in the same group, unless it is the first letter of a word, in which case check that it is equal to the last letter of the previous word, unless it is the first letter of the solution, in which case just ignore it.
[Answer]
# [Python 2](https://docs.python.org/2/), ~~168~~ 156 bytes
```
lambda l,w,J=''.join:(set(J(w))==set(J(l)))*all((v<1or u[-1]==v[0])*u[2:]*(2>(x in p)+(y in p))for u,v in zip(w,w[1:]+[0])for x,y in zip(u,u[1:])for p in l)
```
[Try it online!](https://tio.run/##ZY9Nb4JAEIbP9VdM6IFdXZvq0ZQmhGwIhq9AeqIcsGClWRfCh2j/PHWgX@pl3tmZZ2b2LU/NrpDLfqu99iLZb9IEBOvYWlPVh48ilytSZw1Zk45STRtTQSmdJkIQcnhaFBW00XwRa9oheozptI2Wq3hKls/kCLmEks7IaUzoFll2wNdnXpKOddFiFc9wDFtHdvpptazF1lAusShob2uRYhmewhQn4OdouuE56r6txJNJWeWygS2xGUSqb9lmoDu6yVWmctewvZCrMb2EjMDzQ5WBGuqWjWpz3UV1PZ@jctcyHf1m7m/57@K7e5BFAwlUWSKgK6qUwaZtIM32haybKmmyGt4SCbtEpiKDOpfvIpsjONAg8rqpL00owx0L7ygMlPHYy9n41X@uOcNzPcfi4Q03@EUE/aKiX1T0e0N/m8P@vwPXVGjozrjTw@gFpu5a4xS3x7X9Fw "Python 2 – Try It Online")
Returns `1` for truthy, `0` for falsey.
]