diff --git a/.vscode/launch.json b/.vscode/launch.json index 5f9b49c..23c256b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,27 +1,58 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Run LeetCode Solution", - "type": "python", - "request": "launch", - "program": "${file}", - "console": "integratedTerminal", - "cwd": "${workspaceFolder}" - }, - { - "name": "Run All Tests", - "type": "python", - "request": "launch", - "program": "${workspaceFolder}/run_all_tests.py", - "args": [ - "test-results.xml" - ], - "console": "integratedTerminal", - "cwd": "${workspaceFolder}" - } - ] + "version": "0.2.0", + "inputs": [ + { + "id": "problemNumber", + "description": "Enter the 3-digit problem number to DEBUG:", + "default": "001", + "type": "promptString" + } + ], + "configurations": [ + { + "name": "C++: Run All Tests", + "type": "cppdbg", + "request": "launch", + "program": "C:/Windows/System32/cmd.exe", + "preLaunchTask": "Run All Tests", + "console": "integratedTerminal", + "cwd": "${workspaceFolder}/c++" + }, + { + "name": "C++: Debug Single Problem", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/c++/build/problem_${input:problemNumber}/runner.exe", + "args": [ + "-f", + "problems/problem_${input:problemNumber}/problem_${input:problemNumber}.json" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/c++", + "MIMode": "gdb", + "miDebuggerPath": "C:/msys64/ucrt64/bin/gdb.exe", + // This is the only change: point to the new build task + "preLaunchTask": "C++: Build active problem for debugger", + "console": "integratedTerminal" + }, + { + "name": "Python: Run Active File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "cwd": "${fileDirname}" + }, + { + "name": "Python: Run All Tests", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/run_all_tests.py", + "args": [ + "test-results.xml" + ], + "console": "integratedTerminal", + "cwd": "${workspaceFolder}" + } + ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f597ebc --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,69 @@ +{ + "version": "2.0.0", + "inputs": [ + { + "id": "problemNumber", + "description": "Enter the 3-digit problem number to build:", + "default": "001", + "type": "promptString" + } + ], + "tasks": [ + { + "label": "C++: Build active problem for debugger", + "type": "process", + "command": "C:/msys64/ucrt64/bin/g++.exe", + "args": [ + "-g", + "-std=c++20", + "-Wall", + // --- Include Paths --- + "-IC:/msys64/ucrt64/include", + // CORRECTED: Added "problem_" prefix + "-Iproblems/problem_${input:problemNumber}", + "-Itests_runner", + // --- Source Files --- + "tests_runner/tests_runner.cpp", + // CORRECTED: Added "problem_" prefix + "problems/problem_${input:problemNumber}/solution.cpp", + // --- Output File --- + "-o", + "build/problem_${input:problemNumber}/runner.exe", + // --- Linker Flags --- + "-LC:/msys64/ucrt64/lib", + "-ljsoncpp", + "-static-libstdc++", + "-static-libgcc" + ], + "options": { + "cwd": "${workspaceFolder}/c++" + }, + "problemMatcher": ["$gcc"], + "group": "build" + }, + { + "label": "Run All Tests", + "type": "process", + "command": "C:/msys64/usr/bin/bash.exe", + "args": [ + "-ic", + "cd \"$(cygpath '${workspaceFolder}')/c++\" && make clean && make test-all" + ], + "options": { + "env": { + "PATH": "C:\\msys64\\ucrt64\\bin;C:\\msys64\\usr\\bin;${env:PATH}" + } + }, + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + }, + "problemMatcher": ["$gcc"] + } + ] +} \ No newline at end of file diff --git a/c++/.gitignore b/c++/.gitignore new file mode 100644 index 0000000..adb36c8 --- /dev/null +++ b/c++/.gitignore @@ -0,0 +1 @@ +*.exe \ No newline at end of file diff --git a/c++/Makefile b/c++/Makefile new file mode 100644 index 0000000..8984387 --- /dev/null +++ b/c++/Makefile @@ -0,0 +1,35 @@ +# --- Compiler and Flags --- +CXX := C:/msys64/ucrt64/bin/g++.exe +CXXFLAGS := -O2 -std=c++20 -g -Wall -IC:/msys64/ucrt64/include +# Add the -static-libgcc flag to this line +LDFLAGS := -LC:/msys64/ucrt64/lib -ljsoncpp -static-libstdc++ -static-libgcc + +# --- Project Structure --- +BUILD_DIR := build +TEST_RUNNER_SRC := tests_runner/tests_runner.cpp + +# --- Auto-discovery of Problems --- +PROBLEM_DIRS := $(wildcard problems/problem_*) +PROBLEM_NUMS := $(patsubst problems/problem_%,%,$(PROBLEM_DIRS)) + +# --- Main Targets --- +.PHONY: test-all +test-all: $(addprefix test-, $(PROBLEM_NUMS)) + @echo "" + @echo "✅ All tests completed." + +.PHONY: test-% +test-%: $(BUILD_DIR)/problem_%/runner.exe + @echo "--- Running tests for Problem $* ---" + @$(BUILD_DIR)/problem_$*/runner.exe -f problems/problem_$*/problem_$*.json + +# --- Build Rules --- +$(BUILD_DIR)/problem_%/runner.exe: $(TEST_RUNNER_SRC) problems/problem_%/solution.cpp + @mkdir -p $(@D) + $(CXX) $(CXXFLAGS) -Iproblems/problem_$* -Itests_runner $^ -o $@ $(LDFLAGS) + +# --- Utility Targets --- +.PHONY: clean +clean: + @echo "Cleaning build directory..." + @rm -rf $(BUILD_DIR) \ No newline at end of file diff --git a/c++/hello_world/hello_world.cpp b/c++/hello_world/hello_world.cpp new file mode 100644 index 0000000..244c95d --- /dev/null +++ b/c++/hello_world/hello_world.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +using namespace std; + +int main() +{ + vector msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"}; + + for (const string& word : msg) + { + cout << word << " "; + } + cout << endl; +} \ No newline at end of file diff --git a/c++/problems/problem_001/problem_001.json b/c++/problems/problem_001/problem_001.json new file mode 100644 index 0000000..9765bbd --- /dev/null +++ b/c++/problems/problem_001/problem_001.json @@ -0,0 +1,26 @@ +{ + "method": "twoSum", + "tests": [ + { + "Input": { + "nums": "[2,7,11,15]", + "target": "9" + }, + "Output": "[0,1]" + }, + { + "Input": { + "nums": "[3,2,4]", + "target": "6" + }, + "Output": "[1,2]" + }, + { + "Input": { + "nums": "[3,3]", + "target": "6" + }, + "Output": "[0,1]" + } + ] +} \ No newline at end of file diff --git a/c++/problems/problem_001/solution.cpp b/c++/problems/problem_001/solution.cpp new file mode 100644 index 0000000..3316811 --- /dev/null +++ b/c++/problems/problem_001/solution.cpp @@ -0,0 +1,23 @@ +#include "solution.hpp" + +// Standard LeetCode solution for Two Sum +std::vector Solution::twoSum(std::vector& nums, int target) { + // Input: nums = [2,7,11,15], target = 9 + // Output: [0,1] + // Explanation: Because nums[0] + nums[1] == 9, we return [0, 1]. + + for(auto it=nums.begin(); it != nums.end(); ++it){ + for(auto j=(it + 1); j != nums.end(); ++j){ + if(*it + *j == target) { + auto retval = std::vector(); + int index = std::distance(nums.begin(), it); + int index2 = std::distance(nums.begin(), j); + retval.push_back(index); + retval.push_back(index2); + return retval; + } + } + } + + return std::vector(); // Should not happen for valid inputs +} \ No newline at end of file diff --git a/c++/problems/problem_001/solution.hpp b/c++/problems/problem_001/solution.hpp new file mode 100644 index 0000000..b02181e --- /dev/null +++ b/c++/problems/problem_001/solution.hpp @@ -0,0 +1,11 @@ +#ifndef SOLUTION_HPP_ +#define SOLUTION_HPP_ + +#include + +class Solution { +public: + std::vector twoSum(std::vector& nums, int target); +}; + +#endif // SOLUTION_HPP_ \ No newline at end of file diff --git a/c++/tests_runner/tests_runner.cpp b/c++/tests_runner/tests_runner.cpp new file mode 100644 index 0000000..8aac848 --- /dev/null +++ b/c++/tests_runner/tests_runner.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "solution.hpp" + +// --- ANSI Color Codes for Terminal Output --- +const char* const RESET_COLOR = "\033[0m"; +const char* const GREEN_COLOR = "\033[32m"; +const char* const RED_COLOR = "\033[31m"; +const char* const BOLD_WHITE = "\033[1m\033[37m"; + +// --- Generic Helpers for Parsing and Comparing --- +namespace helpers { + // A set of simple, explicit functions for parsing types from JSON + int parse_int(const Json::Value& value) { + return std::stoi(value.asString()); + } + + std::string parse_string(const Json::Value& value) { + return value.asString(); + } + + std::vector parse_int_vector(const Json::Value& value) { + std::string s = value.asString(); + std::string content = s.substr(1, s.length() - 2); + if (content.empty()) return {}; + std::vector vec; + std::stringstream ss(content); + std::string item; + while (std::getline(ss, item, ',')) { + vec.push_back(std::stoi(item)); + } + return vec; + } +} + +// --- Test Case Handler Framework --- +using TestCaseHandler = std::function; +static std::map test_registry; + +// ======================================================================= +// =================== PROBLEM HANDLERS GO HERE ========================== +// ======================================================================= + +// With helpers, handlers are concise and easy to read. +bool handleTwoSum(Solution& solution, const Json::Value& test) { + auto nums = helpers::parse_int_vector(test["Input"]["nums"]); + auto target = helpers::parse_int(test["Input"]["target"]); + auto expected = helpers::parse_int_vector(test["Output"]); + + auto actual = solution.twoSum(nums, target); + + // Normalize for comparison, since order doesn't matter for this problem + std::sort(actual.begin(), actual.end()); + std::sort(expected.begin(), expected.end()); + + return actual == expected; +} + +// ======================================================================= +// =================== REGISTER ALL HANDLERS HERE ======================== +// ======================================================================= + +void register_all_tests() { + test_registry["twoSum"] = handleTwoSum; + // To add a new problem, you would add a new handler and register it here. +} + +// ======================================================================= +// =================== MAIN TEST RUNNER LOGIC ============================ +// ======================================================================= +int main(int argc, char* argv[]) { + register_all_tests(); + + std::string json_path; + for (int i = 1; i < argc; ++i) { + if (std::string(argv[i]) == "-f" && i + 1 < argc) { + json_path = argv[++i]; + } + } + if (json_path.empty()) { + std::cerr << RED_COLOR << "Error: No JSON test file provided. Usage: -f " << RESET_COLOR << std::endl; + return 1; + } + + std::ifstream ifs(json_path); + if (!ifs.is_open()) { + std::cerr << RED_COLOR << "Error: Could not open file: " << json_path << RESET_COLOR << std::endl; + return 1; + } + + Json::Value root; + ifs >> root; + + std::string method_name = root["method"].asString(); + + if (test_registry.find(method_name) == test_registry.end()) { + std::cerr << RED_COLOR << "Error: No test handler registered for method '" << method_name << "'" << RESET_COLOR << std::endl; + return 1; + } + TestCaseHandler handler = test_registry[method_name]; + + const Json::Value& tests = root["tests"]; + std::cout << BOLD_WHITE << "Running tests for method: " << method_name << RESET_COLOR << std::endl; + std::cout << "------------------------------------------" << std::endl; + + Solution solution; + int tests_passed = 0; + int total_tests = tests.size(); + + for (int i = 0; i < total_tests; ++i) { + std::cout << "Test Case #" << (i + 1) << "... "; + try { + if (handler(solution, tests[i])) { + std::cout << GREEN_COLOR << "PASS" << RESET_COLOR << std::endl; + tests_passed++; + } else { + std::cout << RED_COLOR << "FAIL" << RESET_COLOR << std::endl; + } + } catch (const std::exception& e) { + std::cout << RED_COLOR << "ERROR: An exception occurred: " << e.what() << RESET_COLOR << std::endl; + } + } + + std::cout << "------------------------------------------" << std::endl; + std::cout << BOLD_WHITE << "Summary: " << tests_passed << " / " << total_tests << " tests passed." << RESET_COLOR << std::endl; + + return (tests_passed == total_tests) ? 0 : 1; +} \ No newline at end of file