-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboggleSolver.cpp
270 lines (246 loc) · 10 KB
/
boggleSolver.cpp
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#include <iostream>
#include <cmath>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
//#define DEBUG_
const int MIN_WORD_SIZE = 3;
typedef std::vector<std::string> StringArray;
typedef StringArray GameBoard;
struct Vertex{
typedef std::pair<size_t, size_t> Position;
typedef std::list<Position> NeighborList;
typedef NeighborList::const_iterator ConstNeighborIterator;
typedef NeighborList::iterator NeighborIterator;
Position position;
NeighborList neighbors;
Vertex(){};
Vertex(const Position& p): position(p){}
Vertex(const size_t row, const size_t col): position(Position(row,col)){}
};
enum MatchResult{
NO_MATCH,
MATCH_AND_IS_FULL_WORD,
MATCH_AND_IS_JUST_PREFIX
};
typedef std::vector<std::vector<Vertex> > Neighbors;
void usage();
StringArray readDictionary(const std::string& filePath);
GameBoard readBoard(const std::string& filePath);
Neighbors generateNeighbors(const GameBoard& board);
MatchResult isPrefixMatch(const StringArray& dictionary, const std::string& prefix);
MatchResult isWord(const StringArray& dictionary, const std::string& word);
bool isBoggleWord(const StringArray& dictionary,const std::string& prefix);
StringArray generateBoggleSolution(const GameBoard& board,
const StringArray& dictionary,
const Neighbors& neighbors);
//operators on current vertex; a helper to the other generateBoggleSolution
void generateBoggleSolution(std::string& currentPrefix,
std::vector<Vertex::Position>& usedPositionList,
StringArray& solution,
const Vertex::Position& currentVertex,
const GameBoard& board,
const StringArray& dictionary,
const Neighbors& neighbors);
template<typename Iterator>
void print(Iterator begin, Iterator end, const std::string& delim = " ");
ostream& operator <<(ostream& stream, const Vertex::Position& p){
return stream << "(" << p.first << "," << p.second << ")";
}
int main(int argc, char *argv[])
{
if(argc < 3){
usage();
}
else
{
GameBoard board = readBoard(argv[1]);
StringArray dictionary = readDictionary(argv[2]);
Neighbors neighborMap = generateNeighbors(board);
#ifdef DEBUG_
time_t clkBegin = clock();
#endif
StringArray result = generateBoggleSolution(board,dictionary,neighborMap);
std::sort(result.begin(), result.end());
StringArray::iterator newEnd = std::unique(result.begin(), result.end());
StringArray::iterator begin = result.begin();
while(begin != newEnd){
cout << *begin << endl;
++begin;
}
#ifdef DEBUG_
cout << "Time took = " << float(clock() - clkBegin) / float(CLOCKS_PER_SEC) << "s" << endl;
cout << "Board size = " << board.size() << "x" << board.size() << endl;
cout << "Number of words generated = " << std::distance(result.begin(), newEnd) << endl;
#endif
}
return 0;
}
void usage(){
cout << "Usage = <gameboard.txt> <dictionary.txt> " << endl;
}
StringArray generateBoggleSolution(const GameBoard& board,
const StringArray& dictionary,
const Neighbors& neighbors)
{
StringArray solutionList;
solutionList.reserve(250);
for(int row = 0; row < board.size(); ++row){
for(int col = 0; col < board[row].size(); ++col){
std::vector<Vertex::Position> usedList(1,Vertex::Position(row,col));
std::string prefix(1,board[row][col]);
generateBoggleSolution(prefix,usedList,solutionList,Vertex::Position(row,col),board,dictionary,neighbors);
}
}
return solutionList;
}
void generateBoggleSolution(std::string& currentPrefix,
std::vector<Vertex::Position>& usedPositionList,
StringArray& solution,
const Vertex::Position& currentPosition,
const GameBoard& board,
const StringArray& dictionary,
const Neighbors& neighborMap)
{
const std::list<Vertex::Position>& neighbors = neighborMap[currentPosition.first][currentPosition.second].neighbors;
for(std::list<Vertex::Position>::const_iterator itr = neighbors.begin(); itr != neighbors.end(); ++itr)
{
MatchResult matchResult = isPrefixMatch(dictionary, currentPrefix);
switch (matchResult)
{
case MATCH_AND_IS_JUST_PREFIX:{
if(currentPrefix.size() >= MIN_WORD_SIZE && isWord(dictionary,currentPrefix) == MATCH_AND_IS_FULL_WORD){
solution.push_back(currentPrefix);
}
//continue if only neighbor isn't in our marked list
if(std::find(usedPositionList.begin(), usedPositionList.end(), *itr) == usedPositionList.end())
{
//add new letter to prefix
currentPrefix.push_back(board[itr->first][itr->second]);
//add position to used list
usedPositionList.push_back(*itr);
generateBoggleSolution(currentPrefix,
usedPositionList,
solution,
*itr,
board,
dictionary,
neighborMap);
//undo changes
currentPrefix.erase(currentPrefix.end() - 1);
usedPositionList.erase(usedPositionList.end() - 1);
}
break;
}
default:
break;
}
}
}
inline MatchResult isPrefixMatch(const StringArray& dictionary, const std::string& prefix)
{
int start = 0, end = int(dictionary.size());
while( start <= end){
int mid = start + (end - start) / 2;
int compResult = dictionary[mid].compare(0,prefix.size(),prefix);
if(compResult == 0 ){
return MATCH_AND_IS_JUST_PREFIX;
}else if(compResult < 0){
start = mid + 1;
}else{
end = mid - 1;
}
}
return NO_MATCH;
}
inline MatchResult isWord(const StringArray& dictionary, const std::string& word){
int start = 0, end = int(dictionary.size());
while( start <= end){
int mid = start + (end - start) / 2;
if(dictionary[mid] == word){
return MATCH_AND_IS_FULL_WORD;
}else if(dictionary[mid] < word){
start = mid + 1;
}else{
end = mid - 1;
}
}
return NO_MATCH;
}
//generate map for constant time neighbor lookup
//note there's probably better way to do this but whatevaaaa
Neighbors generateNeighbors(const GameBoard& board){
//assumtion nxn board
Neighbors neighbors( board.size(), std::vector<Vertex>(board.size()));
for (int row = 0; row < board.size() - 1; ++row) {
for(int col = 0; col < board[row].size() - 1; ++col){
Vertex& currentVertex = neighbors[row][col];
//current to right and reverse
currentVertex.neighbors.push_back(Vertex::Position(row,col + 1));
neighbors[row][col+1].neighbors.push_back(Vertex::Position(row,col));
//current to bottom right and reverse
currentVertex.neighbors.push_back(Vertex::Position(row+1,col+1));
neighbors[row+1][col+1].neighbors.push_back(Vertex::Position(row,col));
//current to bottom
currentVertex.neighbors.push_back(Vertex::Position(row+1,col));
neighbors[row+1][col].neighbors.push_back(Vertex::Position(row,col));
}
}
for (int row = 0; row < board.size() - 1; ++row) {
for(int col = 1; col < board[row].size(); ++col){
Vertex& currentVertex = neighbors[row][col];
//current to bottom left and reverse
currentVertex.neighbors.push_back(Vertex::Position(row+1,col-1));
neighbors[row+1][col-1].neighbors.push_back(Vertex::Position(row,col));
}
}
//for the last column
for(int row = 0; row < board.size() - 1; ++row){
int col = (int)board[row].size() - 1;
Vertex& currentVertex = neighbors[row][col];
//current to bottom and reverse
currentVertex.neighbors.push_back(Vertex::Position(row+1,col));
neighbors[row+1][col].neighbors.push_back(Vertex::Position(row,col));
}
//for the last row
for(int col = 0; col < board.size() - 1; ++col){
int row = (int)board.size() - 1;
Vertex& currentVertex = neighbors[row][col];
//currentEnd to right and reverse
currentVertex.neighbors.push_back(Vertex::Position(row,col + 1));
neighbors[row][col+1].neighbors.push_back(Vertex::Position(row,col));
}
for(int i = 0; i < board.size(); ++i){
for(int j = 0; j < board[i].size(); ++j){
neighbors[i][j].position = Vertex::Position(i,j);
}
}
return neighbors;
}
GameBoard readBoard(const std::string& filePath){
StringArray board;
ifstream fileIn(filePath.c_str());
std::copy(std::istream_iterator<std::string>(fileIn),
std::istream_iterator<std::string>(),
std::back_inserter(board));
return board;
}
StringArray readDictionary(const std::string& filePath){
ifstream fileIn(filePath.c_str());
StringArray result;
result.reserve(100000);
std::copy( std::istream_iterator<std::string>(fileIn),
std::istream_iterator<std::string>(),
std::back_inserter(result));
return result;
}
template<typename Iterator>
void print(Iterator begin, Iterator end, const std::string& delim){
while(begin != end){
cout << *begin << delim;
++begin;
}
}