Distribute list of resources equally - c#

It's kind of hard to explain the problem I'm having but let's give it a try :)
The small game I'm working on creates a bunch of nodes that are filled with two kinds of resources.
Each node has an iron value and a gold value.
I want to divide these nodes into two areas, so both areas have about the same amount of gold. However, the difference in iron may not be greater than a certain number (let's pick 50 for this example)
The gold/iron ratios are pretty much random, by the way. Here's an example :
gold 75 iron 30
gold 35 iron 70
gold 65 iron 35
Solution for the above situation : 1 and 3 go to area1, 2 goes to area2.
I'm having a lot of trouble trying to automate this process. I've tried iterating through the list of nodes and always passing the node to the area with the smaller amount of iron but that almost never works.
Trying to reassign some nodes from the richer area proves difficult as well, since some nodes have well above 50 iron.
I don't necessarily need to find the best solution (the one with the smallest difference in gold), although that would be optimal.
Any ideas or input are appreciated.

I've had a bit of a play with this and this is what I've got so far, should give a good starting point. I've randomly generated a list of gold and iron pairs (I've used Point because it was simpler for me to work with but anything would work.)
The idea is to take a group of small value golds and swap them with a single larger value gold from the other list. Which in most cases will talk equivilent amounts of gold, but swap a larger value of iron with a smaller one.
private void button2_Click(object sender, EventArgs e)
{
var GoldIron = new List<Point>(
new Point[]{
new Point(16,23),new Point(16,28),new Point(19,44),new Point(21,29),
new Point(23,16),new Point(24,82),new Point(27,85),new Point(31,63),
new Point(31,78),new Point(32,65),new Point(41,23),new Point(43,79),
new Point(44,76),new Point(45,23),new Point(47,16),new Point(50,15),
new Point(50,37),new Point(52,28),new Point(52,58),new Point(52,71),
new Point(61,39),new Point(61,75),new Point(63,59),new Point(68,25),
new Point(68,61),new Point(70,24),new Point(71,75),new Point(74,78),
new Point(77,59),new Point(82,27)}
);
listBox1.DataSource = GoldIron;
//Split into 2 lists based on the gold amount
var Left = new List<Point>();
var Right = new List<Point>();
var SumGold = GoldIron.Sum(P => P.X);
var SumIron = GoldIron.Sum(P => P.Y);
label2.Text = SumGold.ToString();
label1.Text = SumIron.ToString();
var LeftGold = 0;
Int32 i = 0;
while (LeftGold < SumGold / 2)
{
LeftGold += GoldIron[i].X;
Left.Add(GoldIron[i++]);
}
while (i < GoldIron.Count)
{
Right.Add(GoldIron[i++]);
}
Int32 LIndex = 0;
//Start Algorithm
Int32 LeftIron = Left.Sum(P => P.Y);
Int32 RightIron = Right.Sum(P => P.Y);
while (LeftIron - RightIron > 50 || RightIron - LeftIron > 50)
{
if (LeftIron < RightIron)
{
List<Point> TempList = Left;
Left = Right;
Right = TempList;
LIndex = 0;
}
Int32 SmallestRight = Right[LIndex].X;
LeftGold = 0;
i = 0;
while (LeftGold < SmallestRight)
{
LeftGold += Right[i++].X;
}
Point Temp = Right[LIndex];
Right.RemoveAt(LIndex);
Right.AddRange(Left.Take(i));
Left.RemoveRange(0, i);
Left.Add(Temp);
LIndex += i;
//Sort
Left.Sort(CompareGold);
Right.Sort(CompareGold);
LeftIron = Left.Sum(P => P.Y);
RightIron = Right.Sum(P => P.Y);
}
listBox2.DataSource = Left;
SumGold = Left.Sum(P => P.X);
SumIron = Left.Sum(P => P.Y);
label4.Text = SumGold.ToString();
label3.Text = SumIron.ToString();
listBox3.DataSource = Right;
SumGold = Right.Sum(P => P.X);
SumIron = Right.Sum(P => P.Y);
label6.Text = SumGold.ToString();
label5.Text = SumIron.ToString();
}
And the result:

