How to move stack list in other list c#? - c#

I'm working on a solitaire game.
I need help for move several cards from one pile to another.
I can just move one card to an other pile and I don't know how to change that because I use stack list.
So How can check another index apart the first index ?
// valid card at to pile
public bool TryPushCardsOnPile(Card card, int index)
{
Stack<Card> pile = piles[index];
if (pile.Count() == 0)
{
return (card.getRank() == CardRank.King);
}
Card topPile = piles[index].Peek();
if (!topPile.IsVisible())
{
return false;
}
return (card.IsRed() != topPile.IsRed()) && (card.getRank() == topPile.getRank() - 1);
}
in Form Class
public void PileClicked(int index)
{
if (IsDeckDisplaySelected())
{
Card temp = game.deckDisplay.Last();
if (game.TryPushCardsOnPile(temp, index))
{
game.piles[index].Push(temp);
game.deckDisplay.Remove(temp);
move++;
}
Unselect();
SelectPile(index);
}
else if (IsPileSelected())
{
int oldPile = SelectedPile();
if (index != oldPile)
{
Card temp = game.piles[oldPile].Peek();
if (game.TryPushCardsOnPile (temp, index))
{
game.piles[index].Push(temp);
game.piles[oldPile].Pop();
if (game.piles[oldPile].FirstOrDefault() != null)
{
game.piles[oldPile].FirstOrDefault().IsVisible();
}
move++;
Unselect();
}
else
{
// game.piles[oldPile].Push(temp);
// game.AddToPile(temp, oldPile);
Unselect();
SelectPile(index);
}
}
else Unselect();
}
else
{
SelectPile(index);
game.piles[index].Peek().TurnCardUp();
}
}
thank you

While using List or even custom linked list would be probably better choice, it is easy to move block of items from one stack to another: pop all, reverse, push to other stack:
var items = sourceStack.Take(4).Reverse().ToList();
items.ForEach(x => destinationStack.Push(x));

Related

How do i access specific list object from a function? C#

