Sudoku Solver not working SEG FAULT.?

Pages: 12
Hello, I am writing a Sudoku solver and the program does not run and no compiler errors are thrown. All i get is the exe stopped working. The program takes in a string as input and converts it into a 2D char array and then attempts to solve Sudoku. Any help or direction in where my program is failing would help a ton. Thank you.
*EDIT* APPEARS TO BE SEG FAULT LINE 76 why..?

Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>

#include "PuzzleClass.h"

int main()
{
	Sudoku game;
	char **board = game.GetInput();

	if (game.Solver(board))
	{
		game.printBoard(board);
	}

	else
	{
		std::cout << "No solution found";
	}

	game.freeBoard(board);

	system("pause");
	return 0;
}


PuzzleClass.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#pragma once
#include <iostream>
#include <string>
#define NUM_ROWS 9
#define NUM_COLS 9


class Sudoku
{
	public:
		char ** GetInput();
		void freeBoard(char **board);
		void printBoard(char **board);
		bool Solver(char ** board);
		bool FindEmptySquares(char ** board, int &row, int &col);
		bool NotPlayed(char ** board, int row, int col, int number);
		bool UsedInRow(char ** board, int row, int number);
		bool UsedInCol(char ** board, int col, int number);
		bool UsedInBox(char **board, int startingRow, int startingCol, int number);
};

bool Sudoku::UsedInBox(char ** board, int startingRow, int startingCol, int number)
{
	for (int row = 0; row < 3; ++row)
	{
		for (int col = 0; col < 3; ++col)
		{
			if (board[row + startingRow][col + startingCol] == '0' + number)
			{
				return true;
			}
		}
	}
	return false;
}

bool Sudoku::UsedInCol(char ** board, int col, int number)
{
	for (int row = 0; row < NUM_ROWS; ++row)
	{
		if (board[row][col] == '0' + number)
		{
			return true;
		}
	}
	return false;
}
bool Sudoku::UsedInRow(char ** board, int row, int number)
{
	for (int col = 0; col < NUM_COLS; ++col)
	{
		if (board[row][col] == '0' + number)
		{
			return true;
		}
	}
	return false;
}
bool Sudoku::NotPlayed(char ** board, int row, int col, int number)
{
	if ((UsedInRow(board, row, number) && UsedInCol(board, col, number) &&
		UsedInBox(board, row - row % 3, col - col % 3, number)))
	{
		return false;
	}

	return true;
}

bool Sudoku::FindEmptySquares(char ** board, int &row, int &col)
{
	for (int i = 0; row < NUM_ROWS; ++i)
	{
		for (int j = 0; j < NUM_COLS; ++j)
		{
			if (board[i][j] == '0')
			{
				row = i;
				col = j;
				return true;
			}
		}
	}
	return false;
}

bool Sudoku::Solver(char **board)
{
	int row, col;
	char empty = '0';

	if (!FindEmptySquares(board, row, col))
	{
		return true;
	}
	// Check for numbers 1-9
	for (int number = 1; number <= 9; ++number)
	{
		if (NotPlayed(board, row, col, number))
		{
			// Make an assignment tentatively
			board[row][col] = '0' + number;
			
			// If successful return true
			if (Solver(board))
			{
				return true;
			}
			// Else failure, assign an "empty" square and try again
			else
			{
				board[row][col] = empty;
			}
		}
	}
    return false; // trigger backtracking
}

char ** Sudoku::GetInput()
{
	std::string input;
	char **board = new char*[NUM_ROWS];

	//std::cout << "Enter the board, 0 being an empty square" << std::endl;
	//std::cin >> input;

	input = "306508400520000000087000031003010080900863005050090600130000250000000074005206300";
	//input = "007401680186300040900806710609040530070085902850039004090070806268904007005260091";
	for (int row = 0; row < NUM_ROWS; ++row)
	{
		board[row] = new char[NUM_ROWS];
		for (int col = 0; col < NUM_COLS; ++col)
		{
			 board[row][col] = input[col + (row * NUM_COLS)];
		}
	}
	return board;
}

