My First REAL C++ Program!

So…

I have been working on this for a few days and although I’m not quite done with it, I wanted to go ahead and get some feedback from you guys here. I haven’t done a whole lot of programming before, but this is my first REAL C++ program to accomplish a task.

It’s a Sudoku Solver!

So yeah, my sudoku solving algorithm lies upon a few ideas. Imagine this, Take a sudoku puzzle and write the numbers 1-9 in each blank square. Find the first “filled-in” square (one that is filled in from the beginning). Let’s say it’s a 7. From that square, erase all of the 7’s up and down and across and “all around” (in the same 9x9 sector). This is in the function called ClearRoutine. Move on to the next “filled in” number and do the same. Repeat until you’re done with the entire set of “pre-filled numbers.”

Once you’re done with this you’ll have a few cells which only have one number left. That is the new number for the cell, obviously. Repeat the above step for the new “filled-in” numbers.

After you do that, you look across, up and down, and in the sectors for numbers that appear only once. So if you look at a column, and the number 3 appears as a possibility only once (remember at the beginning we filled the empty squares with all possible numbers and started erasing them). Thus the only square that has 3 as a possibility in that column MUST be 3. This is in the function called FindSolos

This is the logic I tried to include in the program.

Here’s how I went about it…
I started with an array…Yes I realize there’s probably better ways of doing this. That’s why I’m writing this! Please help me improve it if possible!

So I started with an array. It’s a 3d array called grid. it’s initialized as grid[10][10][10] because I wanted to be able to access the cells in a more natural way. I didn’t want to have to call the first one 0,0 but 1,1

Anyway, the first two elements are i, and j. I is the horizontal compenent, and j is the vertical. I realize this is backwards-like but I got used to it. The third component is the important part. That is what I call “k.” Let me explain a bit here with an example.

If cell 1,1 is filled with the number 6, that will be represented by grid[1][1][0]=5. That is to say when a cell is finished, grid*[j][0]=whatever number it is. Otherwise grid*[j][0]=0. So what do grid*[j][1-9] represent? That is where I stored the values for the possible values. If 5 has been ruled out for cell 1,1 then grid[1][1][5]=0. Obviously at the start I fill grid*[j][1-9] with all ones.

So anyway here is the code:



#include <iostream>
#include <string>
#include <fstream>
using namespace std;

void ReadInGrid();
void PrintOutGrid();
void FillPossible();
void ClearRoutine(int, int, int);
void WriteNumb(int, int, int);
void CheckNewFinished();
void IsDone();
void FindSolos();
void DetailComplete();
void CheckNew();

int grid [10][10][10];
int i, j, k;
int o;

int main ()
	{
	
	ReadInGrid();
	PrintOutGrid();
	FillPossible();
	CheckNew();
	int count=0;

	while (grid [0][0][0]!=1 && count < 30)
		{
		CheckNewFinished();
		FindSolos();
		cout << "Pass Number: "<< count << endl << endl;
		PrintOutGrid();
		DetailComplete();
		count++;

		/* cout << "Number Test"<< endl;
		cout << "input i" << endl;
		cin >> i;
		cout << "input j" << endl;
		cin >> j;
		cout << "detail grid" << endl;
		DetailComplete();
		cout << "Some Stats:"<< endl;
		for (k=1; k<=9; k++){
			cout << k << " "<< grid *[j][k]<< ", "<< endl;
				}*/
		}
		return 0;
	}
void ReadInGrid()
	{
	ifstream sdkin("/Users/Mike/sudoku.sdk");

		for (i=1; i<=9; i++)
			{
			j=1;
			for(j=1; j<=9; j++)
				{
				sdkin >> grid *[j][0]; //*[j][0] is the position for the confirmed numbers
				}
			}
	}
void PrintOutGrid()
	{
	cout << "suduko grid as of now"<< endl;
	cout << " --------------------"<< endl;

		for (i=1; i<=9; i++)
			{
			j=1;
			cout << "|";
			for(j=1; j<=9; j++)
				{
				if(grid*[j][0]!=0) cout << grid*[j][0] << " ";
				else cout << "  ";
				if(j%3==0)cout << "|";
				}
			cout << endl;
			if(i%3==0)cout << "---------------------"<< endl;
			}
	}
void FillPossible()
//fills in all of the k values for all empty cells
//only run to initialize in the beginning	
	{	
	for (i=1; i<=9; i++){
		for (j=1; j<=9; j++){

			//new cycle for the possible values in k
			if (grid*[j][0]==0)
				for (k=1; k<=9; k++){
					grid*[j][k]=1;
				}
		}
	}

}

void CheckNewFinished(){
int total = 0;
int newnumb = 0;

	for (i=1; i<=9; i++){
		for (j=1; j<=9; j++){
			for (k=1; k<=9; k++){
				total += grid *[j][k];
				if (grid*[j][k] == 1)
					newnumb=k;
				}

			if (total == 1)
				{
				WriteNumb(i, j, newnumb);
				cout << "HI OPAL!!!"<<endl;
				cout << "Location: " << i << ", "<< j << endl;
				}
			if (grid*[j][0]>0)
				{
				WriteNumb(i,j,grid*[j][0]);
				}
			total=0;
			newnumb=0;
		}
	}

}

