From 0dcc29e205617c5fb7255299b05df7afaaec9b43 Mon Sep 17 00:00:00 2001 From: Simik31 Date: Wed, 29 Dec 2021 00:48:32 +0100 Subject: [PATCH] Add project files. --- Sudoku_CLI.sln | 31 +++++ Sudoku_CLI/Game.cpp | 157 +++++++++++++++++++++++++ Sudoku_CLI/Game.h | 37 ++++++ Sudoku_CLI/Sudoku.cpp | 87 ++++++++++++++ Sudoku_CLI/Sudoku.h | 27 +++++ Sudoku_CLI/Sudoku_CLI.cpp | 9 ++ Sudoku_CLI/Sudoku_CLI.vcxproj | 163 ++++++++++++++++++++++++++ Sudoku_CLI/Sudoku_CLI.vcxproj.filters | 47 ++++++++ Sudoku_CLI/Utils.cpp | 53 +++++++++ Sudoku_CLI/Utils.h | 26 ++++ Sudoku_CLI/default.sudoku | 9 ++ 11 files changed, 646 insertions(+) create mode 100644 Sudoku_CLI.sln create mode 100644 Sudoku_CLI/Game.cpp create mode 100644 Sudoku_CLI/Game.h create mode 100644 Sudoku_CLI/Sudoku.cpp create mode 100644 Sudoku_CLI/Sudoku.h create mode 100644 Sudoku_CLI/Sudoku_CLI.cpp create mode 100644 Sudoku_CLI/Sudoku_CLI.vcxproj create mode 100644 Sudoku_CLI/Sudoku_CLI.vcxproj.filters create mode 100644 Sudoku_CLI/Utils.cpp create mode 100644 Sudoku_CLI/Utils.h create mode 100644 Sudoku_CLI/default.sudoku diff --git a/Sudoku_CLI.sln b/Sudoku_CLI.sln new file mode 100644 index 0000000..6de1adf --- /dev/null +++ b/Sudoku_CLI.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sudoku_CLI", "Sudoku_CLI\Sudoku_CLI.vcxproj", "{7DD30603-9CF4-4DFF-B8E2-5881E986964C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Debug|x64.ActiveCfg = Debug|x64 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Debug|x64.Build.0 = Debug|x64 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Debug|x86.ActiveCfg = Debug|Win32 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Debug|x86.Build.0 = Debug|Win32 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Release|x64.ActiveCfg = Release|x64 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Release|x64.Build.0 = Release|x64 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Release|x86.ActiveCfg = Release|Win32 + {7DD30603-9CF4-4DFF-B8E2-5881E986964C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E0FA3857-14A2-4097-B1C1-6E151A0AB389} + EndGlobalSection +EndGlobal diff --git a/Sudoku_CLI/Game.cpp b/Sudoku_CLI/Game.cpp new file mode 100644 index 0000000..f5080db --- /dev/null +++ b/Sudoku_CLI/Game.cpp @@ -0,0 +1,157 @@ +#include "Game.h" + +void Game::main_menu() +{ + int choice; + do + { + system("cls"); + std::cout << "Welcome at SUDOKU CLI game.\nCopyright (C) 2021: Simik31 / https://github.com/Simik31/Sudoku_CLI / simik31@pm.me \n\n[1] Input sudoku by hand\n[2] Load sudoku from file\n[3] Load 'default' sudoku\n\n > "; + choice = std::cin.get() - 48; + } while (choice < 1 || choice > 3); + + while (!this->loaded) + { + switch (choice) + { + case 1: this->load_by_hand(); break; + case 2: this->load_from_file(); break; + case 3: this->load_default(); break; + } + if (!this->loaded) + { + std::cout << "Press any key to repeat..."; + _getch(); + } + } +} + +void Game::load_by_hand() +{ + std::cout << "Enter sudoku by using arrows to navigate into cell and pressing corresponding number.\nWhen you are done enetering numbers, press Enter to start solving.\n\nPress any key to strart..."; + _getch(); + + Utils::print_sudoku(this->sudoku); + + Sudoku tmp; + + while (true) + { + int fill = -1; + + MOVE_CURSOR_TO_CELL(this->cursor_x, this->cursor_y); + + int in = _getch(); + switch (in) + { + case KEY_ENTER: + { + std::vector init; + init.reserve(81); + + for (int i = 0; i < 81; i++) init.push_back(Sudoku::get_state(tmp, i)); + + this->loaded = true; + + return Utils::print_sudoku(this->sudoku = Sudoku(init)); + }; + case KEY_UP: this->cursor_y = WRAP(this->cursor_y - 1); break; + case KEY_RIGHT: this->cursor_x = WRAP(this->cursor_x + 1); break; + case KEY_DOWN: this->cursor_y = WRAP(this->cursor_y + 1); break; + case KEY_LEFT: this->cursor_x = WRAP(this->cursor_x - 1); break; + default: if (in >= 49 && in <= 57) tmp.fill_number(this->cursor_x, this->cursor_y, in - 48); // 49 = 1, 50 = 2, ... 57 = 9 + } + } +} + +void Game::load_from_file() +{ + system("cls"); + std::cout << "Please enter file path\n\n > "; + std::string path; + std::cin >> path; + + std::vector file_lines = Utils::read_lines_from_file(path); + + if (file_lines.size() == 0) + { + std::cerr << "File not found or empty: " << std::filesystem::absolute(path) << std::endl; + return; + } + else if (file_lines.size() != 9) + { + std::cerr << "Sudoku must have exactly 9 lines. " << file_lines.size() << " given." << std::endl; + return; + } + else + { + for (int i = 0; i < 9; i++) + { + if (file_lines[i].size() != 9) + { + std::cerr << "Each sudoku line must have exactly 9 columns. On line " << (i + 1) << " only " << file_lines[i].size() << " columns given." << std::endl; + return; + } + } + } + + std::vector init; + init.reserve(81); + + for (std::string line : file_lines) + for (char c : line) + init.push_back(c - '0'); + + this->sudoku = Sudoku(init); + this->loaded = true; +} + +void Game::load_default() +{ + int state[9][9]{ + { 0, 2, 0, 0, 0, 4, 3, 0, 0 }, + { 9, 0, 0, 0, 2, 0, 0, 0, 8 }, + { 0, 0, 0, 6, 0, 9, 0, 5, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + { 0, 7, 2, 5, 0, 3, 6, 8, 0 }, + { 6, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 8, 0, 2, 0, 5, 0, 0, 0 }, + { 1, 0, 0, 0, 9, 0, 0, 0, 3 }, + { 0, 0, 9, 8, 0, 0, 0, 6, 0 } + }; + + std::vector init; + for (int (&row)[9] : state) for (int cell : row) init.push_back(cell); + + this->sudoku = Sudoku(init); + this->loaded = true; +} + +void Game::run() +{ + if (!loaded) return; + + Utils::print_sudoku(this->sudoku); + + while (!this->sudoku.is_solved()) + { + int fill = -1; + + MOVE_CURSOR_TO_CELL(this->cursor_x, this->cursor_y); + + int in = _getch(); + switch (in) + { + case KEY_UP: this->cursor_y = WRAP(this->cursor_y - 1); break; + case KEY_RIGHT: this->cursor_x = WRAP(this->cursor_x + 1); break; + case KEY_DOWN: this->cursor_y = WRAP(this->cursor_y + 1); break; + case KEY_LEFT: this->cursor_x = WRAP(this->cursor_x - 1); break; + default: if (in >= 49 && in <= 57) + { + this->sudoku.fill_number(this->cursor_x, this->cursor_y, in - 48); // 49 = 1, 50 = 2, ... 57 = 9 + this->sudoku.test_if_solved(); + } + } + + } +} \ No newline at end of file diff --git a/Sudoku_CLI/Game.h b/Sudoku_CLI/Game.h new file mode 100644 index 0000000..2083f7f --- /dev/null +++ b/Sudoku_CLI/Game.h @@ -0,0 +1,37 @@ +#pragma once +#ifndef GAME_H +#define GAME_H + +#define KEY_ENTER 13 +#define KEY_UP 72 +#define KEY_LEFT 75 +#define KEY_RIGHT 77 +#define KEY_DOWN 80 + +#include +#include +#include +#include + +#include "Sudoku.h" +#include "Utils.h" + +class Game +{ +public: + void main_menu(); + void run(); + +private: + void load_by_hand(); + void load_from_file(); + void load_default(); + + bool loaded = false; + Sudoku sudoku; + int cursor_x = 0, cursor_y = 0; +}; + +#endif + + diff --git a/Sudoku_CLI/Sudoku.cpp b/Sudoku_CLI/Sudoku.cpp new file mode 100644 index 0000000..18e0ea7 --- /dev/null +++ b/Sudoku_CLI/Sudoku.cpp @@ -0,0 +1,87 @@ +#include "Sudoku.h" +#include "Utils.h" + +Sudoku::Sudoku() +{ + this->state.reserve(81); + this->initial.reserve(81); + + for (int i = 0; i < 81; i++) + { + this->state.push_back(0); + this->initial.push_back(false); + } +} + +Sudoku::Sudoku(const std::vector& initial_state) +{ + this->state = initial_state; + for (int state : initial_state) this->initial.push_back(state > 0); +} + +bool Sudoku::is_solved() +{ + return this->solved; +} + +int Sudoku::get_state(const Sudoku& sudoku, const int index) +{ + return sudoku.state[index]; +} + +bool Sudoku::get_initial(const Sudoku& sudoku, const int index) +{ + return sudoku.initial[index]; +} + +void Sudoku::fill_number(const int cell_x, const int cell_y, const int number) +{ + int index = cell_y * 9 + cell_x; + + if (this->initial[index]) return; + + this->state[index] = this->state[index] == number ? 0 : number; + + Utils::print_sudoku(*this); +} + +void Sudoku::test_if_solved() +{ + if (std::find(this->state.begin(), this->state.end(), 0) != this->state.end()) return; + + this->solved = true; + + for (int y = 0; y < 9; y++) + for (int x1 = 0; x1 < 8; x1++) + for (int x2 = x1 + 1; x2 < 9; x2++) + if (this->state[y * 9 + x1] == this->state[y * 9 + x2]) + { + Utils::highlight_cell(*this, x1, y); + Utils::highlight_cell(*this, x2, y); + this->solved = false; + } + + for (int x = 0; x < 9; x++) + for (int y1 = 0; y1 < 8; y1++) + for (int y2 = y1 + 1; y2 < 9; y2++) + if (this->state[y1 * 9 + x] == this->state[y2 * 9 + x]) + { + Utils::highlight_cell(*this, x, y1); + Utils::highlight_cell(*this, x, y2); + this->solved = false; + } + + for (int y = 0; y < 3; y++) + for (int x = 0; x < 3; x++) + for (int oY1 = 0; oY1 < 3; oY1++) + for (int oX1 = 0; oX1 < 3; oX1++) + for (int oY2 = 0; oY2 < 3; oY2++) + for (int oX2 = 0; oX2 < 3; oX2++) + if ((oX1 == oX2 && oY1 == oY2) == false) + if (this->state[(y * 3 + oY1) * 9 + x * 3 + oX1] == this->state[(y * 3 + oY2) * 9 + x * 3 + oX2]) + { + Utils::highlight_cell(*this, x * 3 + oX1, y * 3 + oY1); + Utils::highlight_cell(*this, x * 3 + oX2, y * 3 + oY2); + this->solved = false; + } +} \ No newline at end of file diff --git a/Sudoku_CLI/Sudoku.h b/Sudoku_CLI/Sudoku.h new file mode 100644 index 0000000..162689b --- /dev/null +++ b/Sudoku_CLI/Sudoku.h @@ -0,0 +1,27 @@ +#pragma once +#ifndef SUDOKU_H +#define SUDOKU_H + +#include + +class Sudoku +{ +public: + Sudoku(); + Sudoku(const std::vector& initial_state); + + bool is_solved(); + static int get_state(const Sudoku& sudoku, const int index); + static bool get_initial(const Sudoku& sudoku, const int index); + + void fill_number(const int cell_x, const int cell_y, const int number); + void test_if_solved(); + +private: + bool solved = false; + + std::vector state; + std::vector initial; +}; + +#endif \ No newline at end of file diff --git a/Sudoku_CLI/Sudoku_CLI.cpp b/Sudoku_CLI/Sudoku_CLI.cpp new file mode 100644 index 0000000..dbfe22f --- /dev/null +++ b/Sudoku_CLI/Sudoku_CLI.cpp @@ -0,0 +1,9 @@ +#include "Game.h" + + +int main() +{ + Game game; + game.main_menu(); + game.run(); +} diff --git a/Sudoku_CLI/Sudoku_CLI.vcxproj b/Sudoku_CLI/Sudoku_CLI.vcxproj new file mode 100644 index 0000000..337d127 --- /dev/null +++ b/Sudoku_CLI/Sudoku_CLI.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {7dd30603-9cf4-4dff-b8e2-5881e986964c} + SudokuCLI + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + true + true + + + + + + + + + + + + + + + + true + Text + + + + + + \ No newline at end of file diff --git a/Sudoku_CLI/Sudoku_CLI.vcxproj.filters b/Sudoku_CLI/Sudoku_CLI.vcxproj.filters new file mode 100644 index 0000000..022933b --- /dev/null +++ b/Sudoku_CLI/Sudoku_CLI.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/Sudoku_CLI/Utils.cpp b/Sudoku_CLI/Utils.cpp new file mode 100644 index 0000000..ea86d5a --- /dev/null +++ b/Sudoku_CLI/Utils.cpp @@ -0,0 +1,53 @@ +#include "Utils.h" + +void Utils::print_sudoku(const Sudoku& sudoku) +{ + system("cls"); + + std::cout << "+=====+=====+=====+\n"; // "╔═════╦═════╦═════╗\n" + + for (int y = 0; y < 9; y++) + { + std::cout << "|"; // "║" + + for (int x = 0; x < 9; x++) + { + if (Sudoku::get_initial(sudoku, y * 9 + x)) SET_COLOR(10); + + Sudoku::get_state(sudoku, y * 9 + x) > 0 ? std::cout << Sudoku::get_state(sudoku, y * 9 + x) : std::cout << " "; + + SET_COLOR(7); + + std::cout << "|"; // x % 3 != 2 ? "│" : "║" + } + + if (y % 3 != 2) std::cout << "\n|-+-+-|-+-+-|-+-+-|\n"; // "\n║─┼─┼─║─┼─┼─║─┼─┼─║\n" + else if (y < 8) std::cout << "\n|=====|=====|=====|\n"; // "\n╠═════╬═════╬═════╣\n" + else std::cout << "\n+=====+=====+=====+\n" << std::endl; // "\n╚═════╩═════╩═════╝\n" + } +} + +void Utils::highlight_cell(Sudoku& sudoku, const int x, const int y) +{ + if (Sudoku::get_initial(sudoku, y * 9 + x)) SET_COLOR(202); + else SET_COLOR(199); + + MOVE_CURSOR_TO_CELL(x, y); + + std::cout << Sudoku::get_state(sudoku, y * 9 + x); + + SET_COLOR(7); +} + +std::vector Utils::read_lines_from_file(const std::string& filename) +{ + std::ifstream file{ std::filesystem::absolute(filename) }; + std::vector lines; + std::string line; + + if (file.is_open()) + while (getline(file, line)) + lines.push_back(line); + + return lines; +} \ No newline at end of file diff --git a/Sudoku_CLI/Utils.h b/Sudoku_CLI/Utils.h new file mode 100644 index 0000000..c33d13d --- /dev/null +++ b/Sudoku_CLI/Utils.h @@ -0,0 +1,26 @@ +#pragma once +#ifndef UTILS_H +#define UTILS_H + +#define SET_COLOR(color) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color) +#define WRAP(num) (((num) % 9 + 9) % 9) +#define MOVE_CURSOR_TO_CELL(x, y) SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), COORD{ short(x * 2 + 1), short(y * 2 + 1) }) + +#include +#include +#include +#include +#include +#include + +#include "Sudoku.h" + +class Utils +{ +public: + static void print_sudoku(const Sudoku& sudoku); + static void highlight_cell(Sudoku& sudoku, const int x, const int y); + static std::vector read_lines_from_file(const std::string& filename); +}; + +#endif \ No newline at end of file diff --git a/Sudoku_CLI/default.sudoku b/Sudoku_CLI/default.sudoku new file mode 100644 index 0000000..a270e20 --- /dev/null +++ b/Sudoku_CLI/default.sudoku @@ -0,0 +1,9 @@ +020004300 +900020008 +000609050 +000000001 +072503680 +600000000 +080205000 +100090003 +009800060 \ No newline at end of file