void Sudoku::printBoard(char **board)
{
	for (int row = 0; row < 9; row++)
	{
		for (int col = 0; col < 9; col++)
			std::cout << board[row][col];
		std::cout << "\n";
	}

}

void Sudoku::freeBoard(char **board)
{
	for (int i = 0; i < 81; ++i)
	{
		delete[] board[i];
	}
	delete[] board;
}
Last edited on
and no compiler errors are thrown.


I used the cpp.sh compiler with all 3 warnings levels on, pay attention to the last 2 especially:

4:9: warning: #pragma once in main file In member function 'bool Sudoku::UsedInBox(char**, int, int, int)':
27:29: warning: statement has no effect [-Wunused-value]
At global scope:
25:43: warning: unused parameter 'startingRow' [-Wunused-parameter]
In member function 'bool Sudoku::UsedInCol(char**, int, int)':
43:29: warning: statement has no effect [-Wunused-value]
In member function 'bool Sudoku::UsedInRow(char**, int, int)':
55:29: warning: statement has no effect [-Wunused-value]
At global scope: 75:51: warning: unused parameter 'row' [-Wunused-parameter]
75:61: warning: unused parameter 'col' [-Wunused-parameter]
In member function 'bool Sudoku::Solver(char**)':
105:30: warning: statement has no effect [-Wunused-value]
106:13: warning: 'row' may be used uninitialized in this function [-Wmaybe-uninitialized]
102:3: warning: 'col' may be used uninitialized in this function [-Wmaybe-uninitialized]


Btw, avoid using new, invsetigate using an STL container like std::array, this will be easier than 2 levels of pointers. Google RAII.

Also investigate using a debugger, there might be a GUI one in your IDE.
Last edited on
Part of the problem may be lines 72 to 85. This WON'T return row and col, because you have created new variables with the same names that hide them.

Haven't looked beyond that yet.
Thank you both. dumb mistakes on my part. That helped get to the next step of debugging.
And thank IdeasMan. That is strange I have no warnings. I am using MS visual studio
Check your options, and increase your warning levels. Level 3 (/W3) is common.

I'd also recommend enabling the option to treat all warnings as errors, because then you're forced to deal with them.
Are you sure that you want to delete the content of 81 rows on lines 153 to 156?
1
2
3
4
	for (int i = 0; i < 81; ++i)    // <====== ????   9, not 81
	{
		delete[] board[i];
	}



Personally, I'd use vector<vector<int>> rather than your char**


Also, how are you distinguishing the numbers that were fixed at input (line127ff), since you wouldn't be allowed to empty these (line 112)?
Last edited on
I would also recommend having the ability to store up to 9 possible candidates for each cell. By removing the candidates 1 by 1, you then are eventually left with a solution for that cell. Remember that when a cell is solved, one must propagate that through the remaining candidates.

By having 9 candidates for each of the 81 cells, this makes 4 layers of pointers a difficult solution. I hope you are not using pointers because of the new

There are more complex concepts such as locked pairs and locked triples and others, which a program needs to use to solve.

Also be aware there is more than 1 solution for some of the more difficult problems. I have heard of up to 27 separate solutions.

Programs I have seen were about 600 LOC IIRC.

Good Luck
@lastchance Thanks for that I changed it to 9. And the the empty on line 112 just reassigns one of the characters in the array with a zero. Why wouldn't I be allowed to do that?
CisntEZ wrote:
And the the empty on line 112 just reassigns one of the characters in the array with a zero. Why wouldn't I be allowed to do that?


Sudoku puzzles start with a grid with some of the numbers filled in. These initial ones are fixed. You aren't allowed to change them to zero. However, you may be OK, since (I think) you are only attempting to start from the empty spots. So, it was probably a red herring and you can ignore my comment there.


Incidentally, another reason for using vector<vector<int>> is that you aren't obliged to have 9 by 9 Sudoku grids, but if you have 16 by 16, say, you are going to have trouble if you use char. (But your input would have to deliberately use 0 for a space, rather than '.')