void IsDone(){
	int count = 0;
	for(i=1; i<=9; i++){
		for (j=1; j<=9; j++){
			if (grid*[j][0]==0) count++;

		}
	}

	if (count<=1) grid[0][0][0]=1;
	count=0;
}


void FindSolos(){
//this function also incorporates another sudoku solving technique
//namely that if there is only one existing k:x in a column, row, or quadrant
//then then X must be located in the square. So this function starts and checks every
//row starting with the number one. If the row contains only one cell with a possibility
//of a 1 then it marks it there. Same for the vertical. The final part of this functin
//does a test of all 9 quadrants in a similar way to the ClearRoutine earlier

	int test, testsum, posi, posj;
	
	
	posi=0;
	posj=0;
	testsum=0;
	
//horizontal
	for(test=1; test<=9; test++)
		{
		for(i=1; i<=9; i++)
			{
			testsum=0;
			posi=0;
			posj=0;
			
			for (j=1; j<=9; j++)
				{
				if (grid*[j][test]==1)
					{
					posi=i;
					posj=j;
					testsum++;
					}
			
				}
			
			if (testsum==1)
				{
				WriteNumb(posi, posj, test);
				}
		
			}
		}

	posi=0;
	posj=0;
	testsum=0;
/*	
//vertical
	for(test=1; test<=9; test++)
		{
		for(j=1; j<=9; i++)
			{
			testsum=0;
			posi=0;
			posj=0;
			
			for (i=1; i<=9; i++)
				{
				if (grid*[j][test]==1)
					{
					posi=i;
					posj=j;
					testsum++;
					}
			
				}
			
			if (testsum==1)
				{
				WriteNumb(posi, posj, test);
				}
		
			}
		}

*/


}

void DetailComplete(){
int i, j, test;

cout << " ---------------------------------------------------------------- "<< endl;

for (i=1; i<=9; i++){
	cout << "|";
	for (j=1; j<=9; j++){
		
		for (test=1; test<=3; test++){
			if(grid*[j][test]==1)cout << test << " ";
			else cout << "  ";
			
			if(test%3==0 && j%3==0 && j<9) cout << "||";
			else if(test%3==0) cout << "|";
			

		}	
	}
	cout << endl;	
	cout << "|";
	for (j=1; j<=9; j++){
		for (test=4; test<=6; test++){
			if(grid*[j][test]==1)cout << test << " ";
			else cout << "  ";
			if(test%3==0 && j%3==0 && j<9) cout << "||";
			else if(test%3==0) cout << "|";
		}
	}
	cout <<  endl;
	cout << "|";
	for (j=1; j<=9; j++){
		for (test=7; test<=9; test++){
			if(grid*[j][test]==1)cout << test << " ";
			else cout << "  ";
			if(test%3==0 && j%3==0 && j<9) cout << "||";
			else if(test%3==0) cout << "|";		}
	}
	if(i%3==0 && i<9)cout << "
 ---------------------------------------------------------------- 
 ---------------------------------------------------------------- ";
	else cout <<      "
 ---------------------------------------------------------------- ";

cout << endl;
}

}

void ClearRoutine(int i, int j, int numb)
//clears the relavent parts of the suduko grid
//based on a solved square. This will remove the
//possibility (k values) from all vertical, horizontal
//and quadrant positions. i and j are the position in the
//grid and numb is the number that will be cleared. 
	{
	int q;
	//cout <<"please pass"<< endl;
	//cin >> q;
	//DetailComplete();
	//PrintOutGrid();
	int oldi, oldj, indexi, indexj;
	oldi=i;
	oldj=j;
	

	//horizontal
	for (i=1; i<=9; i++)
		{
		grid*[j][numb]=0;
		}

	i=oldi;					//reset to original values		
	j=oldj;
	
	//vertical
	for (j=1; j<=9; j++)
		{
		grid*[j][numb]=0;
		}
	

	i=oldi;
	j=oldj;
	
	/* In order to do operations on the square
	it is important to make sure that you can 
	input any number and come up with a consistent
	way to clear the cells in the "quadrant" So the
	way it's done here is to subtract one then divide by 3.
	then multiplying by 3 and adding one always results in
	the "index square" which is the top left corner of the
	9x9 "quadrant" to be cleared. This is what is done below
	*/
	
	
	indexi=(((i-1)/3)*3)+1;		
	indexj=(((j-1)/3)*3)+1;
	
	
	// now loop to clear 9x9 quadrant
	
	for (i=indexi; i<=indexi+2; i++)
		{
		for (j=indexj; j<=indexj+2; j++)
			{
			grid*[j][numb]=0;
			}
		}
	
	
	
	}
	
	
	
	
	
void WriteNumb(int i, int j, int numb)
//this is the standard function for writing
//a number to the grid. It's good to control
//it this way in order to make sure the appropriate
//clearing routines are done afterwards to update
//the grid with a new status
	{
	grid*[j][0]=numb;		
	ClearRoutine(i, j, numb);
	for(k=1; k<=9; k++)
		{
		grid*[j][k]=0;		//since it's done, set all other k values to 0
		}
}


