Hello I'm working on this math game and for my last scene I did what feels like a lot of repetitive code but I'm not sure if there's a way to simplify it, I have linked below so maybe some more seasoned programmers might have some more elegant solutions!
For example, Im trying to generate every permutation of something like (a[]b)²[]c[]d where the brackets will be replaced by +,-,*, or /. What I have been doing is just creating random percent if statements to pick a specific version like "(a+b)²/c-d" Is there possibly a less "brute-force" and readable approach then what I have been doing?
if(UnityEngine.Random.Range(0,101)>50){
// 50% of being (a+b)²(c)+d
if(UnityEngine.Random.Range(0,101)>50){
ans = ((int) Mathf.Pow((float) a+ (float) b, 2))*c+d;
input.text = "("+a+"+"+b+")"+"²"+"("+c+")"+"+"+d+"=";
Debug.Log("Problem ID: 78");
// 50% of being (a+b)²(c)-d
} else {
ans = ((int) Mathf.Pow((float) a+ (float) b, 2))*c-d;
input.text = "("+a+"+"+b+")"+"²"+"("+c+")"+"-"+d+"=";
Debug.Log("Problem ID: 79");
}
// 50% of being (a-b)²(c)[]d
} else {
// 50% of being (a-b)²(c)+d
if(UnityEngine.Random.Range(0,101)>50){
ans = ((int) Mathf.Pow((float) a- (float) b, 2))*c+d;
input.text = "("+a+"-"+b+")"+"²"+"("+c+")"+"+"+d+"=";
Debug.Log("Problem ID: 80");
// 50% of being (a-b)²(c)-d
} else {
ans = ((int) Mathf.Pow((float) a- (float) b, 2))*c-d;
input.text = "("+a+"-"+b+")"+"²"+"("+c+")"+"-"+d+"=";
Debug.Log("Problem ID: 81");
}
(Pastebin below for more context)
https://pastebin.pl/view/d1bfb99e
I applaud your desire to make your code more readable. The basic idea is to split (a) defining, (b) choosing and (c) applying your operators.
Step 1: You define Operators. Each Operator combines both a mathematical operation (e.g. Add would be (a, b) => a + b) and a symbol (e.g. Add would be "+").
class Operator
{
public Func<int, int, int> Calculate { get; }
public string Symbol { get; }
public Operator(Func<int, int, int> calculate, string symbol)
{
Calculate = calculate;
Symbol = symbol;
}
}
private Operator Add = new Operator((a, b) => (a + b), "+");
private Operator Subtract = new Operator((a, b) => (a - b), "-");
Step 2: Then you randomly choose your operators (I used System.Random, since I'm not familiar with Unity, but feel free to replace it with the random number generator of your choice):
var rnd = new Random();
private (Operator op1, Operator op2, int problemId) RandomlyChooseProblem()
{
switch (rnd.Next(4))
{
case 0: return (Add, Add, 78);
case 1: return (Add, Subtract, 79);
case 2: return (Subtract, Add, 80);
case 3: return (Subtract, Subtract, 81);
default: throw new InvalidOperationException("This should not happen.");
}
}
Step 3: You apply them:
var (op1, op2, problemId) = RandomlyChooseProblem();
ans = op2.Calculate((int)Math.Pow(op1.Calculate(a, b), 2) * c, d);
input.text = $"(a{op1.Symbol}b)²*c{op2.Symbol}d");
Debug.Log($"Problem ID: {problemId}");
Adding a new operator (e.g. Multiply) or a new problem variant (e.g. (Add, Multiply, 82)) is now just a single line of code.
Break the calculation into parts - a±b, square * c and ±d. Calculate them separately, and multiply them to get the final result. For the text, you can use string interpolation
float ans;
string operator1;
string operator2;
if (UnityEngine.Random.Range(0,101)>50) {
ans = (float) a + (float) b;
operator1 = "+";
} else {
ans = (float) a - (float) b;
operator1 = "-";
}
ans = (int)(ans * ans) * c;
if (UnityEngine.Random.Range(0,101)>50) {
ans += d;
operator2 = "+";
} else {
ans -= d;
operator2 = "-";
}
input.text = $"(a{operator1}b)²(c){operator2}d";
Also note that UnityEngine.Random.Range(0,101) > 50 is not exactly 50% probability. You probably meant UnityEngine.Random.Range(1,101) > 50 instead, but I would just use UnityEngine.Random.Range(0,2) == 0.
The problem IDs can be flattened by generating 2 random bits, and adding the 2-bit number encoded by those bits to 78:
float ans;
string operator1;
string operator2;
int random1 = UnityEngine.Random.Range(0,2);
int random2 = UnityEngine.Random.Range(0,2);
if (random1 == 0) {
ans = (float) a + (float) b;
operator1 = "+";
} else {
ans = (float) a - (float) b;
operator1 = "-";
}
ans = (int)(ans * ans) * c;
if (random2 == 0) {
ans += d;
operator2 = "+";
} else {
ans -= d;
operator2 = "-";
}
int problemID = 78 + random1 + random2 * 2;
Debug.Log($"Problem ID: {problemID}");
input.text = $"(a{operator1}b)²(c){operator2}d";
This trick is not particularly readable IMO though.
Related
i want to get two floats from this string
string sample = "#G10F30";
( G and F can be inverted )
and store their value into the following variables
float g;
float f;
since i'll repeat this for like 100 times i'm looking to get is as fast as possible.
i can iterate through all the characters and store just the digits into a two different strings and then parse their values into a floats, but i'm wondering if any better approach is suitable.
any help?
float g;
float f;
string sample = "#G10F30";
//string sample = "#F10G30"; //vice versa
sample = sample.Replace("#", string.Empty);
var splitG = sample.Split("G",StringSplitOptions.RemoveEmptyEntries);
switch (splitG.Length)
{
case 1:
var splitF = splitG[0].Split("F");
g = float.Parse(splitF[0]);
f = float.Parse(splitF[1]);
break;
case 2:
f = float.Parse(splitG[0].Replace("F",string.Empty));
g = float.Parse(splitG[1]);
break;
default:
throw new Exception("Incorrect input string");
}
Console.WriteLine(f);
Console.WriteLine(g);
To elaborate the proposed Regex sulution:
float g = float.NaN;
float f = float.NaN;
string sample = "#G10F30";
foreach (Match m in Regex.Matches(sample, #"(?<var>[FG])(?<val>[+-]?\d+(\.\d*)?)"))
{
var variable = m.Groups["var"].Value;
var value = float.Parse(m.Groups["val"].Value);
switch (variable)
{
case "F": f = value; break;
case "G": g = value; break;
}
}
Console.WriteLine($"f={f}, g={g}");
(?<var>[FG]) will match F or G and assign it to the group "var".
(?<val>[+-]?\d+(\.\d*)?)will match a floating point number and assign it to the group "val".
Note: The regex for matching floating point numbers is a bit limited and could be extended for your requirements, see Regular expression for floating point numbers
You can always write a parser:
(float? f, float? g) ParseTwoFloats(string input)
{
if (!input.StartsWith('#')) return (null, null);
var currentBlock = default(char?);
var currentStartIndex = default(int?);
var f = default(float?);
var g = default(float?);
for (var index = 1; index < input.Length; index++)
{
var token = input[index];
switch (token)
{
case 'F':
case 'G':
{
if (currentBlock.HasValue) UpdateWithNew(index);
currentBlock = token;
currentStartIndex = index;
break;
}
}
}
if (currentBlock.HasValue) UpdateWithNew(input.Length - 1);
void UpdateWithNew(int index)
{
var value = float.Parse(
input.AsSpan(currentStartIndex.Value + 1, index - currentStartIndex.Value - 1),
provider: CultureInfo.InvariantCulture);
switch (currentBlock.Value)
{
case 'F': f = value; break;
case 'G': g = value; break;
}
}
return (f, g);
}
This does no heap allocations and is pretty easy to extend based on your other needs (e.g. adding another thing to parse, things to skip, proper error handling etc.).
If you want something even simpler, you can always do something like this:
(float f, float g) ParseTwoFloats(string input)
{
return (FindByToken(input, 'F', 'G'), FindByToken(input, 'G', 'F'));
float FindByToken(string input, char startToken, char endToken) =>
input.IndexOf(startToken) is {} start
? Parse(input, start + 1, input.IndexOf(endToken, start))
: 0f;
float Parse(string input, int start, int end) =>
float.Parse(input.AsSpan(start, end == -1 ? input.Length - start : end - start),
provider: CultureInfo.InvariantCulture);
}
This is less flexible, and does involve two iterations through the string. The iteration can be faster than in the parser. As with the parser, there's no heap allocations.
You can probably do even better if you replace the string input with another ReadOnlySpan (assuming you're working on substrings of a larger string) - avoiding heap allocations can make a huge difference when parsing string data. If you can avoid creating millions of substrings, you can get a pretty nice improvement. But as always, measure first.
I wrote a quick code for factorizing formulas, however the line that takes creates the square root of D doesn’t work. The line is line 10. Any help is appreciated.
using System;
public class MainClass {
//Source Numbers
public int A = 1;
public int B = 3;
public int C = 9;
//Calculation Numbers
public float Di;
public static double Sqrt(double Di); //This is the faulted line.
//Answers
public float X;
public float X1;
public float X2;
public static void Main() {
Console.Writeline("D=", Di);
//Calculation for the Square root of D
// (DSq)Math.Sqrt(Di);
Di = B^2-4*A*C;
//Calculation for the answers
if(Di>0) {
X1 = ((0-B)-DSq)/(A*2);
X2 = ((0-B)+DSq)/(A*2);
Console.Writeline("X=", X1, " or X=", X2);
}
else if(Di=0) {
X = 0-B;
Console.Writeline("X=", X);
}
else {
Console.Writeline("The formula cannot be solved.");
}
}
}
You are using a method definition with no body. In any case you dont need to invent the wheel, since Math has already a Math.Sqrt(), method. Try:
........
Di = B^2-4*A*C;
if (Di>0)
{
var sqrDi = Math.Sqrt(Di);
.....
}
...
You have several errors in your code, like the spelling of WriteLine, and comparing in if statements (use ==). This returns a list of valid solutions (X-values):
public IList<double> factorizeABC(double a, double b, double c)
{
var solutions = new List<double>();
var Di = b * b - 4 * a * c;
if (Di > 0)
{
var rtDi = Math.Sqrt(Di);
var X1 = (-b - rtDi) / (a * 2);
var X2 = (-b + rtDi) / (a * 2);
solutions.Add(X1);
solutions.Add(X2);
}
else if (Di == 0)
{
var X = -b / (a * 2);
solutions.Add(X);
}
return solutions;
}
usage:
var results = factorizeABC(1, 2, -8);
if (results.Count() == 0)
Console.WriteLine("The formula cannot be solved.");
if (results.Count() == 1)
Console.WriteLine("X=" + results[0].ToString());
if (results.Count() == 2)
Console.WriteLine("X=" + results[0].ToString() + " or X=" + results[1].ToString());
The problem:
Given an input float (value), round another float (anotherValue) so it has the same significant figures as the first float (value).
What I have tried so far:
private static void Test()
{
var value = 0.12345f;
// ?? Strategy suggested by this post: http://stackoverflow.com/questions/3683718/is-there-a-way-to-get-the-significant-figures-of-a-decimal
var significantFigures = decimal.GetBits((decimal)value);
var anotherValue = 3.987654321;
// ERROR: Argument 2: cannot convert from int[] to int
var resultValue = (float) SetSignificantFigures((double)anotherValue, significantFigures.Length);
// Desired result: resultValue = 3.988
}
This is the definition of SetSignificantFigures:
// Function suggested by this post: http://stackoverflow.com/questions/374316/round-a-double-to-x-significant-figures
public static double SetSignificantFigures(double d, int digits)
{
double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
return scale * Math.Round(d / scale, digits);
}
Blocking point: since decimal.GetBits returns int[], I don't know how to proceed (or if it is the correct strategy).
If you just want the number of digits why don't you parse the string equivalent of the number
var value = 12.123f;
var str = value.ToString().Split('.');
int decimalCount = 0;
if (str.Count() == 2)
{
decimalCount = str[1].Length; // this will give you 3.
}
Replace this
var resultValue = SetSignificantDigits(anotherValue, significantDigits);
with this
var resultValue = SetSignificantDigits(anotherValue, significantDigits.Length);
Or try this
var significantDigits = value.ToString().Split('.')[1].Length; //perhaps replace '.' with ','
And in the SetSignificantDigits-Function replace this
return scale * Math.Round(d / scale, digits);
with this
return Math.Round(scale * (d / scale), digits);
Then it looks like this:
var value = 0.12345f;
var significantDigits = value.ToString().Split(',')[1].Length;
var anotherValue = 3.987654321;
var resultValue = SetSignificantDigits(anotherValue, significantDigits);
public static double SetSignificantDigits(double d, int digits)
{
double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
return Math.Round(scale * (d / scale), digits);
}
And the result is 3.98765
I just wrote my first C# program.
It's a simple piece of code which solves quadratic equations.
It works with some functions (such as -6x2-6x+12) perfectly, while with others, (4x2-20x+25) it exhibits what I suspect are rounding errors.
I'm completely new to C#, and I can't see an problems; would someone be able to help me debug this code?
namespace ConsoleApplication {
class Program {
static int ObtainInput(string prompt, bool canBeZero) {
double a = ObtainInput("A? ", false);
double b = ObtainInput("B? ", true);
double c = ObtainInput("C? ", true);
double d, x1, x2;
while (true) {
Console.Write(prompt);
string input = Console.ReadLine();
int result;
bool success = int.TryParse(input, out result);
if (success && (canBeZero || result != 0))
return result;
Console.WriteLine("Invalid input!");
}
// Calculating a discriminant
d = b * b - 4 * a * c;
if (d == 0) {
x1 = -b / (2 * a);
Console.WriteLine("The only solution is x={0}.", x1);
Console.ReadLine();
}
// If d < 0, no real solutions exist
else if (d < 0) {
Console.WriteLine("There are no real solutions");
Console.ReadLine();
}
// If d > 0, there are two real solutions
else {
x1 = (-b - Math.Sqrt(d)) / (2 * a);
x2 = (-b + Math.Sqrt(d)) / (2 * a);
Console.WriteLine("x1={0} and x2={1}.", x1, x2);
Console.ReadLine();
}
}
}
}
I just wrote my first C# program.
Awesome. Now would be a great time to not get into bad habits:
entA: Console.Write("a?");
try { a = Convert.ToInt32(Console.ReadLine()); }
catch
{ /*If a=0, the equation isn't quadratic*/
Console.WriteLine("Invalid input");
goto entA;
}
Problems abound. First off, use int.TryParse, rather than putting a try-catch around something that can fail.
Second, the comment does not match the action of the code. The code determines if the result is an integer; the comment says that it checks for zero.
Third, do not use a goto when what you are attempting to represent is a loop.
Fourth, look at all that duplicated code! You have the same code repeated three times with minor variations.
Make yourself a helper method:
static int ObtainInput(string prompt, bool canBeZero)
{
while(true) // loop forever!
{
Console.Write(prompt);
string input = Console.ReadLine();
int result;
bool success = int.TryParse(input, out result);
if (success && (canBeZero || result != 0))
return result;
Console.WriteLine("Invalid input!");
}
}
And now your mainline is:
int a = ObtainInput("A? ", false);
int b = ObtainInput("B? ", true);
int c = ObtainInput("C? ", true);
Your bug though is here:
x1 = x2 = -b / (2 * a);
You do the arithmetic in integers, and then convert to doubles. That is, you do the division, round to the nearest integer, and then convert to double. Do it in doubles (or, less likely, in decimals) from the start. It should be:
double a = ObtainInput("A? ", false);
double b = ObtainInput("B? ", true);
double c = ObtainInput("C? ", true);
That is, a, b, and c should not ever be integers.
You're doing integer division when assigning to x1 and x2; (you can just change the 2 to 2.0 to change it to double division and get a double result)
It might also make sense to change your a,b,c, and d values to double which will also get past the problem, and allow people to enter non-int values for the coefficients.
int a, b, c;
int d;
first of all, try to use double instead of int, since 1/3 = 0 using integers.
In the code below it should multiply 2 numbers. It works for 3 and less than 3 digits numbers but when I give numbers with 4 digits or bigger it gives runtime error: stackoverflow exception was unhandled. I've commented where the problem is. I thought the problem is for defining variables in int and changed them in long but the problem still exists. Where is the mistake?
edited:
now,whats do you think about the problem?it doesnt do anything
public long Prod2(long u, long v)
{
var numbers = textBox7.Text.Split(',').Select(p => long.Parse(p)).ToArray();
int n = Math.Max((int)Math.Floor(Math.Log10(u) + 1),(int)Math.Floor(Math.Log10(v) + 1));
int threshold = 3;
if (u == 0 || v == 0)
{
return 0;
}
else if (n <= threshold)
{
return u * v;
}
else
{
int m = (int)Math.Ceiling(n / 2.0);
int x = (int)(u / Math.Pow(10, m));
int y = (int)(u % Math.Pow(10, m));
int w = (int)(u / Math.Pow(10, m));
int z = (int)(v % Math.Pow(10, m));
long r = Prod2(x + y, w + z);
long p = Prod2(x, w);
long q = Prod2(y, z);
return p * (long)Math.Pow(10, 2 * m) + (r - p - q) * (long)Math.Pow(10, m) + q;
long result = Prod2(numbers[0], numbers[1]);
textBox1.Text = result.ToString();
}
}
You are getting into an infinite recursive loop at this point
long result = bigzarb(x, w) * Math.Pow(10, m) + (bigzarb(x, w) + bigzarb(w, y)) * Math.Pow(10, m) + bigzarb(y, z);///here
textBox1.Text = result.ToString();
I notice this line is executed only when intn > 3, so perhaps you have a logic bug there?
Update: After reading your comments I can see that this test is intended to say "if the length of this string is <= 3 then..." when in fact as written it is actually saying "if the VALUE of this converted string is <= 3 then..."
EDIT: I've completely translated the algorithm described in the book for you:
public long Prod2(long u, long v)
{
int n = Math.Max((int)Math.Floor(Math.Log10(u) + 1), (int)Math.Floor(Math.Log10(v) + 1));
int threshold = 3;
if(u == 0 || v == 0)
{
return 0;
}
else if(n <= threshold)
{
return u * v;
}
else
{
int m = (int)Math.Ceiling(n / 2.0);
int x = (int)(u / Math.Pow(10, m));
int y = (int)(u % Math.Pow(10, m));
int w = (int)(u / Math.Pow(10, m));
int z = (int)(v % Math.Pow(10, m));
long r = Prod2(x + y, w + z);
long p = Prod2(x, w);
long q = Prod2(y, z);
return p * (long)Math.Pow(10, 2 * m) + (r - p - q) * (long)Math.Pow(10, m) + q;
}
}
To get the right result, you'd call this method from some other method like this:
void Main()
{
// Call the method and store the result in variable 'r'.
long r = Prod2(1234, 5678);
Console.WriteLine(r);
/////////////////////////////////
//
// OR - In your case read from textBox7 and then store the result in textBox1
//
/////////////////////////////////
var numbers = textBox7.Text.Split(',').Select(p => long.Parse(p)).ToArray();
long result = prod2(numbers[0], numbers[1]);
textBox1.Text = result.ToString();
}
So in your event handler, for example for button1, you'd do this to make the call:
public void button1_Click()
{
var numbers = textBox7.Text.Split(',').Select(p => long.Parse(p)).ToArray();
long result = prod2(numbers[0], numbers[1]);
textBox1.Text = result.ToString();
}
Don't modify the Prod2 that I have, and just paste it with your code. This way, Prod2 does the calculation and then your button1_Click controls the input and what to do with the output.
In a nutshell you have a potential (varying on input) case of:
function bigzarb()
{
bigzarb()
}
so long as the numbers in textBox7 are > 3, i.e. an unclosed recursion loop, which will inevitably become a stackoverflow.
Put a breakpoint on the line in question and you'll quickly see the problem. Without knowing what your method does (I don't recognise the algorithm) I can't help much about cleaning it up, but the first step might be to have a get out clause to return from the function conditionally. However, I also see you overwriting the input arguments u and v before they get used so perhaps you've made a mistake in the algo?
Pow method returns double, so I think your x, y, z, w, and z should be declared as double as well.
you are getting "StackoverFlow" for the Recursive call. its better to mold the holes found in the code. I suggest you to change the logic.
static int callStack = 0;
public double bigzarb(long u, long v)
{
callStack++;
............
............