Im new to programming and this might be some rookie problem that im having, but id appreaciate a hand.
void Sort()
{
List<Lag> lagen = new List<Lag>() { AIK, DIF, MLM, BJK };
List<Lag> sorted = lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
Print(sorted);
}
This is my sorting function, where i take one list, and turn it into a list called "sorted".
What i want to do now is let the user pick one of the objects in the sorted list by entering a number. This far iw written the following code. Where int hemlag, and int bortlag are objects in the sorted list.
And now i want to change the value of "Lag hemmalag" & "Lag bortalag" depending on what numbers (what team) the user put in.
So for example, if the user input "int hemlag" is 2.. i want hemmalag = to be the 2nd object in the list "sorted". And now i run into a problem. Cause i cant access that sorted list from this function.
My theories are that it might have to do something with returning that list from the sorting function, but i have not yet found a way to do that...
void ChangeStats(int hemlag, int bortlag, int mål, int insläpp)
{
Sortera();
Lag hemmalag = AIK;
Lag bortalag = AIK;
if (hemlag == 1) { ; }
if (hemlag == 2) { hemmalag = DIF; }
if (hemlag == 3) { hemmalag = MLM; }
if (hemlag == 4) { hemmalag = BJK; }
if (bortlag == 1) { bortalag = AIK; }
if (bortlag == 2) { bortalag = DIF; }
if (bortlag == 3) { bortalag = MLM; }
if (bortlag == 4) { bortalag = BJK; }
hemmalag.mål += mål;
hemmalag.insläppta += insläpp;
bortalag.insläppta += mål;
bortalag.mål += insläpp;
if (mål > insläpp)
{
hemmalag.poäng += 3;
hemmalag.vinster++;
hemmalag.spel++;
bortalag.förlorade++;
bortalag.spel++;
}
if (mål < insläpp)
{
bortalag.poäng += 3;
bortalag.vinster++;
bortalag.spel++;
hemmalag.förlorade++;
hemmalag.spel++;
}
if (mål == insläpp)
{
bortalag.lika++;
bortalag.poäng++;
hemmalag.lika++;
bortalag.poäng++;
}
Console.WriteLine("Stats changed");
Console.WriteLine("---");
Save();
Sortera();
}
Help appreciated, cheers!
A good practice when programming is to try to ensure that a function has a specific purpose, and only does that specific thing. In your case your Sort-function actually does three things, create the list, sort it, and print it. So lets rewrite your sort-function to return the sorted value:
List<Lag> Sort(IEnumerable<Lag> lagen)
{
return lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
}
This uses the IEnumerable<Lag> to accept any kind of sequence of Lag, List, array, HashSet etc. It helps make the code more flexible to accept a wider type of arguments.
Assuming you got the printing and user input correct, the change stats function should probably look something like:
List<Lag> ChangeStats(List<Lag> lagen, int hemlagIndex, int bortlagIndex, int mål, int insläpp){
var hemlag = lagen[hemlagIndex];
var bortalag = lagen[bortlagIndex];
// Do the stat-update logic
...
return lagen
}
You should probably also make your safe-method take a sequence of Lag as input, and move sorting and saving outside the ChangeStats method. Again try to make sure each method has a specific purpose.
These examples only uses method parameters for all the data. This is often a good thing since it makes it more obvious what data the method is using. But in some cases it might be preferable to use a field in the class instead, something like:
public class MinaLag{
private List<Lag> lagen = new List<Lag>(){ AIK, DIF, MLM, BJK };
public void Sort(){
lagen = lagen.OrderBy(...);
}
public void ChangeStats(int hemlagIndex, int bortlagIndex, int mål, int insläpp){
var hemlag = lagen[hemlagIndex];
var bortalag = lagen[bortlagIndex];
...
}
public void Print(){...}
public void Save(Stream s){...}
public static MinLag Load(Stream s){...}
}
This wraps a list of the objects and provides methods to do all the required operations on them, removing the need for the user give and receive the list for each called method.
Here is my example on the global scope list. I'm not 100% this can sort the issue but I'm confident.
class Example {
private List<Lag> Lagen {
get;
set;
} // Global Scope - Make it public if you need to access it from another
// class.
public Example() {
this.Lagen = new List<Lag>{AIK, DIF, MLM,
BJK}; // Assign intial values on class execution
}
void Sort() {
// Everything else will be the same but now you can access it from anywhere
// within the class
List<Lag> sorted = Lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
Print(sorted);
}
}
your description is very convoluted and I'm not fully getting what you're up to, but...
You can return the sorted list from the Sort() function by changing the return type from void to List and at its bottom just having line like return sorted
You can also consider leaving void as return type but passing original list to it and turning it to a sorted one inside. List is an object so you'll get it back in the calling function and can further work with it there.
private List<Lag> lagen = new List<Lag>() { AIK, DIF, MLM, BJK };
private void Sortera()
{
lagen = lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
Print(lagen);
}
private void ChangeStats(int hemlag, int bortlag, int mål, int insläpp)
{
Sortera();
Lag hemmalag = AIK;
Lag bortalag = AIK;
if (hemlag == 1) {; }
if (hemlag == 2) { lagen[1] = AIK }
if (hemlag == 3) { lagen[2] = MLM; }
if (hemlag == 4) { lagen[3] = BJK; }
if (bortlag == 1) { lagen[0] = AIK; }
if (bortlag == 2) { lagen[1] = DIF; }
if (bortlag == 3) { lagen[2] = MLM; }
if (bortlag == 4) { lagen[3] = BJK; }
etc.....
I'm still not sure what the rest of the solution means but this way you can change your list items

C# Filter Items In A List According To Multiple Criteria

First, what my situation here is...
My SomeObject has a property string Status which I am interested in for this scenario.
Status property can contain "Open", "Closed", "Finished" values exactly.
I have a method called FilterObjects which returns a List<SomeObject>
Method accepts an argument same as its return type, List<SomeObject>
Method is supposed to filter according to following cases explained below and return the list of objects.
The List<SomeObject> I am sending as argument to my method is guaranteed to be in order (through their ID and type).
The cases are (all related to the string Status property I mentioned):
If any item in the list contains Status = "Finished"; then eliminate all other elements that was in the original list and return only the object that has the "Finished" status.
If any item does NOT contain Status = Finished but contains "CLOSED", I need to check if there is any other item that has the value of "Open" after that "CLOSED" one. You can think of this as a "a task can be closed, but can be reopened. But once it is finished, it cannot be reopened".
If it contains a "CLOSED" and does not have any "OPEN" after that item, I will ignore all the items before CLOSED and only return CLOSED object. If it contains "OPEN" after any closed, I need to return anything AFTER that CLOSED, by excluding itself.
I also tried explain the same thing with my awesome MS Paint skills.
The object itself is not really a problem, but my method is something like this:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
var objects = objectList;
var returnList = new List<SomeObject>();
foreach (var obj in objects)
{
if (obj.Status == "Finished")
{
returnList.Add(obj);
return returnList;
}
}
return new List<SomeObject>();
}
Long story short, what would be the best and most efficient way to apply all this logic in this single method? Honestly, I couldn't go further than the first case I already implemented, which is the FINISHED. Could this whole thing be done with some LINQ magic?
It is guaranteed that I receive an ordered list AND I will never get items more than a couple of hundred so the collection will never be massive.
Many thanks in advance for the help.
You can try something like that:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
SomeObject finished = objectList.FirstOrDefault(o => o.Status.Equals("Finished"));
if (finished != null) { return new List<SomeObject> { finished }; }
List<SomeObject> closed = objectList.SkipWhile(o => !o.Status.Equals("Closed")).ToList();
if (closed.Count == 1) { return closed; }
if (closed.Count > 1) { return closed.Skip(1).ToList(); }
// if you need a new list object than return new List<SomeObject>(objectList);
return objectList;
}
I really wouldn't bother using Linq for this, as you will either create an overly complicated instruction to manage or you will require several loop iterations. I would go for something like this instead:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int lastClosed = -1;
for (int i = 0; i < objectList.Count; i++)
{
if (objectList[i].Status == "Closed")
lastClosed = i;
else if (objectList[i].Status == "Finished")
return new List<SomeObject>() { objectList[i] };
}
if (lastClosed > -1)
if (lastClosed == objectList.Count - 1)
return new List<SomeObject>() { objectList[lastClosed] };
else
return objectList.Skip(lastClosed + 1).ToList();
else
return objectList;
}
EDIT: slightly changed the last bit of code so that it won't trigger an exception if the objectList is empty
LINQ is not well suited and inefficient for scenarios where you need to apply logic based on previous / next elements of a sequence.
The optimal way to apply your logic is to use a single loop and track the Closed status and the position where the status change occurred. At the end you'll return a single element at that position if the last status is Closed, or a range starting at that position otherwise.
static List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int pos = 0;
bool closed = false;
for (int i = 0; i < objectList.Count; i++)
{
var item = objectList[i];
if (item.Status == "Finished")
return new List<SomeObject> { item };
if (item.Status == (closed ? "Opened" : "Closed"))
{
pos = i;
closed = !closed;
}
}
return objectList.GetRange(pos, closed ? 1 : objectList.Count - pos);
}
I did it this way:
public static IEnumerable<SomeObject> convert(this IEnumerable<SomeObject> input)
{
var finished = input.FirstOrDefault(x => x.Status == "Finished");
if (finished != null)
{
return new List<SomeObject> {finished};
}
return input.Aggregate(new List<SomeObject>(), (a, b) =>
{
if (!a.Any())
{
a.Add(b);
}
else if (b.Status == "Open")
{
if (a.Last().Status == "Closed")
{
a.Remove(a.Last());
}
a.Add(b);
}
else if (b.Status == "Closed")
{
a = new List<SomeObject> {b};
}
return a;
});
}
You can write a method like this. This is bare minimum you will have to add null check and exception handling.
public List<SomeCls> GetResult(List<SomeCls> lstData)
{
List<SomeCls> lstResult;
if(lstData.Any(x=>x.Status=="Finished"))
{
lstResult = lstData.Where(x=>x.Status=="Finished").ToList();
}
else if(lstData.Any(x=>x.Status=="Closed"))
{
// Here assuming that there is only one Closed in whole list
int index = lstData.FindIndex(0,lstData.Count(),x=>x.Status=="Closed");
lstResult = lstData.GetRange(index,lstData.Count()-index);
if(lstResult.Count()!=1) // check if it contains Open.
{
lstResult = lstResult.Where(x=>x.Status=="Open").ToList();
}
}
else // Only Open
{
lstResult = lstData;
}
return lstResult;
}
something like this :
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
if (objectList.Where(x => x.Status == "Finished").Any())
{
return objectList.Where(x => x.Status == "Finished").ToList();
}
else if (objectList.Where(x => x.Status == "Closed").Any())
{
if (objectList.FindIndex(x => x.Status == "Closed") == objectList.Count() - 1)
{
return objectList.Where(x => x.Status == "Closed").ToList();
}
else
{
return objectList.GetRange(objectList.FindIndex(x => x.Status == "Closed") + 1, objectList.Count() - (objectList.FindIndex(x => x.Status == "Closed") + 1));
}
}
return objectList;
}