void CheckNew()
//This is to initialize the array based on the 
//beginning values.	
	{
	for (i=1; i<=9; i++)
		{
		for (j=1; j<=9; j++)
			{
			if (grid*[j][0]>0){
				{
				ClearRoutine(i,j,grid*[j][0]);
				}
			}
		}
		}
	}

//Finde Finished
//ClearRoutine



Also, if you want to compile and run it you’ll need to change the path for the input file which is in the function ReadInGrid. The format for the file is as follows:



0 5 0 7 0 0 0 0 8
0 0 3 0 5 4 0 7 0
2 0 9 3 0 0 5 0 4
0 0 5 1 0 2 4 0 0
3 4 0 0 0 0 0 0 7
0 0 1 4 3 0 9 0 5
0 0 2 0 0 0 0 5 6
0 3 0 6 0 5 8 0 2
5 9 0 0 2 3 0 0 0


Essentially just write out the numbers and keep spaces in between the numbers. Plain text. Also notice that in the FindSolos function I only have the horizontal component. For some reason when I turned on the vertical part it froze. I have no idea why. And also to do functions in the same quadrant or whatever you want to call it, I did a weird division thing to go to an index cell and do a loop from there.

But the way it is now, it seems to solve the puzzle except for the squares that require guessing. I have tested it with a few puzzles and have found that the ones it filled in were correct. I do want to figure out some way to make it guess the correct answers down the line though.

Anyway, I don’t know a whole lot about this (just finished intro to CompSci with C++) and just want any kind of feedback whatsoever. Should I have gone about it another way? What do you think of what I’ve done? Also do you guys know of a place I could possibly post this to get more feedback? Maybe a more targeted board?

Thanks agian

ETA: Just wanted to ask, how can I prompt the user for the path of the input file? I was having trouble doing that with a string, that’s why I included the string library.

Sorry, but also…
if you run the program, the output is a smaller grid which is what you’d actually see in a sudoku puzzle, and then a larger grid that shows the possibilites within each grid. Essentially the second grid is a blown-up more detailed version of the first.

Easiest is just to pass it in as an argument to the application.


