Repository navigation
Expand file tree
/
Copy pathscrabbleSolver.py
More file actions
230 lines (182 loc) · 7.15 KB
/
Copy pathscrabbleSolver.py
File metadata and controls
230 lines (182 loc) · 7.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/usr/bin/python
#Copyright 2012 Chad Chabot
#
#chad@chadchabot.com
#github.com/chadchabot
import sys
import re
from pydoc import help
class Counter( dict ):
"""
Counter is an extension of the dict class, where keys that are not
found in the dict will not return an error, but will return a value of 0.
The error message should be re-directed towards an error log.
"""
def __missing__( self, key ):
#print "missing key error for [" + key + "]"
return 0
def __keyerror__( self, key ):
#print "key error for [" + key + "]"
return 0
def testWord( dictWord, letterLookupTable, must_includes ):
"""
testWord() checks to see if 'dictWord' can be created using the contents
of 'letterLookupTable'.
Parameters:
dictWord
A string of arbitrary size.
letterLookupTable
A Counter object containing a tally of the letters in the player's
hand.
The testWord() function compares a word from a dictionary or other source
(the 'dictWord' param) against a supplied lookup table (a Counter object)
containing from the tiles in the player's hand ('letterLookupTable' param).
If 'dictWord' can be formed using the letters in 'letterLookupTable',
testWord() returns the value of the word, according to the point
distribution defined in calculateScore().
If 'dictWord' cannot be formed, testWord() returns -1.
"""
# for each letter in dictWord, add to a dictionary
letterCount = Counter()
bonusValue = 50
blanksRequired = 0
wordScore = 0
for letter in must_includes:
if (dictWord.find( letter ) == -1):
return -1
for letter in dictWord:
if letter in letterLookupTable:
letterCount[ letter ] += 1
if ( letterLookupTable[ letter ] < letterCount[ letter ] ):
return -1
else:
wordScore += calculateScore( letter )
else:
if blanksRequired < letterLookupTable[ '?' ]:
blanksRequired += 1
else:
return -1
# if giving the player a bonus for using all 7 tiles, use the return below
#return wordScore if len( dictWord ) != 7 else wordScore + bonusValue
return wordScore
def calculateScore( word ):
"""
calculateScore() totals the point value of the parameter 'word'.
parameters:
word
a string or single character, made up of alpha characters and
question marks ["?"]
reference for point values
https://en.wikipedia.org/wiki/Scrabble_letter_distributions
"""
scoreTable = ( {'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, 'f': 4, 'g': 2,
'h': 4, 'i': 1, 'j': 8, 'k': 5, 'l': 1, 'm': 3, 'n': 1,
'o': 1, 'p': 3, 'q': 10, 'r': 1, 's': 1, 't': 1, 'u': 1,
'v': 4, 'w': 4, 'x': 8, 'y': 4, 'z': 10 } )
# for key in scoreTable:
# print str( key ) + " = " + str( scoreTable[ key ] )
wordScore = 0
for letter in word:
wordScore += scoreTable[ letter ]
return wordScore
def buildPlayerHand( playerTiles, must_includes ):
"""
buildPlayerHand() populates and returns a Counter based on the supplied string.
Parameters:
playerTiles
A string consisting of letters and/or question marks (provided the
string has already been checked for valid characters).
Return:
playerHand, the tally of letters contained in 'playerTiles'.
"""
playerHand = Counter()
playerHand.clear()
for letter in playerTiles:
# add to lookup table
playerHand[ letter ] += 1
for letter in must_includes:
playerHand[ letter ] += 1
return playerHand
def findLongestWord( wordList ):
"""
findLongestWord() takes in a list of words and returns the longest, or a list
containing the longest words.
Parameters:
wordList
an unordered list of words.
Return:
'longestWord' an array of the longest words in 'wordList'.
"""
length = 0
longestWord = []
for word in wordList:
wordLength = len( word )
if ( wordLength == length ):
# add to list
longestWord.append( word )
elif ( wordLength > length ):
del longestWord[:]
length = len( word )
longestWord.append( word )
return longestWord
def validateInput( string ):
badCharacterFound = re.search("[^a-zA-Z?]", string )
return True if badCharacterFound else False
def main():
if ( len( sys.argv ) < 2 ):
print "\tYou need to supply an input string"
sys.exit( 1 )
# what about a flag to show all/some/limited number of results, not just top?
string = sys.argv[ 1 ].lower()
if not string:
sys.exit( 1 )
must_includes = ''
if ( len( sys.argv ) == 3):
must_includes = sys.argv[ 2 ].lower()
# check for any non-alpha characters in input
if validateInput( string ):
print "\tA character that is not a letter was found.\n\tPlease don't use numbers or any weird stuff like that."
sys.exit( 1 )
# build player hand
playerHand = buildPlayerHand( string, must_includes )
playerHandLength = len( string )
# for key in playerHand:
# print key + " = " + str( playerHand[ key ] )
# print "\t -- " + str( playerHand )
# load dictionary file
with open( "/usr/share/dict/words" ) as fp:
dict_file = fp.readlines()
# need a place to store words that can be made from input string
wordList = Counter()
# go through each word in the dict file
for word in dict_file:
# trim the newline character
word = word[:-1]
# is 'word' longer than the length of input string?
"""
Can this check be combined with the readlines() or dict_file building step?
Use a system call (grep?) and pipe results to dict_file?
If the script is a "run once and quit", this is an acceptable approach.
If the script has a longer life/persistance, then all words must be loaded
"""
if ( len( word ) <= playerHandLength ):
# check if 'word' can be built using 'playerHand'
wordScore = testWord( word, playerHand, must_includes )
if ( -1 != wordScore ):
# add to wordList
wordList[ word ] = wordScore
#print wordList
# check for longest word
longest = findLongestWord( wordList )
print "The longest word(s) to be made given [" + string + "]:"
for word in longest:
print "'" + str( word ) + "' is worth " + str( wordList[ word ] )+ " points"
highestValue = 0
print "The highest scoring words that can be made from [%s] are:" % string
for key, value in sorted( wordList.iteritems(), key=lambda ( k,v ): ( v, k ), reverse=True ):
if value > highestValue:
highestValue = value
if value == highestValue:
print "'%s' is worth %s points" % ( key, value )
if __name__ == '__main__':
main()