Keeping track of selected numbers in Bingo - c#

I have to create a class that keeps tracks of numbers that were already selected in the process of generating a board and selecting numbers during the game.
The 3 classes I need:
1) A "reset" class that initalizses the class once the card is filled with numbers, so that the array can be used again to keep track of the numbers selected during the game
2) A "isNumberUsed" class that is given the random integer and checks whether the random number is in the numbersList array or not
3) A "setUsedNumber" class that adds the unique random number generated to the numbersList array.
I have been working on this and would love any feedback. Thank you!
public class SelectedNumbersListType
{
private int[] numbersList;
private int position;
public SelectedNumbersListType()
{
numbersList = new int[76];
position = 0;
}
public void reset()
{
// position = numbersList[0];
// position++;
for (position = 0; position < numbersList.Length; position++)
{
numbersList[position] = 0;
if (position == numbersList.Length)
{
position = numbersList[0];
}
}
// reset();
}
public bool isNumberUsed(int rn)
{
position = numbersList[0];
position++;
if (numbersList[position] == rn)
{
return true;
}
return false;
}
public bool setUsedNumber(int rn)
{
if (numbersList.Contains(rn))
{
Array.Resize(ref numbersList, numbersList.Length+1);
numbersList[numbersList.Length + 1] = rn;
}
return false;
}
}
}