int main(int argc, char** argv) {
   if (argc != 2) {
      printf("Usage: sudosolve <input file path>
");
      return 1;
   }

   ...
}

Cool about the solver. But note that sudoku should never involve guessing–you just need to add in the more advanced techniques of deduction.

That’s a really wonky indenting style you have, by the way. I’d recommend adopting one of the two leading styles, or your code will mess up anyone else who ever tries to edit it.


if () {
   ...
   ...
}
else {
   ...
   ...
}

if ()
{
   ...
   ...
}
else
{
   ...
   ...
}

Hey thanks!

I am going to try to figure out to add in more advanced deduction techniques but right now the FindSolos routine is making it hang.

I don’t know though, I feel like there is probably some easier way to do this with classes but I’m not sure. I never got that far into it.

Also about the indenting style. I have previously used the first one: if () { all on one line but then decided to go and make it different.

So is this a good “first program” for a portfolio? I have no idea.

I really like the thing where it shows all of the possibilities. When I get it to where it will solve a puzzle for sure I’ll have it automatically kick out of the loop in main and be less verbose while doing it.

Okay guys, here it is again in revised form. I’ve tried to make the code as readable as possible plus add in some comments where necessary. I’ve changed most things about it but none of the core logic. I’ve just made it a bit more streamlined.



/****************************************************************
* Sudoku Solver by: Michael Allen								*
*																*
* Description:													*
* This is a little program I wrote to solve (or help solve)		*
* Sudoku puzzles. The information is stored in the array		*
* grid*[j][k]. These are the letters I have used to refer to	*
* the three indices of the array.								*
* The first two indices refer to the position on the sudoku		*
* grid. To work on top-right cell you'd refer to grid[1][9][k]	*
* The K value is where the possibility tables lie.				*
* The program starts by filling the possibility table with		*
* values of 1 (to denote that it is possible) for values		*
* of k 1-9. Then through the ClearRoutine function it removes   *
* these possibilities based on sudoku rules.					*
*																*
* Complteted cells:												*
* The digits 1-9 are stored in the zero position of k to denote *
* a complete cell												*
*																*
* Some examples:												*
* grid[1][1][0]=5; Cell 1,1 is finished and contains the number *
* five.															*
*																*
* grid [5][5][2]=1; Cell 5,5 is not finished and may possibly   *
* contain a 2													*
*																*
* When you see the printout of the Sudoku Grid you'll see boxes *
* like this:													*
*																*		
*		  1,1													*
*		-------													*
*		|  2  |													*
*		|4   6|													*
*		|  8 9|													*
*		-------													*
* This is repsresented by k values as follows:					*
* grid [1][1][1] = 0											*
* grid [1][1][2] = 1											*
* grid [1][1][3] = 0											*
* grid [1][1][4] = 1											*
* grid [1][1][5] = 0											*
* grid [1][1][6] = 1											*
* grid [1][1][7] = 0											*
* grid [1][1][8] = 1	 										*
* grid [1][1][9] = 1											*
*																*
*																*
* When there remains only one k value in the cell, it is        *
* written to the visible portion. ClearRoutine clears these     *
* based on whether it is possible from the surrounding numbers  *
* and FindSolo finds instances where if there is (for example)  *
* only one possible 2 in a column row or quadrant then it must  *
* be located there												*
****************************************************************/
#include <iostream>
#include <fstream>
using namespace std;

void ReadInGrid();
void FillPossible();
void ClearRoutine(int, int, int);
void WriteNumb(int, int, int);
void CheckNewFinished();
void IsDone();
void FindSolos();
void DetailComplete();
void CheckNew();
void Solve();

int grid [10][10][10];
int i, j, k;
int verb;

int main ()
{
	cout << "Verbose? (1 for yes 2 for no)" << endl;
	cin >> verb;
	
	grid[0][0][0]=0; //set counter to zero
	
	ReadInGrid();
	DetailComplete();
	FillPossible();
	CheckNew();
	Solve();
	DetailComplete();
	return 0;
}

void ReadInGrid()
//Reads in the grid from a file that is in the default location:
{

	ifstream sdkin("/Users/Mike/sudoku.sdk");

		for (i=1; i<=9; i++)
		{
			j=1;
			for(j=1; j<=9; j++)
			{
				sdkin >> grid *[j][0]; //*[j][0] is the position for the confirmed numbers
			}
		}
}

void FillPossible()
//fills in all of the k values for all empty cells
//only run to initialize in the beginning	
{	
	for (i=1; i<=9; i++)
	{
		for (j=1; j<=9; j++)
		{
			//new cycle for the possible values in k
			if (grid*[j][0]==0)
				for (k=1; k<=9; k++)
				{
					grid*[j][k]=1;
				}
		}
	}
}

void CheckNewFinished()
//This function loops through and checks the
//possibility tables for each cell and adds it up
//everytime it finds that a number is possible
//it adds 1 to total, and records the number
//if, in the end there was only one number in the possibility
//table it calls WriteNumb for that number
{
	int total = 0;
	int newnumb = 0;

	for (i=1; i<=9; i++)
	{
		for (j=1; j<=9; j++)
		{
			for (k=1; k<=9; k++)
			{
				total += grid *[j][k];
				if (grid*[j][k] == 1)
					newnumb=k;
			}

			if (total == 1)
			{
				WriteNumb(i, j, newnumb);
			}
			
			if (grid*[j][0]>0)
			{
				//WriteNumb(i,j,grid*[j][0]);
			}
			total=0;
			newnumb=0;
		}
	}

}

void FindSolos()
//this function also incorporates another sudoku solving technique
//namely that if there is only one existing k:x in a column, row, or quadrant
//then then X must be located in the square. So this function starts and checks every
//row starting with the number one. If the row contains only one cell with a possibility
//of a 1 then it marks it there. Same for the vertical. The final part of this functin
//does a test of all 9 quadrants in a similar way to the ClearRoutine earlier
{
	int test, testsum, posi, posj;
	posi=0;
	posj=0;
	testsum=0;
	
//horizontal
	for(test=1; test<=9; test++)
	{
		for(i=1; i<=9; i++)
		{
			testsum=0;
			posi=0;
			posj=0;
			
			for (j=1; j<=9; j++)
			{
				if (grid*[j][test]==1)
				{
					posi=i;
					posj=j;
					testsum++;
				}
			}
			
			if (testsum==1)
			{
				WriteNumb(posi, posj, test);
			}
		
		}
	}

	posi=0;
	posj=0;
	testsum=0;

//vertical
	for(test=1; test<=9; test++)
	{
		for(j=1; j<=9; j++)
		{
			testsum=0;
			posi=0;
			posj=0;
			
			for (i=1; i<=9; i++)
			{
				if (grid*[j][test]==1)
				{
					posi=i;
					posj=j;
					testsum++;
				}
			
			}
			
			if (testsum==1)
			{
				WriteNumb(posi, posj, test);
			}
		}
	}

	posi=0;
	posj=0;
	testsum=0;
	int indexi=0;
	int indexj=0;	
	
	// now loop to clear 9x9 quadrant
	
	for (test=1; test<=9; test++)
	{
		for (i=1; i<=9; i+=3)
		{
			for (j=1; j<=9; j+=3)
			{
				for (indexi=i; indexi<=i+2; indexi++)
				{
					for(indexj=j; indexj<=j+2; indexj++)
					{
						if(grid[indexi][indexj][test]==1)
						{
							posi=indexi;
							posj=indexj;
							testsum++;
							
						}
					}
				}
				
				if (testsum==1)
				{
					WriteNumb(posi, posj, test);
					
				}
				
				testsum=0;
				
			}
		}
	}

	

}	

void DetailComplete()
//This function shows the possiblity details in the
//grid. It requires a three part loop to print it because 
//of the way the numbers are displayed. This is the new 
//display as it is more detailed and I have modified it to
//display the actual numbers alongside the possible ones once
//I resolved a few earlier errors.
{

	int i, j, test;
	cout << " ---------------------------------------------------------------- "<< endl;

	for (i=1; i<=9; i++)
	{
		cout << "|";						//creates left hand line
		
		
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0) 
				{
				cout<< ".----.";
				if (j%3==0 && j<9) cout << "||";
				else cout << "|";
				}
					
			else for (test=1; test<=3; test++)
			{
				if(grid*[j][test]==1)cout << test << " ";
				else cout << "  ";
				
				if(test%3==0 && j%3==0 && j<9) cout << "||";
				else if(test%3==0) cout << "|";
				
			}	
		}
		cout << endl;	
		cout << "|";
		
		
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0) 
				{
				cout<< "|  "<< grid*[j][0]<< " |";
				if (j%3==0 && j<9) cout << "||";
				else cout << "|";
				}

		
		
			else for (test=4; test<=6; test++)
			{
				 if(grid*[j][test]==1)cout << test << " ";
				else cout << "  ";
				if(test%3==0 && j%3==0 && j<9) cout << "||";
				else if(test%3==0) cout << "|";
			}
		}
		cout <<  endl;
		cout << "|";
		
		
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0) 
				{
				cout<< "˙----˙";
				if (j%3==0 && j<9) cout << "||";
				else cout << "|";
				}

			else for (test=7; test<=9; test++)
			{
				if(grid*[j][test]==1)cout << test << " ";
				else cout << "  ";
				if(test%3==0 && j%3==0 && j<9) cout << "||";
				else if(test%3==0) cout << "|";		
			}
		}
		
		if(i%3==0 && i<9)cout << "
 ---------------------------------------------------------------- 
 ---------------------------------------------------------------- ";
		else cout <<      "
 ---------------------------------------------------------------- ";

		cout << endl;
	}

}

