-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
646 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<int> 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<std::string> 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<int> 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<int> 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(); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <vector> | ||
#include <iostream> | ||
#include <conio.h> | ||
#include <string> | ||
|
||
#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 | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<int>& 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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#pragma once | ||
#ifndef SUDOKU_H | ||
#define SUDOKU_H | ||
|
||
#include <vector> | ||
|
||
class Sudoku | ||
{ | ||
public: | ||
Sudoku(); | ||
Sudoku(const std::vector<int>& 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<int> state; | ||
std::vector<bool> initial; | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include "Game.h" | ||
|
||
|
||
int main() | ||
{ | ||
Game game; | ||
game.main_menu(); | ||
game.run(); | ||
} |
Oops, something went wrong.