c# shift the elements of array by 1 with for loop - c#

Im making a game in c# and it have a scoreboard with the top 5 players.
I made it to work, at least I thought I did...
The script enter the value of player's name and his score in a array, but there is a problem. It just delete the last one, so if you make the best score you become #1, but the old #1 is deleted and #2 is always #2 unless someone make a result for that place. My question is how to move the array by one from some place (it depends on player's result) and delete the last string of it?
Edit: Can't use list, because im doing so much stuff with that array.
Like this:
string topScores = sbName[i].Substring(4);
int topScoreInt = Convert.ToInt32(topScores);

If you need to use an array instead of a List (which has a handy Insert method), you could work your way from the last value forward, replacing each with it's predecessor's value, until you get to the one you want to update:
int place = 2; // Meaning 3rd place, since arrays are zero-based
// Start at end of array, and replace each value with the one before it
for (int i = array.Length - 1; i > place; i--)
{
array[i] = array[i - 1];
}
// Update the current place with the new score
array[place] = score;

Since you have just 5 items and scoring is normally rare some basic LINQ code would be easier and likely more readable:
class Score { public string Name; public int Result;}
Score[] scores = new Score[5];
var newScore = new Score {Name = "Foof", Result=9001};
scores = scores
.Where(s => s != null) // ignore empty
.Concat(Enumerable.Repeat(newScore,1)) // add new one
.OrderByDescending(s => s.Result) // re-sort
.Concat(Enumerable.Repeat((Score)null,4)) // pad with empty if needed
.Take(5) // take top X
.ToArray(); // back to array
Side note: You really will be better off using List<T> or SortedList<K,V>.

Related

How can I check for integers in list and print the index of them?

so my problem is that I don't know how to go forward in the list and print the next same integer if there is one.
Here is what I have at the moment:
while (list.Contains(input1))
{
Console.WriteLine(input1 + " is at index " + list.IndexOf(input1))
}
I am trying to list all of the integers that are in the list and print the index of them. But not remove after finding one of the integers (this was at least my first idea.).
IndexOf has an overload with two parameters, which allows you to start searching at a later position in the list.
Since this is obviously a learning exercise, I won't spoil it by providing the full code, but rather suggest that you try to implement the following algorithm:
Find the index of input starting at position 0.
If not found (i.e., IndexOf returns -1): we're done. Otherwise:
Print and remember that index.
Start again at step 1, but this time, don't start searching at 0 but at the index you remembered + 1.
You can do the following:
go through the list/array using for statement
for(int i=0; i < list.length; i++) // loop though list
then inside the loop check the value of the current item using if statement:
if(list[i] == input1)
//do smothing
The list[0] represent the first item in the array, which means the index is 0.
so in the example above the i will be the current index so long that you in the loop.
I didn't write the full code for learning purpose in reference to #Heinzi answer.
Hope that could be helpful!
This is an implementation possibility. It is longer than it has to be, but it makes it clearer for beginners how one could tackle this problem.
Since you wanted to only show numbers that come up more than once here is an implementation method. If you want to show numbers that come up only once too just erase everything about lastindex
List<int> yourlist = new List<int> { 1,1,1,1,1,11,2,3,3,4,4,5 };
int input = 0;
input = Convert.ToInt32(Console.ReadLine());
var index = yourlist.IndexOf(input);
//this checks if your input is in the list
var lastindex = yourlist.LastIndexOf(input);
//this does the same but it searches for the last implementation of your input
if (index != -1 && lastindex != index)
//this if checks if your number comes up more than once. IndexOf returns -1 if there is no occurence of your input
{
Console.Write($"the index of {input} is {index}");
for (int i = index+1; i <= yourlist.Count; i++)
//this loop takes the position of the first occurence of your number and then counts up from there
{
var tempindex = yourlist.IndexOf(input, i);
if (tempindex != -1)
//this if lets everything except -1 through
{
Console.Write($" and {tempindex}");
}
}
}
else
{
Console.WriteLine("your number cannot be found twice in the list");
}