void ClearRoutine(int i, int j, int numb)
//clears the relavent parts of the suduko grid
//based on a solved square. This will remove the
//possibility (k values) from all vertical, horizontal
//and quadrant positions. i and j are the position in the
//grid and numb is the number that will be cleared. 
{
	int oldi, oldj, indexi, indexj;
	
	
	oldi=i;
	oldj=j;
	for (i=1; i<=9; i++)		//horizontal
	{
		grid*[j][numb]=0;
	}

	
	i=oldi;						//reset to original values		
	j=oldj;
	for (j=1; j<=9; j++)		//vertical
	{
		grid*[j][numb]=0;
	}
	

	
	/* In order to do operations on the square
	it is important to make sure that you can 
	input any number and come up with a consistent
	way to clear the cells in the "quadrant" So the
	way it's done here is to subtract one then divide by 3.
	then multiplying by 3 and adding one always results in
	the "index square" which is the top left corner of the
	9x9 "quadrant" to be cleared. This is what is done below
	*/
	
	i=oldi;
	j=oldj;
	indexi=(((i-1)/3)*3)+1;		
	indexj=(((j-1)/3)*3)+1;
	for (i=indexi; i<=indexi+2; i++)	// now loop to clear 9x9 quadrant
	{
		for (j=indexj; j<=indexj+2; j++)
		{
			grid*[j][numb]=0;
		}
	}


}
	
void WriteNumb(int i, int j, int numb)
//this is the standard function for writing
//a number to the grid. It's good to control
//it this way in order to make sure the appropriate
//clearing routines are done afterwards to update
//the grid with a new status
{
	grid*[j][0]=numb;		
	ClearRoutine(i, j, numb);
	for(k=1; k<=9; k++)
		{
		grid*[j][k]=0;		//since it's done, set all other k values to 0
		}
	grid[0][0][0]++;			//counter used to stop loop.
}

void CheckNew()
//This is to initialize the array based on the 
//beginning values. This is different from CheckNewFinished
//becuase it checks the 0 position rather than the possibility table
//This is for the startup, CheckNewFinished looks for cells where only
//one possibility exists and writes that number to the visible part.
{
	for (i=1; i<=9; i++)
	{
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0)		//test if finished and if so update possibility table
			{
				ClearRoutine(i,j,grid*[j][0]);
			}	
		}
	}
}

void Solve()
{
//This used to be the main function but I moved it so I could
//possibly try different solving techniques. Since this stops
//once new numbers aren't being added it works well for that

	int oldcount=0;
	int newcount=1;
	
	while (oldcount<newcount)
	{
		if (verb==1) DetailComplete();
		oldcount=grid[0][0][0];
		CheckNewFinished();
		FindSolos();
		newcount=grid[0][0][0];
	}
}


Here’s the input file I’ve been using:



0 0 0 1 0 0 0 4 0 
1 5 0 0 3 6 0 8 0 
0 0 8 0 4 5 0 0 0
9 0 0 7 0 0 0 2 0
0 3 0 0 0 0 0 5 0
0 8 0 0 0 2 0 0 1
0 0 0 3 1 0 2 0 0
0 9 0 5 2 0 0 1 7
0 1 0 0 0 8 0 0 0