Anyway, my 140 lines of code gave this for your puzzle:
Original
+---+---+---+
|3.6|5.8|4..|
|52.|...|...|
|.87|...|.31|
+---+---+---+
|..3|.1.|.8.|
|9..|863|..5|
|.5.|.9.|6..|
+---+---+---+
|13.|...|25.|
|...|...|.74|
|..5|2.6|3..|
+---+---+---+

Result:
+---+---+---+
|316|578|492|
|529|134|768|
|487|629|531|
+---+---+---+
|263|415|987|
|974|863|125|
|851|792|643|
+---+---+---+
|138|947|256|
|692|351|874|
|745|286|319|
+---+---+---+
Last edited on
Also, I wanted to use an int array[][] or the 2D vector but did not because I wasnt sure how to convert my string input into them.
Thanks. And I will look around and see if I can some how parse my string into a 2D vector.
Do you know of any ways on how I can parse that input string into a 2D vector? I cant see to get it. Thank you.
1
2
3
4
5
6
7
8
9
10
11
12
13
const int N = 2;

  int num[N][N];  
  /*
    row / col => index
    num[0][0] => 0
    num[0][1] => 1
    num[1][0] => 2
    num[1][1] => 3
  */
  int index = 3;
  int row = index / N;
  int col = index % N;


If you like video tuts have a look at:
https://www.youtube.com/watch?v=IX3qHsLGvq8
https://www.youtube.com/watch?v=P9_F7kuHU90


Thanks. Also it appears I am getting a seg fault on line 76 no idea why
Look at line 72. Can you see why that loop will never end, and why i will keep incrementing forever, until it causes you to try and access memory beyond the end of the array?
@CisntEZ,
Please don't change earlier code in the thread once it has been discussed. It makes it impossible to follow the thread, especially regarding line numbers.

Also note that it is better if you upload as a single file, so that we can run it in CPP shell (the little icon to top of a runnable code sample). You can soon separate the headers off into different files later. Please note that it is not conventional to put function definitions in what are expected to be header files (.h).


Cisnt wrote:
Thanks. Also it appears I am getting a seg fault on line 76 no idea why
MikeyBoy has pointed out your confusion over i and row in (what is currently) line 72. Also, the last version of code you have actually posted still has
1
2
3
4
for (int i = 0; i < 81; ++i)
	{
		delete[] board[i];
	}

As stated earlier, there are NOT 81 rows.


CisntEZ wrote:
Do you know of any ways on how I can parse that input string into a 2D vector?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>
#include <vector>
using namespace std;

using matrix = vector< vector<int> >;

matrix stringToGrid( string s, int n, char blank )
{
   matrix grid( n, vector<int>( n, 0 ) );
   int pos = 0;
   for ( int i = 0; i < n; i++ )
   {
      for ( int j = 0; j < n; j++ )
      {
         if ( s[pos] != blank ) grid[i][j] = s[pos] - '0';
         pos++;
      }
   }

   return grid;
}


int main()
{
  const int N = 9;
  string s{ "306508400520000000087000031003010080900863005050090600130000250000000074005206300" };

  matrix grid = stringToGrid( s, N, '0' );

  for ( auto row : grid )
  {
     for ( auto e : row ) cout << e;
     cout << '\n';
  }
}

306508400
520000000
087000031
003010080
900863005
050090600
130000250
000000074
005206300




Please make your corrections and then post updated code:
- as a SINGLE file;
- BELOW this post.

Last edited on
@Mikeyboy thank you!! I had to change the variables earlier and missed one. The program now runs desired output is not correct but its a start.

@lastchance thank you. and my apologies I thought I was being helpful by updating it but I see now how that can cause an issue.
Here is the updated code. I changed it and put it all in one file so everyone can run it easier.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include <iostream>
#include <string>

#define NUM_ROWS 9
#define NUM_COLS 9

