Get the next element in Linked List with a condition - c#

I'm using a Linked List for turn management in my game. I have Players which I iterate through them, however when one player completes the game he needs to be skipped over, which is where I'm failing at.
How can I do this? here is what I have right now:
public Player GetNextPlayer() {
var current = linkedPlayerList.Find(currentPlayer);
Player nextPlayer = current.Next == null ? linkedPlayerList.First.Value : current.Next.Value;
SetCurrentPlayer(nextPlayer);
return nextPlayer;
}
I tried the following but it doesn't work.
Player nextPlayer = current.Next == null
? linkedPlayerList.First(f => !f.RoundCompleted)
: current.Next.List.Where(w=>!w.RoundCompleted).ElementAt(linkedPlayerList.ToList().IndexOf(currentPlayer));

I would use a loop to check for your condition. Comments in code to explain.
LinkedList<Player> linkedPlayerList = ......;
Player currentPlayer = .......;
public Player GetNextPlayer()
{
// Find the current node
var curNode = linkedPlayerList.Find(currentPlayer);
// Point to the next
LinkedListNode<Player> nextNode = curNode.Next;
// Check if at the end of the list
nextNode = nextNode == null ? linkedPlayerList.First : nextNode;
// Loop stops when we find the condition true or we reach the starting point
while (curNode != nextNode)
{
// Exit if found....
if (!nextNode.Value.RoundCompleted)
break;
// Manage the next node to check for....
nextNode = nextNode?.Next == null ? linkedPlayerList.First : nextNode.Next;
}
SetCurrentPlayer(nextNode.Value);
return nextNode.Value;
}

You can do something like this (add an an extra null check on next):
Player nextPlayer = current.Next.List.FirstOrDefault (x => !.RoundCompleted);

Related

How to get all children without the parent?

Transform[] drawns = GetComponentsInChildren<Transform>()
This include also the parent but i want to get only the children of the transform the script is connected.
The problem is that it's destroying in the loop also the parent.
The first item in the array of drawns is the parent :
case DrawStates.DrawOnGizmosRuntime:
drawOnce = true;
if (line != null && drawOnGizmos == false)
{
Transform[] drawns = GetComponentsInChildren<Transform>();
if (drawns.Length > 0)
{
foreach (Transform drawn in drawns)
{
Destroy(drawn.gameObject);
}
}
}
if (boxCollider == null)
{
boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>();
}
drawOnGizmos = true;
break;
There are actually several ways to find children without parent.
foreach (var child in children) Debug.Log(child);
Use Where extension:
After using system.linq, you can separate children that are not the original transform, as shown below.
var children = transform.GetComponentsInChildren<Transform>().Where(t => t != transform);
Removing index 0:
Since index 0 is always the main transform, you can delete it after converting children to list.
var children = transform.GetComponentsInChildren<Transform>().ToList();
children.RemoveAt(0);
Using Skip(1)
Thanks to dear #Enigmativity, Another solution is to use Skip(1), which actually avoids the main transform member.
var children = transform.GetComponentsInChildren<Transform>().Skip(1);

Rotating a node up a BST depending on the access count to optimize the tree for searching

