# What are the odds in Scrabble of not being able to make a legal move on opening turn

I've been playing the game for 50 years and this happened to me recently:

I had the following rack

``````PBQZJDH
``````

And so I could not play a legal move.

I've been trying to work out the odds of this (no legal word on the first turn) happening.

• I was going to calculate the odds of a consonant-only rack as the lower bound, but I was surprised to learn there are 40 valid Scrabble words with no vowel (Y included). – Nuclear Hoagie Aug 7 '20 at 13:37
• From a computer science perspective, this is a very interesting question. My first guess would be to pare down the legal scrabble dictionary by removing all words that a subset of the letters forms another word (for example, AND would be removed because AN is a valid word), then for each word find the number of possible pulls from the bag that can form that word and no other word previously examined. Problem is this this at least an NP problem, and we probably don't have enough time left in the universe to calculate it. – DenisS Aug 7 '20 at 13:57
• Did I just get nerd sniped? – DenisS Aug 7 '20 at 14:22
• @DenisS Where did you get 9.33x10^157? – Acccumulation Aug 9 '20 at 3:56
• Can you estimate: (1) how many games you've played in 50 years. (2) how many times you've seen this happen? That will serve as a sanity check on any results that come out of the computations.... – John Aug 11 '20 at 19:50

## 4 Answers

(NOTE : Final revision of my original answer)

The odds of the first tileset not having a single valid word is exactly 91,595,416 / 16,007,560,800 or .5722%, with it occuring once every 174.76378 games. This value is calculated by using the dictionary found in this answer, but can be adapted for any other dictionary.

This was brute-forced via python. Code will be available at the end of the answer.

We start by noticing that any tileset that contains at least one blank tile can play a valid word. The only letter that does not form a two letter word is the letter "V", and there are only 2 "V" tiles in a scrabble game. Therefore, any tileset with one blank can form a valid word, because (?VV....) must contain 4 more letters, one of which will combine with the "?" to form a valid word.

We also discovered, after the fact, that all possible tilesets that contain the letter "O" are also, in fact, valid. The only 6 tile tileset that contains "O" and cannot form a word is "OCCVVQ" and any other letter will form a word. "A" is the only other letter that doesn't form a two letter word with "O" but "AVO" and "OCA" are both valid words, and you can't include more than one O or one A or else you can form "AA" or "OO".

These observations remove 53.32% of all possible tilesets, and allows us to perform the rest of our calculations using a 90-tile scrabble set that has no blanks and no Os in it.

We then remove from the dictionary all words that satisfy one of the following criteria