A question about passing arguments via the command line. I don’t understand that code up above (and pasted below):


int main(int argc, char** argv) {
   if (argc != 2) {
      printf("Usage: sudosolve <input file path>
");
      return 1;
   }

   ...
}


I’m working with an input like this:
ifstream sdkin("/Users/Mike/sudoku.sdk");
I then use sdkin to fill in my array with a loop.

Now how would I use what you showed me above to tell the program down below where to find the file? Would it be in a variable? Why does it test to see if argc != 2? Why does it return 1?

Okay guys, here it is again in revised form. I’ve tried to make the code as readable as possible plus add in some comments where necessary. I’ve changed most things about it but none of the core logic. I’ve just made it a bit more streamlined.



/****************************************************************
* Sudoku Solver by: Merkwurdigliebe								*
*																*
* Description:													*
* This is a little program I wrote to solve (or help solve)		*
* Sudoku puzzles. The information is stored in the array		*
* grid*[j][k]. These are the letters I have used to refer to	*
* the three indices of the array.								*
* The first two indices refer to the position on the sudoku		*
* grid. To work on top-right cell you'd refer to grid[1][9][k]	*
* The K value is where the possibility tables lie.				*
* The program starts by filling the possibility table with		*
* values of 1 (to denote that it is possible) for values		*
* of k 1-9. Then through the ClearRoutine function it removes   *
* these possibilities based on sudoku rules.					*
*																*
* Complteted cells:												*
* The digits 1-9 are stored in the zero position of k to denote *
* a complete cell												*
*																*
* Some examples:												*
* grid[1][1][0]=5; Cell 1,1 is finished and contains the number *
* five.															*
*																*
* grid [5][5][2]=1; Cell 5,5 is not finished and may possibly   *
* contain a 2													*
*																*
* When you see the printout of the Sudoku Grid you'll see boxes *
* like this:													*
*																*		
*		  1,1													*
*		-------													*
*		|  2  |													*
*		|4   6|													*
*		|  8 9|													*
*		-------													*
* This is repsresented by k values as follows:					*
* grid [1][1][1] = 0											*
* grid [1][1][2] = 1											*
* grid [1][1][3] = 0											*
* grid [1][1][4] = 1											*
* grid [1][1][5] = 0											*
* grid [1][1][6] = 1											*
* grid [1][1][7] = 0											*
* grid [1][1][8] = 1	 										*
* grid [1][1][9] = 1											*
*																*
*																*
* When there remains only one k value in the cell, it is        *
* written to the visible portion. ClearRoutine clears these     *
* based on whether it is possible from the surrounding numbers  *
* and FindSolo finds instances where if there is (for example)  *
* only one possible 2 in a column row or quadrant then it must  *
* be located there												*
****************************************************************/





#include <iostream>
#include <fstream>
using namespace std;

void ReadInGrid();
void FillPossible();
void ClearRoutine(int, int, int);
void WriteNumb(int, int, int);
void CheckNewFinished();
void IsDone();
void FindSolos();
void DetailComplete();
void CheckNew();
void Solve();

int grid [10][10][10];
int i, j, k;
int verb;

int main ()
{
	cout << "Verbose? (1 for yes 2 for no)" << endl;
	cin >> verb;
	
	grid[0][0][0]=0; //set counter to zero
	
	ReadInGrid();
	DetailComplete();
	FillPossible();
	CheckNew();
	Solve();
	DetailComplete();
	return 0;
}

void ReadInGrid()
//Reads in the grid from a file that is in the default location:
{

	ifstream sdkin("/Users/Mike/sudoku.sdk");

		for (i=1; i<=9; i++)
		{
			j=1;
			for(j=1; j<=9; j++)
			{
				sdkin >> grid *[j][0]; //*[j][0] is the position for the confirmed numbers
			}
		}
}

void FillPossible()
//fills in all of the k values for all empty cells
//only run to initialize in the beginning	
{	
	for (i=1; i<=9; i++)
	{
		for (j=1; j<=9; j++)
		{
			//new cycle for the possible values in k
			if (grid*[j][0]==0)
				for (k=1; k<=9; k++)
				{
					grid*[j][k]=1;
				}
		}
	}
}

void CheckNewFinished()
//This function loops through and checks the
//possibility tables for each cell and adds it up
//everytime it finds that a number is possible
//it adds 1 to total, and records the number
//if, in the end there was only one number in the possibility
//table it calls WriteNumb for that number
{
	int total = 0;
	int newnumb = 0;

	for (i=1; i<=9; i++)
	{
		for (j=1; j<=9; j++)
		{
			for (k=1; k<=9; k++)
			{
				total += grid *[j][k];
				if (grid*[j][k] == 1)
					newnumb=k;
			}

			if (total == 1)
			{
				WriteNumb(i, j, newnumb);
			}
			
			if (grid*[j][0]>0)
			{
				//WriteNumb(i,j,grid*[j][0]);
			}
			total=0;
			newnumb=0;
		}
	}

}

void FindSolos()
//this function also incorporates another sudoku solving technique
//namely that if there is only one existing k:x in a column, row, or quadrant
//then then X must be located in the square. So this function starts and checks every
//row starting with the number one. If the row contains only one cell with a possibility
//of a 1 then it marks it there. Same for the vertical. The final part of this functin
//does a test of all 9 quadrants in a similar way to the ClearRoutine earlier
{
	int test, testsum, posi, posj;
	posi=0;
	posj=0;
	testsum=0;
	
//horizontal
	for(test=1; test<=9; test++)
	{
		for(i=1; i<=9; i++)
		{
			testsum=0;
			posi=0;
			posj=0;
			
			for (j=1; j<=9; j++)
			{
				if (grid*[j][test]==1)
				{
					posi=i;
					posj=j;
					testsum++;
				}
			}
			
			if (testsum==1)
			{
				WriteNumb(posi, posj, test);
			}
		
		}
	}

	posi=0;
	posj=0;
	testsum=0;

//vertical
	for(test=1; test<=9; test++)
	{
		for(j=1; j<=9; j++)
		{
			testsum=0;
			posi=0;
			posj=0;
			
			for (i=1; i<=9; i++)
			{
				if (grid*[j][test]==1)
				{
					posi=i;
					posj=j;
					testsum++;
				}
			
			}
			
			if (testsum==1)
			{
				WriteNumb(posi, posj, test);
			}
		}
	}

	posi=0;
	posj=0;
	testsum=0;
	int indexi=0;
	int indexj=0;	
	
	// now loop to clear 9x9 quadrant
	
	for (test=1; test<=9; test++)
	{
		for (i=1; i<=9; i+=3)
		{
			for (j=1; j<=9; j+=3)
			{
				for (indexi=i; indexi<=i+2; indexi++)
				{
					for(indexj=j; indexj<=j+2; indexj++)
					{
						if(grid[indexi][indexj][test]==1)
						{
							posi=indexi;
							posj=indexj;
							testsum++;
							
						}
					}
				}
				
				if (testsum==1)
				{
					WriteNumb(posi, posj, test);
					
				}
				
				testsum=0;
				
			}
		}
	}

	

}	

void DetailComplete()
//This function shows the possiblity details in the
//grid. It requires a three part loop to print it because 
//of the way the numbers are displayed. This is the new 
//display as it is more detailed and I have modified it to
//display the actual numbers alongside the possible ones once
//I resolved a few earlier errors.
{

	int i, j, test;
	cout << " ---------------------------------------------------------------- "<< endl;

	for (i=1; i<=9; i++)
	{
		cout << "|";						//creates left hand line
		
		
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0) 
				{
				cout<< ".----.";
				if (j%3==0 && j<9) cout << "||";
				else cout << "|";
				}
					
			else for (test=1; test<=3; test++)
			{
				if(grid*[j][test]==1)cout << test << " ";
				else cout << "  ";
				
				if(test%3==0 && j%3==0 && j<9) cout << "||";
				else if(test%3==0) cout << "|";
				
			}	
		}
		cout << endl;	
		cout << "|";
		
		
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0) 
				{
				cout<< "|  "<< grid*[j][0]<< " |";
				if (j%3==0 && j<9) cout << "||";
				else cout << "|";
				}

		
		
			else for (test=4; test<=6; test++)
			{
				 if(grid*[j][test]==1)cout << test << " ";
				else cout << "  ";
				if(test%3==0 && j%3==0 && j<9) cout << "||";
				else if(test%3==0) cout << "|";
			}
		}
		cout <<  endl;
		cout << "|";
		
		
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0) 
				{
				cout<< "˙----˙";
				if (j%3==0 && j<9) cout << "||";
				else cout << "|";
				}

			else for (test=7; test<=9; test++)
			{
				if(grid*[j][test]==1)cout << test << " ";
				else cout << "  ";
				if(test%3==0 && j%3==0 && j<9) cout << "||";
				else if(test%3==0) cout << "|";		
			}
		}
		
		if(i%3==0 && i<9)cout << "
 ---------------------------------------------------------------- 
 ---------------------------------------------------------------- ";
		else cout <<      "
 ---------------------------------------------------------------- ";

		cout << endl;
	}

}

