├── Assignment_1_1 ├── CMakeLists.txt ├── README.md └── assignment_1_1.cpp ├── Assignment_1_2 ├── CMakeLists.txt ├── README.md └── assignment_1_2.cpp ├── Assignment_1_3 ├── CMakeLists.txt ├── README.md └── assignment_1_3.cpp ├── Assignment_2_1 ├── CMakeLists.txt ├── README.md └── assignment_2_1.cpp ├── Assignment_2_2 ├── CMakeLists.txt ├── README.md └── assignment_2_2.cpp ├── Assignment_2_3 ├── CMakeLists.txt ├── README.md └── assignment_2_3.cpp ├── Assignment_3_1 ├── CMakeLists.txt ├── README.md └── assignment_3_1.cpp ├── Assignment_3_2 ├── CMakeLists.txt ├── README.md └── assignment_3_2.cpp ├── Assignment_3_3 ├── CMakeLists.txt ├── README.md └── assignment_3_3.cpp ├── Gomoku ├── CMakeLists.txt ├── README.md └── gomoku.cpp ├── LICENSE └── README.md /Assignment_1_1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_1_1) 2 | add_executable(${PROJECT_NAME} assignment_1_1.cpp) -------------------------------------------------------------------------------- /Assignment_1_1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1-1 2 | 3 | ## 问题描述 4 | 5 | 股票价格的下一次上涨 6 | 7 | 给定一个整数数组 `prices` ,表示连续几天的股票价格。返回一个数组 `answer` ,其中 `answer[i]` 是指对于第 `i` 天,股价下一次上涨是在几天后。如果在这之后股价都不会上涨,请在该位置用 `0` 来代替。 8 | 9 | 提示: 10 | 11 | * `1 <= prices.length <= 10^5` 12 | * `30 <= prices[i] <= 100` 13 | 14 | ## 测试用例 15 | 16 | * Test Case 1: 17 | 18 | Description: Test the correctness of the algorithm 19 | 20 | Input: 73 74 75 71 69 72 76 73 '\n' 21 | 22 | Expected Output: [1,1,4,2,1,1,0,0] 23 | 24 | * Test Case 2: 25 | 26 | Description: Test the correctness of the algorithm 27 | 28 | Input: 30 40 50 60 '\n' 29 | 30 | Expected Output: [1,1,1,0] 31 | 32 | * Test Case 3: 33 | 34 | Description: Test the correctness of the algorithm 35 | 36 | Input: 100 '\n' 37 | 38 | Expected Output: [0] 39 | 40 | * Test Case 4: 41 | 42 | Description: Test the handling of input data exceeding the upper limit 43 | 44 | Input: 101 '\n' 45 | 46 | Expected Output: Error 47 | 48 | * Test Case 5: 49 | 50 | Description: Test the handling of input data exceeding the upper limit 51 | 52 | Input: 50 60 120 50 80 '\n' 53 | 54 | Expected Output: Error 55 | 56 | * Test Case 6: 57 | 58 | Description: Test the handling of input data below the lower limit 59 | 60 | Input: 29 '\n' 61 | 62 | Expected Output: Error 63 | 64 | * Test Case 7: 65 | 66 | Description: Test the handling of input data below the lower limit 67 | 68 | Input: 95 45 30 20 98 20 '\n' 69 | 70 | Expected Output: Error 71 | 72 | * Test Case 8: 73 | 74 | Description: Test the handling of input data with unreasonable length 75 | 76 | Input: '\n' 77 | 78 | Expected Output: Error 79 | 80 | * Test Case 9: 81 | 82 | Description: Test the handling of incorrect input data types 83 | 84 | Input: a bcd '\n' 85 | 86 | Expected Output: Error 87 | 88 | * Test Case 10: 89 | 90 | Description: Test the handling of incorrect input data types 91 | 92 | Input: 50.5 '\n' 93 | 94 | Expected Output: Error 95 | 96 | ## 项目构建命令 97 | 98 | ``` 99 | mkdir build 100 | cd build 101 | cmake .. 102 | ``` 103 | 104 | ## 编译运行环境 105 | 106 | * 本项目适用于x86和x64架构 107 | 108 | ## 文档更新日期 109 | 110 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_1_1/assignment_1_1.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_1_1 3 | * File Name: assignment_1_1.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/10/11 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using std::cout; 16 | using std::cin; 17 | using std::cerr; 18 | using std::endl; 19 | 20 | /* 21 | * Function Name: operator<< 22 | * Function: Overload operator << 23 | * Input Parameters: std::ostream& out 24 | * const std::vector& vectorVariable 25 | * Return Value: out 26 | */ 27 | template 28 | std::ostream& operator<<(std::ostream& out, const std::vector& vectorVariable) 29 | { 30 | /* Check if the vector is empty */ 31 | if (vectorVariable.empty()) { 32 | cerr << "Vector is empty." << endl; 33 | } 34 | else { 35 | out << "[" << vectorVariable[0]; 36 | for (unsigned int count = 1; count < vectorVariable.size(); count++) 37 | out << "," << vectorVariable[count]; 38 | out << "]"; 39 | } 40 | return out; 41 | } 42 | 43 | /* Define Solution class */ 44 | template 45 | class Solution { 46 | private: 47 | std::vector vec; 48 | public: 49 | /* 50 | * Function Name: getVec 51 | * Function: Get private vector variable 52 | * Input Parameters: void 53 | * Return Value: private vector variable 54 | */ 55 | const std::vector& getVec(void) const 56 | { 57 | return vec; 58 | } 59 | 60 | /* 61 | * Function Name: input 62 | * Function: Input data 63 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 64 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 65 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 66 | * Return Value: true: input data is valid 67 | * false: input data is invalid 68 | */ 69 | bool input(Type lowerLimit, Type upperLimit, unsigned int maxLength) 70 | { 71 | /* Read a line from standard input (cin) */ 72 | std::string str; 73 | std::getline(cin, str); 74 | 75 | /* Create a string stream to parse the input */ 76 | std::istringstream iss(str); 77 | Type num; // Variable to hold the parsed number 78 | 79 | /* Loop to read and process each number from the input */ 80 | while (true) { 81 | /* Read a number from the input stream */ 82 | iss >> num; 83 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 84 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 85 | iss.clear(); 86 | iss.ignore(std::numeric_limits::max(), '\n'); 87 | vec.clear(); 88 | return false; // Return false to indicate invalid input 89 | } 90 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 91 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 92 | iss.clear(); 93 | iss.ignore(std::numeric_limits::max(), '\n'); 94 | vec.clear(); 95 | return false; // Return false to indicate invalid input 96 | } 97 | else { // Valid input 98 | vec.push_back(num); 99 | } 100 | 101 | /* Check for end of input or maximum input length */ 102 | if (iss.eof() || vec.size() >= maxLength) { 103 | break; 104 | } 105 | } 106 | 107 | /* Check the length of vector */ 108 | if (vec.size() <= 0) { 109 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 110 | iss.clear(); 111 | iss.ignore(std::numeric_limits::max(), '\n'); 112 | vec.clear(); 113 | return false; // Return false to indicate invalid input 114 | } 115 | 116 | /* Return true to indicate valid input */ 117 | return true; 118 | } 119 | 120 | /* 121 | * Function Name: nextIncrease 122 | * Function: Calculate next increase 123 | * Input Parameters: const std::vector& prices 124 | * Return Value: increase vector 125 | */ 126 | std::vector nextIncrease(const std::vector& prices) 127 | { 128 | std::vector increaseVector(prices.size(), 0); // The increase vector with zeros 129 | std::stack indices; // Stack to store indices 130 | for (unsigned int count = 0; count < prices.size(); count++) { 131 | /* If the stack is not empty and the current price is greater than the price at the top of the stack */ 132 | while (!indices.empty() && prices[count] > prices[indices.top()]) { 133 | increaseVector[indices.top()] = count - indices.top(); 134 | indices.pop(); 135 | } 136 | 137 | /* Push the current index onto the stack */ 138 | indices.push(count); 139 | } 140 | return increaseVector; 141 | } 142 | 143 | /* 144 | * Function Name: nextIncrease 145 | * Function: Calculate next increase 146 | * Input Parameters: const Type* prices 147 | * int length 148 | * Return Value: increase vector 149 | */ 150 | std::vector nextIncrease(const Type* prices, int length) 151 | { 152 | std::vector increaseVector(length, 0); // The increase vector with zeros 153 | std::stack indices; // Stack to store indices 154 | for (int count = 0; count < length; count++) { 155 | /* If the stack is not empty and the current price is greater than the price at the top of the stack */ 156 | while (!indices.empty() && prices[count] > prices[indices.top()]) { 157 | increaseVector[indices.top()] = count - indices.top(); 158 | indices.pop(); 159 | } 160 | 161 | /* Push the current index onto the stack */ 162 | indices.push(count); 163 | } 164 | return increaseVector; 165 | } 166 | }; 167 | 168 | /* 169 | * Function Name: main 170 | * Function: Main function 171 | * Return Value: 0 172 | */ 173 | int main() 174 | { 175 | /* Define solution object */ 176 | Solution solution; 177 | 178 | /* Input data */ 179 | cout << "Input:" << endl; 180 | cout << "(1 <= prices.length <= 10^5, if exceeded, it will be truncated.)" << endl; 181 | cout << "(30 <= prices[i] <= 100, please separate the data with spaces and press Enter.)" << endl; 182 | while (!solution.input(30, 100, 100000U)) { 183 | continue; 184 | } 185 | 186 | /* Output result */ 187 | cout << "Output:" << endl; 188 | cout << "Increase Vector: " << solution.nextIncrease(solution.getVec()) << endl; 189 | 190 | // Notes: 191 | // To enhance the versatility and extensibility of the code, you can also call the 192 | // std::vector Solution::nextIncrease(const T* prices, int length) function to 193 | // handle cases where the parameter is an array, the input parameters are the array's 194 | // starting address and its length. 195 | 196 | /* Program ends */ 197 | return 0; 198 | } -------------------------------------------------------------------------------- /Assignment_1_2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_1_2) 2 | add_executable(${PROJECT_NAME} assignment_1_2.cpp) -------------------------------------------------------------------------------- /Assignment_1_2/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1-2 2 | 3 | ## 问题描述 4 | 5 | 买卖股票的最佳时机 6 | 7 | 给定一个数组 `prices` ,它的第 `i` 个元素 `prices[i]` 表示一支给定股票第 `i` 天的价格。你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 `0` 。 8 | 9 | 提示: 10 | 11 | * `1 <= prices.length <= 10^5` 12 | * `0 <= prices[i] <= 10^4` 13 | 14 | ## 测试用例 15 | 16 | * Test Case 1: 17 | 18 | Description: Test the correctness of the algorithm 19 | 20 | Input: 7 1 5 3 6 4 '\n' 21 | 22 | Expected Output: 5 23 | 24 | * Test Case 2: 25 | 26 | Description: Test the correctness of the algorithm 27 | 28 | Input: 7 6 4 3 1 '\n' 29 | 30 | Expected Output: 0 31 | 32 | * Test Case 3: 33 | 34 | Description: Test the correctness of the algorithm 35 | 36 | Input: 0 '\n' 37 | 38 | Expected Output: 0 39 | 40 | * Test Case 4: 41 | 42 | Description: Test the handling of input data exceeding the upper limit 43 | 44 | Input: 10001 '\n' 45 | 46 | Expected Output: Error 47 | 48 | * Test Case 5: 49 | 50 | Description: Test the handling of input data exceeding the upper limit 51 | 52 | Input: 50 60 12000 50 80 '\n' 53 | 54 | Expected Output: Error 55 | 56 | * Test Case 6: 57 | 58 | Description: Test the handling of input data below the lower limit 59 | 60 | Input: -1 '\n' 61 | 62 | Expected Output: Error 63 | 64 | * Test Case 7: 65 | 66 | Description: Test the handling of input data below the lower limit 67 | 68 | Input: 95 45 30 -20 98 20 '\n' 69 | 70 | Expected Output: Error 71 | 72 | * Test Case 8: 73 | 74 | Description: Test the handling of input data with unreasonable length 75 | 76 | Input: '\n' 77 | 78 | Expected Output: Error 79 | 80 | * Test Case 9: 81 | 82 | Description: Test the handling of incorrect input data types 83 | 84 | Input: a bcd '\n' 85 | 86 | Expected Output: Error 87 | 88 | * Test Case 10: 89 | 90 | Description: Test the handling of incorrect input data types 91 | 92 | Input: 50.5 '\n' 93 | 94 | Expected Output: Error 95 | 96 | ## 项目构建命令 97 | 98 | ``` 99 | mkdir build 100 | cd build 101 | cmake .. 102 | ``` 103 | 104 | ## 编译运行环境 105 | 106 | * 本项目适用于x86和x64架构 107 | 108 | ## 文档更新日期 109 | 110 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_1_2/assignment_1_2.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_1_2 3 | * File Name: assignment_1_2.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/9/27 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cout; 15 | using std::cin; 16 | using std::cerr; 17 | using std::endl; 18 | 19 | /* Define Solution class */ 20 | template 21 | class Solution { 22 | private: 23 | std::vector vec; 24 | public: 25 | /* 26 | * Function Name: getVec 27 | * Function: Get private vector variable 28 | * Input Parameters: void 29 | * Return Value: private vector variable 30 | */ 31 | const std::vector& getVec(void) const 32 | { 33 | return vec; 34 | } 35 | 36 | /* 37 | * Function Name: input 38 | * Function: Input data 39 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 40 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 41 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 42 | * Return Value: true: input data is valid 43 | * false: input data is invalid 44 | */ 45 | bool input(Type lowerLimit, Type upperLimit, unsigned int maxLength) 46 | { 47 | /* Read a line from standard input (cin) */ 48 | std::string str; 49 | std::getline(cin, str); 50 | 51 | /* Create a string stream to parse the input */ 52 | std::istringstream iss(str); 53 | Type num; // Variable to hold the parsed number 54 | 55 | /* Loop to read and process each number from the input */ 56 | while (true) { 57 | /* Read a number from the input stream */ 58 | iss >> num; 59 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 60 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 61 | iss.clear(); 62 | iss.ignore(std::numeric_limits::max(), '\n'); 63 | vec.clear(); 64 | return false; // Return false to indicate invalid input 65 | } 66 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 67 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 68 | iss.clear(); 69 | iss.ignore(std::numeric_limits::max(), '\n'); 70 | vec.clear(); 71 | return false; // Return false to indicate invalid input 72 | } 73 | else { // Valid input 74 | vec.push_back(num); 75 | } 76 | 77 | /* Check for end of input or maximum input length */ 78 | if (iss.eof() || vec.size() >= maxLength) { 79 | break; 80 | } 81 | } 82 | 83 | /* Check the length of vector */ 84 | if (vec.size() <= 0) { 85 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 86 | iss.clear(); 87 | iss.ignore(std::numeric_limits::max(), '\n'); 88 | vec.clear(); 89 | return false; // Return false to indicate invalid input 90 | } 91 | 92 | /* Return true to indicate valid input */ 93 | return true; 94 | } 95 | 96 | /* 97 | * Function Name: maxProfit 98 | * Function: Calculate maximum profit 99 | * Input Parameters: const std::vector& prices 100 | * Return Value: maximum profit 101 | */ 102 | Type maxProfit(const std::vector& prices) 103 | { 104 | if (prices.empty()) { // Check if the prices is empty 105 | return 0; 106 | } 107 | Type maxProfit = 0; 108 | Type minPrice = prices[0]; // Initialize the minimum price to the first element 109 | for (unsigned int count = 1; count < prices.size(); count++) { 110 | /* Update the minimum price if we find a smaller value */ 111 | minPrice = std::min(minPrice, prices[count]); 112 | 113 | /* Update the maximum profit if we find a higher profit */ 114 | maxProfit = std::max(maxProfit, prices[count] - minPrice); 115 | } 116 | return maxProfit; // Return the maximum profit 117 | } 118 | 119 | /* 120 | * Function Name: maxProfit 121 | * Function: Calculate maximum profit 122 | * Input Parameters: const Type* prices 123 | * int length 124 | * Return Value: maximum profit 125 | */ 126 | Type maxProfit(const Type* prices, int length) 127 | { 128 | if (length <= 0) { // Check if the prices is empty 129 | return 0; 130 | } 131 | Type maxProfit = 0; 132 | Type minPrice = prices[0]; // Initialize the minimum price to the first element 133 | for (int count = 1; count < length; count++) { 134 | /* Update the minimum price if we find a smaller value */ 135 | minPrice = std::min(minPrice, prices[count]); 136 | 137 | /* Update the maximum profit if we find a higher profit */ 138 | maxProfit = std::max(maxProfit, prices[count] - minPrice); 139 | } 140 | return maxProfit; // Return the maximum profit 141 | } 142 | }; 143 | 144 | /* 145 | * Function Name: main 146 | * Function: Main function 147 | * Return Value: 0 148 | */ 149 | int main() 150 | { 151 | /* Define solution object */ 152 | Solution solution; 153 | 154 | /* Input data */ 155 | cout << "Input:" << endl; 156 | cout << "(1 <= prices.length <= 10^5, if exceeded, it will be truncated.)" << endl; 157 | cout << "(0 <= prices[i] <= 10^4, please separate the data with spaces and press Enter.)" << endl; 158 | while (!solution.input(0, 10000, 100000U)) { 159 | continue; 160 | } 161 | 162 | /* Output result */ 163 | cout << "Output:" << endl; 164 | cout << "Maximum Profit: " << solution.maxProfit(solution.getVec()) << endl; 165 | 166 | // Notes: 167 | // To enhance the versatility and extensibility of the code, you can also call the 168 | // T Solution::maxProfit(const T* prices, int length) function to handle cases 169 | // where the parameter is an array, the input parameters are the array's starting 170 | // address and its length. 171 | 172 | /* Program ends */ 173 | return 0; 174 | } -------------------------------------------------------------------------------- /Assignment_1_3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_1_3) 2 | add_executable(${PROJECT_NAME} assignment_1_3.cpp) -------------------------------------------------------------------------------- /Assignment_1_3/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1-3 2 | 3 | ## 问题描述 4 | 5 | 买卖股票的最佳时机含冷冻期 6 | 7 | 给定一个整数数组 `prices` ,其中第 `prices[i]` 表示第 `i` 天的股票价格。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票)。卖出股票后,你无法在第二天买入股票(即冷冻期为1天)。 8 | 9 | 注意: 10 | 11 | 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 12 | 13 | 提示: 14 | 15 | * `1 <= prices.length <= 5000` 16 | * `0 <= prices[i] <= 1000` 17 | 18 | ## 测试用例 19 | 20 | * Test Case 1: 21 | 22 | Description: Test the correctness of the algorithm 23 | 24 | Input: 1 2 3 0 2 '\n' 25 | 26 | Expected Output: 3 27 | 28 | * Test Case 2: 29 | 30 | Description: Test the correctness of the algorithm 31 | 32 | Input: 3 3 '\n' 33 | 34 | Expected Output: 0 35 | 36 | * Test Case 3: 37 | 38 | Description: Test the correctness of the algorithm 39 | 40 | Input: 1 '\n' 41 | 42 | Expected Output: 0 43 | 44 | * Test Case 4: 45 | 46 | Description: Test the handling of input data exceeding the upper limit 47 | 48 | Input: 1001 '\n' 49 | 50 | Expected Output: Error 51 | 52 | * Test Case 5: 53 | 54 | Description: Test the handling of input data exceeding the upper limit 55 | 56 | Input: 50 60 1200 50 80 '\n' 57 | 58 | Expected Output: Error 59 | 60 | * Test Case 6: 61 | 62 | Description: Test the handling of input data below the lower limit 63 | 64 | Input: -1 '\n' 65 | 66 | Expected Output: Error 67 | 68 | * Test Case 7: 69 | 70 | Description: Test the handling of input data below the lower limit 71 | 72 | Input: 95 45 30 -20 98 20 '\n' 73 | 74 | Expected Output: Error 75 | 76 | * Test Case 8: 77 | 78 | Description: Test the handling of input data with unreasonable length 79 | 80 | Input: '\n' 81 | 82 | Expected Output: Error 83 | 84 | * Test Case 9: 85 | 86 | Description: Test the handling of incorrect input data types 87 | 88 | Input: a bcd '\n' 89 | 90 | Expected Output: Error 91 | 92 | * Test Case 10: 93 | 94 | Description: Test the handling of incorrect input data types 95 | 96 | Input: 50.5 '\n' 97 | 98 | Expected Output: Error 99 | 100 | ## 项目构建命令 101 | 102 | ``` 103 | mkdir build 104 | cd build 105 | cmake .. 106 | ``` 107 | 108 | ## 编译运行环境 109 | 110 | * 本项目适用于x86和x64架构 111 | 112 | ## 文档更新日期 113 | 114 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_1_3/assignment_1_3.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_1_3 3 | * File Name: assignment_1_3.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/9/27 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cout; 15 | using std::cin; 16 | using std::cerr; 17 | using std::endl; 18 | 19 | /* Define Solution class */ 20 | template 21 | class Solution { 22 | private: 23 | std::vector vec; 24 | Type m_upperLimit = 0; 25 | public: 26 | /* 27 | * Function Name: getVec 28 | * Function: Get private vector variable 29 | * Input Parameters: void 30 | * Return Value: private vector variable 31 | */ 32 | const std::vector& getVec(void) const 33 | { 34 | return vec; 35 | } 36 | 37 | /* 38 | * Function Name: input 39 | * Function: Input data 40 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 41 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 42 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 43 | * Return Value: true: input data is valid 44 | * false: input data is invalid 45 | */ 46 | bool input(Type lowerLimit, Type upperLimit, unsigned int maxLength) 47 | { 48 | /* Read a line from standard input (cin) */ 49 | std::string str; 50 | std::getline(cin, str); 51 | 52 | /* Create a string stream to parse the input */ 53 | std::istringstream iss(str); 54 | Type num; // Variable to hold the parsed number 55 | 56 | /* Loop to read and process each number from the input */ 57 | while (true) { 58 | /* Read a number from the input stream */ 59 | iss >> num; 60 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 61 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 62 | iss.clear(); 63 | iss.ignore(std::numeric_limits::max(), '\n'); 64 | vec.clear(); 65 | return false; // Return false to indicate invalid input 66 | } 67 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 68 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 69 | iss.clear(); 70 | iss.ignore(std::numeric_limits::max(), '\n'); 71 | vec.clear(); 72 | return false; // Return false to indicate invalid input 73 | } 74 | else { // Valid input 75 | vec.push_back(num); 76 | } 77 | 78 | /* Check for end of input or maximum input length */ 79 | if (iss.eof() || vec.size() >= maxLength) { 80 | break; 81 | } 82 | } 83 | 84 | /* Check the length of vector */ 85 | if (vec.size() <= 0) { 86 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 87 | iss.clear(); 88 | iss.ignore(std::numeric_limits::max(), '\n'); 89 | vec.clear(); 90 | return false; // Return false to indicate invalid input 91 | } 92 | 93 | /* Return true to indicate valid input */ 94 | m_upperLimit = upperLimit; 95 | return true; 96 | } 97 | 98 | /* 99 | * Function Name: maxProfit 100 | * Function: Calculate maximum profit 101 | * Input Parameters: const std::vector& prices 102 | * Return Value: maximum profit 103 | */ 104 | Type maxProfit(const std::vector& prices) 105 | { 106 | /* Check if the prices is empty */ 107 | if (prices.empty()) { 108 | return 0; 109 | } 110 | 111 | /* Initialize status variables for different states */ 112 | Type statusNotHolding = 0, statusBuy = -prices[0], statusHold = -m_upperLimit - 1, statusSell = -m_upperLimit - 1, statusFreeze = -m_upperLimit - 1; 113 | Type tmp; 114 | 115 | /* Iterate through the prices vector and calculate maximum profit based on different states */ 116 | for (unsigned int count = 1; count < prices.size(); count++) { 117 | tmp = statusFreeze; 118 | statusFreeze = statusSell; 119 | statusSell = std::max(statusBuy, statusHold) + prices[count]; 120 | statusHold = std::max(statusBuy, statusHold); 121 | statusBuy = std::max(tmp, statusNotHolding) - prices[count]; 122 | statusNotHolding = std::max(tmp, statusNotHolding); 123 | } 124 | 125 | /* Return the maximum profit */ 126 | return std::max(statusNotHolding, std::max(statusSell, statusFreeze)); 127 | } 128 | 129 | /* 130 | * Function Name: maxProfit 131 | * Function: Calculate maximum profit 132 | * Input Parameters: const Type* prices 133 | * int length 134 | * Return Value: maximum profit 135 | */ 136 | Type maxProfit(const Type* prices, int length) 137 | { 138 | /* Check if the prices is empty */ 139 | if (length <= 0) { 140 | return 0; 141 | } 142 | 143 | /* Initialize status variables for different states */ 144 | Type statusNotHolding = 0, statusBuy = -prices[0], statusHold = -m_upperLimit - 1, statusSell = -m_upperLimit - 1, statusFreeze = -m_upperLimit - 1; 145 | Type tmp; 146 | 147 | /* Iterate through the prices vector and calculate maximum profit based on different states */ 148 | for (int count = 1; count < length; count++) { 149 | tmp = statusFreeze; 150 | statusFreeze = statusSell; 151 | statusSell = std::max(statusBuy, statusHold) + prices[count]; 152 | statusHold = std::max(statusBuy, statusHold); 153 | statusBuy = std::max(tmp, statusNotHolding) - prices[count]; 154 | statusNotHolding = std::max(tmp, statusNotHolding); 155 | } 156 | 157 | /* Return the maximum profit */ 158 | return std::max(statusNotHolding, std::max(statusSell, statusFreeze)); 159 | } 160 | }; 161 | 162 | /* 163 | * Function Name: main 164 | * Function: Main function 165 | * Return Value: 0 166 | */ 167 | int main() 168 | { 169 | /* Define solution object */ 170 | Solution solution; 171 | 172 | /* Input data */ 173 | cout << "Input:" << endl; 174 | cout << "(1 <= prices.length <= 5000, if exceeded, it will be truncated.)" << endl; 175 | cout << "(0 <= prices[i] <= 1000, please separate the data with spaces and press Enter.)" << endl; 176 | while (!solution.input(0, 1000, 5000U)) { 177 | continue; 178 | } 179 | 180 | /* Output result */ 181 | cout << "Output:" << endl; 182 | cout << "Maximum Profit: " << solution.maxProfit(solution.getVec()) << endl; 183 | 184 | // Notes: 185 | // To enhance the versatility and extensibility of the code, you can also call the 186 | // T Solution::maxProfit(const T* prices, int length) function to handle cases 187 | // where the parameter is an array, the input parameters are the array's starting 188 | // address and its length. 189 | 190 | /* Program ends */ 191 | return 0; 192 | } -------------------------------------------------------------------------------- /Assignment_2_1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_2_1) 2 | add_executable(${PROJECT_NAME} assignment_2_1.cpp) -------------------------------------------------------------------------------- /Assignment_2_1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2-1 2 | 3 | ## 问题描述 4 | 5 | 电影播放时间 6 | 7 | 某影院为观众提供了一系列电影的播放时间安排 `intervals` ,每部电影的播放时间由其开始和结束的时间标记,即 `intervals[i] = [starti, endi]` 。请你判断一个人是否能够看完这里面的全部电影,不错过任何一部。 8 | 9 | 提示: 10 | 11 | * `0 <= intervals.length <= 10^4` 12 | * `intervals[i].length == 2` 13 | * `0 <= starti < endi <= 10^6` 14 | 15 | ## 测试用例 16 | 17 | * Test Case 1: 18 | 19 | Description: Test the correctness of the algorithm 20 | 21 | Input: 0 30 5 10 15 20 '\n' 22 | 23 | Expected Output: false 24 | 25 | * Test Case 2: 26 | 27 | Description: Test the correctness of the algorithm 28 | 29 | Input: 7 10 2 4 '\n' 30 | 31 | Expected Output: true 32 | 33 | * Test Case 3: 34 | 35 | Description: Test the correctness of the algorithm 36 | 37 | Input: 1 3 2 6 8 10 15 18 '\n' 38 | 39 | Expected Output: false 40 | 41 | * Test Case 4: 42 | 43 | Description: Test the correctness of the algorithm 44 | 45 | Input: 1 4 4 5 '\n' 46 | 47 | Expected Output: true 48 | 49 | * Test Case 5: 50 | 51 | Description: Test the handling of input data exceeding the upper limit 52 | 53 | Input: 0 10 1000000 1000001 '\n' 54 | 55 | Expected Output: Error 56 | 57 | * Test Case 6: 58 | 59 | Description: Test the handling of input data below the lower limit 60 | 61 | Input: 22 24 -1 36 '\n' 62 | 63 | Expected Output: Error 64 | 65 | * Test Case 7: 66 | 67 | Description: Test the handling of input data with unreasonable time 68 | 69 | Input: 100 200 300 200 '\n' 70 | 71 | Expected Output: Error 72 | 73 | * Test Case 8: 74 | 75 | Description: Test the handling of input data with unreasonable length 76 | 77 | Input: 10 '\n' 78 | 79 | Expected Output: Error 80 | 81 | * Test Case 9: 82 | 83 | Description: Test the handling of input data with unreasonable length 84 | 85 | Input: 0 10 12 22 24 '\n' 86 | 87 | Expected Output: true 88 | 89 | * Test Case 10: 90 | 91 | Description: Test the handling of incorrect input data types 92 | 93 | Input: 5.5 10 a bcd '\n' 94 | 95 | Expected Output: Error 96 | 97 | ## 项目构建命令 98 | 99 | ``` 100 | mkdir build 101 | cd build 102 | cmake .. 103 | ``` 104 | 105 | ## 编译运行环境 106 | 107 | * 本项目适用于x86和x64架构 108 | 109 | ## 文档更新日期 110 | 111 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_2_1/assignment_2_1.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_2_1 3 | * File Name: assignment_2_1.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/9/27 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cout; 15 | using std::cin; 16 | using std::cerr; 17 | using std::endl; 18 | 19 | /* Define Interval structure */ 20 | typedef struct { 21 | int startTime; 22 | int endTime; 23 | } Interval; 24 | 25 | /* Define Solution class */ 26 | template 27 | class Solution { 28 | private: 29 | /* Define private data member */ 30 | std::vector vec; 31 | 32 | /* 33 | * Function Name: quickSort 34 | * Function: Quick sort algorithm 35 | * Input Parameters: int left 36 | * int right 37 | * Return Value: void 38 | */ 39 | void quickSort(int left, int right) 40 | { 41 | /* If the left index is greater than or equal to the right index, return */ 42 | if (left >= right) { 43 | return; 44 | } 45 | 46 | /* Choose the pivot element as the middle element's end time in the vector */ 47 | int pivot = vec[(left + right) / 2].endTime; 48 | int i = left; 49 | int j = right; 50 | 51 | /* Rearrange elements around the pivot */ 52 | while (i <= j) { 53 | while (vec[i].endTime < pivot) { 54 | i++; // Find an element on the left side that is greater than or equal to the pivot 55 | } 56 | while (vec[j].endTime > pivot) { 57 | j--; // Find an element on the right side that is less than or equal to the pivot 58 | } 59 | if (i <= j) { 60 | std::swap(vec[i], vec[j]); // Swap elements if needed to ensure elements on the left are less than or equal to the pivot 61 | i++; 62 | j--; 63 | } 64 | } 65 | 66 | /* Recur for the subarrays on the left and right of the pivot */ 67 | quickSort(left, j); 68 | quickSort(i, right); 69 | } 70 | 71 | /* 72 | * Function Name: quickSort 73 | * Function: Quick sort algorithm 74 | * Input Parameters: void 75 | * Return Value: void 76 | */ 77 | void quickSort(void) 78 | { 79 | quickSort(0, static_cast(vec.size() - 1)); 80 | } 81 | 82 | public: 83 | /* 84 | * Function Name: input 85 | * Function: Input data 86 | * Input Parameters: int lowerLimit: the lower limit of input data, used to verify the validity of the input data 87 | * int upperLimit: the upper limit of input data, used to verify the validity of the input data 88 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 89 | * Return Value: true: input data is valid 90 | * false: input data is invalid 91 | */ 92 | bool input(int lowerLimit, int upperLimit, unsigned int maxLength) 93 | { 94 | /* Read a line from standard input (cin) */ 95 | std::string str; 96 | std::getline(cin, str); 97 | 98 | /* Create a string stream and a temporary input variable to parse the input */ 99 | std::istringstream iss(str); 100 | Interval tmpInput{ -1,-1 }; 101 | int num; // Variable to hold the parsed number 102 | 103 | /* Loop to read and process each number from the input */ 104 | while (true) { 105 | /* Read a number from the input stream */ 106 | iss >> num; 107 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 108 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 109 | iss.clear(); 110 | iss.ignore(std::numeric_limits::max(), '\n'); 111 | vec.clear(); 112 | return false; // Return false to indicate invalid input 113 | } 114 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 115 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 116 | iss.clear(); 117 | iss.ignore(std::numeric_limits::max(), '\n'); 118 | vec.clear(); 119 | return false; // Return false to indicate invalid input 120 | } 121 | else { // Valid input 122 | if (tmpInput.startTime < 0) { 123 | tmpInput.startTime = num; 124 | } 125 | else { 126 | tmpInput.endTime = num; 127 | if (tmpInput.endTime <= tmpInput.startTime) { // Check if the end time is earlier than the start time 128 | cerr << "Error: The end time is earlier than the start time, please check input data and try again." << endl; 129 | iss.clear(); 130 | iss.ignore(std::numeric_limits::max(), '\n'); 131 | vec.clear(); 132 | return false; // Return false to indicate invalid input 133 | } 134 | vec.push_back(tmpInput); 135 | tmpInput.startTime = -1; 136 | tmpInput.endTime = -1; 137 | } 138 | } 139 | 140 | /* Check for end of input or maximum input length */ 141 | if (iss.eof() || vec.size() >= maxLength) { 142 | break; 143 | } 144 | } 145 | 146 | /* Check the length of vector */ 147 | if (vec.size() <= 0) { 148 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 149 | iss.clear(); 150 | iss.ignore(std::numeric_limits::max(), '\n'); 151 | vec.clear(); 152 | return false; // Return false to indicate invalid input 153 | } 154 | 155 | /* Return true to indicate valid input */ 156 | return true; 157 | } 158 | 159 | /* 160 | * Function Name: canWatchAllMovie 161 | * Function: Judge if a person can watch all movies 162 | * Input Parameters: void 163 | * Return Value: true: can watch all movies 164 | * false: can't watch all movies 165 | */ 166 | bool canWatchAllMovie(void) 167 | { 168 | /* Check if the vector is empty */ 169 | if (vec.empty()) { 170 | cerr << "Vector is empty." << endl; 171 | return false; 172 | } 173 | else { 174 | /* At least one movie can be watched */ 175 | unsigned int watchCount = 1; 176 | 177 | /* Quick sort algorithm */ 178 | quickSort(); 179 | 180 | /* Check if a person can watch all movies, the person can watch this movie without missing the previous one */ 181 | for (unsigned int count = 1; count < vec.size(); count++) { 182 | if (vec[count].startTime >= vec[count - 1].endTime) { 183 | watchCount++; 184 | } 185 | } 186 | return (watchCount == vec.size()); 187 | } 188 | } 189 | }; 190 | 191 | /* 192 | * Function Name: main 193 | * Function: Main function 194 | * Return Value: 0 195 | */ 196 | int main() 197 | { 198 | /* Define solution object */ 199 | Solution solution; 200 | 201 | /* Input data */ 202 | cout << "Input:" << endl; 203 | cout << "(0 <= intervals.length <= 10^4, if exceeded, it will be truncated.)" << endl; 204 | cout << "(intervals[i].length == 2, if exceeded, it will be truncated, if the last input data is a start time, it will be discarded.)" << endl; 205 | cout << "(0 <= starti < endi <= 10^6, please separate the data with spaces and press Enter.)" << endl; 206 | while (!solution.input(0, 1000000, 10000U)) { 207 | continue; 208 | } 209 | 210 | /* Output result */ 211 | cout << "Output:" << endl; 212 | cout << "Whether Can Watch All Movies: " << (solution.canWatchAllMovie() ? "true" : "false") << endl; 213 | 214 | /* Program ends */ 215 | return 0; 216 | } -------------------------------------------------------------------------------- /Assignment_2_2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_2_2) 2 | add_executable(${PROJECT_NAME} assignment_2_2.cpp) -------------------------------------------------------------------------------- /Assignment_2_2/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2-2 2 | 3 | ## 问题描述 4 | 5 | 电影院放映厅需求 6 | 7 | 给你一个电影的播放时间安排的数组 `intervals` ,每部电影的播放时间由其开始和结束的时间标记,即 `intervals[i] = [starti, endi]` 。请返回该影院需要的最少放映厅数量,以确保所有电影都能按计划放映。 8 | 9 | 提示: 10 | 11 | * `1 <= intervals.length <= 10^4` 12 | * `0 <= starti < endi <= 10^6` 13 | 14 | ## 测试用例 15 | 16 | * Test Case 1: 17 | 18 | Description: Test the correctness of the algorithm 19 | 20 | Input: 0 30 5 10 15 20 '\n' 21 | 22 | Expected Output: 2 23 | 24 | * Test Case 2: 25 | 26 | Description: Test the correctness of the algorithm 27 | 28 | Input: 7 10 2 4 '\n' 29 | 30 | Expected Output: 1 31 | 32 | * Test Case 3: 33 | 34 | Description: Test the correctness of the algorithm 35 | 36 | Input: 1 3 2 6 8 10 15 18 '\n' 37 | 38 | Expected Output: 2 39 | 40 | * Test Case 4: 41 | 42 | Description: Test the correctness of the algorithm 43 | 44 | Input: 1 4 4 5 '\n' 45 | 46 | Expected Output: 1 47 | 48 | * Test Case 5: 49 | 50 | Description: Test the handling of input data exceeding the upper limit 51 | 52 | Input: 0 10 1000000 1000001 '\n' 53 | 54 | Expected Output: Error 55 | 56 | * Test Case 6: 57 | 58 | Description: Test the handling of input data below the lower limit 59 | 60 | Input: 22 24 -1 36 '\n' 61 | 62 | Expected Output: Error 63 | 64 | * Test Case 7: 65 | 66 | Description: Test the handling of input data with unreasonable time 67 | 68 | Input: 100 200 300 200 '\n' 69 | 70 | Expected Output: Error 71 | 72 | * Test Case 8: 73 | 74 | Description: Test the handling of input data with unreasonable length 75 | 76 | Input: 10 '\n' 77 | 78 | Expected Output: Error 79 | 80 | * Test Case 9: 81 | 82 | Description: Test the handling of input data with unreasonable length 83 | 84 | Input: 0 10 12 22 24 '\n' 85 | 86 | Expected Output: 1 87 | 88 | * Test Case 10: 89 | 90 | Description: Test the handling of incorrect input data types 91 | 92 | Input: 5.5 10 a bcd '\n' 93 | 94 | Expected Output: Error 95 | 96 | ## 项目构建命令 97 | 98 | ``` 99 | mkdir build 100 | cd build 101 | cmake .. 102 | ``` 103 | 104 | ## 编译运行环境 105 | 106 | * 本项目适用于x86和x64架构 107 | 108 | ## 文档更新日期 109 | 110 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_2_2/assignment_2_2.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_2_2 3 | * File Name: assignment_2_2.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/9/27 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using std::cout; 16 | using std::cin; 17 | using std::cerr; 18 | using std::endl; 19 | 20 | /* Define Interval structure */ 21 | typedef struct { 22 | int startTime; 23 | int endTime; 24 | } Interval; 25 | 26 | /* Define Solution class */ 27 | template 28 | class Solution { 29 | private: 30 | /* Define private data member */ 31 | std::vector vec; 32 | 33 | /* 34 | * Function Name: quickSort 35 | * Function: Quick sort algorithm 36 | * Input Parameters: int left 37 | * int right 38 | * Return Value: void 39 | */ 40 | void quickSort(int left, int right) 41 | { 42 | /* If the left index is greater than or equal to the right index, return */ 43 | if (left >= right) { 44 | return; 45 | } 46 | 47 | /* Choose the pivot element as the middle element's start time in the vector */ 48 | int pivot = vec[(left + right) / 2].startTime; 49 | int i = left; 50 | int j = right; 51 | 52 | /* Rearrange elements around the pivot */ 53 | while (i <= j) { 54 | while (vec[i].startTime < pivot) { 55 | i++; // Find an element on the left side that is greater than or equal to the pivot 56 | } 57 | while (vec[j].startTime > pivot) { 58 | j--; // Find an element on the right side that is less than or equal to the pivot 59 | } 60 | if (i <= j) { 61 | std::swap(vec[i], vec[j]); // Swap elements if needed to ensure elements on the left are less than or equal to the pivot 62 | i++; 63 | j--; 64 | } 65 | } 66 | 67 | /* Recur for the subarrays on the left and right of the pivot */ 68 | quickSort(left, j); 69 | quickSort(i, right); 70 | } 71 | 72 | /* 73 | * Function Name: quickSort 74 | * Function: Quick sort algorithm 75 | * Input Parameters: void 76 | * Return Value: void 77 | */ 78 | void quickSort(void) 79 | { 80 | quickSort(0, static_cast(vec.size() - 1)); 81 | } 82 | 83 | public: 84 | /* 85 | * Function Name: input 86 | * Function: Input data 87 | * Input Parameters: int lowerLimit: the lower limit of input data, used to verify the validity of the input data 88 | * int upperLimit: the upper limit of input data, used to verify the validity of the input data 89 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 90 | * Return Value: true: input data is valid 91 | * false: input data is invalid 92 | */ 93 | bool input(int lowerLimit, int upperLimit, unsigned int maxLength) 94 | { 95 | /* Read a line from standard input (cin) */ 96 | std::string str; 97 | std::getline(cin, str); 98 | 99 | /* Create a string stream and a temporary input variable to parse the input */ 100 | std::istringstream iss(str); 101 | Interval tmpInput{ -1,-1 }; 102 | int num; // Variable to hold the parsed number 103 | 104 | /* Loop to read and process each number from the input */ 105 | while (true) { 106 | /* Read a number from the input stream */ 107 | iss >> num; 108 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 109 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 110 | iss.clear(); 111 | iss.ignore(std::numeric_limits::max(), '\n'); 112 | vec.clear(); 113 | return false; // Return false to indicate invalid input 114 | } 115 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 116 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 117 | iss.clear(); 118 | iss.ignore(std::numeric_limits::max(), '\n'); 119 | vec.clear(); 120 | return false; // Return false to indicate invalid input 121 | } 122 | else { // Valid input 123 | if (tmpInput.startTime < 0) { 124 | tmpInput.startTime = num; 125 | } 126 | else { 127 | tmpInput.endTime = num; 128 | if (tmpInput.endTime <= tmpInput.startTime) { // Check if the end time is earlier than the start time 129 | cerr << "Error: The end time is earlier than the start time, please check input data and try again." << endl; 130 | iss.clear(); 131 | iss.ignore(std::numeric_limits::max(), '\n'); 132 | vec.clear(); 133 | return false; // Return false to indicate invalid input 134 | } 135 | vec.push_back(tmpInput); 136 | tmpInput.startTime = -1; 137 | tmpInput.endTime = -1; 138 | } 139 | } 140 | 141 | /* Check for end of input or maximum input length */ 142 | if (iss.eof() || vec.size() >= maxLength) { 143 | break; 144 | } 145 | } 146 | 147 | /* Check the length of vector */ 148 | if (vec.size() <= 0) { 149 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 150 | iss.clear(); 151 | iss.ignore(std::numeric_limits::max(), '\n'); 152 | vec.clear(); 153 | return false; // Return false to indicate invalid input 154 | } 155 | 156 | /* Return true to indicate valid input */ 157 | return true; 158 | } 159 | 160 | /* 161 | * Function Name: minRoomNum 162 | * Function: Calculate minimum number of screening rooms 163 | * Input Parameters: void 164 | * Return Value: minimum number of screening rooms 165 | */ 166 | int minRoomNum(void) 167 | { 168 | /* Check if the vector is empty */ 169 | if (vec.empty()) { 170 | cerr << "Vector is empty." << endl; 171 | return 0; 172 | } 173 | else { 174 | /* Sort the intervals by their start times */ 175 | quickSort(); 176 | 177 | /* Min heap to keep track of end times */ 178 | std::priority_queue, std::greater> minHeap; 179 | 180 | /* Add the end time of the first interval to the heap */ 181 | minHeap.push(vec[0].endTime); 182 | 183 | /* Iterate through the intervals starting from the second one */ 184 | for (unsigned int count = 1; count < vec.size(); count++) { 185 | /* If the current interval's start time is after the earliest end time in the heap, remove that end time as it can be reused for the current interval */ 186 | if (vec[count].startTime >= minHeap.top()) { 187 | minHeap.pop(); 188 | } 189 | 190 | /* Add the end time of the current interval to the heap */ 191 | minHeap.push(vec[count].endTime); 192 | } 193 | 194 | /* The size of the heap is the minimum number of screening rooms needed */ 195 | return static_cast(minHeap.size()); 196 | } 197 | } 198 | }; 199 | 200 | /* 201 | * Function Name: main 202 | * Function: Main function 203 | * Return Value: 0 204 | */ 205 | int main() 206 | { 207 | /* Define solution object */ 208 | Solution solution; 209 | 210 | /* Input data */ 211 | cout << "Input:" << endl; 212 | cout << "(0 <= intervals.length <= 10^4, if exceeded, it will be truncated.)" << endl; 213 | cout << "(0 <= starti < endi <= 10^6, please separate the data with spaces and press Enter.)" << endl; 214 | while (!solution.input(0, 1000000, 10000U)) { 215 | continue; 216 | } 217 | 218 | /* Output result */ 219 | cout << "Output:" << endl; 220 | cout << "Minimum Number of Screening Rooms: " << solution.minRoomNum() << endl; 221 | 222 | /* Program ends */ 223 | return 0; 224 | } -------------------------------------------------------------------------------- /Assignment_2_3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_2_3) 2 | add_executable(${PROJECT_NAME} assignment_2_3.cpp) -------------------------------------------------------------------------------- /Assignment_2_3/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2-3 2 | 3 | ## 问题描述 4 | 5 | 电影节目合并 6 | 7 | 影院为观众提供了一系列电影的播放时间安排 `intervals` ,其中每部电影的播放时间由其开始和结束的时间标记,即 `intervals[i] = [starti, endi]` 。由于部分电影有重叠的播放时间,影院决定合并这些时间,以提供一个新的播放时间表。请你合并所有重叠的播放时间,并返回一个不重叠的播放时间数组,该数组需恰好覆盖输入中的所有时间。 8 | 9 | 提示: 10 | 11 | * `1 <= intervals.length <= 10^4` 12 | * `intervals[i].length == 2` 13 | * `0 <= starti <= endi <= 10^4` 14 | 15 | ## 测试用例 16 | 17 | * Test Case 1: 18 | 19 | Description: Test the correctness of the algorithm 20 | 21 | Input: 0 30 5 10 15 20 '\n' 22 | 23 | Expected Output: [[0,30]] 24 | 25 | * Test Case 2: 26 | 27 | Description: Test the correctness of the algorithm 28 | 29 | Input: 7 10 2 4 '\n' 30 | 31 | Expected Output: [[2,4],[7,10]] 32 | 33 | * Test Case 3: 34 | 35 | Description: Test the correctness of the algorithm 36 | 37 | Input: 1 3 2 6 8 10 15 18 '\n' 38 | 39 | Expected Output: [[1,6],[8,10],[15,18]] 40 | 41 | * Test Case 4: 42 | 43 | Description: Test the correctness of the algorithm 44 | 45 | Input: 1 4 4 5 '\n' 46 | 47 | Expected Output: [[1,5]] 48 | 49 | * Test Case 5: 50 | 51 | Description: Test the handling of input data exceeding the upper limit 52 | 53 | Input: 0 10 10000 10001 '\n' 54 | 55 | Expected Output: Error 56 | 57 | * Test Case 6: 58 | 59 | Description: Test the handling of input data below the lower limit 60 | 61 | Input: 22 24 -1 36 '\n' 62 | 63 | Expected Output: Error 64 | 65 | * Test Case 7: 66 | 67 | Description: Test the handling of input data with unreasonable time 68 | 69 | Input: 100 200 300 200 '\n' 70 | 71 | Expected Output: Error 72 | 73 | * Test Case 8: 74 | 75 | Description: Test the handling of input data with unreasonable length 76 | 77 | Input: 10 '\n' 78 | 79 | Expected Output: Error 80 | 81 | * Test Case 9: 82 | 83 | Description: Test the handling of input data with unreasonable length 84 | 85 | Input: 0 10 12 22 24 '\n' 86 | 87 | Expected Output: [[0,10],[12,22]] 88 | 89 | * Test Case 10: 90 | 91 | Description: Test the handling of incorrect input data types 92 | 93 | Input: 5.5 10 a bcd '\n' 94 | 95 | Expected Output: Error 96 | 97 | ## 项目构建命令 98 | 99 | ``` 100 | mkdir build 101 | cd build 102 | cmake .. 103 | ``` 104 | 105 | ## 编译运行环境 106 | 107 | * 本项目适用于x86和x64架构 108 | 109 | ## 文档更新日期 110 | 111 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_2_3/assignment_2_3.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_2_3 3 | * File Name: assignment_2_3.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/9/27 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cout; 15 | using std::cin; 16 | using std::cerr; 17 | using std::endl; 18 | 19 | /* Define Interval structure */ 20 | typedef struct { 21 | int startTime; 22 | int endTime; 23 | } Interval; 24 | 25 | /* 26 | * Function Name: operator<< 27 | * Function: Overload operator << 28 | * Input Parameters: std::ostream& out 29 | * const std::vector& vectorVariable 30 | * Return Value: out 31 | */ 32 | template 33 | std::ostream& operator<<(std::ostream& out, const std::vector& vectorVariable) 34 | { 35 | /* Check if the vector is empty */ 36 | if (vectorVariable.empty()) { 37 | cerr << "Vector is empty." << endl; 38 | } 39 | else { 40 | out << "[" << vectorVariable[0]; 41 | for (unsigned int count = 1; count < vectorVariable.size(); count++) 42 | out << "," << vectorVariable[count]; 43 | out << "]"; 44 | } 45 | return out; 46 | } 47 | 48 | /* 49 | * Function Name: operator<< 50 | * Function: Overload operator << 51 | * Input Parameters: std::ostream& out 52 | * const Interval& interval 53 | * Return Value: out 54 | */ 55 | std::ostream& operator<<(std::ostream& out, const Interval& interval) { 56 | return (out << "[" << interval.startTime << "," << interval.endTime << "]"); 57 | } 58 | 59 | /* Define Solution class */ 60 | template 61 | class Solution { 62 | private: 63 | /* Define private data member */ 64 | std::vector vec; 65 | 66 | /* 67 | * Function Name: quickSort 68 | * Function: Quick sort algorithm 69 | * Input Parameters: int left 70 | * int right 71 | * Return Value: void 72 | */ 73 | void quickSort(int left, int right) 74 | { 75 | /* If the left index is greater than or equal to the right index, return */ 76 | if (left >= right) { 77 | return; 78 | } 79 | 80 | /* Choose the pivot element as the middle element's start time in the vector */ 81 | int pivot = vec[(left + right) / 2].startTime; 82 | int i = left; 83 | int j = right; 84 | 85 | /* Rearrange elements around the pivot */ 86 | while (i <= j) { 87 | while (vec[i].startTime < pivot) { 88 | i++; // Find an element on the left side that is greater than or equal to the pivot 89 | } 90 | while (vec[j].startTime > pivot) { 91 | j--; // Find an element on the right side that is less than or equal to the pivot 92 | } 93 | if (i <= j) { 94 | std::swap(vec[i], vec[j]); // Swap elements if needed to ensure elements on the left are less than or equal to the pivot 95 | i++; 96 | j--; 97 | } 98 | } 99 | 100 | /* Recur for the subarrays on the left and right of the pivot */ 101 | quickSort(left, j); 102 | quickSort(i, right); 103 | } 104 | 105 | /* 106 | * Function Name: quickSort 107 | * Function: Quick sort algorithm 108 | * Input Parameters: void 109 | * Return Value: void 110 | */ 111 | void quickSort(void) 112 | { 113 | quickSort(0, static_cast(vec.size() - 1)); 114 | } 115 | 116 | public: 117 | /* 118 | * Function Name: input 119 | * Function: Input data 120 | * Input Parameters: int lowerLimit: the lower limit of input data, used to verify the validity of the input data 121 | * int upperLimit: the upper limit of input data, used to verify the validity of the input data 122 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 123 | * Return Value: true: input data is valid 124 | * false: input data is invalid 125 | */ 126 | bool input(int lowerLimit, int upperLimit, unsigned int maxLength) 127 | { 128 | /* Read a line from standard input (cin) */ 129 | std::string str; 130 | std::getline(cin, str); 131 | 132 | /* Create a string stream and a temporary input variable to parse the input */ 133 | std::istringstream iss(str); 134 | Interval tmpInput{ -1,-1 }; 135 | int num; // Variable to hold the parsed number 136 | 137 | /* Loop to read and process each number from the input */ 138 | while (true) { 139 | /* Read a number from the input stream */ 140 | iss >> num; 141 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 142 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 143 | iss.clear(); 144 | iss.ignore(std::numeric_limits::max(), '\n'); 145 | vec.clear(); 146 | return false; // Return false to indicate invalid input 147 | } 148 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 149 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 150 | iss.clear(); 151 | iss.ignore(std::numeric_limits::max(), '\n'); 152 | vec.clear(); 153 | return false; // Return false to indicate invalid input 154 | } 155 | else { // Valid input 156 | if (tmpInput.startTime < 0) { 157 | tmpInput.startTime = num; 158 | } 159 | else { 160 | tmpInput.endTime = num; 161 | if (tmpInput.endTime < tmpInput.startTime) { // Check if the end time is earlier than the start time 162 | cerr << "Error: The end time is earlier than the start time, please check input data and try again." << endl; 163 | iss.clear(); 164 | iss.ignore(std::numeric_limits::max(), '\n'); 165 | vec.clear(); 166 | return false; // Return false to indicate invalid input 167 | } 168 | vec.push_back(tmpInput); 169 | tmpInput.startTime = -1; 170 | tmpInput.endTime = -1; 171 | } 172 | } 173 | 174 | /* Check for end of input or maximum input length */ 175 | if (iss.eof() || vec.size() >= maxLength) { 176 | break; 177 | } 178 | } 179 | 180 | /* Check the length of vector */ 181 | if (vec.size() <= 0) { 182 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 183 | iss.clear(); 184 | iss.ignore(std::numeric_limits::max(), '\n'); 185 | vec.clear(); 186 | return false; // Return false to indicate invalid input 187 | } 188 | 189 | /* Return true to indicate valid input */ 190 | return true; 191 | } 192 | 193 | /* 194 | * Function Name: mergeMovie 195 | * Function: Merge movies 196 | * Input Parameters: void 197 | * Return Value: merged vector 198 | */ 199 | std::vector mergeMovie(void) 200 | { 201 | /* Initialize merged vector */ 202 | std::vector mergedVector; 203 | 204 | /* Check if the vector is empty */ 205 | if (vec.empty()) { 206 | cerr << "Vector is empty." << endl; 207 | return mergedVector; // Return an empty vector 208 | } 209 | else { 210 | /* Sort intervals based on their end times using quick sort */ 211 | quickSort(); 212 | 213 | /* Initialize the first interval as the first interval in the sorted vector */ 214 | Interval currentInterval = vec[0]; 215 | 216 | /* Traverse the sorted intervals and merge overlapping intervals */ 217 | for (unsigned int count = 1; count < vec.size(); count++) { 218 | if (currentInterval.endTime >= vec[count].startTime) { 219 | /* If the intervals overlap, update the end time of the current interval */ 220 | currentInterval.endTime = std::max(currentInterval.endTime, vec[count].endTime); 221 | } 222 | else { 223 | /* If there is no overlap, add the current interval to the merged vector */ 224 | mergedVector.push_back(currentInterval); 225 | currentInterval = vec[count]; 226 | } 227 | } 228 | 229 | /* Add the last merged interval */ 230 | mergedVector.push_back(currentInterval); 231 | return mergedVector; 232 | } 233 | } 234 | }; 235 | 236 | /* 237 | * Function Name: main 238 | * Function: Main function 239 | * Return Value: 0 240 | */ 241 | int main() 242 | { 243 | /* Define solution object */ 244 | Solution solution; 245 | 246 | /* Input data */ 247 | cout << "Input:" << endl; 248 | cout << "(1 <= intervals.length <= 10^4, if exceeded, it will be truncated.)" << endl; 249 | cout << "(intervals[i].length == 2, if exceeded, it will be truncated, if the last input data is a start time, it will be discarded.)" << endl; 250 | cout << "(0 <= starti <= endi <= 10^4, please separate the data with spaces and press Enter.)" << endl; 251 | while (!solution.input(0, 10000, 10000U)) { 252 | continue; 253 | } 254 | 255 | /* Output result */ 256 | cout << "Output:" << endl; 257 | cout << "Merged Vector: " << solution.mergeMovie() << endl; 258 | 259 | /* Program ends */ 260 | return 0; 261 | } -------------------------------------------------------------------------------- /Assignment_3_1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_3_1) 2 | add_executable(${PROJECT_NAME} assignment_3_1.cpp) -------------------------------------------------------------------------------- /Assignment_3_1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 3-1 2 | 3 | ## 问题描述 4 | 5 | 存在重复元素Ⅰ 6 | 7 | 给你一个整数数组 `nums` 。如果任意一值在数组中出现至少两次,返回 `true` ;如果数组中每个值仅出现一次,返回 `false` 。 8 | 9 | 提示: 10 | 11 | * `1 <= nums.length <= 10^5` 12 | * `-10^9 <= nums[i] <= 10^9` 13 | 14 | ## 测试用例 15 | 16 | * Test Case 1: 17 | 18 | Description: Test the correctness of the algorithm 19 | 20 | Input: 1 2 3 1 '\n' 21 | 22 | Expected Output: true 23 | 24 | * Test Case 2: 25 | 26 | Description: Test the correctness of the algorithm 27 | 28 | Input: 1 2 3 4 '\n' 29 | 30 | Expected Output: false 31 | 32 | * Test Case 3: 33 | 34 | Description: Test the correctness of the algorithm 35 | 36 | Input: 1 1 1 3 3 4 3 2 4 2 '\n' 37 | 38 | Expected Output: true 39 | 40 | * Test Case 4: 41 | 42 | Description: Test the handling of input data exceeding the upper limit 43 | 44 | Input: 2147483647 '\n' 45 | 46 | Expected Output: Error 47 | 48 | * Test Case 5: 49 | 50 | Description: Test the handling of input data exceeding the upper limit 51 | 52 | Input: 1 2147483648 2 3 '\n' 53 | 54 | Expected Output: Error 55 | 56 | * Test Case 6: 57 | 58 | Description: Test the handling of input data below the lower limit 59 | 60 | Input: -2147483648 '\n' 61 | 62 | Expected Output: Error 63 | 64 | * Test Case 7: 65 | 66 | Description: Test the handling of input data below the lower limit 67 | 68 | Input: 1 -2147483649 2 3 '\n' 69 | 70 | Expected Output: Error 71 | 72 | * Test Case 8: 73 | 74 | Description: Test the handling of input data with unreasonable length 75 | 76 | Input: '\n' 77 | 78 | Expected Output: Error 79 | 80 | * Test Case 9: 81 | 82 | Description: Test the handling of incorrect input data types 83 | 84 | Input: a bcd '\n' 85 | 86 | Expected Output: Error 87 | 88 | * Test Case 10: 89 | 90 | Description: Test the handling of incorrect input data types 91 | 92 | Input: 1.5 '\n' 93 | 94 | Expected Output: Error 95 | 96 | ## 项目构建命令 97 | 98 | ``` 99 | mkdir build 100 | cd build 101 | cmake .. 102 | ``` 103 | 104 | ## 编译运行环境 105 | 106 | * 本项目适用于x86和x64架构 107 | 108 | ## 文档更新日期 109 | 110 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_3_1/assignment_3_1.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_3_1 3 | * File Name: assignment_3_1.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/10/14 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using std::cout; 16 | using std::cin; 17 | using std::cerr; 18 | using std::endl; 19 | 20 | /* Define Solution class */ 21 | template 22 | class Solution { 23 | private: 24 | std::vector vec; 25 | public: 26 | /* 27 | * Function Name: input 28 | * Function: Input data 29 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 30 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 31 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 32 | * Return Value: true: input data is valid 33 | * false: input data is invalid 34 | */ 35 | bool input(Type lowerLimit, Type upperLimit, unsigned int maxLength) 36 | { 37 | /* Read a line from standard input (cin) */ 38 | std::string str; 39 | std::getline(cin, str); 40 | 41 | /* Create a string stream to parse the input */ 42 | std::istringstream iss(str); 43 | Type num; // Variable to hold the parsed number 44 | 45 | /* Loop to read and process each number from the input */ 46 | while (true) { 47 | /* Read a number from the input stream */ 48 | iss >> num; 49 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 50 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 51 | iss.clear(); 52 | iss.ignore(std::numeric_limits::max(), '\n'); 53 | vec.clear(); 54 | return false; // Return false to indicate invalid input 55 | } 56 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 57 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 58 | iss.clear(); 59 | iss.ignore(std::numeric_limits::max(), '\n'); 60 | vec.clear(); 61 | return false; // Return false to indicate invalid input 62 | } 63 | else { // Valid input 64 | vec.push_back(num); 65 | } 66 | 67 | /* Check for end of input or maximum input length */ 68 | if (iss.eof() || vec.size() >= maxLength) { 69 | break; 70 | } 71 | } 72 | 73 | /* Check the length of vector */ 74 | if (vec.size() <= 0) { 75 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 76 | iss.clear(); 77 | iss.ignore(std::numeric_limits::max(), '\n'); 78 | vec.clear(); 79 | return false; // Return false to indicate invalid input 80 | } 81 | 82 | /* Return true to indicate valid input */ 83 | return true; 84 | } 85 | 86 | /* 87 | * Function Name: checkDuplicateElement 88 | * Function: Check for duplicate elements 89 | * Input Parameters: void 90 | * Return Value: true: any value appears at least twice in the array 91 | * false: each value in the array appears only once 92 | */ 93 | bool checkDuplicateElement() 94 | { 95 | /* Sort the array */ 96 | std::vector vecCopy = vec; 97 | sort(vecCopy.begin(), vecCopy.end()); 98 | 99 | /* Check for duplicate elements */ 100 | for (unsigned int count = 0; count < vecCopy.size() - 1; count++) { 101 | if (vecCopy[count] == vecCopy[count + 1]) { 102 | return true; // Found duplicate elements 103 | } 104 | } 105 | return false; // Not found duplicate elements 106 | } 107 | }; 108 | 109 | /* 110 | * Function Name: main 111 | * Function: Main function 112 | * Return Value: 0 113 | */ 114 | int main() 115 | { 116 | /* Define solution object */ 117 | Solution solution; 118 | 119 | /* Input data */ 120 | cout << "Input:" << endl; 121 | cout << "(1 <= nums.length <= 10^5, if exceeded, it will be truncated.)" << endl; 122 | cout << "(-10^9 <= nums[i] <= 10^9, please separate the data with spaces and press Enter.)" << endl; 123 | while (!solution.input(-1000000000, 1000000000, 100000U)) { 124 | continue; 125 | } 126 | 127 | /* Output result */ 128 | cout << "Output:" << endl; 129 | cout << "Whether any value appears at least twice in the array: " << (solution.checkDuplicateElement() ? "true" : "false") << endl; 130 | 131 | /* Program ends */ 132 | return 0; 133 | } -------------------------------------------------------------------------------- /Assignment_3_2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_3_2) 2 | add_executable(${PROJECT_NAME} assignment_3_2.cpp) -------------------------------------------------------------------------------- /Assignment_3_2/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 3-2 2 | 3 | ## 问题描述 4 | 5 | 存在重复元素Ⅱ 6 | 7 | 给你一个整数数组 `nums` 和一个整数 `k` ,判断数组中是否存在两个不同的索引 `i` 和 `j` ,使得 `nums[i] == nums[j]` ,并且 `abs(i - j) <= k` 。 8 | 9 | 提示: 10 | 11 | * `1 <= nums.length <= 10^5` 12 | * `-10^9 <= nums[i] <= 10^9` 13 | * `0 <= k <= 10^5` 14 | 15 | ## 测试用例 16 | 17 | * Test Case 1: 18 | 19 | Description: Test the correctness of the algorithm 20 | 21 | Input: 1 2 3 1 '\n' 3 '\n' 22 | 23 | Expected Output: true 24 | 25 | * Test Case 2: 26 | 27 | Description: Test the correctness of the algorithm 28 | 29 | Input: 1 0 1 1 '\n' 1 '\n' 30 | 31 | Expected Output: true 32 | 33 | * Test Case 3: 34 | 35 | Description: Test the correctness of the algorithm 36 | 37 | Input: 1 2 3 1 2 3 '\n' 2 '\n' 38 | 39 | Expected Output: false 40 | 41 | * Test Case 4: 42 | 43 | Description: Test the handling of input data exceeding the upper limit 44 | 45 | Input: 2147483647 '\n' 46 | 47 | Expected Output: Error 48 | 49 | * Test Case 5: 50 | 51 | Description: Test the handling of input data exceeding the upper limit 52 | 53 | Input: 1 2 3 '\n' 100001 '\n' 54 | 55 | Expected Output: Error 56 | 57 | * Test Case 6: 58 | 59 | Description: Test the handling of input data below the lower limit 60 | 61 | Input: -2147483648 '\n' 62 | 63 | Expected Output: Error 64 | 65 | * Test Case 7: 66 | 67 | Description: Test the handling of input data below the lower limit 68 | 69 | Input: 1 2 3 '\n' -1 '\n' 70 | 71 | Expected Output: Error 72 | 73 | * Test Case 8: 74 | 75 | Description: Test the handling of input data with unreasonable length 76 | 77 | Input: '\n' 78 | 79 | Expected Output: Error 80 | 81 | * Test Case 9: 82 | 83 | Description: Test the handling of incorrect input data types 84 | 85 | Input: 1.5 '\n' 86 | 87 | Expected Output: Error 88 | 89 | * Test Case 10: 90 | 91 | Description: Test the handling of incorrect input data types 92 | 93 | Input: 1 2 3 '\n' a '\n' 94 | 95 | Expected Output: Error 96 | 97 | ## 项目构建命令 98 | 99 | ``` 100 | mkdir build 101 | cd build 102 | cmake .. 103 | ``` 104 | 105 | ## 编译运行环境 106 | 107 | * 本项目适用于x86和x64架构 108 | 109 | ## 文档更新日期 110 | 111 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_3_2/assignment_3_2.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_3_2 3 | * File Name: assignment_3_2.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/10/14 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cout; 15 | using std::cin; 16 | using std::cerr; 17 | using std::endl; 18 | 19 | /* Define Solution class */ 20 | template 21 | class Solution { 22 | private: 23 | /* Define private data members */ 24 | std::vector vec; 25 | Type m_k = 0; 26 | 27 | /* 28 | * Function Name: inputVariable 29 | * Function: Input data 30 | * Input Parameters: Type& input: input variable 31 | * Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 32 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 33 | * Return Value: void 34 | */ 35 | void inputVariable(Type& input, Type lowerLimit, Type upperLimit) 36 | { 37 | while (true) { 38 | cin >> input; 39 | if (cin.good()) { 40 | if (input >= lowerLimit && input <= upperLimit) { 41 | return; 42 | } 43 | else { 44 | cin.clear(); 45 | cin.ignore(std::numeric_limits::max(), '\n'); 46 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 47 | } 48 | } 49 | else { 50 | cin.clear(); 51 | cin.ignore(std::numeric_limits::max(), '\n'); 52 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 53 | } 54 | } 55 | } 56 | 57 | public: 58 | /* 59 | * Function Name: input 60 | * Function: Input data 61 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 62 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 63 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 64 | * Return Value: true: input data is valid 65 | * false: input data is invalid 66 | */ 67 | bool input(Type lowerLimit, Type upperLimit, unsigned int maxLength) 68 | { 69 | /* Read a line from standard input (cin) */ 70 | std::string str; 71 | std::getline(cin, str); 72 | 73 | /* Create a string stream to parse the input */ 74 | std::istringstream iss(str); 75 | Type num; // Variable to hold the parsed number 76 | 77 | /* Loop to read and process each number from the input */ 78 | while (true) { 79 | /* Read a number from the input stream */ 80 | iss >> num; 81 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 82 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 83 | iss.clear(); 84 | iss.ignore(std::numeric_limits::max(), '\n'); 85 | vec.clear(); 86 | return false; // Return false to indicate invalid input 87 | } 88 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 89 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 90 | iss.clear(); 91 | iss.ignore(std::numeric_limits::max(), '\n'); 92 | vec.clear(); 93 | return false; // Return false to indicate invalid input 94 | } 95 | else { // Valid input 96 | vec.push_back(num); 97 | } 98 | 99 | /* Check for end of input or maximum input length */ 100 | if (iss.eof() || vec.size() >= maxLength) { 101 | break; 102 | } 103 | } 104 | 105 | /* Check the length of vector */ 106 | if (vec.size() <= 0) { 107 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 108 | iss.clear(); 109 | iss.ignore(std::numeric_limits::max(), '\n'); 110 | vec.clear(); 111 | return false; // Return false to indicate invalid input 112 | } 113 | 114 | /* Return true to indicate valid input */ 115 | return true; 116 | } 117 | 118 | /* 119 | * Function Name: inputK 120 | * Function: Input variable k 121 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 122 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 123 | * Return Value: void 124 | */ 125 | void inputK(Type lowerLimit, Type upperLimit) 126 | { 127 | inputVariable(m_k, lowerLimit, upperLimit); 128 | } 129 | 130 | /* 131 | * Function Name: checkDuplicateElement 132 | * Function: Check for duplicate elements 133 | * Input Parameters: void 134 | * Return Value: true: existence of two indices meeting the criteria 135 | * false: absence of two indices meeting the criteria 136 | */ 137 | bool checkDuplicateElement() 138 | { 139 | /* Iterate through the array */ 140 | for (unsigned int left = 0; left < vec.size(); left++) { 141 | /* Iterate through elements to the right of the current element */ 142 | for (unsigned int right = left + 1; right < vec.size() && static_cast(right - left) <= m_k; right++) { 143 | /* Check if the current and right elements are equal */ 144 | if (vec[left] == vec[right]) { 145 | return true; 146 | } 147 | } 148 | } 149 | return false; 150 | } 151 | }; 152 | 153 | /* 154 | * Function Name: main 155 | * Function: Main function 156 | * Return Value: 0 157 | */ 158 | int main() 159 | { 160 | /* Define solution object */ 161 | Solution solution; 162 | 163 | /* Input integer array */ 164 | cout << "Input integer array:" << endl; 165 | cout << "(1 <= nums.length <= 10^5, if exceeded, it will be truncated.)" << endl; 166 | cout << "(-10^9 <= nums[i] <= 10^9, please separate the data with spaces and press Enter.)" << endl; 167 | while (!solution.input(-1000000000, 1000000000, 100000U)) { 168 | continue; 169 | } 170 | 171 | /* Input integer k */ 172 | cout << "Input integer k:" << endl; 173 | cout << "(0 <= k <= 10^5, please input an integer and press Enter.)" << endl; 174 | solution.inputK(0, 100000); 175 | 176 | /* Output result */ 177 | cout << "Output:" << endl; 178 | cout << "Whether there exist two indices meeting the criteria: " << (solution.checkDuplicateElement() ? "true" : "false") << endl; 179 | 180 | /* Program ends */ 181 | return 0; 182 | } -------------------------------------------------------------------------------- /Assignment_3_3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Assignment_3_3) 2 | add_executable(${PROJECT_NAME} assignment_3_3.cpp) -------------------------------------------------------------------------------- /Assignment_3_3/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 3-3 2 | 3 | ## 问题描述 4 | 5 | 存在重复元素Ⅲ 6 | 7 | 给你一个整数数组 `nums` 和两个整数 `indexDiff` 和 `valueDiff` 。找出是否存在这样的两个下标 `(i, j)` : 8 | 9 | * `i != j` 10 | * `abs(i - j) <= indexDiff` 11 | * `abs(nums[i] - nums[j]) <= valueDiff` 12 | 13 | 如果存在,返回 `true` ;否则,返回 `false` 。 14 | 15 | 提示: 16 | 17 | * `2 <= nums.length <= 10^5` 18 | * `-10^9 <= nums[i] <= 10^9` 19 | * `1 <= indexDiff <= nums.length` 20 | * `0 <= valueDiff <= 10^9` 21 | 22 | ## 测试用例 23 | 24 | * Test Case 1: 25 | 26 | Description: Test the correctness of the algorithm 27 | 28 | Input: 1 2 3 1 '\n' 3 '\n' 0 '\n' 29 | 30 | Expected Output: true 31 | 32 | * Test Case 2: 33 | 34 | Description: Test the correctness of the algorithm 35 | 36 | Input: 1 5 9 1 5 9 '\n' 2 '\n' 3 '\n' 37 | 38 | Expected Output: false 39 | 40 | * Test Case 3: 41 | 42 | Description: Test the correctness of the algorithm 43 | 44 | Input: 1 2 3 1 2 3 '\n' 2 '\n' 2 '\n' 45 | 46 | Expected Output: true 47 | 48 | * Test Case 4: 49 | 50 | Description: Test the handling of input data exceeding the upper limit 51 | 52 | Input: 2147483647 '\n' 53 | 54 | Expected Output: Error 55 | 56 | * Test Case 5: 57 | 58 | Description: Test the handling of input data exceeding the upper limit 59 | 60 | Input: 1 2 3 '\n' 4 '\n' 61 | 62 | Expected Output: Error 63 | 64 | * Test Case 6: 65 | 66 | Description: Test the handling of input data below the lower limit 67 | 68 | Input: -2147483648 '\n' 69 | 70 | Expected Output: Error 71 | 72 | * Test Case 7: 73 | 74 | Description: Test the handling of input data below the lower limit 75 | 76 | Input: 1 2 3 '\n' 0 '\n' 77 | 78 | Expected Output: Error 79 | 80 | * Test Case 8: 81 | 82 | Description: Test the handling of input data with unreasonable length 83 | 84 | Input: 1 '\n' 85 | 86 | Expected Output: Error 87 | 88 | * Test Case 9: 89 | 90 | Description: Test the handling of incorrect input data types 91 | 92 | Input: 1.5 '\n' 93 | 94 | Expected Output: Error 95 | 96 | * Test Case 10: 97 | 98 | Description: Test the handling of incorrect input data types 99 | 100 | Input: 1 2 3 '\n' a '\n' 101 | 102 | Expected Output: Error 103 | 104 | ## 项目构建命令 105 | 106 | ``` 107 | mkdir build 108 | cd build 109 | cmake .. 110 | ``` 111 | 112 | ## 编译运行环境 113 | 114 | * 本项目适用于x86和x64架构 115 | 116 | ## 文档更新日期 117 | 118 | 2024年3月10日 -------------------------------------------------------------------------------- /Assignment_3_3/assignment_3_3.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Assignment_3_3 3 | * File Name: assignment_3_3.cpp 4 | * File Function: Problem solution 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/10/14 7 | ****************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using std::cout; 16 | using std::cin; 17 | using std::cerr; 18 | using std::endl; 19 | 20 | /* Define Solution class */ 21 | template 22 | class Solution { 23 | private: 24 | /* Define private data members */ 25 | std::vector vec; 26 | Type m_indexDiff = 0; 27 | Type m_valueDiff = 0; 28 | 29 | /* 30 | * Function Name: inputVariable 31 | * Function: Input data 32 | * Input Parameters: Type& input: input variable 33 | * Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 34 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 35 | * Return Value: void 36 | */ 37 | void inputVariable(Type& input, Type lowerLimit, Type upperLimit) 38 | { 39 | while (true) { 40 | cin >> input; 41 | if (cin.good()) { 42 | if (input >= lowerLimit && input <= upperLimit) { 43 | return; 44 | } 45 | else { 46 | cin.clear(); 47 | cin.ignore(std::numeric_limits::max(), '\n'); 48 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 49 | } 50 | } 51 | else { 52 | cin.clear(); 53 | cin.ignore(std::numeric_limits::max(), '\n'); 54 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 55 | } 56 | } 57 | } 58 | 59 | public: 60 | /* 61 | * Function Name: getVecLength 62 | * Function: Get private vector variable's length 63 | * Input Parameters: void 64 | * Return Value: private vector variable's length 65 | */ 66 | const size_t getVecLength(void) const 67 | { 68 | return vec.size(); 69 | } 70 | 71 | /* 72 | * Function Name: input 73 | * Function: Input data 74 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 75 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 76 | * unsigned int minLength: the minimum length of input data, used to verify the validity of the input data 77 | * unsigned int maxLength: the maximum length of input data, if exceeded, it will be truncated 78 | * Return Value: true: input data is valid 79 | * false: input data is invalid 80 | */ 81 | bool input(Type lowerLimit, Type upperLimit, unsigned int minLength, unsigned int maxLength) 82 | { 83 | /* Read a line from standard input (cin) */ 84 | std::string str; 85 | std::getline(cin, str); 86 | 87 | /* Create a string stream to parse the input */ 88 | std::istringstream iss(str); 89 | Type num; // Variable to hold the parsed number 90 | 91 | /* Loop to read and process each number from the input */ 92 | while (true) { 93 | /* Read a number from the input stream */ 94 | iss >> num; 95 | if (iss.fail()) { // Check if the read operation failed (invalid data type) 96 | cerr << "Error: Input data is invalid, please check data type and try again." << endl; 97 | iss.clear(); 98 | iss.ignore(std::numeric_limits::max(), '\n'); 99 | vec.clear(); 100 | return false; // Return false to indicate invalid input 101 | } 102 | else if (num < lowerLimit || num > upperLimit) { // Check if the number is outside the valid range 103 | cerr << "Error: Input data is not within the valid range, please check input data and try again." << endl; 104 | iss.clear(); 105 | iss.ignore(std::numeric_limits::max(), '\n'); 106 | vec.clear(); 107 | return false; // Return false to indicate invalid input 108 | } 109 | else { // Valid input 110 | vec.push_back(num); 111 | } 112 | 113 | /* Check for end of input or maximum input length */ 114 | if (iss.eof() || vec.size() >= maxLength) { 115 | break; 116 | } 117 | } 118 | 119 | /* Check the length of vector */ 120 | if (vec.size() < minLength) { 121 | cerr << "Error: Input data is invalid, please check input data and try again." << endl; 122 | iss.clear(); 123 | iss.ignore(std::numeric_limits::max(), '\n'); 124 | vec.clear(); 125 | return false; // Return false to indicate invalid input 126 | } 127 | 128 | /* Return true to indicate valid input */ 129 | return true; 130 | } 131 | 132 | /* 133 | * Function Name: inputIndexDiff 134 | * Function: Input variable indexDiff 135 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 136 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 137 | * Return Value: void 138 | */ 139 | void inputIndexDiff(Type lowerLimit, Type upperLimit) 140 | { 141 | inputVariable(m_indexDiff, lowerLimit, upperLimit); 142 | } 143 | 144 | /* 145 | * Function Name: inputValueDiff 146 | * Function: Input variable valueDiff 147 | * Input Parameters: Type lowerLimit: the lower limit of input data, used to verify the validity of the input data 148 | * Type upperLimit: the upper limit of input data, used to verify the validity of the input data 149 | * Return Value: void 150 | */ 151 | void inputValueDiff(Type lowerLimit, Type upperLimit) 152 | { 153 | inputVariable(m_valueDiff, lowerLimit, upperLimit); 154 | } 155 | 156 | /* 157 | * Function Name: checkDuplicateElement 158 | * Function: Check for duplicate elements 159 | * Input Parameters: void 160 | * Return Value: true: existence of two indices meeting the criteria 161 | * false: absence of two indices meeting the criteria 162 | */ 163 | bool checkDuplicateElement() 164 | { 165 | /* Iterate through the array */ 166 | for (unsigned int i = 0; i < vec.size(); i++) { 167 | /* Iterate through elements within the index difference from the current element */ 168 | for (unsigned int j = i + 1; j < i + m_indexDiff + 1 && j < vec.size(); j++) { 169 | /* Check if the absolute difference of elements is within the value difference */ 170 | if (abs(vec[i] - vec[j]) <= m_valueDiff) 171 | return true; 172 | } 173 | } 174 | return false; 175 | } 176 | }; 177 | 178 | /* 179 | * Function Name: main 180 | * Function: Main function 181 | * Return Value: 0 182 | */ 183 | int main() 184 | { 185 | /* Define solution object */ 186 | Solution solution; 187 | 188 | /* Input integer array */ 189 | cout << "Input integer array:" << endl; 190 | cout << "(2 <= nums.length <= 10^5, if exceeded, it will be truncated.)" << endl; 191 | cout << "(-10^9 <= nums[i] <= 10^9, please separate the data with spaces and press Enter.)" << endl; 192 | while (!solution.input(-1000000000, 1000000000, 2U, 100000U)) { 193 | continue; 194 | } 195 | 196 | /* Input integer indexDiff */ 197 | cout << "Input integer indexDiff:" << endl; 198 | cout << "(1 <= indexDiff <= nums.length, please input an integer and press Enter.)" << endl; 199 | solution.inputIndexDiff(1, static_cast(solution.getVecLength())); 200 | 201 | /* Input integer valueDiff */ 202 | cout << "Input integer valueDiff:" << endl; 203 | cout << "(0 <= valueDiff <= 10^9, please input an integer and press Enter.)" << endl; 204 | solution.inputValueDiff(0, 1000000000); 205 | 206 | /* Output result */ 207 | cout << "Output:" << endl; 208 | cout << "Whether there exist two indices meeting the criteria: " << (solution.checkDuplicateElement() ? "true" : "false") << endl; 209 | 210 | /* Program ends */ 211 | return 0; 212 | } -------------------------------------------------------------------------------- /Gomoku/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Gomoku) 2 | add_executable(${PROJECT_NAME} gomoku.cpp) -------------------------------------------------------------------------------- /Gomoku/README.md: -------------------------------------------------------------------------------- 1 | # Gomoku 2 | 3 | ## 项目名称 4 | 5 | Gomoku 6 | 7 | ## 项目实现功能 8 | 9 | 五子棋AI对战程序 10 | 11 | ### 比赛规则 12 | 13 | 本规则是五子棋的原始规则。 14 | 15 | * 对局在一个12*12的方形网格上进行; 16 | 17 | * 对局由两名玩家(被称为黑方和白方)进行。黑方先行,然后玩家轮流在空棋位上放置自己的棋子; 18 | 19 | * 对局在一名玩家成功地以自己的颜色形成连续五颗或以上棋子的一条直线时结束,这条直线可以是水平、垂直或对角线上的; 20 | 21 | * 如果棋盘上的所有空棋位都被占据,并且没有玩家达到获胜条件,对局被视为和棋; 22 | 23 | * 轮到己方走子时,大脑程序需要在2秒时间内给出落子方案; 24 | 25 | * 每一局中,己方不能使用超过90秒的总时间(对方走子时不算己方用时); 26 | 27 | * 大脑程序任何时刻都不能使用超过350MB的内存; 28 | 29 | * 在对方走子时,己方的程序会继续保持运行。 30 | 31 | ### 输入输出格式 32 | 33 | 五子棋大脑程序需要从标准输入( `stdin` )接收指令,并相应地做出响应,将响应输出到标准输出( `stdout` )。每一个指令都独占一行。大脑程序的响应也需要独占一行(即跟随一个换行符 `'\n'` )。以下是大脑程序需要支持的指令。 34 | 35 | #### START [FIELD] 36 | 37 | 在开始对局前,大脑程序一定会收到该指令,指令表明了己方有关的信息。 `FIELD` 代表该己方大脑执子颜色, `FIELD` 为 `1` 时代表己方执黑棋, `FIELD` 为 `2` 时代表己方执白棋。收到该指令后,大脑程序需要在1秒响应OK,否则判负。 38 | 39 | #### PLACE [X] [Y] 40 | 41 | 该指令代表一次对手的行棋, `X` 、 `Y` 是对手要放置棋子的行列坐标(坐标从 `0` 开始)。大脑程序不需要回复该指令。 42 | 43 | #### TURN 44 | 45 | 该指令代表轮到己方操作。大脑程序收到该指令后,经过计算得出己方走子,并将要放置棋子的行坐标、列坐标作为响应内容。大脑程序需要在指定时限内做出走子响应,否则判负。该指令可能直接出现在 `START` 指令之后,即己方执黑棋开局,也可能出现在一次 `PLACE` 指令之后,即对手落子完毕轮到己方落子。注意,若对手落子完毕后对局直接结束,则 `PLACE` 指令之后不会跟随有 `TURN` 指令。 46 | 47 | #### END [FIELD] 48 | 49 | 代表该局比赛结束,其中 `FIELD` 代表获胜方, `FIELD` 为 `0` 时是平局, `FIELD` 为 `1` 时是己方获胜, `FIELD` 为 `2` 时是对方获胜。在收到该指令后,大脑程序不需要做任何响应,可以自行决定是否要退出程序。该指令可能在任何时刻出现,例如出现在 `BEGIN` 前的话,可能是对手程序崩溃导致的这场比赛直接结束。 50 | 51 | ## 项目文件组成 52 | 53 | ### 源文件 54 | 55 | * `gomoku.cpp` 56 | 五子棋AI对战程序的实现 57 | 58 | ## 项目技术细节 59 | 60 | ### `Status` 类型的实现 61 | 62 | ```cpp 63 | typedef enum { 64 | Empty, // 空位标志 65 | Black, // 黑棋标志 66 | White // 白棋标志 67 | } Status; 68 | ``` 69 | 70 | ### `Move` 类型的实现 71 | 72 | ```cpp 73 | typedef struct { 74 | int x; // 行数 75 | int y; // 列数 76 | int score; // 评分 77 | } Move; 78 | ``` 79 | 80 | ### `HashEntry` 类型的实现 81 | 82 | ```cpp 83 | typedef struct { 84 | int depth; // 深度 85 | int score; // 评分 86 | } HashEntry; 87 | ``` 88 | 89 | ### 对战程序主体架构的实现 90 | 91 | ```cpp 92 | int main() 93 | { 94 | char tag[10] = { 0 }; 95 | while (true) { 96 | memset(tag, 0, sizeof(tag)); 97 | scanf("%s", tag); 98 | if (!strcmp(tag, "START")) { // Start 99 | start(); 100 | } 101 | else if (!strcmp(tag, "PLACE")) { // Place 102 | place(); 103 | } 104 | else if (!strcmp(tag, "TURN")) { // Turn 105 | #if DEBUG_MODE 106 | clock_t startTime = clock(); 107 | #endif // DEBUG_MODE 108 | turn(); 109 | #if DEBUG_MODE 110 | printf("DEBUG [Time = %ldms]\n", clock() - startTime); 111 | fflush(stdout); 112 | #endif // DEBUG_MODE 113 | } 114 | else if (!strcmp(tag, "END")) { // End 115 | int status; 116 | scanf("%d", &status); 117 | return 0; 118 | } 119 | } 120 | } 121 | ``` 122 | 123 | ### 调试模式的实现 124 | 125 | ```cpp 126 | #define DEBUG_MODE 0 // 调试模式开关 127 | 128 | #if DEBUG_MODE 129 | void printBoard(void) { ... } 130 | #endif // DEBUG_MODE 131 | 132 | #if DEBUG_MODE 133 | void printScore(void) { ... } 134 | #endif // DEBUG_MODE 135 | 136 | #if DEBUG_MODE 137 | printScore(); 138 | printBoard(); 139 | printf("DEBUG [Depth = %d, NextX = %d, NextY = %d, Score = %d]\n", SEARCH_DEPTH, nextX, nextY, score); 140 | fflush(stdout); 141 | #endif // DEBUG_MODE 142 | 143 | #if DEBUG_MODE 144 | clock_t startTime = clock(); 145 | #endif // DEBUG_MODE 146 | 147 | #if DEBUG_MODE 148 | printf("DEBUG [Time = %ldms]\n", clock() - startTime); 149 | fflush(stdout); 150 | #endif // DEBUG_MODE 151 | ``` 152 | 153 | ### Zobrist置换表 154 | 155 | ```cpp 156 | ULL g_zobristTable[BOARD_SIZE][BOARD_SIZE][3]; // Zobrist 数 157 | ULL g_currentZobristHash; // 当前 Zobrist 哈希值 158 | std::unordered_map g_transTable; // Zobrist 置换表 159 | 160 | void start(void) 161 | { 162 | ... 163 | /* Initialize zobrist hashing table */ 164 | std::random_device g_randomDevice; // Non-deterministic random number generator 165 | std::mt19937_64 g_rng(g_randomDevice()); // Random number generator based on the Mersenne Twister algorithm 166 | std::uniform_int_distribution g_uniDist; // Uniform distribution for generating 64-bit unsigned integers 167 | for (int i = 0; i < BOARD_SIZE; i++) { 168 | for (int j = 0; j < BOARD_SIZE; j++) { 169 | for (int k = 0; k < 3; k++) { 170 | g_zobristTable[i][j][k] = g_uniDist(g_rng); 171 | } 172 | } 173 | } 174 | g_currentZobristHash = 0; 175 | ... 176 | } 177 | 178 | inline void updateZobristHash(const int x, const int y, const Status flag) 179 | { 180 | g_currentZobristHash ^= g_zobristTable[x][y][flag]; 181 | } 182 | ``` 183 | 184 | ### 状态评估函数的实现 185 | 186 | ```cpp 187 | const int MAX_SCORE = 1000000000; // 最高分数 188 | const int MIN_SCORE = -1000000000; // 最低分数 189 | const int FIVE_LINE = 10000000; // 五连分数 190 | const int LIVE_FOUR = 100000; // 活四分数 191 | const int BLOCK_FOUR = 1000; // 冲四分数 192 | const int LIVE_THREE = 1000; // 活三分数 193 | const int BLOCK_THREE = 100; // 冲三分数 194 | const int LIVE_TWO = 100; // 活二分数 195 | const int BLOCK_TWO = 10; // 冲二分数 196 | const int LIVE_ONE = 10; // 活一分数 197 | const int BLOCK_ONE = 1; // 冲一分数 198 | 199 | int evaluateState(const int count, const int block, const int empty) 200 | { 201 | if (empty <= 0) { 202 | if (count >= 5) { 203 | return FIVE_LINE; // 五连 204 | } 205 | if (block == 0) { 206 | switch (count) { 207 | case 1: return LIVE_ONE; // 活一 208 | case 2: return LIVE_TWO; // 活二 209 | case 3: return LIVE_THREE; // 活三 210 | case 4: return LIVE_FOUR; // 活四 211 | default: break; 212 | } 213 | } 214 | if (block == 1) { 215 | switch (count) { 216 | case 1: return BLOCK_ONE; // 冲一 217 | case 2: return BLOCK_TWO; // 冲二 218 | case 3: return BLOCK_THREE; // 冲三 219 | case 4: return BLOCK_FOUR; // 冲四 220 | default: break; 221 | } 222 | } 223 | } // end of if (empty <= 0) 224 | else if (empty == 1 || empty == count - 1) { 225 | if (count >= 6) { 226 | return FIVE_LINE; // 五连 227 | } 228 | if (block == 0) { 229 | switch (count) { 230 | case 2: return LIVE_TWO; // 活二 231 | case 3: return LIVE_THREE; // 活三 232 | case 4: return BLOCK_FOUR; // 冲四 233 | case 5: return LIVE_FOUR; // 活四 234 | default: break; 235 | } 236 | } 237 | if (block == 1) { 238 | switch (count) { 239 | case 2: return BLOCK_TWO; // 冲二 240 | case 3: return BLOCK_THREE; // 冲三 241 | case 4: return BLOCK_FOUR; // 冲四 242 | case 5: return BLOCK_FOUR; // 冲四 243 | default: break; 244 | } 245 | } 246 | } // end of else if (empty == 1 || empty == count - 1) 247 | else if (empty == 2 || empty == count - 2) { 248 | if (count >= 7) { 249 | return FIVE_LINE; // 五连 250 | } 251 | if (block == 0) { 252 | switch (count) { 253 | case 3: return LIVE_THREE; // 活三 254 | case 4: return BLOCK_FOUR; // 冲四 255 | case 5: return BLOCK_FOUR; // 冲四 256 | case 6: return LIVE_FOUR; // 活四 257 | default: break; 258 | } 259 | } 260 | if (block == 1) { 261 | switch (count) { 262 | case 3: return BLOCK_THREE; // 冲三 263 | case 4: return BLOCK_FOUR; // 冲四 264 | case 5: return BLOCK_FOUR; // 冲四 265 | case 6: return LIVE_FOUR; // 活四 266 | default: break; 267 | } 268 | } 269 | if (block == 2) { 270 | switch (count) { 271 | case 4: return BLOCK_FOUR; // 冲四 272 | case 5: return BLOCK_FOUR; // 冲四 273 | case 6: return BLOCK_FOUR; // 冲四 274 | default: break; 275 | } 276 | } 277 | } // end of else if (empty == 2 || empty == count - 2) 278 | else if (empty == 3 || empty == count - 3) { 279 | if (count >= 8) { 280 | return FIVE_LINE; // 五连 281 | } 282 | if (block == 0) { 283 | switch (count) { 284 | case 4: return LIVE_THREE; // 活三 285 | case 5: return LIVE_THREE; // 活三 286 | case 6: return BLOCK_FOUR; // 冲四 287 | case 7: return LIVE_FOUR; // 活四 288 | default: break; 289 | } 290 | } 291 | if (block == 1) { 292 | switch (count) { 293 | case 4: return BLOCK_FOUR; // 冲四 294 | case 5: return BLOCK_FOUR; // 冲四 295 | case 6: return BLOCK_FOUR; // 冲四 296 | case 7: return LIVE_FOUR; // 活四 297 | default: break; 298 | } 299 | } 300 | if (block == 2) { 301 | switch (count) { 302 | case 4: return BLOCK_FOUR; // 冲四 303 | case 5: return BLOCK_FOUR; // 冲四 304 | case 6: return BLOCK_FOUR; // 冲四 305 | case 7: return BLOCK_FOUR; // 冲四 306 | default: break; 307 | } 308 | } 309 | } // end of else if (empty == 3 || empty == count - 3) 310 | else if (empty == 4 || empty == count - 4) { 311 | if (count >= 9) { 312 | return FIVE_LINE; // 五连 313 | } 314 | if (block == 0) { 315 | switch (count) { 316 | case 5: return LIVE_FOUR; // 活四 317 | case 6: return LIVE_FOUR; // 活四 318 | case 7: return LIVE_FOUR; // 活四 319 | case 8: return LIVE_FOUR; // 活四 320 | default: break; 321 | } 322 | } 323 | if (block == 1) { 324 | switch (count) { 325 | case 4: return BLOCK_FOUR; // 冲四 326 | case 5: return BLOCK_FOUR; // 冲四 327 | case 6: return BLOCK_FOUR; // 冲四 328 | case 7: return BLOCK_FOUR; // 冲四 329 | case 8: return LIVE_FOUR; // 活四 330 | default: break; 331 | } 332 | } 333 | if (block == 2) { 334 | switch (count) { 335 | case 5: return BLOCK_FOUR; // 冲四 336 | case 6: return BLOCK_FOUR; // 冲四 337 | case 7: return BLOCK_FOUR; // 冲四 338 | case 8: return BLOCK_FOUR; // 冲四 339 | default: break; 340 | } 341 | } 342 | } // end of else if (empty == 4 || empty == count - 4) 343 | else if (empty == 5 || empty == count - 5) { 344 | return FIVE_LINE; // 五连 345 | } // end of else if (empty == 5 || empty == count - 5) 346 | return 0; // 缺省 347 | } 348 | ``` 349 | 350 | ### 位置评估函数的实现 351 | 352 | ```cpp 353 | int evaluatePoint(const int x, const int y, const Status flag) 354 | { 355 | /* Horizontal direction ( - ) */ 356 | int score = 0, count = 1, block = 0, empty = -1; 357 | for (int i = y + 1; true; i++) { 358 | if (i >= BOARD_SIZE) { 359 | block++; 360 | break; 361 | } 362 | if (g_board[x][i] == Empty) { 363 | if (empty == -1 && i < BOARD_SIZE - 1 && g_board[x][i + 1] == flag) { 364 | empty = count; 365 | continue; 366 | } 367 | else { 368 | break; 369 | } 370 | } 371 | else if (g_board[x][i] == flag) { 372 | count++; 373 | continue; 374 | } 375 | else { 376 | block++; 377 | break; 378 | } 379 | } // end of for 380 | for (int i = y - 1; true; i--) { 381 | if (i < 0) { 382 | block++; 383 | break; 384 | } 385 | if (g_board[x][i] == Empty) { 386 | if (empty == -1 && i > 0 && g_board[x][i - 1] == flag) { 387 | empty = 0; 388 | continue; 389 | } 390 | else { 391 | break; 392 | } 393 | } 394 | else if (g_board[x][i] == flag) { 395 | count++; 396 | if (empty != -1) { 397 | empty++; 398 | } 399 | continue; 400 | } 401 | else { 402 | block++; 403 | break; 404 | } 405 | } // end of for 406 | score += evaluateState(count, block, empty); 407 | 408 | /* Vertical direction ( | ) */ 409 | count = 1; 410 | block = 0; 411 | empty = -1; 412 | for (int i = x + 1; true; i++) { 413 | if (i >= BOARD_SIZE) { 414 | block++; 415 | break; 416 | } 417 | if (g_board[i][y] == Empty) { 418 | if (empty == -1 && i < BOARD_SIZE - 1 && g_board[i + 1][y] == flag) { 419 | empty = count; 420 | continue; 421 | } 422 | else { 423 | break; 424 | } 425 | } 426 | else if (g_board[i][y] == flag) { 427 | count++; 428 | continue; 429 | } 430 | else { 431 | block++; 432 | break; 433 | } 434 | } // end of for 435 | for (int i = x - 1; true; i--) { 436 | if (i < 0) { 437 | block++; 438 | break; 439 | } 440 | if (g_board[i][y] == Empty) { 441 | if (empty == -1 && i > 0 && g_board[i - 1][y] == flag) { 442 | empty = 0; 443 | continue; 444 | } 445 | else { 446 | break; 447 | } 448 | } 449 | else if (g_board[i][y] == flag) { 450 | count++; 451 | if (empty != -1) { 452 | empty++; 453 | } 454 | continue; 455 | } 456 | else { 457 | block++; 458 | break; 459 | } 460 | } // end of for 461 | score += evaluateState(count, block, empty); 462 | 463 | /* Main diagonal direction ( \ ) */ 464 | count = 1; 465 | block = 0; 466 | empty = -1; 467 | for (int i = 1; true; i++) { 468 | int X = x + i; 469 | int Y = y + i; 470 | if (X >= BOARD_SIZE || Y >= BOARD_SIZE) { 471 | block++; 472 | break; 473 | } 474 | if (g_board[X][Y] == Empty) { 475 | if (empty == -1 && (X < BOARD_SIZE - 1 && Y < BOARD_SIZE - 1) && g_board[X + 1][Y + 1] == flag) { 476 | empty = count; 477 | continue; 478 | } 479 | else { 480 | break; 481 | } 482 | } 483 | else if (g_board[X][Y] == flag) { 484 | count++; 485 | continue; 486 | } 487 | else { 488 | block++; 489 | break; 490 | } 491 | } // end of for 492 | for (int i = 1; true; i++) { 493 | int X = x - i; 494 | int Y = y - i; 495 | if (X < 0 || Y < 0) { 496 | block++; 497 | break; 498 | } 499 | if (g_board[X][Y] == Empty) { 500 | if (empty == -1 && (X > 0 && Y > 0) && g_board[X - 1][Y - 1] == flag) { 501 | empty = 0; 502 | continue; 503 | } 504 | else { 505 | break; 506 | } 507 | } 508 | else if (g_board[X][Y] == flag) { 509 | count++; 510 | if (empty != -1) { 511 | empty++; 512 | } 513 | continue; 514 | } 515 | else { 516 | block++; 517 | break; 518 | } 519 | } // end of for 520 | score += evaluateState(count, block, empty); 521 | 522 | /* Minor diagonal direction ( / ) */ 523 | count = 1; 524 | block = 0; 525 | empty = -1; 526 | for (int i = 1; true; i++) { 527 | int X = x + i; 528 | int Y = y - i; 529 | if (X < 0 || Y < 0 || X >= BOARD_SIZE || Y >= BOARD_SIZE) { 530 | block++; 531 | break; 532 | } 533 | if (g_board[X][Y] == Empty) { 534 | if (empty == -1 && (X < BOARD_SIZE - 1 && Y > 0) && g_board[X + 1][Y - 1] == flag) { 535 | empty = count; 536 | continue; 537 | } 538 | else { 539 | break; 540 | } 541 | } 542 | else if (g_board[X][Y] == flag) { 543 | count++; 544 | continue; 545 | } 546 | else { 547 | block++; 548 | break; 549 | } 550 | } // end of for 551 | for (int i = 1; true; i++) { 552 | int X = x - i; 553 | int Y = y + i; 554 | if (X < 0 || Y < 0 || X >= BOARD_SIZE || Y >= BOARD_SIZE) { 555 | block++; 556 | break; 557 | } 558 | if (g_board[X][Y] == Empty) { 559 | if (empty == -1 && (X > 0 && Y < BOARD_SIZE - 1) && g_board[X - 1][Y + 1] == flag) { 560 | empty = 0; 561 | continue; 562 | } 563 | else { 564 | break; 565 | } 566 | } 567 | else if (g_board[X][Y] == flag) { 568 | count++; 569 | if (empty == -1) { 570 | empty++; 571 | } 572 | continue; 573 | } 574 | else { 575 | block++; 576 | break; 577 | } 578 | } // end of for 579 | score += evaluateState(count, block, empty); 580 | return score; 581 | } 582 | ``` 583 | 584 | ### 局面评估函数的实现 585 | 586 | ```cpp 587 | int evaluateBoard(void) 588 | { 589 | int result = 0; 590 | for (int i = 0; i < BOARD_SIZE; i++) { 591 | for (int j = 0; j < BOARD_SIZE; j++) { 592 | if (g_board[i][j] == g_myFlag) { 593 | result += g_myScore[i][j]; // Add our side's score 594 | } 595 | else if (g_board[i][j] == g_enemyFlag) { 596 | result -= g_enemyScore[i][j]; // Subtract opponent's score 597 | } 598 | } 599 | } 600 | return result; 601 | } 602 | ``` 603 | 604 | ### 评估函数的局部更新 605 | 606 | ```cpp 607 | void updateScore(const int x, const int y) 608 | { 609 | /* Update the current point score */ 610 | if (g_board[x][y] == g_myFlag) { 611 | g_myScore[x][y] = evaluatePoint(x, y, g_myFlag); 612 | } 613 | else if (g_board[x][y] == g_enemyFlag) { 614 | g_enemyScore[x][y] = evaluatePoint(x, y, g_enemyFlag); 615 | } 616 | else { 617 | g_myScore[x][y] = 0; 618 | g_enemyScore[x][y] = 0; 619 | } 620 | 621 | /* Update the scores of points in eight directions */ 622 | for (int i = 0; i < 8; i++) { 623 | int newX = x, newY = y; 624 | while (true) { 625 | newX += OFFSET_X[i]; 626 | newY += OFFSET_Y[i]; 627 | if (!isInBound(newX, newY)) { 628 | break; 629 | } 630 | if (g_board[newX][newY] == g_myFlag) { 631 | g_myScore[newX][newY] = evaluatePoint(newX, newY, g_myFlag); 632 | } 633 | else if (g_board[newX][newY] == g_enemyFlag) { 634 | g_enemyScore[newX][newY] = evaluatePoint(newX, newY, g_enemyFlag); 635 | } 636 | } 637 | } 638 | } 639 | ``` 640 | 641 | ### 启发式搜索 642 | 643 | ```cpp 644 | void generateMoves(Move moves[], int& movesLength, const Status flag) 645 | { 646 | /* Find candidate points */ 647 | movesLength = 0; 648 | for (int i = 0; i < BOARD_SIZE; i++) { 649 | for (int j = 0; j < BOARD_SIZE; j++) { 650 | if (g_board[i][j] == Empty && hasNeighbor(i, j)) { 651 | moves[movesLength].x = i; 652 | moves[movesLength].y = j; 653 | moves[movesLength].score = evaluatePoint(i, j, flag); 654 | movesLength++; 655 | } 656 | } 657 | } 658 | 659 | /* Heuristic search */ 660 | std::sort(moves, moves + movesLength, [](const Move& a, const Move& b) { 661 | return a.score > b.score; 662 | }); 663 | } 664 | ``` 665 | 666 | ### Negamax算法和Alpha-Beta剪枝算法 667 | 668 | ```cpp 669 | int negamaxAlphaBeta(const int depth, int alpha, const int beta, const Status flag, int& bestX, int& bestY, bool top = false) 670 | { 671 | /* Check transposition table */ 672 | std::unordered_map::iterator it = g_transTable.find(g_currentZobristHash); 673 | if (it != g_transTable.end() && it->second.depth >= depth) { 674 | return it->second.score; 675 | } 676 | 677 | /* Evaluate g_board */ 678 | if (depth == 0) { 679 | int score = -evaluateBoard(); 680 | g_transTable[g_currentZobristHash] = { depth, score }; 681 | return score; 682 | } 683 | 684 | /* Heuristic Search */ 685 | int score = 0, bestLocalX = INVALID_COORD, bestLocalY = INVALID_COORD, movesLength = 0; 686 | Move nextMoves[BOARD_SIZE * BOARD_SIZE]; 687 | generateMoves(nextMoves, movesLength, flag); 688 | 689 | /* Traverse candidate points */ 690 | for (int k = 0; k < movesLength; k++) { 691 | int i = nextMoves[k].x, j = nextMoves[k].y; 692 | makeMove(i, j, flag); // Make a move 693 | score = -negamaxAlphaBeta(depth - 1, -beta, -alpha, static_cast(3 - flag), bestX, bestY); 694 | makeMove(i, j); // Undo a move 695 | if (score >= beta) { 696 | return beta; // Beta cutoff 697 | } 698 | if (score > alpha) { 699 | alpha = score; // Update alpha 700 | bestLocalX = i; 701 | bestLocalY = j; 702 | } 703 | } 704 | 705 | /* Update best move */ 706 | if (top && bestLocalX != INVALID_COORD && bestLocalY != INVALID_COORD) { 707 | bestX = bestLocalX; 708 | bestY = bestLocalY; 709 | } 710 | 711 | /* Return the best score */ 712 | return alpha; 713 | } 714 | ``` 715 | 716 | ## 项目构建命令 717 | 718 | ``` 719 | mkdir build 720 | cd build 721 | cmake .. 722 | ``` 723 | 724 | ## 编译运行环境 725 | 726 | * 本项目适用于x86和x64架构 727 | 728 | ## 文档更新日期 729 | 730 | 2024年3月10日 -------------------------------------------------------------------------------- /Gomoku/gomoku.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * Project Name: Gomoku 3 | * File Name: gomoku.cpp 4 | * File Function: 五子棋AI对战程序 5 | * Author: Jishen Lin (林继申) 6 | * Update Date: 2023/11/21 7 | ****************************************************************/ 8 | 9 | #define _CRT_SECURE_NO_WARNINGS 10 | 11 | #include // C++ 随机数生成器和分布 12 | #include // C++ 标准模板库(泛型算法组件) 13 | #include // C++ 标准模板库(哈希表关联容器) 14 | #include // C 语言标准输入输出库 15 | #include // C 标准通用工具库 16 | #include // C 风格字符串处理函数库 17 | #include // C 日期和时间处理函数库 18 | 19 | /* Namespace */ 20 | using ULL = unsigned long long; // 64 位无符号整数类型 21 | 22 | /* Conditional compilation */ 23 | #define DEBUG_MODE 0 // 调试模式开关 24 | 25 | /* Define Status type */ 26 | typedef enum { 27 | Empty, // 空位标志 28 | Black, // 黑棋标志 29 | White // 白棋标志 30 | } Status; 31 | 32 | /* Define Move type */ 33 | typedef struct { 34 | int x; // 行数 35 | int y; // 列数 36 | int score; // 评分 37 | } Move; 38 | 39 | /* Define HashEntry type */ 40 | typedef struct { 41 | int depth; // 深度 42 | int score; // 评分 43 | } HashEntry; 44 | 45 | /* Define constant variables */ 46 | const int OFFSET_X[] = { -1,-1,-1,0,0,1,1,1 }; // X 方向偏移量 47 | const int OFFSET_Y[] = { -1,0,1,-1,1,-1,0,1 }; // Y 方向偏移量 48 | const int BOARD_SIZE = 12; // 棋盘大小 49 | const int INVALID_COORD = -1; // 无效坐标 50 | const int MAX_SCORE = 1000000000; // 最高分数 51 | const int MIN_SCORE = -1000000000; // 最低分数 52 | const int FIVE_LINE = 10000000; // 五连分数 53 | const int LIVE_FOUR = 100000; // 活四分数 54 | const int BLOCK_FOUR = 1000; // 冲四分数 55 | const int LIVE_THREE = 1000; // 活三分数 56 | const int BLOCK_THREE = 100; // 冲三分数 57 | const int LIVE_TWO = 100; // 活二分数 58 | const int BLOCK_TWO = 10; // 冲二分数 59 | const int LIVE_ONE = 10; // 活一分数 60 | const int BLOCK_ONE = 1; // 冲一分数 61 | const int SEARCH_DEPTH = 3; // 搜索深度 62 | 63 | /* Define global variables */ 64 | Status g_myFlag; // 我方棋子标志 65 | Status g_enemyFlag; // 对方棋子标志 66 | Status g_board[BOARD_SIZE][BOARD_SIZE]; // 棋盘信息 67 | int g_myScore[BOARD_SIZE][BOARD_SIZE]; // 我方分数 68 | int g_enemyScore[BOARD_SIZE][BOARD_SIZE]; // 对方分数 69 | int g_stepCount; // 步数统计 70 | ULL g_zobristTable[BOARD_SIZE][BOARD_SIZE][3]; // Zobrist 数 71 | ULL g_currentZobristHash; // 当前 Zobrist 哈希值 72 | std::unordered_map g_transTable; // Zobrist 置换表 73 | 74 | #if DEBUG_MODE 75 | /* 76 | * Function Name: printBoard 77 | * Function: Print board used for debugging 78 | * Input Parameters: void 79 | * Return Value: void 80 | */ 81 | void printBoard(void) 82 | { 83 | printf(" "); 84 | fflush(stdout); 85 | for (int col = 0; col < BOARD_SIZE; col++) { 86 | printf("%2d ", col); 87 | fflush(stdout); 88 | } 89 | printf("\n"); 90 | fflush(stdout); 91 | for (int i = 0; i < BOARD_SIZE; i++) { 92 | printf(" +"); 93 | fflush(stdout); 94 | for (int j = 0; j < BOARD_SIZE; j++) { 95 | printf("--+"); 96 | fflush(stdout); 97 | } 98 | printf("\n%2d ", i); 99 | fflush(stdout); 100 | for (int j = 0; j < BOARD_SIZE; j++) { 101 | if (g_board[i][j] == White) { 102 | printf("|●"); 103 | fflush(stdout); 104 | } 105 | else if (g_board[i][j] == Black) { 106 | printf("|○"); 107 | fflush(stdout); 108 | } 109 | else { 110 | printf("| "); 111 | fflush(stdout); 112 | } 113 | } 114 | printf("|\n"); 115 | fflush(stdout); 116 | } 117 | printf(" +"); 118 | fflush(stdout); 119 | for (int j = 0; j < BOARD_SIZE; j++) { 120 | printf("--+"); 121 | fflush(stdout); 122 | } 123 | printf("\n"); 124 | fflush(stdout); 125 | } 126 | #endif // DEBUG_MODE 127 | 128 | #if DEBUG_MODE 129 | /* 130 | * Function Name: printScore 131 | * Function: Print score used for debugging 132 | * Input Parameters: void 133 | * Return Value: void 134 | */ 135 | void printScore(void) 136 | { 137 | printf(" "); 138 | fflush(stdout); 139 | for (int col = 0; col < BOARD_SIZE; col++) { 140 | printf("%8d ", col); 141 | fflush(stdout); 142 | } 143 | printf("\n"); 144 | fflush(stdout); 145 | for (int i = 0; i < BOARD_SIZE; i++) { 146 | printf(" +"); 147 | fflush(stdout); 148 | for (int j = 0; j < BOARD_SIZE; j++) { 149 | printf("--------+"); 150 | fflush(stdout); 151 | } 152 | printf("\n%2d ", i); 153 | fflush(stdout); 154 | for (int j = 0; j < BOARD_SIZE; j++) { 155 | printf("|"); 156 | fflush(stdout); 157 | if (g_board[i][j] == g_myFlag) { 158 | printf(g_myFlag == Black ? "○" : "●"); 159 | printf("%6d", g_myScore[i][j]); 160 | fflush(stdout); 161 | } 162 | else if (g_board[i][j] == g_enemyFlag) { 163 | printf(g_enemyFlag == Black ? "○" : "●"); 164 | printf("%6d", -g_enemyScore[i][j]); 165 | fflush(stdout); 166 | } 167 | else { 168 | printf(" "); 169 | fflush(stdout); 170 | } 171 | } 172 | printf("|\n"); 173 | fflush(stdout); 174 | } 175 | printf(" +"); 176 | fflush(stdout); 177 | for (int j = 0; j < BOARD_SIZE; j++) { 178 | printf("--------+"); 179 | fflush(stdout); 180 | } 181 | printf("\n"); 182 | fflush(stdout); 183 | } 184 | #endif // DEBUG_MODE 185 | 186 | /* 187 | * Function Name: isInBound 188 | * Function: Determine if the point is within the chessboard 189 | * Input Parameters: const int x 190 | * const int y 191 | * Return Value: true / false 192 | */ 193 | inline bool isInBound(const int x, const int y) 194 | { 195 | return (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE); 196 | } 197 | 198 | /* 199 | * Function Name: updateZobristHash 200 | * Function: Update zobrist hash 201 | * Input Parameters: const int x 202 | * const int y 203 | * const Status flag 204 | * Return Value: void 205 | */ 206 | inline void updateZobristHash(const int x, const int y, const Status flag) 207 | { 208 | g_currentZobristHash ^= g_zobristTable[x][y][flag]; 209 | } 210 | 211 | /* 212 | * Function Name: hasNeighbor 213 | * Function: Determine whether there are other chess pieces around a certain point 214 | * Input Parameters: const int x 215 | * const int y 216 | * const int radius 217 | * int count 218 | * Return Value: true / false 219 | */ 220 | bool hasNeighbor(const int x, const int y, const int radius = 1, int count = 1) 221 | { 222 | /* Determine the scope of the check */ 223 | int startRow = std::max(0, x - radius); 224 | int endRow = std::min(BOARD_SIZE - 1, x + radius); 225 | int startCol = std::max(0, y - radius); 226 | int endCol = std::min(BOARD_SIZE - 1, y + radius); 227 | 228 | /* Determine whether there are other chess pieces around a certain point */ 229 | for (int i = startRow; i <= endRow; i++) { 230 | for (int j = startCol; j <= endCol; j++) { 231 | if (i == x && j == y) { 232 | continue; // Skip the current point 233 | } 234 | if (g_board[i][j] != Empty) { 235 | if (--count <= 0) { // Decrease the count for each neighbor found 236 | return true; 237 | } 238 | } 239 | } 240 | } 241 | return false; 242 | } 243 | 244 | /* 245 | * Function Name: evaluateState 246 | * Function: Evaluate the state of a specific point 247 | * Input Parameters: const int count 248 | * const int block 249 | * const int empty 250 | * Return Value: score 251 | */ 252 | int evaluateState(const int count, const int block, const int empty) 253 | { 254 | if (empty <= 0) { 255 | if (count >= 5) { 256 | return FIVE_LINE; // 五连 257 | } 258 | if (block == 0) { 259 | switch (count) { 260 | case 1: return LIVE_ONE; // 活一 261 | case 2: return LIVE_TWO; // 活二 262 | case 3: return LIVE_THREE; // 活三 263 | case 4: return LIVE_FOUR; // 活四 264 | default: break; 265 | } 266 | } 267 | if (block == 1) { 268 | switch (count) { 269 | case 1: return BLOCK_ONE; // 冲一 270 | case 2: return BLOCK_TWO; // 冲二 271 | case 3: return BLOCK_THREE; // 冲三 272 | case 4: return BLOCK_FOUR; // 冲四 273 | default: break; 274 | } 275 | } 276 | } // end of if (empty <= 0) 277 | else if (empty == 1 || empty == count - 1) { 278 | if (count >= 6) { 279 | return FIVE_LINE; // 五连 280 | } 281 | if (block == 0) { 282 | switch (count) { 283 | case 2: return LIVE_TWO; // 活二 284 | case 3: return LIVE_THREE; // 活三 285 | case 4: return BLOCK_FOUR; // 冲四 286 | case 5: return LIVE_FOUR; // 活四 287 | default: break; 288 | } 289 | } 290 | if (block == 1) { 291 | switch (count) { 292 | case 2: return BLOCK_TWO; // 冲二 293 | case 3: return BLOCK_THREE; // 冲三 294 | case 4: return BLOCK_FOUR; // 冲四 295 | case 5: return BLOCK_FOUR; // 冲四 296 | default: break; 297 | } 298 | } 299 | } // end of else if (empty == 1 || empty == count - 1) 300 | else if (empty == 2 || empty == count - 2) { 301 | if (count >= 7) { 302 | return FIVE_LINE; // 五连 303 | } 304 | if (block == 0) { 305 | switch (count) { 306 | case 3: return LIVE_THREE; // 活三 307 | case 4: return BLOCK_FOUR; // 冲四 308 | case 5: return BLOCK_FOUR; // 冲四 309 | case 6: return LIVE_FOUR; // 活四 310 | default: break; 311 | } 312 | } 313 | if (block == 1) { 314 | switch (count) { 315 | case 3: return BLOCK_THREE; // 冲三 316 | case 4: return BLOCK_FOUR; // 冲四 317 | case 5: return BLOCK_FOUR; // 冲四 318 | case 6: return LIVE_FOUR; // 活四 319 | default: break; 320 | } 321 | } 322 | if (block == 2) { 323 | switch (count) { 324 | case 4: return BLOCK_FOUR; // 冲四 325 | case 5: return BLOCK_FOUR; // 冲四 326 | case 6: return BLOCK_FOUR; // 冲四 327 | default: break; 328 | } 329 | } 330 | } // end of else if (empty == 2 || empty == count - 2) 331 | else if (empty == 3 || empty == count - 3) { 332 | if (count >= 8) { 333 | return FIVE_LINE; // 五连 334 | } 335 | if (block == 0) { 336 | switch (count) { 337 | case 4: return LIVE_THREE; // 活三 338 | case 5: return LIVE_THREE; // 活三 339 | case 6: return BLOCK_FOUR; // 冲四 340 | case 7: return LIVE_FOUR; // 活四 341 | default: break; 342 | } 343 | } 344 | if (block == 1) { 345 | switch (count) { 346 | case 4: return BLOCK_FOUR; // 冲四 347 | case 5: return BLOCK_FOUR; // 冲四 348 | case 6: return BLOCK_FOUR; // 冲四 349 | case 7: return LIVE_FOUR; // 活四 350 | default: break; 351 | } 352 | } 353 | if (block == 2) { 354 | switch (count) { 355 | case 4: return BLOCK_FOUR; // 冲四 356 | case 5: return BLOCK_FOUR; // 冲四 357 | case 6: return BLOCK_FOUR; // 冲四 358 | case 7: return BLOCK_FOUR; // 冲四 359 | default: break; 360 | } 361 | } 362 | } // end of else if (empty == 3 || empty == count - 3) 363 | else if (empty == 4 || empty == count - 4) { 364 | if (count >= 9) { 365 | return FIVE_LINE; // 五连 366 | } 367 | if (block == 0) { 368 | switch (count) { 369 | case 5: return LIVE_FOUR; // 活四 370 | case 6: return LIVE_FOUR; // 活四 371 | case 7: return LIVE_FOUR; // 活四 372 | case 8: return LIVE_FOUR; // 活四 373 | default: break; 374 | } 375 | } 376 | if (block == 1) { 377 | switch (count) { 378 | case 4: return BLOCK_FOUR; // 冲四 379 | case 5: return BLOCK_FOUR; // 冲四 380 | case 6: return BLOCK_FOUR; // 冲四 381 | case 7: return BLOCK_FOUR; // 冲四 382 | case 8: return LIVE_FOUR; // 活四 383 | default: break; 384 | } 385 | } 386 | if (block == 2) { 387 | switch (count) { 388 | case 5: return BLOCK_FOUR; // 冲四 389 | case 6: return BLOCK_FOUR; // 冲四 390 | case 7: return BLOCK_FOUR; // 冲四 391 | case 8: return BLOCK_FOUR; // 冲四 392 | default: break; 393 | } 394 | } 395 | } // end of else if (empty == 4 || empty == count - 4) 396 | else if (empty == 5 || empty == count - 5) { 397 | return FIVE_LINE; // 五连 398 | } // end of else if (empty == 5 || empty == count - 5) 399 | return 0; // 缺省 400 | } 401 | 402 | /* 403 | * Function Name: evaluatePoint 404 | * Function: Evaluate the situation at a specific point 405 | * Input Parameters: const int x 406 | * const int y 407 | * const Status flag 408 | * Return Value: score 409 | */ 410 | int evaluatePoint(const int x, const int y, const Status flag) 411 | { 412 | /* Horizontal direction ( - ) */ 413 | int score = 0, count = 1, block = 0, empty = -1; 414 | for (int i = y + 1; true; i++) { 415 | if (i >= BOARD_SIZE) { 416 | block++; 417 | break; 418 | } 419 | if (g_board[x][i] == Empty) { 420 | if (empty == -1 && i < BOARD_SIZE - 1 && g_board[x][i + 1] == flag) { 421 | empty = count; 422 | continue; 423 | } 424 | else { 425 | break; 426 | } 427 | } 428 | else if (g_board[x][i] == flag) { 429 | count++; 430 | continue; 431 | } 432 | else { 433 | block++; 434 | break; 435 | } 436 | } // end of for 437 | for (int i = y - 1; true; i--) { 438 | if (i < 0) { 439 | block++; 440 | break; 441 | } 442 | if (g_board[x][i] == Empty) { 443 | if (empty == -1 && i > 0 && g_board[x][i - 1] == flag) { 444 | empty = 0; 445 | continue; 446 | } 447 | else { 448 | break; 449 | } 450 | } 451 | else if (g_board[x][i] == flag) { 452 | count++; 453 | if (empty != -1) { 454 | empty++; 455 | } 456 | continue; 457 | } 458 | else { 459 | block++; 460 | break; 461 | } 462 | } // end of for 463 | score += evaluateState(count, block, empty); 464 | 465 | /* Vertical direction ( | ) */ 466 | count = 1; 467 | block = 0; 468 | empty = -1; 469 | for (int i = x + 1; true; i++) { 470 | if (i >= BOARD_SIZE) { 471 | block++; 472 | break; 473 | } 474 | if (g_board[i][y] == Empty) { 475 | if (empty == -1 && i < BOARD_SIZE - 1 && g_board[i + 1][y] == flag) { 476 | empty = count; 477 | continue; 478 | } 479 | else { 480 | break; 481 | } 482 | } 483 | else if (g_board[i][y] == flag) { 484 | count++; 485 | continue; 486 | } 487 | else { 488 | block++; 489 | break; 490 | } 491 | } // end of for 492 | for (int i = x - 1; true; i--) { 493 | if (i < 0) { 494 | block++; 495 | break; 496 | } 497 | if (g_board[i][y] == Empty) { 498 | if (empty == -1 && i > 0 && g_board[i - 1][y] == flag) { 499 | empty = 0; 500 | continue; 501 | } 502 | else { 503 | break; 504 | } 505 | } 506 | else if (g_board[i][y] == flag) { 507 | count++; 508 | if (empty != -1) { 509 | empty++; 510 | } 511 | continue; 512 | } 513 | else { 514 | block++; 515 | break; 516 | } 517 | } // end of for 518 | score += evaluateState(count, block, empty); 519 | 520 | /* Main diagonal direction ( \ ) */ 521 | count = 1; 522 | block = 0; 523 | empty = -1; 524 | for (int i = 1; true; i++) { 525 | int X = x + i; 526 | int Y = y + i; 527 | if (X >= BOARD_SIZE || Y >= BOARD_SIZE) { 528 | block++; 529 | break; 530 | } 531 | if (g_board[X][Y] == Empty) { 532 | if (empty == -1 && (X < BOARD_SIZE - 1 && Y < BOARD_SIZE - 1) && g_board[X + 1][Y + 1] == flag) { 533 | empty = count; 534 | continue; 535 | } 536 | else { 537 | break; 538 | } 539 | } 540 | else if (g_board[X][Y] == flag) { 541 | count++; 542 | continue; 543 | } 544 | else { 545 | block++; 546 | break; 547 | } 548 | } // end of for 549 | for (int i = 1; true; i++) { 550 | int X = x - i; 551 | int Y = y - i; 552 | if (X < 0 || Y < 0) { 553 | block++; 554 | break; 555 | } 556 | if (g_board[X][Y] == Empty) { 557 | if (empty == -1 && (X > 0 && Y > 0) && g_board[X - 1][Y - 1] == flag) { 558 | empty = 0; 559 | continue; 560 | } 561 | else { 562 | break; 563 | } 564 | } 565 | else if (g_board[X][Y] == flag) { 566 | count++; 567 | if (empty != -1) { 568 | empty++; 569 | } 570 | continue; 571 | } 572 | else { 573 | block++; 574 | break; 575 | } 576 | } // end of for 577 | score += evaluateState(count, block, empty); 578 | 579 | /* Minor diagonal direction ( / ) */ 580 | count = 1; 581 | block = 0; 582 | empty = -1; 583 | for (int i = 1; true; i++) { 584 | int X = x + i; 585 | int Y = y - i; 586 | if (X < 0 || Y < 0 || X >= BOARD_SIZE || Y >= BOARD_SIZE) { 587 | block++; 588 | break; 589 | } 590 | if (g_board[X][Y] == Empty) { 591 | if (empty == -1 && (X < BOARD_SIZE - 1 && Y > 0) && g_board[X + 1][Y - 1] == flag) { 592 | empty = count; 593 | continue; 594 | } 595 | else { 596 | break; 597 | } 598 | } 599 | else if (g_board[X][Y] == flag) { 600 | count++; 601 | continue; 602 | } 603 | else { 604 | block++; 605 | break; 606 | } 607 | } // end of for 608 | for (int i = 1; true; i++) { 609 | int X = x - i; 610 | int Y = y + i; 611 | if (X < 0 || Y < 0 || X >= BOARD_SIZE || Y >= BOARD_SIZE) { 612 | block++; 613 | break; 614 | } 615 | if (g_board[X][Y] == Empty) { 616 | if (empty == -1 && (X > 0 && Y < BOARD_SIZE - 1) && g_board[X - 1][Y + 1] == flag) { 617 | empty = 0; 618 | continue; 619 | } 620 | else { 621 | break; 622 | } 623 | } 624 | else if (g_board[X][Y] == flag) { 625 | count++; 626 | if (empty == -1) { 627 | empty++; 628 | } 629 | continue; 630 | } 631 | else { 632 | block++; 633 | break; 634 | } 635 | } // end of for 636 | score += evaluateState(count, block, empty); 637 | return score; 638 | } 639 | 640 | /* 641 | * Function Name: evaluateBoard 642 | * Function: Evaluate the situation of our side 643 | * Input Parameters: void 644 | * Return Value: score 645 | */ 646 | int evaluateBoard(void) 647 | { 648 | int result = 0; 649 | for (int i = 0; i < BOARD_SIZE; i++) { 650 | for (int j = 0; j < BOARD_SIZE; j++) { 651 | if (g_board[i][j] == g_myFlag) { 652 | result += g_myScore[i][j]; // Add our side's score 653 | } 654 | else if (g_board[i][j] == g_enemyFlag) { 655 | result -= g_enemyScore[i][j]; // Subtract opponent's score 656 | } 657 | } 658 | } 659 | return result; 660 | } 661 | 662 | /* 663 | * Function Name: updateScore 664 | * Function: Update the chessboard score 665 | * Input Parameters: const int x 666 | * const int y 667 | * Return Value: void 668 | */ 669 | void updateScore(const int x, const int y) 670 | { 671 | /* Update the current point score */ 672 | if (g_board[x][y] == g_myFlag) { 673 | g_myScore[x][y] = evaluatePoint(x, y, g_myFlag); 674 | } 675 | else if (g_board[x][y] == g_enemyFlag) { 676 | g_enemyScore[x][y] = evaluatePoint(x, y, g_enemyFlag); 677 | } 678 | else { 679 | g_myScore[x][y] = 0; 680 | g_enemyScore[x][y] = 0; 681 | } 682 | 683 | /* Update the scores of points in eight directions */ 684 | for (int i = 0; i < 8; i++) { 685 | int newX = x, newY = y; 686 | while (true) { 687 | newX += OFFSET_X[i]; 688 | newY += OFFSET_Y[i]; 689 | if (!isInBound(newX, newY)) { 690 | break; 691 | } 692 | if (g_board[newX][newY] == g_myFlag) { 693 | g_myScore[newX][newY] = evaluatePoint(newX, newY, g_myFlag); 694 | } 695 | else if (g_board[newX][newY] == g_enemyFlag) { 696 | g_enemyScore[newX][newY] = evaluatePoint(newX, newY, g_enemyFlag); 697 | } 698 | } 699 | } 700 | } 701 | 702 | /* 703 | * Function Name: makeMove 704 | * Function: Make a move 705 | * Input Parameters: const int x 706 | * const int y 707 | * const Status flag 708 | * Return Value: void 709 | */ 710 | void makeMove(const int x, const int y, const Status flag = Empty) 711 | { 712 | updateZobristHash(x, y, g_board[x][y]); 713 | g_board[x][y] = flag; 714 | updateZobristHash(x, y, flag); 715 | updateScore(x, y); 716 | } 717 | 718 | /* 719 | * Function Name: findEmptyToPlace 720 | * Function: Find a empty position to place 721 | * Input Parameters: const Status flag 722 | * int& bestX 723 | * int& bestY 724 | * Return Value: void 725 | */ 726 | void findEmptyToPlace(const Status flag, int& bestX, int& bestY) 727 | { 728 | for (int i = 0; i < BOARD_SIZE; i++) { 729 | for (int j = 0; j < BOARD_SIZE; j++) { 730 | if (g_board[i][j] == Empty) { 731 | bestX = i; 732 | bestY = j; 733 | return; 734 | } 735 | } 736 | } 737 | } 738 | 739 | /* 740 | * Function Name: checkLine 741 | * Function: Check five in a line 742 | * Input Parameters: const int x 743 | * const int y 744 | * const Status flag 745 | * Return Value: true / false 746 | */ 747 | bool checkLine(const int x, const int y, const Status flag) 748 | { 749 | int i, j, count; 750 | for (int dir = 4; dir < 8; dir++) { 751 | count = 1; 752 | i = x + OFFSET_X[dir]; 753 | j = y + OFFSET_Y[dir]; 754 | while (isInBound(i, j) && g_board[i][j] == flag) { 755 | count++; 756 | i += OFFSET_X[dir]; 757 | j += OFFSET_Y[dir]; 758 | } 759 | i = x - OFFSET_X[dir]; 760 | j = y - OFFSET_Y[dir]; 761 | while (isInBound(i, j) && g_board[i][j] == flag) { 762 | count++; 763 | i -= OFFSET_X[dir]; 764 | j -= OFFSET_Y[dir]; 765 | } 766 | if (count >= 5) { 767 | return true; 768 | } 769 | } 770 | return false; 771 | } 772 | 773 | /* 774 | * Function Name: checkLine 775 | * Function: check line 776 | * Input Parameters: const int x 777 | * const int y 778 | * const Status flag 779 | * const int checkCount 780 | * const int checkEmpty 781 | * Return Value: true / false 782 | */ 783 | bool checkLine(const int x, const int y, const Status flag, const int checkCount, const int checkEmpty) 784 | { 785 | int i, j, count, empty; 786 | for (int dir = 4; dir < 8; dir++) { 787 | count = 1; 788 | empty = 0; 789 | i = x + OFFSET_X[dir]; 790 | j = y + OFFSET_Y[dir]; 791 | while (isInBound(i, j)) { 792 | if (g_board[i][j] == flag) { 793 | count++; 794 | } 795 | else if (g_board[i][j] == Empty) { 796 | empty++; 797 | break; 798 | } 799 | else { 800 | break; 801 | } 802 | i += OFFSET_X[dir]; 803 | j += OFFSET_Y[dir]; 804 | } 805 | i = x - OFFSET_X[dir]; 806 | j = y - OFFSET_Y[dir]; 807 | while (isInBound(i, j)) { 808 | if (g_board[i][j] == flag) { 809 | count++; 810 | } 811 | else if (g_board[i][j] == Empty) { 812 | empty++; 813 | break; 814 | } 815 | else { 816 | break; 817 | } 818 | i -= OFFSET_X[dir]; 819 | j -= OFFSET_Y[dir]; 820 | } 821 | if (count == checkCount && empty == checkEmpty) { 822 | return true; 823 | } 824 | } 825 | return false; 826 | } 827 | 828 | /* 829 | * Function Name: generateMoves 830 | * Function: Generate moves with heuristic search 831 | * Input Parameters: Move moves[] 832 | * int& movesLength 833 | * const Status flag 834 | * Return Value: void 835 | */ 836 | void generateMoves(Move moves[], int& movesLength, const Status flag) 837 | { 838 | /* Find candidate points */ 839 | movesLength = 0; 840 | for (int i = 0; i < BOARD_SIZE; i++) { 841 | for (int j = 0; j < BOARD_SIZE; j++) { 842 | if (g_board[i][j] == Empty && hasNeighbor(i, j)) { 843 | moves[movesLength].x = i; 844 | moves[movesLength].y = j; 845 | moves[movesLength].score = evaluatePoint(i, j, flag); 846 | movesLength++; 847 | } 848 | } 849 | } 850 | 851 | /* Heuristic search */ 852 | std::sort(moves, moves + movesLength, [](const Move& a, const Move& b) { 853 | return a.score > b.score; 854 | }); 855 | } 856 | 857 | /* 858 | * Function Name: negamaxAlphaBeta 859 | * Function: Negamax and alpha-beta pruning algorithm with heuristic search 860 | * Input Parameters: const int depth 861 | * int alpha 862 | * const int beta 863 | * const Status flag 864 | * int& bestX 865 | * int& bestY 866 | * bool top 867 | * Return Value: score 868 | */ 869 | int negamaxAlphaBeta(const int depth, int alpha, const int beta, const Status flag, int& bestX, int& bestY, bool top = false) 870 | { 871 | /* Check transposition table */ 872 | std::unordered_map::iterator it = g_transTable.find(g_currentZobristHash); 873 | if (it != g_transTable.end() && it->second.depth >= depth) { 874 | return it->second.score; 875 | } 876 | 877 | /* Evaluate g_board */ 878 | if (depth == 0) { 879 | int score = -evaluateBoard(); 880 | g_transTable[g_currentZobristHash] = { depth, score }; 881 | return score; 882 | } 883 | 884 | /* Heuristic Search */ 885 | int score = 0, bestLocalX = INVALID_COORD, bestLocalY = INVALID_COORD, movesLength = 0; 886 | Move nextMoves[BOARD_SIZE * BOARD_SIZE]; 887 | generateMoves(nextMoves, movesLength, flag); 888 | 889 | /* Traverse candidate points */ 890 | for (int k = 0; k < movesLength; k++) { 891 | int i = nextMoves[k].x, j = nextMoves[k].y; 892 | makeMove(i, j, flag); // Make a move 893 | score = -negamaxAlphaBeta(depth - 1, -beta, -alpha, static_cast(3 - flag), bestX, bestY); 894 | makeMove(i, j); // Undo a move 895 | if (score >= beta) { 896 | return beta; // Beta cutoff 897 | } 898 | if (score > alpha) { 899 | alpha = score; // Update alpha 900 | bestLocalX = i; 901 | bestLocalY = j; 902 | } 903 | } 904 | 905 | /* Update best move */ 906 | if (top && bestLocalX != INVALID_COORD && bestLocalY != INVALID_COORD) { 907 | bestX = bestLocalX; 908 | bestY = bestLocalY; 909 | } 910 | 911 | /* Return the best score */ 912 | return alpha; 913 | } 914 | 915 | /* 916 | * Function Name: tryMove 917 | * Function: Try a move 918 | * Input Parameters: int& nextX 919 | * int& nextY 920 | * int& score 921 | * Return Value: true / false 922 | */ 923 | bool tryMove(int& nextX, int& nextY, int& score) 924 | { 925 | /* First move */ 926 | if (g_stepCount == 4) { 927 | nextX = 4; 928 | nextY = 4; 929 | return false; 930 | } 931 | if (g_stepCount == 5) { 932 | if (g_board[4][7] == Black) { 933 | nextX = 7; 934 | nextY = 4; 935 | return false; 936 | } 937 | if (g_board[7][4] == Black) { 938 | nextX = 4; 939 | nextY = 7; 940 | return false; 941 | } 942 | if (g_board[4][4] == Black) { 943 | nextX = 5; 944 | nextY = 7; 945 | return false; 946 | } 947 | if (g_board[7][7] == Black) { 948 | nextX = 6; 949 | nextY = 4; 950 | return false; 951 | } 952 | } 953 | 954 | /* Check for our winning move (FIVE_LINE) */ 955 | for (int i = 0; i < BOARD_SIZE; i++) { 956 | for (int j = 0; j < BOARD_SIZE; j++) { 957 | if (g_board[i][j] == Empty && checkLine(i, j, g_myFlag)) { 958 | nextX = i; 959 | nextY = j; 960 | return false; 961 | } 962 | } 963 | } 964 | 965 | /* Check for opponent's move (FIVE_LINE) */ 966 | for (int i = 0; i < BOARD_SIZE; i++) { 967 | for (int j = 0; j < BOARD_SIZE; j++) { 968 | if (g_board[i][j] == Empty && checkLine(i, j, g_enemyFlag)) { 969 | nextX = i; 970 | nextY = j; 971 | return false; 972 | } 973 | } 974 | } 975 | 976 | /* Check for our winning move (LIVE_FOUR || (BLOCK_FOUR && LIVE_THREE)) */ 977 | for (int i = 0; i < BOARD_SIZE; i++) { 978 | for (int j = 0; j < BOARD_SIZE; j++) { 979 | if (g_board[i][j] == Empty && (checkLine(i, j, g_myFlag, 4, 2) || (checkLine(i, j, g_myFlag, 4, 1) && checkLine(i, j, g_myFlag, 3, 2)))) { 980 | nextX = i; 981 | nextY = j; 982 | return false; 983 | } 984 | } 985 | } 986 | 987 | /* Check for opponent's move (LIVE_FOUR || (BLOCK_FOUR && LIVE_THREE)) */ 988 | for (int i = 0; i < BOARD_SIZE; i++) { 989 | for (int j = 0; j < BOARD_SIZE; j++) { 990 | if (g_board[i][j] == Empty && (checkLine(i, j, g_enemyFlag, 4, 2) || (checkLine(i, j, g_enemyFlag, 4, 1) && checkLine(i, j, g_enemyFlag, 3, 2)))) { 991 | nextX = i; 992 | nextY = j; 993 | return false; 994 | } 995 | } 996 | } 997 | return true; 998 | } 999 | 1000 | /* 1001 | * Function Name: start 1002 | * Function: Start the game 1003 | * Input Parameters: void 1004 | * Return Value: void 1005 | */ 1006 | void start(void) 1007 | { 1008 | /* Get our side's symbol */ 1009 | scanf("%d", &g_myFlag); 1010 | 1011 | /* Initialization */ 1012 | g_enemyFlag = static_cast(3 - g_myFlag); 1013 | memset(g_board, 0, sizeof(g_board)); 1014 | memset(g_myScore, 0, sizeof(g_myScore)); 1015 | memset(g_enemyScore, 0, sizeof(g_enemyScore)); 1016 | 1017 | /* Initialize zobrist hashing table */ 1018 | std::random_device g_randomDevice; // Non-deterministic random number generator 1019 | std::mt19937_64 g_rng(g_randomDevice()); // Random number generator based on the Mersenne Twister algorithm 1020 | std::uniform_int_distribution g_uniDist; // Uniform distribution for generating 64-bit unsigned integers 1021 | for (int i = 0; i < BOARD_SIZE; i++) { 1022 | for (int j = 0; j < BOARD_SIZE; j++) { 1023 | for (int k = 0; k < 3; k++) { 1024 | g_zobristTable[i][j][k] = g_uniDist(g_rng); 1025 | } 1026 | } 1027 | } 1028 | g_currentZobristHash = 0; 1029 | 1030 | /* Default moves */ 1031 | makeMove(5, 5, White); 1032 | makeMove(6, 6, White); 1033 | makeMove(5, 6, Black); 1034 | makeMove(6, 5, Black); 1035 | g_stepCount = 4; 1036 | 1037 | /* Respond to terminal program */ 1038 | printf("OK\n"); 1039 | fflush(stdout); 1040 | } 1041 | 1042 | /* 1043 | * Function Name: place 1044 | * Function: Opponent makes a move 1045 | * Input Parameters: void 1046 | * Return Value: void 1047 | */ 1048 | void place(void) 1049 | { 1050 | Move command = { 0, 0, 0 }; 1051 | scanf("%d %d", &command.x, &command.y); 1052 | makeMove(command.x, command.y, g_enemyFlag); 1053 | g_stepCount++; 1054 | } 1055 | 1056 | /* 1057 | * Function Name: turn 1058 | * Function: Our side makes a move 1059 | * Input Parameters: void 1060 | * Return Value: void 1061 | */ 1062 | void turn(void) 1063 | { 1064 | int nextX = INVALID_COORD, nextY = INVALID_COORD, score = 0; 1065 | if (tryMove(nextX, nextY, score)) { 1066 | score = negamaxAlphaBeta(SEARCH_DEPTH, MIN_SCORE, MAX_SCORE, g_myFlag, nextX, nextY, true); 1067 | } 1068 | if (nextX == INVALID_COORD || nextY == INVALID_COORD) { 1069 | findEmptyToPlace(g_myFlag, nextX, nextY); 1070 | } 1071 | makeMove(nextX, nextY, g_myFlag); 1072 | printf("%d %d\n", nextX, nextY); 1073 | fflush(stdout); 1074 | g_stepCount++; 1075 | #if DEBUG_MODE 1076 | printScore(); 1077 | printBoard(); 1078 | printf("DEBUG [Depth = %d, NextX = %d, NextY = %d, Score = %d]\n", SEARCH_DEPTH, nextX, nextY, score); 1079 | fflush(stdout); 1080 | #endif // DEBUG_MODE 1081 | } 1082 | 1083 | /* 1084 | * Function Name: main 1085 | * Function: Main function 1086 | * Return Value: 0 1087 | */ 1088 | int main() 1089 | { 1090 | char tag[10] = { 0 }; 1091 | while (true) { 1092 | memset(tag, 0, sizeof(tag)); 1093 | scanf("%s", tag); 1094 | if (!strcmp(tag, "START")) { // Start 1095 | start(); 1096 | } 1097 | else if (!strcmp(tag, "PLACE")) { // Place 1098 | place(); 1099 | } 1100 | else if (!strcmp(tag, "TURN")) { // Turn 1101 | #if DEBUG_MODE 1102 | clock_t startTime = clock(); 1103 | #endif // DEBUG_MODE 1104 | turn(); 1105 | #if DEBUG_MODE 1106 | printf("DEBUG [Time = %ldms]\n", clock() - startTime); 1107 | fflush(stdout); 1108 | #endif // DEBUG_MODE 1109 | } 1110 | else if (!strcmp(tag, "END")) { // End 1111 | int status; 1112 | scanf("%d", &status); 1113 | return 0; 1114 | } 1115 | } 1116 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jishen Lin 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programming Paradigms Course Assignments 2 | 3 | ## 仓库名称 4 | 5 | Programming_Paradigms_Course_Assignments 6 | 7 | ## 仓库简介 8 | 9 | A collection of my programming paradigms course assignments. 10 | 11 | 2023年同济大学程序设计范式课程作业合集。 12 | 13 | > ***Relevant course*** 14 | > * Programing Paradigms 2023 (2023年同济大学程序设计范式) 15 | 16 | ## 仓库组成 17 | 18 | * [Assignment 1-1](Assignment_1_1) 19 | 股票价格的下一次上涨 20 | 21 | * [Assignment 1-2](Assignment_1_2) 22 | 买卖股票的最佳时机 23 | 24 | * [Assignment 1-3](Assignment_1_3) 25 | 买卖股票的最佳时机含冷冻期 26 | 27 | * [Assignment 2-1](Assignment_2_1) 28 | 电影播放时间 29 | 30 | * [Assignment 2-2](Assignment_2_2) 31 | 电影院放映厅需求 32 | 33 | * [Assignment 2-3](Assignment_2_3) 34 | 电影节目合并 35 | 36 | * [Assignment 3-1](Assignment_3_1) 37 | 存在重复元素Ⅰ 38 | 39 | * [Assignment 3-2](Assignment_3_2) 40 | 存在重复元素Ⅱ 41 | 42 | * [Assignment 3-3](Assignment_3_3) 43 | 存在重复元素Ⅲ 44 | 45 | * [Gomoku](Gomoku) 46 | 五子棋AI对战程序 47 | 48 | ## 相关仓库 49 | 50 | * [Teamfight Tactics](https://github.com/MinmusLin/Teamfight_Tactics) 51 | 基于 [Cocos2d-x 3.17.2](https://docs.cocos.com/cocos2d-x/manual) 开发的金铲铲之战游戏项目 52 | 53 | ## 免责声明 54 | 55 | The code and materials contained in this repository are intended for personal learning and research purposes only and may not be used for any commercial purposes. Other users who download or refer to the content of this repository must strictly adhere to the **principles of academic integrity** and must not use these materials for any form of homework submission or other actions that may violate academic honesty. I am not responsible for any direct or indirect consequences arising from the improper use of the contents of this repository. Please ensure that your actions comply with the regulations of your school or institution, as well as applicable laws and regulations, before using this content. If you have any questions, please contact me via [email](mailto:minmuslin@outlook.com). 56 | 57 | 本仓库包含的代码和资料仅用于个人学习和研究目的,不得用于任何商业用途。请其他用户在下载或参考本仓库内容时,严格遵守**学术诚信原则**,不得将这些资料用于任何形式的作业提交或其他可能违反学术诚信的行为。本人对因不恰当使用仓库内容导致的任何直接或间接后果不承担责任。请在使用前务必确保您的行为符合所在学校或机构的规定,以及适用的法律法规。如有任何问题,请通过[电子邮件](mailto:minmuslin@outlook.com)与我联系。 58 | 59 | ## 文档更新日期 60 | 61 | 2024年6月12日 62 | --------------------------------------------------------------------------------