Okay, i have a list and where i have structs stored, and i need to go through them from the last to the first, to check if one of the variables in the struct is 1. the code look like this:
for(int i = (checkpoints.Count - 1); i == 0; i--)
{
if(checkpoints[i].active == 1)
{
playerPositionX = checkpoints[i].xPosition;
playerPositionY = checkpoints[i].yPosition;
camPositionX = checkpoints[i].xPosition;
break;
}
}
this is the struct that i use:
private struct checkpoint
{
public int xPosition;
public int yPosition;
public int active;
}
what i need to do is to check if the variable active is == 1 in the struct that i have stored in the list. i have around 3-8 structs stored in the list. I need to start the check from the last struct in the list and work my way to the first.
when i try to debug the program it looks like it's not going from the last, but it starts at i=0.
please leave a comment if you have a fix, or if you need more information.
You can also use LastOrDefault() function. But, here can be one problem, because we are searching for Struct.
If nothing found?
LastOrDefault() will return default(checkpoint) if nothing found. The default value of a struct is the struct with all its values in turn default initialized. So, we must cast them to nullable using .Cast<checkpoint?>.
var activeCheckPoint = checkpoints
.Where(x => x.active == 1)
.Cast<checkpoint?>()
.LastOrDefault();
Or we must do the second check afterward that the returned object's active value is 1.
var activeCheckPoint = checkpoints.LastOrDefault(x => x.active == 1);
if(actactiveCheckPoint.active == 1)
{
// Then it is Ok
}
else
{
// Nothins was found
}
But, if you want to use for loop, then you must change i == 0 to i >= 0.
Your mistake was that you said to go though the loop if i was equal to 0, when it wasn't. You want the loop to loop until i is greater or equal to zero.
for(int i = (checkpoints.Count - 1); i >= 0; i--) // your mistake was here
{
if(checkpoints[i].active == 1)
{
playerPositionX = checkpoints[i].xPosition;
playerPositionY = checkpoints[i].yPosition;
camPositionX = checkpoints[i].xPosition;
break;
}
}
Related
I'm working on an implementation of the A-star algorithm in C# in Unity.
I need to evaluate a collection of Node :
class Node
{
public Cell cell;
public Node previous;
public int f;
public int h;
public Node(Cell cell, Node previous = null, int f = 0, int h = 0)
{
this.cell = cell;
this.previous = previous;
this.f = f;
this.h = h;
}
}
I have a SortedSet which allows me to store several Node, sorted by h property. Though, I need to be able to store two nodes with the same h property. So I've implemented a specific IComparer, in a way that allow me sorting by h property, and triggerring equality only when two nodes are representing the exact same cell.
class ByHCost : IComparer<Node>
{
public int Compare(Node n1, Node n2)
{
int result = n1.h.CompareTo(n2.h);
result = (result == 0) ? 1 : result;
result = (n1.cell == n2.cell) ? 0 : result;
return result;
}
}
My problem : I have a hard time to remove things from my SortedSet (I named it openSet).Here is an example:
At some point in the algorithm, I need to remove a node from the list based on some criteria (NB: I use isCell127 variable to focus my debug on an unique cell)
int removedNodesNb = openSet.RemoveWhere((Node n) => {
bool isSame = n.cell == candidateNode.cell;
bool hasWorseCost = n.f > candidateNode.f;
if(isCell127)
{
Debug.Log(isSame && hasWorseCost); // the predicate match exactly one time and debug.log return true
}
return isSame && hasWorseCost;
});
if(isCell127)
{
Debug.Log($"removed {removedNodesNb}"); // 0 nodes where removed
}
Here, the removeWhere method seems to find a match, but doesn't remove the node.
I tried another way :
Node worseNode = openSet.SingleOrDefault(n => {
bool isSame = n.cell == candidateNode.cell;
bool hasWorseCost = n.f > candidateNode.f;
return isSame && hasWorseCost;
});
if(isCell127)
{
Debug.Log($"does worseNode exists ? {worseNode != null}"); // Debug returns true, it does exist.
}
if(worseNode != null)
{
if(isCell127)
{
Debug.Log($"openSet length {openSet.Count}"); // 10
}
openSet.Remove(worseNode);
if(isCell127)
{
Debug.Log($"openSet length {openSet.Count}"); // 10 - It should have been 9.
}
}
I think the problem is related to my pretty unusual IComparer, but I can't figure whats exatcly the problem.
Also, I would like to know if there is a significative performance improvment about using an auto SortedSet instead of a manually sorted List, especially in the A-star algorithm use case.
If i write your test you do:
n1.h < n2.h
n1.cell = n2.cell -> final result = 0
n1.h > n2.h
n1.cell = n2.cell -> final result = 0
n1.h = n2.h
n1.cell != n2.cell -> final result = 1
n1.h < n2.h
n1.cell != n2.cell -> final result = -1
n1.h > n2.h
n1.cell != n2.cell -> final result = 1
when you have equality on h value (test number 3) you choose to have always the same result -> 1. so its no good you have to have another test on cell to clarify the position bacause there is a confusion with other test which gives the same result (test number 5)
So i could test with sample, but i am pretty sure you break the Sort.
So if you clarify the test, i suggest you to use Linq with a list...its best performance.
I'll answer my own topic because I've a pretty complete one.
Comparison
The comparison of the IComparer interface needs to follow some rules. Like #frenchy said, my own comparison was broken. Here are math fundamentals of a comparison I totally forgot (I found them here):
1) A.CompareTo(A) must return zero.
2) If A.CompareTo(B) returns zero, then B.CompareTo(A) must return zero.
3) If A.CompareTo(B) returns zero and B.CompareTo(C) returns zero, then A.CompareTo(C) must return zero.
4) If A.CompareTo(B) returns a value other than zero, then B.CompareTo(A) must return a value of the opposite sign.
5) If A.CompareTo(B) returns a value x not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) must return a value of the same sign as x and y.
6) By definition, any object compares greater than (or follows) null, and two null references compare equal to each other.
In my case, rule 4) - symetry - was broken.
I needed to store multiple node with the same h property, but also to sort by that h property. So, I needed to avoid equality when h property are the same.
What I decided to do, instead of a default value when h comparison lead to 0 (which broke 4th rule), is refine the comparison in a way that never lead to 0 with a unique value foreach node instance. Well, this implementation is probably not the best, maybe there is something better to do for a unique value, but here is what I did.
private class Node
{
private static int globalIncrement = 0;
public Cell cell;
public Node previous;
public int f;
public int h;
public int uid;
public Node(Cell cell, Node previous = null, int f = 0, int h = 0)
{
Node.globalIncrement++;
this.cell = cell;
this.previous = previous;
this.f = f;
this.h = h;
this.uid = Node.globalIncrement;
}
}
private class ByHCost : IComparer<Node>
{
public int Compare(Node n1, Node n2)
{
if(n1.cell == n2.cell)
{
return 0;
}
int result = n1.h.CompareTo(n2.h);
result = (result == 0) ? n1.uid.CompareTo(n2.uid) : result; // Here is the additional comparison which never lead to 0. Depending on use case and number of object, it would be better to use another system of unique values.
return result;
}
}
RemoveWhere method
RemoveWhere use a predicate to look into the collection so I didn't think it cares about comparison. But RemoveWhere use internally Remove method, which do care about the comparison. So, even if the RemoveWhere have found one element, if your comparison is inconstent, it will silently pass its way. That's a pretty weird implementation, no ?
For a part of my project, I want to enforce the rule that the user input can only be within a min/max word boundary. With a minimum of 1 word, and a maximum of 50 words. The boolean isn't changing from the default set value of false. Here is my code:
bool WordCount_Bool = false;
//Goto the method that handles the calculation of whether the users input is within the boundary.
WordCount_EH(WordCount_Bool);
//Decide whether to continue with the program depending on the users input.
if (WordCount_Bool == true)
{
/*TEMP*/MessageBox.Show("Valid input");/*TEMP*/
/*Split(split_text);*/
}
else
{
MessageBox.Show("Please keep witin the Min-Max word count margin.", "Error - Outside Word Limit boundary");
}
Method handling the array and the change of the boolean:
private bool WordCount_EH(bool WordCount_Bool)
{
string[] TEMPWordCount_Array = input_box.Text.Split(' ');
int j = 0;
int wordcount = 0;
for (int i = 100; i <=0; i--)
{
if (string.IsNullOrEmpty(TEMPWordCount_Array[j]))
{
//do nothing
}
else
{
wordcount++;
}
j++;
}
if (wordcount >= 1)
{
WordCount_Bool = true;
}
if (wordcount < 1)
{
WordCount_Bool = false;
}
return WordCount_Bool;
}
Thank you all in advance.
Side note: I realize that the for loop will throw an exception or at least is not optimal for its purpose so any advice will be much appreciated.
Extra Side note: Sorry I should have said that the reason i haven't used length is that wherever possible I should do my own code instead of using built-in functions.
The short answer is you should just return a true or false value from your WordCount_EH method like others have said
But just to clear up why it doesn't work. C# by default passes arguments by value. With Value types such as Boolean the actual value of true or false is stored in the variable. So when you pass your Boolean value into your method all you are doing is saying please put this bool value into my new variable (the method parameter). When you make changes to that new variable it only changes that variable. It has no relation to the variable that it was copied from. This is why you don't see a change in original bool variable. You may have named the variables the same but they are infact two different variables.
Jon Skeet explains it fantastically here http://jonskeet.uk/csharp/parameters.html
Here you go this should solve it:
if(input_box.Text.Split(' ').Length>50)
return false;
else
return true;
You need to pass WordCount_Bool by ref if you want to change it in WordCount_EH:
private bool WordCount_EH(ref bool WordCount_Bool) { ... }
bool WordCount_Bool = false;
WordCount_EH(ref WordCount_Bool);
although in this case you might as well use the return value:
bool WordCount_Bool = false;
WordCount_Bool = WordCount_EH(WordCount_Bool);
If you want to pass argument by reference, you need to do as per #Lee suggestion.
For your logic implementation, you can use following code to avoid array indexing.
// It would return true if you word count is greater than 0 and less or equal to 50
private bool WordCount_EH()
{
var splittedWords = input_box.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
return (splittedWords.Count > 0 && splittedWords.Count <= 50);
}
I am looping through a list and would like to add multiple occurrences, should I find them.
so far I have,
public struct optionsSort
{
public string name;
public string date;
public double strike;
public string callPut;
public double size;
public string isin;
}
List<List<optionsSort>> stocks = new List<List<optionsSort>>();
optionsSort tempStock1 = new optionsSort();
List<optionsSort> posCheckOptions = new List<optionsSort>();
then some code, then,
for(int k = 0; k<posCheckOptions.Count; k++)
{
for(int l = 0; l<posCheckOptions[l].Count; l++)
{
if(posCheckOptions[l+1] == null)
{
//finished the iteration
break;
}
else if
(posCheckOptions[k][l + 1].date == posCheckOptions[k][l].date
&& posCheckOptions[k][l + 1].strike == posCheckOptions[k][l].strike
&& posCheckOptions[k][l + 1].callPut == posCheckOptions[k][l].callPut)
{
posCheckOptions[k][l].size = posCheckOptions[k][l].size
+ posCheckOptions[k][l + 1].size;
}
}
}
Basicly, Im looking forward from the start of the list. asking the question, are certain elements of the list at i+1 the same as i, if so, add those elements to i and delete the entire row.
i get this error
"Error 1 Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable C:\Users\WindowsFormsApplication1\WindowsFormsApplication1\ReadCSV.cs 890 25 WindowsFormsApplication1
"
Many Thanks for looking.
I believe your problem is that you are using a mutable struct. Mutable structs are evil.
The simplest solution is to change optionsSort to a class. That should fix the error message.
To explain the error message, when you call posCheckOptions[k][l], since optionsSort is a struct, it returns a copy of the value in the list. When you change size, it will update the copy, but not the one in the list. The copy would then be discarded. The compiler recognizes this and stops you.
I recommend you read up on the differences between reference types and value types.
I'm having a little difficulty with the array.sort. I have a class and this class has two fields, one is a random string the other one is a random number. If i want to sort it with one parameter it just works fine. But i would like to sort it with two parameters. The first one is the SUM of the numbers(from low to high), and THEN if these numbers are equal by the random string that is give to them(from low to high).
Can you give some hint and tips how may i can "merge" these two kinds of sort?
Array.Sort(Phonebook, delegate(PBook user1, PBook user2)
{ return user1.Sum().CompareTo(user2.Sum()); });
Console.WriteLine("ORDER");
foreach (PBook user in Phonebook)
{
Console.WriteLine(user.name);
}
That's how i order it with one parameter.
i think this is what you are after:
sourcearray.OrderBy(a=> a.sum).ThenBy(a => a.random)
Here is the general algorithm that you'll use for comparing multiple fields in a CompareTo method:
public int compare(MyClass first, MyClass second)
{
int firstComparison = first.FirstValue.CompareTo(second.SecondValue);
if (firstComparison != 0)
{
return firstComparison;
}
else
{
return first.SecondValue.CompareTo(second.SecondValue);
}
}
However, LINQ does make the syntax for doing this much easier, allowing you to only write:
Phonebook = Phonebook.OrderBy(book=> book.Sum())
.ThenBy(book => book.OtherProperty)
.ToArray();
You can do this in-place by using a custom IComparer<PBook>. The following should order your array as per your original code, but if two sums are equal it should fall back on the random string (which I've called RandomString):
public class PBookComparer : IComparer<PBook>
{
public int Compare(PBook x, PBook y)
{
// Sort null items to the top; you can drop this
// if you don't care about null items.
if (x == null)
return y == null ? 0 : -1;
else if (y == null)
return 1;
// Comparison of sums.
var sumCompare = x.Sum().CompareTo(y.Sum());
if (sumCompare != 0)
return sumCompare;
// Sums are the same; return comparison of strings
return String.Compare(x.RandomString, y.RandomString);
}
}
You call this as
Array.Sort(Phonebook, new PBookComparer());
You could just do this inline but it gets a bit hard to follow:
Array.Sort(Phonebook, (x, y) => {
int sc = x.Sum().CompareTo(y.Sum());
return sc != 0 ? sc : string.Compare(x.RandomString, y.RandomString); });
... Actually, that isn't too bad, although I have dropped the null checks.
Well i got this if else statement where i have 12 variables that can either be 0, 1, or 2. if its a 0 its pass, if its 1 it fails, if its a 2 its unknown. I was wondering if anyone knows a shorter way of writing it in C#
here is what i have to write if there isn't
string pass = "pass";
string fail = "fail";
string unknown = "unknown"
if ( value == 0)
{
test1 = pass;
}
else if (value == 1)
{
test1 = fail;
}
else if (value == 2)
{
test1 = unknown;
}
if ( value1 == 0)
{
test2 = pass;
}
else if (value1 == 1)
{
test2 = fail;
}
else if (value1 == 2)
{
test2 = unknown;
}
.
.
.
if ( value12 == 0)
{
test13 = pass;
}
Let me explain a little more. I have 12 pictures on webpage, that need to be updated, depending on a database for the values. Each picture can be only 1 of 3 options and can change depending on the database. A pass(check mark), a fail(a red x) and an unknown (question mark). Let me know if you need more details.
Well i got this if else statement where i have 12 variables that can either be 0 1 or 2.
Any time you have several variables which you want to be able to treat in a similar way, you should use a collection for them, e.g. an array or a List<T>. If you don't already have a collection for them, you can create one:
int[] values = { value0, value, value2 /* etc * };
... although it would be better if you could have them as a collection from the very start.
Then you can iterate over all of them. It's not really clear why in this case you're overwriting the value of test in each block, but having a collection of inputs ends up with a natural way of creating a collection of outputs. You can also use a switch statement or a conditional expression to make the checks simpler. For example:
public static string ConvertValueToLabel(int value)
{
switch (value)
{
case 0: return "pass";
case 1: return "fail";
case 2: return "unknown";
// Adjust this behaviour as appropriate...
default: throw new ArgumentOutOfRangeException("value");
}
}
Or:
public static string ConvertValueToLabel(int value)
{
// Note that this doesn't do the same range checking as the version above
return value == 0 ? "pass"
: value == 1 ? "fail"
: "unknown";
}
(Some people don't like "stacking" conditionals like this, and I probably wouldn't use it in this case where a switch statement is probably more sensible, but it can be really handy.)
Looks like a case for arrays.
int[] values = { 1, 1, 2, 2, 1, 2 };
for (int i = 0; i < values.Length; i++)
{
if (values[i] == 1)
{
}
else if (values[i] == 2)
{
}
}//for
According to your code there is no need to check values 0-11 cause the test variable is changed again using value12. So you can just check the last value and and skip all other.
I would write a for loop which checks each variable. Or, at the very least, write the check as a function which can be referenced with each variable. Do the former if the variables are in series and their names can be determined sequentially. Do the later if the variable names are not really related to each other.
If necessary, place the variables into an array which can be looped through.