void ClearRoutine(int i, int j, int numb)
//clears the relavent parts of the suduko grid
//based on a solved square. This will remove the
//possibility (k values) from all vertical, horizontal
//and quadrant positions. i and j are the position in the
//grid and numb is the number that will be cleared. 
{
	int oldi, oldj, indexi, indexj;
	
	
	oldi=i;
	oldj=j;
	for (i=1; i<=9; i++)		//horizontal
	{
		grid*[j][numb]=0;
	}

	
	i=oldi;						//reset to original values		
	j=oldj;
	for (j=1; j<=9; j++)		//vertical
	{
		grid*[j][numb]=0;
	}
	

	
	/* In order to do operations on the square
	it is important to make sure that you can 
	input any number and come up with a consistent
	way to clear the cells in the "quadrant" So the
	way it's done here is to subtract one then divide by 3.
	then multiplying by 3 and adding one always results in
	the "index square" which is the top left corner of the
	9x9 "quadrant" to be cleared. This is what is done below
	*/
	
	i=oldi;
	j=oldj;
	indexi=(((i-1)/3)*3)+1;		
	indexj=(((j-1)/3)*3)+1;
	for (i=indexi; i<=indexi+2; i++)	// now loop to clear 9x9 quadrant
	{
		for (j=indexj; j<=indexj+2; j++)
		{
			grid*[j][numb]=0;
		}
	}


}
	
