Naxx
Naxx

Reputation: 25

I am trying to create a base function to use on multiple randomisation in a program (Beginner level)

Dice Roller

For example on this app (I simply created for my character creation needs for D&D), when you press "Roll", the program rolls 3 times "d6 (6 sided dice)" and adds them up, and adds the sum to a the corresponding "Stat" Array, it does this 3 times, and then takes the highest value from the array as a result.

My noob function code here is here;

    func rollStr() {

    strArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)

    strArray.append(r1 + r2 + r3)
    strArray.append(r4 + r5 + r6)
    strArray.append(r7 + r8 + r9)
    
    strStat = strArray.max()!

}

I didn't manage to declare only 3 values and roll it 3 times, so I decided to do it with 9 variables. Anyway as you see above there are "strArray" and "strStat" in this function. I had to write 6 copies of this function for each different stat. I tried to use an editable function like;

    func rollStat(arrayNumber: Int, statNumber: Int) {
    
    var allArrays = [strArray, dexArray, consArray, intArray, wisArray, chaArray]
    var allStats = [strStat, dexStat, consStat, intStat, wisStat, chaStat]
    

    allArrays[arrayNumber].removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)

    allArrays[arrayNumber].append(r1 + r2 + r3)
    allArrays[arrayNumber].append(r4 + r5 + r6)
    allArrays[arrayNumber].append(r7 + r8 + r9)
    
    allStats[statNumber] = allArrays[arrayNumber].max()!

}

and wanted to call the function as "rollStat(arrayNumber: 1, statNumber: 1)" to use one base function to call what value I need instead of using multiple copies, but I couldn't manage to make it work. What am I doing wrong here?

As I said I achieved what I needed, the program works but if possible I want to know the tricks of doing it with only one function.

I will paste all the code below, if you wanted to know anything about the previous lines. Thanks in advance.

struct ContentView: View {

// MARK: PROPERTY

@State private var strArray:[Int] = [0,0,0]
@State private var dexArray:[Int] = [0,0,0]
@State private var consArray:[Int] = [0,0,0]
@State private var intArray:[Int] = [0,0,0]
@State private var wisArray:[Int] = [0,0,0]
@State private var chaArray:[Int] = [0,0,0]

@State private var strStat = 0
@State private var dexStat = 0
@State private var consStat = 0
@State private var intStat = 0
@State private var wisStat = 0
@State private var chaStat = 0



// MARK: METHOD

func rollStr() {

    strArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)

    strArray.append(r1 + r2 + r3)
    strArray.append(r4 + r5 + r6)
    strArray.append(r7 + r8 + r9)
    
    strStat = strArray.max()!

}

func rollDex() {
    
    dexArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)
    
    dexArray.append(r1 + r2 + r3)
    dexArray.append(r4 + r5 + r6)
    dexArray.append(r7 + r8 + r9)
    
    dexStat = dexArray.max()!
    
}

func rollCons() {
    
    consArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)
    
    consArray.append(r1 + r2 + r3)
    consArray.append(r4 + r5 + r6)
    consArray.append(r7 + r8 + r9)
    
    consStat = consArray.max()!
    
}

func rollInt() {
    
    intArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)
    
    intArray.append(r1 + r2 + r3)
    intArray.append(r4 + r5 + r6)
    intArray.append(r7 + r8 + r9)
    
    intStat = intArray.max()!
    
}

func rollWis() {
    
    wisArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)
    
    wisArray.append(r1 + r2 + r3)
    wisArray.append(r4 + r5 + r6)
    wisArray.append(r7 + r8 + r9)
    
    wisStat = wisArray.max()!
    
}

func rollCha() {
    
    chaArray.removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)
    
    chaArray.append(r1 + r2 + r3)
    chaArray.append(r4 + r5 + r6)
    chaArray.append(r7 + r8 + r9)
    
    chaStat = chaArray.max()!
    
}

