TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Why is my app crashing? Project 2: Guess the Flag. 100 days of SwiftUI

Forums > 100 Days of SwiftUI

Hello,

I'm completely new to programming and have never done this before.

After doing project 2 of 100 days of SwiftUI, I thought I would try to add some of my own code before moving on to the next project. I managed to add a player name, but the app crashes after question 9 of the game when I play it again & I am not sure why.

I see a "Thread 1: fatal error: index out of range" error on line 53: Image(countries[number]).

Why does this error occur during a new game?

Here is my code:

static let allCountries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Monaco", "Nigeria", "Poland", "Spain", "UK", "Ukraine", "US"]
@State private var countries = allCountries.shuffled()
@State private var correctAnswer = Int.random(in: 0...2)
@State private var nameGiven = true
@State private var playerName = ""

@State private var showingResults = false
@State private var showingScore = false
@State private var scoreTitle = ""
@State private var score = 0
@State private var QuestionCounter = 1
var body: some View {
    ZStack {
        RadialGradient(stops: [
            .init(color: Color(red: 0.1, green: 0.2, blue: 0.45), location: 0.3),
            .init(color: Color(red: 0.76, green: 0.15, blue: 0.26), location: 0.3)
        ], center: .top, startRadius: 200, endRadius: 700)

        .ignoresSafeArea()

        VStack {
            Spacer()

            Text("Guess the flag")
                .font(.largeTitle.weight(.bold))
                .foregroundStyle(.white)
            VStack (spacing: 15) {

                VStack {
                    Text("Tap the flag of")
                        .foregroundStyle(.secondary)
                        .font(.subheadline.weight(.heavy))

                    Text(countries[correctAnswer])
                        .font(.largeTitle.weight(.semibold))

                }
                ForEach(0..<3){ number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                            .clipShape(.capsule)
                            .shadow(radius: 5)
                    }
                }
            }
            .frame(maxWidth: .infinity)
            .padding(.vertical, 20)
            .background(.regularMaterial)
            .clipShape(.rect(cornerRadius: 20))

            Spacer()
            Spacer()

            Text ("\(playerName)'s Score: \(score) / 10")
                .foregroundStyle(.white)
                .font(.title.bold())
        }
    }
    .alert(scoreTitle, isPresented: $showingScore) {
        Button("Continue", action: askQuestion)
    } message: {
        Text("Your score is \(score) / 10")
    }
    .alert("Game over!", isPresented: $showingResults) {
        Button("Finish", action: restartGame)
    } message: {
        Text("Your final score was \(score) / 10")
    }

    .alert("Enter your name", isPresented: $nameGiven) {
        TextField("Name", text: $playerName)
        Text("Play")
    }
}

func flagTapped(_ number: Int) {
    if number == correctAnswer {
        scoreTitle = "Correct, \(playerName)!"
        score += 1
    } else {
        let needsThe = ["UK", "US"]
        let theirAnswer = countries[number]

        if needsThe.contains(theirAnswer) {
            scoreTitle = "Wrong, \(playerName)! that's the flag of the \(theirAnswer)"
        } else {
            scoreTitle = "Wrong, \(playerName)! That's the flag of \(theirAnswer)"
        }
    }
    if QuestionCounter == 10 {
        showingResults = true
    } else {
        showingScore = true
    }
}

func askQuestion() {
    countries.remove(at: correctAnswer)
    countries.shuffle()
    correctAnswer = Int.random(in: 0...2)
    QuestionCounter += 1
}
func restartGame() {
    QuestionCounter = 0
    score = 0
    countries = Self.allCountries
    askQuestion()
    askForPlayerName()
}
func askForPlayerName() {
    if score == 0 {
        nameGiven = true
        playerName = ""
    } else {
        nameGiven = false
    }
}
}

Thank you

3      

Because when you restartGame() the QuestionCounter is set to zero when it should be set to one(1). You ask more questions in the restart games and as you removing the correct answer from the array you cannot add three countries for the last question.

PS you should use camelCase for properties eg questionCounter

3      

Thank you @NigelGee!

Now when the game restarts, only 9 questions are asked and the final score message is always 9 / 10 for any new games played after the first one. I have tried many combinations to fix this, but I am lost and do not understand why only 9 questions are asked for new games after it restarts.

Thank you

3      

Change your restartGame() method to be this

    func restartGame() {
        QuestionCounter = 1
        score = 0
        countries = Self.allCountries
//        askQuestion() <- Remove this
        countries.shuffle() // <- add this
        correctAnswer = Int.random(in: 0...2)  // <- add this
        askForPlayerName()
    }

3      

Thank you so much for the help!

3      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

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.