Successfully solves sudoku

This commit is contained in:
Trianta 2023-10-22 21:39:36 -05:00
parent 670027c119
commit e12cb72dcf
4 changed files with 187 additions and 38 deletions

View File

@ -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<int> 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<int> 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<int> Sudoku::GetUnusedInRow(int row) {
std::vector<int> Sudoku::GetUnusedRow(int row) {
std::vector<int> 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<int> Sudoku::GetUnusedInRow(int row) {
return leftOver;
}
std::vector<int> Sudoku::GetUnusedInColumn(int column) {
std::vector<int> Sudoku::GetUnusedColumn(int column) {
std::vector<int> 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<int> Sudoku::GetUnusedSubgrid(int column, int row) {
std::vector<int> 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();) {

View File

@ -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<int> GetUnusedInRow(int row);
std::vector<int> GetUnusedInColumn(int column);
std::vector<int> GetUnusedRow(int row);
std::vector<int> GetUnusedColumn(int column);
std::vector<int> GetUnusedSubgrid(int column, int row);
};
#endif

View File

@ -1,9 +1,14 @@
#include "sudoku.hpp"
#include <iostream>
// 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;
}

View File

@ -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();
}