/* * Recursive Computer Search * * Recursively searches the Boggle board to find all the * words that can be made on the board and haven't * been guessed correctly by the human player */ void recursiveComputer(int row, int col, string word, Grid<string>& board, Lexicon& dictionary, Set<string>& results, Grid<bool>& visitedCubes, int& computerScore, Set<string>& usedWords){ if(!board.inBounds(row, col)) return; if(dictionary.contains(word) && word.length() >= 4) { if (!results.contains(word) && !usedWords.contains(word)) { computerScore += word.length() - 3; results.add(word); } } if(!dictionary.containsPrefix(word)){ return; } word = word + board[row][col]; visitedCubes[row][col] = true; for(int i = -1; i <= 1; i++){ for(int j = -1; j <= 1; j++){ if(visitedCubes.inBounds(row + i, col + j) && !visitedCubes[row + i][col + j]){ recursiveComputer(row + i, col + j, word, board, dictionary, results, visitedCubes, computerScore, usedWords); } if(visitedCubes.inBounds(row + i, col + j) && dictionary.contains(word) && !results.contains(word) && word.length() >=4) { computerScore += word.length() - 3; results.add(word); } } } visitedCubes[row][col] = false; }
TIMED_TEST(LexiconTests, basicTest_Lexicon, TEST_TIMEOUT_DEFAULT) { std::initializer_list<std::string> words = { "a", "ab", "aab", "aaab", "aardvark", "b", "banana" }; std::initializer_list<std::string> badWords = { "abb", "ad", "and", "aaardvark", "aardvarks", }; std::initializer_list<std::string> badPrefixes = { "aaaa", "abb", "aardvarz", "bb", "bananas", "c", "r", "z" }; Lexicon lex; for (std::string word : words) { lex.add(word); } assertEquals("Lexicon size", words.size(), lex.size()); for (std::string word : words) { assertTrue("Lexicon contains " + word, lex.contains(word)); } for (std::string word : badWords) { assertFalse("Lexicon contains " + word, lex.contains(word)); } for (std::string word : words) { for (int i = 0; i < (int) word.length(); i++) { std::string prefix = word.substr(0, i); assertTrue("Lexicon containsPrefix " + word, lex.containsPrefix(word)); } } for (std::string word : badPrefixes) { assertFalse("Lexicon containsPrefix " + word, lex.containsPrefix(word)); } }
/* The function to solve the problem * Parameters: * start, dest => the two words to connect (MUST BE VALID WORDS) * dict => Lexicon restores words * Return: The ladder / an empty Vector (when there is no solution) * No side effects */ Vector<string> find_ladder(const string & start, const string & dest, const Lexicon & dict) { Vector<LadderStep> steps; //keep answers Lexicon appeared; //word that has appeared Queue<int> queue; //the queue, saves the labels of steps //init steps.push_back(LadderStep(start,NO_MORE_STEP)); appeared.add(start); queue.enqueue(0); int last = NO_MORE_STEP; //the last step of the answer //main loop while(!queue.isEmpty()){ int cur = queue.dequeue(); if (steps[cur].str == dest) //arrives destination { last = cur; //mark position break; } for (int i = 0; i < steps[cur].str.size(); ++i){ for (char c = 'a'; c <= 'z'; ++c){ //generate all possible steps string next(steps[cur].str); next[i] = c; //A valid word that hasn't appeared yet? if (dict.contains(next)&& !appeared.contains(next)){ //enqueue appeared.add(next); steps.push_back(LadderStep(next, cur)); queue.enqueue(steps.size() - 1); } } } } Vector<string> tmp; //the answer, reversed while(last != NO_MORE_STEP){ //has more steps? tmp.push_back(steps[last].str); last = steps[last].forward; //go forward } //reverse to get the answer Vector<string> res; for (int i = tmp.size() - 1; i>=0; --i){ res.push_back(tmp[i]); } return res; }
/** * Function: oneHopAway * This helper function loops through each letter of the alphabet * at each position for a word passed through, checking whether * the newly formed word is both English and hasn't been used * previously. The latter check is maintained by a Lexicon * passed through by reference. * Parameters: dictionary cleared in main as constant reference, Lexicon of used words * passed by reference since it can be altered, constant reference to the word * looking to build off of */ static Lexicon oneHopAway(const Lexicon& dictionary, Lexicon& usedWords, const string& topWord) { Lexicon oneHopAway; string alphabet = "abcdefghijklmnopqrstuvwxyz"; for(int i = 0; i < topWord.length(); i++) { for(int j = 0; j < alphabet.length(); j++) { string testWord = topWord; testWord[i] = alphabet[j]; if(dictionary.contains(testWord) && !usedWords.contains(testWord)) { oneHopAway.add(testWord); usedWords.add(testWord); } } } return oneHopAway; }
/* Change the current word by one letter to create new true word. * Copy last ladder into new, and add new word to it. */ void createNextLadder(string ¤tWord, Vector<string>& firstLadder, Lexicon& usedWords, Queue<Vector<string>>& result, Lexicon& english){ for(int i = 0; i < currentWord.length (); i++){ for(char c = 'a'; c <= 'z'; c++){ if(currentWord[i] != c){ string newWord = currentWord; newWord[i] = c; if(english.contains (newWord) && !usedWords.contains (newWord)){ Vector<string> nextLadder = firstLadder; nextLadder.add(newWord); result.enqueue (nextLadder); } } } } }
static void generateAllPossibleWords(const Grid<char> & boggleBoard, Set<string> & wordsSpottedSoFar, const Lexicon & english, int rowIndex, int colIndex, string buildingWord, Set<coord> wordPath){ buildingWord += boggleBoard[rowIndex][colIndex]; if (buildingWord.size() >= kMinGuessLength && english.contains(buildingWord) && !wordsSpottedSoFar.contains(buildingWord)) { wordsSpottedSoFar.add(buildingWord); recordWordForPlayer(buildingWord, COMPUTER); } if (!english.containsPrefix(buildingWord)) return; for (int i = -1; i <= 1; i++) { for (int j = -1; j <=1; j++){ if(isShiftValid(boggleBoard, rowIndex, colIndex, i, j)){ coord nextPos; nextPos.row = rowIndex + i; nextPos.col = colIndex + j; if (!wordPath.contains(nextPos)){ wordPath.add(nextPos); generateAllPossibleWords(boggleBoard, wordsSpottedSoFar, english, rowIndex + i, colIndex + j, buildingWord, wordPath); wordPath.remove(nextPos); } } } } }
static void tryPlayerGuess(const Grid<char> & boggleBoard, Set<string> & playerAnswers, string playerGuess, const Lexicon & english) { if (playerGuess.size() < kMinGuessLength){ cout << endl << "Words need to be at least " + integerToString(kMinGuessLength) + " characters long" << endl; return; } if (!english.contains(playerGuess)){ cout << endl << "That word is not in the english language" << endl; return; } playerGuess = toUpperCase(playerGuess); if (playerAnswers.contains(playerGuess)){ cout << endl << "You have already guessed that word" << endl; return; } for(int i = 0; i < boggleBoard.numRows(); i++){ for(int j = 0; j < boggleBoard.numCols(); j++){ Set <coord> wordPath; coord pos; pos.row = i; pos.col = j; wordPath.add(pos); if(tryPlayerGuess(boggleBoard, playerAnswers, playerGuess, english, "", i, j, wordPath)){ pause(highlightPause); clearBoard(wordPath); recordWordForPlayer(playerGuess, HUMAN); return; } } } cout << endl << "That word is not on the board." << endl; }
//credit to erickwill, his code guided me to the answer void recursiveFindWord(int row, int col,std::string soFar, Grid<char> &grid, Lexicon words, Vector<std::string> &foundWords) { char orig = grid.get(row,col); soFar = soFar + grid.get(row,col); drawCubes(grid); cout << "row = " << row << " col = " << col << endl; cout << "sofar = " << soFar << endl; if(words.contains(soFar) && !containsWord(foundWords,soFar)){ cout << "contains word: " << soFar << endl; foundWords.push_back(soFar); return; } if(!words.containsPrefix(soFar)){ cout << "does not contain prefix: " << soFar << endl; return; } for(int i = -1; i < 2; i++){ for(int j = -1; j < 2; j++){ if(row+i>=0 && col+j >=0 && row+i < grid.numRows() && col+j < grid.numCols() && (row+i < grid.numRows() && col+j < grid.numCols()) && !(i==0 && j==0) && grid.get(row+i,col+j) != '~') { grid.set(row,col,'~'); recursiveFindWord(row+i,col+j,soFar,grid,words,foundWords); grid.set(row,col,orig); } } } return; }
//return a string vector as the shortest ladder, if found //return an empty vector, if not found Vector<string> bfs(string &startingWord, string &endingWord) { Lexicon english("EnglishWords.dat"); Lexicon wordUsed; Queue<Vector<string>> queue; Vector<string> ladder; ladder.push_back(startingWord); queue.enqueue(ladder); while (!queue.isEmpty()) { Vector<string> front = queue.dequeue(); if (front.get(front.size() - 1) == endingWord) return front; string current = front.get(front.size() - 1); for (int i = 0; i < current.size(); i++) for (char j = 'a'; j <= 'z'; j++) if (current[i] != j){ string next = current; next[i] = j; if (!english.contains(next)) continue; if (wordUsed.contains(next)) continue; wordUsed.add(next); Vector<string> nextLadder = front; nextLadder.push_back(next); queue.enqueue(nextLadder); } } Vector<string> empty; return empty; }
static string getWord(Lexicon& english, string prompt) { while (true) { string response = trim(toLowerCase(getLine(prompt))); if (response.empty() || english.contains(response)) return response; cout << "Your response needs to be an English word, so please try again." << endl; } }
bool isInLexicon(string word, Lexicon & lex) { if (lex.contains(word)) { return true; } else { return false; } }
/* ListCompletions(string, Lexicon): * Prints all words from the lexicon that can be formed by extending * the given digit sequence. */ void ListCompletions(string digits, Lexicon & lex) { int index=0; /*find the first digit*/ while(index<(int)digits.length() && !isdigit(digits[index])) index++; /*Don't start checking until the digits have been entirely converted to letters*/ if(index == digits.length() ){ string word = digits; //redundant, but helps me understand /*Lexicon doesn't containPrefix=>these aren't the droids I'm looking for*/ if(!lex.containsPrefix(word)) return; /*Otherwise check if the current word is in the lexicon and print it if it is*/ if( lex.contains(word)){ cout << "\t" << word << endl; } /*...and then try recursively checking after adding each letter*/ for( char ch='a'; ch<='z'; ++ch){ string newWord = digits + ch; ListCompletions(newWord, lex); } return; } /*make a prefix out of the current letters, check what could come next, and recursively check each*/ string prefix = digits.substr(0, index); string digitLetters = letterSets[digits[index] - '0']; //subtracting '0' converts digits[index] a char to an int int i=0; //not intuitive at all while(i<digitLetters.length()){ ListCompletions(prefix + digitLetters[i] + digits.substr(index + 1), lex ); i++; } }
/*Check the entered words with specified parameters. Convert them to lower case */ void readWords(string & firstWord, string & secondWord, Lexicon & lexicon){ string quit = "RETURN"; firstWord = getLine("Enter start word (RETURN to exit): "); if(firstWord == "" || !lexicon.contains (firstWord)){ cout << "You entered not valid word. Try again..." << endl; } else if (firstWord == quit){ exit(0); } firstWord = toLowerCase (firstWord); secondWord = toLowerCase (getLine ("Enter destination word: ")); if(secondWord == "" || !lexicon.contains (secondWord)){ cout << "You entered not valid word. Try again..." << endl; } else if (secondWord.length () != firstWord.length ()){ cout << "The length of both words must be the same. Try again..."; } }
/* main */ int main() { Lexicon english; english.addWordsFromFile("dictionary.txt"); int total = 0; int totalWords = english.size(); // C++11 "for" iteration loop for (string str : english) { string prefix = "s" + str; string suffix = str + "s"; if (english.contains(prefix) && english.contains(suffix)) { cout << str << " :: " << prefix << " & " << suffix << endl; total++; } } cout << endl << "The total number of words: " << totalWords << endl; cout << "The total S words: " << total << endl; return 0; }
/* * Function: computersTurn * Usage: computersTurn(Lexicon &dictionary, Lexicon &usedWords, Grid<char> &charGrid, int gSize); * -------------------------- * Accepts a lexicon of the English dictionary, a lexicon of the words which the human player * has found, a character grid representing the current letters on the display and the board * size. For each word in the dictionary, if it is four letters or more and has a prefix that * corresponds to a current location on the display, the word is checked if it can be formed * at that position on the board. Word that the player has already found are subtracted from the * words which the computer has found. The computers points are then totaled. */ void computersTurn(Lexicon &dictionary, Lexicon &usedWords, Grid<char> &charGrid, int gSize) { cout << endl << "Now the computer is taking its turn, prepare to lose! " << endl; Lexicon allWords = findAllWords(dictionary, charGrid, gSize); foreach(string word in allWords) { if(!usedWords.contains(word)) { recordWordForPlayer(word, COMPUTER); } } }
/* * Function: isValid() * -------------------------- * Determines the validity of a word according to 4 requirements: * • It is at least four letters long. * • It has not already been included in the player’s word list (even if there is an alternate path on the * board to form the same word, the word is counted at most once). * • It is contained in the English lexicon. * • (function call) It can be formed on the board (i.e., it is composed of adjoining letters and each cube is used at most once). */ bool isValid(string& word, Grid<char>& board, Grid<bool>& usedLetters, Lexicon& usedWords, Lexicon& english){ if (word.length()<4){ cout<<"\tSorry, that word is too short (4 character minimum)"<<endl; return false; } if (usedWords.contains(word)){ cout<<"\tSorry, that word has already been counted."<<endl; return false; } if (!english.contains(word)){ cout<<"\tSorry, that word is not in my dictionary."<<endl; return false; } if(!canBeMade(word, board, usedLetters, 0, 0, board.numRows()-1)){ cout<<"\tSorry, that word can't be made from the available board."<<endl; return false; } else return true; }
bool isElementSpellable(string text, Lexicon& symbols) { for (int i = 1; i <= text.length() && i <=3 ; i++) { string tmp = text.substr(0, i); if(symbols.contains(text.substr(0, i)) && isElementSpellable(text.substr(i), symbols)) return true; } return false; }
void ListCompletionsRec(Vector<string>prefixes, Lexicon & lex) { for (int i = 0; i < prefixes.size(); i ++) { string current = prefixes[i]; if (lex.contains(current) || lex.containsPrefix(current)) { for (int i = 0; i < alphabet.length(); i ++) { current += alphabet[i]; ListCompletionsRec(prefixes, lex); } } } }
/** * Function: getEnglishWord * getEnglishWord is similar to the Stanford getLine function but also adds * the program-specific requirements of checking if the user's input is an * English word or blank. Rather than calling a separate helper function * to determine if the user's input is an English word and housing all * condition checks in the main, this function can be called to check any word. * It returns a boolean as the check. * Parameters: dictionary declared in main as constant reference, a user-inputted word * that needs to be validated as a constant reference, and the prompt as a literal */ static bool getEnglishWord(const Lexicon& dictionary, string& word, string prompt) { while(true) { cout << prompt; getline(cin, word); if(word.empty()) { return true; } else if(dictionary.contains(word)) { return false; } cout << "Your response needs to be an English word, so please try again." << endl; } }
bool isElementSpellable(string text, Lexicon& symbol) { // Base Case 0 (if there is no text at all): if (text == "") return true; for (int i = 1; i <= text.size() && i <= MAX_SYMBOL_SIZE; i++) { if (symbol.contains(text.substr(0, i)) && isElementSpellable(text.substr(i), symbol)) { return true; } } return false; }
/* * Function: humansTurn * Usage: humansTurn(Lexicon dictionary, Lexicon &usedWords, Grid<char> &charGrid, int gSize); * -------------------------- * Accepts a lexicon of an English dictionary, a lexicon to store used words, * a grid of the current letters displayed and the size of the board. * Implements the human players turn by first prompting them to input an word then * it runs a series of checks on the word. If the word meets all the criteria, it * it added to the players score. The player is allowed to enter words until the enter nothing. */ void humansTurn(Lexicon &dictionary, Lexicon &usedWords, Grid<char> &charGrid, int gSize) { cout << endl << "Take as long as you need to find as many words as you can within the Boggle display. " << endl; cout << endl << "Signal you are finished by entering an empty line. " << endl; string word = ""; word = getLine("Enter a word: "); for(int i = 0; i < word.size(); i++) { word[i] = toupper(word[i]); } while(word != "") { bool flag = true; if(word.length() < MIN_WORD_LENGTH) { cout << endl << "Please enter a word that is four letters or more." << endl; flag = false; } if(flag && !dictionary.contains(word)) { cout << endl << "This word is not found in the dictionary." << endl; flag = false; } if(flag && usedWords.contains(word)) { cout << endl << "You have already used this word." << endl; flag = false; } int count = 0; Grid<bool> boolGrid(gSize, gSize); if(flag && !checkWord(charGrid, boolGrid, gSize, word, count, 0, 0)) { cout << endl << "The word could not be found on the board." << endl; flag = false; } /* If all of the criteria is met (flags are true), the word it recorded. */ if(flag) { usedWords.add(word); recordWordForPlayer(word, HUMAN); displayWord(boolGrid, gSize); } word = getLine("Enter a word: "); for(int i = 0; i < word.size(); i++) { word[i] = toupper(word[i]); } } }
/* * Tries to identify a ladder of words from src to trg using Beadth-First Search */ Stack<string> createLadder(Lexicon& dict, string& src, string& trg){ Queue<Stack<string> > queue; bool complete = false; // Create initial stack for queue, containing source word // and push onto queue. Stack<string> init; init.push(src); queue.enqueue(init); // usedWords keeps track of words already used HashSet<string> usedWords; usedWords.add(src); Stack<string> currentChain; Stack<string> newChain; string last, test; // This loop will iterate through the queue, adding new longer // ladder of words to the back of the queue if found. If a // ladder is identified as having the target word at it's top, // it is flaged as the successful ladder and returned after the // loop. while(!complete && !queue.isEmpty()){ currentChain = queue.dequeue(); last = currentChain.peek(); if(last == trg){ complete = true; }else{ for(int i=0; i<last.length(); i++){ for(char c='a'; c<='z'; c++){ test = last.substr(0,i) + c + last.substr(i+1); if(dict.contains(test) && !usedWords.contains(test)){ newChain = currentChain; newChain.push(test); queue.enqueue(newChain); usedWords.add(test); } } } } } if(complete){ return currentChain; }else{ // empty stack indicated no ladder found. Stack<string> empty; return empty; } }
static void generateLadder(Lexicon& english, string start, string end) { Lexicon usedWord; Queue<Vector<string> > queue; Vector<string> initialWordLadder; initialWordLadder.add(start); usedWord.add(start); queue.enqueue(initialWordLadder); while (!queue.isEmpty()) { Vector<string> wordLadder = queue.dequeue(); string topWord = wordLadder.get(wordLadder.size() - 1); if (topWord == end) { cout << "Found Ladder: "; for (int i = 0; i < wordLadder.size(); i++) { cout << wordLadder.get(i) << " "; } cout << endl << endl; break; } else { for (int i = 0; i < topWord.length(); i++) { for (int j = 'a'; j <= 'z'; j++) { if (topWord.at(i) != j) { string newWord = topWord.substr(0, i) + char(j) + topWord.substr(i + 1, topWord.length() - i - 1); if (english.contains(newWord) && !usedWord.contains(newWord)) { Vector<string> wordLadderClone = wordLadder; wordLadderClone.add(newWord); queue.enqueue(wordLadderClone); usedWord.add(newWord); } } } } } } }
string wordLadder(string start, string destination, Lexicon& english){ Queue< Vector<string> > ladderQueue; Lexicon usedWords; Vector<string> ladder, newLadder; string lastWordInLadder, newWord; int n; ladder.add(start); usedWords.add(start); ladderQueue.enqueue(ladder); while(!ladderQueue.isEmpty()){ ladder = ladderQueue.dequeue(); lastWordInLadder = ladder[ladder.size() - 1]; if(lastWordInLadder == destination)return printLadder(ladder); for( n = 0; n < lastWordInLadder.length(); n++){ newWord = lastWordInLadder; for(char c = 'a'; c <= 'z' ; c++){ newWord[n] = c; if(english.contains(newWord) && !usedWords.contains(newWord)){ newLadder = ladder; newLadder.add(newWord); usedWords.add(newWord); ladderQueue.enqueue(newLadder); } } } } return "No ladder found" ; }
/** Recursive function used to populate a vector of words found from an initial position on the board and used to search words for the computer. This function returns a Vector of string elements. **/ Vector<string> ComputerWordSearch(Grid<char> &Board,string Candidate,Location Cursor,Lexicon &English) { //Board copy to make recursive call Grid<char> NewBoard=Grid<char>(); Location Pos; //Vector to store results Vector<string> Aux=Vector<string>(); //vector to indicate failure, empty vector Vector<string> EmptyVec=Vector<string>(); //if the search is heading to failure finish it if((!English.containsPrefix(Candidate))||Candidate.length()>8) return EmptyVec; //if you found a valid word, add it to your vector if(Candidate.length()>3&&English.contains(Candidate)) { Aux.add(Candidate); } //search the adjacent cubes in search of a valid word path for(int dx=-1;dx<=1;dx++) { for(int dy=-1;dy<=1;dy++) { //if I point to the same cube or I am out of the board or I already used the cube do not search that way if(!(dx==0&&dy==0)&&Board.inBounds(Cursor.row+dx,Cursor.col+dy)&&(Board[Cursor.row+dx][Cursor.col+dy]!=USED)) { //add char to candidate Candidate+=Board[Cursor.row+dx][Cursor.col+dy]; //create board copy and mark used the char taken NewBoard=Board; NewBoard[Cursor.row+dx][Cursor.col+dy]=USED; Pos.row=Cursor.row+dx; Pos.col=Cursor.col+dy; //if you can find a word using the path chosen, add the word to aux if(!ComputerWordSearch(NewBoard,Candidate,Pos,English).isEmpty()) { Aux+=ComputerWordSearch(NewBoard,Candidate,Pos,English); } //backtracking, if I could not find a word this way I need to remove the last char from my candidate Candidate=Candidate.substr(0,Candidate.length()-1); } } } //return your vector full of valid words return Aux; }
void playOneGame(Boggle& boggle) { while(true){ if(yesOrNo("Manually enter a board configuration? (Y/N) ")){ getUserBoard(boggle); break; }else{ boggle.makeBoard(); break; } } boggle.clearUsedWords(); Lexicon dic; dic.addWordsFromFile("EnglishWords.dat"); vector<string> foundWords; string input; while(true){ clearConsole(); boggle.printUserWords(cout); cout << "Your score: " << boggle.userScore() << endl; boggle.clearAllVisited(); boggle.printBoard(cout); cout << "Type a word (or press Enter to end your turn): " << endl; getline(cin, input); std::transform(input.begin(), input.end(), input.begin(), ::toupper); if (input.size() <= 16 && input.size() > 3 && dic.contains(input)){ if(boggle.possibleWord(input, dic)){ cout << "You found a new word! " << input << endl; foundWords.push_back(input); } } if(input.empty()){ break; } } cout << "It's my turn!" << endl; boggle.computerPlayer(dic); boggle.printComputerWords(cout); cout << "My score: " << boggle.computerScore() << endl; if(boggle.computerScore() > boggle.userScore()){ cout << "Ha ha ha, I destroyed you. Better luck next time, puny human!" << endl; } }
TIMED_TEST(LexiconTests, initializerListTest_Lexicon, TEST_TIMEOUT_DEFAULT) { std::initializer_list<std::string> lexlist = {"sixty", "seventy"}; std::initializer_list<std::string> lexallwords = { "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy" }; Lexicon lex {"ten", "twenty", "thirty"}; assertEqualsString("init list Lexicon", "{\"ten\", \"thirty\", \"twenty\"}", lex.toString()); assertEqualsInt("init list Lexicon size", 3, lex.size()); assertTrue("init list Lexicon contains ten", lex.contains("ten")); assertTrue("init list Lexicon contains twenty", lex.contains("twenty")); assertTrue("init list Lexicon contains thirty", lex.contains("thirty")); assertFalse("init list Lexicon contains forty", lex.contains("forty")); assertFalse("init list Lexicon contains fifty", lex.contains("fifty")); lex += {"forty", "fifty"}; assertEqualsString("after += Lexicon", "{\"fifty\", \"forty\", \"ten\", \"thirty\", \"twenty\"}", lex.toString()); assertEqualsInt("after += Lexicon size", 5, lex.size()); assertTrue("init list Lexicon contains ten", lex.contains("ten")); assertTrue("init list Lexicon contains twenty", lex.contains("twenty")); assertTrue("init list Lexicon contains thirty", lex.contains("thirty")); assertTrue("init list Lexicon contains forty", lex.contains("forty")); assertTrue("init list Lexicon contains fifty", lex.contains("fifty")); assertFalse("init list Lexicon contains sixty", lex.contains("sixty")); assertFalse("init list Lexicon contains seventy", lex.contains("seventy")); Lexicon lex2 = (lex + lexlist); assertEqualsString("after += Lexicon", "{\"fifty\", \"forty\", \"ten\", \"thirty\", \"twenty\"}", lex.toString()); assertEqualsInt("after + Lexicon size", 5, lex.size()); assertTrue("init list Lexicon contains ten", lex.contains("ten")); assertTrue("init list Lexicon contains twenty", lex.contains("twenty")); assertTrue("init list Lexicon contains thirty", lex.contains("thirty")); assertTrue("init list Lexicon contains forty", lex.contains("forty")); assertTrue("init list Lexicon contains fifty", lex.contains("fifty")); assertFalse("init list Lexicon contains sixty", lex.contains("sixty")); assertFalse("init list Lexicon contains seventy", lex.contains("seventy")); assertEqualsString("after + Lexicon 2", "{\"fifty\", \"forty\", \"seventy\", \"sixty\", \"ten\", \"thirty\", \"twenty\"}", lex2.toString()); assertEqualsInt("after + Lexicon 2 size", 7, lex2.size()); assertTrue("init list Lexicon contains ten", lex2.contains("ten")); assertTrue("init list Lexicon contains twenty", lex2.contains("twenty")); assertTrue("init list Lexicon contains thirty", lex2.contains("thirty")); assertTrue("init list Lexicon contains forty", lex2.contains("forty")); assertTrue("init list Lexicon contains fifty", lex2.contains("fifty")); assertTrue("init list Lexicon contains sixty", lex2.contains("sixty")); assertTrue("init list Lexicon contains seventy", lex2.contains("seventy")); }
static int generateLadder(string start, string end) { // A queue of vector <string> holds data from breadth first search Queue < Vector <string> > wordLadder; // add a base case which just includes the start word Vector <string> base; base.add(start); wordLadder.enqueue(base); // Lexicon from data file Lexicon english("EnglishWords.dat"); // Lexicon holds words previously encountered Lexicon previousWords; previousWords.add(start); while(!wordLadder.isEmpty()){ Vector<string> currentVector = wordLadder.dequeue(); // if the last string in the Vector equals the end word, we have found a complete ladder if (currentVector[currentVector.size()-1]==end) { cout << "Found ladder: "; for (int i = 0; i < currentVector.size()-1; i++){ cout << currentVector[i] << " "; } cout << endl; return 1; } else { // for loop that goes through each character position in the word for (int i = 0; i < start.length(); i ++){ // loops through the letters of the alphabet for (char alphabet = 'a'; alphabet < '{'; alphabet++){ string wordAltered = currentVector[currentVector.size()-1]; // replacing the character in that index position with each of the 26 letters in turn wordAltered[i]=alphabet; // if end word isn't in dictionary but is found, cout ladder. if (wordAltered==end){ // N.B. don't use currentVector.size()-1 because the currentVector hasn't been updated with end word for (int i = 0; i < currentVector.size(); i++){ cout << currentVector[i] << " "; } cout << end << endl; return 1; } // if wordAltered is in dictionary and hasn't been reached before, update word ladder if (english.contains(wordAltered.c_str()) && !previousWords.contains(wordAltered.c_str())){ // copy currentVector, add the wordAltered to the newWords vector, update previous words Lexicon, enqueue newWords vector Vector <string> newWords = currentVector; newWords.add(wordAltered); previousWords.add(wordAltered); wordLadder.enqueue(newWords); } } } } } // if queue becomes empty, can't create a ladder. Ask user to play again if(wordLadder.isEmpty()){ cout << "Can't create a word ladder between " << start << " and " << end << endl; cout << "Try again" << endl; playWordLadder(); } return 0; }