100 Days of SwiftUI Day 25

100 Days of SwiftUI – Day 25 – Rock, Paper, Scissors

We’ve arrived at day 25 of the 100 Days of SwiftUI. A milestone, as we have now completed the first quarter of the course. Not bad at all!

Yesterday, we finished our third project and reviewed what we’ve learned, as well as faced a few challenges. Today, we’ve had our second consolidation day of the course. It wasn’t just reviewing what we’ve learned these past two weeks though. There was a major challenge waiting for us. Let’s dive in right away!

Rock Paper Scissors with a twist in SwiftUI

The challenge for today was creating a new app from scratch. The app is a brain training game that’s a twist on Rock, Paper, Scissors. What’s the twist, I hear you say? Well, the app tells you to either win or lose. It’s actually a bit tricky!

The rough requirements as given by Paul:

  • Each turn of the game the app will randomly pick either rock, paper, or scissors.
  • Each turn the app will alternate between prompting the player to win or lose.
  • The player must then tap the correct move to win or lose the game.
  • If they are correct they score a point; otherwise they lose a point.
  • The game ends after 10 questions, at which point their score is shown.

The solution

import SwiftUI

struct ContentView: View {
    
    @State private var appAnswers = ["Rock", "Paper", "Scissors"] // The possible options the app/computer opponent can choose
    @State private var appChoice = Int.random(in:0...2) // Used to randomize appChoice
    @State private var mustWin = Bool.random() // Decides whether the user should win or lose
    
    
    @State private var questionCounter = 1 // Tracks how many questions there've been
    @State private var userScore = 0 // Tracks the users score
    
    @State private var scoreAlert = "" // This will be displayed in an alert when an answer is selected and is set in the answerSelected function
    
    @State private var showScore = false // Used to trigger alert after an answer is selected
    @State private var gameOver = false // Used to trigger an alert after the final question
    
    
    var body: some View {
        ZStack {
            RadialGradient(stops: [
                .init(color: Color.indigo, location: 0.3),
                .init(color: Color.mint, location: 0.3)
            ], center: .top, startRadius: 200, endRadius: 300)
            .ignoresSafeArea()
            VStack {
                Text("Brain Training")
                    .font(.largeTitle).bold()
                    .foregroundColor(.white)
                    .frame(height: 100)
                Text("Question \(questionCounter) of 10")
                    .font(.headline.bold())
                    .foregroundColor(.white)
                Spacer()
                Text("Score: \(userScore)")
                    .font(.largeTitle).bold()
                    .foregroundColor(.white)
            }
            
            VStack {
                Spacer()
                Spacer()
                Text(mustWin ? "You must win!" : "You must lose!"  )
                    .frame(height: 70)
                    .font(.largeTitle.bold().smallCaps())
                    .foregroundColor(.indigo)
                    .background(.thinMaterial)
                    .padding()
                
                Text("The computer chose: ")
                    .frame(height: 40)
                    .font(.title.bold().smallCaps())
                    .foregroundColor(.white)
                    .background(.ultraThinMaterial)
                Image("\(appAnswers[appChoice])")
                    .resizable()
                    .frame(width: 100, height: 100)
                Text("Choose your move:")
                    .font(.title.bold().smallCaps())
                    .foregroundColor(.white)
                    .background(.ultraThinMaterial)
                
                HStack {
                    ForEach(0..<3) { number in
                        Button {
                            checkAnswer(number)
                        } label: {
                            Image("\(appAnswers[number])")
                                .resizable()
                                .frame(width: 100, height: 100)
                        }
                    }
                }
                Spacer()
            }
        }
        .alert(scoreAlert, isPresented: $showScore) {
            Button("Next question", action: newQuestion)
        } message: {
            Text("Your score is \(userScore)")
        }
        
        .alert("Game finished!", isPresented: $gameOver) {
            Button("Restart game", action: restartGame)
        } message: {
            Text("You finished the game with a score of \(userScore).")
        }
    }
    
    func checkAnswer(_ userChoice: Int) {
        let requirementWinLose = mustWin ? "win" : "lose"
        let correctAnswerAlert = "Correct! You had to \(requirementWinLose). You chose \(appAnswers[userChoice]) and the computer chose \(appAnswers[appChoice])"
        let incorrectAnswerAlert = "Incorrect! You had to \(requirementWinLose). You chose \(appAnswers[userChoice]) and the computer chose \(appAnswers[appChoice])"
        
        if mustWin {
            if userChoice == 0 && appChoice == 0 || userChoice == 0 && appChoice == 1 {
                scoreAlert = incorrectAnswerAlert
                userScore-=1
            } else if userChoice == 0 && appChoice == 2 {
                scoreAlert = correctAnswerAlert
                userScore+=1
            } else if userChoice == 1 && appChoice == 1 || userChoice == 1 && appChoice == 2 {
                scoreAlert = incorrectAnswerAlert
                userScore-=1
            } else if userChoice == 1 && appChoice == 0 {
                scoreAlert = correctAnswerAlert
                userScore+=1
            } else if userChoice == 2 && appChoice == 0 || userChoice == 2 && appChoice == 2 {
                scoreAlert = incorrectAnswerAlert
                userScore-=1
            } else if userChoice == 2 && appChoice == 1 {
                scoreAlert = correctAnswerAlert
                userScore+=1
            }
        } else {
            if userChoice == 0 && appChoice == 0 || userChoice == 0 && appChoice == 2 {
                scoreAlert = incorrectAnswerAlert
                userScore-=1
            } else if userChoice == 0 && appChoice == 1 {
                scoreAlert = correctAnswerAlert
                userScore+=1
            } else if userChoice == 1 && appChoice == 0 || userChoice == 1 && appChoice == 1 {
                scoreAlert = incorrectAnswerAlert
                userScore-=1
            } else if userChoice == 1 && appChoice == 2 {
                scoreAlert = correctAnswerAlert
                userScore+=1
            } else if userChoice == 2 && appChoice == 1 || userChoice == 2 && appChoice == 2 {
                scoreAlert = incorrectAnswerAlert
                userScore-=1
            } else if userChoice == 2 && appChoice == 0 {
                scoreAlert = correctAnswerAlert
                userScore+=1
            }
        }
        
        showScore = true
        trackQuestions()
        
    }
    
    func trackQuestions() {
        if questionCounter == 10 {
            gameOver = true
            showScore = false
        }
    }
    
    func newQuestion() {
        appChoice = Int.random(in: 0...2)
        mustWin.toggle()
        questionCounter+=1
    }
    
    func restartGame() {
        appChoice = Int.random(in: 0...2)
        mustWin.toggle()
        userScore = 0
        questionCounter = 1
    }
    
}

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

I’ve spent a few hours on this challenge, trying to work out how to best implement certain functions and refactoring parts of my code so that it was less complex and easier to understand. I’m not quite done yet, as I’m looking to refactor the if else statements as well.

That being said, I am quite happy that I was able to solve this challenge on my own and by referencing my own posts. I’m also quite happy with the way the design turned out.

If I find the time, I will try to break down my project here, which will help me solidify the knowledge I’ve gained over the past few weeks. For now, this was it for day 25! We’re moving on to a new set of projects now, so it’s high time to recharge!

Darryl

Hi! My name is Darryl and this is my personal blog where I write about my journey as I learn programming! You'll also find articles about other things that interest me including games, tech and anime.

Post navigation

Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

100 Days of SwiftUI – Day 80

100 Days of SwiftUI – Day 33

100 Days of SwiftUI – Day 48

100 Days of SwiftUI – Day 1 – Variables & Constants