I have been running into an interesting error while building a "search optimized" BST (each node has an access count identifier). I can successfully move nodes up the tree however, some nodes do not move up the tree as many times as expected.
For example:
When I call my find method on value 1 the tree successfully adjusts and 1 becomes the root. Subsequently when I call the method on 4 the node moves up to be the right child of the root 1.
However, when I call on 5 or 6 the node only rotates upwards once, then the optimization ends.
Here is my code that optimizes the tree, it is called on the root of the tree after a node is found using the find method:
private void RecOptimize(TreeNode<T> currentNode, TreeNode<T> prevNode)
{
if (currentNode == null) return;
RecOptimize(currentNode.left, currentNode);
RecOptimize(currentNode.right, currentNode);
var left = currentNode.left;
var right = currentNode.right;
var oldNode = currentNode;
if(right != null)
{
if (right.iAccessCount > currentNode.iAccessCount)
{
currentNode = MakeRightRoot(currentNode);
}
}
if(left != null)
{
if (left.iAccessCount > currentNode.iAccessCount)
{
currentNode = MakeLeftRoot(currentNode);
}
}
if(prevNode != null)
{
if(prevNode.left != null && prevNode.left.Equals(oldNode))
{
prevNode.left = currentNode;
}
if(prevNode.right != null && prevNode.right.Equals(oldNode))
{
prevNode.right = currentNode;
}
}
else
{
this.rootNode = currentNode;
}
}
Edit Thought I would clear something up.
MakeLeftRoot(node) and MakeRightRoot(node) simply perform a rotation about the node, MakeLeftRoot rotates the nodes left node and returns it. While MakeRightRoot does the same but to the right side.
Edit 2 Heres the code for the rotation methods
protected TreeNode<T> MakeRightRoot(TreeNode<T> oldRoot)
{
var newRoot = oldRoot.right;
oldRoot.right = newRoot.left;
newRoot.left = oldRoot;
return newRoot;
}
protected TreeNode<T> MakeLeftRoot(TreeNode<T> oldRoot)
{
var newRoot = oldRoot.left;
oldRoot.left = newRoot.right;
newRoot.right = oldRoot;
return newRoot;
}
I will assume all your other code is correct, including the functions that perform rotations. Then there is still this issue in RecOptimize:
If currentNode changes by the call of MakeRightRoot(currentNode), then the left and right variables no longer represent the left and right nodes of currentNode and so the next if block will not be doing what is intended, as at that moment left is a grandchild of currentNode.
I understand why you also pass prevNode to this function, and how the if(prevNode != null) block is dealing with setting the link with prevNode and the potentially changed reference for currentNode, but it will be much easier if you use the same coding pattern as for your rotation functions: let the function return the potentially changed reference for currentNode, and let the caller be responsible for attaching that node back to its parent node.
Also, you should determine whether it is possible that a higher access count occurs in both subtrees of a node or not. If it cannot occur (because after each access you call this function), then you may want to use an if ...else if construct for the two first blocks. If however it could occur, then think how you want the tree to get rotated. I believe it is better to then to deal with each subtree separately: first finish the work for one subtree completely (recursive call + potential rotation), and then only deal with the other subtree (recursive call + potential rotation).
Here is the suggested code:
private TreeNode<T> RecOptimize(TreeNode<T> node) {
if (node == null) return null;
if (node.right != null) {
node = RecOptimize(node.right);
if (node.right.iAccessCount > node.iAccessCount) {
node = MakeRightRoot(node);
}
}
if (node.left != null) {
node = RecOptimize(node.left);
if (node.left.iAccessCount > node.iAccessCount) {
node = MakeLeftRoot(node);
}
}
return node;
}
Main call:
this.rootNode = RecOptimize(this.rootNode);
One more comment: RecOptimize is going to visit every node in the tree. This is not optimal. If you access a node through binary search, then you should only have to check for rotations along the path to that found node, as a "bubble up" phase.

Best way to compare variables between instances of the same class?

