Member Avatar for loserspearl

I'm making a math problem generator in visual c#, using a series of random number generators to create a unique math problem each time with some protocols implemented(later to use this for harder difficulties). There are different types of math to be used but right now I'm working on a tutorial to teach simple order of operations. The program right now needs to be able to give a problem to the user and compare their answer, but I only have set up the amount of problem numbers, their values, and symbol generation, with a simple display. I'm looking for any tips or advice on how to go about seperating the arrays that contain the mathValues and symbolCompute/symbolDisplay into a readable math problem (string) and a problem the computer can solve itself (which is a combination of math characters and integers at the moment).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace AcceleratedAptitude
{
    public partial class FormTutorials : Form
    {
        public FormTutorials()
        {
            InitializeComponent();
        }
        //difficulty settings
        Random rand = new Random();
        int easyValueMin = 1, easyValueMax = 20;
        int easyNumMin = 3, easyNumMax = 5;

        private void buttonOoO_Click(object sender, EventArgs e)
        {
            //panel control
            panelTutorStart.Visible = false;
            //panelTutorStart.BringToFront = false;
            panelTutorStart.Enabled = false;
            panelOoP.Visible = true;
            //panelOoP.BringToFront = true;
            panelOoP.Enabled = true;

            //number generation
            int mathNum = rand.Next(easyNumMin, easyNumMax);
            int threshCtr = 0;
            int threshold = easyValueMax / 2;
            int[] mathValue = new int[mathNum];
            for (int ctr = 0; ctr < mathNum; ctr++)
            {
                //check and change threshold so not all the numbers are too high
                mathValue[ctr] = rand.Next(easyValueMin, easyValueMax);
                if (mathValue[ctr] > threshold)
                    threshCtr++;
                if (threshCtr > mathNum / 2)
                    mathValue[ctr] = rand.Next(easyValueMin, threshold);
            }

            //symbol generation creates 1 symbol less than their are numbers
            int symbolNum = mathNum - 1;
            int[] symbolValue = new int[symbolNum];
            for (int ctr = 0; ctr < symbolNum; ctr++)
            {
                symbolValue[ctr] = rand.Next(1, 4);
            }
            char[] symbolCompute = new char[symbolNum];
            char[] symbolDisplay = new char[symbolNum];
            //converts random numbers in corresponding symbols for display and compute
            for (int ctr = 0; ctr < symbolNum; ctr++)
            {
                switch (symbolValue[ctr])
                {
                    case 1: symbolCompute[ctr] = '+'; 
                            symbolDisplay[ctr] = '+';
                        break;
                    case 2: symbolCompute[ctr] = '-'; 
                            symbolDisplay[ctr] = '-';
                        break;
                    case 3: symbolCompute[ctr] = '*'; 
                            symbolDisplay[ctr] = 'x';
                        break;
                    case 4: symbolCompute[ctr] = '/'; 
                            symbolDisplay[ctr] = '÷';
                        break;
                }
            }

            //problem compute assembly
            int problemLength = symbolCompute.Length + mathValue.Length;
            char[] problemValue = new char[problemLength];
            //fix out of bounds error here
            /*
            int assemblerCtr = 0;
            while (assemblerCtr < problemLength)
            {
                problemValue[assemblerCtr] = (char)mathValue[assemblerCtr];
                assemblerCtr += 2; 
            }
            assemblerCtr = 1;
            while (assemblerCtr < problemLength)
            {
                problemValue[assemblerCtr] = (char)symbolValue[assemblerCtr];
                assemblerCtr += 2;
            }

            String problemCompute = new string(problemValue);

            */

            //problem display assembly
            var ints = mathValue;
            var result = string.Join(",", ints.Select(x => x.ToString()).ToArray());
            string result2 = new string(symbolDisplay);
            StringBuilder strBuild = new StringBuilder();
            strBuild.Append(result);
            strBuild.Append(result2);
            richTextBoxProblemDisplay.Text = strBuild.ToString();
        }

        private void buttonBack_Click(object sender, EventArgs e)
        {
            //panel control
            panelTutorStart.Visible = true;
            panelTutorStart.Enabled = true;
            panelOoP.Visible = false;
            panelOoP.Enabled = false;
        }

        private void buttonNext_Click(object sender, EventArgs e)
        {

        }
    }
}