Related

Find points with maximum visibility in a room-as-grid

I have a 2D grid of cells, like so:
I want to find the "centroids," or the places in each room that can see the most other cells. For example, "centroid" 1 can see 500 other cells, "centroid" 2 can see 400 other cells (NOT included in the first 500), and so on (if there's a better name for this let me know).
I'm currently doing this with the code below.
public void SetCentroids(CellWalkableState[,] grid)
{
centroids = new List<((int, int), int)>();
List<(int, int)> cellsCopy = new List<(int, int)>();
for (int i = 0; i < cells.Count; i++)
{
cellsCopy.Add(cells[i]);
}
Debug.Log(DateTime.Now.ToString("o") + " - Setting centroids for room with " + cells.Count + " cells");
var perCellInView = cellsCopy.AsParallel().Select(x => (x, StaticClass.FindInView(x, grid))).ToList();
var force_start = perCellInView.First();
Debug.Log(DateTime.Now.ToString("o") + " - got in view");
var perCellInViewOrdered = perCellInView.AsParallel().OrderByDescending(xxx => xxx.Item2.Count);
var force_start_1 = perCellInViewOrdered.First();
Debug.Log(DateTime.Now.ToString("o") + " - sorted");
List<(int, int)> roomCellsAdded = new List<(int, int)>();
while(roomCellsAdded.Count < (cells.Count*0.9))
{
if(cellsCopy.Count == 0)
{
Debug.LogError("something is wrong here.");
}
var centroid = perCellInViewOrdered.First().x;
var centroidCells = perCellInViewOrdered.First().Item2;
if(centroidCells.Count == 0)
{
Debug.Log("this shouldnt be happening");
break;
}
roomCellsAdded.AddRange(centroidCells);
centroids.Add((centroid, centroidCells.Count));
Debug.Log(DateTime.Now.ToString("o") + " - added centroids, " + roomCellsAdded.Count + " cells in view");
var loopPerCellInView = perCellInView.AsParallel().Where(x => centroids.Select(y => y.Item1).Contains(x.x) == false).Select(x => (x.x, x.Item2.Except(roomCellsAdded).ToList())).ToList();
Debug.Log(DateTime.Now.ToString("o") + " - excluded");
perCellInViewOrdered = loopPerCellInView.AsParallel().OrderByDescending(xxx => xxx.Item2.Count);
Debug.Log(DateTime.Now.ToString("o") + " - resorted");
}
}
public static List<(int, int)> FindInView((int,int) start, CellWalkableState[,] grid)
{
List<(int, int)> visible = new List<(int, int)>() { start };
bool alive = true;
int r = 1;
var length_x = grid.GetLength(0);
var length_y = grid.GetLength(1);
List<(int, int)> searched = new List<(int, int)>();
List<double> angles = new List<double>();
while(alive)
{
//alive = false;
int newR = r;
int count = CountFromR(newR);
var angleInc = 360.0 / count;
var rNexts = Enumerable.Repeat(1, count).ToArray();
for (int i = 0; i < count; i++)
{
var angle = angleInc * i;
if(angles.Contains(angle) == false)
{
angles.Add(angle);
float cos = Mathf.Cos((float)(Mathf.Deg2Rad * angle));
float sin = Mathf.Sin((float)(Mathf.Deg2Rad * angle));
var b = r;
var p = i % (r * 2);
var d = math.sqrt(math.pow(b, 2) + math.pow(p, 2));
var dScaled = d / r;
bool keepGoing = true;
while(keepGoing)
{
var rCur = dScaled * (rNexts[i]);
var loc = (start.Item1 + Mathf.RoundToInt(rCur * cos), start.Item2 + Mathf.RoundToInt(rCur * sin));
if (searched.Contains(loc) == false)
{
searched.Add(loc);
if (loc.Item1 >= 0 && loc.Item1 < length_x && loc.Item2 >= 0 && loc.Item2 < length_y)
{
if (grid[loc.Item1, loc.Item2] == CellWalkableState.Interactive || grid[loc.Item1, loc.Item2] == CellWalkableState.Walkable)
{
visible.Add(loc);
}
else
{
keepGoing = false;
}
}
else
{
keepGoing = false; // invalid, stop
}
}
else
{
if (visible.Contains(loc) == false)
{
keepGoing = false; // can stop, because we can't see past this place
}
}
if(keepGoing)
{
rNexts[i]++;
}
}
}
}
angles = angles.Distinct().ToList();
searched = searched.Distinct().ToList();
visible = visible.Distinct().ToList();
if(rNexts.All(x => x <= r))
{
alive = false;
}
else
{
r = rNexts.Max();
}
}
return visible;
}
static int CountFromR(int r)
{
return 8 * r;
}
The "short" summary of the code above is that each location first determines what cells around itself it can see. That becomes a list of tuples, List<((int,int), List<(int,int)>)>, where the first item is the location and the second is all cells it views. That main list is sorted by the count of the sublist, such that the item with the most cells-it-can-vew is first. That's added as a centroid, and all cells it can view are added to a second ("already handled") list. A modified "main list" is formed, with each sublist now excluding anything in the second list. It loops doing this until 90% of the cells have been added.
Some output:
2021-04-27T15:24:39.8678545-04:00 - Setting centroids for room with 7129 cells
2021-04-27T15:45:26.4418515-04:00 - got in view
2021-04-27T15:45:26.4578551-04:00 - sorted
2021-04-27T15:45:27.3168517-04:00 - added centroids, 4756 cells in view
2021-04-27T15:45:27.9868523-04:00 - excluded
2021-04-27T15:45:27.9868523-04:00 - resorted
2021-04-27T15:45:28.1058514-04:00 - added centroids, 6838 cells in view
2021-04-27T15:45:28.2513513-04:00 - excluded
2021-04-27T15:45:28.2513513-04:00 - resorted
2021-04-27T15:45:28.2523509-04:00 - Setting centroids for room with 20671 cells
This is just too slow for my purposes. Can anyone suggest alternate methods of doing this? For all of the cells essentially the only information one has is whether they're "open" or one can see through them or not (vs something like a wall).
An approximate solution with fixed integer slopes
For a big speedup (from quadratic in the number of rooms to linear), you could decide to check just a few integer slopes at each point. These are equivalence classes of visibility, i.e., if cell x can see cell y along such a line, and cell y can see cell z along the same line, then all 3 cells can see each other. Then you only need to compute each "visibility interval" along a particular line once, rather than per-cell.
You would probably want to check at least horizontal, vertical and both 45-degree diagonal slopes. For horizontal, start at cell (1, 1), and move right until you hit a wall, let's say at (5, 1). Then the cells (1, 1), (2, 1), (3, 1) and (4, 1) can all see each other along this slope, so although you started at (1, 1), there's no need to repeat the computation for the other 3 cells -- just add a copy of this list (or even a pointer to it, which is faster) to the visibility lists for all 4 cells. Keep heading right, and repeat the process as soon as you hit a non-wall. Then begin again on the next row.
Visibility checking for 45-degree diagonals is slightly harder, but not much, and checking for other slopes in which we advance 1 horizontally and some k vertically (or vice versa) is about the same. (Checking for for general slopes, like for every 2 steps right go up 3, is perhaps a bit trickier.)
Provided you use pointers rather than list copies, for a given slope, this approach spends amortised constant time per cell: Although finding the k horizontally-visible neighbours of some cell takes O(k) time, it means no further horizontal processing needs to be done for any of them. So if you check a constant number of slopes (e.g., the four I suggested), this is O(n) time overall to process n cells. In contrast, I think your current approach takes at least O(nq) time, where q is the average number of cells visible to a cell.

Getting specific random number

i want to generate random number between (1 to 6),is there any way to change the chance of geting number 6 more than other numbers?
for example for this code
private void pictureBox5_MouseClick(object sender, MouseEventArgs e)
{
Random u = new Random();
m = u.Next(1,6);
label2.Text = m.ToString();
}
Let p be probability of any 1..5 numbers and 1 - p is a probability of 6:
//DONE: do not recreate Random
private static Random s_Generator = new Random();
private void pictureBox5_MouseClick(object sender, MouseEventArgs e) {
const double p = 0.1; // each 1..5 has 0.1 probability, 6 - 0.5
// we have ranges [0..p); [p..2p); [2p..3p); [3p..4p); [4p..5p); [5p..1)
// all numbers 1..5 are equal, but the last one (6)
int value = (int) (s_Generator.NexDouble() / p) + 1;
if (value > 6)
value = 6;
label2.Text = value.ToString();
}
That wouldn't be random then. If you wanted to weight it so you would get 6 half the time, you could do this:
m = u.Next(1,2);
if(m == 2)
{
label2.Text = "6";
}
else
{
label2.Text = u.Next(1,5).ToString();
}
Based on what weighting you want you could change it-> 3 instead of 2 get a 33.33% weighting and so on. Otherwise, as the commenter said, you'd have to look into probability distributions for a more mathematically elegant solution.
Depends on how more likely. An easy way (but not very flexible) would be the following:
private void pictureBox5_MouseClick(object sender, MouseEventArgs e)
{
Random u = new Random();
m = u.Next(1,7);
if (m > 6) m = 6;
label2.Text = m.ToString();
}
If you want a totally random distribution of 1...5 and just a skwed 6, then Dmitry's seems best.
If you what to skew ALL the numbers, then try this:
Create a 100 element array.
Fill it with the number 1-6 in proportions based on how often you want the number to come up. (make 33 of them 6, if you want 6 to come up 1/3rd of the time. Four 4s means it'll only come up one in 25 rolls etc. 15 or 16 of each number will make it about evenly distributed, so adjust the counts from there)
Then pick a number from 0...99 and use the value in the element of the array.
You could define the possibility in a percentage of getting each of the numbers in an array:
/*static if applicable*/
int[] p = { (int)Math.Ceiling((double)100/6), (int)Math.Floor((double)100/6), (int)Math.Ceiling((double)100/6), (int)Math.Floor((double)100/6), (int)Math.Ceiling((double)100/6), (int)Math.Ceiling((double)100/6) };
////The array of probabilities for 1 through p.Length
Random rnd = new Random();
////The random number generator
int nextPercentIndex = rnd.Next() % 100; //I can't remember if it's length or count for arrays off the top of my head
////Convert the random number into a number between 0 and 100
int TempPercent = 0;
////A temporary container for comparisons between the percents
int returnVal = 0;
////The final value
for(int i = 0; i!= p.Length; i++){
TempPercent += p[i];
////Add the new percent to the temporary percent counter
if(nextPercentIndex <= TempPercent){
returnVal = i + 1;
////And... Here is your value
break;
}
}
Hope this helps.

Nested Loop function taking minuets to run, anyway to improve efficiency?

I knew the function would include a lot of data processing, but I didn't think it would end up taking minuets to process.
The function in question is fed a jagged 2D array which is made up of Paragraphs > Sentences this is made from a text file fed by the user so can be massive. It takes this array and compares every sentence to each other and saves a score between each sentence which is the number of common words.
This takes forever and I honestly didn't think it would.
My main test text is only 181 sentences long, but this translates to 32.7 thousand values scored in a 2D array.
This matrix of values is then used to calculate and select the most "relevant" sentences from each paragraph, and other things.
The 181 sentence text takes 1min 15seconds to process, a text of only 70 sentences takes 35 seconds, but this is based on number of sentences not words, but it gives you an idea. I dread to think how long it would take on an actual document.
The function in question:
protected void Intersection2DArray()
{
mainSentenceCoord = -1;
for (int x1 = 0; x1 < results.Length; x1++)
{
for (int x2 = 0; x2 < results[x1].Length; x2++)
{
var mainSentencesWords = wordSplit(results[x1][x2]);
secondarySentenceCoord = -1;
mainSentenceCoord++;
for (int y1 = 0; y1 < results.Length; y1++)
{
for (int y2 = 0; y2 < results[y1].Length; y2++)
{
var secondarySentencesWords = wordSplit(results[y1][y2]);
int commonElements = mainSentencesWords.Intersect(secondarySentencesWords).ToList().Count();
secondarySentenceCoord++;
intersectionArray[mainSentenceCoord, secondarySentenceCoord] = commonElements;
}
}
}
}
}
The wordSplit function:
protected List<String> wordSplit(string sentence)
{
var symbols = "£$€#&%+-.";
var punctuationsChars = Enumerable.Range(char.MinValue, char.MaxValue - char.MinValue)
.Select(i => (char)i)
.Where(c => char.IsPunctuation(c))
.Except(symbols)
.ToArray();
var words = sentence.Split(punctuationsChars)
.SelectMany(x => x.Split())
.Where(x => !(x.Length == 1 && symbols.Contains(x[0])))
.Distinct()
.ToList();
return words;
}
I initially wanted to do this split using one Regex line, but wouldn't figure it out, that may make it faster.
This loops through to select each sentence against each other, this is best I could come up. I'm fine with doing a total overall if it will drastically increase speed.
Edit: Using Moby Disk suggestion heres my new instant code:
Word Split function which is called once now and returns a List of List
public List<List<string>> createWordList()
{
List<List<string>> wordList = new List<List<string>>();
var symbols = "£$€#&%+-.";
var punctuationsChars = Enumerable.Range(char.MinValue, char.MaxValue - char.MinValue)
.Select(i => (char)i)
.Where(c => char.IsPunctuation(c))
.Except(symbols)
.ToArray();
for (int x1 = 0; x1 < results.Length; x1++)
{
for (int x2 = 0; x2 < results[x1].Length; x2++)
{
var words = results[x1][x2].Split(punctuationsChars)
.SelectMany(x => x.Split())
.Where(x => !(x.Length == 1 && symbols.Contains(x[0])))
.Distinct()
.ToList();
wordList.Add(words);
}
}
return wordList;
}
And the now super slim Intersection Function
protected void intersectionMatrix()
{
List<List<string>> wordList = createWordList();
mainSentenceCoord = -1;
for (var x = 0; x < wordList.Count; x++)
{
secondarySentenceCoord = -1;
mainSentenceCoord++;
for (var y = 0; y < wordList.Count; y++)
{
secondarySentenceCoord++;
intersectionArray[mainSentenceCoord, secondarySentenceCoord] = wordList[x].Intersect(wordList[y]).Count();
}
}
}
See update at the end:
There is some "low-hanging fruit" here that could speed it up a lot with out changing the algorithm itself:
wordSplit() recalculates "punctuationsChars" each time it is called. Do that once up front.
You are calling wordSplit() for the same sentence a N^2 number of times instead of N number of times since it is in both the outer (x1,x2) loop and the inner (y1,y2) loop. You only need to split 181 sentences, not 181^2 number of sentences. So instead, loop through results, call wordSplit once on each sentence, and store that result. If that takes up too much memory, look into memoization (http://en.wikipedia.org/wiki/Memoization) although I think you should be okay since it will only result in about 1 more copy of the text.
You don't need the ToList() after the Intersect(). That creates a list you don't use.
I'm confused as to the values of mainSentenceCoord and secondarySentenceCoord. What are the dimensions of the resulting intersectionArray?
OMG! #1 is it! That sped this up by a factor of 80x. Look at this line:
Enumerable.Range(char.MinValue, char.MaxValue - char.MinValue)
char.MaxValue is 65536. So if N=181, you are looping 181 x 181 x 65536! I just ran the profiler to confirm it: 98.4% of the CPU time is spent in the ToArray() calls in wordSplit.

2D Textbox array causes early for loop exit

I've been looking for a while and i couldn't find anything that would help me with my problem, but sorry if i missed something.
So for school we had to learn VB and make a game, and i chose to make Sudoku. I found VB easy to understand so i decided to try a different language to see if it was the same. C# was my choice. I decided to start off by making the Sudoku game again and compare it to my VB game.
In the VB code i was able to make an array of all the textboxes that make up the 9x9 grid from the code:
For Y = 0 to 8
For X = 0 to 8
Grid(X, Y) = New Windows.Forms.TextBox
Pencil(X, Y) = New Windows.Forms.TextBox
With Grid(X, Y)
.BackColor = Grid(X, Y).BackColor
.Name = Asc(97 + X) & Y + 1
.Location = New System.Drawing.Point(35 + 50 * X + (FindBox(X) - 1) * 15, 50 + 50 * Y + (FindBox(Y) - 1) * 15)
.Size = New System.Drawing.Size(50, 50)
.Multiline = True
.MaxLength = 1
.Font = New Font(Grid(X, Y).Font.Name, Grid(X, Y).Font.Size + 10)
.TextAlign = HorizontalAlignment.Center
.TabIndex = (X + 1) + (Y * 9) + 1
.BorderStyle = BorderStyle.FixedSingle
End With
Me.Controls.Add(Grid(X, Y))
next
next
This meant i could easily refer to the Sudoku textbox's as a grid coordinate in the array. I attempted to replicate this in C# and ran into a problem almost instantly
public partial class Form1 : Form
{
TextBox[,] Grid = new TextBox[8,8];
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
for (int Y = 0; Y < 9; Y++)
{
for (int X = 0; X < 9; X++)
{
TextBox TBox = new TextBox();
Grid[X, Y] = TBox;
TBox.Location = new Point(50 + X * 100, 50 + Y * 50);
this.Controls.Add(TBox);
}
}
}
This code runs, but for some reason it only runs till Y = 7, then stops and does not loop any more times. This code works fine until i try to add anything that links the textbox's to the array (In this case Grid[X,Y] = TBox). I've tried it without using TBox (And just straight away using the array, but the same problem persists).
Just wondering if anyone can enlighten me as to why adding the line "Grid[X, Y] = TBox;" can completely ruin a nested for loop.
Thanks in advance, sorry if i didn't say enough/Said too much.
There is an important difference between C# and VB.NET in the context of arrays. Just a simple example. In C# the following array has exactly 10 elements and allowed indexes are from 0 to 9:
int[] array= new int[10];
In VB.NET the following array has 11 elements and allowed indexes are from 0 to 10:
Dim array(10) as Integer
You translated you code from VB.NET to C# without taking this difference into account and it is why you have problems. To fix this problem you should use:
TextBox[,] Grid = new TextBox[9,9];
Instead of:
TextBox[,] Grid = new TextBox[8,8];
It doesn't just stop.You get a IndexOutOfRangeException
Change this
new TextBox[8,8]
to this
new TextBox[9,9]
Or make the for loops "< 8"

How to populate two separate arrays from one comma-delimited list?

I have a comma delimited text file that contains 20 digits separated by commas. These numbers represent earned points and possible points for ten different assignments. We're to use these to calculate a final score for the course.
Normally, I'd iterate through the numbers, creating two sums, divide and be done with it. However, our assignment dictates that we load the list of numbers into two arrays.
so this:
10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85
becomes this:
int[10] earned = {10,20,30,40,45,50,20,45,85};
int[10] possible = {10,20,35,50,50,50,20,90,85};
Right now, I'm using
for (x=0;x<10;x++)
{
earned[x] = scores[x*2]
poss [x] = scores[(x*2)+1]
}
which gives me the results I want, but seems excessively clunky.
Is there a better way?
The following should split each alternating item the list into the other two lists.
int[20] scores = {10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85};
int[10] earned;
int[10] possible;
int a = 0;
for(int x=0; x<10; x++)
{
earned[x] = scores[a++];
possible[x] = scores[a++];
}
You can use LINQ here:
var arrays = csv.Split(',')
.Select((v, index) => new {Value = int.Parse(v), Index = index})
.GroupBy(g => g.Index % 2,
g => g.Value,
(key, values) => values.ToArray())
.ToList();
and then
var earned = arrays[0];
var possible = arrays[1];
Get rid of the "magic" multiplications and illegible array index computations.
var earned = new List<int>();
var possible = new List<int>();
for (x=0; x<scores.Length; x += 2)
{
earned.Add(scores[x + 0]);
possible.Add(scores[x + 1]);
}
This has very little that would need a text comment. This is the gold standard for self-documenting code.
I initially thought the question was a C question because of all the incomprehensible indexing. It looked like pointer magic. It was too clever.
In my codebases I usually have an AsChunked extension available that splits a list into chunks of the given size.
var earned = new List<int>();
var possible = new List<int>();
foreach (var pair in scores.AsChunked(2)) {
earned.Add(pair[0]);
possible.Add(pair[1]);
}
Now the meaning of the code is apparent. The magic is gone.
Even shorter:
var pairs = scores.AsChunked(2);
var earned = pairs.Select(x => x[0]).ToArray();
var possible = pairs.Select(x => x[1]).ToArray();
I suppose you could do it like this:
int[] earned = new int[10];
int[] possible = new int[10];
int resultIndex = 0;
for (int i = 0; i < scores.Count; i = i + 2)
{
earned[resultIndex] = scores[i];
possible[resultIndex] = scores[i + 1];
resultIndex++;
}
You would have to be sure that an equal number of values are stored in scores.
I would leave your code as is. You are technically expressing very directly what your intent is, every 2nd element goes into each array.
The only way to improve that solution is to comment why you are multiplying. But I would expect someone to quickly recognize the trick, or easily reproduce what it is doing. Here is an excessive example of how to comment it. I wouldn't recommend using this directly.
for (x=0;x<10;x++)
{
//scores contains the elements inline one after the other
earned[x] = scores[x*2] //Get the even elements into earned
poss [x] = scores[(x*2)+1] //And the odd into poss
}
However if you really don't like the multiplication, you can track the scores index separately.
int i = 0;
for (int x = 0; x < 10; x++)
{
earned[x] = scores[i++];
poss [x] = scores[i++];
}
But I would probably prefer your version since it does not depend on the order of the operations.
var res = grades.Select((x, i) => new {x,i}).ToLookup(y=>y.i%2, y=>y.x)
int[] earned = res[0].ToArray();
int[] possible = res[1].ToArray();
This will group all grades into two buckets based on index, then you can just do ToArray if you need result in array form.
here is an example of my comment so you do not need to change the code regardless of the list size:
ArrayList Test = new ArrayList { "10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85" };
int[] earned = new int[Test.Count / 2];
int[] Score = new int[Test.Count / 2];
int Counter = 1; // start at one so earned is the first array entered in to
foreach (string TestRow in Test)
{
if (Counter % 2 != 0) // is the counter even
{
int nextNumber = 0;
for (int i = 0; i < Score.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(Score[i])))
{
nextNumber = i;
break;
}
}
Score[nextNumber] = Convert.ToInt32(TestRow);
}
else
{
int nextNumber = 0;
for (int i = 0; i < earned.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(earned[i])))
{
nextNumber = i;
break;
}
}
earned[nextNumber] = Convert.ToInt32(TestRow);
}
Counter++
}

Categories