I’m trying to make a C# application using Visual Studio 2008, that solves Sudoku puzzles.
My problem is that I can’t seem to figure the code for generating a new puzzle by the application. The idea that I’m trying to implement is as follows:
Start with an empty puzzle
Generate numbers for all cells in the puzzle
Empty the appropriate number of cells, based on required level of difficulty
Solve the puzzle
Is the score for the puzzle in the acceptable range for the required level of difficulty?
6a. If NO -> Regenerate puzzle (go to 1.)
6b. If YES -> Puzzle generated (display it)
Used code for “New game” button:
//--------------------------------------------------
// Starting a new game menu button
//--------------------------------------------------
public void NewToolStripMenuItem_Click(System.Object sender, System.EventArgs e)
{
if (GameStarted) // this cheking part seems to work (message is displayed and game gets saved if selected)
{
MsgBoxResult response = (MsgBoxResult)(MessageBox.Show("Doriți salvarea jocului curent?", "Salvează jocul curent...", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question));
if (response == MsgBoxResult.Yes)
{
SaveGameToDisk(false);
}
else if (response == MsgBoxResult.Cancel)
{
return;
}
}
// Changing the cursor while generating
System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;
ToolStripStatusLabel1.Text = "Se generează un puzzle nou...";
// Creating an instance for the SudokuPuzzle class
SudokuPuzzle sp = new SudokuPuzzle();
string puzzle = string.Empty;
// Determining the difficulty level selected (from menu objects)
if (EasyToolStripMenuItem.Checked)
{
puzzle = sp.GetPuzzle(1);
}
else if (MediumToolStripMenuItem.Checked)
{
puzzle = sp.GetPuzzle(2);
}
else if (DifficultToolStripMenuItem.Checked)
{
puzzle = sp.GetPuzzle(3);
}
else if (ExtremelyDifficultToolStripMenuItem.Checked)
{
puzzle = sp.GetPuzzle(4);
}
else if (EmptyPuzzleToolStripMenuItem.Checked)
{
puzzle = sp.GetPuzzle(5);
}
// Changing to default cursor
System.Windows.Forms.Cursor.Current = Cursors.Default;
StartNewGame();
// Initialisation of the grid
int counter = 0;
for (int row = 1; row <= 9; row++)
{
for (int col = 1; col <= 9; col++)
{
if (puzzle[counter].ToString() != "0")
{
SetCell(col, row, System.Convert.ToInt32(puzzle[counter].ToString()), (short)0);
}
counter++;
}
}
}
So the code for the next function called, GetPuzzle(1) :
//--------------------------------------------------
// Obtaining a new puzzle (of the required level)
//--------------------------------------------------
public string GetPuzzle(int level)
{
int score = 0;
string result;
do
{
result = GenerateNewPuzzle(level, ref score);
if (result != string.Empty)
{
// Verify if the generated puzzle is of the selected dificulty
switch (level)
{
// The average for dificutly 1
case 1:
if (score >= 42 && score <= 46)
{
goto endOfDoLoop;
}
break;
// The average for dificutly 2
case 2:
if (score >= 49 && score <= 53)
{
goto endOfDoLoop;
}
break;
// The average for dificutly 3 case 3:
if (score >= 56 && score <= 60)
{
goto endOfDoLoop;
}
break;
// The average for dificutly 4
case 4:
if (score >= 112 && score <= 116)
{
goto endOfDoLoop;
}
break;
}
}
} while (!false); // loops ending
endOfDoLoop:
return result;
}
Next function used is GenerateNewPuzzle():
//--------------------------------------------------
// Generating a new puzzle
//--------------------------------------------------
public string GenerateNewPuzzle(int level, ref int score)
{
int c;
int r;
string str;
int numberofemptycells = 0;
// Initializing the entire grid
for (r = 1; r <= 9; r++)
{
for (c = 1; c <= 9; c++)
{
actual[c, r] = 0;
possible[c, r] = string.Empty;
}
}
// Empty the stacks used
ActualStack.Clear();
PossibleStack.Clear();
// Complete by solving an empty grid
try
{
// First used logical methods to solve the grid
if (!SolvePuzzle())
{
// Then use brute force
SolvePuzzleByBruteForce();
}
}
catch (Exception)
{
// If there’s any error, return emptry string
return string.Empty;
}
// Create a copy for the actual array
actual_backup = (int[,])(actual.Clone());
// Set the number of empty cells based on the difficulty level
switch (level)
{
// For difficulty level 1
case 1:
numberofemptycells = RandomNumber(40, 45);
break;
// For difficulty level 2
case 2:
numberofemptycells = RandomNumber(46, 49);
break;
// For difficulty level 3
case 3:
numberofemptycells = RandomNumber(50, 53);
break;
// For difficulty level 4
case 4:
numberofemptycells = RandomNumber(54, 58);
break;
}
// Empty the stacks used by brute force
ActualStack.Clear();
PossibleStack.Clear();
BruteForceStop = false;
// Create empty cells
CreateEmptyCells(numberofemptycells);
// Convert the values from the actual array to string
str = string.Empty;
for (r = 1; r <= 9; r++)
{
for (c = 1; c <= 9; c++)
{
str += (string)(actual[c, r].ToString());
}
}
// Verrify that the puzzle has only one solution
int tries = 0;
do
{
totalscore = 0;
try
{
if (!SolvePuzzle())
{
// If puzzle is not solved and difficulty level is 1-3
if (level < 4)
{
// Choose another combination of cells to empty
VacateAnotherPairOfCells(ref str);
tries++;
}
else
{
// Puzzles of difficulty 4 don’t guranty a single solution
SolvePuzzleByBruteForce();
goto endOfDoLoop;
}
}
else
{
// The puzzle has 1 solution
goto endOfDoLoop;
}
}
catch (Exception)
{
return string.Empty;
}
// If too many tries are executed, exit at 50
if (tries > 50)
{
return string.Empty;
}
}
while (true);
endOfDoLoop:
// Return the obtained score and the puzzle as a string
score = totalscore;
return str;
}
And last useful (I think) function, VacateAnotherPairOfCells():
//--------------------------------------------------
// Empty another pair of cells
//--------------------------------------------------
private void VacateAnotherPairOfCells(ref string str)
{
int c;
int r;
// Search for a pair of cells to empty (the empty cells should be simetrical from the center of the grid)
do
{
c = RandomNumber(1, 9);
r = RandomNumber(1, 9);
} while (!(int.Parse(str[(c - 1) + (r - 1) * 9].ToString()) == 0));
// Restore the value of the cell from the backup array
str = str.Remove(System.Convert.ToInt32((c - 1) + (r - 1) * 9), 1);
str = str.Insert(System.Convert.ToInt32((c - 1) + (r - 1) * 9), (string)(actual_backup[c, r].ToString()));
// Restore the value of the simetrical cell
str = str.Remove(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), 1);
str = str.Insert(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), (string)(actual_backup[10 - c, 10 - r].ToString()));
// Search for another pair of cells that can be emptyed
do
{
c = RandomNumber(1, 9);
r = RandomNumber(1, 9);
} while (!(int.Parse(str[(c - 1) + (r - 1) * 9].ToString()) != 0));
// Delete the cell from the string
str = str.Remove(System.Convert.ToInt32((c - 1) + (r - 1) * 9), 1);
str = str.Insert(System.Convert.ToInt32((c - 1) + (r - 1) * 9), "0");
// Delete the simetrical cell from the string
str = str.Remove(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), 1);
str = str.Insert(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), "0");
// Reinitilisation of the grid
short counter = (short)0;
for (int row = 1; row <= 9; row++)
{
for (int col = 1; col <= 9; col++)
{
if (System.Convert.ToInt32(str[counter].ToString()) != 0)
{
actual[col, row] = System.Convert.ToInt32(str[counter].ToString());
possible[col, row] = (string)(str[counter].ToString());
}
else
{
actual[col, row] = 0;
possible[col, row] = string.Empty;
}
counter++;
}
}
}
} }
The rest of the functions and code I didn't think to be necessary as everything else works. If I import an empty or partial puzzle the application can automatically solve it using the same methods used for solving the automatically generated grids. But when I click "New puzzle" from the menu, the application gets stuck with no error (so I have to kill the process).
Maybe this isn't the easiest method to generate a valid board, and I apologize for the length of the code, but I really need to fix and use this one. I tried to fix this myself, many times, but in the last 2 months I came to no solution (just a lot of frustration from my incompetence in this matter)… So I would appreciate any kind of help that I could get from here.
Problem solved!
There is actually no problem with the code above, my problem was given to the fact that I had some coding mistake in "SolvePuzzleByBruteForce()" function. The function in question was not posted here, so the code above needs no correction and it functions properly.
I can put the "SolvePuzzleByBruteForce()" if anyone wants to, but I find it irrelevant given the fact that it's nothing special and the internet is full of such functions anyway.
To reply to the comments:
#HighCore the use of goto is not relevant to my question.
#Mark Lakata the question was not vague. I stated the problem, the thing it needed to do, and the code used. And it wasn't homework problem as I'm not in school, nor would I try 2 months to make a homework.
#Wilson thank you for your comments, it helped me trace down and figure out the problem in the bad function.
Related
I am making the quiz application on C# in Console version. I have almost done all things, but I don't know how to show the number of attempts for each question, after when the quiz is finished. If you know something, let me know.
I can not add more lines of the code, as the website doesn't allow to do it
if (keys[index] == answer) // Answer is correct
{
Console.WriteLine();
Console.WriteLine("Congratulations. That's correct!");
Console.WriteLine();
totalScore += markPerQuestion;
index++;
Console.WriteLine("The total score is: {0}", totalScore);
Console.WriteLine("Used attempt(s): {0}", attempt);
attempt = 1;
count = attempt;
markPerQuestion = 20;
}
else // Answer is incorrect
{
attempt++;
count++;
if (attempt <= 3)
{
markPerQuestion /= 2;
}
else if (attempt > 3 && attempt < 5) // The fourth attempt gives zero points
{
markPerQuestion = 0;
totalScore += markPerQuestion;
}
else if(attempt >= 5) // Move to the next question
{
Console.WriteLine("Sorry, you used all attempts for that question. Moving to the next question");
index++;
markPerQuestion = 20;
attempt = 1;
count = attempt;
continue;
}
Console.WriteLine("Oops, try again");
}
if ((index > keys.Length - 1 && index > questions.Length - 1)) // Questions and answer keys are finished
{
for (int i = 0; i < questions.Length; i++)
{
Console.WriteLine("Question {0} was answered after {1} attempt(s)", (i + 1), count);
}
break;
}
Consider this solution:
Create a public class that will allow you to store the results.
public class QuizMark
{
public int Attempts;
public int Mark;
}
For the Console app create a method to control the Quiz. Call the method Quiz() from the Main method.
private const int MAX_ATTEMPTS = 5;
private static void Quiz()
{
var quizResults = new List<QuizMark>();
var questionAnswers = new List<int>() { 1, 3, 5, 2, 3, 6 };
foreach(var a in questionAnswers)
{
var v = QuizQuestion(a);
quizResults.Add(v);
}
int i = 0;
quizResults.ForEach(e => Console.WriteLine($"Question: {++i} Attempts: {e.Attempts} Mark: {e.Mark}"));
var total = quizResults.Sum(s => s.Mark);
Console.WriteLine($"Total Points: {total}");
}
Notice the List collection that stores an object of the class QuizMark. This is where the results of each question are stored: attempts and points.
The List questionAnswers simply contains the expected answer to each of the questions.
Now create the method that is going to control how each question in the quiz will be handled:
private static QuizMark QuizQuestion(int answer)
{
var quizMark = new QuizMark();
int guess = 0; //Store ReadLine in this variable
int mark = 20;
for (int attempt = 1; attempt < MAX_ATTEMPTS + 1; attempt++)
{
guess++; //remove when adding Console.ReadLine
if (guess.Equals(answer))
{
quizMark.Attempts = attempt;
quizMark.Mark = mark;
break;
}
else
{
mark = attempt <= 3 ? mark/2 : 0;
quizMark.Attempts = attempt;
quizMark.Mark = mark;
}
}
return quizMark;
}
You will need to replace the incrementor guess++ with the actual guess the user makes. This code is designed to go though automatically just as a demonstration.
IMPORTANT NOTE:
You will want to do some error handling any time you allow users to enter data. They might enter non-integer values. Probably using a loop around a Console.ReadLine where you check the value of the input with a Int32.TryParse().
I'm doing one method for calculating a pause, which has certain breaks checks. The method of calculating the time (from-to), at the same time I want to check, and if the time is less than 30, let me report the error.
It would have succeeded in a commencement if the loop would not have been repeated.
Can I somehow, if the condition is correct, skip that part?
In the pause of the variables I come to my values: 6, 56, 4
this is the method
private double CalculateBreak(List<PRAESENZZEIT> arrivals, out string error)
{
error = null;
double totalBreak = 0;
for (int i = 1; i < arrivals.Count();)
{
for (int n=i-1; n < arrivals.Count();)
{
double pause = (arrivals[i].ZPZ_Von - arrivals[n].ZPZ_Bis).TotalMinutes;
//this part
if (pause > 30)
error = "OK";
else
error = "error1";
//end
i += 1;
totalBreak += pause;
break;
}
if (totalBreak < 60)
{
error = "error2";
}
}
return totalBreak;
}
Edit: after some to-and-fro in the comments there is a further change required. If this still doesn't do what you want then lease open another question which concentrates on that part alone because I have no idea what you're saying. Code in the original answer commented out. This now returns the error if there has been one
Your loops are wrong. You only need one of them. The for n loop is going through all arrivals, when it looks like you only need to look at the previous one. Try something like this:
private double CalculateBreak(List<PRAESENZZEIT> arrivals, out string error)
{
error = null;
double totalBreak = 0;
for (int i = 1; i < arrivals.Count();)
{
// Check the i-th against the previous one (i - 1)
double pause = (arrivals[i].ZPZ_Von - arrivals[i - 1].ZPZ_Bis).TotalMinutes;
// //this part
// if (pause > 30)
// error = "OK";
// else
if (pause < 30) // new
{ // new
error = "Pausenzeitverletzung";
} // new
//end
i += 1;
totalBreak += pause;
// break;
// if (totalBreak < 60)
// {
// error = "Pausenzeitverletzung";
// }
}
// new: moved if block
if (totalBreak < 60)
{
error = "Pausenzeitverletzung";
}
return totalBreak;
}
I roughly translated vb.net code to c# for a simple POS system i'm coding for school. But when i try to add things to the datagrid using button click event there is no change in the datagrid. there are no errors. the datagrid is not connected to a database, at least not yet.
here's the code where i insert things to the datagrid:
private void txtAdd_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtProduct.Text) & !string.IsNullOrEmpty(txtQuantity.Text) & !string.IsNullOrEmpty(txtPrice.Text))
{
var with = dataSales;
int ii = 0;
int orderproduct = 0;
int count = 0;
for (ii = 0; ii <= with.RowCount - 1; ii++)
{
if (txtProduct.Text == Convert.ToString(dataSales[0, ii].Value))
{
count = count + 1;
if (count != 0)
{
orderproduct = Convert.ToInt32(dataSales[2, ii].Value) + Convert.ToInt32(txtQuantity.Text);
dataSales[2, ii].Value = orderproduct;
dataSales[3, ii].Value = Convert.ToInt32(dataSales[2, ii].Value) * Convert.ToInt32(dataSales[1, ii].Value);
}
else
{
if (count == 0)
{
float sum = Convert.ToInt32(txtPrice.Text) * Convert.ToInt32(txtQuantity.Text);
dataSales.Rows.Add(txtProduct.Text, txtPrice.Text, txtQuantity.Text, sum);
count = 0;
}
txtProduct.Clear();
txtQuantity.Clear();
txtPrice.Clear();
}
}
else
{
MessageBox.Show("Nothing is Selected", "Error");
}
}
}
}
Follow your counter and you will understand why you never get anything added. You start with 0 but add +1 every single time there is text to add. PUt it in debug and watch.
EDITED: Due to your comment you do not know how to debug. Open the .cs file in Visual Studio, Click on the left margin for the line that tests for text in the fields (the line below):
if (!string.IsNullOrEmpty(txtProduct.Text) & !string.IsNullOrEmpty(txtQuantity.Text) & !string.IsNullOrEmpty(txtPrice.Text))
Now start the program, enter text and run it. It will stop on that line. You can then hit F11 and it will go to the line that increments the counter. This means you will always run this branch.
if (count != 0)
{
orderproduct = Convert.ToInt32(dataSales[2, ii].Value) + Convert.ToInt32(txtQuantity.Text);
dataSales[2, ii].Value = orderproduct;
dataSales[3, ii].Value = Convert.ToInt32(dataSales[2, ii].Value) * Convert.ToInt32(dataSales[1, ii].Value);
}
NOTE: You do not need this:
if (count == 0)
{
}
Why? You have already sent anything not 0 to the other branch. The else is sufficient at this point, and no need to waste a cycle determining if something that will ALWAYS be zero is zero
In this code, I think I have it nearly right, but it isn't calculating the way I need it to.
The code should ignore the first grade, only if the second is higher for a single grade.
Here is the code I've constructed for it, but when I run the program it will always calculate the second grade irregardless of its value.
double ComputeGPA()
{
if (Count == 0) return 0;
bool bForgiven = false;
int nCourseCount = 0;
int i;
double gpa = 0.0;
double gpaToAdd;
for (i = 0; i < this.Count; i++)
{
gpaToAdd = 0.0;
Course c = this[i];
gpaToAdd = GradePoints(c.Grade);
if (c.Grade == null || c.Grade == "W") continue;
if (bForgiven == false)
{
int nRep = FindCourse(c.Number, i + 1);
if (nRep > 0)
{
Course x = this[i + 1];
if(GradePoints(this[nRep].Grade > GradePoints(c.Grade)))
gpaToAdd = GradePoints(x.Grade);
// This means we forgive only one grade
bForgiven = true;
continue;
}
}
gpa = gpa + GradePoints(c.Grade);
nCourseCount++;
}
//If we've forgiven a grade , we divide by one less course
gpa = (nCourseCount > 0) ? gpa / nCourseCount : 0.0;
return gpa;
}
I also have these functions to work with it:
public int FindCourse(int Number, int nStart)
{
int i;
for (i = nStart; i< this.Count; i++)
{
Course c = this[i];
if (c.Number == Number) return i;
}
return -1; // Signifies no course was found
}
public int FindCourse(int Number)
{
return FindCourse(Number, 0);
}
What is the construct that will allow this to work?
Thank you,
Travis
Here are some test inputs:
TestTranscript();
}
static void TestTranscript()
{
Transcript trans = new Transcript();
trans.Add(new Course(1, 3113, "A", false));
trans.Add(new Course(1, 3232, "A", false));
trans.Add(new Course(1, 4212, "A", false));
trans.Add(new Course(1, 3113, "F", false));
trans.Add(new Course(1, 4220, "A", false));
trans.Add(new Course(1, 4234, "A", false));
trans.Add(new Course(1, 4300, "A", false));
TranscriptForm frm = new TranscriptForm("Test Transcript", trans);
frm.ShowDialog();
MessageBox.Show("GPA is computed to be " + trans.GPA.ToString());
}
And these are their corresponding values per letter:
public static double GradePoints(string grade)
{
switch (grade)
{
case "A":
case "A+":
return 4.0;
case "A-":
return 3.7;
case "B+":
return 3.3;
case "B":
return 3.0;
case "B-":
return 3.7;
case "C+":
return 2.3;
case "C":
return 2.0;
case "C-":
return 1.7;
case "D+":
return 1.3;
case "D":
return 1.0;
case "D-":
default:
return 0.0;
}
}
In addition to Chris Shain's comments, this part looks odd to me:
Course x = this[i + 1];
if(GradePoints(this[nRep].Grade > GradePoints(c.Grade)))
gpaToAdd = GradePoints(x.Grade);
Why i+1 here - isn't that what happens to be next in the list, not necessarily the same course number? Shouldn't gpaToAdd rather be the better grade (this[nRep].Grade)?
Looks like a flow control problem. See my note on the continue statement:
for (i = 0; i < this.Count; i++)
{
gpaToAdd = 0.0;
Course c = this[i];
gpaToAdd = GradePoints(c.Grade);
if (c.Grade == null || c.Grade == "W") continue;
if (bForgiven == false)
{
int nRep = FindCourse(c.Number, i + 1);
if (nRep > 0)
{
Course x = this[i + 1];
if(GradePoints(this[nRep].Grade > GradePoints(c.Grade)))
gpaToAdd = GradePoints(x.Grade);
// This means we forgive only one grade
bForgiven = true;
// This will restart the for loop at the next item,
// presumably skipping whatever code you left out
// below
continue;
}
}
// Something else is going on down here that you didnt show us-
// my guess is something like:
gpa = gpaToAdd + gpa;
// but it is getting skipped when a grade is forgiven
Continue is documented here: http://msdn.microsoft.com/en-us/library/923ahwt1(v=vs.80).aspx
There are two issue that are causing the wrong calculation to happen.
1) As others have mentioned, the continue statement is a problem. The thing here is that you only want it to continue (and set bForgiven) when you are skipping that item. In other words, it needs to be included in the if statement as follows:
if (GradePoints(trans[nRep].Grade) > GradePoints(c.Grade))
{
gpaToAdd = GradePoints(x.Grade);
// This means we forgive only one grade
bForgiven = true;
continue;
}
2) You're looping through "this" with the line
for (i = 0; i < this.Count; i++)
Later you're searching for another grade for the same "c.Number" with this line
int nRep = FindCourse(c.Number, i + 1);
The issue is that you're looking for a later (in the list) Number and never comparing to earlier records, so when it gets to the first "3113" (record 0), it finds and compares against the second (record 3), but when it gets to record 3, it never finds the first because it is searching starting at record 4.
The fix for this is for FindCourse to search from the beginning up to the current so in your example it triggers hits with record 3 instead of record 0 like this.
public int FindCourse(int Number, int nCurrent)
{
int i;
for (i = 0; i < nCurrent; i++)
{
Course c = this[i];
if (c.Number == Number) return i;
}
return -1; // Signifies no course was found
}
Note: This will require calling with i instead of i+1 like this.
int nRep = FindCourse(c.Number, i);
I need to add functionality in my program so that any file imported it will find the text within the "" of the addTestingPageContentText method as seen below. The two values on each line will then be added to a datagridview which has 2 columns so first text in first column then second in the 2nd column. How would i go about Finding the "sometext" ?
addTestingPageContentText("Sometext", "Sometext");
addTestingPageContentText("Sometext2", "Sometext2");
... continues n number of times.
Neither fast nor efficient, but it's easier to understand for those new to regular expressions:
while (!endOfFile)
{
//get the next line of the file
string line = file.readLine();
EDIT: //Trim WhiteSpaces at start
line = line.Trim();
//check for your string
if (line.StartsWith("addTestingPageContentText"))
{
int start1;
int start2;
//get the first something by finding a "
for (start1 = 0; start1 < line.Length; start1++)
{
if (line.Substring(start1, 1) == '"'.ToString())
{
start1++;
break;
}
}
//get the end of the first something
for (start2 = start1; start2 < line.Length; start2++)
{
if (line.Substring(start2, 1) == '"'.ToString())
{
start2--;
break;
}
}
string sometext1 = line.Substring(start1, start2 - start1);
//get the second something by finding a "
for (start1 = start2 + 2; start1 < line.Length; start1++)
{
if (line.Substring(start1, 1) == '"'.ToString())
{
start1++;
break;
}
}
//get the end of the second something
for (start2 = start1; start2 < line.Length; start2++)
{
if (line.Substring(start2, 1) == '"'.ToString())
{
start2--;
break;
}
}
string sometext2 = line.Substring(start1, start2 - start1);
}
}
However I would seriously recommend going through some of the great tutorials out there on the internet. This is quite a good one
The expression "\"[^"]*\"" would find each...