I'm thinking for the display there a way i can use string.join and use my symbolDisplay with spaces as a delimeter, not sure how. And as for the compute I'm fairly lost, unless there is a data type of math symbols I could just call from.

I'd so something like this for the problems

private enum OPERATOR {
    Add,
    Sub,
    Mul,
    Div,
    LAST
}

public class MathProblem {
    private static Random rand = new Random();

    private int operandx;
    private int operandy;
    private OPERATOR op;
    private int answer

    public MathProblem(int min, int max) {
        operandx = MathProblem.rand.Next(min, max+1);
        operandy = MathProblem.rand.Next(min, max+1);
        op = (OPERATOR)MathProblem.rand.Next(OPERATOR.LAST);
        switch (op) {
            case OPERATOR.Add: answer = operandx + operandy; break;
            case OPERATOR.Sub: answer = operandx - operandy; break;
            case OPERATOR.Mul: answer = operandx * operandy; break;
            case OPERATOR.Div: answer = operandx * operandy; break; // yes, multiply
        }
    }

    public Boolean CheckAnswer(int userAnswer) {
        switch (op) {
            case OPERATOR.Add:
            case OPERATOR.Sub:
            case OPERATOR.Mul: return userAnswer == answer; break;
            case OPERATOR.Div: return userAnswer == operandy; break;
        }
        return false;
    }

    public override String ToString() {
        String result = String.Empty;
        switch (op) {
            case OPERATOR.Add: result = String.Format("{0} + {1} = ??", operandx, operandy); break;
            case OPERATOR.Sub: result = String.Format("{0} - {1} = ??", operandx, operandy); break;
            case OPERATOR.Mul: result = String.Format("{0} * {1} = ??", operandx, operandy); break;
            case OPERATOR.Div: result = String.Format("{0} / {1} = ??", answer, operandx); break;
        }

        return result;
    }
}

This way I can change the implementation of the problems without affecting the code that does the display and collecting of the answer. You could simply add more types of problems (modulus, power) without having to change anything but the MathProblem class. You could switch to real numbers, etc. still only changing the MathProblem class

Member Avatar for loserspearl

You mean like a nested inner class? Havent used c# in a while, still used to java terms.

You could make it nested, but there is no need to do so.

Member Avatar for loserspearl

Alright I'm trying to implement a version of this code while still using my protocols of having any number of operands and 1 less than the amount of operands is the amount of operators, as in my number generation. The threshold is an attempt to make the problems not so random, to make the each unique problems values not so drastically different from on another.

I can just switch out my protocol code from to create the array operands(mathValue), but I'm confused about the enum operation symbol generation. When defining op is seems youre doing a typecast to the operater type but I'm confused on how random and operator.last will choose one of the 4 operaters. And how can I use multiple operators between the any number of operands?

When defining op is seems youre doing a typecast to the operater type but I'm confused on how random and operator.last will choose one of the 4 operaters.

Internally, by default, an enum type is represented by an integer. If you don't specify values for the elements of the enum type, they are assigned by the system. They start at zero and increase by one. So in the example I posted, we have 4 values (0, 1, 2, 3, and 4, which is the value of LAST). Random.Next returns a value from zero to one less than the specified maximum, so in the case where we use LAST, it generates a value from 0 to 3 which gives us the operand.

And how can I use multiple operators between the any number of operands?

If you want to have multiple operands and operators you'll need some sort of data structure to hold them. Two List<T> would work. But the evaluation of the problem becomes a lot more complex as you'll need to include order of operations (2+3*4 = 14, not 20). I wrote a while back an expression parser that you could use to help evalutate your expressions.

Member Avatar for loserspearl

Ha, awesome. I'll try that and get back if I have any issues. I'm trying to include parentheses, exponents, fractions and variables, when I get my problem generator working.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.