├── .gitignore ├── HW1_Life └── life.cpp ├── HW2_Serafini ├── ngrams.cpp └── wordladder.cpp ├── HW3_Recursion ├── fractals.cpp └── grammarsolver.cpp ├── HW4_Boggle ├── Boggle.cpp ├── Boggle.h └── boggleplay.cpp ├── HW5_PatientQueue ├── HeapPatientQueue.cpp ├── HeapPatientQueue.h ├── LinkedListPatientQueue.cpp ├── LinkedListPatientQueue.h ├── VectorPatientQueue.cpp └── VectorPatientQueue.h ├── HW6_Huffman └── encoding.cpp ├── HW7_Trailblazer └── Trailblazer.cpp ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS OS file 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /HW1_Life/life.cpp: -------------------------------------------------------------------------------- 1 | // life.cpp 2 | // 3 | // Created by Jian Zhong on 5/29/20. 4 | // Copyright © 2020 Jian Zhong. All rights reserved. 5 | //*********************************************************************** 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "console.h" 14 | #include "filelib.h" 15 | #include "grid.h" 16 | #include "gwindow.h" 17 | #include "simpio.h" 18 | #include "strlib.h" 19 | #include "lifegui.h" 20 | #include "gevents.h" 21 | using namespace std; 22 | 23 | /*** Function Prototypes ***/ 24 | /* Functions in main */ 25 | void welcome_Messages(); 26 | void input_File(string, ifstream&); 27 | void colony_Initializer(Grid&, Grid&, int&, int&, ifstream&, LifeGUI&); 28 | void menu(char&, Grid&, Grid&, bool, LifeGUI&); 29 | bool wrapping_Indicator(); 30 | 31 | /* Sub-functions */ 32 | void display_All_Colony(const Grid&); 33 | void read_Colony(Grid&, ifstream&, LifeGUI&); 34 | int neighbourNum_NonWrap(const Grid&, int, int); 35 | int neighbourNum_Wrap(const Grid&, int, int); 36 | void colony_Iteration(Grid&, Grid&, bool, LifeGUI&); 37 | void colony_Animation(Grid&, Grid&, int, bool); 38 | 39 | 40 | /*** main function begins here ***/ 41 | int main() { 42 | /* Print welcome messages */ 43 | welcome_Messages(); 44 | 45 | /* Input colony file */ 46 | ifstream fin; // for file input 47 | string filePath; // input file path 48 | input_File(filePath, fin); 49 | 50 | /* Initialize cell colony with data in file */ 51 | int row, col; // row # and col # for each colony 52 | Grid currColony; // current cell colony 53 | Grid nextColony; // next generation of current colony 54 | LifeGUI gui; // gui whindow for game of life 55 | colony_Initializer(currColony, nextColony, row, col, fin, gui); 56 | 57 | /* Menu */ 58 | char option; // menu option 59 | menu(option, currColony, nextColony, wrapping_Indicator(), gui); 60 | 61 | /* Ending */ 62 | cout << "Have a nice Life!" << endl; 63 | fin.close(); 64 | 65 | return 0; 66 | } /*** main function ends here ***/ 67 | 68 | 69 | // print welcome messages 70 | void welcome_Messages() { 71 | cout << "Welcome to the CS 106B Game of Life,\n" 72 | "a simulation of the lifecycle of a bacteria colony.\n" 73 | "Cells (X) live and die by the following rules:\n" 74 | "- A cell with 1 or fewer neighbors dies.\n" 75 | "- Locations with 2 neighbors remain stable.\n" 76 | "- Locations with 3 neighbors will create life.\n" 77 | "- A cell with 4 or more neighbors dies.\n\n"; 78 | } 79 | 80 | // Input text file 81 | void input_File(string filePath, ifstream& fin) { 82 | while(true) { 83 | filePath = getLine("Grid input file name? "); 84 | if (!fileExists(filePath)) { 85 | cout << "Can't locate the file, try again.\n"; 86 | } else { 87 | openFile(fin, filePath); 88 | break; 89 | } 90 | } 91 | } 92 | 93 | 94 | void colony_Initializer(Grid& currColony, Grid& nextColony, int& row, int& col, ifstream& fin, LifeGUI& gui) { 95 | // Read in data from file: 96 | fin >> row >> col; 97 | currColony.resize(row, col); // setup size for current generation grids 98 | nextColony.resize(row, col); // setup size for next generation grids 99 | gui.resize(row, col); // setup size for GUI window 100 | read_Colony(currColony, fin, gui); // Read in currColony 101 | nextColony.deepCopy(currColony); // copy currColony into nextColony 102 | 103 | // Print grids for the first time: 104 | cout << endl; 105 | display_All_Colony(currColony); 106 | } 107 | 108 | // create annimation accroding to frame number 109 | void colony_Animation(Grid& currColony, Grid& nextColony, int frames, bool wrapping, LifeGUI& gui) { 110 | for (int i = 0; i < frames; i++) { 111 | // how many frames would generate for the animation 112 | clearConsole(); 113 | colony_Iteration(currColony, nextColony, wrapping, gui); 114 | currColony.deepCopy(nextColony); 115 | pause(50); // pause time 116 | } 117 | } 118 | 119 | // main menu 120 | void menu(char& option, Grid& currColony, Grid& nextColony, bool wrapping, LifeGUI& gui) { 121 | int frames; // for how many new generations are shown 122 | 123 | cout << "\nA)inmate, T)ick, Q)uit? "; 124 | cin >> option; 125 | // input validation 126 | while (!cin || (cin.peek() != '\n')) { 127 | cout << "Invalid input, try again.\n"; 128 | cin.clear(); 129 | cin.ignore(999, '\n'); 130 | cout << "\nA)inmate, T)ick, Q)uit? "; 131 | cin >> option; 132 | } 133 | 134 | switch (option) { 135 | case 'a': 136 | case 'A': 137 | // animation option: 138 | cout << "How many frames? "; 139 | cin >> frames; 140 | // input validation 141 | while (!cin || (cin.peek() != '\n')) { 142 | cout << "Invalid input, try again.\n"; 143 | cin.clear(); 144 | cin.ignore(999, '\n'); 145 | cout << "How many frames? "; 146 | cin >> frames; 147 | } 148 | colony_Animation(currColony, nextColony, frames, wrapping, gui); 149 | menu(option, currColony, nextColony, wrapping, gui); 150 | break; 151 | 152 | case 't': 153 | case 'T': 154 | // tick option: 155 | colony_Animation(currColony, nextColony, 1, wrapping, gui); 156 | menu(option, currColony, nextColony, wrapping, gui); 157 | break; 158 | 159 | case 'q': 160 | case 'Q': 161 | // quit program 162 | break; 163 | 164 | default: 165 | // otherwise prompt again: 166 | cin.clear(); 167 | cin.ignore(999, '\n'); 168 | cout << "Invalid input, Try again."; 169 | menu(option, currColony, nextColony, wrapping, gui); 170 | break; 171 | } 172 | } 173 | 174 | // print colony 175 | void display_All_Colony(const Grid& g) { 176 | for (int i = 0; i < g.numRows(); i++) { 177 | for(int j = 0; j < g.numCols(); j++){ 178 | cout << g[i][j]; 179 | } 180 | cout << endl; 181 | } 182 | } 183 | 184 | // non-wrapping condition 185 | int neighbourNum_NonWrap(const Grid& g, int r, int c) { 186 | int neighbour = 0; 187 | 188 | // count neighbours around each cell's 8 directioins 189 | for (int i = -1; i < 2; i++) { 190 | for (int j = -1; j < 2; j++) { 191 | if (g.inBounds(r + i, c + j) && g[r + i][c + j] == 'X') { 192 | neighbour++; 193 | } 194 | } 195 | } 196 | 197 | // do not count itself if cell exists 198 | if (g[r][c] == 'X') { 199 | neighbour--; 200 | } 201 | 202 | return neighbour; 203 | } 204 | 205 | // wrapping condition 206 | int neighbourNum_Wrap(const Grid& g, int r, int c) { 207 | int neighbour = 0; 208 | 209 | // count neighbours around each cell's 8 directioins 210 | for (int i = -1; i < 2; i++) { 211 | for (int j = -1; j < 2; j++) { 212 | if (g[(r+i+g.numRows()) % g.numRows()][(c+j+g.numCols()) % g.numCols()] == 'X') { 213 | neighbour++; 214 | } 215 | } 216 | } 217 | 218 | // do not count itself if cell exists 219 | if (g[r][c] == 'X') { 220 | neighbour--; 221 | } 222 | 223 | return neighbour; 224 | } 225 | 226 | // generation iteration: each function call will update the whole grid by 1. 227 | void colony_Iteration(Grid& currColony, Grid& nextColony, bool wrapping, LifeGUI& gui) { 228 | int neigbourNumber; // number of neigbour for each cell 229 | 230 | // loop over each grid in the colony 231 | for (int i = 0; i < currColony.numRows(); i++) { 232 | // for each row 233 | for (int j = 0; j < currColony.numCols(); j++) { 234 | // for each grid 235 | 236 | // eable wrapping or not 237 | if (wrapping) { 238 | neigbourNumber = neighbourNum_Wrap(currColony, i, j); 239 | } else { 240 | neigbourNumber = neighbourNum_NonWrap(currColony, i, j); 241 | } 242 | 243 | // apply rules of game of life: 244 | if (neigbourNumber <= 1 || neigbourNumber >= 4) { // when neigbour # >= 4 or <= 1, die; 245 | nextColony.set(i, j, '-'); 246 | gui.drawCell(i, j, false); 247 | } else if (neigbourNumber == 3) { // when neigbour # == 3, live; 248 | nextColony.set(i, j, 'X'); 249 | gui.drawCell(i, j, true); 250 | } 251 | 252 | } 253 | } 254 | 255 | // after update, print colony 256 | display_All_Colony(nextColony); 257 | } 258 | 259 | 260 | // Prompt user for wrapping condition: 261 | bool wrapping_Indicator() { 262 | char wrapOption; 263 | 264 | cout << "Should the simulation wrap around the grid (y/n)? "; 265 | cin >> wrapOption; 266 | 267 | // input validation 268 | while(!cin || cin.peek() != '\n') { 269 | cout << "Invalid input, try again.\n"; 270 | cin.clear(); 271 | cin.ignore(999, '\n'); 272 | cout << "Should the simulation wrap around the grid (y/n)? "; 273 | cin >> wrapOption; 274 | } 275 | 276 | switch (wrapOption) { 277 | case 'y': 278 | case 'Y': 279 | return true; 280 | break; 281 | case 'n': 282 | case 'N': 283 | return false; 284 | break; 285 | default: 286 | cin.clear(); 287 | cin.ignore(9999, '\n'); 288 | cout << "Invalid input, try again. \n"; 289 | return wrapping_Indicator(); 290 | break; 291 | } 292 | } 293 | 294 | // read in colony grids 295 | void read_Colony(Grid& g, ifstream& fin, LifeGUI& gui) { 296 | for (int i = 0; i < g.numRows(); i++) { 297 | for(int j = 0; j < g.numCols(); j++){ 298 | fin >> g[i][j]; 299 | gui.drawCell(i, j, g[i][j] == 'X'); 300 | } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /HW2_Serafini/ngrams.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jian Zhong on 6/12/20. 3 | // Copyright © 2020 Jian Zhong. All rights reserved. 4 | // 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "console.h" 13 | #include "hashmap.h" 14 | #include "set.h" 15 | #include "random.h" 16 | #include "queue.h" 17 | #include 18 | 19 | using namespace std; 20 | void greetings(); 21 | void inputMap(ifstream&); 22 | HashMap,Vector> buildMap(const int&, ifstream&); 23 | int getN(); 24 | void slideRandomWindow(Vector& , Vector&, string&,const HashMap, Vector >&); 25 | bool getLenth(int&); 26 | void NGrams(const HashMap, Vector >& map, const int& wordsLength); 27 | 28 | 29 | int main() { 30 | ifstream inFile; // input file stream 31 | HashMap, Vector> map; // Ngrams map 32 | int wordsLength; // # of random words to generate 33 | 34 | greetings(); // print welcome messages 35 | inputMap(inFile); // prompt user for file path of Ngrams map 36 | map = buildMap(getN(), inFile); // prompt user for N, and setup Ngrams map 37 | while (getLenth(wordsLength)) { // repeatdly prompt use for words length to generate random text until input is 0. 38 | NGrams(map, wordsLength); // generate random text 39 | } 40 | 41 | cout << "Exiting."<< endl; 42 | return 0; 43 | } 44 | 45 | 46 | // Function to print greetings to user 47 | void greetings() { 48 | cout << "Welcome to CS 106B Random Writer ('N-Grams'). " << endl 49 | << "This program makes random text based on a document. " << endl 50 | << "Give me an input file and an 'N' value for groups" << endl 51 | << "of words, and I'll create random text for you." << endl << endl; 52 | } 53 | 54 | // Function to open a file 55 | // @param inFile file input stream 56 | void inputMap(ifstream& inFile) { 57 | string fileName; 58 | while (true) { 59 | cout << "Input file name? "; 60 | getline(cin, fileName); 61 | inFile.open(fileName); 62 | if (!inFile.fail()) break; 63 | cout << "Canoot open the file. Try again." << endl; 64 | } 65 | } 66 | 67 | // Function to get N-Grams value 68 | int getN() { 69 | int N; 70 | cout << "Value of N? "; 71 | cin >> N; 72 | while (!cin || cin.peek()!= '\n' || N < 2) { 73 | cin.clear(); 74 | cin.ignore(200, '\n'); 75 | cout << "Invalid input. Try again." << endl; 76 | cout << "Value of N? "; 77 | cin >> N; 78 | } 79 | return N; 80 | } 81 | 82 | // Function to build a Map for N-Grams 83 | // 84 | // @param N number of N-Grams 85 | // @param inFile file input stream 86 | HashMap, Vector > buildMap(const int& N, ifstream& inFile) { 87 | HashMap< Vector, Vector > map; 88 | Vector window, suffix; 89 | Queue wrapAround; 90 | string word; 91 | 92 | // Initalize window: read N - 1 words without prefixes. 93 | int windowSize = N - 1; // window size, 94 | for (int i = 0; i < windowSize; i++) { 95 | inFile >> word; // to be 96 | window.add(word); 97 | // wrap around: save first N-1 words for later use 98 | wrapAround.enqueue(word); 99 | } 100 | 101 | // continue to read words until the end of file 102 | while (inFile >> word) { 103 | if (map.containsKey(window)) { 104 | map[window].add(word); 105 | } else { 106 | suffix.add(word); 107 | map.put(window, suffix); // add suffix 108 | } 109 | // reset window 110 | window.remove(0); 111 | window.add(word); 112 | suffix.clear(); // empty the sffix vector!!! 113 | } 114 | 115 | // now is the end of file, deal with wrap around: 116 | while (!wrapAround.isEmpty()) { 117 | word = wrapAround.dequeue(); 118 | if (map.containsKey(window)) { 119 | map[window].add(word); 120 | } else { 121 | suffix.add(word); 122 | map.put(window, suffix); // add suffix 123 | } 124 | // reset window 125 | window.remove(0); 126 | window.add(word); 127 | suffix.clear(); // empty the sffix vector, before next loop!!! 128 | } 129 | 130 | return map; 131 | } 132 | 133 | // DescriptiongetLenth 134 | // 135 | // @param wordsLength # of random words to generate 136 | bool getLenth(int& wordsLength) { 137 | cout << "# of random words to generate (0 to quit)? "; 138 | cin >> wordsLength; 139 | 140 | while (!cin || cin.peek()!= '\n' || wordsLength < 4) { 141 | 142 | if (cin && cin.peek() == '\n' && wordsLength == 0) { 143 | return false; // when input 0, return false. 144 | } else if (cin && cin.peek() == '\n' && wordsLength < 4) { 145 | cout << "Must be at least 4 words." << endl << endl; 146 | } else { 147 | cin.clear(); 148 | cin.ignore(200, '\n'); 149 | cout << "Invalid input. Try again." << endl << endl; 150 | } 151 | 152 | cout << "# of random words to generate (0 to quit)? "; 153 | cin >> wordsLength; 154 | } 155 | 156 | return true; 157 | } 158 | 159 | // Generating Random Text based on map 160 | // 161 | // @param map map of NGrams from buildMap function 162 | // @param wordsLength # of random words to generate 163 | void NGrams(const HashMap, Vector >& map, const int& wordsLength) { 164 | 165 | Vector randomText = randomKey(map); // random text from random key 166 | // // Extra feature (1): find words of random text that starts with a Upper case letter. 167 | // char fLetter = randomText[0][0]; 168 | // while (!isupper(fLetter)) { 169 | // // check if the first letter is upper case 170 | // randomText = randomKey(map); 171 | // fLetter = randomText[0][0]; 172 | // } 173 | 174 | // Setup random window and random suffix: 175 | Vector randomWindow = randomText; // setup random window 176 | string randomTextSuffix = randomElement(map[randomText]); // get a random suffix from map 177 | 178 | // Get the rest random text: 179 | int l = wordsLength - randomWindow.size(); // the length of rest test 180 | if (l <= 1){ 181 | cout << "# of words should be greater than N." << endl << endl; 182 | return; 183 | } 184 | for (int i = 0; i < l; i++) { 185 | slideRandomWindow(randomText, randomWindow, randomTextSuffix, map); 186 | } 187 | 188 | // // Extra feature (2): continue to get words until found a punctuation character. 189 | // int suffixSize = randomTextSuffix.size(); 190 | // char lLetter = randomTextSuffix[suffixSize - 1]; // get last letter of last word 191 | // // check if suffix has a punctuation character: 192 | // while (!ispunct(lLetter)) { 193 | // slideRandomWindow(randomText, randomWindow, randomTextSuffix, map); 194 | // // update last letter of last word!!! 195 | // suffixSize = randomTextSuffix.size(); 196 | // lLetter = randomTextSuffix[suffixSize - 1]; 197 | // } 198 | 199 | // Print the whole random text: 200 | cout << "... "; 201 | for (string w : randomText) { 202 | cout << w << " "; 203 | } 204 | cout << " ..." << endl << endl; 205 | } 206 | 207 | // Function to slide a random window of N-1 size in the Ngrams map 208 | // then choose a reandom key and add values of the key into random text(final output). 209 | // 210 | // @param randomText final result of random text 211 | // @param randomWindow window of N-1 words 212 | // @param randomTextSuffix suffixes of winodw 213 | // @param map map of NGrams from buildMap function 214 | void slideRandomWindow(Vector& randomText, Vector& randomWindow, string& randomTextSuffix, 215 | const HashMap, Vector >& map) { 216 | // reset window 217 | randomWindow.remove(0); 218 | randomWindow.add(randomTextSuffix); 219 | // choose a random suffix from random window 220 | randomTextSuffix = randomElement(map[randomWindow]); 221 | // add random suffix into random text(output) 222 | randomText.add(randomTextSuffix); 223 | } 224 | 225 | -------------------------------------------------------------------------------- /HW2_Serafini/wordladder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jian Zhong on 6/8/20. 3 | // Copyright © 2020 Jian Zhong. All rights reserved. 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "console.h" 12 | #include "lexicon.h" 13 | #include "queue.h" 14 | #include 15 | using namespace std; 16 | 17 | void printGreetings(); 18 | void promptDictionary(fstream&, string&, Lexicon&); 19 | bool promptWords(string&, string&); 20 | bool validFormat(const Lexicon&, const string&, const string&); 21 | void printStack(Stack); 22 | void findWordLadder(const string&, const string&, const Lexicon&); 23 | 24 | int main() { 25 | fstream inFile; 26 | string fileName, startWord, endWord; 27 | Lexicon dictionary; 28 | 29 | printGreetings(); 30 | promptDictionary(inFile, fileName, dictionary); 31 | while (promptWords(startWord, endWord)) { 32 | findWordLadder(startWord, endWord, dictionary); 33 | } 34 | 35 | cout << "Have a nice day!" << endl; 36 | 37 | return 0; 38 | } 39 | 40 | 41 | void printGreetings() { 42 | cout << "Welcome to CS 106B Word Ladder." << endl 43 | << "Please give me two English words, and I will change the" << endl 44 | << "first into the second by changing one letter at a time." << endl 45 | << endl; 46 | } 47 | 48 | void promptDictionary(fstream& inFile, string& fileName, Lexicon& dictionary) { 49 | while (true) { 50 | cout << "Dictionary file name? "; 51 | getline(cin, fileName); 52 | inFile.open(fileName); 53 | if (!inFile.fail()) break; 54 | cout << "Unable to open that file. Try again." << endl; 55 | } 56 | dictionary.addWordsFromFile(inFile); 57 | } 58 | 59 | bool promptWords(string& startWord, string& endWord) { 60 | // if either one input is an Enter key, end the program. 61 | cout << endl; 62 | cout << "Word #1 (or Enter to quit):"; 63 | getline(cin, startWord); 64 | if (startWord.empty()) return false; 65 | 66 | cout << "Word #2 (or Enter to quit):"; 67 | getline(cin, endWord); 68 | if (endWord.empty()) return false; 69 | 70 | // else, continue the game and uppercase both words. 71 | startWord = toLowerCase(startWord); 72 | endWord = toLowerCase(endWord); 73 | return true; 74 | } 75 | 76 | bool validFormat(const Lexicon& dictionary, const string& startWord, const string& endWord) { 77 | if (!dictionary.contains(startWord) || !dictionary.contains(endWord)) { 78 | cout << "The two words must be found in the dictionary." << endl; 79 | return false; 80 | } else if (startWord.length() != endWord.length()) { 81 | cout << "The two words must be the same length." << endl; 82 | return false; 83 | } else if (startWord == endWord) { 84 | cout << "The two words must be different." << endl; 85 | return false; 86 | } 87 | return true; 88 | } 89 | 90 | void printResult(const string& startW, const string& endW, Stack& ladder, bool found = false) { 91 | if (found) { 92 | cout << "A ladder from \"" << endW << "\" to \"" << startW << "\":" << endl; 93 | while(!ladder.isEmpty()) { 94 | cout << ladder.pop(); 95 | if (ladder.size() != 0) cout << " -> "; 96 | } 97 | cout << endl; 98 | } else { 99 | cout << "No word ladder found from \"" << endW << "\" back to \"" 100 | << startW << "\"." << endl; 101 | } 102 | } 103 | 104 | Lexicon neighborsOf(string currWord, const Lexicon& dictionary, Lexicon& usedWords) { 105 | // Find all neighbors of current word and store them into "neighbors": 106 | Lexicon neighbors; // all neighbors of current word 107 | int size = currWord.size(); // word length 108 | 109 | // loop through the first letter to last letter of current word. 110 | for (int i = 0; i < size; i++) { 111 | for(char letter = 'a'; letter <= 'z'; letter++) { 112 | string temp = currWord; 113 | temp[i] = letter; 114 | if (dictionary.contains(temp) && !usedWords.contains(temp)) { // if is legal word and not been used yet. 115 | neighbors.add(temp); // add it into neighbors collection 116 | usedWords.add(temp); // also add it into used collection 117 | } 118 | } 119 | } 120 | return neighbors; 121 | } 122 | 123 | // findWordLadder(), this BFS algorithm check solution only when dequeueing a node from queue => slightly slower! 124 | void findWordLadder(const string& startWord, const string& endWord, const Lexicon& dictionary) { 125 | // If invalid format, terminate this function: 126 | if (!validFormat(dictionary, startWord, endWord)) return; 127 | 128 | // Solve Word Ladder begins: 129 | Queue< Stack > ladders; // ladder combination 130 | Stack ladder; // each possible ladder 131 | Lexicon usedWords; // used words collection 132 | 133 | ladder.push(startWord); // add start word to ladder stack 134 | ladders.enqueue(ladder); // add ladder stack to the ladders queue 135 | usedWords.add(startWord); // add start word to the used word collection 136 | 137 | // Use BFS to serach a solution: 138 | while (!ladders.isEmpty()) { 139 | Stack currLadder = ladders.dequeue(); // take out a ladder stack for evaluation 140 | string currWord = currLadder.peek(); // take out a word on top of the ladder stack 141 | 142 | // if solution found! 143 | if (currWord == endWord) { 144 | printResult(startWord, endWord, currLadder, true); // print the result. 145 | return; 146 | } 147 | 148 | // For each unvisited neighbor: if found end word, return ladder with the neighbor, else enqueue sub-ladders of curr-ladder. 149 | for (string neighbor : neighborsOf(currWord, dictionary, usedWords)) { 150 | Stack subLadder = currLadder; 151 | subLadder.push(neighbor); 152 | ladders.enqueue(subLadder); 153 | } 154 | } 155 | 156 | printResult(startWord, endWord, ladder, false); // all tried, print no results. 157 | } 158 | -------------------------------------------------------------------------------- /HW3_Recursion/fractals.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: fractals.cpp 3 | * -------------------------- 4 | * Name: Jian Zhong 5 | * This file contains fractal problems for CS106B. 6 | */ 7 | 8 | 9 | #include "fractals.h" 10 | #include 11 | #include "gbufferedimage.h" 12 | 13 | using namespace std; 14 | 15 | const int LEAF_COLOR = 0x2e8b57; /* Color of all leaves of recursive tree (level 1) */ 16 | const int BRANCH_COLOR = 0x8b7765; /* Color of all branches of recursive tree (level >=2) */ 17 | 18 | /** 19 | ******************************** HW 3.1 Sierpinski ************************************** 20 | * Draws a Sierpinski triangle of the specified size and order, placing its 21 | * bottom-left corner at position (x, y). 22 | * 23 | * This will be called by fractalgui.cpp. 24 | * 25 | * @param gw - The window in which to draw the Sierpinski triangle. 26 | * @param x - The x coordinate of the bottom-left corner of the triangle. 27 | * @param y - The y coordinate of the bottom-left corner of the triangle. 28 | * @param size - The length of one side of the triangle. 29 | * @param order - The order of the fractal. 30 | * ************************************************************************************** 31 | */ 32 | void drawATriangle(GWindow& gw, double x, double y, double size) { 33 | gw.drawLine(x, y, x + size, y); 34 | gw.drawLine(x + size, y, x + (size/2), y + (sqrt(3)/2) * size); 35 | gw.drawLine(x + (size/2), y + (sqrt(3)/2) * size, x, y); 36 | } 37 | 38 | void drawSierpinskiTriangle(GWindow& gw, double x, double y, double size, int order) { 39 | if (x < 0 || y < 0 || size < 0 || order < 0) { //causes error 40 | throw("invalid input"); 41 | return; 42 | } 43 | 44 | if (order == 1) { 45 | drawATriangle(gw, x, y, size); // draw a triangle 46 | } else { 47 | drawSierpinskiTriangle(gw, x, y, size/2, order-1); 48 | drawSierpinskiTriangle(gw, x+(size/2), y, size/2, order-1); 49 | drawSierpinskiTriangle(gw, x+(size/4), y+(sqrt(3)/2)*(size)/2, size/2, order-1); 50 | } 51 | } 52 | 53 | 54 | /** 55 | ******************************** HW 3.2 Recurisve Tree ********************************** 56 | * Draws a recursive tree fractal image of the specified size and order, 57 | * placing the bounding box's top-left corner at position (x,y). 58 | * 59 | * This will be called by fractalgui.cpp. 60 | * 61 | * @param gw - The window in which to draw the recursive tree. 62 | * @param x - The x coordinate of the top-left corner of the "bounding box". // 800 63 | * @param y - The y coordinate of the top-left corner of the "bounding box". // 650 64 | * @param size - The length of one side of the bounding box. // 650 65 | * @param order - The order of the fractal. 66 | * ************************************************************************************** 67 | */ 68 | void drawTreeHelper(GWindow& gw, double x, double y, double size, int order, int angle) { 69 | if (order < 0) { 70 | error("*** Invalid input: order should be >= 0. ***"); 71 | } else if (order != 0) { 72 | 73 | // Set color 74 | if (order == 1) { // the outter most area 75 | gw.setColor(LEAF_COLOR); 76 | } else { // NOT outter most area 77 | gw.setColor(BRANCH_COLOR); 78 | } 79 | 80 | // Draw tree trunk and get the trunk's tip 81 | GPoint tip = gw.drawPolarLine(x, y, size, angle); 82 | 83 | // Recursive Case: while NOT leaf, draw branches by recursion: 84 | int j = 45; 85 | for (int i = 0; i < 7; i++) { 86 | drawTreeHelper(gw, tip.getX(), tip.getY(), size/2, order-1, angle-90+j); // angle-90+j: 15, 30, 45, 60, 75, 90, 105 87 | j += 15; 88 | } 89 | } 90 | } 91 | 92 | void drawTree(GWindow& gw, double x, double y, double size, int order) { 93 | drawTreeHelper(gw, x + size/2, y + size, size / 2, order, 90); 94 | } 95 | 96 | /** 97 | ******************************** HW 3.3 Mandelbrot Set ********************************** 98 | * Draws a Mandelbrot Set in the graphical window give, with maxIterations 99 | * (size in GUI) and in a given color (zero for palette) 100 | * 101 | * This will be called by fractalgui.cpp. 102 | * 103 | * @param gw - The window in which to draw the Mandelbrot set. 104 | * @param minX - left-most column of grid 105 | * @param incX - increment value of columns of grid 106 | * @param minY - top-most row of grid 107 | * @param incY - increment value of rows of grid 108 | * @param maxIterations - The maximum number of iterations to run recursive step 109 | * @param color - The color of the fractal; zero if palette is to be used 110 | * ************************************************************************************** 111 | */ 112 | void mandelbrotSet(GWindow& gw, double minX, double incX, 113 | double minY, double incY, int maxIter, int color) { 114 | 115 | color = 0; 116 | Vector palette = setPalette(); // setup the palette 117 | int width = gw.getCanvasWidth(); // Canvas is the whole white window 118 | int height = gw.getCanvasHeight(); 119 | GBufferedImage image(width, height, 0xffffff); // create an image of black color 120 | gw.add(&image); // add image into (leftX, topY) 121 | Grid pixels = image.toGrid(); // Convert the entire image to pixels in Gird collection 122 | 123 | // traverse all pixels in the canvas 124 | for (int c = 0; c < pixels.numCols(); c++) { 125 | for (int r = 0; r < pixels.numRows(); r++) { 126 | 127 | // Draw pixel in M set 128 | int numIterations = mandelbrotSetIterations(Complex(minX + c*incX, minY + r*incY), maxIter); 129 | if (color != 0) { // if color is non-zero, set as int color. 130 | if (numIterations == maxIter) { 131 | pixels[r][c] = color; // C is in M set, draw the pixel 132 | } 133 | } else { // else color is zero, use the palette of colors for all pixels 134 | pixels[r][c] = palette[numIterations % palette.size()]; 135 | } 136 | 137 | } // end inner for-loop 138 | } // end outer for-loop 139 | 140 | image.fromGrid(pixels); // Converts and puts the grid back into the image onscreen 141 | } 142 | 143 | int mandelbrotSetIterations(Complex Z, Complex C, int maxIterations, int numIterations) { 144 | 145 | // if exhaust max iteration => in M set 146 | // if absolute value of Z > 4 => Not in M set 147 | if (Z.abs() > 4 || numIterations == maxIterations) { 148 | return numIterations; 149 | } else { 150 | return mandelbrotSetIterations(Z * Z + C, C, maxIterations, numIterations + 1); 151 | } 152 | } 153 | 154 | int mandelbrotSetIterations(Complex cpx, int maxIterations) { 155 | // first Z starts is 0, 156 | // first iteration is 0. 157 | return mandelbrotSetIterations(Complex (0, 0), cpx, maxIterations, 0); 158 | } 159 | 160 | // Helper function to set the palette 161 | Vector setPalette() { 162 | Vector colors; 163 | 164 | // Feel free to replace with any palette. 165 | // You can find palettes at: 166 | // http://www.colourlovers.com/palettes 167 | 168 | // Example palettes: 169 | // http://www.colourlovers.com/palette/4480793/in_the_middle 170 | // string colorSt = "#A0B965,#908F84,#BF3C43,#9D8E70,#C9BE91,#A0B965,#908F84,#BF3C43"; 171 | 172 | // http://www.colourlovers.com/palette/4480786/Classy_Glass 173 | // string colorSt = "#9AB0E9,#C47624,#25269A,#B72202,#00002E,#9AB0E9,#C47624,#25269A"; 174 | 175 | // The following is the "Hope" palette: 176 | // http://www.colourlovers.com/palette/524048/Hope 177 | string colorSt = "#04182B,#5A8C8C,#F2D99D,#738585,#AB1111,#04182B,#5A8C8C,#F2D99D"; 178 | VectorcolorsStrVec = stringSplit(colorSt,","); 179 | for (string color : colorsStrVec) { 180 | colors.add(convertColorToRGB(trim(color))); 181 | } 182 | return colors; 183 | } 184 | -------------------------------------------------------------------------------- /HW3_Recursion/grammarsolver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: grammarsolver.cpp 3 | * -------------------------- 4 | * Name: Jian Zhong 5 | */ 6 | 7 | #include "grammarsolver.h" 8 | #include "hashmap.h" 9 | #include "random.h" 10 | 11 | using namespace std; 12 | 13 | // Function Prototypes: 14 | Vector grammarGenerate(istream&, const string&, int); 15 | HashMap> scanFileIntoMap(istream&); 16 | void grammarHelper(const HashMap>&, string, string&); 17 | 18 | /** 19 | * Generates grammar for a given symbol a certain number of times given 20 | * a BNF input file. 21 | * 22 | * This will be called by grammarmain.cpp. 23 | * 24 | * @param input - Input stream of BNF file. 25 | * @param symbol - Symbol to generate 26 | * @param times - Number of times grammar is generated 27 | * @return Vector of strings of size times with random generations of symbol 28 | */ 29 | Vector grammarGenerate(istream& input, string symbol, int times) { 30 | Vector allResults; 31 | HashMap> BNF = scanFileIntoMap(input); 32 | 33 | for (int i = 0; i < times; i++) { 34 | string result; 35 | grammarHelper(BNF, symbol, result); 36 | allResults.add(result); 37 | } 38 | 39 | return allResults; 40 | } 41 | 42 | /// Function to read the Input File and return a map of BNF content. 43 | /// @param input - File input stream 44 | /// @return a map of BNF content. 45 | HashMap > scanFileIntoMap(istream& input) { 46 | // scan the file line by line and stores contents into the BNFmap. 47 | string line; 48 | HashMap > BNF; 49 | 50 | // while read a line in file, 51 | while(getline(input, line)) { 52 | // split symbol from rules by "::=" 53 | Vector splitedLine = stringSplit(line, "::="); 54 | 55 | // add symbols(splited[0]) and rules(splited[1]) into the BNFmap 56 | BNF.put(splitedLine[0], stringSplit(splitedLine[1], "|")); 57 | } 58 | return BNF; 59 | } 60 | 61 | /// Function to generate Random Expansions from the Grammar: 62 | /// @param BNF - A map of BNF content 63 | /// @param symbol - Symbol to generate 64 | /// @param result - Grammer result 65 | void grammarHelper(const HashMap>& BNF, string symbol, string& result) { 66 | 67 | // if symbol is non-terminal symbol, randomly choose a sub-symbol and explorer it. 68 | if (BNF.containsKey(symbol)) { 69 | 70 | // find a random next symbol 71 | string symbolStr = randomElement(BNF[symbol]); 72 | 73 | // if the symbol contains spaces, multiple symbols exist. 74 | if (stringContains(symbolStr, " ")) { 75 | 76 | // split multiple symbols into a vector by whitespace. 77 | Vector subSymbols = stringSplit(symbolStr, " "); 78 | 79 | // explorer each sub-symbol in subSymbols recursively. 80 | for (string subSymbol : subSymbols) { 81 | grammarHelper(BNF, subSymbol, result); 82 | } 83 | 84 | } else { 85 | // else, explorer the just 1 symbol recursively. 86 | grammarHelper(BNF, symbolStr, result); 87 | } 88 | 89 | } else { 90 | // found a terminal symbol! 91 | result += symbol + " "; // add it into result. 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /HW4_Boggle/Boggle.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // boggle.cpp 3 | // 4 | // Created by Jian Zhong on 7/14/20. 5 | // Copyright © 2020 Jian Zhong. All rights reserved. 6 | // 7 | 8 | #include "Boggle.h" 9 | 10 | // letters on all 6 sides of every cube 11 | static string CUBES[16] = { 12 | "AAEEGN", "ABBJOO", "ACHOPS", "AFFKPS", 13 | "AOOTTW", "CIMOTU", "DEILRX", "DELRVY", 14 | "DISTTY", "EEGHNW", "EEINSU", "EHRTVW", 15 | "EIOSST", "ELRTTY", "HIMNQU", "HLNNRZ" 16 | }; 17 | 18 | Boggle::Boggle(Lexicon& dictionary, string& boardText) { 19 | boggleBoard.resize(4,4); // initialize a 4x4 game board 20 | boggleDict = dictionary; // setup boggle dictionary 21 | 22 | // BoggleGUI::labelAllCubes(boardText); 23 | 24 | // generated ramdom board text 25 | if (boardText.empty()) { 26 | shuffle(CUBES, 16); // shuffle the 16 cube loations in CUBES 27 | int pos = 0; // shuffle the cube letter 28 | for (int r = 0; r < boggleBoard.nRows; r++) { 29 | for(int c = 0; c < boggleBoard.nCols; c++) { 30 | boggleBoard[r][c] = CUBES[pos][randomInteger(0, 5)]; 31 | boardText += boggleBoard[r][c]; 32 | pos++; 33 | } 34 | } 35 | } else { 36 | // user mannual input board text 37 | boardText = toUpperCase(boardText); // just uppcase all letters 38 | int pos = 0; 39 | for (int r = 0; r < boggleBoard.nRows; r++) { 40 | for(int c = 0; c < boggleBoard.nCols; c++) { 41 | boggleBoard[r][c] = boardText[pos]; 42 | pos++; 43 | } 44 | } 45 | } 46 | 47 | } 48 | 49 | char Boggle::getLetter(int row, int col) { 50 | if (!boggleBoard.inBounds(row, col)) throw int(); // if out of bounds 51 | return boggleBoard[row][col]; 52 | } 53 | 54 | 55 | bool Boggle::checkWord(string word) { 56 | word = toUpperCase(word); // case-insensity 57 | 58 | if (word.length() >= 4 && // valid Boggle word 59 | !wordsHuman.contains(word) && // has not already been found 60 | boggleDict.contains(word)) { // is in boggleDict 61 | return true; 62 | } 63 | return false; 64 | } 65 | 66 | bool Boggle::humanWordSearch(string word) { 67 | BoggleGUI::setAnimationDelay(100); // set GUI delay time 68 | word = toUpperCase(word); // case-insensitive 69 | char first = word[0]; // letter to be found 70 | Grid marked; // creat a separate board for marking 71 | marked.resize(4, 4); // a separate grid to record used grid, 'X' means used. 72 | 73 | // loop through the board grids 74 | for (int r = 0; r < boggleBoard.nRows; r++) { 75 | for (int c = 0; c < boggleBoard.nCols; c++) { 76 | 77 | // hightlight each grid while searching 78 | BoggleGUI::setHighlighted(r, c, true); 79 | marked.set(r, c, 'X'); // mark the grid as used. 80 | 81 | // if first letter of word found on a grid: 82 | if (first == boggleBoard[r][c]) { 83 | 84 | // try to form word based on boggleBoard[r][c] by recursive bactrack 85 | if (humanHelper(word, word.substr(0, 1), r, c, marked)) { // if can be formed! 86 | wordsHuman.add(word); 87 | return true; 88 | } else { // if can't be formed 89 | BoggleGUI::setHighlighted(r, c, false); // un-hightlight the grid 90 | marked.set(r, c, ' '); // un-mark the grid 91 | } 92 | 93 | } else { // if not found on that grid 94 | BoggleGUI::setHighlighted(r, c, false); // un-hightlight the grid 95 | marked.set(r, c, ' '); // un-mark the grid 96 | } 97 | 98 | } 99 | } 100 | return false; // finish loopping the entire board, if no word can be formed, return false. 101 | } 102 | 103 | bool Boggle::humanHelper(string word, string formed, int row, int col, Grid& marked) const { 104 | char next = word[1]; // get the second letter of word 105 | 106 | // Base Cases: if word only 1 letter, it means formed now is a complete word, so word found! 107 | if (word.length() == 1) return true; 108 | 109 | // generate all available neighbors of current gird 110 | Vector nList = neigborsOf(row, col); 111 | 112 | // for each available neighbor of current grid 113 | for (int i = 0; i < nList.size(); i++) { 114 | char neiToTry = nList[i].letter; // choose a availabe negihbor 115 | int nRow = nList[i].row; // neigbor's row 116 | int nCol = nList[i].col; // neigbor's col 117 | 118 | // each time meets a marked(used) neighbor, skip it. 119 | if (marked[nRow][nCol] == 'X') continue; 120 | 121 | BoggleGUI::setHighlighted(nRow, nCol, true); // hightlight the chosen neighbor 122 | 123 | // if the neighbor is exactly the next letter , fomred is in dictionary, and unmarked: 124 | if (neiToTry == next && boggleDict.containsPrefix(formed + next)) { 125 | 126 | // Choose: 127 | marked.set(nRow, nCol, 'X'); // mark the neigbor 128 | BoggleGUI::setHighlighted(nRow, nCol, true); // hightlight the choosen negihbor 129 | 130 | // Explore: the rest by recursion, return true if found! 131 | if (humanHelper(word.substr(1), formed + next, nRow, nCol, marked)) return true; 132 | 133 | // Un-choose: 134 | marked.set(nRow, nCol, ' '); // un-mark the neigbor 135 | BoggleGUI::setHighlighted(nRow, nCol, false); // un-hightlight every un-choosen path 136 | } 137 | 138 | BoggleGUI::setHighlighted(nRow, nCol, false); // un-hightlight the choosen negihbor 139 | } 140 | return false; // all neigbors have tried, return false. 141 | } 142 | 143 | Vector Boggle::neigborsOf(int row, int col) const { 144 | Vector neighborList; 145 | neigborT newNeigbor; 146 | 147 | for (int r = row-1; r <= row+1; r++) { 148 | for (int c = col-1; c <= col+1; c++) { 149 | if (r != row || c != col ) { // don't want itself 150 | if (boggleBoard.inBounds(r, c)) { // must be inside the board 151 | newNeigbor.row = r; 152 | newNeigbor.col = c; 153 | newNeigbor.letter = boggleBoard[r][c]; 154 | neighborList.add(newNeigbor); 155 | } 156 | } 157 | } 158 | } 159 | return neighborList; 160 | } 161 | 162 | Set Boggle::computerWordSearch() { 163 | Set result; // all words found by computer 164 | Grid used; // a separate grid to record used grid, 'X' means used. 165 | used.resize(4, 4); 166 | 167 | for (int c = 0; c < boggleBoard.nCols; c++) { 168 | for (int r = 0; r < boggleBoard.nRows; r++) { 169 | string formed = ""; 170 | formed += getLetter(c, r); // get the visted formed letter 171 | used.set(c, r, 'X'); // marked it as used 172 | computerHelper(c, r, formed, used, result); 173 | used.set(c, r, ' '); // un-marked it 174 | } 175 | } 176 | wordsComputer = result; // update computer's word set. 177 | 178 | return result; 179 | } 180 | 181 | 182 | void Boggle::computerHelper(int row, int col, string formed, 183 | Grid& used, Set& result) { 184 | // Base cases: 185 | if (checkWord(formed)) result.add(formed); // Found word! add into words' set! 186 | 187 | // Find all available neigbors of current gird. 188 | Vector neigList = neigborsOf(row, col); 189 | int neigSize = neigList.size(); 190 | 191 | // Keep searching if board[row][col] have neigbors, 192 | if (neigSize > 0){ 193 | 194 | // for each neighbor 195 | for (int i = 0; i < neigSize; i++) { 196 | // choose a neigbor 197 | char neigh = neigList[i].letter; 198 | int nRow = neigList[i].row; 199 | int nCol = neigList[i].col; 200 | if (boggleDict.containsPrefix(formed + neigh) && used[nRow][nCol] != 'X') { 201 | // if is the neighbor contians valid prefix and not used 202 | used.set(nRow, nCol, 'X'); // marked as used 203 | computerHelper(nRow, nCol, formed + neigh, used, result); // explore next by recursion 204 | used.set(nRow, nCol, ' '); // unmark it 205 | } 206 | } 207 | 208 | } 209 | 210 | } 211 | 212 | int Boggle::scoreAward(const Set& wordsList) const { 213 | int score = 0; 214 | for (string word : wordsList) { 215 | score += word.length() - 3; 216 | } 217 | return score; 218 | } 219 | 220 | int Boggle::getScoreHuman() { 221 | return scoreAward(wordsHuman); 222 | } 223 | 224 | int Boggle::getScoreComputer() { 225 | return scoreAward(wordsComputer); 226 | } 227 | 228 | Set Boggle::getWordsFound(const PlayerT& player) const { 229 | if (player == Human) { 230 | return wordsHuman; 231 | } else if (player == Computer) { 232 | return wordsComputer; 233 | } 234 | throw "The player type is invalid."; 235 | } 236 | 237 | ostream& operator<<(ostream& out, Boggle& boggle) { 238 | for (int r = 0; r < 4; r++) { 239 | for(int c = 0; c < 4; c++) { 240 | out << boggle.getLetter(r, c); 241 | if ((c+1) % 4 == 0) out << endl; 242 | } 243 | } 244 | return out; 245 | } 246 | -------------------------------------------------------------------------------- /HW4_Boggle/Boggle.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // boggle.h 4 | // 5 | // files for a Boggle class representing the state of the current Boggle game 6 | // 7 | // Created by Jian Zhong on 7/14/20. 8 | // Copyright © 2020 Jian Zhong. All rights reserved. 9 | // 10 | 11 | #ifndef _boggle_h 12 | #define _boggle_h 13 | 14 | #include 15 | #include 16 | #include "lexicon.h" 17 | #include "grid.h" 18 | #include "bogglegui.h" 19 | #include "shuffle.h" 20 | 21 | using namespace std; 22 | 23 | enum PlayerT {Human, Computer}; 24 | 25 | struct neigborT { 26 | char letter; 27 | int row = 0; 28 | int col = 0; 29 | }; 30 | 31 | class Boggle { 32 | public: 33 | /* 34 | * Constrct a game board according to boardText string. 35 | */ 36 | Boggle(Lexicon& dictionary, string& boardText); 37 | 38 | /* 39 | * Check if the given word is suitable to search for. 40 | */ 41 | bool checkWord(string word); 42 | 43 | /* 44 | * Cheack if the user-input word can be formed in boggle board. 45 | */ 46 | bool humanWordSearch(string word); 47 | 48 | /* 49 | * perform a search on the board for all words that can be formed 50 | */ 51 | Set computerWordSearch(); 52 | 53 | /* 54 | * Get human score 55 | */ 56 | int getScoreHuman(); 57 | 58 | /* 59 | * Get computer score 60 | */ 61 | int getScoreComputer(); 62 | 63 | /* 64 | * Get words found so far by player Human or by Computer. 65 | */ 66 | Set getWordsFound(const PlayerT& player) const; 67 | 68 | /* 69 | * Get the letter on board[row][col]. 70 | */ 71 | char getLetter(int row, int col); 72 | 73 | /* 74 | * Operator to print the boggle object 75 | */ 76 | friend ostream& operator<<(ostream& out, Boggle& boggle); 77 | 78 | private: 79 | /* 80 | * Helper for humanWordSearch(). 81 | */ 82 | bool humanHelper(string word, string formed, 83 | int row, int col, Grid& marked) const; 84 | 85 | /* 86 | * Helper for computerWordSearch(). 87 | */ 88 | void computerHelper(int row, int col, string formed, 89 | Grid& used, Set& result); 90 | 91 | /* 92 | * Find all neighbors of a specified grid in boggle board, called by humanHelper(). 93 | */ 94 | Vector neigborsOf(int row, int col) const; 95 | 96 | /* 97 | * Adward score by words so far, called by getScoreHuman() and getScoreComputer(). 98 | */ 99 | int scoreAward(const Set& wordsList) const; 100 | 101 | Lexicon boggleDict; // boggle dictionary 102 | Grid boggleBoard; // boggle board 103 | Set wordsHuman; // human words list 104 | Set wordsComputer; // human words list 105 | }; 106 | 107 | #endif // _boggle_h 108 | -------------------------------------------------------------------------------- /HW4_Boggle/boggleplay.cpp: -------------------------------------------------------------------------------- 1 | // boggleplay.cpp 2 | // 3 | // client to perform console UI and work with Boggle class to play a game 4 | // 5 | // Created by Jian Zhong on 7/14/20. 6 | // 7 | // Copyright © 2020 Jian Zhong. All rights reserved. 8 | 9 | #include "lexicon.h" // store dictionary file 10 | #include "simpio.h" // getYesOrNo(); 11 | #include "bogglegui.h" // BoggleGUI 12 | #include "Boggle.h" // Boggle class 13 | #include "console.h" // clearConsole(): to clear the screen 14 | #include // isalpha() 15 | using namespace std; 16 | 17 | 18 | // Function prototypes: 19 | void blankBoard(); 20 | string getBoardText(); 21 | void printBoard(const string&); 22 | void playBoggle(Boggle&, string = "FirstPrompt"); 23 | void announceWinner(Boggle&); 24 | bool isValidText(string&); 25 | string mannualInput(); 26 | void printState(Boggle&, const PlayerT&); 27 | 28 | 29 | /* 30 | * playOneGame() 31 | */ 32 | void playOneGame(Lexicon& dictionary) { 33 | // Setup blank Boggle GUI 34 | blankBoard(); 35 | 36 | // Get board text for the Boggle. 37 | string boardText = getBoardText(); 38 | 39 | // Setup Boggle Board with board text 40 | Boggle board(dictionary, boardText); 41 | 42 | // Setup and print Boggle GUI 43 | printBoard(boardText); 44 | 45 | // Play Boggle game one time 46 | playBoggle(board); 47 | 48 | // Announce Winner 49 | announceWinner(board); 50 | } 51 | 52 | 53 | // Function Definitions: 54 | bool isValidBoardText(string& boardText) { 55 | boardText = getLine("Type the 16 letters to appear on the board: "); 56 | 57 | if (boardText.length() != 16) return false; 58 | 59 | for (unsigned i = 0; i < boardText.length(); i++) { 60 | if (!isalpha(boardText[i])) return false; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | string mannualInput() { 67 | string boardText; 68 | while (!isValidBoardText(boardText)) { 69 | cout << "That is not a valid 16-letter board string. Try again." << endl; 70 | } 71 | 72 | return boardText; 73 | } 74 | 75 | string getBoardText() { 76 | return (getYesOrNo("Do you want to generate a random board?")) ? "" : mannualInput(); 77 | } 78 | 79 | void blankBoard() { 80 | if (!BoggleGUI::isInitialized()) { 81 | BoggleGUI::initialize(4,4); 82 | } else { 83 | BoggleGUI::reset(); 84 | } 85 | } 86 | 87 | void printBoard(const string& boardText) { 88 | BoggleGUI::labelAllCubes(boardText); 89 | BoggleGUI::setScore(0, BoggleGUI::HUMAN); 90 | } 91 | 92 | void printState(Boggle& board, const PlayerT& whichPlayer) { 93 | if (whichPlayer == Human) { 94 | cout << board << endl; // print boggle board 95 | cout << "Your words (" << board.getWordsFound(Human).size() << "): " 96 | << board.getWordsFound(Human) << endl; 97 | cout << "Your score: " << board.getScoreHuman() << endl; 98 | } else if (whichPlayer == Computer) { 99 | 100 | cout << endl << "It's my turn!" << endl; 101 | cout << "My words (" << board.getWordsFound(Computer).size() << "): " 102 | << board.getWordsFound(Computer) << endl; 103 | cout << "My score: " << board.getScoreComputer() << endl; 104 | 105 | } else { 106 | throw "Invaild Player type."; 107 | } 108 | } 109 | 110 | 111 | void playBoggle(Boggle& board, string word) { 112 | bool QUIT = false; 113 | string response; 114 | 115 | // Human turn comes first, after enter key is pressed, then starts the computer turn. 116 | do { 117 | // Human Turn: 118 | clearConsole(); // clean up console 119 | if (word == "FirstPrompt") { 120 | response = "It's your turn!"; 121 | } else if (!board.checkWord(word)) { 122 | response = "You must enter an unfound 4+ letter word from the dictionary."; 123 | } else if (!board.humanWordSearch(word)) { 124 | response = "That word can't be formed on this board."; 125 | } else { // word found ! 126 | BoggleGUI::recordWord(word, BoggleGUI::HUMAN); 127 | response = "You found a new word! \"" + toUpperCase(word) + "\""; 128 | } 129 | cout << response << endl; 130 | BoggleGUI::setStatusMessage(response); // print GUI message 131 | printState(board, Human); // print state 132 | word = getLine("Type a word (or Enter to stop): "); // re-prompt for word 133 | BoggleGUI::clearHighlighting(); // clean GUI Highlighting 134 | 135 | // Computer Turn: 136 | if (word == "") { 137 | // let computer to find all words on board 138 | board.computerWordSearch(); 139 | 140 | // print computer state on console 141 | printState(board, Computer); 142 | 143 | // print computer words so far on GUI window. 144 | for (string word : board.getWordsFound(Computer)) { 145 | BoggleGUI::recordWord(word, BoggleGUI::COMPUTER); 146 | } 147 | 148 | // end the game for one round 149 | QUIT = true; 150 | } 151 | } while(!QUIT); 152 | } 153 | 154 | void announceWinner(Boggle& board) { 155 | string response; 156 | if (board.getScoreHuman() < board.getScoreComputer()) { 157 | response = "Ha ha ha, I destroyed you. Better luck next time, puny human!"; 158 | } else { 159 | response = "WOW, you defeated me! Congratulations!"; 160 | } 161 | cout << response << endl; 162 | BoggleGUI::setStatusMessage(response); 163 | } 164 | -------------------------------------------------------------------------------- /HW5_PatientQueue/HeapPatientQueue.cpp: -------------------------------------------------------------------------------- 1 | // heappatientqueue.cpp 2 | 3 | #include "HeapPatientQueue.h" 4 | 5 | HeapPatientQueue::HeapPatientQueue() { 6 | clear(); 7 | } 8 | 9 | 10 | HeapPatientQueue::~HeapPatientQueue() { 11 | delete [] PQueue; 12 | PQueue = nullptr; // advoid dangling pionter 13 | } 14 | 15 | 16 | void HeapPatientQueue::clear() { 17 | // free memory if necessary 18 | if (PQueue != nullptr) { 19 | delete [] PQueue; 20 | } 21 | 22 | // intialize a new patient queue 23 | capacity = IINITIAL_CAPACITY; 24 | PQueue = new PatientNode[capacity]; 25 | size = 0; 26 | } 27 | 28 | 29 | string HeapPatientQueue::frontName() { 30 | return PQueue[1].name; 31 | } 32 | 33 | 34 | int HeapPatientQueue::frontPriority() { 35 | return PQueue[1].priority; 36 | } 37 | 38 | 39 | bool HeapPatientQueue::isEmpty() { 40 | return size == 0; 41 | } 42 | 43 | // log n 44 | void HeapPatientQueue::newPatient(string name, int priority){ 45 | // if non-empty queue but size is full 46 | if (size + 1 >= capacity) { // Note: max size + 1 == capacity 47 | expandCapacity(); 48 | } 49 | 50 | // place new patient at the first empty index 51 | size++; 52 | PQueue[size].name = name; 53 | PQueue[size].priority = priority; 54 | 55 | // percolate up 56 | percolateUp(size); 57 | } 58 | 59 | 60 | // log n 61 | string HeapPatientQueue::processPatient() { 62 | if (isEmpty()) { 63 | error("Processing patient in an EMPTY queue."); 64 | } 65 | 66 | string removedName = frontName(); // name for later output 67 | 68 | // Replace the front with last patient. 69 | swap(PQueue[1], PQueue[size]); 70 | size--; // remove the last patient 71 | 72 | // Percolate down the front patient. 73 | percolateDown(1); 74 | 75 | return removedName; 76 | } 77 | 78 | 79 | // n 80 | void HeapPatientQueue::upgradePatient(string name, int newPriority) { 81 | if (isEmpty()) { 82 | error("Upgrading patient in an EMPTY queue."); 83 | } 84 | 85 | // Search through heap to find the specified patient 86 | int indexToFind; // the index of the specified patient 87 | for (indexToFind = 1; indexToFind <= size; indexToFind++) { 88 | if (PQueue[indexToFind].name == name) break; 89 | } 90 | 91 | // Now currIndex is the index of the specifed patient: 92 | if (indexToFind > size) { 93 | // if no such patient name, throw exception 94 | error("No such patient name in the queue."); 95 | } else if (newPriority >= PQueue[indexToFind].priority) { 96 | // if existing priority is already more urgent, throw exception 97 | error("The existing priority is already more urgent."); 98 | } 99 | 100 | // Upgrade the new priority 101 | PQueue[indexToFind].priority = newPriority; 102 | 103 | // Percolate up 104 | percolateUp(indexToFind); 105 | } 106 | 107 | 108 | string HeapPatientQueue::toString() { 109 | string output = ""; 110 | for (int i = 1; i <= size; i++) { 111 | if (i != 1) output += ", "; 112 | output += integerToString(PQueue[i].priority) + ":" + PQueue[i].name; 113 | } 114 | return "{" + output + "}"; 115 | } 116 | 117 | 118 | // Percolate Up algorithm 119 | void HeapPatientQueue::percolateUp(int child) { 120 | // percolate up by swapping with its parent 121 | int parent = child / 2; 122 | while (child != 1 && PQueue[parent].priority > PQueue[child].priority) { 123 | swap(PQueue[parent], PQueue[child]); // swap child and parent 124 | child = parent; // update child index 125 | parent = child / 2; // update parent index 126 | } 127 | } 128 | 129 | 130 | // Percolate Down algorithm 131 | void HeapPatientQueue::percolateDown(int parent) { 132 | int left = parent * 2; 133 | int right = parent * 2 + 1; 134 | while ( (left <= size || right <= size) && // has at least one child 135 | (PQueue[parent].priority > PQueue[left].priority || // not correct order 136 | PQueue[parent].priority > PQueue[right].priority) ) { 137 | 138 | // CASE 1: has 1 child(left child). 139 | if (right > size) { 140 | if (PQueue[parent].priority >= PQueue[left].priority) { 141 | // if left child is urgent than or equal to parent 142 | swap(PQueue[parent], PQueue[left]); // swap the left child 143 | parent = left; // update parent index 144 | } 145 | break; // Done for percolating! Now in correct order along with only one child 146 | } 147 | 148 | // CASE 2: has 2 children. 149 | if (PQueue[left].priority < PQueue[right].priority) { 150 | // left child is more urgent. 151 | swap(PQueue[parent], PQueue[left]); // swap the left child 152 | parent = left; // update parent index 153 | } else if (PQueue[left].priority > PQueue[right].priority){ 154 | // right child is more urgent. 155 | swap(PQueue[parent], PQueue[right]); // swap the right child 156 | parent = right; // update parent index 157 | } else { 158 | // left child and right child have a tie in priority. 159 | if (PQueue[left].name < PQueue[right].name) { // breaking ties by string comparison 160 | swap(PQueue[parent], PQueue[left]); // swap the left child 161 | parent = left; // update parent index 162 | } else { 163 | swap(PQueue[parent], PQueue[right]); // swap the right child 164 | parent = right; // update parent index 165 | } 166 | } 167 | 168 | left = parent * 2; // update left child index 169 | right = parent * 2 + 1; // update right child index 170 | } 171 | } 172 | 173 | 174 | // Expand the capacity by doubling it. 175 | void HeapPatientQueue::expandCapacity() { 176 | capacity *= 2; // update capacity first (private variable) 177 | PatientNode* newPQ = new PatientNode[capacity]; 178 | for (int i = 0; i <= size; i++) { 179 | newPQ[i] = PQueue[i]; 180 | } 181 | delete [] PQueue; 182 | PQueue = newPQ; 183 | } 184 | -------------------------------------------------------------------------------- /HW5_PatientQueue/HeapPatientQueue.h: -------------------------------------------------------------------------------- 1 | // heappatientqueue.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include "patientnode.h" 8 | #include "patientqueue.h" 9 | #include "error.h" 10 | #include "strlib.h" 11 | 12 | using namespace std; 13 | 14 | const int IINITIAL_CAPACITY = 10; 15 | 16 | class HeapPatientQueue : public PatientQueue { 17 | public: 18 | // constructor 19 | HeapPatientQueue(); 20 | 21 | // destructor 22 | ~HeapPatientQueue(); 23 | 24 | // clean up the current queue 25 | void clear(); 26 | 27 | // get the front patient's name 28 | string frontName(); 29 | 30 | // cheack if current queue is empty 31 | bool isEmpty(); 32 | 33 | // get the most urgent patient's priority 34 | int frontPriority(); 35 | 36 | // add a new paitient along with the priority 37 | void newPatient(string name, int priority); 38 | 39 | // upgrade an existing patient's priority 40 | void upgradePatient(string name, int newPriority); 41 | 42 | // remove the most urgent patient 43 | string processPatient(); 44 | 45 | // display the current patient queue as a string 46 | string toString(); 47 | 48 | private: 49 | PatientNode* PQueue = nullptr; // a patient queue pionter to the most urgent patient 50 | int capacity; // total capacity of the queue 51 | int size; // current size of the queue 52 | 53 | // Percolate Up algorithm 54 | void percolateUp(int child); 55 | 56 | // Percolate Down algorithm 57 | void percolateDown(int parent); 58 | 59 | // double the current queue capacity 60 | void expandCapacity(); 61 | }; 62 | -------------------------------------------------------------------------------- /HW5_PatientQueue/LinkedListPatientQueue.cpp: -------------------------------------------------------------------------------- 1 | // LinkedListPatientQueue.cpp 2 | 3 | #include "LinkedListPatientQueue.h" 4 | 5 | LinkedListPatientQueue::LinkedListPatientQueue() { 6 | head = nullptr; 7 | } 8 | 9 | 10 | LinkedListPatientQueue::~LinkedListPatientQueue() { 11 | clear(); 12 | } 13 | 14 | 15 | void LinkedListPatientQueue::clear() { 16 | // free memory of all patient nodes 17 | PatientNode* currP = head; 18 | PatientNode* trash = nullptr; 19 | while (currP != nullptr) { 20 | trash = currP; 21 | currP = currP->next; 22 | delete trash; 23 | } 24 | 25 | // Reset head and trash 26 | head = trash = nullptr; // don't want trash to be a dangling pointer. 27 | } 28 | 29 | 30 | string LinkedListPatientQueue::frontName() { 31 | if (isEmpty()) { 32 | error("Attempting to return front name in an EMPTY queue."); 33 | } 34 | return head->name; 35 | } 36 | 37 | 38 | int LinkedListPatientQueue::frontPriority() { 39 | if (isEmpty()) { 40 | error("Attempting to return front priority in an EMPTY queue."); 41 | } 42 | return head->priority; 43 | } 44 | 45 | 46 | bool LinkedListPatientQueue::isEmpty() { 47 | return head == nullptr; 48 | } 49 | 50 | 51 | void LinkedListPatientQueue::newPatient(string name, int priority) { 52 | PatientNode* newPatient = new PatientNode(name, priority, nullptr); 53 | PatientNode* currP = head; 54 | PatientNode* preP = nullptr; 55 | 56 | // find proper place to add new patient 57 | for (currP = head; currP != nullptr; currP = currP->next) { 58 | if (newPatient->priority < currP->priority) { 59 | break; 60 | } 61 | preP = currP; 62 | } 63 | 64 | // execute the add operation 65 | newPatient->next = currP; 66 | if (preP == nullptr) { // either empty queue or non-empty queue but most urgent. 67 | head = newPatient; 68 | } else { // add in between or behind the last patient. 69 | preP->next = newPatient; 70 | } 71 | } 72 | 73 | 74 | string LinkedListPatientQueue::processPatient() { 75 | if (isEmpty()) { 76 | error("Attempting to process patient in an EMPTY queue."); 77 | } 78 | string nameToReturn = head->name; 79 | PatientNode* trash = head; 80 | head = head->next; 81 | delete trash; 82 | 83 | return nameToReturn; 84 | } 85 | 86 | 87 | void LinkedListPatientQueue::upgradePatient(string name, int newPriority) { 88 | // if empty queue, throw exception 89 | if (isEmpty()) { 90 | error("Attempting to upgade a patient in an EMPTY queue."); 91 | } 92 | 93 | // search for name, if name occurs multiple times, update the most urgent's priority 94 | PatientNode* currP = head; 95 | PatientNode* preP = nullptr; 96 | while (currP != nullptr && currP->name != name) { 97 | preP = currP; 98 | currP = currP->next; 99 | } 100 | 101 | // if name matches, update priority. 102 | if (currP->name == name) { 103 | if (currP->priority > newPriority) { // only update while the new priority is more ergent 104 | 105 | if (currP != head && newPriority < preP->priority) { // if the paitent needs to change position 106 | preP->next = currP->next; 107 | delete currP; // delete the current patient 108 | newPatient(name, newPriority); // re-insert the patient 109 | } else { // else just update the priority 110 | currP->priority = newPriority; 111 | } 112 | 113 | } else { 114 | error("Attempting to upgade a patient that is already MORE ERGENT."); 115 | } 116 | } else { 117 | // else no such a paitient name, throw exception 118 | error("Attempting to upgade a patient that DOES NOT EXIST in the queue."); 119 | } 120 | } 121 | 122 | 123 | string LinkedListPatientQueue::toString() { 124 | string output = ""; 125 | PatientNode* currP = head; 126 | 127 | while (currP != nullptr) { 128 | if (currP != head) output += ", "; 129 | output += integerToString(currP->priority) + ":" + currP->name; 130 | currP = currP->next; 131 | } 132 | return "{" + output + "}"; 133 | } 134 | -------------------------------------------------------------------------------- /HW5_PatientQueue/LinkedListPatientQueue.h: -------------------------------------------------------------------------------- 1 | // LinkedListPatientQueue.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include "patientnode.h" 8 | #include "patientqueue.h" 9 | #include "error.h" 10 | #include "strlib.h" 11 | 12 | using namespace std; 13 | 14 | class LinkedListPatientQueue : public PatientQueue { 15 | public: 16 | /* 17 | * Method: VectorPatientQueue 18 | * Usage: VectorPatientQueue PQ; 19 | * ---------------------- 20 | * Initialize a new empty patient queue 21 | */ 22 | LinkedListPatientQueue(); 23 | 24 | /* 25 | * Method: ~VectorPatientQueue 26 | * Usage: automatically execute when object out of scope 27 | * ---------------------- 28 | * Advoid memory leak when the class goes out of scope 29 | */ 30 | ~LinkedListPatientQueue(); 31 | 32 | /* 33 | * Method: frontName 34 | * Usage: cout << PQ.frontName(); 35 | * ---------------------- 36 | * Return the name of the most urgent patient 37 | */ 38 | string frontName(); 39 | 40 | /* 41 | * Method: clear 42 | * Usage: PQ.clear(); 43 | * ---------------------- 44 | * Free all memory occupied by the patient queue 45 | */ 46 | void clear(); 47 | 48 | /* 49 | * Method: frontPriority 50 | * Usage: cout << PQ.frontPriority(); 51 | * ---------------------- 52 | * Return the integer priority of the most urgent patient 53 | */ 54 | int frontPriority(); 55 | 56 | /* 57 | * Method: isEmpty 58 | * Usage: if (PQ.isEmpty())... 59 | * ---------------------- 60 | * Return true if the patient queue does not contain any paitients 61 | * , and false if it does contain at least one patient. 62 | */ 63 | bool isEmpty(); 64 | 65 | /* 66 | * Method: newPatient 67 | * Usage: PQ.newPatient(); 68 | * ---------------------- 69 | * Add a new patient into the patient queue. 70 | */ 71 | void newPatient(string name, int priority); 72 | 73 | /* 74 | * Method: processPatient 75 | * Usage: cout << PQ.processPatient(); 76 | * ---------------------- 77 | * Remove a most urgent patient from the patient queue. 78 | */ 79 | string processPatient(); 80 | 81 | /* 82 | * Method: upgradePatient 83 | * Usage: PQ.upgradePatient(); 84 | * ---------------------- 85 | * Updates a existing patient to a higher priority 86 | */ 87 | void upgradePatient(string name, int newPriority); 88 | 89 | /* 90 | * Method: toString 91 | * Usage: cout << PQ.toString(); 92 | * ---------------------- 93 | * Returns the PatientQueue as a string in front-to-back order 94 | */ 95 | string toString(); 96 | 97 | private: 98 | PatientNode* head; /* Pointer to the patient at the queue head */ 99 | }; 100 | -------------------------------------------------------------------------------- /HW5_PatientQueue/VectorPatientQueue.cpp: -------------------------------------------------------------------------------- 1 | // VectorPatientQueue.cpp 2 | 3 | #include "VectorPatientQueue.h" 4 | 5 | // constructor 6 | VectorPatientQueue::VectorPatientQueue() {} 7 | 8 | 9 | // destructor: do nothing for Vector type queue 10 | VectorPatientQueue::~VectorPatientQueue() {} 11 | 12 | 13 | void VectorPatientQueue::clear() { 14 | patients.clear(); 15 | } 16 | 17 | int VectorPatientQueue::mostUrgentIndex() { 18 | int minIndex = 0; 19 | int minPriority = patients[minIndex].priority; // minimum priority 20 | 21 | for (int i = 1; i < patients.size(); i++) { 22 | if (minPriority > patients[i].priority) { 23 | minPriority = patients[i].priority; 24 | minIndex = i; 25 | } 26 | } 27 | 28 | return minIndex; 29 | } 30 | 31 | string VectorPatientQueue::frontName() { 32 | // throw exception when accessing an empty list 33 | if (isEmpty()) { 34 | error("Attempting to find a front name from an empty queue."); 35 | } 36 | 37 | // find the first patient's name with the lowest priority. 38 | string minName = patients[mostUrgentIndex()].name; 39 | 40 | return minName; 41 | } 42 | 43 | 44 | int VectorPatientQueue::frontPriority() { 45 | // throw exception when accessing an empty list 46 | if (isEmpty()) { 47 | error("Attempting to find front priority from an empty queue."); 48 | } 49 | 50 | // find the first patient with the lowest priority. 51 | int minPriority = patients[mostUrgentIndex()].priority; 52 | 53 | return minPriority; 54 | } 55 | 56 | 57 | bool VectorPatientQueue::isEmpty() { 58 | return patients.size() == 0; 59 | } 60 | 61 | 62 | void VectorPatientQueue::newPatient(string name, int priority) { 63 | // initialize a new patient type 64 | patientT newPatient; 65 | newPatient.name = name; 66 | newPatient.priority = priority; 67 | 68 | // add into the patient list 69 | patients.add(newPatient); 70 | } 71 | 72 | 73 | string VectorPatientQueue::processPatient() { 74 | // throw exception when accessing an empty list 75 | if (isEmpty()) { 76 | error("There is no top. I must abort. I never learned how to love..."); 77 | } 78 | 79 | // remove the most urgent patient and return the patient name 80 | int minIndex = mostUrgentIndex(); 81 | string minName = patients[minIndex].name; 82 | patients.remove(minIndex); // processing patient 83 | 84 | return minName; 85 | } 86 | 87 | 88 | void VectorPatientQueue::upgradePatient(string name, int newPriority) { 89 | int minPriority = MAXIMUM_PRIORITY; // set to maximum so that it can be updated in the loop's firt iteration. 90 | int index = -1; // set initial index as -1 to indicat that patient is not found 91 | 92 | // find the patient name with the highest priority in the patient queue 93 | for (int i = 0; i < patients.size(); i++) { 94 | if (patients[i].name == name && patients[i].priority < minPriority) { 95 | minPriority = patients[i].priority; // get minimum priority of the patient 96 | index = i; // get index of the patient 97 | } 98 | } 99 | 100 | // throw exception if patient does not exsit in queue, 101 | // otherwise, update priority only when it is more urgent: 102 | if (index == -1) { 103 | error("There is no such a patient in the queue."); 104 | } else { 105 | if (patients[index].priority > newPriority) { // update priority only when new priority is more urgent 106 | patients[index].priority = newPriority; // update priority of that name! 107 | } else { 108 | error("The paitient already has a more urgent priority."); 109 | } 110 | } 111 | } 112 | 113 | 114 | string VectorPatientQueue::toString() { 115 | string output = ""; 116 | 117 | for (int i = 0; i < patients.size(); i++) { 118 | if (i != 0) output += ", "; 119 | output += integerToString(patients[i].priority) + ":" + patients[i].name; 120 | } 121 | return "{" + output + "}"; 122 | } 123 | -------------------------------------------------------------------------------- /HW5_PatientQueue/VectorPatientQueue.h: -------------------------------------------------------------------------------- 1 | // VectorPatientQueue.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include "patientqueue.h" 8 | #include "vector.h" 9 | 10 | using namespace std; 11 | 12 | const int MAXIMUM_PRIORITY = 999999999; 13 | 14 | class VectorPatientQueue : public PatientQueue { 15 | public: 16 | /* 17 | * Method: VectorPatientQueue 18 | * Usage: VectorPatientQueue PQ; 19 | * ---------------------- 20 | * Initialize a new empty patient queue 21 | */ 22 | VectorPatientQueue(); 23 | 24 | /* 25 | * Method: ~VectorPatientQueue 26 | * Usage: automatically execute when object out of scope 27 | * ---------------------- 28 | * Do nothing for vector type patient queue 29 | */ 30 | ~VectorPatientQueue(); 31 | 32 | /* 33 | * Method: clear 34 | * Usage: PQ.clear(); 35 | * ---------------------- 36 | * Remove all elements from the patient queue 37 | */ 38 | void clear(); 39 | 40 | /* 41 | * Method: isEmpty 42 | * Usage: if (PQ.isEmpty())... 43 | * ---------------------- 44 | * Return true if the patient queue does not contain any paitients 45 | * , and false if it does contain at least one patient. 46 | */ 47 | bool isEmpty(); 48 | 49 | /* 50 | * Method: newPatient 51 | * Usage: PQ.newPatient(); 52 | * ---------------------- 53 | * Puts person into the patient queue, append to a vector 54 | */ 55 | void newPatient(string name, int priority); 56 | 57 | /* 58 | * Method: frontPriority 59 | * Usage: cout << PQ.frontPriority(); 60 | * ---------------------- 61 | * Return the integer priority of the most urgent patient 62 | */ 63 | int frontPriority(); 64 | 65 | /* 66 | * Method: frontName 67 | * Usage: cout << PQ.frontName(); 68 | * ---------------------- 69 | * Return the name of the most urgent patient 70 | */ 71 | string frontName(); 72 | 73 | /* 74 | * Method: processPatient 75 | * Usage: cout << PQ.processPatient(); 76 | * ---------------------- 77 | * Scan vector list, return and remove the highest-priority patient. 78 | */ 79 | string processPatient(); 80 | 81 | 82 | /* 83 | * Method: upgradePatient 84 | * Usage: PQ.upgradePatient(); 85 | * ---------------------- 86 | * Updates a existing patient to a higher priority 87 | */ 88 | void upgradePatient(string name, int newPriority); 89 | 90 | 91 | 92 | /* 93 | * Method: toString 94 | * Usage: cout << PQ.toString(); 95 | * ---------------------- 96 | * Returns the PatientQueue as a string in front-to-back order 97 | */ 98 | string toString(); 99 | 100 | private: 101 | struct patientT { // patient type defines here. 102 | string name; 103 | int priority; 104 | }; 105 | 106 | /* 107 | * Method: mostUrgentIndex 108 | * Usage: int index = mostUrgentIndex(); 109 | * ---------------------- 110 | * Returns the most urgent patient's index 111 | */ 112 | int mostUrgentIndex(); 113 | 114 | Vector patients; // patient queue 115 | }; 116 | 117 | -------------------------------------------------------------------------------- /HW6_Huffman/encoding.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // encoding.cpp 3 | // 4 | // Created by Jian Zhong on 8/10/20. 5 | // 6 | // Copyright © 2020 Jian Zhong. All rights reserved. 7 | // 8 | 9 | #include "encoding.h" 10 | #include "HuffmanNode.h" // HuffmanNode 11 | #include "pqueue.h" // PriorityQueue PQ; 12 | #include "filelib.h" // rewindStream(input); 13 | 14 | // To build a Frequency Table 15 | Map buildFrequencyTable(istream& input) { 16 | Map freqMap; // frequency map 17 | 18 | // read each character in input file, add it into frequence map until EOF. 19 | for (int ch = input.get(); ch != -1; ch = input.get()) { 20 | if (!freqMap.containsKey(ch)) { 21 | freqMap.put(ch, 1); 22 | } else { 23 | freqMap[ch]++; 24 | } 25 | } 26 | 27 | freqMap.put(PSEUDO_EOF, 1); // add EOF character 28 | return freqMap; 29 | } 30 | 31 | // Called by buildEncodingTree, to build an encoding tree. 32 | HuffmanNode* recEncodingTree(PriorityQueue & PQ) { 33 | // Base Case: return node if only one node in PQ 34 | if (PQ.size() == 1) { 35 | return PQ.dequeue(); 36 | } 37 | 38 | // Recursive Case: repeatly dequeue two nodes as children of a new node, then re-insert. 39 | HuffmanNode *left = PQ.dequeue(), 40 | *right = PQ.dequeue(), 41 | *newSum = new HuffmanNode(NOT_A_CHAR, left->count + right->count, left, right); 42 | 43 | // Re-insert into PQ 44 | PQ.enqueue(newSum, newSum->count); 45 | 46 | return recEncodingTree(PQ); // dequeue the rest in PQ by recursion 47 | } 48 | 49 | // To build an encoding tree, recEncodingTree does the actual building work. 50 | HuffmanNode* buildEncodingTree(const Map& freqMap) { 51 | PriorityQueue PQ; // priority queue of node pointers 52 | 53 | // create PQ from freqMap 54 | for (int ch : freqMap) { 55 | HuffmanNode* letter = new HuffmanNode(ch, freqMap[ch], NULL, NULL); 56 | PQ.enqueue(letter, letter->count); 57 | } 58 | 59 | // return Huffman Tree based on PQ 60 | return recEncodingTree(PQ); 61 | } 62 | 63 | // Fecursively build encoding map 64 | Map recEncodingMap(HuffmanNode* encodingTree, Map& encodingMap, string encode) { 65 | if (encodingTree->isLeaf()) { // when meets leaf 66 | encodingMap.put(encodingTree->character, encode); // add the character into encoding map 67 | } else { 68 | recEncodingMap(encodingTree->zero, encodingMap, encode + "0"); 69 | recEncodingMap(encodingTree->one, encodingMap, encode + "1"); 70 | } 71 | 72 | return encodingMap; 73 | } 74 | 75 | // Call recEncodingMap(), which does the actual work to build encoding map. 76 | Map buildEncodingMap(HuffmanNode* encodingTree) { 77 | Map encodingMap; 78 | 79 | // Build encoding map. by traversing the Huffman Tree. 80 | return recEncodingMap(encodingTree, encodingMap, ""); 81 | } 82 | 83 | 84 | // Encode a file's text into a compressed binary representation using the encoding map. 85 | void encodeData(istream& input, const Map& encodingMap, obitstream& output) { 86 | for (int ch = input.get(); ch != -1; ch = input.get()) { // read each character in file 87 | if (encodingMap.containsKey(ch)) { 88 | string encode = encodingMap.get(ch); // find encode in encoding map 89 | for (unsigned i = 0; i < encode.size(); i++) { 90 | output.writeBit(encode[i] - '0'); // write encode to output bit by bit 91 | } 92 | } 93 | } 94 | 95 | // write delimiter binary representation. 96 | string delimiter = encodingMap.get(PSEUDO_EOF); 97 | for (unsigned i = 0; i < delimiter.size(); i++) { // take out one bit per time 98 | output.writeBit(delimiter[i] - '0'); // write to output file 99 | } 100 | } 101 | 102 | // Decode text that was previously encoded with its binary patterns 103 | void decodeData(ibitstream& input, HuffmanNode* encodingTree, ostream& output) { 104 | HuffmanNode* curr = encodingTree; 105 | int bitToRead = input.readBit(); 106 | 107 | // Read bits until the EOF 108 | while (bitToRead != -1) { 109 | if (curr != NULL) { // make sure continue to read bit if empty encoding Tree 110 | 111 | // Moving 112 | if (bitToRead == 0) 113 | curr = curr->zero; 114 | else 115 | curr = curr->one; 116 | 117 | // Character found! 118 | if (curr->isLeaf()) { 119 | if (curr->character == PSEUDO_EOF) break; // don't want to output EOF 120 | output << char(curr->character); // output character 121 | curr = encodingTree; // move back to find another 122 | } 123 | 124 | } 125 | bitToRead = input.readBit(); // Read next bit 126 | } 127 | } 128 | 129 | // To compress a given input file into a given output file. 130 | void compress(istream& input, obitstream& output) { 131 | // Build encoding of its contents 132 | Map F_Map = buildFrequencyTable(input); 133 | HuffmanNode* H_Tree = buildEncodingTree(F_Map); 134 | Map E_Map = buildEncodingMap(H_Tree); 135 | 136 | // Write encoded data to output file 137 | output << F_Map; // write header 138 | rewindStream(input); // move input stream back for encoding data(important!) 139 | encodeData(input, E_Map, output); // write compressed content 140 | } 141 | 142 | 143 | // To decompress a given input file into a given output file. 144 | void decompress(ibitstream& input, ostream& output) { 145 | // Read header(frequency map) in input file 146 | Map F_Map; 147 | input >> F_Map; 148 | 149 | // Build encoding tree and encoding map 150 | HuffmanNode* H_Tree = buildEncodingTree(F_Map); 151 | 152 | // Write decoded data to output file 153 | decodeData(input, H_Tree, output); 154 | } 155 | 156 | 157 | // Free memory occupied by encoding tree 158 | void freeTree(HuffmanNode* node) { 159 | if (node != NULL) { 160 | freeTree(node->zero); 161 | freeTree(node->one); 162 | delete node; 163 | } 164 | } 165 | 166 | 167 | /********************************* Extra ***************************************************/ 168 | 169 | /* 170 | * Recursive Version of Decoding Data(CANNOT work on file that is too large). 171 | * 172 | * Used in decodeData(); 173 | * 174 | * Usage: recDecodeData(input, encodingTree, output, encodingTree); 175 | * 176 | */ 177 | void recDecodeData(ibitstream& input, HuffmanNode* tree, ostream& output, HuffmanNode* root) { 178 | // if hitting a leaf 179 | if (tree->isLeaf()) { 180 | if (tree->character == PSEUDO_EOF) return; // stop if meets EOF 181 | output << char(tree->character); // else output the character 182 | recDecodeData(input, root, output, root); // move back to find another character 183 | } else { 184 | // traverse Huffman tree to find character 185 | int bitToRead = input.readBit(); // get one bit from file text 186 | if (bitToRead == 0) { 187 | recDecodeData(input, tree->zero, output, root); // if read 0, move left. 188 | } else if (bitToRead == 1){ 189 | recDecodeData(input, tree->one, output, root); // if read 1, move right. 190 | } else { 191 | return; // stop if detect EOF or non-binary digits in input file 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /HW7_Trailblazer/Trailblazer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Trailblazer.cpp 3 | // 4 | // Created by Jian Zhong on 8/24/20. 5 | // 6 | // Copyright © 2020 Jian Zhong. All rights reserved. 7 | // 8 | 9 | #include "Trailblazer.h" 10 | #include "queue.h" 11 | #include "priorityqueue.h" 12 | #include "stack.h" 13 | #include "hashset.h" 14 | #include "set.h" 15 | 16 | using namespace std; 17 | 18 | static const double SUFFICIENT_DIFFERENCE = 0.2; 19 | 20 | /* 21 | * Prototypes of Helper Functions 22 | */ 23 | 24 | // get Heuristic cost speicified by (crow-fly-distance / max-speed) between S and E(goal node). 25 | double Heuristic(const RoadGraph& g, RoadNode* s, RoadNode* e); 26 | // get total path cost by adding up all edges' costs in a path. 27 | double pathCostOf(const RoadGraph& graph, const Path& path); 28 | // get all edges from a path 29 | Queue edgesOf(const RoadGraph& graph, Path& path); 30 | // Return true if a path has enough different nodes from another path. 31 | bool isLegalPath(Path& pathA, Path& pathB); 32 | 33 | 34 | // BFS algorithm uses Queue pathsQueue, 35 | // and finds solution only when dequeuing a node from queue. 36 | // It also uses extended set to greatly improve efficiency. 37 | Path breadthFirstSearch(const RoadGraph& graph, RoadNode* start, RoadNode* end) { 38 | Queue paths; // queue of possible paths 39 | HashSet extended; // set for storing extended nodes 40 | paths.enqueue(Path(1, start)); // initialize a path storing the start node 41 | start->setColor(Color::YELLOW); 42 | 43 | // BFS Search Algorithm: 44 | while (!paths.isEmpty()) { 45 | Path pathToTry = paths.dequeue(); // get first path from queue 46 | RoadNode* currLoc = pathToTry[pathToTry.size() - 1]; // get the last location of the path 47 | 48 | if (extended.contains(currLoc)) continue; // if have already been extended, skip it 49 | extended.add(currLoc); // store visited location 50 | currLoc->setColor(Color::GREEN); // set Green for each visited location 51 | 52 | if (currLoc == end) return pathToTry; // Return the path if path found! 53 | 54 | for (RoadNode* neighbor : graph.neighborsOf(currLoc)) { // for each neighbor 55 | if (!extended.contains(neighbor)) { 56 | paths.enqueue(pathToTry + Path(1, neighbor)); // enqueue path + unvisited nieghbor into queue 57 | neighbor->setColor(Color::YELLOW); 58 | } 59 | } 60 | } 61 | 62 | return Path(); // If arrive here, return empty path(no solution) 63 | } 64 | 65 | 66 | // Dijkstras algorithm implementation. 67 | Path dijkstrasAlgorithm(const RoadGraph& graph, RoadNode* start, RoadNode* end) { 68 | PriorityQueue paths; 69 | HashSet extended; 70 | paths.enqueue(Path(1, start), 0); // enqueue starting node with a priority of 0 cost 71 | start->setColor(Color::YELLOW); 72 | 73 | // Implementing Dijkstras Algorithm 74 | while (!paths.isEmpty()) { 75 | double currCost = paths.peekPriority(); 76 | Path pathToTry = paths.dequeue(); 77 | RoadNode* currLoc = pathToTry[pathToTry.size() - 1]; 78 | 79 | if (extended.contains(currLoc)) continue; 80 | extended.add(currLoc); 81 | currLoc->setColor(Color::GREEN); 82 | 83 | if (currLoc == end) return pathToTry; 84 | 85 | for (RoadNode* neighbor : graph.neighborsOf(currLoc)) { 86 | if (!extended.contains(neighbor)) { 87 | double newCost = currCost + graph.edgeBetween(currLoc, neighbor)->cost(); 88 | paths.enqueue(pathToTry + Path(1, neighbor), newCost); 89 | neighbor->setColor(Color::YELLOW); 90 | } 91 | } 92 | } 93 | 94 | return Path(); // If arrive here, return empty path(no solution) 95 | } 96 | 97 | 98 | // An A* based algorithm which you can choose to implement Dijkstras, or A*, or Alternate Route algorithm. 99 | // Implement A* algorithm by default. 100 | Path aStarBasedSearch(const RoadGraph& graph, RoadNode* start, RoadNode* end, 101 | bool FindAlternate = false, RoadEdge* edgeToRemove = NULL) { 102 | PriorityQueue paths; 103 | HashSet extended; 104 | paths.enqueue(Path(1, start), Heuristic(graph, start, end)); // enqueue starting node with a priority of heuristic value 105 | start->setColor(Color::YELLOW); 106 | 107 | // Implementing Dijkstras Algorithm 108 | while (!paths.isEmpty()) { 109 | double currCost = paths.peekPriority(); // node's accumulative path cost combined with its Heuristc 110 | Path pathToTry = paths.dequeue(); 111 | RoadNode* currLoc = pathToTry[pathToTry.size() - 1]; 112 | currCost -= Heuristic(graph, currLoc, end); // node's accumulative path cost 113 | 114 | if (extended.contains(currLoc)) continue; // improve efficiency, don't want to explore longer repeated route 115 | extended.add(currLoc); 116 | currLoc->setColor(Color::GREEN); 117 | 118 | if (currLoc == end) return pathToTry; // Return the path if found! 119 | 120 | for (RoadNode* neighbor : graph.neighborsOf(currLoc)) { 121 | if (!extended.contains(neighbor)) { 122 | 123 | // Specified codes for finding alternative path 124 | if (FindAlternate) { 125 | if (edgeToRemove->from() == currLoc && edgeToRemove->to() == neighbor) continue; 126 | } 127 | 128 | double newCost = currCost + graph.edgeBetween(currLoc, neighbor)->cost(); // new path cost from start to neighbor. 129 | double combinedHeuristic = newCost + Heuristic(graph, neighbor, end); // estimated cost from start node to end 130 | paths.enqueue(pathToTry + Path(1, neighbor), combinedHeuristic); 131 | neighbor->setColor(Color::YELLOW); 132 | } 133 | } 134 | } 135 | 136 | return Path(); // If arrive here, return empty path(no solution) 137 | } 138 | 139 | 140 | // A* algorithm implementation. 141 | Path aStar(const RoadGraph& graph, RoadNode* start, RoadNode* end) { 142 | return aStarBasedSearch(graph, start, end); // If arrive here, return empty path(no solution) 143 | } 144 | 145 | 146 | // Alternative Route algorithm implementation: find a path that is at least 20% different than another path. 147 | Path alternativeRoute(const RoadGraph& graph, RoadNode* start, RoadNode* end) { 148 | Path bestPath = aStar(graph, start, end); // get the best path 149 | Queue allEdges = edgesOf(graph, bestPath); // get edges from best path 150 | PriorityQueue alternativePathsQueue; // alternative candidates with cost as its priority 151 | 152 | // for each edge, find the shortest path doesn't use that edge and add it into PQ: 153 | while (!allEdges.isEmpty()) { 154 | RoadEdge* edgeToSkip = allEdges.dequeue(); 155 | Path alternativePath = aStarBasedSearch(graph, start, end, true, edgeToSkip); 156 | if (isLegalPath(alternativePath, bestPath)) { 157 | alternativePathsQueue.add(alternativePath, pathCostOf(graph, alternativePath) ); 158 | } 159 | } 160 | 161 | // If empty queue, then no solutions, else return alternative path. 162 | if (alternativePathsQueue.isEmpty()) { 163 | return Path(); 164 | } 165 | else { 166 | return alternativePathsQueue.dequeue(); 167 | } 168 | } 169 | 170 | 171 | /*** Definitions of Helper Functions ***/ 172 | 173 | // get Heuristic cost from s node to e node in a graph. 174 | double Heuristic(const RoadGraph& g, RoadNode* s, RoadNode* e) { 175 | return g.crowFlyDistanceBetween(s, e) / g.maxRoadSpeed(); // the fastest time from s node to e node 176 | } 177 | 178 | // get total path cost by adding up all edges' costs in a path. 179 | double pathCostOf(const RoadGraph& graph, const Path& path) { 180 | double cost = 0; 181 | for(int i = 0; i < path.size() - 1; i++) { 182 | cost += graph.edgeBetween(path[i], path[i+1])->cost(); 183 | } 184 | return cost; 185 | } 186 | 187 | // get all edges from a path 188 | Queue edgesOf(const RoadGraph& graph, Path& path) { 189 | Queue edges; 190 | for (int i = 0; i < path.size() - 1; i++) { 191 | edges.enqueue(graph.edgeBetween(path[i], path[i+1]) ); 192 | } 193 | return edges; 194 | } 195 | 196 | // Return true if a path that is at least 20% different than another path. 197 | bool isLegalPath(Path& pathA, Path& pathB) { 198 | HashSet nodesA; // nodes in path A 199 | HashSet nodesB; // nodes in path B 200 | 201 | for (RoadNode* node : pathA) { 202 | nodesA.add(node); 203 | } 204 | for (RoadNode* node : pathB) { 205 | nodesB.add(node); 206 | } 207 | 208 | nodesA -= nodesB; // nodes in path A but not in path B 209 | double diff = ( (double)nodesA.size() / nodesB.size() ); 210 | return diff > SUFFICIENT_DIFFERENCE; // true if diff > 0.2 211 | } 212 | 213 | 214 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📕 CS106B-HWs (CPPs only) 2 | ### My Solutions to CS106B Homework: Programming Abstractions 2018 3 | ✅ All DONE. 4 | 5 | [Course Website](https://web.stanford.edu/class/archive/cs/cs106b/cs106b.1192/) 6 | --------------------------------------------------------------------------------