I am not very experienced with C#. I am trying to build circular lists and I have done it like this:
public List<string> items = new List<string> {"one", "two", "three"};
private int index = 0;
void nextItem() {
if (index < items.Count - 1)
index += 1;
else
index = 0;
setItem();
}
void previousItem() {
if (index > 0)
index -= 1;
else
index = items.Count - 1;
setItem();
}
void Update() {
if (Input.GetKeyDown(KeyCode.RightArrow)) nextItem();
else if (Input.GetKeyDown(KeyCode.LeftArrow)) previousItem();
}
But now I am wondering: Am I reinventing the wheel? Does C# already come with a proper data structure for this?
EDIT: In case some context is needed. I have a game menu, where I display a collection of items, and I want that when I press "next" and Im at the last one, to show the first item again.
With utilization of the % (remainder) operator your code gets quite simple:
void nextItem() {
index++; // increment index
index %= items.Count; // clip index (turns to 0 if index == items.Count)
// as a one-liner:
/* index = (index + 1) % items.Count; */
setItem();
}
void previousItem() {
index--; // decrement index
if(index < 0) {
index = items.Count - 1; // clip index (sadly, % cannot be used here, because it is NOT a modulus operator)
}
// or above code as a one-liner:
/* index = (items.Count+index-1)%items.Count; */ // (credits to Matthew Watson)
setItem();
}
You cann also write your own circular list
public class CircularList<T> : List<T>, IEnumerable<T>
{
public new IEnumerator<T> GetEnumerator()
{
return new CircularEnumerator<T>(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new CircularEnumerator<T>(this);
}
}
class CircularEnumerator<T> : IEnumerator<T>
{
private readonly List<T> list;
int i = 0;
public CircularEnumerator(List<T> list){
this.list = list;
}
public T Current => list[i];
object IEnumerator.Current => this;
public void Dispose()
{
}
public bool MoveNext()
{
i = (i + 1) % list.Count;
return true;
}
public void Reset()
{
i = 0;
}
}
public class CircularList<T> : List<T>
{
private int Index;
public CircularList() : this(0) { }
public CircularList(int index)
{
if (index < 0 || index >= Count)
throw new Exception(string.Format("Index must between {0} and {1}", 0, Count));
Index = index;
}
public T Current()
{
return this[Index];
}
public T Next()
{
Index++;
Index %= Count;
return this[Index];
}
public T Previous()
{
Index--;
if (Index < 0)
Index = Count - 1;
return this[Index];
}
public void Reset()
{
Index = 0;
}
public void MoveToEnd()
{
Index = Count - 1;
}
}
Related
I am looking to randomly generate 3 items using a loot table. The loot table currently works as expected, it will randomly generate an item based on the item's rarity. However, I don't want there to be duplicates of the same item. How can I set this up? This script is currently attached to 3 separate GameObject item pedestals.
using System.Collections.Generic;
using UnityEngine;
public class ShopItemSpawner : MonoBehaviour
{
[System.Serializable]
public class DropItem
{
public string name;
public GameObject item;
public int dropRarity;
}
public List<DropItem> ShopItemPool = new List<DropItem>();
private void Start()
{
int itemWeight = 0;
for (int i = 0; i < ShopItemPool.Count; i++)
{
itemWeight += ShopItemPool[i].dropRarity;
}
int randomValue = Random.Range(0, itemWeight);
for (int j = 0; j < ShopItemPool.Count; j++)
{
if (randomValue <= ShopItemPool[j].dropRarity)
{
Instantiate(ShopItemPool[j].item, transform.position, Quaternion.identity);
return;
}
randomValue -= ShopItemPool[j].dropRarity;
}
}
}
You could clone the ShopItemPool list and with each item rolled you remove that item from the list. You then need to go all over again by recalculating the total weight.
I find it useful to have a general purpose class for randomizing items with weights, it allows you do this this:
var randomizer = new WeightRandomizer<DropItem>();
foreach (var shopItem in ShopItemPool) {
randomizer.Add(shopItem, shopItem.dropRarity);
}
randomizer.Roll(); // Get a random element based on weight.
randomizer.Take(); // Get a random element based on weight and remove it from the randomizer.
General Purpose Weight Randomizer:
public class WeightRandomizer<T> {
[Serializable]
public class WeightedElement<T> {
public T value;
public int weight;
public WeightedElement (T value, int weight) {
this.value = value;
this.weight = weight;
}
}
private readonly List<WeightedElement<T>> elements = new();
public void Add (T value, int weight) => elements.Add(new WeightedElement<T>(value, weight));
public void AddRange (IEnumerable<WeightedElement<T>> weightedElements) => elements.AddRange(weightedElements);
public int TotalWeight() => elements.Sum(x => x.weight);
public T Roll() => Pick(false);
public T Take() => Pick(true);
private T Pick (bool remove) {
if (elements.Count == 0) {
Debug.LogWarning($"{nameof(WeightRandomizer<T>)} is missing elements.");
return default(T);
}
var roll = Random.Range(0, TotalWeight());
var selectedIndex = elements.Count - 1;
var selected = elements[selectedIndex].value;
for (var i = 0; i < elements.Count; i++) {
var element = elements[i];
// Found an element with a low enough value.
if (roll < element.weight) {
selected = element.value;
selectedIndex = i;
break;
}
// Keep searching for an element with a lower value.
roll -= element.weight;
}
// Sometimes we want to take and remove the element from the pool.
if (remove) elements.RemoveAt(selectedIndex);
return selected;
}
}
I'm working on a radix sort algorithm with linked list. I have in mind how I'm going to solve it, but I need to be able to delete the whole linked list (deleting node one by one when looping through linked list). The problem which I encountered is my current (node) pointer doesn't get the value first. I have tried many different things, but I guess I don't grasp how to reset the pointers 'first', 'current', 'left', 'right' when deleting a node
Radixsort.cs:
public static void Counting_sort(LinkedList<int> list, int exp)
{
LinkedList<int> D = new LinkedList<int>();
LinkedList<int>[] lists = new LinkedList<int>[10];
for (int i = 0; i < lists.Length; i++)
{
lists[i] = new LinkedList<int>();
}
// Skaiciu daznumas
int number = 0;
for (list.Beginning(); list.Exists(); list.Next())
{
number = (list.Take() / exp % 10);
lists[number].Add(list.Take());
list.Remove(list.TakeNode()); <---------- Removing node one by one till it's empty
}
for (int i = 0; i < lists.Length; i++)
{
for (list.Beginning(); list.Exists(); list.Next())
{
//list.Add(lists)
}
}
}
public static void Radix_sort(LinkedList<NumberPlate> list)
{
LinkedList<int> intList = new LinkedList<int>();
AddIntToLinkedList(list, intList);
for (int exp = 1; exp < Math.Pow(10, 9); exp *= 10)
{
Counting_sort(intList, exp);
}
}
LinkedList.cs
public class LinkedList<T> : IEnumerable<T> where T : IComparable<T>, IEquatable<T>
{
public sealed class Node<T>
{
public Node<T> Right;
public Node<T> Left;
public T data;
public Node(T value, Node<T> left, Node<T> right)
{
Left = left;
Right = right;
data = value;
}
}
private Node<T> first;
private Node<T> last;
private Node<T> current;
public int size;
public LinkedList()
{
first = null;
last = null;
current = null;
size = 0;
}
public void Add(T element)
{
var node = new Node<T>(element, last, null);
if (first != null)
{
last.Right = node;
last = node;
}
else
{
first = node;
last = node;
}
current = node;
size++;
}
public void Remove(Node<T> node)
{
if (node == first)
{
first = first.Right;
}
else if (node == last)
{
last = last.Left;
}
else if (node.Left != null)
{
node.Left.Right = node.Right;
}
else if (node.Right != null)
{
node.Right.Left = node.Left;
}
size--;
}
public void Beginning()
{
current = first;
}
public void End()
{
current = last;
}
public void Next()
{
current = current.Right;
}
public void Previous()
{
current = current.Left;
}
public bool Exists()
{
return current != null;
}
public T Take()
{
return current.data;
}
public Node<T> TakeNode()
{
return current;
}
I think you should be able to do this to clear a list:
foreach (var node in list)
{
list.Remove(node);
}
The Remove method is already implemented for you on the LinkedList class, so you should make use of it.
Because the LinkedList implements IEnumerable, you can iterate over it using foreach.
I want to be able to count multiple entries in an already sorted array. In previous attempts, I've tried to use a list like this.
public static List<int> duplicate = new List<int>();
However, it wouldn't count properly and kept printing the same index number. I'm pretty much stumped on what to do at this point any help would be appreciated.
class binarysearch
{
public static int lowestIndex = 0;
public static int highestIndex;
public static int middleIndex = 0;
public static int indexValue = -1;
public static int binarySearch(double[] data, double target)
{
int highestIndex = data.Length - 1;
while (lowestIndex < highestIndex)
{
middleIndex = lowestIndex + (highestIndex - lowestIndex) / 2;
if (data[middleIndex] == target)
{
indexValue = middleIndex;
highestIndex = middleIndex;
}
else if (data[middleIndex] > target)
{
highestIndex = middleIndex;
}
else
{
lowestIndex = middleIndex + 1;
}
}
return indexValue;
}
}
edit
public static List<int> duplicate = new List<int>();
is declared earlier in the code in
class dataset
{
public static List<int> duplicate = new List<int>();
}
then printed later in the main method
foreach (object dupes in dataset.duplicate)
{
Console.WriteLine(dupes);
}
I'm trying to do a Radix sort in a Linked list class. I found radix sort algorithm for array and am trying to change it to work with my linked list. However, I'm a bit struggling. The code I'm trying to change is taken from http://www.w3resource.com/csharp-exercises/searching-and-sorting-algorithm/searching-and-sorting-algorithm-exercise-10.php I tested the code with an array and it worked. Does anybody have any ideas how to make radix sort work in a linked list?
//abstract class
abstract class DataList
{
protected int length;
public int Length { get { return length; } }
public abstract double Head();
public abstract double Next();
public abstract void Swap(int a, int b);
public void Print(int n)
{
Console.Write("{0} ", Head());
for (int i = 1; i < n; i++)
Console.Write("{0} ", Next());
Console.WriteLine();
}
}
//linked list class
class LinkedList : DataList
{
class MyLinkedListNode
{
public MyLinkedListNode nextNode { get; set; }
public int data { get; set; }
public MyLinkedListNode(int data)
{
this.data = data;
}
public MyLinkedListNode()
{
this.data = 0;
}
}
MyLinkedListNode headNode;
MyLinkedListNode prevNode;
MyLinkedListNode currentNode;
public LinkedList(int n, int min, int max)
{
length = n;
Random rand = new Random();
headNode = new MyLinkedListNode(rand.Next(min, max));
currentNode = headNode;
for (int i = 1; i < length; i++)
{
prevNode = currentNode;
currentNode.nextNode = new MyLinkedListNode(rand.Next(min, max));
currentNode = currentNode.nextNode;
}
currentNode.nextNode = null;
}
public LinkedList()
{
headNode = new MyLinkedListNode();
currentNode = headNode;
}
public override double Head()
{
currentNode = headNode;
prevNode = null;
return currentNode.data;
}
public override double Next()
{
prevNode = currentNode;
currentNode = currentNode.nextNode;
return currentNode.data;
}
public override void Swap(int a, int b)
{
prevNode.data = a;
currentNode.data = b;
}
//my radix sort
public void radixSort()
{
int j = 0;
LinkedList tmp = new LinkedList();
for (int shift = 31; shift > -1; --shift)
{
//I try to go trough old list
MyLinkedListNode current = headNode;
while (current != null)
{
bool move = (current.data << shift) >= 0;
//I found this expression somewhere and I'm trying to use it to form a new Linked list (tmp)
if (shift == 0 ? !move : move)
;
else
{
if (tmp.headNode == null)
tmp.headNode = currentNode;
else
{
tmp.currentNode.nextNode = current;
//infinite loop happens on the commented line
//tmp.currentNode = tmp.currentNode.nextNode;
j++;
}
current = current.nextNode;
}
}
}
}
Following the C# radix sort example, you need an array of ten lists. Move nodes from the original list into the ten lists, appending a node with least signfificant digit == '0' into array_of_lists[0], '1' into array_of_list[1], and so on. After the original list is emptied, then concatenate the array of lists back into the original list and repeat for the next to least significant digit. Repeat the process until all the digits are handled.
You could use a larger base, such as base 16, where you would use an array of 16 lists. You can then select each "digit" using a shift and an and .
I have a generic type called Vector<T>, I created it as so, cause the T might be float or Complex :
public class Vector<T>
{
#region Properties
public ulong Length
{
get
{
return _length;
}
}
public VectorType VectorType
{
get
{
return _vectorType;
}
}
#endregion
#region Indexers
public T this[ulong index]
{
get
{
return _data[index];
}
set
{
_data[index] = value;
}
}
#endregion
#region Constructors
public Vector(VectorType vectorType, T[] data)
{
if (!((data is float[]) || (data is Complex[])))
{
throw new InvalidDataException("Data must be array of float or array of Complex");
}
_data = new T[_length = (ulong)data.Length];
for (ulong i = 0; i < _length; i++)
{
_data[i] = data[i];
}
_vectorType = vectorType;
}
public Vector(VectorType vectorType, Vector<T> vector)
{
_data = new T[_length = vector.Length];
for (ulong i = 0; i < _length; i++)
{
_data[i] = vector[i];
}
_vectorType = vectorType;
}
#endregion
#region Methods
//Unity Matrix, this vector has 1/N everywhere
public static Vector<float> e(VectorType vectorType, ulong length)
{
var data = new float[length];
for (ulong i = 0; i < length; i++)
{
data[i] = (float)1 / length;
}
var vectorE = new Vector<float>(vectorType, data);
return vectorE;
}
public float Sum()
{
float sum = 0;
if (_data is float[])
{
sum = (_data as float[]).Sum();
}
else
{
if (_data is Complex[])
{
for (ulong i = 0; i < _length; i++)
{
sum += (float)
Math.Sqrt(Math.Pow((_data[i] as Complex?).Value.Real, 2) +
Math.Pow((_data[i] as Complex?).Value.Imaginary, 2));
}
}
}
return sum;
}
public bool CheckIfSochasitc()
{
return Math.Abs(Sum() - 1) < float.Epsilon;
}
public void Normalize()
{
var sum = Sum();
if (_data is float[])
{
for (ulong i = 0; i < _length; i++)
{
float x = ((float) _data[i])/sum;
_data[i] = (T)x;
}
}
}
#endregion
#region Operators
//I omitted the code inhere to avoid overload
#endregion
#region Fields
private ulong _length;
private readonly VectorType _vectorType;
private T[] _data;
#endregion
}
public enum VectorType
{
Row,Column
}
My problem is that I have a generic array (if I can call it so) :
private T[] _data;
And I have the Normalize() method:
public void Normalize()
{
var sum = Sum();
if (_data is float[])
{
for (ulong i = 0; i < _length; i++)
{
//Here is the problem
_data[i] = ((_data[i] as float?) / sum);
}
}
}
This doesn't work saying can't cast float to T tried to search but couldn't find helpful aide, any clarification I'd be thankful.
Update :
The Sum() method always returns a float
It's not clear why you're converting to float? at all (or why you're using ulong as the index variable type...) but you just need to cast the result back to T - otherwise you can't assign it back into an array of type T[]. Additionally, you need to cast to object (in order to convert back to T:
float x = ((float) (object) data[i]) / sum;
data[i] = (T) (object) x;
You can use float? for the first line, with as, to avoid boxing - but then you need to get the non-nullable value:
float x = (data[i] as float?).Value / sum;
Both are pretty ugly :(
As noted in comments though, this sort of thing is usually an indication of the design not really being properly generic at all. We don't know what type Sum() returns, but you should consider just how "general" your type is to start with.
May be you can try this
if (typeof(_data) == float[])
{
for (ulong i = 0; i < _length; i++)
{
_data[i] = ((_data[i] as float?) / sum);
}
}