This is a game based on certain elemental combos, I'm trying to find a way to have two elements compare to each other in order to reduce hard coding. I do have a working solution provided at the end, but I'm trying to learn any ways that would be more simple, straight forward, and readable.
//Wizard is a class
Wizard player1;
Wizard player2;
player1.health = 3;
player2.health = 3;
//Elements is an enum that allows fire/water/air to be selected
player1.elementSelected = Elements.fire;
player2.elementSelected = Elements.water;
Wizard[] bothPlayers = { player1, player2 };
I want to search for the active element in bothPlayers so I can effect the Player's health. I know this doesn't work, but I was wondering if I could do something like:
if (bothPlayers.Contains(Wizard.elementSelected.Elements.fire) && bothPlayers.Contains(Wizard.elementSelected.Elements.water))
Alternatively I was thinking of just setting it to a new array but that wont let me call back to effect player health unless I set them up to a new variable like:
Elements[] bothSelectedElements = { player1.elementSelected, player2.elementSelected };
if (bothSelectedElements.Contains(Elements.fire) && bothSelectedElements.Contains(Elements.water))
{
Wizard playerWithFire; // = player who selected fire. Can't set without hardcoding
Wizard playerWithWater; // = player who selected water. Can't set without hardcoding
playerWithFire.health--;
playerWithWater.waterStrength++;
}
//CURRENT WORKING SOLUTION
//set each Wizard container to null at the start of each check
Wizard fire = null;
Wizard water = null;
Wizard air = null;
//add check to make sure same elements aren't selected. Then assign the players to the containers
foreach (Wizard player in bothPlayers)
{
if (player.elementSelected == Elements.fire)
{
fire = player;
}
if (player.elementSelected == Elements.water)
{
water = player;
}
if (player.elementSelected == Elements.air)
{
air = player;
}
}
//then do the actual check
if (fire != null && water != null)
{
fire.health--;
water.waterStrength++;
}
//repeat with other if statement comparisons
This is a fiddly problem, but thankfully you are working with a language that has all the tools you need to hide the complexity away.
var elementWizards = wizards.GroupBy(w => w.elementSelected).ToDictionary(g => g.Key);
var elements = elementWizards.Keys.ToHashSet(); // gives us access to SetEquals
if (elements.SetEquals(new[] { Elements.Fire, Elements.Water }))
{
foreach (var wizard in elementWizards[Elements.Fire]) wizard.health--;
foreach (var wizard in elementWizards[Elements.Water]) wizard.waterStrength++;
}
else if (elements.SetEquals(new[] { Elements.Earth, Elements.Fire }))
{
// more effects...
}
Note that SetEquals doesn't care about the order of items, so you don't need to worry about handling water/fire instead of fire/water.
Footnote: in the real world I would define some static HashSet<Element> objects and call if(foo.SetEquals(elementWizards.Keys)), but I kept things simple for this answer.
Solution with Linq:
Check if there are water and fire wizard (with Linq)
Foreach to search and add health etc...
if (bothPlayers.Count(wizard => wizard.elementSelected == Elements.fire) > 0 &&
bothPlayers.Count(wizard => wizard.elementSelected == Elements.water) > 0)
{
bothPlayers.ForEach(wizard =>
{
var _ = wizard.elementSelected == Elements.water ? wizard.waterStrength++ :
wizard.elementSelected == Elements.fire ? wizard.health-- : 1;
});
}
you can change the wizards you are searching for and the properties that depends on it
There are multiple ways of finding item in a collection. For example :
// using Array.Find method
Wizard fire = Array.Find(bothPlayers, player => player.elementSelected == Elements.fire);
// or using System.Linq FirstOrDefault extension
Wizard water = bothPlayers.FirstOrDefault(player => player.elementSelected == Elements.water);
if (fire != null && water != null)
{
fire.health--;
water.waterStrength++;
}
For more than two players, a Lookup can be used to separate the players into groups :
var lookup = bothPlayers.ToLookup(player => player.elementSelected);
if (lookup.Contains(Elements.fire) && lookup.Contains(Elements.water))
{
foreach (Wizard fire in lookup[Elements.fire] ) fire.health--;
foreach (Wizard water in lookup[Elements.water]) water.waterStrength++;
}

is there a way to set a variable to something at the end of foreach loop if condition not met in C#?