LINQ List<> Moving Elements up and down

Would it be possible to tell me the best way of moving elements in a List<> up and down.
For example I have a class called Building and Building has a list of Rooms objects List<Room>. The rooms are added to the building by name, but I am using this structure to generate a tree view. The user has the option to move a room up and down within a building.
I was trying to use .Reverse(index, count) but this didn't seem to do anything:
// can this item actually be moved up (is it at the first position in it's current parent?)
if (moveDirection == MoveDirection.UP)
{
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
else
{
// move this room up.
parentBuilding.Rooms.Reverse(roomIndex, 1);
}
}
Create a list extension. Call as List<T>.Move(1, MoveDirection.Up).
public static class ListExtensions
{
public static void Move<T>(this IList<T> list, int iIndexToMove,
MoveDirection direction)
{
if (direction == MoveDirection.Up)
{
var old = list[iIndexToMove - 1];
list[iIndexToMove - 1] = list[iIndexToMove];
list[iIndexToMove] = old;
}
else
{
var old = list[iIndexToMove + 1];
list[iIndexToMove + 1] = list[iIndexToMove];
list[iIndexToMove] = old;
}
}
}
public enum MoveDirection
{
Up,
Down
}
Things to consider
Exception handling - what if you are trying to move the bottom
element down or top element up? You will get an index out of range
because you can't move these up or down.
You could improve this and prevent handling exception by extending
functionality to moving the top element to the bottom element and
bottom element down to the top element etc.
Just do a swap:
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
else
{
// move this room up.
var temp = parentBuilding.Rooms[index-1];
parentBuilding.Rooms[index-1] = parentBuilding.Rooms[index];
parentBuilding.Rooms[index] = temp;
}
Personally, I'd make extension method:
static void Swap<TSource>(this IList<TSource> source, int fromIndex, int toIndex)
{
if (source == null)
throw new ArgumentNullExcpetion("source");
TSource tmp = source[toIndex];
source[toIndex] = source[fromIndex];
source[fromIndex] = tmp;
}
Usage:
if (moveDirection == MoveDirection.UP)
{
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
else
{
// move this room up.
parentBuilding.Rooms.Swap(roomIndex, roomIndex - 1);
}
}
How about using SortedDictionary<int, Room> instead of a list. You could store an index in as a Key of the Dictionary and just swap the values when needed.
Swapping places with the room that used to be above should do it:
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
var wasAbove = parentBuilding.Rooms[roomIndex - 1];
parentBuilding.Rooms[roomIndex - 1] = room;
parentBuilding.Rooms[roomIndex] = wasAbove;
That said, I 'm not sure that this is the best object model for the situation; it's not clear that the order of rooms in the list plays a role, and it's also not clear how a room can be "moved up" -- what does that mean?
It might be better to have a RoomPlacement class that aggregates a room and enough information to locate it, and work with that instead.
try this:
int newIndex = whateverIndexYouWantItAt;
int oldIndex = parentBuilding.Rooms.IndexOf(room);
var item = parentBuilding.Rooms[oldIndex];
list.RemoveAt(oldIndex);
if (newIndex > oldIndex) newIndex--;
parentBuilding.Rooms.Insert(newIndex, item);

