So I'm currently working on a project for my C# class where we have to recreate Minesweeper using the Console. I'm fairly new to C# but have worked with other code languages before so I've been able to sort out most problems. The newest one i'm having is I've made two functions (one that creates cells and stores them in a list, the other to print them in a grid pattern), one of them works (GenerateCells) but the other doesn't output anything when ran.
//PrintGrid Function
public static void PrintGrid(int gsize, List<Cell> cells)
{
for (int x = 0; x > gsize; x++)
{
//testing output
Console.WriteLine(cells[0].Name);
}
}
class Program
{
static void Main(string[] args) {
//Set Default Grid Size
int gsize = 6;
//Create List of Cells
var cells = new List<Cell>
{
//Generate "Ghost Cell" preventing exception
new Cell { Name = "Ghost Cell", XCord = 0, YCord = 0 }
};
//Generate Cells for grid
Cell.GenerateCells(gsize, cells);
//Print Cells in grid
Cell.PrintGrid(gsize, cells);
}
}
I'm not sure what I'm doing wrong since there are no errors that pop up. All I can figure is I've either called it wrong or set up the method wrong, but I can't figure it out. Any and all help is appreciated.
You just have '>' instead of '<'. So this is what it should look like:
public static void PrintGrid(int gsize, List<Cell> cells)
{
for (int x = 0; x < gsize; x++)
{
//testing output
//This one has to have the index instead of 0.
Console.WriteLine(cells[x].Name);
}
}
Related
I am trying to insert data into a 2D array of objects from a csv file but I have problems with initializing the 2D array of objects, and I get this error message:
IndexOutOfRangeException: Index was outside the bounds of the array.
UPDATE:
I think the problem is with Initializing the array of object , because I tried to insert data manually without using CSV file like this code below and still having the same error message :
UPDATE No.2 :
I tried to Simplify the code by making 1 object only but still cant insert data
Simplified code :
public CoreManager mng = new CoreManager();
void Start()
{
mng.actual_words[1] = "n"; //-> the problem I'm facing is on this line
Debug.Log(mng.actual_words[1]);
}
Here is my code trying to insert data from csv file :
public class NewBehaviourScript : MonoBehaviour
{
int sentenceNumber = 0;
int storynumber = 0;
public CoreManager[][] mng = new CoreManager[5][];
void Start()
{
TextAsset questdata = Resources.Load<TextAsset> ("first_storytest");
string[] data = questdata.text.Split(new char[] { '\n' });
Debug.Log(data.Length);
for (int i = 1; i < data.Length - 1; i++)
{
string[] row = data[i].Split(new char[] { ',' });
for(int x = 0; x < row.Length; x++)
{
if (row[x] != "")
{
if (row[x] == "***")
{
storynumber++;
sentenceNumber = 0;
}
else
{
mng[storynumber][sentenceNumber].actual_words[x] = row[x];
}
}
}
sentenceNumber++;
}
}
}
Here is my code trying to insert one item manually :
int sentenceNumber = 0;
int storynumber = 0;
public CoreManager[][] mng = new CoreManager[5][];
void Start()
{
mng[storynumber][sentenceNumber].actual_words[1]="test1";
}
public class CoreManager
{
[Tooltip("Write these words in actual sequence of the sentence")]
public string[] actual_words;
[Tooltip("Write these words in any sequence of the sentence")]
public string[] mixed_words;
}
This line of code only allows 5 entries in one dimension but doesn't allocate any space for the other direction. If you're using arrays, you need to provide the size up front.
public CoreManager[][] mng = new CoreManager[5][];
For testing purposes, I'd set the mng to [100][100] and use a small csv and see if this fixes your problem. If it does, then that's definitely the problem. If it doesn't, then something else is wrong.
Edit: After reviewing this some more, I'd suggest changing your data structure to use Dictionaries instead. This will provide more flexibility at runtime.
2nd Edit:
Or you can go with a multidemensional array which has a different syntax.
Feel free to run this example for yourself:
public static void Main()
{
Console.WriteLine("Lets get started!");
int[,] mng = new int[5,5];
mng[0,0] = 5;
Console.WriteLine(mng[0,0]);
}
// Output:
// Lets get started!
// 5
So I am trying to figure out why filling sudoku board in order of how I generate fields is many times faster then filling it which shuffled order, and I can't come to any reasonable conclusions.
Speed of filling and checking should be about the same no matter if fields are shuffled or not, only noticeable difference is the fact that filling boxes in a shuffled order causes the main loop to easily go for 60.000.000+ iterations not sure how many on average as I am not patient enough to check. And filling them in order of how they were generated gets finished in usually < 1.000 iterations rarely going over it and basically never going over 5.000.
I do understand that code doesn't follow C# standards too strictly but it's a project I am supposed to make and it's not my main language there are probably some possible optimizations but it's not what I am interested in all i want to know why shuffling the order of fill the boxes causes the process to take this much longer.
Edit: forgot to attach order of creating/filling boxes order of filling/creating boxes:
Edit2: more concise rephrased question:
The question is why generating an valid fully filled sudoku board in order from image creates a lot less invalid boards from which there is required backtracking, as compared to generating a board in a random order of choosing fields?
example of random order
Here is the code used for the project https://github.com/Piterm21/sudoku
And here are all the parts used in generation.
filledBox class:
public class filledBox
{
public int textBoxIndex;
public int value;
public int nextIndexToTry;
public int[] values;
public int lineIndex;
public int columnIndex;
public int groupIndex;
}
Here is a main loop:
filledBox[] boxes = this.shuffleBoxesCreateCheckingLists();
long iter = 0;
int nextIndexToFill = 0;
while (nextIndexToFill != 81) {
for (; boxes[nextIndexToFill].nextIndexToTry < 9; boxes[nextIndexToFill].nextIndexToTry++) {
// check if is valid
if (!createsError(boxes[nextIndexToFill])) {
boxes[nextIndexToFill].value = boxes[nextIndexToFill].values[boxes[nextIndexToFill].nextIndexToTry];
nextIndexToFill++;
break;
}
if (boxes[nextIndexToFill].nextIndexToTry == 8) {
boxes[nextIndexToFill].nextIndexToTry = 0;
boxes[nextIndexToFill].value = 0;
nextIndexToFill--;
boxes[nextIndexToFill].nextIndexToTry++;
while (boxes[nextIndexToFill].nextIndexToTry == 9) {
boxes[nextIndexToFill].nextIndexToTry = 0;
boxes[nextIndexToFill].value = 0;
nextIndexToFill--;
boxes[nextIndexToFill].nextIndexToTry++;
}
break;
}
}
iter++;
}
System.Diagnostics.Debug.WriteLine(iter);
Generation of boxes with setting of lists used for checking for errors:
List<filledBox>[] generationLines;
List<filledBox>[] generationColumns;
List<filledBox>[] generationGroups;
public filledBox[] shuffleBoxesCreateCheckingLists()
{
filledBox[] boxes = new filledBox[81];
for (int i = 0; i < 81; i++) {
boxes[i] = new filledBox();
boxes[i].values = new int[9]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
boxes[i].values = shuffle(boxes[i].values, true);
}
generationLines = new List<filledBox>[9];
generationColumns = new List<filledBox>[9];
generationGroups = new List<filledBox>[9];
for (int i = 0; i < 9; i++) {
generationLines[i] = new List<filledBox>();
generationColumns[i] = new List<filledBox>();
generationGroups[i] = new List<filledBox>();
}
int boxIndex = 0;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
for (int innerY = 0; innerY < 3; innerY++) {
for (int innerX = 0; innerX < 3; innerX++) {
int subPanelIndex = x + y * 3;
int lineIndex = innerY + y * 3;
int columnIndex = innerX + x * 3;
boxes[boxIndex].textBoxIndex = boxIndex;
boxes[boxIndex].groupIndex = subPanelIndex;
boxes[boxIndex].columnIndex = columnIndex;
boxes[boxIndex].lineIndex = lineIndex;
boxes[boxIndex].nextIndexToTry = 0;
boxes[boxIndex].value = 0;
generationLines[lineIndex].Add(boxes[boxIndex]);
generationColumns[columnIndex].Add(boxes[boxIndex]);
generationGroups[subPanelIndex].Add(boxes[boxIndex]);
boxIndex++;
}
}
}
}
#if !fast
boxes = shuffle(boxes);
#endif
return boxes;
}
Shuffling code:
public T[] shuffle<T> (T[] array)
{
int i = array.Length;
while (i > 1) {
i--;
int j = rnd.Next(0, i - 1);
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
And code responsible for checking for errors:
public bool hasError (List<filledBox> list, filledBox box)
{
bool result = false;
foreach (filledBox filledBoxToCheck in list) {
if (filledBoxToCheck.value == box.values[box.nextIndexToTry]) {
if (box.lineIndex != filledBoxToCheck.lineIndex ||
box.columnIndex != filledBoxToCheck.columnIndex) {
result = true;
break;
}
}
}
return result;
}
public bool createsError (filledBox box)
{
bool result = false;
if (hasError(generationGroups[box.groupIndex], box)) {
result = true;
} else if (hasError(generationLines[box.lineIndex], box)) {
result = true;
} else if (hasError(generationColumns[box.columnIndex], box)) {
result = true;
}
return result;
}
The reason the shuffled order of visiting boxes is worse is that, for the first few boxes, you've increased the odds of filling them in independently. I'm calling a 3x3 region a box. You could visit each little square randomly, but I'll confine my analysis to fillng in one 3x3 box at a time. It should illustrate the problem.
Lets assume, whether you used the in-order or random method, you filled in the top left box first.
VISITING BOXES IN ORDER:
Now consider filling in the box next to it, that is, the top middle box. Just look at filling in the top row of that box, there are 6x5x4 = 120 ways to fill it in.
VISITING BOXES RANDOMLY:
Now consider instead choosing any box to fill in next. If you happen to choose a box that is not in the top row or left column, filling it in is unaffected by the way the first box was filled. The top row of that box can be filled in 9x8x7 = 504 ways.
Taking this further, if you fill in the boxes sequentially, say, across the top, the third box (the top right one), begins with only 3x2x1 = 6 ways to fill in the top row of it. Whereas, if you select the next box randomly, you might, at worst, select a box that is not in the same row or column of either of the first 2 boxes, which means that the top row of that box has yet again 9x8x7 = 504 ways of being filled in.
If you tried randomly selecting the order for each little square to be filled in, the first few might all be in different rows and columns. At worst, none of the first 9 squares will align meaning there will be 9^9 = 387,420,489 options. But filling them in across the top row, the choices are guaranteed to be 9! = 362,880.
This illustrates one of the strategies for how to approach backtracking. Ideally, you first solve parts of the problem that most tightly constrain the rest of the problem. For some problems sequential is better, and for some random is better.
I have 3 classes in my program: Program, Board, and Square.
In program I'm creating a new board:
class Program
{
static void Main(string[] args)
{
Board b = new Board();
}
}
Now I'm looking for a way to call the Board I created here in my Square class.
Anyone have an Idea how to do this? (not to create a new Board in the Square class).
Edit:
I'm trying to create a simple mine-sweeper game.
In the Board class I have a two-dimensional array of Squars. then I creat in each part of the square array a new Square. Then the board is creating mines with the "AddMine" function in the Square class. I have deleted the way the board is choosing which square to set to a mine.
class Board
{
public Square[,] board;
public int n;
public Board()
{
Console.Write("Enter the nuber of rows on column(int): ");
n = int.Parse(Console.ReadLine());
board = new Square[n, n];
mines = (n * n) / 6;
for (int row = 0; row < board.GetLength(0); row++)
{
for (int col = 0; col < board.GetLength(1); col++)
{
board[row, col] = new Square(row, col);
}
}
board[row, col].AddMine();
}
}
In the Square class, at first it is setting is val to 0. After that when I'm calling The "AddMine" function from board, I need to add one to the val of each Square nearby the square who is set to mine.
class Square
{
public Board b;
private int row, col;
public int val;
private bool open;
public Square(int row, int col)
{
this.row = row;
this.col = col;
open = false;
val = 0;
}
public void AddMine()
{
#region set this to bomb (-9), nearby squars ++
val = -9;
b.board[row, (col + 1)].val++;
#endregion
}
}
The real question is how do I call the board array in the Board class in the Square class? because this way is not working, I get a 'System.NullReferenceException' error, I know it is because b is set to 'null', but I don't know to what to do so it will be able to see the Board from Main in Programe class.
All the answers I found in the internet is or to set a new Board, or to set a prorame class in the squarwe class, but because I'm setting a new board in a static function, it is not working.
Thanks to anyone for helping and I hope now the question is clearer.
As many have said you need to pass the reference to the board into the Square. You almost have it in your example, you just don't actually pass the board in.
Here's what your square class should look like.
class Square
{
public Board b;
private int row, col;
public int val;
private bool open;
public Square(Board board, int row, int col)
{
this.row = row;
this.col = col;
open = false;
val = 0;
**b = board;**
}
public void AddMine()
{
#region set this to bomb (-9), nearby squars ++
val = -9;
b.board[row, (col + 1)].val++;
#endregion
}
}
You received the null reference exception because you never actually set the board object so it was null when you tried to use it.
Then to edit your board example, this is how you would add the new squares.
class Board
{
public Square[,] board;
public int n;
public Board()
{
Console.Write("Enter the nuber of rows on column(int): ");
n = int.Parse(Console.ReadLine());
board = new Square[n, n];
mines = (n * n) / 6;
for (int row = 0; row < board.GetLength(0); row++)
{
for (int col = 0; col < board.GetLength(1); col++)
{
board[row, col] = new Square(this, row, col);
}
}
board[row, col].AddMine();
}
}
The "this" keywoard is just a reference to the calling object.
That should solve your problem, but I should note this is not usually considered the best practice. It seems your heading in the direction of tightly coupling your classes which can easily lead to smelly and unmaintainable code. You should look into tightly coupled vs loosely coupled code. A quick google search turned up this article
http://www.c-sharpcorner.com/uploadfile/yusufkaratoprak/difference-between-loose-coupling-and-tight-coupling/
Using passing object reference as a parameter in method you can achieve your desire output. Here is an example:
class Program
{
static void Main(string[] args)
{
Board d=new Board();
Square s=new Square();
s.m1(d);
}
}
class Board
{
int x=10;
}
class Square
{
public void m1(Board a)
{
Console.WriteLine(a.x);
}
Console.ReadLine();
}
In my current project I have an algorythm that generats data, this data will be shown in a Linechart, however the chart only updates once the algorythm has ended rather than after each "step".
private void simulate(share[] shares)
{
int k = 0;
Random r = new Random(); //random values just for testing
while (k < 10)//10 steps (10 values for each share)
{
for (int i = 0; i < shares.Length; i++)
{
shares[i].value = r.Next(0, 10000);//random a new value
shares[i].history.Add(shares[i].value);//add the value to the value history
}
//now update the chart so that it first shows only one x point than two and so on
drawchart(shares);
k++;
}
}
private void drawchart(share[] shares)
{
cHshares.Series.Clear();//clear the chart
for (int i = 0; i < shares.Length; i++)//for each share
{
cHshares.Series.Add(shares[i].name);//add a new share to the chart
cHshares.Series[shares[i].name].ChartType = SeriesChartType.FastLine;
int j = 0;
//draw the lines with each value that exists for each share
foreach (double value in shares[i].history)
{
cHshares.Series[shares[i].name].Points.AddXY(j, value);
j++;
}
}
}
Since I call the drawchart funktion every step why is it that it only shows after all steps are finished?
you have put your code in a loop like for loop
After each drawchart, update your chart. i.e.
drawchart(shares);
cHshares.Update();
k++;
I hope this will be useful.
I'm entering data from a CSV file into a OpenOffice spreadsheet.
This code gets the a new sheet in a spreadsheet:
Public Spreadsheet getSpreadsheet(int sheetIndex, XComponent xComp)
{
XSpreadsheet xSheets = ((XSpreadsheetDocument)xComp).getSheets();
XIndexAccess xSheetIA = (XIndexAccess)xSheets;
XSpreadsheet XSheet = (XSpreadsheet)xSheetsA.getByIndex(sheetIndex).Value;
return XSheet;
}
I then have method that enters a list into a cell range one cell at a time. I want to be able to automatically set the column size for these cells. which is something like
string final DataCell;
Xspreadsheet newSheet = getSpreadsheet(sheetIndex, xComp);
int numberOfRecords = ( int numberOfColumns * int numberOfRows);
for(cellNumber = 0; cellNumber < numberOfrecords; cellNumber++)
{
XCell tableData = newSheet.getCellbyPosition(columnValue, rowValue);
((XText)tableData).setString(finalDataCell);
column Value++;
if(columnValue > = numberOfColumns)
{
rowVal++ column = 0;
}
}
After googling i have found the function:
columns.OptimalWidth = True on http://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=31292
but im unsure on how to use this. Could anyone explain this further or think of another way to have the cell autofit?
I understand the comments in the code are in Spanish I think, but the code is in English. I ran the comments through Google translate so now they are in English. I copied it from here:
//Auto Enlarge col width
private void largeurAuto(string NomCol)
{
XCellRange Range = null;
Range = Sheet.getCellRangeByName(NomCol + "1"); //Recover the range, a cell is
XColumnRowRange RCol = (XColumnRowRange)Range; //Creates a collar ranks
XTableColumns LCol = RCol.getColumns(); // Retrieves the list of passes
uno.Any Col = LCol.getByIndex(0); //Extract the first Col
XPropertySet xPropSet = (XPropertySet)Col.Value;
xPropSet.setPropertyValue("OptimalWidth", new one.Any((bool)true));
}
What this does it this: First it gets the range name and then gets the first column. The real code, though, is XpropertySet being used, which is explained REALLY well here.
public void optimalWidth(XSpreadsheet newSheet)
{
// gets the used range of the sheet
XSheetCellCursor XCursor = newSheet.createCursor();
XUsedAreaCursor xUsedCursor = (XUsedAreaCursor)XCursor;
xUsedCursor.gotoStartOfUsedArea(true);
xUsedCursor.gotoEndOfUsedArea(true);
XCellRangeAddressable nomCol = (XCellRangeAddressable)xUsedCursor;
XColumnRowRange RCol = (XColumnRowRange)nomCol;
XTableColumns LCol = RCol.getColumns();
// loops round all of the columns
for (int i = 0; i < nomCol.getRangeAddress().EndColumn;i++)
{
XPropertySet xPropSet = (XPropertySet)LCol.getByIndex(i).Value;
xPropSet.setPropertyValue("OptimalWidth", new uno.Any(true));
}
}