I am trying to make a practice program that calculates a persons overall grade. The test consists of three parts each with different weightings. Part 1 is worth 5 grades, part two 3 grades and part 3 is worth 2 grades.
So if a person got A B C they would receive 5A 3B and 2C.
Now, in order to receive and A/B/C overall requires a certain amount of each grade. For example in order to receive an A overall you need to have at least 5A's and 7 of the grades must be B or higher and all the grades need to be C or or higher.
B, C, D etc all have their own requirements too.
What is the best way to code this as at the moment I am using a counter for each grade and then doing if/else if statements to check the amount of each grade a person has got like so:
if (aGradeCount >= 5)
{
//Add total grade
}
}
}
//To receive a B
if(bGradeCount >= 3 && aGradeCount <5 && cGradeCount >=2)
{
if(bGradeCount + cGradeCount +dGradeCount + aGradeCount>= 7)
{
if(dGradeCount <= 3)
{
//Add new total grade
}
}
}
Now I understand this is terrible practice, but how can I code this better? Using a switch statement? If so how do I go about doing that?
how can I code this better?
Write a specification. Then for every concept mentioned in the specification, write a method. Here's part of a specification; you already wrote it:
in order to receive an A overall you need to have at least 5 A's and least 7 of the grades must be B or higher and all the grades need to be C or better.
Break it down
in order to receive an A overall
at least 5 A's AND
at least 7 of the grades must be B or higher AND
all the grades need to be C or better
OK, now we can start turning that into a method:
static bool QualifiesForA(int aCount, int bCount, int cCount, int dCount)
{
// In order to receive an A overall we require:
// TODO: at least 5 A's AND
// TODO: at least 7 of the grades must be B or higher AND
// TODO: all the grades need to be C or better
// If these conditions are not met then an A is not earned.
return false;
}
All right we have turned our specification into code. Wrong code, but code. Let's keep going. We have a line of a specification. Write a method:
static bool AtLeastFiveA(int aCount)
{
return aCount >= 5;
}
Hey, that was a correct method. We are making progress. Now use it:
static bool QualifiesForA(int aCount, int bCount, int cCount, int dCount)
{
// In order to receive an A overall we require:
// at least 5 A's AND
// TODO: at least 7 of the grades must be B or higher AND
// TODO: all the grades need to be C or better
bool atLeast5A = AtLeastFiveA(aCount);
// If these conditions are not met then an A is not earned.
return false;
}
Now we have another problem. At least 7 are B or higher. OK, write a method:
static bool AtLeastSevenB(int aCount, int bCount)
{
return aCount + bCount >= 7;
}
Another correct method! Use it!
static bool QualifiesForA(int aCount, int bCount, int cCount, int dCount)
{
// In order to receive an A overall we require:
// at least 5 A's AND
// at least 7 of the grades must be B or higher AND
// TODO: all the grades need to be C or better
bool atLeast5A = AtLeastFiveA(aCount);
bool atLeast7B = AtLeastSevenB(aCount, bCount);
// If these conditions are not met then an A is not earned.
return false;
}
Now we need the last bit:
static bool NoD(int dCount)
{
return dCount == 0;
}
Put it together.
static bool QualifiesForA(int aCount, int bCount, int cCount, int dCount)
{
// In order to receive an A overall we require:
// at least 5 A's AND
// at least 7 of the grades must be B or higher AND
// all the grades need to be C or better
bool atLeast5A = AtLeastFiveA(aCount);
bool atLeast7B = AtLeastSevenB(aCount, bCount);
bool noD = NoD(dCount);
if (atLeast5A && atLeast7B && noD)
return true;
// If these conditions are not met then an A is not earned.
return false;
}
Now, the question to ask yourself is:
Is this code correct? GET IT CORRECT FIRST. This code is very verbose but I'll tell you right now, it exactly matches the specification you gave.
Once the code is correct, can we make it more clear?
Yes; we could for instance say:
static bool QualifiesForA(int aCount, int bCount, int cCount, int dCount)
{
// In order to receive an A overall we require:
// at least 5 A's AND
// at least 7 of the grades must be B or higher AND
// all the grades need to be C or better
bool atLeast5A = AtLeastFiveA(aCount);
bool atLeast7B = AtLeastSevenB(aCount, bCount);
bool noD = NoD(dCount);
return atLeast5A && atLeast7B && noD;
}
And now maybe you will say, you know, some of these methods are unnecessary abstractions, maybe I can just replace them with their bodies:
static bool QualifiesForA(int aCount, int bCount, int cCount, int dCount)
{
// In order to receive an A overall we require:
// at least 5 A's AND
// at least 7 of the grades must be B or higher AND
// all the grades need to be C or better
bool atLeast5A = aCount >= 5;
bool atLeast7B = aCount + bCount >= 7;
bool noD = dCount == 0;
return atLeast5A && atLeast7B && noD;
}
The point is: we start from a very verbose, CLEARLY CORRECT program, and then we make small, simple, clearly correct transformations to make it more concise. When you think you have a good balance of concision and readability, stop.
OK, now you have solved the problem of "did we earn an A?" Now you do "did we earn a B?" and so on. Write a specification for every part, and then write code that clearly implements the specification.
This sounds like a heavyweight process, but this will pay huge dividends as you learn how to program. Your code will be better organized, it will be less buggy, it will be easier to read and understand and modify.
The point of this technique is to focus on obvious correctness of every part. Always concentrate on obvious correctness. A program which is correct but you cannot tell it is correct is a program that might not be correct! Always concentrate on correctness first. Making a wrong program more elegant, or faster, or more feature complete means that you have an elegant, fast, rich-featured bug farm.
Summing up:
Write clear specifications.
Write code to clearly match the spec.
Tiny methods are A-OK. You can always eliminate them later.
Correctness is more important than everything else; make the code better once you know it is correct.
I don't know if that's terrible practice. It's a little unnecessary, since there's nothing else in the block, but another if statement. You can use more && operators and parenthesis if you just want to use one if statement.
if ((bGradeCount >= 3 && aGradeCount <5 && cGradeCount >=2) &&
(bGradeCount + cGradeCount +dGradeCount + aGradeCount>= 7) &&
(dGradeCount <= 3))
{
char b = 'B';
person.TotalGrade = b.ToString();
}
For code clarity I would do this that way :
//Main function
{
///code
if(MethodWhichDescribesLogic(aGradeCOunt,bGradeCount,cGradeCount,dGradeCount){
char b = 'B';
person.TotalGrade = b.ToString();
}
}
Then in some place else :
bool MethodWhichDescribesLogic(type aGradeCount, type bGradeCount, type cGradeCount, type dGradeCount){
return
(PassingGrade(bGradeCount,aGradeCount,cGradeCount) &&
GoodGradesType(bGradeCount,cGradeCount,dGradeCount,aGradeCount) &&
dGradeCount <= 3);
}
bool PassingGradesCount(type bGradeCount,type aGradeCount,type cGradeCount)
{
return bGradeCount >= 3 && aGradeCount <5 && cGradeCount >=2;
}
bool GoodGradesCount(type cGradeCount,type bGradeCount,type aGradeCount,type dGradeCount)
{
return bGradeCount + cGradeCount +dGradeCount + aGradeCount>= 7;
}
Remember that every if-else switches can be replaced by conditional table.
So if overall grades count would be like 10. It could be
A B C Overall
5 7 10 A
4 7 10 B
Then you make array of it and find where you are in the array.
For example (I admit that I'm puzzled by your example so I might get it wrong here.):
var grades = new[]{
new { A = 5. B = 7, C = 10, Overall = "A"},
new { A = 4, B = 7, C = 10, Overall = "B"},
...
}
var myGrade = grades.FirstOrDefault(g => myA >= g.A && myB >= g.B && enoughC)
With proper formatting it looks much better than tons of if's. And you always have your choice table in front of you.
Firstly, make use of Boolean Algebra in the OverallGrade() to see which cases you don't need to consider. For example, if for checking an A grade you have already seen that gradeDistribution.A >= 5, don't test gradeDistribution.A < 5 when testing for grade 'B' as it is obvious that if you are testing the case for B, you have already tested gradeDistribution.A < 5 as true.
Next, put the grade calculation in another method and make that method return as early as possible.
Finally, in order to get the overall grade, you may write the method as:
private static char OverallGrade(int partA, int partB, int partC)
{
//Now in this method, check one by one which
//overall grade the provided values fall in
//Call another method to get count of individual As, Bs, Cs etc
var gradeDistribution = GetIndividualCount(partA, partB, partC);
//Now, first check for A and return immediately if true
if (gradeDistribution.A >= 5) return 'A';
//Now, check for B and again return if the values satisfy for B
if (gradeDistribution.B >=3
&& gradeDistribution.C <= 2
&& gradeDistribution.D <= 3
&& ...)
return 'B';
//Keep adding for each case and return as soon as you find the grade.
}
Now, the class GradeDistribution, whose variable we have used above can be created which will hold count of each grade:
public class GradeDistribution
{
public int A; //Count for Grade A
public int B; //Count for Grade B
public int C; //Count for Grade C
public int D; //Count for Grade D
}
The above code is an example of introducing a class for an entity that does not exist in real world.
Next, GetIndividualCount() can be written as:
private static GradeDistribution GetIndividualCount(int partA, int partB, int partC)
{
var gradeDistribution = new GradeDistribution();
/*
Calculate and assign values to gradeDistribution.A, gradeDistribution.B...
*/
return gradeDistribution;
}
Related
I am very VERY new to all things coding, and I have run into a bit of trouble. I have this method that generates a random number between 1 - 6 (its supposed to simulate a dice roll), and what I want to do is through an input enable the user to change how many sides the dices has (The second parameter of the int nr in this case).
I want to be able to change the 7 to any number with a piece of code that is outside the method itself.
I can not wrap my head around how I am supposed to accomplish this.
static int RullaTärning(Random slumpObjekt)
{
int nr = slumpObjekt.Next(1, 7);
return nr;
}
Any and all help is appreciated, and if there is some piece of information missing that is needed to solve this, please let me know.
Thanks
I've tried changing the 7 to an integer variable "sides", but I don't know how to change this integer variable later on in the program.
I have tried a load of random stuff without much thought, but I don't know how to accomplish this at all. I really am lost.
You managed to realize that you need to somehow make the second param to Next method variable. Great! Let's do it:
static int RullaTärning(Random slumpObjekt)
{
int sides = 6;
int nr = slumpObjekt.Next(1, sides +1);
return nr;
}
now all you need to do is get the value from the user instead of hardcode it:
static int RullaTärning(Random slumpObjekt, int sides)
{
int nr = slumpObjekt.Next(1, sides+1);
return nr;
}
If you do not want to force the user to choose a side-count (most dice would be 6-sided) you can work with a default:
static int RullaTärning(Random slumpObjekt, int sides = 6)
{
int nr = slumpObjekt.Next(1, sides+1);
return nr;
}
// Can be used as
int roll = RullaTärning(rnd); // no second param => 6 will be used
// or
int damage = RullaTärning(rnd, 16);
I would always encourage to check your inputs:
static int RullaTärning(Random slumpObjekt, int sides = 6)
{
// Assuming we had a requirement to allow no less than 6 sides.
if( sides < 6 ) throw new ArgumentException("Dice need at least 6 sides.", nameof(sides));
// Mind that I did not check `slumpObjekt` for null,
// because a NullReferenceException will be thrown anyway
// on the attempt to call `Next` on `null`.
int nr = slumpObjekt.Next(1, sides+1);
return nr;
}
// because there is always "that guy" who will try
int nonsense = RullaTärning(rnd, -5);
... and a really pedantic fellow just made me mention clean code and "no magic numbers" ...
const int MINIMAL_ALLOWED_SIDES = 6; // Now we know why "6" and
// we only need to change 1 place in code
// if requirement of minimal allowed sides
// changes
static int RullaTärning(Random slumpObjekt, int sides = MINIMAL_ALLOWED_SIDES)
{
if( sides < MINIMAL_ALLOWED_SIDES) throw new ArgumentException($"Dice need at least {MINIMAL_ALLOWED_SIDES} sides.", nameof(sides));
int nr = slumpObjekt.Next(1, sides+1);
return nr;
}
Hope this works for you this is simple method. Please go through basics
static int RullaTärning(Random slumpObjekt, int numberOfFaces){
int nr = slumpObjekt.Next(1, numberOfFaces+1);
return nr;
}
I am creating a Dungeons and Dragons Character Creator. There is a randomize feature that is going to create a complete character sheet. There is a part that I have gotten to and I am not quite sure the best way to proceed.
The way I have the racial modifiers set up is with if statements. Here is an example.
if (raceInt == 0 || raceInt == 2 || raceInt == 10)
{
raceStrMod = 2;
}
if (raceInt == 3 || raceInt == 4 || raceInt == 5 || raceInt == 11 || raceInt == 12)
{
raceDexMod = 2;
}
However there are races that have modifiers that let you select two stats to add a modifier to, such as Strength or Dexterity. What would be the best way to select two random ints for just those races?
For example, the half-elf race which would get +2 to Dex and then +1 to two other random stats. So I need to find a way to randomly select two of the remaining ints to make the value = 1.
My race mod ints are initialized as
int raceStrMod = 0;
int raceDexMod = 0;
int raceConMod = 0;
int raceIntMod = 0;
int raceWisMod = 0;
int raceChaMod = 0;
Then the if statements assign a value dependent on which race was randomly selected.
Thank you all for the input! This is how I ended up coding it
if (raceInt == 9)
{
int randomX = rnd.Next(1, 5);
int randomY = rnd.Next(1, 5);
int attempts = 0;
while (randomX == randomY && attempts < 10)
{
randomY = rnd.Next(1, 5);
attempts++;
}
//if they are still not unique after 10 attempts
if (randomX == randomY)
{
if (randomX == 5)
randomY = 1;
else
randomY = randomX + 1;
}
int[] randomNumbers = { randomX, randomY };
foreach (int i in randomNumbers)
{
switch (i)
{
case 1:
raceStrMod = 1;
break;
case 2:
raceDexMod = 1;
break;
case 3:
raceConMod = 1;
break;
case 4:
raceIntMod = 1;
break;
case 5:
raceWisMod = 1;
break;
}
}
}
Has your class introduced you to enum types yet? If not, is there any restriction on your final project with respect to using language features that weren't taught in the class?
Your question is arguably too broad, as there are many different ways to address this sort of thing even in real-world code, and the classroom context introduces potential roadblocks that while might constrain the question, being unknown they make it impossible to know what answer is actually going to work for you.
That said…
Ignoring the classroom aspect and focusing only on the problem itself, I would use enum types and dictionaries for this sort of thing. For example:
enum Attribute
{
Strength,
Dexterity,
Constitution,
Charisma,
Intelligence,
Wisdom,
Count, // must always be last
}
Dictionary<Attribute, int> modifiers = new Dictionary<Attribute, int>();
Then you can pick a random attribute like (assuming you have a random variable referencing a Random object…don't make the classic newbie mistake of creating a new Random object every time you want to pick a new random number):
Attribute attributeToModify = (Attribute)random.Next((int)Attribute.Count);
And you can store that selection like:
modifiers[attributeToModify] = 1;
This can be used to store however many modifiers you like. You can encapsulate that in an object representing the character itself, or you could put it into a separate AttributeModifiers class. One advantage of doing the latter would be that if you have modifiers that come from different sources, you can track that in the character object as a list of AttributeModifier instances, each in turn keeping track of what the actual source of those modifiers are.
This just barely scratches the surface. As I noted, the question itself is fairly broad. But I strongly recommend using the available language features to ensure that your variables represent things in a type-specific way, rather than just using int values for things that aren't really integers, and to use collection classes that more correctly represent the semantics of what your code is intended to do.
Note that this also means you probably should have an enum type for the races. E.g.:
enum Race
{
Dwarf,
Elf,
HalfElf,
Halfling,
HalfOrc,
Human,
// etc.
}
And your chain of if statements is probably better represented as a switch:
Attribute racialMod;
switch (race)
{
case Human:
case Halfling:
// etc.
racialMod = Attribute.Strength;
break;
case Elf:
case HalfElf:
// etc.
racialMod = Attribute.Dexterity;
break;
}
modifiers[racialMod] = 2;
Something like that. The point is to make sure the code reads more like what the original specification would say (if you actually had written one). This will make the code easier to understand, and it will be less likely for you to put bugs in the code (e.g. you accidentally type the wrong magic, unnamed integer).
I am creating a Dungeons and Dragons Character Creator.
That's a fun beginner project; I did the same when I was learning to program.
I need to find a way to randomly select two of the remaining...
You need to find two distinct values, call then x and y. The solution you've arrived at is:
Generate x
Try to generate y ten times
If no attempt succeeded to find a distinct y, hard-code a choice.
That works, and you almost never have to use the hard-coded choice. But I thought you might be interested to know that there is an easier way to generate two distinct numbers. Let's suppose we want two distinct numbers from 0, 1, 2, 3 or 4. (Obviously if you want a different range, say, 1 through 5, you can solve that problem by generating two distinct numbers 0->4 and then adding one to each.)
The improved algorithm is:
Choose x between 0 and 4 as usual.
Choose n between 1 and 4.
y = (x + n) % 5;
Think about it this way. Suppose we make a list like this:
0, 1, 2, 3, 4, 0, 1, 2, 3
We randomly choose x from the first five entries on the list, and then we choose y by stepping forwards between 1 and 4 steps. Since the list does not repeat in one to four steps, we know that we'll get two unique elements. The math does the equivalent of that.
You could similarly have used % in your program:
if (randomX == 5)
randomY = 1;
else
randomY = randomX + 1;
could be written
randomY = randomX % 5 + 1
If you're unfamiliar with %, it is the remainder operator. It is the complement of the / operator. The rule is:
int x = whatever;
int y = whatever;
int r = x % y;
is the same as:
int r = x - (x / y) * y;
That is, it is the remainder when x is divided by y. Keep in mind that the remainder can be negative!
(Disclaimer: I don't love this option, but couldn't think of another way other than reflection which is even nastier)
You could define a class that masks the fact that all of the mods are stored as an array and therefore can be indexed using a random number.
Something like the following:
public class StatMods
{
public int RaceStrMod { get { return this.mods[0]; } set { this.mods[0] = value; } }
public int RaceDexMod { get { return this.mods[1]; } set { this.mods[1] = value; } }
public int RaceConMod { get { return this.mods[2]; } set { this.mods[2] = value; } }
public int RaceIntMod { get { return this.mods[3]; } set { this.mods[3] = value; } }
public int RaceWisMod { get { return this.mods[4]; } set { this.mods[4] = value; } }
public int RaceChaMod { get { return this.mods[5]; } set { this.mods[5] = value; } }
private readonly int[] mods;
private static readonly Random rand = new Random();
public StatMods()
{
this.mods = new int[6];
}
public void ApplyRandomMod(int modification)
{
this.mods[rand.Next(0, 6)] += modification;
}
}
I always struggle with these types of algorithms. I have a scenario where I have a cubic value for freight and need to split this value into cartons of different sizes, there are 3 sizes available in this instance, 0.12m3, 0.09m3 and 0.05m3. A few examples;
Assume total m3 is 0.16m3, I need to consume this value into the appropriate cartons.
I will have 1 carton of 0.12m3, this leave 0.04m3 to consume. This fits into the 0.05m3 so therefore I will have 1 carton of 0.05m, consumption is now complete. Final answer is 1 x 0.12m3 and 1 x 0.05m3.
Assume total m3 is 0.32m3, I would end up with 2 x 0.12m3 and 1 x 0.09m3.
I would prefer something either in c# or SQL that would easily return to me the results.
Many thanks for any help.
Cheers
I wrote an algorithm that may be a little messy but I do think it works. Your problem statement isn't 100% unambiguous, so this solution is assuming you want to pick containers so that you minimize the remaining space, when started filling from the largest container.
// List of cartons
var cartons = new List<double>
{
0.12,
0.09,
0.05
};
// Amount of stuff that you want to put into cartons
var stuff = 0.32;
var distribution = new Dictionary<double, int>();
// For this algorithm, I want to sort by descending first.
cartons = cartons.OrderByDescending(x => x).ToList();
foreach (var carton in cartons)
{
var count = 0;
while (stuff >= 0)
{
if (stuff >= carton)
{
// If the amount of stuff bigger than the carton size, we use carton size, then update stuff
count++;
stuff = stuff - carton;
distribution.CreateNewOrUpdateExisting(carton, 1);
}
else
{
// Otherwise, among remaining cartons we pick the ones that will have empty space if the remaining stuff is put in
var partial = cartons.Where(x => x - stuff >= 0 && x != carton);
if (partial != null && partial.Count() > 0)
{
var min = partial.Min();
if (min > 0)
{
distribution.CreateNewOrUpdateExisting(min, 1);
stuff = stuff - min;
}
}
else
{
break;
}
}
}
There' an accompanying extension method, which either adds an item to a dictionary, or if the Key exists, then increments the Value.
public static class DictionaryExtensions
{
public static void CreateNewOrUpdateExisting(this IDictionary<double, int> map, double key, int value)
{
if (map.ContainsKey(key))
{
map[key]++;
}
else
{
map.Add(key, value);
}
}
}
EDIT
Found a bug in the case where initial stuff is smaller than the largest container, so code updated to fix it.
NOTE
This may still not be a 100% foolproof algorithm as I haven't tested extensively. But it should give you an idea on how to proceed.
EDIT EDIT
Changing the condition to while (stuff > 0) should fix the bug mentioned in the comments.
I'm trying to solve a problem on code wars and the unit tests provided make absolutely no sense...
The problem is as follows and sounds absolutely simple enough to have something working in 5 minutes
Consider a sequence u where u is defined as follows:
The number u(0) = 1 is the first one in u.
For each x in u, then y = 2 * x + 1 and z = 3 * x + 1 must be in u too.
There are no other numbers in u.
Ex: u = [1, 3, 4, 7, 9, 10, 13, 15, 19, 21, 22, 27, ...]
1 gives 3 and 4, then 3 gives 7 and 10, 4 gives 9 and 13, then 7 gives 15 and 22 and so on...
Task:
Given parameter n the function dbl_linear (or dblLinear...) returns the element u(n) of the ordered (with <) sequence u.
Example:
dbl_linear(10) should return 22
At first I used a sortedset with a linq query as I didnt really care about efficiency, I quickly learned that this operation will have to calculate to ranges where n could equal ~100000 in under 12 seconds.
So this abomination was born, then butchered time and time again since a for loop would generate issues for some reason. It was then "upgraded" to a while loop which gave slightly more passed unit tests ( 4 -> 8 ).
public class DoubleLinear {
public static int DblLinear(int n) {
ListSet<int> table = new ListSet<int> {1};
for (int i = 0; i < n; i++) {
table.Put(Y(table[i]));
table.Put(Z(table[i]));
}
table.Sort();
return table[n];
}
private static int Y(int y) {
return 2 * y + 1;
}
private static int Z(int z) {
return 3 * z + 1;
}
}
public class ListSet<T> : List<T> {
public void Put(T item) {
if (!this.Contains(item))
this.Add(item);
}
}
With this code it still fails the calculation in excess of n = 75000, but passes up to 8 tests.
I've checked if other people have passed this, and they have. However, i cannot check what they wrote to learn from it.
Can anyone provide insight to what could be wrong here? I'm sure the answer is blatantly obvious and I'm being dumb.
Also is using a custom list in this way a bad idea? is there a better way?
ListSet is slow for sorting, and you constantly get memory reallocation as you build the set. I would start by allocating the table in its full size first, though honestly I would also tell you using a barebones array of the size you need is best for performance.
If you know you need n = 75,000+, allocate a ListSet (or an ARRAY!) of that size. If the unit tests start taking you into the stratosphere, there is a binary segmentation technique we can discuss, but that's a bit involved and logically tougher to build.
I don't see anything logically wrong with the code. The numbers it generates are correct from where I'm standing.
EDIT: Since you know 3n+1 > 2n+1, you only ever have to maintain 6 values:
Target index in u
Current index in u
Current x for y
Current x for z
Current val for y
Current val for z
public static int DblLinear(int target) {
uint index = 1;
uint ind_y = 1;
uint ind_z = 1;
uint val_y = 3;
uint val_z = 4;
if(target < 1)
return 1;
while(index < target) {
if(val_y < val_z) {
ind_y++;
val_y = 2*ind_y + 1;
} else {
ind_z++;
val_z = 3*ind_z + 1;
}
index++;
}
return (val_y < val_z) ? val_y : val_z;
}
You could modify the val_y if to be a while loop (more efficient critical path) if you either widen the branch to 2 conditions or implement a backstep loop for when you blow past your target index.
No memory allocation will definitely speed your calculations up, even f people want to (incorrectly) belly ache about branch prediction in such an easily predictable case.
Also, did you turn optimization on in your Visual Studio project? If you're submitting a binary and not a code file, then that can also shave quite a bit of time.
This code works properly well but it doesn't have a memory for storing user input.
I also need to categorize the grades into its respective columns in array format, having "S/N, Category and Count" and I dont know how to go about it. Any help would be appreciated.
namespace ExamScore
{
class YourExamScore
{
private static string GetGrade(int examScore)
{
if (examScore >= 90 && examScore <= 100)
return "Excellent";
if (examScore >= 70 && examScore <= 89)
return "Good";
if (examScore >= 50 && examScore <= 69)
return "Satisfactory";
if (examScore >= 0 && examScore <= 49)
return "Unsatisfactory";
return "Invalid";
}
static void Main(string[] args)
{
// Print a greeting message. After all, why not?
Console.WriteLine("Welcome to ExamScore Calculator!");
Console.WriteLine("Input Your Exam Score...");
Console.WriteLine("Press -2 when you have inputed all scores");
while (true)
{
var examScore = Convert.ToInt32(Console.ReadLine());
if (examScore == -2)
{
break;
}
var grade = GetGrade(examScore);
Console.WriteLine(grade);
}
Console.WriteLine("\n\nProccessing Scores... Please Wait...");
Console.ReadLine();
}
}
}
Well it seems like a homework, so i am not going to help with codes directly,
for the storage of marks, you can follow either of one way
if you know how many input you are going to use, then use a fixed size array
var marks = new int[10];
if the no. of input is not fixed then you can use List<int>
var marks = new List();
I am not sure if I understood you correctly, but I think you are asking to keep track of the amount of grades that fall in a certain category. In that case, I would do it as follows.
You will need to keep track of an array of ints (int[]) with an entry for each category that keeps track of the amount of grades in that category so far. So you will first have to make a static array of size 4, which will automatically initialize with zeroes everywhere. Semantically, each index stands for a category (say 0 for excellent, 1 for good, etc.). Then, in the GetGrade method you should for each category also increase the right category before returning.
To store user input in memory you can use array or list (List<MyData> is better if you have to insert often, Hashtable if you want to search fast, MyData[] - least memory and fastest access by index, etc.)
After entering -2 you may want to store (serialize) your data somehow, so data will persists between runs. It can be xml-file (XmlSerializer), own format text-/binary- file, database, etc.
You also need to define how you will work with data: are they saved immediately after entering (then database itself is a memory storage) or upon exit, delete/correct possibility and such.
Finally, you can add statistic and reporting, which query your data and produce results.
It is a bit unclear what
"S/N, Category and Count"
stands for. Could you elaborate? Your code is asking for score and producing grade. You could count how many users have which grade, how many total users, how many grades (all those are "Counts"), but "S/N" and "Category" are confusing.
To example, you only want to print how many users belongs different grades.
1) Very manual approach, count occurrences (statistic) upon entering (use)
int grade90plus; // fields
int grade70plus;
...
// inside GetGrade
if(examScore > 90)
{
grade90plus++;
return "Excellent";
}
else
if(examScore > 70)
{
grade70plus++;
return "Good";
}
...
// report
Console.WriteLine("Excellent: " + grade90plus);
Console.WriteLine("Good: " + grade90plus);
2) High tec (serialization unfriendly)
public class Grade
{
public string Name;
public int Count;
public int ScoreMin;
public int ScoreMax;
public bool Test(int score) { return score >= ScoreMin && score <= ScoreMax; }
public static readonly Grade Excellent = new Grade() {Name = "Excellent", ScoreMin = 90, ScoreMax = 100};
public static readonly Grade Good = new Grade() {Name = "Good", ScoreMin = 70, ScoreMax = 89};
public static readonly Grade[] GetAll = new Grade[] { Excellent, Good };
}
private static string GetGrade(int examScore)
{
foreach(var grade in Grade.GetAll)
if(grade.Test)
{
grade.Count++;
return grade.Name;
}
return "Invalid";
}
// report
foreach(var grade in Grade.GetAll)
Console.WriteLine(grade.Name + "," + grade.Count);
You can surely improve those or make own solution (serialization friendly, utilizing database, one what uses expressions for Test, etc).