diff --git a/src/sudoku.cpp b/src/sudoku.cpp index 903f035..52240a2 100644 --- a/src/sudoku.cpp +++ b/src/sudoku.cpp @@ -15,53 +15,129 @@ void Sudoku::FillBoard(std::string filePath) { exit(1); } std::string line; - while (!sudokuFile.eof()) { + int height = 0; + while (!sudokuFile.eof() && height < 9) { sudokuFile >> line; - std::cout << line << std::endl; + for (int i = 0; i < 9; ++i) { board[height][i] = line[i] - '0'; } + ++height; } - // TODO: Finish inputting sudoku file - // Really just needs to be split by digit then stored - } -// TODO: Implement solving algorithm +// Base global call for solving void Sudoku::Solve(void) { - + _Solve(); } -// TODO: Add checking for full board completion bool Sudoku::IsBoardSolved(void) { + for (int i = 0; i < 9; ++i) { + if (!IsColumnComplete(i)) { return false; } + } return true; } -// TODO: Add printing of board for visuals -// Maybe also print during solve? void Sudoku::Print(void) { - + std::cout << "===================" << std::endl; + for (auto i : board) { + std::cout << '|'; + for (auto j : i) { + std::cout << j << '|'; + } + std::cout << std::endl; + } + std::cout << "===================" << std::endl; +} + +// Recursive call for solving +bool Sudoku::_Solve(void) { + int column, row; + if (!FindEmptyCell(&column, &row)) { return true; } + for (int i : GetUnusedRow(row)) { + for (int j : GetUnusedColumn(column)) { + for (int k : GetUnusedSubgrid(column, row)) { + if (i == j == k) { + board[row][column] = i; + if (_Solve()) { return true; } + board[row][column] = 0; + } + } + } + } + return 0; +} + +// Fills column and row of an empty cell and returns true +// If no empty cell is found, return false +bool Sudoku::FindEmptyCell(int* column, int* row) { + for (*row = 0; *row < 9; ++(*row)) { + for (*column = 0; *column < 9; ++(*column)) { + if (board[*row][*column] == 0) { return true; } + } + } + return false; +} + +bool Sudoku::IsValidPlacement(int column, int row, int box) { + // Check column and row + for (int i = 0; i < 9; ++i) { + if (board[row][i] == box || board[i][column] == box) { + return false; + } + } + + // Check subgrid + int startRow = row - row % 3; + int startColumn = column - column % 3; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (board[startRow + i][startColumn + j] == box) { return false; } + } + } + return true; } -// TODO: Add checking for row completion bool Sudoku::IsRowComplete(int row) { + std::vector leftOver{0,1,2,3,4,5,6,7,8,9}; + int box; + for (int i = 0; i < 9; i++) { + box = board[row][i]; + if (leftOver[box] == 0 && box != 0) { + std::cerr << "Invalid assignment detected. Aborting..." << std::endl; + exit(1); + } + if (leftOver[box] == box) { leftOver[box] = 0; } + } + for (auto it = leftOver.begin(); it != leftOver.end();) { + if (*it == 0) { it = leftOver.erase(it); } + else { ++it; } + } + if (leftOver.size() != 0) { return false; } return true; } -// TODO: Add checking for column completion bool Sudoku::IsColumnComplete(int column) { + std::vector leftOver{0,1,2,3,4,5,6,7,8,9}; + int box; + for (int i = 0; i < 9; i++) { + box = board[i][column]; + if (leftOver[box] == 0 && box != 0) { + leftOver[box] = -1; // Duplicate detected in same column + } + if (leftOver[box] == box) { leftOver[box] = 0; } + } + for (auto it = leftOver.begin(); it != leftOver.end();) { + if (*it == 0) { it = leftOver.erase(it); } + else { ++it; } + } + if (leftOver.size() != 0) { return false; } return true; } -// WARNING: This function and one below could be swapped, untested -std::vector Sudoku::GetUnusedInRow(int row) { +std::vector Sudoku::GetUnusedRow(int row) { std::vector leftOver{0,1,2,3,4,5,6,7,8,9}; - int number; + int box; for (int i = 0; i < 9; i++) { - number = board[i][row]; - if (leftOver[number] == number) { - leftOver[number] = 0; - } - if (leftOver[number] == 0) { // Two of same number was placed - leftOver[number] = -1; - } + box = board[row][i]; + if (leftOver[box] == box) { leftOver[box] = 0; } } for (auto it = leftOver.begin(); it != leftOver.end();) { if (*it == 0) { it = leftOver.erase(it); } @@ -70,16 +146,31 @@ std::vector Sudoku::GetUnusedInRow(int row) { return leftOver; } -std::vector Sudoku::GetUnusedInColumn(int column) { +std::vector Sudoku::GetUnusedColumn(int column) { std::vector leftOver{0,1,2,3,4,5,6,7,8,9}; - int number; + int box; for (int i = 0; i < 9; i++) { - number = board[column][i]; - if (leftOver[number] == number) { - leftOver[number] = 0; - } - if (leftOver[number] == 0) { // Two of same number was placed - leftOver[number] = -1; + box = board[i][column]; + if (leftOver[box] == box) { leftOver[box] = 0; } + } + for (auto it = leftOver.begin(); it != leftOver.end();) { + if (*it == 0) { it = leftOver.erase(it); } + else { ++it; } + } + return leftOver; +} + +std::vector Sudoku::GetUnusedSubgrid(int column, int row) { + std::vector leftOver{0,1,2,3,4,5,6,7,8,9}; + int box; + int startRow = row - row % 3; + int startColumn = column - column % 3; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + box = board[row][column]; + if (board[startRow + i][startColumn + j] == box) { + leftOver[box] = 0; + } } } for (auto it = leftOver.begin(); it != leftOver.end();) { diff --git a/src/sudoku.hpp b/src/sudoku.hpp index a466e61..bb919d9 100644 --- a/src/sudoku.hpp +++ b/src/sudoku.hpp @@ -13,10 +13,14 @@ public: bool IsBoardSolved(void); void Print(void); private: + bool _Solve(void); + bool FindEmptyCell(int* column, int* row); + bool IsValidPlacement(int column, int row, int box); bool IsRowComplete(int row); bool IsColumnComplete(int column); - std::vector GetUnusedInRow(int row); - std::vector GetUnusedInColumn(int column); + std::vector GetUnusedRow(int row); + std::vector GetUnusedColumn(int column); + std::vector GetUnusedSubgrid(int column, int row); }; #endif diff --git a/src/sudoku_solver.cpp b/src/sudoku_solver.cpp index 05f35f9..879c0d7 100644 --- a/src/sudoku_solver.cpp +++ b/src/sudoku_solver.cpp @@ -1,9 +1,14 @@ #include "sudoku.hpp" +#include -// TODO: -// Take in sudoku file from arguments -// Run solve -// Print solution -int main(void) { +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " path/to/sudoku" << std::endl; + return 1; + } + Sudoku newGame; + newGame.FillBoard(argv[1]); + newGame.Solve(); + newGame.Print(); return 0; } diff --git a/test/test.cpp b/test/test.cpp index d65e30f..66c8bdc 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -10,6 +10,55 @@ void tearDown() { ; } int main(void) { UNITY_BEGIN(); RUN_TEST(test_SudokuEasy01); + RUN_TEST(test_SudokuEasy02); + RUN_TEST(test_SudokuEasy03); + RUN_TEST(test_SudokuEasy04); + RUN_TEST(test_SudokuEasy05); + RUN_TEST(test_SudokuEasy06); + RUN_TEST(test_SudokuEasy07); + RUN_TEST(test_SudokuEasy08); + RUN_TEST(test_SudokuEasy09); + RUN_TEST(test_SudokuEasy10); + RUN_TEST(test_SudokuEasy11); + RUN_TEST(test_SudokuEasy12); + RUN_TEST(test_SudokuEasy13); + RUN_TEST(test_SudokuEasy14); + RUN_TEST(test_SudokuEasy15); + RUN_TEST(test_SudokuEasy16); + RUN_TEST(test_SudokuEasy17); + RUN_TEST(test_SudokuEasy18); + RUN_TEST(test_SudokuEasy19); + RUN_TEST(test_SudokuEasy20); + RUN_TEST(test_SudokuEasy21); + RUN_TEST(test_SudokuEasy22); + RUN_TEST(test_SudokuEasy23); + RUN_TEST(test_SudokuEasy24); + RUN_TEST(test_SudokuEasy25); + RUN_TEST(test_SudokuEasy26); + RUN_TEST(test_SudokuEasy27); + RUN_TEST(test_SudokuEasy28); + RUN_TEST(test_SudokuEasy29); + RUN_TEST(test_SudokuEasy30); + RUN_TEST(test_SudokuEasy31); + RUN_TEST(test_SudokuEasy32); + RUN_TEST(test_SudokuEasy33); + RUN_TEST(test_SudokuEasy34); + RUN_TEST(test_SudokuEasy35); + RUN_TEST(test_SudokuEasy36); + RUN_TEST(test_SudokuEasy37); + RUN_TEST(test_SudokuEasy38); + RUN_TEST(test_SudokuEasy39); + RUN_TEST(test_SudokuEasy40); + RUN_TEST(test_SudokuEasy41); + RUN_TEST(test_SudokuEasy42); + RUN_TEST(test_SudokuEasy43); + RUN_TEST(test_SudokuEasy44); + RUN_TEST(test_SudokuEasy45); + RUN_TEST(test_SudokuEasy46); + RUN_TEST(test_SudokuEasy47); + RUN_TEST(test_SudokuEasy48); + RUN_TEST(test_SudokuEasy49); + RUN_TEST(test_SudokuEasy50); return UNITY_END(); }