void WriteNumb(int i, int j, int numb)
//this is the standard function for writing
//a number to the grid. It's good to control
//it this way in order to make sure the appropriate
//clearing routines are done afterwards to update
//the grid with a new status
{
	grid*[j][0]=numb;		
	ClearRoutine(i, j, numb);
	for(k=1; k<=9; k++)
		{
		grid*[j][k]=0;		//since it's done, set all other k values to 0
		}
	grid[0][0][0]++;			//counter used to stop loop.
}

void CheckNew()
//This is to initialize the array based on the 
//beginning values. This is different from CheckNewFinished
//becuase it checks the 0 position rather than the possibility table
//This is for the startup, CheckNewFinished looks for cells where only
//one possibility exists and writes that number to the visible part.
{
	for (i=1; i<=9; i++)
	{
		for (j=1; j<=9; j++)
		{
			if (grid*[j][0]>0)		//test if finished and if so update possibility table
			{
				ClearRoutine(i,j,grid*[j][0]);
			}	
		}
	}
}

void Solve()
{
//This used to be the main function but I moved it so I could
//possibly try different solving techniques. Since this stops
//once new numbers aren't being added it works well for that

	int oldcount=0;
	int newcount=1;
	
	while (oldcount<newcount)
	{
		if (verb==1) DetailComplete();
		oldcount=grid[0][0][0];
		CheckNewFinished();
		FindSolos();
		newcount=grid[0][0][0];
	}
}


Here’s the input file I’ve been using:



0 0 0 1 0 0 0 4 0 
1 5 0 0 3 6 0 8 0 
0 0 8 0 4 5 0 0 0
9 0 0 7 0 0 0 2 0
0 3 0 0 0 0 0 5 0
0 8 0 0 0 2 0 0 1
0 0 0 3 1 0 2 0 0
0 9 0 5 2 0 0 1 7
0 1 0 0 0 8 0 0 0


A question about passing arguments via the command line. I don’t understand that code up above (and pasted below):




int main(int argc, char** argv) {
   if (argc != 2) {
      printf("Usage: sudosolve <input file path>
");
      return 1;
   }

   ...
}


I’m working with an input like this:
ifstream sdkin("/Users/Mike/sudoku.sdk");
I then use sdkin to fill in my array with a loop.

Now how would I use what you showed me above to tell the program down below where to find the file? Would it be in a variable? Why does it test to see if argc != 2? Why does it return 1?

Command-line arguments are easy enough. main() is passed two parameters: an int and an array of strings(char*). The int is usually called argc, the argument count. The array of strings is usually called argv, the argument vector. argc contains the number of parameters on the command line. argv is an array containing those arguments. argv[0] is always the name of the program. Therefore, argc must be greater than or equal to 1. Now, if my program is called foo and I call it like this:

foo arg1 another_arg last_arg

then argc=4
arg[0]=“foo”
arg[1]=“arg1”
arg[2]=“another_arg”
arg[3]=“last_arg”

So what you’d want is:


int main(int argc, char ** argv)
{
    if(argc != 2) {
        std::cout << "Usage " << argv[0] << " <sudoku-file
";
        return 1;
    }

    ifstream sdkin(arg[1]);
    //rest of your program


We first check that the correct number of arguments have been passed to the program(ie we have two arguments including the name of the executable). If we don’t have the correct number we print an error message and exit. Returning 1 from main is the same as saying exit(1). Exitting with a non-zero value means that the program ended with an error.

Once we know that we have the correct number of arguments, we know that argv[1] is a valid argument and we can read the file the user specified and continue.

Returning an error code when you exit early is a good habit, even though it’s sort of pointless here.

What it lets you do is run a series of command line applications from a batch file or whatever, and then detect if one of the applications along the line failed, so you know to emal the administrator a warning message, to not go on to run the next application, or whatever.

Among the *nix world, command line applications are sort of like a poor man’s DLL or object. You just create an instance, hand it some parameters on the command line, and it does its thing without you having to worry about the internals. The last job I worked on, the entire back system was a daisy chain of small applications that incrementally stepped through the process of downloading files, parsing them, inserting the data into a database, and compiling stats periodically. It was easier to create them as independent applications than as a single uber-app.

Okay, thanks for your help there Sage Rat, I see what you mean now. I got around it by using a C-style string before I read your post but I might go back and change it.

So do you guys understand it at all? I tried my best to make it understandable.

I note that you use a lot of global variables. I would suggest using globals as little as possible. Especially i, j and k: having them be globals is just asking for trouble.