UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Impossible? Randomizing numbers in unique questions involving unique calculations

Forums > SwiftUI

Before tutoring some math the other day, I wondered if it's possible to create an app that would present varying math problems using different numbers each time. After a few days, the only solution I can come up is not very clean and involves creating a struct for each individual question. Let's say these are two of the questions:

struct Question1: View {
    var questionNumber = 1
    var length = Int.random(in: 10...30); var width = Int.random(in: 15...25)

    var body: some View {
        VStack {
            Text("Evaluate the area of a rectangle with a length of \(length) cm and a width of \(width) cm.")            
            Divider()            
            VStack(alignment: .leading) {
                Text("Area = length x width")
                Text("A = \(length) * \(width) = \(length * width)")
} } } }

struct Question2: View {
    var questionNumber = 2
    var perimeter = Int.random(in: 50...56); var width = Int.random(in: 15...22)
    var firstCalculation: Double { Double(perimeter - 2 * width) }
    var secondCalculation: Double { firstCalculation / 2 }

    var body: some View {
        VStack {
            Text("A rectangular field has a perimeter of \(perimeter) m. If the width of the field is \(width) m, what is the field's length?")            
            Divider()            
            VStack(alignment: .leading) {
                Text("Perimeter = 2 * length + 2 * width")
                Text("\(perimeter) = 2L + 2 * \(width)")
                Text("2L = \(firstCalculation, specifier: "%.0f")")
                Text("L = \(secondCalculation, specifier: "%.1f")")
} } } }

Combining these with the following ContentView works, but making 100 different structs seems unwieldy, especially given that I couldn't figure out how to call each struct programmatically; I just used an Int array and a switch statement, which is hardly elegant.

struct ContentView: View {
    var allQuestions = [1, 2]
    @State var index: Int = 0; var selectedQuestion: Int { index + 1 }

    var body: some View {
        VStack {
            Picker("Select problem", selection: $index) {
                ForEach(allQuestions, id: \.self) { questionNumber in
                    Text("Question #\(questionNumber)") } }
            QuestionDisplay(questionNumber: selectedQuestion)            
} } }

struct QuestionDisplay: View {
    var questionNumber: Int
    var body: some View {
        switch questionNumber { case 1: return Question1(); case 2: return Question2(); default: return Question1()
} } }

Ideally, the goal would be to use a combination of something like:

struct Question: Identifiable {
    var questionNumber: Int; var id: Int { questionNumber }   
    var givens: [Any]; var calculations: [(Double...) -> Double]    
    var question: String
    var solutionLines: [String]
}

and

struct ContentView2: View {
    var allQuestions: [Question]
    @State var selectedQuestion: Int = 1; var arrayIndex: Int { selectedQuestion - 1 }
    var given1: Int { allQuestions[arrayIndex].givens[0] }
    var given2: Int { allQuestions[arrayIndex].givens[1] }
    var calculation1: <something something > { allQuestions[arrayIndex].calculations[0] }
    var calculation2: <something something > { allQuestions[arrayIndex].calculations[1] }
    var body: some View {
        VStack {
            Picker("Select problem", selection: $selectedQuestion) {
                ForEach(allQuestions) { question in
                    Text("Question #\(question.questionNumber)") } }
            VStack {
                Text(allQuestions[arrayIndex].question); Divider()            
                VStack(alignment: .leading) {
                    ForEach(allQuestions[arrayIndex].solutionLines, id: \.self) { line in
                        Text(line)
} } } } } }

where the mathematical quantities like length, width, and area would just have to be referred to as givens[0], givens[1], givens[2], etc.

Then we could create individual questions of type Question and toss them into an array of Question objects (i.e., var question1: Question. However, even disregarding the craziness that entering the various calculations would require for each question and the fact that I can't include the specifiers in the Strings and would have to use a function–which I have written, actually–to round the values instead, you can't (afaik) deal with the string interpolation in a parameter.

Everytime I try what I think is a different angle, I come upon the same issue: a parameter value contains string interpolation or depends upon another parameter's value presented at the same time (e.g., Question(givens: [Int.random(..), Int.random(..)], .., question: "A rectangle's parameter is (givens[0]).."..)).

So would making individual structs (only some other object) be the only way to pull this off?

2      

You could take the Array of questions and .shuffled() or .shuffle() then go over the array

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.