I think it makes sense to step back here and start from the beginning. As #ThomasWeller points out, object-oriented analysis starts with identifying the nouns in your problem space, and then the verbs that act upon those nouns.
When I look at your bingo problem, I only see one noun, the BingoGame. So, I'm going to have a class BingoGame. From the caller's point of view, there are two main actions (verbs) that act upon that noun: DrawNumber and RestartGame. In addition, I think it's useful to be able to get the list of numbers drawn in the game, both in draw order and in sorted order. As a result, I see two more verbs GetDrawnNumbers and GetDrawnNumbersSorted.
In C-family languages, it always makes more sense to start counting at 0 rather than at 1. So, I'm going to work with numbers in the range 0-74, not 1-75. Whenever I display a number, I'll prepend it with the appropriate letter (one of B, I, N, G, or O) and add one. So, I created a private function to do this: GetBingoNumberFromNumber.
In order to draw a number, I need a source of random-ness. The framework provides one (System.Random) - it's not the best random number generator (don't use it to start a casino), but it's good enough here.
I also need a way to retain what numbers have been drawn (both for display, and to reject any duplicate numbers in a game). I'll keep track of these in a List<int>. Finally, I need to know what the letters are in "BINGO", so I'll keept an array of those letters (I could have treated the string "BINGO" as an array of characters, but I thought an array of characters was more obvious. As a result, my class starts with:
public class BingoGame
{
private readonly List<int> _drawnNumbers = new List<int>();
private readonly Random _random = new Random();
//the indexes for this array are (naturally) 0 through 4
private static readonly char[] BingoChars = {'B', 'I', 'N', 'G', 'O'};
}
Now I need to add functionality. Starting the game simply means taking previously drawn numbers and putting them back in the drum. In our case, it means clearing the list of previously drawn numbers:
public void StartGame()
{
_drawnNumbers.Clear();
}
To draw a number, we'll rely on Random.Next, pulling numbers between 0 and 74. However, we need to take care of the case where a duplicate number is pulled during single game. If that happens, we'll throw away the newly drawn number and get another one. We'll look in the list of previously drawn numbers to decide if a number has been previously drawn:
public string DrawNumber()
{
int nextNum;
do
{
nextNum = _random.Next(0, 75); //this will return a number between 0 & 74
} while (_drawnNumbers.Contains(nextNum));
_drawnNumbers.Add(nextNum);
return GetBingoNumberFromNumber(nextNum);
}
Here's the first time we've had to translate one of our 0-74 numbers to something that makes sense in Bingo. Let's do that function:
private string GetBingoNumberFromNumber(int num)
{
//this will result in a number in the 0-4 range
var bingoIndex = num / 15;
return $"{BingoChars[bingoIndex]}-{num + 1}";
}
It's important to understand integer division when reading that function. Dividing any integer between 0 and 14 by the integer 15 will yield zero. Similarly, any number between 15 and 29 when divided by 15 will yield 1. And so on.
Finally, we need to get the "numbers" for the current game. We'll just return the list of drawn numbers, transformed using GetBingoNumberFromNumber. The easiest way to transform a collection is to use LINQ, so:
public IEnumerable<string> GetDrawnNumbers()
{
return from num in _drawnNumbers
select GetBingoNumberFromNumber(num);
}
And, LINQ is good at sorting, so we'll just return the sorted list in nearly the same way:
public IEnumerable<string> GetDrawnNumbersSorted()
{
return from num in _drawnNumbers
orderby num
select GetBingoNumberFromNumber(num);
}
To test this, I threw together a simple WinForms app with two buttons (StartGameBtn and DrawNumberBtn) and two skinny listboxes (NumbersList and SortedList). After wiring up the Click handlers, it looked like:
public partial class Form1 : Form
{
private readonly BingoGame _game = new BingoGame();
public Form1()
{
InitializeComponent();
_game.StartGame();
}
private void StartGameBtn_Click(object sender, EventArgs e)
{
_game.StartGame();
NumbersList.Items.Clear();
SortedList.Items.Clear();
}
private void DrawNumberBtn_Click(object sender, EventArgs e)
{
_game.DrawNumber();
SetListboxItems(NumbersList, _game.GetDrawnNumbers());
SetListboxItems(SortedList, _game.GetDrawnNumbersSorted());
}
private void SetListboxItems(ListBox listbox, IEnumerable<string> numbers)
{
listbox.Items.Clear();
foreach (var item in numbers)
{
listbox.Items.Add(item);
}
}
}

Related

Constantly generating unique and random values in a specific range in C#

I am implementing this code in my discord bot, where I want to be able to generate unique numbers from 1 to 10 (as suggested above) whenever I input a single command.
Turns out that the values sometimes are repeated. Therefore I was suggested to add an array (used[ ]) and a loop in order to check every time if the value has been generated already.
Random random = new Random();
int[] used = new int[10];
int rng = 0;
while (!used.Contains(rng))
{
rng = random.Next(1, 10);
}
/*
I wish to store the generated value "rng" to the "used" array.
e.g.
used[0] = rng
used[1] = rng
used[2] = rng
etc.
*/
Console.WriteLine("The number generated is " + Convert.ToString(rng));
However, I don't know how to constantly add values to an array in an arranged order. (as seen by the commentations above)
In a more simple way, For 10 times I want the system to generate a number, and those numbers are randomly picked out of [1,10] and only once. If all the numbers have been generated once, all are free to be generated again.
Sorry for my bad interpretation. I have refined it with the comments that everyone has contributed.
Here is a solution, that shuffles the values and returns each value until the list is exhausted, then starts all over:
int[] GenerateNewArray(int n)
{
// Define an array of values we wish to return and populate it
int[] baseValues = Enumerable.Range(1, n).ToArray();
Random rnd=new Random();
// Shuffle the array randomly using Linq
return baseValues.OrderBy(x => rnd.Next()).ToArray();
}
void Main()
{
int nNumbers = 10;
while (true)
{
// Generate a new randomized array
var values = GenerateNewArray(nNumbers);
// Print each value
for (int i=0;i<nNumbers;i++)
{
Console.WriteLine($"The number generated is {values[i]}");
}
}
}
EDIT
Parameterized the number of unique values.
Rather than storing in an array, looking for duplicates etc., just use a HashSet
This only accepts unique values, and if you try to add a duplicate it will be simply ignored.
Or you can just do a check to see if it exists
HashSet<int> integerSet = new HashSet<int>();
if (hashSet.Contains(rng ))
// element already exists in set
else
//Doesn't exist
Of course, if you just keep generating each number randomly until you have generated everything in the range, then you might as well just simply generate an array/list of elements with every number and save yourself processing time!
Using a list is a good way to start, but before continuing the comment go check out the list documentation
In small talk a list is an IEnumerable of a type you choose, in your case to declare and instanciate a list you should write like this List<int> listName = new List<int>(); and the main difference between an array and a list is that you can populate the list whenever you want without the length declaration, to do that you can add listName.Add(2); (2 : the integer value that you want to add) or remove values listName.Remove(0) (0 : the position of the list that need to be removed, ex: 0 is the first value inside the list because position 0 is the start)
Random random = new Random();
List<int> listName = new List<int>();
int rng = 0, counterOfValues = 0;
while (counterOfValues < 10)
{
rng = random.Next(1, 10);
If(!listName.Contains(rng))
{
listName.Add(rng);
Console.WriteLine("The number generated is " + listName.Last().ToString());
If(counterOfValues < 10)
{
counterOfValues++;
}
else
{
counterOfValues = 0;
}
}
}
This should probably do the work ("should" because it's some code I wrote out of my head, so sorry about caps ecc)
Let me know if it works as intended or we need to correct something!
P.S: If you want the user to select the range of the randomic values just change the value of counterOfValues in the lowest value and the 10's with the higher value
Using a HashSet is better to know the numbers that you are use. But in your case, I think it's better use a List and remove elements:
var list = new List<int>();
for (int i = 1; i <= 10; i++)
list.Add(i);
// List is 0-index
// list = 1 2 3 4 5 6 7 8 9 10
int rng = random.Next(0, list.Count - 1);
// Suppose rgn=3: You get 3 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 4 5 6 7 8 9 10
// Now you get a value between 0...8 (list.Count is 9)
rng = random.Next(0, list.Count - 1);
// Suppose rgn=3: You get 4 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 5 6 7 8 9 10
// Now you get a value between 0...7 (list.Count is 8)
rng = random.Next(0, list.Count - 1);
// Suppose rgn=5: You get 7 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 5 6 8 9 10
...
When list has one element you don't need use random.
When your list is empty, fill again and repeat the whole process.
In this form you always get an element with each random call. Using HashSet you may try a lot of random calls to get a nonused number.
A class for that maybe:
public class RandomClass
{
private readonly List<int> _list;
private readonly Random _random;
public RandomClass()
{
this._list = new List<int>();
this._random = new Random();
}
private void PopulateList()
{
for (int i = 1; i <= 10; i++)
this._list.Add(i);
}
public int GetNumber()
{
if (this._list.Count == 0)
{
this.PopulateList();
}
if (this._list.Count > 1)
{
int rng = this._random.Next(0, this._list.Count);
var value = this._list[rgn];
this._list.RemoveAt(rgn);
return value;
}
else
{
var value = this._list[0];
this._list.RemoveAt(0);
return value;
}
}
}
UPDATE: Some about List, Arrays and other collections
Array and List are very similar. The main difference is that an array is a continuous block of memory while List (in memory) it's not a block. Array is faster to iterate (address of first element + multiply for sizeof each element) but slower when you want to add/remove elements (you must create other array and copy the elements). From the point of view of an user, both of them are used to store a list of items and iterate them.
Other collections very interesting are HashSet and Dictionary. These collections are sets, you can't access their elements using an index. You saw an example for HashSet in this page: Add a number to the set and query it if you want know if that number appear before. The key with this sets are that you access to their elements using a hash. Instead of an index, you use whatever you want as a key and an algorithm get quickly the position of the element in the set. Dictionary is like a HashSet but you can store addicional data asociated to the key.
In many cases, you'll use both of them: a list to iterate sequencially and a dictionary to direct access to elements. For example, you have a lot of Persons that you show in some order (name, surname...). You store them in a List an allow user to change the order (using Sort of the list). You use a list because you show and use that order many times: you need ordered Persons.
But you have many, many Persons. Suppose that your program allow search Persons by Id and Name. Each time a user search for Persons you must iterate a very long list. It's not efficient. So you define two Dictionary:
Dictionary<int, List<Person>> personsById;
Dictionary<string, List<Person>> personsByName;
Also, you have a List:
List<Person> allPersons;
And we need some operations:
public void AddPerson(Person person)
{
Person existingPerson;
if (personsById.TryGetValue(person.Id, out existingPerson))
{
// Already exists: don't add duplicated persons
return;
}
personsById.Add(person.Id, person);
// Maybe more than one person with the same name. It's the reason why use a List here
List<Person> list;
if (personsByName.TryGetValue(person.Name, out list))
{
// There are other persons with this name, so the list exists. Simply add person to list
list.Add(person);
}
else
{
// Is the first person with this name: create the list and associate to the name
personsByName.Add(person.Name, new List<Person> { person });
}
// Always add to main list
allPersons.Add(person);
}
public void RemovePerson(Person person)
{
if (!personsById.TryGetValue(person.Id, out _))
{
// Not exists: do nothing
return;
}
personsById.Remove(person.Id);
// NOTE: Here we are removing person assuming that you never have different instances
// of person. If you can create different instances for a person, here we must iterate
// the lists to find the person
List<Person> list;
if (personsByName.TryGetValue(person.Name, out list))
{
list.Remove(person);
}
allPersons.Remove(person);
}
public Person GetPerson(int id)
{
return this.personsById.TryGetValue(id, out Person person) ? person : null;
}
public List<Person> GetPersons(string name)
{
return this.personsByName.TryGetValue(name, out List<Person> list) ? list : null;
}
It's a very basic example but I think maybe a good one to learn about this classes.

Unity C# Cycling some texts without repeating them

I've a line in the bottom part of the main screen of the game, that every time the scene is loaded, it shows a different tip (how to play, how to change music...).
The question is that I'm using Random.Range for that but, honestly, I'll prefer a way where all tips are showed, one by one in a random way, but without repeating any of them.
My code is as follows:
int randNum;
void Start () {
randNum = Random.Range(0,5);
}
void Update () {
switch (randNum)
{
case 0:
// blah, blah, blah...
case 1...
How can I achieve what I want?
Thans for yout timeeee :)
You can remove the switch statement and store each message in a list.
var tips = new List<string>();
tips.Add("The clouds are white");
...
Then you can randomize the elements in the list (more on that here) and show tips one by one. All you need is to keep track of the index. Example:
// This needs to be a field.
int index = 0;
void ShowTip()
{
// TODO: make sure index is not equal or greater than tips.Count
tip = tips[index];
index++;
// Display the tip
}
What you can do is to shuffle your list of tip. The Fisher-Yates shuffle is one of the most common.
static Random _random = new Random();
static void Shuffle<T>(T[] array)
{
int n = array.Length;
for (int i = 0; i < n; i++)
{
// Use Next on random instance with an argument.
// ... The argument is an exclusive bound.
// So we will not go past the end of the array.
int r = i + _random.Next(n - i);
T t = array[r];
array[r] = array[i];
array[i] = t;
}
}
public static void Main()
{
string[] array = { "tip 1", "tip 2", "tip 3" };
Shuffle(array);
foreach (string value in array)
{
Console.WriteLine(value);
}
}
output
net
dot
perls
source
Suppose your messages are stored in a list of string declared at the global level together with your random class and with an additional List of strings initially empty
List<string> needToDisplayMessages = new List<string>();
List<string> base_messages = new List<string>{"message1","message2","message3","message4","message5"};
Random rnd = new Random();
In your update method check if the list of messages to be displayed is empty and if yes copy the messages from the list with the predefined message. Now use the random instance to choose the index of the message to display and get it from the dynamic list. Finally remove the message from the list of message still to be displayed.
void Update () {
// We refill the list if it is empty
if(needToDisplayMessages.Count == 0)
needToDisplayMessages.AddRange(base_messages);
// Choose a random number topped by the count of messages still to be displayed
int index = rnd.Next(0, needToDisplayMessages.Count);
string message = needToDisplayMessages[index];
..... display the message someway .....
// Remove the message from the list
needToDisplayMessages.RemoveAt(index);
}
Of course, if you want to display the messages in sequential order there is no need of this but (as already explained) just an index. But if you want to randomly choose the message until you have shown all of them and then restart the cycle, perhaps this approach is not too much complex.

How to display the items from a dictionary in a random order but no two adjacent items being the same

First of all, there is actually more restrictions than stated in the title. Plz readon.
say, i have a dictionary<char,int> where key acts as the item, and value means the number of occurrence in the output. (somewhat like weighting but without replacement)
e.g. ('a',2) ('b',3) ('c',1)
a possible output would be 'babcab'
I am thinking of the following way to implement it.
build a new list containing (accumulated weightings,char) as its entry.
randomly select an item from the list,
recalculate the accumulated weightings, also set the recent drawn item weighing as 0.
repeat.
to some extent there might be a situation like such: 'bacab' is generated, but can do no further (as only 'b' left, but the weighting is set to 0 as no immediate repetition allowed). in this case i discard all the results and start over from the very beginning.
Is there any other good approach?
Also, what if i skip the "set the corresponding weighting to 0" process, instead I reject any infeasible solution. e.g. already i got 'bab'. In the next rng selection i get 'b', then i redo the draw process, until i get something that is not 'b', and then continue. Does this perform better?
How about this recursive algorithm.
Create a list of all characters (candidate list), repeating them according to their weight.
Create an empty list of characters (your solution list)
Pick a random entry from the candidate list
If the selected item (character) is the same as the last in solution list then start scanning for another character in the candidate list (wrapping around if needed).
If no such character in step 4 can be found and candidate list is not empty then backtrack
Append the selected character to the solution list.
If the candidate list is empty print out the solution and 'backtrack', else go to step 3.
I'm not quite sure about the 'backtrack' step yet, but you should get the general idea.
Try this out, it should generate a (pseudo) random ordering of the elements in your enumerable. I'd recommend flattening from your dictionary to a list:
AKA Dictionary of
{b, 2}, {a, 3} becomes {b} {b} {a} {a} {a}
public static IEnumerable<T> RandomPermutation<T>(this IEnumerable<T> enumerable)
{
if (enumerable.Count() < 1)
throw new InvalidOperationException("Must have some elements yo");
Random random = new Random(DateTime.Now.Millisecond);
while (enumerable.Any())
{
int currentCount = enumerable.Count();
int randomIndex = random.Next(0, currentCount);
yield return enumerable.ElementAt(randomIndex);
if (randomIndex == 0)
enumerable = enumerable.Skip(1);
else if (randomIndex + 1 == currentCount)
enumerable = enumerable.Take(currentCount - 1);
else
{
T removeditem = enumerable.ElementAt(randomIndex);
enumerable = enumerable.Where(item => !item.Equals(removeditem));
}
}
}
If you need additional permutations, simply call it again for another random ordering. While this wont get you every permutation, you should find something useful. You can also use this as a base line to get a solution going.
This should be split into some seperate methods and could use some refactoring but the idea is to implement it in such a way that it does not depend on randomly moving things around till you get a valid result. That way you can't predict how long it would take
Concatenate all chars to a string and randomize that string
Loop through the randomized string and find any char that violates the rule
Remove that char from the string
Pick a random number. Use this number as "put the removed char at the nth valid position")
Loop around the remaining string to find the Nth valid postion to put the char back.
If there is no valid position left drop the char
Repeat from step 2 until no more violations are found
using System;
using System.Collections.Generic;
namespace RandomString
{
class Program
{
static void Main(string[] args)
{
Random rnd = new Random(DateTime.Now.Millisecond);
Dictionary<char, int> chars = new Dictionary<char, int> { { 'a', 2 }, { 'b', 3 }, { 'c', 1 } };
// Convert to a string with all chars
string basestring = "";
foreach (var pair in chars)
{
basestring += new String(pair.Key, pair.Value);
}
// Randomize the string
string randomstring = "";
while (basestring.Length > 0)
{
int randomIndex = rnd.Next(basestring.Length);
randomstring += basestring.Substring(randomIndex, 1);
basestring = basestring.Remove(randomIndex, 1);
}
// Now fix 'violations of the rule
// this can be optimized by not starting over each time but this is easier to read
bool done;
do
{
Console.WriteLine("Current string: " + randomstring);
done = true;
char lastchar = randomstring[0];
for (int i = 1; i < randomstring.Length; i++)
{
if (randomstring[i] == lastchar)
{
// uhoh violation of the rule. We pick a random position to move it to
// this means it gets placed at the nth location where it doesn't violate the rule
Console.WriteLine("Violation at position {0} ({1})", i, randomstring[i]);
done = false;
char tomove = randomstring[i];
randomstring = randomstring.Remove(i, 1);
int putinposition = rnd.Next(randomstring.Length);
Console.WriteLine("Moving to {0}th valid position", putinposition);
bool anyplacefound;
do
{
anyplacefound = false;
for (int replace = 0; replace < randomstring.Length; replace++)
{
if (replace == 0 || randomstring[replace - 1] != tomove)
{
// then no problem on the left side
if (randomstring[replace] != tomove)
{
// no problem right either. We can put it here
anyplacefound = true;
if (putinposition == 0)
{
randomstring = randomstring.Insert(replace, tomove.ToString());
break;
}
putinposition--;
}
}
}
} while (putinposition > 0 && anyplacefound);
break;
}
lastchar = randomstring[i];
}
} while (!done);
Console.WriteLine("Final string: " + randomstring);
Console.ReadKey();
}
}
}

How do I keep a list of only the last n objects?

I want to do some performance measuring of a particular method, but I'd like to average the time it takes to complete. (This is a C# Winforms application, but this question could well apply to other frameworks.)
I have a Stopwatch which I reset at the start of the method and stop at the end. I'd like to store the last 10 values in a list or array. Each new value added should push the oldest value off the list.
Periodically I will call another method which will average all stored values.
Am I correct in thinking that this construct is a circular buffer?
How can I create such a buffer with optimal performance? Right now I have the following:
List<long> PerfTimes = new List<long>(10);
// ...
private void DoStuff()
{
MyStopWatch.Restart();
// ...
MyStopWatch.Stop();
PerfTimes.Add(MyStopWatch.ElapsedMilliseconds);
if (PerfTimes.Count > 10) PerfTimes.RemoveAt(0);
}
This seems inefficient somehow, but perhaps it's not.
Suggestions?
You could create a custom collection:
class SlidingBuffer<T> : IEnumerable<T>
{
private readonly Queue<T> _queue;
private readonly int _maxCount;
public SlidingBuffer(int maxCount)
{
_maxCount = maxCount;
_queue = new Queue<T>(maxCount);
}
public void Add(T item)
{
if (_queue.Count == _maxCount)
_queue.Dequeue();
_queue.Enqueue(item);
}
public IEnumerator<T> GetEnumerator()
{
return _queue.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Your current solution works, but it's inefficient, because removing the first item of a List<T> is expensive.
private int ct = 0;
private long[] times = new long[10];
void DoStuff ()
{
...
times[ct] = MyStopWatch.ElapsedMilliseconds;
ct = (ct + 1) % times.Length; // Wrap back around to 0 when we reach the end.
}
Here is a simple circular structure.
This requires none of the array copying or garbage collection of linked list nodes that the other solutions have.
For optimal performance, you can probably just use an array of longs rather than a list.
We had a similar requirement at one point to implement a download time estimator, and we used a circular buffer to store the speed over each of the last N seconds.
We weren't interested in how fast the download was over the entire time, just roughly how long it was expected to take based on recent activity but not so recent that the figures would be jumping all over the place (such as if we just used the last second to calculate it).
The reason we weren't interested in the entire time frame was that a download could so 1M/s for half an hour then switch up to 10M/s for the next ten minutes. That first half hour will drag down the average speed quite severely, despite the fact that you're now downloading quite fast.
We created a circular buffer with each cell holding the amount downloaded in a 1-second period. The circular buffer size was 300, allowing for 5 minutes of historical data, and every cell was initialised to zero. In your case, you would only need ten cells.
We also maintained a total (the sum of all entries in the buffer, so also initially zero) and the count (initially zero, obviously).
Every second, we would figure out how much data had been downloaded since the last second and then:
subtract the current cell from the total.
put the current figure into that cell and advance the cell pointer.
add that current figure to the total.
increase the count if it wasn't already 300.
update the figure displayed to the user, based on total / count.
Basically, in pseudo-code:
def init (sz):
buffer = new int[sz]
for i = 0 to sz - 1:
buffer[i] = 0
total = 0
count = 0
index = 0
maxsz = sz
def update (kbps):
total = total - buffer[index] + kbps # Adjust sum based on deleted/inserted values.
buffer[index] = kbps # Insert new value.
index = (index + 1) % maxsz # Update pointer.
if count < maxsz: # Update count.
count = count + 1
return total / count # Return average.
That should be easily adaptable to your own requirements. The sum is a nice feature to "cache" information which may make your code even faster. By that I mean: if you need to work out the sum or average, you can work it out only when the data changes, and using the minimal necessary calculations.
The alternative would be a function which added up all ten numbers when requested, something that would be slower than the single subtract/add when loading another value into the buffer.
You may want to look at using the Queue data structure instead. You could use a simple linear List, but it is wholly inefficient. A circular array could be used but then you must resize it constantly. Therefore, I suggest you go with the Queue.
I needed to keep 5 last scores in a array and I came up with this simple solution.
Hope it will help some one.
void UpdateScoreRecords(int _latestScore){
latestScore = _latestScore;
for (int cnt = 0; cnt < scoreRecords.Length; cnt++) {
if (cnt == scoreRecords.Length - 1) {
scoreRecords [cnt] = latestScore;
} else {
scoreRecords [cnt] = scoreRecords [cnt+1];
}
}
}
Seems okay to me. What about using a LinkedList instead? When using a List, if you remove the first item, all of the other items have to be bumped back one item. With a LinkedList, you can add or remove items anywhere in the list at very little cost. However, I don't know how much difference this would make, since we're only talking about ten items.
The trade-off of a linked list is that you can't efficiently access random elements of the list, because the linked list must essentially "walk" along the list, passing each item, until it gets to the one you need. But for sequential access, linked lists are fine.
For java, it could be that way
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
public class SlidingBuffer<T> implements Iterable<T>{
private Queue<T> _queue;
private int _maxCount;
public SlidingBuffer(int maxCount) {
_maxCount = maxCount;
_queue = new LinkedList<T>();
}
public void Add(T item) {
if (_queue.size() == _maxCount)
_queue.remove();
_queue.add(item);
}
public Queue<T> getQueue() {
return _queue;
}
public Iterator<T> iterator() {
return _queue.iterator();
}
}
It could be started that way
public class ListT {
public static void main(String[] args) {
start();
}
private static void start() {
SlidingBuffer<String> sb = new SlidingBuffer<>(5);
sb.Add("Array1");
sb.Add("Array2");
sb.Add("Array3");
sb.Add("Array4");
sb.Add("Array5");
sb.Add("Array6");
sb.Add("Array7");
sb.Add("Array8");
sb.Add("Array9");
//Test printout
for (String s: sb) {
System.out.println(s);
}
}
}
The result is
Array5
Array6
Array7
Array8
Array9
Years after the latest answer I stumbled on this questions while looking for the same solution. I ended with a combination of the above answers especially the one of: cycling by agent-j and of using a queue by Thomas Levesque
public class SlidingBuffer<T> : IEnumerable<T>
{
protected T[] items;
protected int index = -1;
protected bool hasCycled = false;
public SlidingBuffer(int windowSize)
{
items = new T[windowSize];
}
public void Add(T item)
{
index++;
if (index >= items.Length) {
hasCycled = true;
index %= items.Length;
}
items[index] = item;
}
public IEnumerator<T> GetEnumerator()
{
if (index == -1)
yield break;
for (int i = index; i > -1; i--)
{
yield return items[i];
}
if (hasCycled)
{
for (int i = items.Length-1; i > index; i--)
{
yield return items[i];
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
I had to forego the very elegant one-liner of j-agent: ct = (ct + 1) % times.Length;
because I needed to detect when we circled back (through hasCycled) to have a well behaving enumerator. Note that the enumerator returns values from most-recent to oldest value.

C# Look up dictionary

I'm currently trying to create a program that estimates location based on signal strength. The signal strength value is an int and then i need a lookup dictionary with ranges.
So I would have something like:
Signal Strenth Position
0-9 1
10-19 2
20-29 3
and then I would want to look up what position a signal strength relates to, for example 15 would relate to position 2.
I know I can just have a load of if statements but is there a good way to do this using some sort of lookup dictionary?
If you have arbitrary but consecutive ranges you can use an array of the upper bounds and perform a binary search to get the position:
// Definition of ranges
int[] ranges = new int[] { 9, 19, 29 };
// Lookup
int position = Array.BinarySearch(ranges, 15);
if (position < 0)
position = ~position;
// Definition of range names
string[] names = new string[] { "home", "street", "city", "far away" };
Console.WriteLine("Position is: {0}", names[position]);
Array.BinarySearch returns the index of the item in the array if it exists (array must be sorted obviously) or the bitwise inverted index where the item should be inserted to keep the array sorted.
What about :
int position = signalStrength / 10 + 1;
Kindness,
Dan
When you want to use Dictionary, you need at least some special key type to deal with the ranges. KeyType can be abstract and two derived types KeyTypeRange(int int) and KEyTypeSearch( int). Some special comparison logic must be implemented to compare an KeyTypeSearch with an KeyTypeRange.
SortedDictionary<KeyType,int> lookup = new Dictionary<int,int>();
lookup.Add( new KeyTypeRange(1,10),1);
lookup.Add( new KeyTypeRange(11,20),2);
lookup.Add( new KeyTypeRange(21,30),3);
lookup.TryGetValue( new KeyTypeSearch(15) );
It shows a possible solution to use different esearch keys and key values in dictionaries. But this seems to be Overkill for this problem. This problem is solved best by the BinarySearch solution.
Good is a function of purpose. All of the above solutions work well presuming that any given range is a small number of integers. Otherwise you may want to use whatever the real world math function is to determine your group. For instance, for the example given, your answer function would be x % 10 + 1; That'll run much faster than a dictionary.
You could do a Dictionary, where the first int is the signal strength and the second int is the position. You would need to add an entry for every value in the range (so, one for signal strength 0, position 1, signal strength 1, position 1, etc.), but it would be a very quick, single line lookup.
Something like:
Dictionary<int, int> values;
values = new Dictionary<int, int>();
values[0] = 1;
values[1] = 1;
...
values[29] = 3;
and then, to access it:
Console.WriteLine(values[27].ToString());
For future expansion i would do 2 dictionaries.
Just in case those rates change
so a
dictionary<string,dictionary<int,int>>
or just use custom classes
the string would be static strings like low med, high, then you can change the ranges in your foreach initilixing the initial values
One solution would be to use a simple list, where each position in the list represents a different position that you're scanning for. In code, it might look something like this (assuming that all position numbers are sequential):
** Note: I have not actually run this code to make sure it works as-is... you might also need to implement an IEqualityComparer on Range in order for the IndexOf operation to return the proper position:
public class Controller
{
List m_positions = new List();
public void LoadPositions()
{
m_positions.Add(new Range(0, 9));
m_positions.Add(new Range(10, 19));
m_positions.Add(new Range(20, 29));
}
public int GetPosition (int signal)
{
Range range = m_positions.Single(a => IsBetween(signal, a.Min, a.Max));
return m_positions.IndexOf(range);
}
private static bool IsBetween (int target, int min, int max)
{
return min = target;
}
}
It's probably pretty self-explanatory, but to avoid any confusion, here's what the Range class might look like:
public class Range
{
public Range(int min, int max)
{
this.Min = min;
this.Max = max;
}
public int Min
{
get;
private set;
}
public int Max
{
get;
private set;
}
}
if there is a direct correlation between signal range and the position then use what #agileguy suggested.
If you have positions distributed non linearly across the signal strength then one way would be:
class SignalStrengthPositionMapper
{
private static readonly int[] signalStrength = { Int32.MinValue, 0, 5, 11, 15, 20, 27, 35 };
public static int GetPosition(int strength)
{
return StrengthSearch(0, signalStrength.Length, strength);
}
// modified binary search
private static int StrengthSearch(int start, int end, int strength)
{
int mid = 0;
while (start <= end)
{
mid = (start + end) / 2;
if (strength >= signalStrength[mid]) // lower bound check
{
start = mid + 1;
if (strength < signalStrength[start]) // upper bound check
return mid;
}
else if (strength < signalStrength[mid]) // upper bound check
{
end = mid - 1;
if (strength >= signalStrength[end]) // lower bound check
return mid;
}
}
return 0;
}
}
Try using generics:
Dictionary<int,int> lookup = new Dictionary<int,int>();
lookup.Add(0,1);
lookup.Add(1,1);
lookup.Add(2,1);
lookup.Add(3,1);
...
lookup.Add(9,1);
lookup.Add(10,2);
lookup.Add(11,2);
etc
Then, lookup[22] would return value of 3. I suggest using a set of loops to create your 'ranges'. With this method, you're guaranteed O(1) access time.

Categories

Resources