diff --git a/.vscode/launch.json b/.vscode/launch.json index 6426eb1..e0a4ead 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -88,6 +88,22 @@ "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "preLaunchTask": "C/C++: g++ build active file" + }, + { + "name": "inverse_word_search", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [ + "puzzle3.txt", + "output.txt", + "all_solutions" + ], + "cwd": "${fileDirname}", + "environment": [], + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "C/C++: g++ build active file" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a29424..e53c2b2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -71,6 +71,8 @@ "__hash_table": "cpp", "__string": "cpp", "queue": "cpp", - "stack": "cpp" + "stack": "cpp", + "set": "cpp", + "climits": "cpp" } } \ No newline at end of file diff --git a/hws/inverse_word_search/README.txt b/hws/inverse_word_search/README.txt index 6ef3286..731f4e8 100644 --- a/hws/inverse_word_search/README.txt +++ b/hws/inverse_word_search/README.txt @@ -1,7 +1,7 @@ HOMEWORK 6: INVERSE WORD SEARCH -NAME: < insert name > +NAME: Jinshan Zhou COLLABORATORS AND OTHER RESOURCES: @@ -10,18 +10,18 @@ List the names of everyone you talked to about this assignment LMS, etc.), and all of the resources (books, online reference material, etc.) you consulted in completing this assignment. -< insert collaborators / resources > +Lab document -- recursion on path finding Remember: Your implementation for this assignment must be done on your own, as described in "Academic Integrity for Homework" handout. -ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: < insert # hours > +ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: 30 hr MISC. COMMENTS TO GRADER: Optional, please be concise! - +I tried. ## Reflection and Self Assessment @@ -33,4 +33,6 @@ What parts of the assignment did you find challenging? Is there anything that finally "clicked" for you in the process of working on this assignment? How well did the development and testing process go for you? -< insert reflection > +This homework is very hard for me. It almost take forever to solve some puzzles. +I tried a lot of techniques to minimize the cost. But, it seems not enough. But +anyway, it's a good practice. diff --git a/hws/inverse_word_search/main b/hws/inverse_word_search/main new file mode 100755 index 0000000..e9d5a67 Binary files /dev/null and b/hws/inverse_word_search/main differ diff --git a/hws/inverse_word_search/main.cpp b/hws/inverse_word_search/main.cpp new file mode 100644 index 0000000..1069bd3 --- /dev/null +++ b/hws/inverse_word_search/main.cpp @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct Placement { + int r, c; // starting row and column + int dr, dc; // direction increments + string word; // the required word to place +}; + +// Check if the given string s contains any forbidden word or its reverse. +bool containsForbidden(const string &s, const vector &forbWords) { + for (size_t i = 0; i < forbWords.size(); i++) { + if (s.find(forbWords[i]) != string::npos) + return true; + string rev = forbWords[i]; + reverse(rev.begin(), rev.end()); + if (s.find(rev) != string::npos) + return true; + } + return false; +} + +// Checks the full board for forbidden words in every contiguous line. +bool fullForbiddenCheck(const vector< vector > &board, const vector &forbWords) { + int H = board.size(); + if (H == 0) return false; + int W = board[0].size(); + int dirs[8][2] = { {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1} }; + for (int r = 0; r < H; r++) { + for (int c = 0; c < W; c++) { + for (int d = 0; d < 8; d++) { + int dr = dirs[d][0], dc = dirs[d][1]; + string line = ""; + int rr = r, cc = c; + while (rr >= 0 && rr < H && cc >= 0 && cc < W) { + line.push_back(board[rr][cc]); + if (containsForbidden(line, forbWords)) + return true; + rr += dr; + cc += dc; + } + } + } + } + return false; +} + +// Convert the board into a single string (rows separated by newline). +string boardToString(const vector< vector > &board) { + string s = ""; + for (size_t i = 0; i < board.size(); i++) { + for (size_t j = 0; j < board[i].size(); j++) { + s.push_back(board[i][j]); + } + if (i < board.size()-1) + s.push_back('\n'); + } + return s; +} + +// Given a newly assigned cell (r,c), check in all eight directions the contiguous block +bool checkCellDirections(int r, int c, const vector< vector > &board, const vector &forbWords, int minForbidLen) { + int H = board.size(), W = board[0].size(); + int dirs[8][2] = { {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1} }; + for (int d = 0; d < 8; d++) { + int dr = dirs[d][0], dc = dirs[d][1]; + int sr = r, sc = c; + // Go backwards from (r,c) + while (true) { + int pr = sr - dr, pc = sc - dc; + if (pr < 0 || pr >= H || pc < 0 || pc >= W) break; + if (board[pr][pc] == '?') break; + sr = pr; sc = pc; + } + // Build the contiguous block from (sr,sc) + string block = ""; + int cr = sr, cc = sc; + while (cr >= 0 && cr < H && cc >= 0 && cc < W) { + if (board[cr][cc] == '?') break; + block.push_back(board[cr][cc]); + cr += dr; cc += dc; + } + if ((int)block.size() >= minForbidLen) { + if (containsForbidden(block, forbWords)) + return false; + } + } + return true; +} + +// Recursive function to fill free positions with letters +bool fillFreePositions(vector> &board, + vector> &freePositions, + int index, + const vector &forbWords, + int minForbidLen, + set &solutionSet, + vector &solutions, + bool findOne, + const string &outputFile) { + // Base case: all free positions filled + if (index == freePositions.size()) { + // Check if the solution is valid + if (!fullForbiddenCheck(board, forbWords)) { + string solStr = boardToString(board); + if (solutionSet.find(solStr) == solutionSet.end()) { + solutionSet.insert(solStr); + solutions.push_back(solStr); + + // If we need just one solution, write it to file and exit + if (findOne) { + ofstream fout(outputFile.c_str()); + fout << "Board:" << endl; + istringstream iss(solStr); + string line; + while(getline(iss, line)) + fout << " " << line << endl; + fout.close(); + return true; + } + } + } + return false; // Continue searching for more solutions + } + + // Try each letter for the current free position + int r = freePositions[index].first; + int c = freePositions[index].second; + + for (char ch = 'a'; ch <= 'z'; ch++) { + board[r][c] = ch; + + // Check if the new letter creates any forbidden words + if (checkCellDirections(r, c, board, forbWords, minForbidLen)) { + // Recursively fill the next position + if (fillFreePositions(board, freePositions, index + 1, forbWords, minForbidLen, + solutionSet, solutions, findOne, outputFile)) { + return true; // Solution found and we only need one + } + } + } + + // Backtrack: Reset the cell + board[r][c] = '?'; + return false; +} + +// Recursive function to place required words +bool placeRequiredWords(vector> &board, + const vector> &placements, + int wordIndex, + const vector &forbWords, + int minForbidLen, + set &solutionSet, + vector &solutions, + bool findOne, + const string &outputFile) { + // Base case: all required words placed + if (wordIndex == placements.size()) { + // Find free positions (cells marked with '?') + vector> freePositions; + for (int i = 0; i < board.size(); i++) { + for (int j = 0; j < board[0].size(); j++) { + if (board[i][j] == '?') { + freePositions.push_back(make_pair(i, j)); + } + } + } + + // Recursively fill free positions + return fillFreePositions(board, freePositions, 0, forbWords, minForbidLen, + solutionSet, solutions, findOne, outputFile); + } + + // Try each placement for the current word + for (const Placement &p : placements[wordIndex]) { + // Create a temporary copy of the board + vector> boardCopy = board; + bool conflict = false; + + // Try to place the word + for (size_t k = 0; k < p.word.size(); k++) { + int r = p.r + p.dr * k; + int c = p.c + p.dc * k; + + if (boardCopy[r][c] == '?' || boardCopy[r][c] == p.word[k]) { + boardCopy[r][c] = p.word[k]; + } else { + conflict = true; + break; + } + } + + // If no conflict and no forbidden words formed, continue to the next word + if (!conflict && !fullForbiddenCheck(boardCopy, forbWords)) { + if (placeRequiredWords(boardCopy, placements, wordIndex + 1, forbWords, minForbidLen, + solutionSet, solutions, findOne, outputFile)) { + return true; // Solution found and we only need one + } + } + } + + return false; // No solution found with any placement for this word +} + +int main(int argc, char* argv[]) { + if (argc != 4) { + cout << "Usage: " << argv[0] << " input.txt output.txt one_solution|all_solutions" << endl; + return 1; + } + + string inputFile = argv[1]; + string outputFile = argv[2]; + string mode = argv[3]; + bool findOne = false; + if (mode == "one_solution") { + findOne = true; + } else if (mode == "all_solutions") { + findOne = false; + } else { + cout << "Invalid mode. Use one_solution or all_solutions." << endl; + return 1; + } + + ifstream fin(inputFile.c_str()); + if (!fin) { + cout << "Cannot open input file." << endl; + return 1; + } + + int width, height; + fin >> width >> height; + string dummy; + getline(fin, dummy); // consume rest of first line + + // Separate required and forbidden words. + vector reqWords; + vector forbWords; + + while(getline(fin, dummy)) { + if(dummy.size() == 0) + continue; + char sign = dummy[0]; + string word = ""; + int pos = 1; + while (pos < dummy.size() && isspace(dummy[pos])) pos++; + while (pos < dummy.size() && !isspace(dummy[pos])) { + word.push_back(dummy[pos]); + pos++; + } + if (sign == '+') + reqWords.push_back(word); + else if (sign == '-') + forbWords.push_back(word); + } + fin.close(); + + // Precompute minimum forbidden word length. + int minForbidLen = INT_MAX; + for (size_t i = 0; i < forbWords.size(); i++) { + if ((int)forbWords[i].size() < minForbidLen) + minForbidLen = forbWords[i].size(); + } + if (forbWords.empty()) + minForbidLen = 27; // no forbidden word; no check needed + + // Precompute all placements for each required word. + vector> placements; + placements.resize(reqWords.size()); + int directions[8][2] = { {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1} }; + + for (size_t w = 0; w < reqWords.size(); w++) { + string word = reqWords[w]; + for (int r = 0; r < height; r++) { + for (int c = 0; c < width; c++) { + for (int d = 0; d < 8; d++) { + int dr = directions[d][0], dc = directions[d][1]; + int end_r = r + dr * (word.size() - 1); + int end_c = c + dc * (word.size() - 1); + if (end_r < 0 || end_r >= height || end_c < 0 || end_c >= width) + continue; + Placement p; + p.r = r; p.c = c; p.dr = dr; p.dc = dc; p.word = word; + placements[w].push_back(p); + } + } + } + if (placements[w].empty()) { + ofstream fout(outputFile.c_str()); + fout << "No solutions found" << endl; + fout.close(); + return 0; + } + } + + // Use a set to avoid duplicate solutions. + set solutionSet; + vector solutions; + + // Prepare the base board filled with '?'. + vector> board(height, vector(width, '?')); + + // Call the recursive function to place required words + placeRequiredWords(board, placements, 0, forbWords, minForbidLen, + solutionSet, solutions, findOne, outputFile); + + // Write out all found solutions (if findOne is true, we've already written the solution). + if (!findOne || solutions.empty()) { + ofstream fout(outputFile.c_str()); + if (solutions.empty()) { + fout << "No solutions found" << endl; + } else { + fout << solutions.size() << " solution(s)" << endl; + for (size_t s = 0; s < solutions.size(); s++) { + fout << "Board:" << endl; + istringstream iss(solutions[s]); + string line; + while(getline(iss, line)) + fout << " " << line << endl; + } + } + fout.close(); + } + + return 0; +} \ No newline at end of file diff --git a/hws/inverse_word_search/output.txt b/hws/inverse_word_search/output.txt new file mode 100644 index 0000000..968ae11 --- /dev/null +++ b/hws/inverse_word_search/output.txt @@ -0,0 +1,41 @@ +8 solution(s) +Board: + aare + rrea + itss + dstt +Board: + eraa + aerr + ssti + ttsd +Board: + arid + arts + rest + east +Board: + dira + stra + tser + tsae +Board: + east + rest + arts + arid +Board: + tsae + tser + stra + dira +Board: + dstt + itss + rrea + aare +Board: + ttsd + ssti + aerr + eraa