After having some hard times with days 32 to 34 and several repetitons of those days, it's done.
I could finally finish the milestone project of the educational app and it took me about 10 hours...
But I must say that I learned more of it, than in any milestone and challenge before.
Time to move on with a bunch of self-esteem.
If someone has suggestion, I'm open for all type of criticism.
Thanks to Paul, who is motivating me each day :D
//
// ContentView.swift
// Plutimication
import SwiftUI
struct Background: View{
var body: some View {
ZStack {
RadialGradient(gradient: Gradient(colors: [.yellow,.brown, .blue ]),
center: .init(x: 0.5, y: 0.50),
startRadius: 20, endRadius: 280)
VStack{
Text("\nPlutimication")
.padding(20)
.font(.largeTitle.weight(.black))
.shadow(color: .gray, radius: 4, x: 5, y: 5)
.opacity(0.7)
Spacer()
}
}
.ignoresSafeArea()
}
}
struct ContentView: View {
@State private var difficulty: Int = 5
@State private var numberOfQuestions = 5
@State private var optNumberOfQuestions = [ 5, 10, 20]
@State private var rangeDifficulty = 3...12
@State private var questions = [(num1: Int, num2: Int)]()
@State private var answers = [Int]()
@State private var round = 0
@State private var score = 0
@State private var showMenuButtons = true
@State private var showParameters = false
@State private var gameHasStarted = false
@State private var gameOver = false
var body: some View {
ZStack {
Background()
// Startmenu view
if showMenuButtons {
VStack{
Spacer()
Button ("Start Game") {
withAnimation {
startGame()
}
}
.padding(60)
.background(.green.opacity(0.4))
.clipShape(Circle())
.transition(.scale)
Button () {
withAnimation {
setParameters()
}
} label: { Image(systemName: "gearshape").resizable().frame(width: 35, height: 35) }
.padding(15)
.background(.blue.opacity(0.4))
.foregroundColor(.yellow.opacity(0.6))
.clipShape(Circle())
.transition(.scale)
Spacer()
}
.transition(.scale)
}
// Parameter view
if showParameters {
VStack(spacing: 30) {
Section{
Picker ("Number of Questions:", selection: $numberOfQuestions) {
ForEach (optNumberOfQuestions, id: \.self) {
Text("\($0)")
}
}
.pickerStyle(.segmented)
} header: { Text ("Choose number of questions:") }
Section{
Picker ("Difficulty:" , selection: $difficulty) {
ForEach (rangeDifficulty, id: \.self) {
Text("\($0)")
}
}
.pickerStyle(.segmented)
} header: { Text ("Choose level of difficulty:") }
Button("Done"){
withAnimation{
showParameters = false
showMenuButtons = true
}
}
.padding(10)
.background(.blue.opacity(0.4))
.foregroundColor(.yellow.opacity(0.6))
.clipShape(Capsule())
}
.frame(width: 260, height: 300, alignment: .center)
.padding(20)
.background(.white.opacity(0.2))
.foregroundColor(.black.opacity(0.6))
.clipShape(RoundedRectangle(cornerRadius: 30))
.transition(.scale)
}
// game is started
if gameHasStarted && !gameOver {
VStack(spacing: 20){
Text ("Round \(round+1) of \(numberOfQuestions)")
Text ("How much is \(questions[round].num1) * \(questions[round].num2)?")
.font(.headline)
HStack{
ForEach (answers, id: \.self) {num in
Button (){
withAnimation{
buttonTapped(num)
nextRound()
}
} label: { Text ("\(num)") }
.padding(12)
.foregroundColor(.yellow)
.background(.blue.opacity(0.5))
.clipShape(Circle())
}
}
Text ("Your score: \(score)")
.font(.headline)
}
.foregroundColor(.black.opacity(0.6))
}
// game ended and show score
if gameOver {
VStack(spacing: 20){
Text ("Game Over!")
.font(.headline.weight(.bold))
.foregroundColor(.red)
Text (yourEvaluation())
.multilineTextAlignment(.center)
Button ("New game") {
withAnimation {
mainMenu()
}
}
.padding(10)
.background(.blue.opacity(0.4))
.foregroundColor(.yellow.opacity(0.8))
.clipShape(Capsule())
.transition(.scale)
}
.frame(width: 200, height: 200, alignment: .center)
.background(.white.opacity(0.2))
.clipShape(RoundedRectangle(cornerRadius: 30))
.transition(.scale)
}
}
}
// set different views
func mainMenu () {
showMenuButtons = true
showParameters = false
gameHasStarted = false
gameOver = false
}
func startGame() {
round = 0
score = 0
generateQuestions()
generateAnswers()
gameHasStarted = true
gameOver = false
showMenuButtons = false
showParameters = false
}
func setParameters(){
showMenuButtons = false
showParameters = true
gameHasStarted = false
}
// actions
func buttonTapped (_ button: Int) {
let rightAnswer = questions[round].num1 * questions[round].num2
if button == rightAnswer {
score += 1
}
}
func nextRound() {
round += 1
guard (round < (numberOfQuestions)) else {
gameOver = true
return
}
generateAnswers()
}
// initialize game play
func generateQuestions () {
var questionsGenerated = [(num1: Int, num2: Int)]() // tuple of random multipliers
for _ in 1...numberOfQuestions {
questionsGenerated.append((
num1: Int.random(in: 2...difficulty),
num2: Int.random(in: 2...difficulty)))
}
questions = questionsGenerated
}
func generateAnswers () {
let myQuestion = questions[round]
let rightAnswer = myQuestion.num1 * myQuestion.num2
// make some unique answers
var myAnswers = Set<Int>()
myAnswers.insert(rightAnswer)
repeat {
myAnswers.insert (rightAnswer + Int.random(in: -20...20))
myAnswers = myAnswers.filter{$0 > 3} // cut off results < 4
} while myAnswers.count < 5
// save answers in mixed form
answers = Array(myAnswers).shuffled()
}
// calculate result message
func yourEvaluation() -> String {
var output: String = "You scored \(score) of \(numberOfQuestions)!\n"
if score <= 1 {
output += "Serious?"
} else if score <= (numberOfQuestions / 2) {
output += "Well..."
} else if score <= (numberOfQuestions / 4 * 3) {
output += "Not that bad!"
} else if score < numberOfQuestions {
output += "Great!"
} else {
output += "Perfect!"
}
return output
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}