What is an effective way to validate a bunch of variables before moving on?

I've been reading about the ideal size of methods and the single responsibility principle then I go look at some of my code. I feel I can break up a lot (>90%) of my stuff to be small manageable methods but then I get to validating a data or a form. It always seems really large and bloated. I tend to validate my data with nested if statements and try to catch errors or issues at each level. But when I start to get 6, 8, 10+ levels of validation it is very cumbersome. But I'm not sure how to break it up to be more effective.
An example of something I think is cumbersome but not sure how to improve upon it is below.
Each of the levels has a unique action associated with it and only once all the conditions return true can the whole thing return true but this is tough to read, especially after coming back to the program after a month or so.
if (InitialUsageSettings.zeroed || sender.Equals(btnZero))
{
if (InitialUsageSettings.StandardFilterRun || sender.Equals(btnStandard))
{
if (InitialUsageSettings.ReferenceFilterRun || sender.Equals(btnReference) || sender.Equals(btnStandard))
{
if (InitialUsageSettings.PrecisionTestRun || sender.Equals(btnPrecision) || sender.Equals(btnReference) || sender.Equals(btnStandard))
{
if (txtOperatorID.Text.Length > 0 && cboProject.Text.Length > 0 && cboFilterType.Text.Length > 0 && cboInstType.Text.Length > 0)
{
if (txtFilterID.Text.Length > 0 && txtLot.Text.Length > 0)
{
return true;
}
else
{
if (txtFilterID.Text.Length == 0)
{
//E
}
if (txtLot.Text.Length == 0)
{
//D
}
}
}
else
{
if (txtOperatorID.Text.Length == 0)
{
//A
}
if (cboProject.Text.Length == 0)
{
//B
}
if (cboFilterType.Text.Length == 0)
{
//C
}
if (cboInstType.Text.Length == 0)
{
//D
}
//return false;
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: X");
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: Y");
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: Z");
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: A");
}
If your main purpose is to break the methods up into manageable chunks, you could encapsulate each if block in its own method. e.g.:
if (InitialUsageSettings.zeroed || sender.Equals(btnZero))
{
ValidateStandardFilter();
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: A");
}
But it seems to me that this method has too many responsibilities: You're trying to make it validate and also output a message. Instead, the method should be solely responsible for validating.
public ValidationResult Validate(Sender sender)
{
if (!(InitialUsageSettings.zeroed || sender.Equals(btnZero)))
{
return ValidationResult.Error("A");
}
if (!(InitialUsageSettings.StandardFilterRun || sender.Equals(btnStandard)))
{
return ValidationResult.Error("Z");
}
// Etc...
if (txtOperatorID.Text.Length == 0)
{
errors.Add("A");
}
if (cboProject.Text.Length == 0)
{
errors.Add("B");
}
if (cboFilterType.Text.Length == 0)
{
errors.Add("C");
}
if (cboInstType.Text.Length == 0)
{
errors.Add("D");
}
if(errors.Count > 0)
{
return ValidationResult.Errors(errors);
}
if (txtFilterID.Text.Length == 0)
{
errors.Add("E");
}
if (txtLot.Text.Length == 0)
{
errors.Add("D");
}
return errors.Count > 0
? ValidationResult.Errors(errors)
: ValidationResult.Success();
}
And then the calling code can worry about the output:
var result = Validate(sender);
if (result.IsError)
{
outputMessages.AppendLine("Please correct...: " + result.Issue);
}
To get an idea of what the ValidationResult class might look like, see my answer here.
Update
The code above could be further refactored to reduce repetition even more:
public ValidationResult Validate(Sender sender)
{
if (!(InitialUsageSettings.zeroed || sender.Equals(btnZero)))
{
return ValidationResult.Error("A");
}
if (!(InitialUsageSettings.StandardFilterRun || sender.Equals(btnStandard)))
{
return ValidationResult.Error("Z");
}
// Etc...
var firstErrorBatch = GetEmptyStringErrors(
new[]{
new InputCheckPair(txtOperatorID, "A"),
new InputCheckPair(cboProject, "B"),
new InputCheckPair(cboFilterType, "C"),
new InputCheckPair(cboInstType, "D"),
})
.ToList();
if(firstErrorBatch.Count > 0)
{
return ValidationResult.Errors(firstErrorBatch);
}
var secondErrorBatch = GetEmptyStringErrors(
new[]{
new InputCheckPair(txtFilterID, "E"),
new InputCheckPair(txtLot, "D"),
})
.ToList();
return secondErrorBatch.Count > 0
? ValidationResult.Errors(secondErrorBatch)
: ValidationResult.Success();
}
private class InputCheckPair
{
public InputCheckPair(TextBox input, string errorIfEmpty)
{
Input = input;
ErrorIfEmpty = errorIfEmpty;
}
public TextBox Input {get; private set;}
public string ErrorIfEmpty{get; private set;}
}
public IEnumerable<string> GetEmptyStringErrors(IEnumerable<InputCheckPair> pairs)
{
return from p in pairs where p.Input.Text.Length == 0 select p.ErrorIfEmpty;
}
Something akin to
if(errorCondition1)
errors.add(message1);
if(errorCondition2)
errors.add(message2);
return errors.Count == 0;
So each condition is not nested
You can invert your if statements and use Guard Clauses instead. See this example.
Reverse the flow. Instead of
If(cond) {
if(someothercond) {
//great sucess!
return true;
} else {
// handle
return false;
}
} else {
// handle
return false;
}
do:
if(!cond1) {
// handle
return false;
}
if(!someothercond) {
// handle
return false;
}
// great sucess!
return true;
One way is to have a validation method that is called prior to executing your other code.
For example:
private String ValidateThis() {
StringBuilder result = new StringBuilder();
if (!cond1) {
result.AppendLine("error on cond1");
}
if (!cond2) {
result.AppendLine("error on cond2");
}
return result.ToString();
}
public void ButtonClick(object sender) {
String isValid = ValidateThis();
if (!String.IsNullOrEmpty(isValid)) {
// set your error message
outputMessages.AppendLine(isValid);
return;
}
// ... perform your other operations.
}
I would try to have each validation defined as a predicate, something like this...
delegate bool Validator(object sender, out string message);
Then you could string as many of those together as you need.
There are a number of ways to tackle this. You really want to limit the amount of repeated code, such as the code that adds an output message, which is nearly identical in four or more places.
If you think of these nested if…else blocks as a sequence, where as soon as one fails you take action and stop further processing, you can create a list and leverage LINQ's FirstOrDefault functionality to process the list of conditions sequentially until one fails, or you get null if they all pass.
Creating an object to encapsulate the conditions will help consolidate and reduce duplication.
Here is an example:
public class Validator
{
public Validator(string code, bool settingsCheck, Button button, object sender)
{
Code = code;
IsValid = sender != null && button != null && sender.Equals(button);
}
public bool IsValid { get; private set; }
public string Code { get; private set; }
}
Now, your method looks more like this:
var validationChecks = new List<Validator>
{
new Validator("A", InitialUsageSettings.zeroed, btnZero, sender),
new Validator("Z", InitialUsageSettings.StandardFilterRun, btnStandard, sender),
new Validator("Y", InitialUsageSettings.ReferenceFilterRun, btnReference, sender),
new Validator("X", InitialUsageSettings.PrecisionTestRun, btnPrecision, sender)
}
var failure = validationChecks.FirstOrDefault(check => !check.IsValid);
if (failure != null)
{
outputMessages.AppendLineFormat(
"Please correct the following issues before taking a reading: {0}", failure.Code);
return;
}
else
{
// further checks; I'm not sure what you're doing there with A-E
}

List<> Get next or previous element that satisfys a condition

I am writing a card game and have been using the following method to get the next Player who's turn it is
There is a direction to the game which could be forwards or backwards, and it needs to respect this too
private Player GetNextPlayer()
{
int currentPlayerIndex = Players.FindIndex(o => o.IsThisPlayersTurn);
Player nextPlayer;
if (_direction.Equals(Direction.Forwards))
{
nextPlayer = currentPlayerIndex == Players.Count - 1 ? Players[0] : Players[currentPlayerIndex + 1];
}
else
{
nextPlayer = currentPlayerIndex == 0 ? Players[Players.Count - 1] : Players[currentPlayerIndex - 1];
}
return nextPlayer;
}
This works fine until a player has finished the game. Then it can potentially return a player who is no longer in the game.
When a player has finished the game their PlayerState is HasNoCards
So I changed it to this, but it seems to be buggy in certain cases
public Player GetNextPlayer()
{
var players = Players.Where(o => o.PlayerState != PlayerState.HasNoCards);
if (Direction.Equals(Direction.Backwards))
{
players = players.Reverse();
}
bool selectNextPlayer = false;
foreach (Player player in players)
{
if (selectNextPlayer)
{
return player;
}
if (player.IsThisPlayersTurn)
{
selectNextPlayer = true;
}
}
return players.First();
}
I reckon there must be a smart way with linq to say "get the next player , where the Player.PlayerState is not PlayerState.HasNoCards"
Any ideas?
I should add that I can't remove the player from the list to solve the problem as it would screw my databinding
EDIT
I have a failing unit test for the scenario that the second method can't handle. It is when a player plays their last card when the direction is backwards. As I immediately filter the current player from the list, with
var players = Players.Where(o => o.PlayerState != PlayerState.HasNoCards);
public Player GetNextPlayer()
{
int currentPlayerIndex = Players.FindIndex(o => o.IsThisPlayersTurn);
int next = _direction.Equals(Direction.Forwards) ? 1 : -1;
int nextPlayerIndex = currentPlayerIndex;
do
{
nextPlayerIndex = (nextPlayerIndex + next + Players.Count) % Players.Count;
}while(Players[nextPlayerIndex].HasNoCards && nextPlayerIndex != currentPlayerIndex);
return Players[nextPlayerIndex];
}
The trick with LINQ is to carefully design your starting sequence so that it contains all possible output values in logical order. In this case, you want the starting sequence to be all the other players, in turn order, starting with the player following the current player. Once you can express that, it is trivial to handle cases like backwards direction, or players who have no cards.
private Player GetNextPlayer() {
if (!Players.Any()) throw new InvalidOperationException("No players.");
if (Players.Count(p => p.IsThisPlayersTurn) != 1) {
throw new InvalidOperationException(
"It must be one--and only one--player's turn.");
}
var current = Players.Single(p => p.IsThisPlayersTurn);
var subsequent = Players.Concat(Players)
.SkipWhile(p => p != current)
.Skip(1) // skip current player
.TakeWhile(p => p != current);
if (_direction == Direction.Backwards) {
subsequent = subsequent.Reverse();
}
return subsequent
.FirstOrDefault(p => p.PlayerState != PlayerState.HasNoCards);
}
Have come up with the following, which works, but would be interested in any more elegant solutions
private Player GetNextPlayer()
{
var players = Players.AsEnumerable();
if (Direction.Equals(Direction.Backwards))
{
players = players.Reverse();
}
bool selectNextPlayer = false;
foreach (Player player in players)
{
if (selectNextPlayer && !player.PlayerState.Equals(PlayerState.HasNoCards))
{
return player;
}
if (player.IsThisPlayersTurn)
{
selectNextPlayer = true;
}
}
return players.First(o => !o.PlayerState.Equals(PlayerState.HasNoCards));
}

Categories