Cinema seat row c# - c#

I've recently finished a small project that I have been working on, except there is a small problem that I'm trying to figure out.
This project utilizes C# web services using a SOAP client along with both the Request and Response Classes. This project has been designed to save inputted data. What it does is that it reserves a seat with a row in the cinema, and when I book that seat and try to select that seat again to find another available seat in the row (within the form), it displays them both as "0". However, when I book another seat in a different row, it successfully saves that data in the cache but it won't display any other seat and row within the "else if" statement of the code.
Here is the Webmethod for the Webservice
public string Name { get; set; } //gets and sets the Name from the ReserveSeatResponse
public int Row { get; set; } //gets and sets the Row from the ReserveSeatResponse
public int Seat { get; set; } //gets and sets the Seat from the ReserveSeatResponse
private const int maxRows = 13; //sets the max amount of Rows in the Array tried setting the rows to 12, but since the array starts at 0, I had to set the Rows to 13)
private const int maxSeats = 17; //sets the max amount of Seats in the Array (tried setting the seats at 16, but since the array starts at 0, I had to set it to 17)
private bool[,]reservedSeats = new bool[13, 17]; //same description above, but this sets a boolean in the reserved seats, to check if its taken or no)
private bool[,]reservedRows = new bool[13, 17]; //same description for the max row/seats.
[WebMethod]
public GetSeatResponse Booking (GetSeatRequest req)
{
GetSeatResponse resp = new GetSeatResponse();
// resp.Seat = req.SeatNumber;
// resp.Row = req.RowNumber;
object abc = HttpContext.Current.Cache["CinemaReservation"];
//if the cache does not exist
if (abc == null)
{
//creates a blank multidimensional array
reservedSeats = new bool[maxRows,maxSeats];
}
//if the cache exists
else
{
//using the cache object as an array
try
{
reservedSeats = (bool[,])abc;
}
catch
{
}
}
//if the seat is vacant
if (reservedSeats[req.RowNumber, req.SeatNumber] != true)
{
resp.Name = req.Name;
resp.Seat = req.SeatNumber;
resp.Row = req.RowNumber;
reservedSeats[req.RowNumber, req.SeatNumber] = true;
HttpContext.Current.Cache["CinemaReservation"] = reservedSeats;
return resp;
}
//if the seat is taken
else if (reservedSeats[req.RowNumber, req.SeatNumber] != true)
{
bool breakTest = false;
string Name = req.Name;
int row = req.RowNumber;
int seat = req.SeatNumber;
for (int i = row; i < reservedSeats.GetLength(0); i++)
{
for (int j = seat; j < reservedSeats.GetLength(1); j++)
{
if (reservedSeats[i, j] !=true )
{
resp.Name = Name;
resp.Row = i+1;
resp.Seat = j+1;
reservedSeats[i, j] = true;
breakTest = true;
break;
}
}
seat = 0;
if (breakTest == true)
{
break;
}
}
//getNextSeat(req, resp, reservedSeats);
HttpContext.Current.Cache["CinemaReservation"] = reservedSeats;
}
return resp;
And here is the part where I am stuck on in the method that calls from the Webservice
if (resp.erroresp != true)
{
if ((resp.Row > 0) && (resp.Seat > 0))
{
MessageBox.Show(String.Format("Hi there {0}!!, you have reserved a Seat at Row: {1} in Seat Number : {2}", resp.Name, resp.Row, resp.Seat));
}
else if (resp.erroresp != true)
{
if ((resp.Row < 1) && (resp.Seat < 1))
{
MessageBox.Show(String.Format("Sorry, your selected Seat has been taken, however there is an available seat at row {0} in seat {1}", resp.Row, resp.Seat));
}
}
}

I see a few problems with your code.
Serverside:
reservedRows is never used. Whats its purpose?
You define constants i.e. maxRows but you don't use them. Write
private bool[,]reservedSeats = new bool[maxRows, maxSeats]
In both checks (Seat taken or not) you use the same condition - that wont work. Just write:
if (!reservedSeats[req.RowNumber, req.SeatNumber]) {
// Seat is free
} else {
// Seat is taken
}
If you want to differentiate whether the returned seat is an alternative, you should include a boolean flag i your response or even better an enumeration like this:
public enum ResultEnum {
Ok, // the requested seat is free
Full, // no seat is available
Alternative // you got another seat
}
Clientside:
Your checks are somewhat redundant (you check two times for resp.erroresp != true) and not clear at all.
When using the approach with an enum you can check like this:
switch (resp.Result) {
case ResultEnum.Ok:
break;
case ResultEnum.Full:
break;
case ResultEnum.Alternative:
break;
}

I spot 2 errors in ur code!
Firstly, I couldn't see the initialization for your 2-dimensional array after:
`if (abc == null)
{
//creates a blank multidimensional array
reservedSeats = new bool[maxRows,maxSeats];
}`
Secondly, your if else conditions (reservedSeats[req.RowNumber, req.SeatNumber] != true) are exactly the same!
Hope this helps

Related

Sellers in a certain amount wont stack [duplicate]

This question already has answers here:
Use a variable from another method in C#
(2 answers)
Closed 1 year ago.
Im sorting sellers in a data table. If a seller reach a curtain amount it will stand "Amount of sellers in lvl 4 is "X" ".
If I print the values for my if-statments it works. I get 1 salesman in each label for all my levels.
Now the problem is that if another salesman has the same amount the label wont update and it will still stand that 1 seller has reached that amount.
foreach (DataRow Drow in information.Rows)
{
int num = dataGridView1.Rows.Add();
dataGridView1.Rows[num].Cells[0].Value = Drow["Namn"].ToString();
dataGridView1.Rows[num].Cells[1].Value = Drow["Personnummer"].ToString();
dataGridView1.Rows[num].Cells[2].Value = Drow["Distrikt"].ToString();
dataGridView1.Rows[num].Cells[3].Value = Drow["Antal artiklar"];
WhatLevel(salesman);
}
public void WhatLevel(Salesman sales)
{
int levelOne = 0;
int levelTwo = 0;
int levelThree = 0;
int levelFour = 0;
if (sales.AmountSold < 50)
{
levelOne++;
label8.Text = levelOne.ToString();
}
if (sales.AmountSold >= 50 && sales.AmountSold < 99)
{
levelTwo++;
label12.Text = levelTwo.ToString();
}
if (sales.AmountSold >= 100 && sales.AmountSold < 199)
{
levelThree++;
label13.Text = levelThree.ToString();
}
if (sales.AmountSold >= 199)
{
levelFour++;
label14.Text = levelFour.ToString();
}
}
You have defined 4 local variables within the WhatLevel method. The scope of these 4 variables is limited to that method. Also, when the method is called, they are always initialized to zero before being incremented.
You'll have to do one of the following:
Make the 4 level* variables be fields instead. That will preserve the value across calls to WhatLevel.
If the WhatLevel method is only being called from within the foreach loop, move its content directly into the loop and avoid a separate method altogether, then place the declaration of the variables before the foreach loop.
I agree with Julius solution but i want to add some remarks.
You don't need to update label.Text in every iteration. You can do it at the end of the loop.
Based on single responsibility rule, i think it's better to add a property or a method to get level from salesman class.
finally use a collection to store number of level, imagine you have 10 or 20 levels.
I suggest this solution
Add a new property to the salesman class
public class Salesman
{
public int AmountSold { get; set; }
// Can be a method if you don't want to have property with a lot of code
public int Level
{
get
{
if (AmountSold < 50)
{
return 1;
}
if (AmountSold >= 50 && AmountSold < 99)
{
return 2;
}
if (AmountSold >= 100 && AmountSold < 199)
{
return 3;
}
// AmountSold >= 199
return 4;
}
}
}
Change implementation
Dictionary<int, int> levelNumbers = new Dictionary<int, int>
{
{ 1 , 0 },
{ 2 , 0 },
{ 3 , 0 },
{ 4 , 0 }
};
foreach (DataRow Drow in information.Rows)
{
int num = dataGridView1.Rows.Add();
dataGridView1.Rows[num].Cells[0].Value = Drow["Namn"].ToString();
dataGridView1.Rows[num].Cells[1].Value = Drow["Personnummer"].ToString();
dataGridView1.Rows[num].Cells[2].Value = Drow["Distrikt"].ToString();
dataGridView1.Rows[num].Cells[3].Value = Drow["Antal artiklar"];
levelNumbers[salesman.Level]++;
}
label8.Text = levelNumbers[1].ToString();
label12.Text = levelNumbers[2].ToString();
label13.Text = levelNumbers[3].ToString();
label14.Text = levelNumbers[4].ToString();
If you want more flexibility and maybe lisibility you can use enum
public enum AmountSoldLevel
{
One,
Two,
Three,
Four
}
In the class return AmountSoldLevel instead of int
public AmountSoldLevel Level
{
get
{
if (AmountSold < 50)
{
return AmountSoldLevel.One;
}
// ...
}
}
and init dictionary using
Dictionary<AmountSoldLevel, int> levelNumbers = new Dictionary<AmountSoldLevel, int>();
foreach (AmountSoldLevel amountSoldLevel in Enum.GetValues(typeof(AmountSoldLevel)))
{
levelNumbers.Add(amountSoldLevel, 0);
}
// ...
label8.Text = levelNumbers[AmountSoldLevel.One].ToString();
label12.Text = levelNumbers[AmountSoldLevel.Two].ToString();
Sample test with app

Why does my bubblesort code need - 2 after the structure.Length for it to work?

I am trying to understand why my productTable.Length has to be - 2 for my bubblesort code to work.
I created two int variables, Last_Position and i. I created one product variable called temp and one bool called swap which is set to false. I then set Last_Position to equal productTable.Length - 2.
This is where I fail to understand, from what I have read the .Length counts the amount of characters and returns the amount however since 1 counts as 0 in programming, you have to - 1 to have the cap be accurate (i.e 1000 = 999) which has remained true until this part.
For some reason - 1 will throw up an error when the program runs targeting this code: if (String.Compare(productTable[i].prodCode, productTable[i + 1].prodCode) > 0) and states "System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'"
The code works when I set it to - 2 but I want to understand why that is.
struct product
{
public string prodCode;
public string description;
public double price;
public int quantity;
}
product[] productTable;
public void loadData()
{
string path = "C:\\Users\\5004303\\Documents\\productFile.csv";
int lineCount = File.ReadLines(path).Count();
productTable = new product[lineCount];
product currentProduct = new product();
try
{
StreamReader sr = new StreamReader(path);
string line;
int currentArrayLocation = 0;
while (!sr.EndOfStream)
{
line = sr.ReadLine();
string[] fields = line.Split(',');
currentProduct.prodCode = fields[0];
currentProduct.description = fields[1];
currentProduct.price = Convert.ToDouble(fields[2]);
currentProduct.quantity = Convert.ToInt32(fields[3]);
productTable[currentArrayLocation] = currentProduct;
currentArrayLocation++;
}
sr.Close();
}
catch (FileNotFoundException)
{
MessageBox.Show("An error occured. Could not find file 'productFile.csv'.");
}
}
public void listProducts()
{
int currentArrayLocation = 0;
for (currentArrayLocation = 0; currentArrayLocation < productTable.Length; currentArrayLocation++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = productTable[currentArrayLocation].prodCode;
lvi.SubItems.Add(Convert.ToString(productTable[currentArrayLocation].description));
lvi.SubItems.Add(Convert.ToString(productTable[currentArrayLocation].price));
lvi.SubItems.Add(Convert.ToString(productTable[currentArrayLocation].quantity));
lvProducts.Items.Add(lvi);
}
}
public void bubbleSort()
{
int last_Postion, i;
product temp;
last_Postion = productTable.Length - 2;
Boolean swap = false;
do
{
swap = false;
for (i = 0; i <= last_Postion; i++)
{
if (String.Compare(productTable[i].prodCode, productTable[i + 1].prodCode) > 0)
{
temp = productTable[i];
productTable[i] = productTable[i + 1];
productTable[i + 1] = temp;
swap = true;
}
}
}
while (swap == true);
}
Short answer:
Change
productTable.Lenght - 2 to productTable.Lenght - 1
and
for (i = 0; i <= last_Postion; i++) to for (i = 0; i < last_Postion; i++)
Explanation:
productTable.Lenght gives you the lenght of the list so productTable.Lenght - 1 is the last position in the list (0 to productTable.Lenght - 1).
In your "bubble" for loop inside the while you test against i+1 so i should only go up to the last_position - 1.
In your code when i == last_position then i + 1 is beyond the last position in the list.
Note: I did not check your code for validity, even if you make these changes, there may be other bugs.
Note on style, C# coding guidelines usually specify camel case for variable names, it is better to use lastPosition instead of last_Position. There are other styling "errors" in your code, such as declaring variables at the top of the function, using types instead of var. It may be some of this "errors" are course requirements, but a short read of any coding conventions document (e.g. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) would be beneficial to you. Most work places have their own coding guidelines or adopt a public one, but on all of them are pretty similar.

Why looping twice on string length test

I'm working in C# (2013) Windows Forms. My instructor wanted us to ensure that the txtStateInput is upperCase when we hit the calculate button. However when I input a two character string such as "wi" and then hit calculate, it throws the message "enter valid state" and clears out the textbox. When I enter the "wi" in a second time then it works. I can't figure out why this is happening, the code would lead me to believe that it would check to ensure the string in txtStateInput is two characters and then when calculate is clicked it would uppercase the string. I can't figure out why it only works once I enter in the state "wi" a second time.
private void btnCalc_Click(object sender, EventArgs e)
{
//declare variables.
int startPop = 0;
int endPop = 0;
string Message = "Error";
decimal Percent = 0.0m;
string State = "";
string City = String.Empty;
int dTimes = 0;
try
{
City = txtCityInput.Text;
//System Globalization was initialized so this method works.
TextInfo myTI = new CultureInfo("en-US", false).TextInfo;
txtCityInput.Text = myTI.ToTitleCase(City);
if(txtStateInput.Text.Length != 2)
{
MessageBox.Show("Enter valid State");
txtStateInput.Focus();
txtStateInput.SelectAll();
}
else
{
State = txtStateInput.Text.ToUpper();
txtStateInput.Text = State;
if ((int.TryParse(txtStartPopInput.Text, out startPop)) && int.TryParse(txtEndPopInput.Text, out endPop))
{
if ((startPop > 0) && (endPop > 0))
{
//if population has decreased.
if ((startPop > endPop))
{
Percent = ((decimal.Parse(endPop.ToString()) - decimal.Parse(startPop.ToString())) / decimal.Parse(startPop.ToString()));
Message = "Pop. Decrease of " + Percent.ToString("p");
}
//if population has increased.
if ((startPop < endPop))
{
Percent = ((decimal.Parse(endPop.ToString()) - decimal.Parse(startPop.ToString())) / decimal.Parse(startPop.ToString()));
Message = "Pop. Increase of " + Percent.ToString("p");
}
//if population has not changed.
if ((startPop == endPop))
{
Percent = ((decimal.Parse(endPop.ToString()) - decimal.Parse(startPop.ToString())) / decimal.Parse(startPop.ToString()));
Message = "No Change in Population";
}
}
else
{
MessageBox.Show("Please enter valid population figures.");
}
}
if (int.TryParse(txtDisplayTimes.Text, out dTimes))
{
if (dTimes > 0)
{
lstResults.Items.Clear();
int iSum = 0;
int iLoopCount = dTimes;
//displays the results according to value in txtDisplayTimes.
for (iSum = 1; iSum <= iLoopCount; iSum++)
{
lstResults.Items.Add(Message);
}
}
}
}
}
catch
{
MessageBox.Show("Something went wrong.");
}
}

Dictionary or list in c#

I got a strange C# programming problem. There is a data retrieval in groups of random lengths of number groups. The numbers should be all unique, like:
group[1]{1,2,15};
group[2]{3,4,7,33,22,100};
group[3]{11,12,9};
// Now there is a routine that adds a number to a group.
// For the example, just imagine the active group looks like:
// group[active]=(10,5,0)
group[active].add(new_number);
// Now if 100 were to be added to the active group
// then the active group should be merged to group[2]
// (as that one already contained 100)
// And then as a result it would like
group[1]{1,2,15};
group[2]{3,4,7,33,22,100,10,5,0}; // 10 5 0 added to group[2]
group[3]{11,12,9};
// 100 wasn't added to group[2] since it was already in there.
If the number to be added is already used (not unique) in a previous group.
Then I should merge all numbers in the active group towards that previous group, so I don’t get double numbers.
So in the above example if number 100 was added to the active
group, then all numbers in the group[active] should be merged into group[2].
And then the group[active] should start clean fresh again without any items. And since 100 was already in group[2] it should not be added double.
I am not entirely sure on how to deal with this in a proper way.
As an important criteria here is that it has to work fast.
I will have around minimal 30 groups (upper-bound unknown might be 2000 or more), and their length on average contains five integer numbers, but it could be much longer or only one number.
I kind of feel that I am reinventing the wheel here.
I wonder what this problem is called (does it go by a name, some sorting, or grouping math problem)?, with a name I might find some articles related to such problems.
But maybe it’s indeed something new, then what would be recommended? Should I use list of lists or a dictionary of lists.. or something else? Somehow the checking if the number is already present should be done fast.
I'm thinking along this path now and am not sure if it’s the best.
Instead of a single number, I use a struct now. It wasn't written in the original question as I was afraid, explaining that would make it too complex.
struct data{int ID; int additionalNumber}
Dictionary <int,List<data>> group =new Dictionary<int, List<data>>();
I can step aside from using a struct in here. A lookup list could connect the other data to the proper index. So this makes it again more close to the original description.
On a side note, great answers are given.
So far I don’t know yet what would work best for me in my situation.
Note on the selected answer
Several answers were given here, but I went for the pure dictionary solution.
Just as a note for people in similar problem scenarios: I'd still recommend testing, and maybe the others work better for you. It’s just that in my case currently it worked best. The code was also quite short which I liked, and a dictionary adds also other handy options for my future coding on this.
I would go with Dictionary<int, HashSet<int>>, since you want to avoid duplicates and want a fast way to check if given number already exists:
Usage example:
var groups = new Dictionary<int, HashSet<int>>();
// populate the groups
groups[1] = new HashSet<int>(new[] { 1,2,15 });
groups[2] = new HashSet<int>(new[] { 3,4,7,33,22,100 });
int number = 5;
int groupId = 4;
bool numberExists = groups.Values.Any(x => x.Contains(number));
// if there is already a group that contains the number
// merge it with the current group and add the new number
if (numberExists)
{
var group = groups.First(kvp => kvp.Value.Contains(number));
groups[group.Key].UnionWith(groups[groupId]);
groups[groupId] = new HashSet<int>();
}
// otherwise just add the new number
else
{
groups[groupId].Add(number);
}
From what I gather you want to iteratively assign numbers to groups satisfying these conditions:
Each number can be contained in only one of the groups
Groups are sets (numbers can occur only once in given group)
If number n exists in group g and we try to add it to group g', all numbers from g' should be transferred to g instead (avoiding repetitions in g)
Although approaches utilizing Dictionary<int, HashSet<int>> are correct, here's another one (more mathematically based).
You could simply maintain a Dictionary<int, int>, in which the key would be the number, and the corresponding value would indicate the group, to which that number belongs (this stems from condition 1.). And here's the add routine:
//let's assume dict is a reference to the dictionary
//k is a number, and g is a group
void AddNumber(int k, int g)
{
//if k already has assigned a group, we assign all numbers from g
//to k's group (which should be O(n))
if(dict.ContainsKey(k) && dict[k] != g)
{
foreach(var keyValuePair in dict.Where(kvp => kvp.Value == g).ToList())
dict[keyValuePair.Key] = dict[k];
}
//otherwise simply assign number k to group g (which should be O(1))
else
{
dict[k] = g;
}
}
Notice that from a mathematical point of view what you want to model is a function from a set of numbers to a set of groups.
I have kept it as easy to follow as I can, trying not to impact the speed or deviate from the spec.
Create a class called Groups.cs and copy and paste this code into it:
using System;
using System.Collections.Generic;
namespace XXXNAMESPACEXXX
{
public static class Groups
{
public static List<List<int>> group { get; set; }
public static int active { get; set; }
public static void AddNumberToGroup(int numberToAdd, int groupToAddItTo)
{
try
{
if (group == null)
{
group = new List<List<int>>();
}
while (group.Count < groupToAddItTo)
{
group.Add(new List<int>());
}
int IndexOfListToRefresh = -1;
List<int> NumbersToMove = new List<int>();
foreach (List<int> Numbers in group)
{
if (Numbers.Contains(numberToAdd) && (group.IndexOf(Numbers) + 1) != groupToAddItTo)
{
active = group.IndexOf(Numbers) + 1;
IndexOfListToRefresh = group.IndexOf(Numbers);
foreach (int Number in Numbers)
{
NumbersToMove.Add(Number);
}
}
}
foreach (int Number in NumbersToMove)
{
if (!group[groupToAddItTo - 1].Contains(Number))
{
group[groupToAddItTo - 1].Add(Number);
}
}
if (!group[groupToAddItTo - 1].Contains(numberToAdd))
{
group[groupToAddItTo - 1].Add(numberToAdd);
}
if (IndexOfListToRefresh != -1)
{
group[IndexOfListToRefresh] = new List<int>();
}
}
catch//(Exception ex)
{
//Exception handling here
}
}
public static string GetString()
{
string MethodResult = "";
try
{
string Working = "";
bool FirstPass = true;
foreach (List<int> Numbers in group)
{
if (!FirstPass)
{
Working += "\r\n";
}
else
{
FirstPass = false;
}
Working += "group[" + (group.IndexOf(Numbers) + 1) + "]{";
bool InnerFirstPass = true;
foreach (int Number in Numbers)
{
if (!InnerFirstPass)
{
Working += ", ";
}
else
{
InnerFirstPass = false;
}
Working += Number.ToString();
}
Working += "};";
if ((active - 1) == group.IndexOf(Numbers))
{
Working += " //<active>";
}
}
MethodResult = Working;
}
catch//(Exception ex)
{
//Exception handling here
}
return MethodResult;
}
}
}
I don't know if foreach is more or less efficient than standard for loops, so I have made an alternative version that uses standard for loops:
using System;
using System.Collections.Generic;
namespace XXXNAMESPACEXXX
{
public static class Groups
{
public static List<List<int>> group { get; set; }
public static int active { get; set; }
public static void AddNumberToGroup(int numberToAdd, int groupToAddItTo)
{
try
{
if (group == null)
{
group = new List<List<int>>();
}
while (group.Count < groupToAddItTo)
{
group.Add(new List<int>());
}
int IndexOfListToRefresh = -1;
List<int> NumbersToMove = new List<int>();
for(int i = 0; i < group.Count; i++)
{
List<int> Numbers = group[i];
int IndexOfNumbers = group.IndexOf(Numbers) + 1;
if (Numbers.Contains(numberToAdd) && IndexOfNumbers != groupToAddItTo)
{
active = IndexOfNumbers;
IndexOfListToRefresh = IndexOfNumbers - 1;
for (int j = 0; j < Numbers.Count; j++)
{
int Number = NumbersToMove[j];
NumbersToMove.Add(Number);
}
}
}
for(int i = 0; i < NumbersToMove.Count; i++)
{
int Number = NumbersToMove[i];
if (!group[groupToAddItTo - 1].Contains(Number))
{
group[groupToAddItTo - 1].Add(Number);
}
}
if (!group[groupToAddItTo - 1].Contains(numberToAdd))
{
group[groupToAddItTo - 1].Add(numberToAdd);
}
if (IndexOfListToRefresh != -1)
{
group[IndexOfListToRefresh] = new List<int>();
}
}
catch//(Exception ex)
{
//Exception handling here
}
}
public static string GetString()
{
string MethodResult = "";
try
{
string Working = "";
bool FirstPass = true;
for(int i = 0; i < group.Count; i++)
{
List<int> Numbers = group[i];
if (!FirstPass)
{
Working += "\r\n";
}
else
{
FirstPass = false;
}
Working += "group[" + (group.IndexOf(Numbers) + 1) + "]{";
bool InnerFirstPass = true;
for(int j = 0; j < Numbers.Count; j++)
{
int Number = Numbers[j];
if (!InnerFirstPass)
{
Working += ", ";
}
else
{
InnerFirstPass = false;
}
Working += Number.ToString();
}
Working += "};";
if ((active - 1) == group.IndexOf(Numbers))
{
Working += " //<active>";
}
}
MethodResult = Working;
}
catch//(Exception ex)
{
//Exception handling here
}
return MethodResult;
}
}
}
Both implimentations contain the group variable and two methods, which are; AddNumberToGroup and GetString, where GetString is used to check the current status of the group variable.
Note: You'll need to replace XXXNAMESPACEXXX with the Namespace of your project. Hint: Take this from another class.
When adding an item to your List, do this:
int NumberToAdd = 10;
int GroupToAddItTo = 2;
AddNumberToGroup(NumberToAdd, GroupToAddItTo);
...or...
AddNumberToGroup(10, 2);
In the example above, I am adding the number 10 to group 2.
Test the speed with the following:
DateTime StartTime = DateTime.Now;
int NumberOfTimesToRepeatTest = 1000;
for (int i = 0; i < NumberOfTimesToRepeatTest; i++)
{
Groups.AddNumberToGroup(4, 1);
Groups.AddNumberToGroup(3, 1);
Groups.AddNumberToGroup(8, 2);
Groups.AddNumberToGroup(5, 2);
Groups.AddNumberToGroup(7, 3);
Groups.AddNumberToGroup(3, 3);
Groups.AddNumberToGroup(8, 4);
Groups.AddNumberToGroup(43, 4);
Groups.AddNumberToGroup(100, 5);
Groups.AddNumberToGroup(1, 5);
Groups.AddNumberToGroup(5, 6);
Groups.AddNumberToGroup(78, 6);
Groups.AddNumberToGroup(34, 7);
Groups.AddNumberToGroup(456, 7);
Groups.AddNumberToGroup(456, 8);
Groups.AddNumberToGroup(7, 8);
Groups.AddNumberToGroup(7, 9);
}
long MillisecondsTaken = DateTime.Now.Ticks - StartTime.Ticks;
Console.WriteLine(Groups.GetString());
Console.WriteLine("Process took: " + MillisecondsTaken);
I think this is what you need. Let me know if I misunderstood anything in the question.
As far as I can tell it's brilliant, it's fast and it's tested.
Enjoy!
...and one more thing:
For the little windows interface app, I just created a simple winforms app with three textboxes (one set to multiline) and a button.
Then, after adding the Groups class above, in the button-click event I wrote the following:
private void BtnAdd_Click(object sender, EventArgs e)
{
try
{
int Group = int.Parse(TxtGroup.Text);
int Number = int.Parse(TxtNumber.Text);
Groups.AddNumberToGroup(Number, Group);
TxtOutput.Text = Groups.GetString();
}
catch//(Exception ex)
{
//Exception handling here
}
}

Generating new Sudoku grid C#

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.

Categories