generated from Trianta/cpp-unity-template
Successfully solves sudoku
This commit is contained in:
parent
670027c119
commit
e12cb72dcf
153
src/sudoku.cpp
153
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<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();) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user