foreach (Objecta a in aList())
{
foreach (Objectb b in bList)
{
if (a.variable != b.variable1 && a.variable() != b.variable2)
{
a.setVariable("Error");
}
}
}
The problem I am getting is that it goes through the foreach loop the first time and it sets variable to error without checking if other values (when it goes through the loop again) finds a match.
What I would like is to wait until it goes through all the lists and at the last foreach loop iteration if nothing in aList matches the variable target && variable source in bList then finally set it to Error flag.
Any suggestions to get around this will be appreciated.
Try doing it the other way around. Search for a match instead of searching for non-matches.
foreach (Objecta a in aList())
{
bool foundMatch = false;
foreach (Objectb b in bList)
{
if (a.variable == b.variable1 || a.variable() == b.variable2)
{
foundMatch = true;
break;
}
}
if (!foundMatch)
{
a.setVariable("Error");
}
}
I think this is what you are looking for. So if StoreList is the outer loop and LinkList is the inner loop. You want to search all the links to see if there's an ID that matches the store ID. If you find a match, stop searching the links. After the search through the links, set an error on the store if there was no match, then go to the next store.
foreach (Objecta a in aList())
{
var foundMatch = false;
foreach (Objectb b in bList)
{
if (a.variable == b.variable1 || a.variable() == b.variable2)
{
fondMatch = true;
break;
}
}
if (!foundMatch) a.setVariable("Error");
}
I think you want something like this:
First select all the item values from aList and bList and put them in a seperate array:
var aVals = aList.Select(x=>x.value1).ToArray();
var bListVals1 = bItems.Select(x=>x.value1).ToArray();
var bListVals2 = bItems.Select(x=>x.value2).ToArray();
var bVals = bListVals1.Concat(bListVals2);
Then, get the values both lists have in common:
var correctVals = bVals.Intersect(aVals);
These are the correct values and so all the other values are wrong:
var wrongVals = aVals.Except(correctVals);
Now you have the values that are wrong and can act accordingly:
wrongAItems = aList.Where(a => wrongVals.Contains(a.value));
foreach(wrongA in wrongAItems){
wrongA.setVariable("Error");
}
foreach (Store s in processFlowStores.getStoresList())
{
if (!processFlowLinks.Any(l => s.getNodeId() == l.getLinkSource() ||
s.getNodeId() == l.getLinkTarget()))
{
s.setID("Error: FailedOperation Error - 123.123.121");
}
}
EDIT: more compact solution using Linq. Basically, if none of the links has it as either source or target, mark it as error.

Can a var = 2 things

I've been working with Unity for a while now and been scripting with C# a bit.
I'm working on a script that checks if selected target is a NPC or a Craft(station).
As the base of the script is calling upon many other scripts and variables, I don't want to duplicate all of the scripts in order to make the other kind of NPC which is , in my case, the Craft(station).
Let me explain in detail with examples now:
void UpdateNpcTrading(Player player) {
// only if visible
if (!npcTradingPanel.activeSelf) return;
// npc trading
if (player.target != null && player.target is Craft &&
Vector3.Distance(player.transform.position, player.target.transform.position) <= player.talkRange) {
var npc = (Craft)player.target; // Here is the var I need to edit
// items for sale
for (int i = 0; i < npcTradingContent.childCount; ++i) {
var entry = npcTradingContent.GetChild(i).GetChild(0);
// get the item
if (i < npc.saleItems.Length) {
var item = npc.saleItems[i];
There is more to it ( feel free to ask for it if needed )
So the line : var npc = (Craft)player.target; identifies npc as Craft because player.Target = Craft.
But I'd like to edit it so It checks if the player.target = Craft then, Set var npc = Craft
but if player.target = Npc , Set var npc = Npc instead.
Not sure if I make myself clear or not. Please feel free to ask for more details and I'll do my best.
Any help is most appreciated, Mike
EDIT :
Ok so I've scripted it like this. Still when I run in game it doesnt looks like it knows what to do.. ( But compilers doesnt show syntax error )
// npc trading
if (player.target != null && player.target is Craft &&
Vector3.Distance(player.transform.position, player.target.transform.position) <= player.talkRange) {
var npc = (Craft)player.target;
}else if (player.target != null && player.target is Npc &&
Vector3.Distance(player.transform.position, player.target.transform.position) <= player.talkRange) {
var npc = (Npc)player.target;
// items for sale
for (int i = 0; i < npcTradingContent.childCount; ++i) {
var entry = npcTradingContent.GetChild(i).GetChild(0);
// get the item
if (i < npc.saleItems.Length) {
var item = npc.saleItems[i];
Where am I wrong
It looks like you are looking for the is operator.
if (player.target is Craft)
{
var npc = (Craft)player.target;
// do other 'Craft' logic here
}
else if (player.target is Npc)
{
var npc = (Npc)player.target;
// do other 'Npc' logic here
}
I'm assuming that player.target can either be an Npc or a Craft? If this is the case then they both inherit from whatever base type player.target is. You can check if it is either a Craft or Npc with the is operator before you cast.

Categories

Resources