Fill an array with unique random integers [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 5 years ago.
I want to fill a small array with unique values from a bigger array. How do I check the uniqueness?
Here's what I have:
int[] numbers45 = new int[45];
for (int i = 0; i <= 44; i++) // I create a big array
{
numbers45[i] = i + 1;
}
Random r = new Random();
int[] draw5 = new int[5]; //new small array
Console.WriteLine("The 5 draws are:");
for (int i = 1; i <= 4; i++)
{
draw5[i] = numbers45[r.Next(numbers45.Length)]; //I fill the small array with values from the big one. BUT the values might not be unique.
Console.WriteLine(draw5[i]);
}
There are multiple ways to do what you are asking.
First off, though, I would recommend to use one of the classes which wraps the array type and adds some extra functionality you could use (in this case a List would probably be a perfect structure to use)!
One way to handle this is to check if the value is already in the draw5 array. This can be done with (for example) the List<T>.Contains(T) function, and if it exists, try another.
Personally though, I would probably have randomized the first array with the OrderBy linq method and just return a random number, like:
numbers45.OrderBy(o => random.Next());
That way the numbers are already random and unique when it is supposed to be added to the second list.
And a side note: Remember that arrays indexes starts on index 0. In your second loop, you start at 1 and go to 4, that is, you wont set a value to the first index.
You could just run for (int i=0;i<5;i++) to get it right.
Inspired by Jite's answer, I changed to use Guid to randomize the numbers
var randomized = numbers45.OrderBy(o => Guid.NewGuid().ToString());
Then you could take the draws by:
var draws = randomized.Take(5).ToArray;
HashSet<int> hs = new HashSet<int>();
int next = random.next(45);
while(hs.Length <=5)
{
if(!hs.Contains(array[next]))
hs.Add(array[next]);
next = random next(45);
}

How to iterate backwards (with pauses for input) through a set of randomly generated numbers?

// EDIT:
This is not a duplicate to: "When should I use a List vs a LinkedList". Check the answer I've provided below.
LinkedList might be useful though if someone wants to insert some positions at a specific place after we have a properly ordered List (in that case - LinkedList) already - how to make one, check my answer below. //
How would you iterate backwards (with pauses for player input) through a set of randomly generated numbers?
I'm trying to build a turn-based game. The order of actions is determined by a result of something like that:
int randomPosition = Random.Range(1,31) + someModifier;
// please note that someModifier can be a negative number!
// There is no foreseeable min or max someModifier.
// Let's assume we can't set limits.
I have a List of KeyValue pairs already containing names of objects and their corresponding randomPosition. // Update: Values of it are already sorted by a custom Sort() function, from highest to lowest.
// A list of KeyValue pairs containing names of objects and their corresponding randomPosition.
public List<KeyValuePair<string, int>> turnOrder = new List<KeyValuePair<string, int>> ();
// GameObject names are taken from a list of player and AI GameObjects.
List <GameObject> listOfCombatants = new List<GameObjects>();
// Each GameObject name is taken from listOfCombatants list.
listOfCombatants[i].name;
I thought, maybe let's add player and AI GameObjects to a list on Index positions equal to each of their randomPosition. Unfortunately, a Generic List can't have "gaps" in it. So we can't create it, let alone iterate it backwards.
Also, I'm not sure how we'd stop a for loop to wait for player input. I have a button, pressing which will perfom an action - switch state and run some functions.
Button combat_action_button;
combat_action_button.onClick.AddListener (AttackButton);
// When player takes his turn, so in TurnState.PLAYER_ACTION:
public void AttackButton() {
switch(actionState) {
case PlayerAction.ATTACK:
Debug.Log (actionState);
// Do something here - run function, etc. Then...
currentState = TurnState.ENEMY_ACTION;
break;
}
To make things worse, I've read that "pausing" a while loop isn't good for performance. That it's better to take player input out of loops.
So maybe I should create more for loops, iterate a new loop from position to position until the last GameObject acted, and use some delegates/events, as some things players/AI can do are in different scripts (classes).
This is not a real-time project, so we can't base anything on time (other than potential max turn time).
Another thing is, we don't know how many GameObjects will take turns.
But maybe there's some collection type that can store GameObjects with gaps between their index positions and iterate a loop from highest to lowest with no problem?...
I want to make it as simple as possible.
For the issue of simultaneously having user input and looping, I recommend looking into background workers or threading/tasks. This should facilitate your problem with simultaneously doing two things at once.
For your list problem I personally prefer the lists as well which is why I would designate each "gap" with a specific character like - 1, and when referencing the data ignore the - 1. To ignore the gaps I recommend LINQ queries but if you don't want to use LINQ that should not be a problem.
EDIT*
I know very little about Unity but from what people have been saying it sounds like running multiple threads is or can be an issue. I looked into this issue and it sounds like you just cannot call the unity api from a background thread. So basically, as long as you do not reference the unity api from the background thread, you should be ok. With that said, you may possibly need/want to make a call to the api inside of the background worker. To do this you need to either invoke the call before or after the background worker thread. I am pretty sure there is also a simultaneous invocation from the main thread by setting the workers step property to true.
I've decided to share my own solutions as I think I've developped some accurate answers to my questions.
Solution #1. First, declare an array, 2 variables and 1 function:
int[] arrayOrderedCombatants;
int min = 100000;
int max;
public void SomePlayerAction() {
Debug.Log ("This is some player or AI action.");
}
public void IterateThroughTurnOrderPositions() {
for (int i=max; i >= min; i--) {
if (arrayOrderedCombatants [i] >= 0 && arrayOrderedCombatants [i] >= min) {
Debug.Log ("This is an existing position in turn order: " + arrayOrderedCombatants [i]);
foreach (var position in turnOrder) {
if (position.Value == arrayOrderedCombatants [i]) {
max = (arrayOrderedCombatants [i] - 1);
goto ExitLoop;
}
}
}
}
ExitLoop:
SomePlayerAction ();
}
Then, for our testing purposes let's use an Update() method triggered by Input.GetKeyDown:
if (Input.GetKeyDown (KeyCode.O)) {
arrayOrderedCombatants = new int[turnOrder[0].Value + 1];
Debug.Log ("This is arrayOrderedCombatants Length: " + arrayOrderedCombatants.Length);
foreach (var number in turnOrder) {
if (number.Value < min)
min = number.Value;
}
Debug.Log ("This is min result in random combat order: " + min);
for (int i=0; i < arrayOrderedCombatants.Length; i++)
arrayOrderedCombatants[i] = min -1;
foreach (var combatant in turnOrder) {
if (combatant.Value >= 0) {
arrayOrderedCombatants [combatant.Value] = combatant.Value;
}
}
max = turnOrder [0].Value;
while (max >= min)
IterateThroughTurnOrderPositions ();
}
The above code answers my question. Unfortunately, problems with this solution may be two. First - you can't have a negative index position. So if someModifier makes randomPosition go below 0, it won't work. Second problem - if you have more than 1 occurance of any value from randomPosition, it will be added to the arrayOrderedCombatants only once. So it will be iterated once too.
But that's obvious - you can't have more than one value occupying an int type Arrays' index position.
So I will provide you a better solution. It's a different approach, but works like it should.
Solution #2. First, declare a list of GameObjects:
List<GameObject> orderedCombatants = new List<GameObject> ();
Then, in Update() method:
if (Input.GetKeyDown (KeyCode.I)) {
orderedCombatants.Clear ();
foreach (var combatant in initiative) {
Debug.Log (combatant);
for (int i=0; i < listOfCombatants.Count; i++) {
if (listOfCombatants[i].name.Contains(combatant.Key)) {
orderedCombatants.Add(listOfCombatants[i]);
}
}
}
foreach (var combatant in orderedCombatants) {
Debug.Log (combatant.name);
}
}
The above creates a new list of GameObjects already set in the right order. Now you can iterate through it, easily access each GameObject and perform any actions you need.

Sorting a list and figuring out the index

Basically I'm trying to get a "rankings" between 4 people. I have an array of player scores which contains the scores of each person such that person 1's score is at index 0, person 2's score is at index 1, etc... I want to obtain the index of the top scorer in the list and get the second, and the third, and the last.
My attempt at the solution:
I made a list out of the array playerScores (I feel like it might not be necessary, but due to what I'm about to do, I didn't want to ruin the original scores) which contains the scores in order. And then, I find the max number in the list and get its index. Then I change the value to a negative value at that index. And then I redo the steps.
List<int> listOfScores = new List<int>(playerScores.ToList());
// We get the max value to determine the top scorer of the game
// and then we insert a negative value at that same spot of the list so we can
// continue on figuring out the following max value in the list, etc.
rank1 = listOfScores.IndexOf(listOfScores.Max());
listOfScores[rank1] = -1;
rank2 = listOfScores.IndexOf(listOfScores.Max());
listOfScores[rank2] = -1;
rank3 = listOfScores.IndexOf(listOfScores.Max());
listOfScores[rank3] = -1;
rank4 = listOfScores.IndexOf(listOfScores.Max());
listOfScores[rank4] = -1;
I feel like I can do this in a much more efficient manner and as less messy as this code...
Well, this is also assuming that negative is not a score a person can get. Is there any other way, to be more efficient than this? And what about if say we want to have negative scores?
Using LINQ:
using System.Linq;
var ranked = playerScores.Select((score, index) =>
new {Player=index+1, Score=score})
.OrderByDescending(pair => pair.Score)
.ToList();
And then display the winner, for example:
Console.WriteLine(String.Format("Winner: Player {0} with score {1}",
ranked[0].Player, ranked[0].Score));
You can build a dictionary where a player is a key and score is a value:
Dictionary<Player int> playerToScore;
Now you may sort players as you want, but when you need to get or change its score you just do:
var playerScore = playerToScore[myPlayer];
Try using a dictionary like this:
var userScores = new Dictionary<int,int>{{1,3},{2,2},{3,1},{4,6}};
var orderedUserScores = userScores.OrderBy(x => x.Value);
//orderedUserScores[0] returns KeyValuePair of {3,1}
//So, the key is the player number/position, and the value is the score

Array sorting efficiency... Beginner need advice

I'll start by saying I am very much a beginner programmer, this is essentially my first real project outside of using learning material. I've been making a 'Simon Says' style game (the game where you repeat the pattern generated by the computer) using C# and XNA, the actual game is complete and working fine but while creating it, I wanted to also create a 'top 10' scoreboard. The scoreboard would record player name, level (how many 'rounds' they've completed) and combo (how many buttons presses they got correct), the scoreboard would then be sorted by combo score. This led me to XML, the first time using it, and I eventually got to the point of having an XML file that recorded the top 10 scores. The XML file is managed within a scoreboard class, which is also responsible for adding new scores and sorting scores. Which gets me to the point... I'd like some feedback on the way I've gone about sorting the score list and how I could have done it better, I have no other way to gain feedback =(. I know .NET features Array.Sort() but I wasn't too sure of how to use it as it's not just a single array that needs to be sorted. When a new score needs to be entered into the scoreboard, the player name and level also have to be added. These are stored within an 'array of arrays' (10 = for 'top 10' scores)
scoreboardComboData = new int[10]; // Combo
scoreboardTextData = new string[2][];
scoreboardTextData[0] = new string[10]; // Name
scoreboardTextData[1] = new string[10]; // Level as string
The scoreboard class works as follows:
- Checks to see if 'scoreboard.xml' exists, if not it creates it
- Initialises above arrays and adds any player data from scoreboard.xml, from previous run
- when AddScore(name, level, combo) is called the sort begins
- Another method can also be called that populates the XML file with above array data
The sort checks to see if the new score (combo) is less than or equal to any recorded scores within the scoreboardComboData array (if it's greater than a score, it moves onto the next element). If so, it moves all scores below the score it is less than or equal to down one element, essentially removing the last score and then places the new score within the element below the score it is less than or equal to. If the score is greater than all recorded scores, it moves all scores down one and inserts the new score within the first element. If it's the only score, it simply adds it to the first element. When a new score is added, the Name and Level data is also added to their relevant arrays, in the same way. What a tongue twister. Below is the AddScore method, I've added comments in the hope that it makes things clearer O_o. You can get the actual source file HERE. Below the method is an example of the quickest way to add a score to follow through with a debugger.
public static void AddScore(string name, string level, int combo)
{
// If the scoreboard has not yet been filled, this adds another 'active'
// array element each time a new score is added. The actual array size is
// defined within PopulateScoreBoard() (set to 10 - for 'top 10'
if (totalScores < scoreboardComboData.Length)
totalScores++;
// Does the scoreboard even need sorting?
if (totalScores > 1)
{
for (int i = totalScores - 1; i > - 1; i--)
{
// Check to see if score (combo) is greater than score stored in
// array
if (combo > scoreboardComboData[i] && i != 0)
{
// If so continue to next element
continue;
}
// Check to see if score (combo) is less or equal to element 'i'
// score && that the element is not the last in the
// array, if so the score does not need to be added to the scoreboard
else if (combo <= scoreboardComboData[i] && i != scoreboardComboData.Length - 1)
{
// If the score is lower than element 'i' and greater than the last
// element within the array, it needs to be added to the scoreboard. This is achieved
// by moving each element under element 'i' down an element. The new score is then inserted
// into the array under element 'i'
for (int j = totalScores - 1; j > i; j--)
{
// Name and level data are moved down in their relevant arrays
scoreboardTextData[0][j] = scoreboardTextData[0][j - 1];
scoreboardTextData[1][j] = scoreboardTextData[1][j - 1];
// Score (combo) data is moved down in relevant array
scoreboardComboData[j] = scoreboardComboData[j - 1];
}
// The new Name, level and score (combo) data is inserted into the relevant array under element 'i'
scoreboardTextData[0][i + 1] = name;
scoreboardTextData[1][i + 1] = level;
scoreboardComboData[i + 1] = combo;
break;
}
// If the method gets the this point, it means that the score is greater than all scores within
// the array and therefore cannot be added in the above way. As it is not less than any score within
// the array.
else if (i == 0)
{
// All Names, levels and scores are moved down within their relevant arrays
for (int j = totalScores - 1; j != 0; j--)
{
scoreboardTextData[0][j] = scoreboardTextData[0][j - 1];
scoreboardTextData[1][j] = scoreboardTextData[1][j - 1];
scoreboardComboData[j] = scoreboardComboData[j - 1];
}
// The new number 1 top name, level and score, are added into the first element
// within each of their relevant arrays.
scoreboardTextData[0][0] = name;
scoreboardTextData[1][0] = level;
scoreboardComboData[0] = combo;
break;
}
// If the methods get to this point, the combo score is not high enough
// to be on the top10 score list and therefore needs to break
break;
}
}
// As totalScores < 1, the current score is the first to be added. Therefore no checks need to be made
// and the Name, Level and combo data can be entered directly into the first element of their relevant
// array.
else
{
scoreboardTextData[0][0] = name;
scoreboardTextData[1][0] = level;
scoreboardComboData[0] = combo;
}
}
}
Example for adding score:
private static void Initialize()
{
scoreboardDoc = new XmlDocument();
if (!File.Exists("Scoreboard.xml"))
GenerateXML("Scoreboard.xml");
PopulateScoreBoard("Scoreboard.xml");
// ADD TEST SCORES HERE!
AddScore("EXAMPLE", "10", 100);
AddScore("EXAMPLE2", "24", 999);
PopulateXML("Scoreboard.xml");
}
In it's current state the source file is just used for testing, initialize is called within main and PopulateScoreBoard handles the majority of other initialising, so nothing else is needed, except to add a test score.
I thank you for your time!
As a programming tip, you should replace the 3 different arrays with a single array of Score Objects, each of these objects would have all 3 properties you mentioned (Name, Level and Score). You can then create a comparator-like function (based on the score attribute) for it which can be used to sort it via the Arrays.Sort() method.
If you want to keep you current 3 arrays then you can look up sorting algorithms here: http://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms and just make it so that any changes are done to the 3 arrays simultaneously instead of one by one (since you keep the data in them synchronized by index).
You can consider placing your user name, level, and score in a separate class (let's call it ScoreBoardEntry) that inherits from IComparable and then write a comparator. This will allow you to use the built in sort function of List and will be a neater solution than just writing your own.
Your code should look something like this:
1) Create/load a List < ScoreBoardEntry> with all the previous scores
2) push the new score into it
3) Sort List
4) Print only first 10 entries
I wrote some code for you. I've been learning some new-ish type C# stuff like linq, IEnumerable, and yield and kind of wanted to put them together, so hopefully it helps.
you might like the snippet editor i used - linqpad - it's good for learning/experimenting
void Main()
{
string [] playerNames = {"Aaron", "Rick", "Josie"};
var sim = new GameSimulator(playerNames, 1000000, 5000);
var scores = sim.GetScores(5);
var sorted = scores.OrderBy(c=>c.Score).Reverse();
var top = sorted.Take(2);
// uncomment if using linq pad
//scores.Dump("scores");
//sorted.Dump("sorted");
//top.Dump("top");
}
class GameSimulator
{
public GameSimulator(string [] playerNames, int maxScore, int maxLevel)
{
this.playerNames = playerNames;
this.maxScore = maxScore;
this.maxLevel = maxLevel;
}
private string [] playerNames;
private int maxScore;
private int maxLevel;
public IEnumerable<ScoreBoardEntry> GetScores(int numGames)
{
for (int i = 0; i < numGames; i++)
{
string randomName = playerNames[random.Next(0, playerNames.Length-1)];
int randomScore = random.Next(1, maxScore);
int randomLevel = random.Next(1, maxLevel);
yield return new ScoreBoardEntry(randomName, randomLevel, randomScore);
}
}
static private Random random = new Random();
}
class ScoreBoardEntry
{
public ScoreBoardEntry(string playerName, int levenNumber, int score)
{
PlayerName = playerName;
LevelNumber = levenNumber;
Score = score;
}
public string PlayerName { set; get; }
public int LevelNumber { set; get; }
public int Score { set; get; }
}

Categories