1. Word is longer than 7 letters long (can't spell an 8 letter word on the first turn)
2. A subset of the tiles required to form that word can also make another word (we're just looking to see if we can form a valid word, therefore there is no reason to check to see if a tileset can form the word "AND" when "AN" is a perfectly valid word as well)
3. Word requires at least one blank tile to solve (any tileset with a blank is valid, therefore no reason to examine these words). These are the words FUFF (3rd F), JUJU (2nd J), KECK and KUKU (2nd K), SYZYGY (3rd Y), ZIZ, ZUZ, and ZZZ (2nd/3rd Z).
4. Word requires an O to solve. All O tilesets are valid and we've removed them from the bag.

By applying these three rules, we can reduce the scrabble dictionary down to 149 words out of the original 280k~.

AA AB AD AE AG AH AI AL AM AN AR AS AT AW AX AY BE BI BRR BUB BUD BULK BULL BY CH CIRRI CIVIC CLY CRY CUB CUD CUE CUFF CULL CUZ CWM DE DI DRY DUD EE EF EGG EH EL EM EN ER ES ET EUK EW EX FA FLU FUB FUCK FUD FY GHYLL GI GRR GU GYP HI HM HYP ICE ICY IF IN IS IT IVY IWI JA JEU JIZ JUD JUKU KA KEG KI KUDU KUZU KY LI LUCK LUD LULL LUV LUZ MI MM MU MY NTH NU NY PA PE PFFT PHT PI PLY PRY PWN PYX QI QUA RHY RIZ SH SLY SPY ST SWY THY TRY TWP TYG TYPP TYPY UH ULU UP UR US UT UVA VAC VAV VEG VIE VLY WHY WIZ WRY WUD WULL WUZ XI XU XYLYL YE YIRR YU ZA ZE ZO

At this point, we are going to brute force the number of invalid tilesets to get the numerator. We do this by examining tilesets that are in alphabetical order, but do not care if it's unique. For example, we will examine (A1A2BCDEF) and (A1A3BCDEF) but not (A2A1BCDEF), where A1, A2, and A3 refer to distinct A tiles in the bag.

The first tileset examined is (A1A2A3A4A5A6A7), followed by (A1A2A3A4A5A6A8), then (A1A2A3A4A5A6A9), and then finally (A1A2A3A4A5A6B1). We continue down that path in the way that only a computer can do to determine every single alphabetized combination.

(Note that in the final version of my code, we change the definition of "alphabetical order" in order to get a speed improvement, but the final solution is the same. Also, for speed reasons, we would never actually examine (AA.....) because "AA" is a valid word, so we would skip all tilesets of the form (AA.....))

After running through all the tilesets, we end with a final count of 91,595,416 tilesets. This is our numerator, and the denominator is very easily calculated

100! / ((100-7)! * 7!) = 16,007,560,800

This is the calculation for finding the number of combinations possible, without replacement, and not caring about order (which we don't because otherwise this problem would be an few orders of magnitude harder to brute force.

I'm going to be putting some interesting notes down here as I play around with the program.

• There is exactly 1 distinct tileset that has an "A" in it and is still invalid. This tileset is "ACCUUUU". The proof is left as an exercise for the reader.
• Of the 91,595,416 invalid tilesets found, there are only 22,308 that contain exclusively vowels. This is a much smaller number than I originally thought, but from reading the valid word list it makes sense. As previously discussed, A only has the invalid tileset "ACCUUUU" and all O tilesets are valid. Combined with the fact that "EE" is also a valid word, all vowel only tilesets without a valid word have to be of the form "EI...U..." or "I...U...".
• Tilesets with a mix of vowels and consanants takes up a slightly larger amount of invalid tilesets, at 106,201 possible combinations.
• Shocking no one, the vast majority of invalid tilesets are consonant only tilesets. Of the 91,595,416 invalid tilesets, 91,466,907 of them are consonant only sets, which makes up 99.86% of all invalid tilesets.
• Once the dictionary is purged of words that contains the letters [AEIOUY] there are only 13 words that can still be formed. These 13 words can be, in turn, checked by looking at the letters [HPMRS]. This means that any tileset that contains nothing but the letters [BCDFGJKLNQTVWXZ] is invalid. This comprises 15 of the 26 letters and 38 of the tiles in the bag. Tilesets that contain only these letters comprise roughly 12% of all invalid tilesets.

Code, if anyone wants to run it for themselves (WARNING: slow, running on a beefy computer it still about a half hour to spit out an answer)

``````words = []

words_in_dictionary = 0
words_short_enough = 0

def is_all_c(arr1):
for c in arr1:
if c in ['A','E','I','O','U']:
return False
return True

def is_all_v(arr1):
for v in arr1:
if v in ['B','C','D','F','G','H','J','K','L','M','N','P','Q','R','S','T','V','W','X','Y','Z']:
return False
return True

#basic function to check if an array, arr1, is fully within a second array, arr2
def i_in_j(arr1, arr2):
for a in arr1:
if a in arr2:
arr2.remove(a)
else:
return False
return True

#basic function to check to see if word can be made from the tileset passed in
def is_valid_tileset(tileset):
for word in words:
if i_in_j(word["sorted"][:], tileset[:]):
return word["base"]
return None

# we have a big dictionary, 270k-ish words long, we need to load it
print("loading dictionary")
with open("dictionary.txt", "r") as dictfile:
for line in dictfile:
words_in_dictionary = words_in_dictionary + 1
base_word = line.strip()

#we are going to parse out the words longer than 7 letters now because it's quicker
#we are also going to get rid of words with "O" in it
if len(base_word) <= 7:
if not "O" in base_word:
words_short_enough = words_short_enough + 1
word = {"base": base_word, "sorted": sorted(base_word)}
words.append(word)

print("total words in dictionary is " + str(words_in_dictionary))
print("words 7 letters or shorter is " + str(words_short_enough))

# now we need to build our dictionary of unique words
# any word where a subset of the letters in that word can be used to build another word will be discarded
# for example, "AND" will be discarded because we can make "AN" out of that word
i = 0
while i < len(words):
temp_sorted_working_word = words[i]["sorted"]

j = 0
while j < len(words):
if i == j:
j = j + 1
continue

if i_in_j(words[i]["sorted"][:], words[j]["sorted"][:]):
del words[j]
if i > j:
i = i - 1
j = j - 1
elif j > i:
j = j - 1

j = j + 1
i = i + 1

# there are also 8 words from this list that cannot be built without blanks, and we know that any tileset
# with at least one blank is valid
i = 0
while i < len(words):
if words[i]["base"] in ["FUFF", "JUJU", "KECK", "KUKU", "SYZYGY", "ZIZ", "ZUZ", "ZZZ"]:
del words[i]
else:
i = i + 1

print("unique minimilized word combinations is " + str(len(words)))

# this is an array of all tiles in scrabble, minus the blanks (since we don't care about them)
# because we're pruning words from the dictionary, we've rearranged the letters to try and remove as many
# words as possible as quickly as possible, to make lookups run faster, this means that the vowels and
# R,S,T,L,N have been moved up the list, it doesn't really impact anything, as alphabetical order is arbitary
# and nothing from this point forward depends on us having a sorted list
letters = ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'Y', 'Y', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'I', 'I', 'I', 'I', 'I', 'U', 'U', 'U', 'U', 'H', 'H', 'P', 'P', 'M', 'M', 'R', 'R', 'R', 'R', 'R', 'R', 'T', 'T', 'T', 'T', 'T', 'T', 'W', 'W', 'C', 'C', 'N', 'N', 'N', 'N', 'N', 'N', 'S', 'S', 'S', 'S', 'B', 'B', 'F', 'F', 'G', 'G', 'G', 'L', 'L', 'L', 'L', 'D', 'D', 'D', 'D', 'J', 'K', 'Q', 'V', 'V', 'X', 'Z']

invalid_tilesets = 0

for a in range(0, 84):
# if we've finished using a letter in the tileset, we're gonna remove the words in the condensed dictionary
# that utilize those letters, this is more of a speed thing than anything else because those dictionary lookups
# are expensive
if a > 0 and letters[a-1] != letters[a]:
i = 0
while i < len(words):
if i_in_j([letters[a-1]], words[i]["sorted"]):
del words[i]
else:
i = i + 1

print("invalid_tilesets = " + str(invalid_tilesets) + " | new_first_letter = " + letters[a] + " | words in dictionary = " + str(len(words)))

for b in range(a+1, 85):
if not is_valid_tileset([letters[a], letters[b]]):
for c in range(b+1, 86):
if not is_valid_tileset([letters[a], letters[b], letters[c]]):
for d in range(c+1, 87):
if not is_valid_tileset([letters[a], letters[b], letters[c], letters[d]]):
for e in range(d+1, 88):
if not is_valid_tileset([letters[a], letters[b], letters[c], letters[d], letters[e]]):
for f in range(e+1, 89):
if not is_valid_tileset([letters[a], letters[b], letters[c], letters[d], letters[e], letters[f]]):
for g in range(f+1, 90):
if not is_valid_tileset([letters[a], letters[b], letters[c], letters[d], letters[e], letters[f], letters[g]]):
invalid_tilesets += 1

if invalid_tilesets % 10000 == 0:
print("invalid_tilesets = " + str(invalid_tilesets) + " | " + str([letters[a], letters[b], letters[c], letters[d], letters[e], letters[f], letters[g]]) + " | " + str([a,b,c,d,e,f,g]))

print("invalid_tilesets = " + str(invalid_tilesets))
``````
• If someone knows the answer could they put it in odds of like 1000 to 1. – Jim McGuigan Aug 8 '20 at 10:09
• CH is in the list you're using. – studog Aug 11 '20 at 3:16
• I used the beginning of your program to run a quick monte-carlo experiment and got 0.7% probability of not being able to make a word. – Stef Aug 11 '20 at 13:58
• A few comments: Your word list is slightly longer than required, e.g. you include ACE but that is unnecessary as AE is already in the list. You also have some words in your list which have too many of some letters (e.g. ZZZ) as the list you used is allowing for words which require blanks. I ended up with 173 words in my tests. – borrible Aug 11 '20 at 14:29
• My attempt at a solution to the program gave a probability of 0.57% or approximately 1 in 175. – borrible Aug 11 '20 at 14:31

I used the beginning of the program in @DenisS's answer to build the Scrabble dictionary, then I used it to write a small monte-carlo program to estimate the probability that no word can be formed with seven random tiles.

The result is a 0.58% +- 0.27% probability that no word can be formed.

Output

``````\$ python3 get_proba.py 1000 50
loading dictionary
total words in dictionary is 279497
words 7 letters or shorter is 77459
Running for 50 experiments of 1000 draws...
Ran for 50 experiments of 1000 draws.
Successes: [996, 996, 996, 995, 992, 996, 998, 993, 994, 993, 992, 993, 998, 994, 994, 986, 994, 996, 990, 994, 997, 998, 994, 993, 993, 991, 999, 991, 997, 996, 993, 989, 995, 996, 998, 996, 995, 996, 992, 992, 998, 994, 993, 989, 993, 991, 991, 999, 995, 995]
Proba of failure = 0.00582000000000005 +- 0.0027472895733795517
``````

Code

``````def build_dict():
words = []
words_in_dictionary = 0
words_short_enough = 0
print("loading dictionary")
with open("dictionary.txt", "r") as dictfile:
for line in dictfile:
base_word = line.strip()
if len(base_word) > 0:
words_in_dictionary = words_in_dictionary + 1
if len(base_word) <= 7:
words_short_enough = words_short_enough + 1
word = {"base": base_word, "sorted": sorted(base_word)}
words.append(word)
print("total words in dictionary is " + str(words_in_dictionary))
print("words 7 letters or shorter is " + str(words_short_enough))
ok_combinations = [''.join(word["sorted"]) for word in words]
return(ok_combinations)

def flatten(ll):
return [x for l in ll for x in l]

def build_letter_bag():
return flatten([['A']*9, ['B']*2, ['C']*2, ['D']*4, ['E']*12, ['F']*2, ['G']*3, ['H']*2, ['I']*9, ['J']*1, ['K']*1, ['L']*4, ['M']*2, ['N']*6, ['O']*8, ['P']*2, ['Q']*1, ['R']*6, ['S']*4, ['T']*6, ['U']*4, ['V']*2, ['W']*2, ['X']*1, ['Y']*2, ['Z']*1, ['*']*2])

dico = build_dict()
letter_bag=build_letter_bag()

from itertools import chain, combinations

def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def can_make_word(letters):
if '*' in letters:
return True
return any((''.join(subset) in dico) for subset in powerset(sorted(letters)))

import random

def montecarlo(n):
nb_ok = 0
for i in range(n):
letters = random.sample(letter_bag, 7)
nb_ok += (1 if can_make_word(letters) else 0)
return nb_ok

import statistics

def run_experiments(nb_draws, nb_experiments):
nb_ok_list = [montecarlo(nb_draws) for i in range(nb_experiments)]
average = statistics.fmean(nb_ok_list)
stdev = statistics.pstdev(nb_ok_list, mu=average)
return average, stdev, nb_ok_list

def get_args(argv):
nb_draws, nb_exp = 1000, 1
if len(argv) > 1:
nb_draws = int(argv)
if len(argv) > 2:
nb_exp = int(argv)
return nb_draws, nb_exp

def main(argv):
random.seed()
nb_draws, nb_experiments = get_args(argv)
print('Running for {} experiments of {} draws...'.format(nb_experiments, nb_draws))
average, stdev, l = run_experiments(nb_draws, nb_experiments)
print('Ran for {} experiments of {} draws.'.format(nb_experiments, nb_draws))
print('Successes:', l)
print('Proba of failure = {} +- {}'.format((nb_draws - average)/nb_draws, stdev/nb_draws))

import sys
if __name__=='__main__':
main(sys.argv)
``````

Rendering unto Caesar:

• The code in `build_dict()` is from @DenisS's answer;
• The rest of the code is from me;
• The file `dictionary.txt` is the 2019 Collins Scrabble Words file linked in this answer to a related question;
• The justification that a hand with a blank tile can always score is in @DenisS's answer (`if '*' in letters: return True` in my code);
• The basic idea of the algorithm is to use a Monte-Carlo method, because browsing the dictionary is acceptable but trying out all possible hand combinations is unreasonable.
• Considering that both myself and borrible calculated the odds as 0.572% you're pretty close with the Monte Carlo method. – DenisS Aug 25 '20 at 16:26
• The average 0.58% is pretty close, but the standard deviation 0.27% is huge. I should look for a concentration inequality that gives better bounds than just writing "average +- standard dev" – Stef Aug 26 '20 at 8:53

91592097 in 16007560800 which is approximately 0.572% (or 1 in 175).

Some of what follows is already covered in @DenisS's answer and I have used the same dictionary of words (Collins Scrabble Words (2019)) for easy comparison. Note in particular in that answer the argument for discounting blanks when looking for valid combinations without words (i.e. that the only letter not in a 2 letter word is a V and that there are not enough of those to fill up our 7 selected tiles) and the discussions on pruning.

The following approach is rather “quick and dirty” and relies on several tools that are available on multiple platforms.

Firstly, I took the dictionary and alphabetised the letters in each word. (I removed duplicates, caused by words which were anagrams of each other, although this was not necessary. This resulted in a dictionary containing 247491 words.)

The 2 letter words (93 unique alphabetised words) were then removed and the dictionary pruned so that it no longer contained any words which contained all the letters of one of those words. (For example, the word `AE` removed words from the list including those where the letters were adjacent such as `AESSSY` and where they were not adjacent `AABCELN`).

This was done as a simple iteration over the 2 letter words in `bash` using `grep` with some shell parameter expansions.

``````for f in \$(cat 2LetterWords) ; do grep -v \${f:0:1}".*"\${f:1:1} temp > a; rm temp; mv a temp; done
``````

The 3 letter words (61 unique alphabetised words) were then extracted and the new dictionary pruned in a similar manner. 4 letter words (15) and 5 letter words (4) were similarly extracted. At each stage, the handful of dictionary words that could not be formed without using blanks were also removed.

As all other words in the dictionary contain the letters that enable us to make one of these 2-5 letter words, these are the only ones we need to consider. I.e. we only need to find the combinations of 7 tiles where we cannot make any of the following 173 words:

AA AB AD AE AF AG AH AI AJ AK AL AM AN AP AR AS AT AW AX AY AZ BE BI BO BY CH DE DI DO EE EF EH EL EM EN EO EP ER ES ET EW EX EY EZ FI FO FY GI GO GU HI HM HO HS HU IK IL IM IN IO IP IQ IS IT IX JO KO KY LO MM MO MU MY NO NU NY OO OP OR OS OT OU OW OX OY OZ PU RU ST SU TU UX UY ACO ACV AOV AQU AUV AVV BBU BCU BDU BFU BRR CDU CEI CEU CIY CLY CMW CRY CUZ DDU DFU DJU DLU DRY DUW EGG EGK EGV EIV EJU EKU FLU GPY GRR GTY HNT HPT HPY HRY HTY HWY IIW IJZ IRZ IVY IWZ LPY LSY LUU LUV LUZ LVY NPW PRY PSY PTW PXY RTY RWY SWY UWZ BKLU BLLU CFFU CFKU CKLU CLLU DKUU FFPT IRRY JKUU KUUZ LLLU LLUW PPTY PTYY CCIIV CIIRR GHLLY LLXYY

There are 16,007,560,800 (100 C 7) combinations of tiles we can pick, although some of these combinations will be indistinguishable from each other. If we only consider the number of combinations that are distinguishable we are reduced to 3,199,724 which is a far more tractable value and, from any given distinguishable combination we can easily calculate the number of different combinations of tiles which are indistinguishable.

That value can be calculated using some brute-force methods. A bunch of nested loops in C such as

``````for (A=0;A<=anMax;A++)
for (B=0;B<=anMax;B++)
for (C=0;C<=anMax;C++)
for (D=0;D<=anMax;D++)
…
``````

where the `anMax` array (offset from 0) is set to the number of available tiles for each letter struggles but a few short-circuit checks to ensure that we do not go over the required number of tiles

``````…
for (C=0;C<=anMax;C++) if (A+B+C<8)
…
``````

is sufficient to run the calculation in a couple of seconds. (My first attempt, adding checks spaced out on the C, E, G, L, O, S and W was good enough.)

A little more shell scripting in `awk`, such as:

``````awk '{print (substr(\$0,1,1)" && "substr(\$0,2,2)") ||"}' 2LetterWords
``````

with a little bit of editing (to account for repeated letters), e.g. (for the two letter words)

``````if (
(A>1) || (A && B) || (A && D) || (A && E) || (A && F) || (A && G) || (A && H) || (A && I) || (A && J) || (A && K) || (A && L) || (A && M) || (A && N) ||
(A && P) || (A && R) || (A && S) || (A && T) || (A && W) || (A && X) || (A && Y) || (A && Z) || (B && E) || (B && I) || (B && O) || (B && Y) || (C && H) ||
(D && E) || (D && I) || (D && O) || (E>1) || (E && F) || (E && H) || (E && L) || (E && M) || (E && N) || (E && O) || (E && P) || (E && R) || (E && S) ||
(E && T) || (E && W) || (E && X) || (E && Y) || (E && Z) || (F && I) || (F && O) || (F && Y) || (G && I) || (G && O) || (G && U) || (H && I) || (H && M) ||
(H && O) || (H && S) || (H && U) || (I && K) || (I && L) || (I && M) || (I && N) || (I && O) || (I && P) || (I && Q) || (I && S) || (I && T) || (I && X) ||
(J && O) || (K && O) || (K && Y) || (L && O) || (M>1) || (M && O) || (M && U) || (M && Y) || (N && O) || (N && U) || (N && Y) || (O>1) || (O && P) ||
(O && R) || (O && S) || (O && T) || (O && U) || (O && W) || (O && X) || (O && Y) || (O && Z) || (P && U) || (R && U) || (S && T) || (S && U) || (T && U) ||
(U && X) || (U && Y)
) return 0;
``````

gave some simple conditional checks to ensure the word list did not appear.

There are 309831 distinguishable combinations where none of the 2-letter words can be formed. 252242 if we ensure 2 and 3 letter words can not be formed. 251180 excluding 2,3 and 4 letter words and down to 251021 excluding the complete list.

We cannot just look at 251021 / 3199724 as our probability as different distinguishable combinations have different numbers of corresponding tile combinations. The distinguishable combinations excluding the word list tend to use the rarer tiles which means they tend to have fewer corresponding tile combinations.

We can count the number of combinations that correspond to a given distinguishable combination by looking at the number of ways the corresponding letters might have been chosen, which was calculated as:

``````Cr(0,A)* nCr(1,B)* nCr(2,C)* nCr(3,D)* nCr(4,E)* nCr(5,F)* nCr(6,G)* nCr(7,H)* nCr(8,I)* nCr(9,J)*
nCr(10,K)* nCr(11,L)* nCr(12,M)* nCr(13,N)* nCr(14,O)* nCr(15,P)* nCr(16,Q)* nCr(17,R)* nCr(18,S)*
nCr(19,T)* nCr(20,U)* nCr(21,V)* nCr(22,W)* nCr(23,X)* nCr(24,Y)* nCr(25,Z)
``````

This gives us 91,592,097 combinations (of which there are 251,021 distinguishable sets) out of 16,007,560,800.

• Those odds seem very high, given that OP has played for 50 years and never had this happen before.... – John Aug 11 '20 at 19:49
• @John If the OP had played once a month for 50 years, the odds of never having personally drawn such a rack would be (1-0.00572)^600 which is about 3%. If they've played half as often it's about 18%. Neither of those values look unreasonable. This answer is also in accordance with Stef's answer which was arrived at using a different methodology (Monte Carlo simulation rather than combinatoric enumeration). – borrible Aug 13 '20 at 8:53
• Also this can only happen when the OP is the first player, which is not every game. – Stef Aug 13 '20 at 15:11
• @borrible fair enough. I was thinking of my aunt, who averaged 30 games a month for 50 years, not someone playing only once / month. – John Aug 14 '20 at 17:23
• @borrible I finally got off my lazy ass and finished my program, which went about finding the combinations differently, but got an answer that agrees with yours – DenisS Aug 21 '20 at 17:32

I'm going to make an estimate from the following assumption:

Any hand that contains at least one vowel, y, or a blank allows a valid move. Any hand that contains entirely consonants does not. Obviously there are exceptions, but they should be rare enough to have a negligible effect (and the false positives and false negatives work to cancel each other out).

There are 46 of these tiles and 54 that are not. The chance of consecutively drawing 7 consonants is therefore:

54/100 * 53/99 * 52/98 * 51/97 * 50/96 * 49/95 * 48/94

This works out at 1.11%, or about 1 in 90 games.

• I like the approximation approach but your problem is that your false positives and false negatives are highly unbalanced. In particular, there are a huge number of all consonant combinations that include valid words. The top problematic short valid words being HS, HNT, CH and HM. – borrible Aug 11 '20 at 17:54
• @borrible Fair point, and I was a bit off. – Studoku Aug 11 '20 at 21:42
• I love the simplicity of this approximation. Would be even better if it could also give an upper and lower bound on the true probability by estimating by how much it is off. – Stef Aug 12 '20 at 12:27