func rollStat(arrayNumber: Int, statNumber: Int) {
    
    var allArrays = [strArray, dexArray, consArray, intArray, wisArray, chaArray]
    var allStats = [strStat, dexStat, consStat, intStat, wisStat, chaStat]
    

    allArrays[arrayNumber].removeAll()
    
    let r1 = Int.random(in: 1...6)
    let r2 = Int.random(in: 1...6)
    let r3 = Int.random(in: 1...6)
    let r4 = Int.random(in: 1...6)
    let r5 = Int.random(in: 1...6)
    let r6 = Int.random(in: 1...6)
    let r7 = Int.random(in: 1...6)
    let r8 = Int.random(in: 1...6)
    let r9 = Int.random(in: 1...6)

    allArrays[arrayNumber].append(r1 + r2 + r3)
    allArrays[arrayNumber].append(r4 + r5 + r6)
    allArrays[arrayNumber].append(r7 + r8 + r9)
    
    allStats[statNumber] = allArrays[arrayNumber].max()!

}

// MARK: BODY

var body: some View {

// MARK: STATS UI
    
VStack {
    HStack(spacing: 10) {
            
            VStack(spacing: 6) {
                sixDiceView()
                sixDiceView()
                sixDiceView()
                sixDiceView()
                sixDiceView()
                sixDiceView()
            } //: Vstack
            statNames()
            
            ZStack {
                VStack(spacing: 16)  {
                    Text(String(strStat))
                    Text(String(dexStat))
                    Text(String(consStat))
                    Text(String(intStat))
                    Text(String(wisStat))
                    Text(String(chaStat))
                }
                VStack(spacing: 6)  {
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 60, height: 30)
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 60, height: 30)
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 60, height: 30)
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 60, height: 30)
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 60, height: 30)
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 60, height: 30)
                    
                } //: VStack
            } //: ZStack
            
        ZStack {
            rollsCapsule()
            VStack(alignment: .leading, spacing: 16) {
                Text("\(strArray[0]),\(strArray[1]),\(strArray[2])")
                Text("\(dexArray[0]),\(dexArray[1]),\(dexArray[2])")
                Text("\(consArray[0]),\(consArray[1]),\(consArray[2])")
                Text("\(intArray[0]),\(intArray[1]),\(intArray[2])")
                Text("\(wisArray[0]),\(wisArray[1]),\(wisArray[2])")
                Text("\(chaArray[0]),\(chaArray[1]),\(chaArray[2])")
            } //: VStack
        } //: ZStack
            
    } //: HStack
        
// MARK: BUTTON ROLL
    Button(action:  {
        
        rollStr()
        rollDex()
        rollCons()
        rollInt()
        rollWis()
        rollCha()
        
        })  {
            rollButton()
        } //: BUTTON
  
    } //: VStack
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

}

Upvotes: 2

Views: 66

Answers (1)

vacawama
vacawama

Reputation: 154671

Swift functions can return multiple values using a tuple. If you create a general roll function to return the array and maximum value, you can just call it and assign the result to your variables:

func roll() -> (array: [Int], max: Int) {
    var result = [Int]()
    
    for _ in 1...3 {
        var sum = 0
        for _ in 1...3 {
            sum += Int.random(in: 1...6)
        }
        result.append(sum)
    }

    return (result, result.max()!)
}

Then you'd call it like this:

(strArray, strStat) = roll()
(dexArray, dexStat) = roll()
    

Define a RollData struct

Your array and stat shouldn't be two different @State vars since they are related. You should combine them into a struct.

struct RollData {
    let array: [Int]
    let stat: Int
}

func roll() -> RollData {
    var result = [Int]()
    
    for _ in 1...3 {
        var sum = 0
        for _ in 1...3 {
            sum += Int.random(in: 1...6)
        }
        result.append(sum)
    }
    
    return RollData(array: result, stat: result.max()!)
}

Then you'd declare your @State vars like this:

@State private var strRoll = roll()
@State private var dexRoll = roll()

and roll like this:

strRoll = roll()
dexRoll = roll()

and access the data like this:

Text(String(strRoll.stat))

Text("\(strRoll.array[0]),\(strRoll.array[1]),\(strRoll.array[2])")

Complete Example

This is a fun project to play with. I made some additional changes that I thought you might like to see.

Notes:

  1. I created a DieType enum which has a value for each die type. I made it of type Int so that we could use it as in index into an array of all of the dies. I made it CaseIterable so that we can get an array of all types with DieType.allCases. I made it CustomStringConvertible so the type would nicely print its name as returned by the computed property description. I made it Identifiable so that we can use it SwiftUI like this: ForEach(DieType.allCases) { die in.
  2. I created an AllDice struct which holds all of the dice. Now we only need one @State dice = AllDice() for the app. It has properties to roll all of the dice, roll a single die by type, get the stat and array for a die.
  3. The main loop for the app is a VStack with a row for each die type. There are no hardcoded 6's for number of die types. By defining a new die type, or removing one, the code adapts automatically.

import SwiftUI

struct RollData {
    let array: [Int]
    let stat: Int
}

func roll() -> RollData {
    var result = [Int]()
    
    for _ in 1...3 {
        var sum = 0
        for _ in 1...3 {
            sum += Int.random(in: 1...6)
        }
        result.append(sum)
    }
    
    return RollData(array: result, stat: result.max()!)
}

enum DieType: Int, CaseIterable, CustomStringConvertible, Identifiable {
    case strength = 0, dexterity, constitution, intelligence, wisdom, charisma
    
    var id: Int { self.rawValue }
    
    var description: String {
        switch self {
        case .strength: return "Strength"
        case .dexterity: return "Dexterity"
        case .constitution: return "Constitution"
        case .intelligence: return "Intelligence"
        case .wisdom: return "Wisdom"
        case .charisma: return "Charisma"
        }
    }
}

struct AllDice {
    private var dice: [RollData] = (0..<DieType.allCases.count).map { _ in roll() }
    
    func array(die: DieType) -> [Int] {
        dice[die.rawValue].array
    }
    
    func stat(die: DieType) -> Int {
        dice[die.rawValue].stat
    }
    
    mutating func rollDie(die: DieType) {
        dice[die.rawValue] = roll()
    }
    
    mutating func rollAll() {
        for die in DieType.allCases {
            rollDie(die: die)
        }
    }
}

struct ContentView: View {
    
    @State private var dice = AllDice()
    
    var body: some View {
        
        // MARK: STATS UI
        
        VStack {
            ForEach(DieType.allCases) { die in
                let array = dice.array(die: die)
                
                HStack(spacing: 10) {
                    
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 80, height: 30)
                        .overlay (
                            HStack {
                                Text("3 x")
                                Image(systemName: "dice")
                                    .foregroundColor(.red)
                            }
                        )
                    
                    Text(String(describing: die))
                        .frame(width: 100)
                    
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 50, height: 30)
                        .overlay (
                            Text(String(describing: dice.stat(die: die)))

                        )
                    
                    Capsule()
                        .fill(Color.blue.opacity(0.2))
                        .frame(width: 100, height: 30)
                        .overlay (
                            Text("\(array[0]), \(array[1]), \(array[2])")
                        )
                } //: HStack
                
            } //: ForEach
            
            // MARK: BUTTON ROLL
            Button(action:  {
                
                dice.rollAll()
                
            })  {
                Capsule()
                    .fill(Color.blue.opacity(0.2))
                    .frame(width: 130, height: 30)
                    .overlay (
                        HStack {
                            Image(systemName: "dice")
                            Text("Roll Stats")
                        }
                        .foregroundColor(.red)
                    )
                        
            } //: BUTTON
            
        } //: VStack
    }
}

Upvotes: 3

Related Questions