// Preprocessor directives
char ** GetInput();
void freeBoard(char **board);
void printBoard(char **board);
bool Solver(char ** board);
bool FindEmptySquares(char ** board, int &row, int &col);
bool NotPlayed(char ** board, int row, int col, int number);
bool UsedInRow(char ** board, int row, int number);
bool UsedInCol(char ** board, int col, int number);
bool UsedInBox(char **board, int startingRow, int startingCol, int number);

int main()
{
	char **board = GetInput();

	//game.printBoard(board);
	if (Solver(board))
	{
	    printBoard(board);
	}

	else
	{
		std::cout << "No solution found";
	}

	freeBoard(board);

	//system("pause");
	return 0;
}



bool UsedInBox(char ** board, int startingRow, int startingCol, int number)
{
	for (int row = 0; row < 3; ++row)
	{
		for (int col = 0; col < 3; ++col)
		{
			if (board[row + startingRow][col + startingCol] == '0' + number)
			{
				return true;
			}
		}
	}
	return false;
}

bool UsedInCol(char ** board, int col, int number)
{
	for (int row = 0; row < NUM_ROWS; ++row)
	{
		if (board[row][col] == '0' + number)
		{
			return true;
		}
	}
	return false;
}
bool UsedInRow(char ** board, int row, int number)
{
	for (int col = 0; col < NUM_COLS; ++col)
	{
		if (board[row][col] == '0' + number)
		{
			return true;
		}
	}
	return false;
}
bool NotPlayed(char ** board, int row, int col, int number)
{
	if ((UsedInRow(board, row, number) && UsedInCol(board, col, number))) //&&
		//UsedInBox(board, row - row % 3, col - col % 3, number)))
	{
		return false;
	}

	return true;
}

bool FindEmptySquares(char ** board, int &row, int &col)
{
	char mychar = '0';
	for (int i = 0; i < NUM_ROWS; ++i)
	{
		for (int j = 0; j < NUM_COLS; ++j)
		{
			if (board[i][j] == mychar)
			{
				row = i;
				col = j;
				return true;
			}
		}
	}
	return false;
}

bool Solver(char **board)
{
	int row = 0;
	int col = 0;
	char empty = '0';
	
	if (!FindEmptySquares(board, row, col))
	{
		return true;
	}
	// Check for numbers 1-9
	for (int number = 0; number <= 9; ++number)
	{
		if (NotPlayed(board, row, col, number))
		{
			// Make an assignment tentatively
			board[row][col] = '0' + number;
			
			// If successful return true
			if (Solver(board))
			{
				return true;
			}
			// Else failure, assign an "empty" square and try again
			else
			{
				board[row][col] = empty;
			}
		}
	}
    return false; // trigger backtracking
}

char ** GetInput()
{
	std::string input;
	char **board = new char*[NUM_ROWS];

	//std::cout << "Enter the board, 0 being an empty square" << std::endl;
	//std::cin >> input;

    input = "306508400520000000087000031003010080900863005050090600130000250000000074005206300";

	//input = "007401680186300040900806710609040530070085902850039004090070806268904007005260091";
	
	for (int row = 0; row < NUM_ROWS; ++row)
	{
		board[row] = new char[NUM_ROWS];
		for (int col = 0; col < NUM_COLS; ++col)
		{
			 board[row][col] = input[col + (row * NUM_COLS)];
		}
	}
	


	return board;
}

void printBoard(char **board)
{
	for (int row = 0; row < 9; row++)
	{
		for (int col = 0; col < 9; col++)
			std::cout << board[row][col];
		std::cout << "\n";
	}
}

void freeBoard(char **board)
{
	for (int i = 0; i < 9; ++i)
	{
		delete[] board[i];
	}
	delete[] board;
}
In your "NotPlayed()" routine you need OR (||), not AND (&&). Try:
if ( UsedInRow(board, row, number) || UsedInCol(board, col, number) || UsedInBox(board, row - row % 3, col - col % 3, number) )


On line 118 you should be placing numbers 1 to 9, not 0 to 9; so:
for (int number = 1; number <= 9; ++number)


Make those two changes and it gives the same result as my solver.
Last edited on
Pages: 12