Mercurial > public > geoquiz
changeset 3:4dbe0cd9dadc
first game prototype
author | Dennis C. M. <dennis@denniscm.com> |
---|---|
date | Thu, 22 Sep 2022 10:42:39 +0200 |
parents | 5b7c89bd45c3 |
children | de54f05adb78 |
files | GeoQuiz.xcodeproj/project.pbxproj GeoQuiz/GuessTheCapitalView.swift GeoQuiz/GuessTheFlagView.swift GeoQuiz/Helpers/GameAlertsModifier.swift GeoQuiz/Helpers/GameToolbar.swift GeoQuiz/Logic/Game.swift GeoQuiz/Logic/GameProtocol.swift GeoQuiz/Logic/GuessTheCapital.swift GeoQuiz/Logic/GuessTheFlag.swift GeoQuiz/Tests/Animation.swift GeoQuiz/Tests/ClassTest.swift GeoQuiz/Tests/ProtocolTest.swift GeoQuiz/Tests/Protocols.swift |
diffstat | 13 files changed, 487 insertions(+), 304 deletions(-) [+] |
line wrap: on
line diff
--- a/GeoQuiz.xcodeproj/project.pbxproj Tue Sep 20 11:54:06 2022 +0200 +++ b/GeoQuiz.xcodeproj/project.pbxproj Thu Sep 22 10:42:39 2022 +0200 @@ -8,13 +8,18 @@ /* Begin PBXBuildFile section */ 95030CEA28D1BA4D001AA3A1 /* AnswerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95030CE928D1BA4D001AA3A1 /* AnswerButton.swift */; }; + 950C59F528DADA10007C8504 /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C59F428DADA10007C8504 /* Animation.swift */; }; + 950C59F928DB181C007C8504 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C59F828DB181C007C8504 /* Protocols.swift */; }; + 950C59FB28DB1F73007C8504 /* ProtocolTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C59FA28DB1F73007C8504 /* ProtocolTest.swift */; }; + 950C59FD28DB1F79007C8504 /* ClassTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C59FC28DB1F79007C8504 /* ClassTest.swift */; }; 951B630228D1A87C004F9877 /* GuessTheCapitalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951B630128D1A87C004F9877 /* GuessTheCapitalView.swift */; }; 951D197328D485E000671FAD /* CustomColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951D197228D485E000671FAD /* CustomColors.swift */; }; + 952E41E928DC521200198643 /* GameAlertsModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952E41E828DC521200198643 /* GameAlertsModifier.swift */; }; 9539829328C51EDE00B70973 /* GeoQuizApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9539829228C51EDE00B70973 /* GeoQuizApp.swift */; }; 9539829728C51EDF00B70973 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9539829628C51EDF00B70973 /* Assets.xcassets */; }; 9539829A28C51EDF00B70973 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9539829928C51EDF00B70973 /* Preview Assets.xcassets */; }; 955A658128D703EB00CEEC6D /* GameToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955A658028D703EB00CEEC6D /* GameToolbar.swift */; }; - 955A658328D733E400CEEC6D /* GameProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955A658228D733E400CEEC6D /* GameProtocol.swift */; }; + 955A658328D733E400CEEC6D /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955A658228D733E400CEEC6D /* Game.swift */; }; 955A658528D73EC600CEEC6D /* GuessTheCapital.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955A658428D73EC600CEEC6D /* GuessTheCapital.swift */; }; 955A65A928D7815E00CEEC6D /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955A65A828D7815E00CEEC6D /* Haptics.swift */; }; 956273EA28CB2E98008DC094 /* FlagImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956273E928CB2E98008DC094 /* FlagImage.swift */; }; @@ -42,14 +47,19 @@ /* Begin PBXFileReference section */ 95030CE928D1BA4D001AA3A1 /* AnswerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnswerButton.swift; sourceTree = "<group>"; }; + 950C59F428DADA10007C8504 /* Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Animation.swift; sourceTree = "<group>"; }; + 950C59F828DB181C007C8504 /* Protocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; }; + 950C59FA28DB1F73007C8504 /* ProtocolTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolTest.swift; sourceTree = "<group>"; }; + 950C59FC28DB1F79007C8504 /* ClassTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassTest.swift; sourceTree = "<group>"; }; 951B630128D1A87C004F9877 /* GuessTheCapitalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuessTheCapitalView.swift; sourceTree = "<group>"; }; 951D197228D485E000671FAD /* CustomColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomColors.swift; sourceTree = "<group>"; }; + 952E41E828DC521200198643 /* GameAlertsModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameAlertsModifier.swift; sourceTree = "<group>"; }; 9539828F28C51EDE00B70973 /* GeoQuiz.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GeoQuiz.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9539829228C51EDE00B70973 /* GeoQuizApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoQuizApp.swift; sourceTree = "<group>"; }; 9539829628C51EDF00B70973 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 9539829928C51EDF00B70973 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; }; 955A658028D703EB00CEEC6D /* GameToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameToolbar.swift; sourceTree = "<group>"; }; - 955A658228D733E400CEEC6D /* GameProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameProtocol.swift; sourceTree = "<group>"; }; + 955A658228D733E400CEEC6D /* Game.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Game.swift; sourceTree = "<group>"; }; 955A658428D73EC600CEEC6D /* GuessTheCapital.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuessTheCapital.swift; sourceTree = "<group>"; }; 955A65A828D7815E00CEEC6D /* Haptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptics.swift; sourceTree = "<group>"; }; 956273E928CB2E98008DC094 /* FlagImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagImage.swift; sourceTree = "<group>"; }; @@ -92,15 +102,26 @@ 95C430F628D092FC00480D23 /* GameSelection.swift */, 95C430F828D0A8E500480D23 /* CustomGradients.swift */, 951D197228D485E000671FAD /* CustomColors.swift */, - 955A658228D733E400CEEC6D /* GameProtocol.swift */, 95AE8D5628C8750E0067F219 /* Load.swift */, 955A65A828D7815E00CEEC6D /* Haptics.swift */, + 955A658228D733E400CEEC6D /* Game.swift */, 95FA409B28D9881100129B60 /* GuessTheFlag.swift */, 955A658428D73EC600CEEC6D /* GuessTheCapital.swift */, ); path = Logic; sourceTree = "<group>"; }; + 950C59F328DADA01007C8504 /* Tests */ = { + isa = PBXGroup; + children = ( + 950C59F428DADA10007C8504 /* Animation.swift */, + 950C59F828DB181C007C8504 /* Protocols.swift */, + 950C59FA28DB1F73007C8504 /* ProtocolTest.swift */, + 950C59FC28DB1F79007C8504 /* ClassTest.swift */, + ); + path = Tests; + sourceTree = "<group>"; + }; 9520ABBA28C86D0300A3D4D7 /* Data */ = { isa = PBXGroup; children = ( @@ -141,6 +162,7 @@ 959D414728C87EA600BAAC14 /* Helpers */, 959D414528C87E8D00BAAC14 /* Models */, 95030CE728D1B60F001AA3A1 /* Logic */, + 950C59F328DADA01007C8504 /* Tests */, 9520ABBA28C86D0300A3D4D7 /* Data */, 9539829628C51EDF00B70973 /* Assets.xcassets */, 9539829828C51EDF00B70973 /* Preview Content */, @@ -176,6 +198,7 @@ 956273E928CB2E98008DC094 /* FlagImage.swift */, 95030CE928D1BA4D001AA3A1 /* AnswerButton.swift */, 955A658028D703EB00CEEC6D /* GameToolbar.swift */, + 952E41E828DC521200198643 /* GameAlertsModifier.swift */, ); path = Helpers; sourceTree = "<group>"; @@ -257,15 +280,19 @@ buildActionMask = 2147483647; files = ( 955A65A928D7815E00CEEC6D /* Haptics.swift in Sources */, + 952E41E928DC521200198643 /* GameAlertsModifier.swift in Sources */, 959D415F28C8861F00BAAC14 /* CountryPopulation.swift in Sources */, - 955A658328D733E400CEEC6D /* GameProtocol.swift in Sources */, + 955A658328D733E400CEEC6D /* Game.swift in Sources */, 95C4315628C64A8C00212131 /* ContentView.swift in Sources */, 95C4315928C6500000212131 /* GameButton.swift in Sources */, + 950C59F928DB181C007C8504 /* Protocols.swift in Sources */, 956273EA28CB2E98008DC094 /* FlagImage.swift in Sources */, + 950C59FB28DB1F73007C8504 /* ProtocolTest.swift in Sources */, 959D414928C87ED900BAAC14 /* CityPopulation.swift in Sources */, 955A658528D73EC600CEEC6D /* GuessTheCapital.swift in Sources */, 959D257028C7251F00C55A5E /* BuyLivesModal.swift in Sources */, 959D415928C8854800BAAC14 /* CountryCities.swift in Sources */, + 950C59F528DADA10007C8504 /* Animation.swift in Sources */, 951B630228D1A87C004F9877 /* GuessTheCapitalView.swift in Sources */, 959D415728C8851900BAAC14 /* CountryCapitals.swift in Sources */, 9539829328C51EDE00B70973 /* GeoQuizApp.swift in Sources */, @@ -276,6 +303,7 @@ 959D415D28C885ED00BAAC14 /* CountryFlags.swift in Sources */, 959D415B28C885C700BAAC14 /* CountryCurrencies.swift in Sources */, 95C430F728D092FC00480D23 /* GameSelection.swift in Sources */, + 950C59FD28DB1F79007C8504 /* ClassTest.swift in Sources */, 951D197328D485E000671FAD /* CustomColors.swift in Sources */, 95C430F928D0A8E500480D23 /* CustomGradients.swift in Sources */, 95FA409A28D9876B00129B60 /* GuessTheFlagView.swift in Sources */,
--- a/GeoQuiz/GuessTheCapitalView.swift Tue Sep 20 11:54:06 2022 +0200 +++ b/GeoQuiz/GuessTheCapitalView.swift Thu Sep 22 10:42:39 2022 +0200 @@ -11,13 +11,13 @@ @Binding var gameName: GameName? @StateObject var game = GuessTheCapital() - var flagSymbol: String { - if let countryAsked = game.countries[game.countryNameAsked] { - return countryAsked.flagSymbol - } else { - fatalError("Couldn't find \(game.countryNameAsked) in countries") - } - } +// var flagSymbol: String { +// if let countryAsked = game.countries[game.countryNameAsked] { +// return countryAsked.flagSymbol +// } else { +// fatalError("Couldn't find \(game.countryNameAsked) in countries") +// } +// } var body: some View { ZStack { @@ -26,44 +26,44 @@ GeometryReader { geo in VStack(spacing: 20) { - GameToolbar( - userScore: $game.userScore, - userLives: $game.userLives, - gameName: $gameName, - showingBuyLivesView: $game.showingBuyLivesView - ) +// GameToolbar( +// userScore: $game.userScore, +// userLives: $game.userLives, +// gameName: $gameName, +// showingBuyLivesView: $game.showingBuyLivesView +// ) Spacer() - FlagImage(flagSymbol: flagSymbol, cornerRadius: 20) - .shadow(radius: 10) - .frame(height: geo.size.height * 0.15) +// FlagImage(flagSymbol: flagSymbol, cornerRadius: 20) +// .shadow(radius: 10) +// .frame(height: geo.size.height * 0.15) Spacer() HStack { - VStack(alignment: .leading, spacing: 10) { - Text("Question \(game.questionCounter) of \(game.countries.count)") - .font(.title3) - - Text("What is the capital of \(game.countryNameAsked)?") - .font(.title) - .fontWeight(.semibold) - } - .foregroundColor(.white) +// VStack(alignment: .leading, spacing: 10) { +// Text("Question \(game.questionCounter) of \(game.countries.count)") +// .font(.title3) +// +// Text("What is the capital of \(game.countryNameAsked)?") +// .font(.title) +// .fontWeight(.semibold) +// } +// .foregroundColor(.white) Spacer() } VStack { - ForEach(Array(game.userChoices.values), id: \.self) { country in - Button { - game.answered(userChoice: country.capitalName) - } label: { - AnswerButton(optionName: country.capitalName, color: .secondary) - .frame(height: geo.size.height * 0.08) - } - } +// ForEach(Array(game.userChoices.values), id: \.self) { country in +// Button { +// game.answered(userChoice: country.capitalName) +// } label: { +// AnswerButton(optionName: country.capitalName, color: .secondary) +// .frame(height: geo.size.height * 0.08) +// } +// } } } .padding() @@ -71,29 +71,29 @@ } .navigationBarHidden(true) - .sheet(isPresented: $game.showingBuyLivesView) { - BuyLivesModal() - } - - .alert(game.alertTitle, isPresented: $game.showingNoLivesAlert) { - Button("Buy lives") { game.showingBuyLivesView = true } - Button("Exit", role: .destructive) { gameName = nil } - Button("Cancel", role: .cancel) { } - } message: { - Text(game.alertMessage) - } - - .alert(game.alertTitle, isPresented: $game.showingWrongAnswerAlert) { - Button("Continue", role: .cancel) { game.askQuestion() } - } message: { - Text(game.alertMessage) - } - - .alert(game.alertTitle, isPresented: $game.showingEndGameAlert) { - Button("Exit", role: .cancel) { gameName = nil } - } message: { - Text(game.alertMessage) - } +// .sheet(isPresented: $game.showingBuyLivesView) { +// BuyLivesModal() +// } +// +// .alert(game.alertTitle, isPresented: $game.showingNoLivesAlert) { +// Button("Buy lives") { game.showingBuyLivesView = true } +// Button("Exit", role: .destructive) { gameName = nil } +// Button("Cancel", role: .cancel) { } +// } message: { +// Text(game.alertMessage) +// } +// +// .alert(game.alertTitle, isPresented: $game.showingWrongAnswerAlert) { +// Button("Continue", role: .cancel) { game.askQuestion() } +// } message: { +// Text(game.alertMessage) +// } +// +// .alert(game.alertTitle, isPresented: $game.showingEndGameAlert) { +// Button("Exit", role: .cancel) { gameName = nil } +// } message: { +// Text(game.alertMessage) +// } } }
--- a/GeoQuiz/GuessTheFlagView.swift Tue Sep 20 11:54:06 2022 +0200 +++ b/GeoQuiz/GuessTheFlagView.swift Thu Sep 22 10:42:39 2022 +0200 @@ -16,21 +16,16 @@ LinearGradient(gradient: .main, startPoint: .top, endPoint: .bottom) .ignoresSafeArea() - GeometryReader{ geo in + GeometryReader { geo in VStack(spacing: 20) { - GameToolbar( - userScore: $game.userScore, - userLives: $game.userLives, - gameName: $gameName, - showingBuyLivesView: $game.showingBuyLivesView - ) + GameToolbar(gameName: $gameName, game: game) HStack { VStack(alignment: .leading, spacing: 10) { - Text("Question \(game.questionCounter) of \(game.countries.count)") + Text("Question \(game.questionCounter) of \(game.data.count)") .font(.title3) - Text("What is the flag of \(game.countryNameAsked)?") + Text("What is the flag of \(game.correctAnswer.key)?") .font(.title) .fontWeight(.semibold) } @@ -41,11 +36,11 @@ Spacer() - ForEach(Array(game.userChoices.values), id: \.self) { flagSymbol in + ForEach(Array(game.userChoices.keys), id: \.self) { countryName in Button { - game.answered(userChoice: flagSymbol) + game.answer((key: countryName, value: game.data[countryName]!)) } label: { - FlagImage(flagSymbol: flagSymbol, cornerRadius: 20) + FlagImage(flagSymbol: game.data[countryName]!, cornerRadius: 20) .shadow(radius: 10) .frame(height: geo.size.height * 0.15) } @@ -58,30 +53,10 @@ } } .navigationBarHidden(true) - + .modifier(GameAlertsModifier(game: game, gameName: $gameName)) .sheet(isPresented: $game.showingBuyLivesView) { BuyLivesModal() } - - .alert(game.alertTitle, isPresented: $game.showingNoLivesAlert) { - Button("Buy lives") { game.showingBuyLivesView = true } - Button("Exit", role: .destructive) { gameName = nil } - Button("Cancel", role: .cancel) { } - } message: { - Text(game.alertMessage) - } - - .alert(game.alertTitle, isPresented: $game.showingWrongAnswerAlert) { - Button("Continue", role: .cancel) { game.askQuestion() } - } message: { - Text(game.alertMessage) - } - - .alert(game.alertTitle, isPresented: $game.showingEndGameAlert) { - Button("Exit", role: .cancel) { gameName = nil } - } message: { - Text(game.alertMessage) - } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeoQuiz/Helpers/GameAlertsModifier.swift Thu Sep 22 10:42:39 2022 +0200 @@ -0,0 +1,36 @@ +// +// GameAlertsModifier.swift +// GeoQuiz +// +// Created by Dennis Concepción Martín on 22/9/22. +// + +import SwiftUI + +struct GameAlertsModifier<T: Game>: ViewModifier { + @ObservedObject var game: T + @Binding var gameName: GameName? + + func body(content: Content) -> some View { + content + .alert(game.alertTitle, isPresented: $game.showingWrongAnswerAlert) { + Button("Continue", role: .cancel) { game.askQuestion() } + } message: { + Text(game.alertMessage) + } + + .alert(game.alertTitle, isPresented: $game.showingNoLivesAlert) { + Button("Buy lives") { game.showingBuyLivesView = true } + Button("Exit", role: .destructive) { gameName = nil } + Button("Cancel", role: .cancel) { } + } message: { + Text(game.alertMessage) + } + + .alert(game.alertTitle, isPresented: $game.showingEndGameAlert) { + Button("Exit", role: .cancel) { gameName = nil } + } message: { + Text(game.alertMessage) + } + } +}
--- a/GeoQuiz/Helpers/GameToolbar.swift Tue Sep 20 11:54:06 2022 +0200 +++ b/GeoQuiz/Helpers/GameToolbar.swift Thu Sep 22 10:42:39 2022 +0200 @@ -7,11 +7,10 @@ import SwiftUI -struct GameToolbar: View { - @Binding var userScore: Int - @Binding var userLives: Int +struct GameToolbar<T: Game>: View { @Binding var gameName: GameName? - @Binding var showingBuyLivesView: Bool + @ObservedObject var game: T + var body: some View { HStack(spacing: 0) { @@ -32,7 +31,7 @@ .frame(maxWidth: .infinity, alignment: .leading) Group { - Text("\(userScore)") + Text("\(game.userScore)") .padding() .background( Circle() @@ -41,21 +40,23 @@ } .foregroundColor(.white) .font(.title2) + .scaleEffect(game.scoreScaleAmount) .frame(maxWidth: .infinity, alignment: .center) Group { Button { - showingBuyLivesView = true + game.showingBuyLivesView = true } label: { HStack { Image(systemName: "heart.fill") - Text("\(userLives)") + Text("\(game.userLives)") } .padding(10) .background( Capsule() .strokeBorder(lineWidth: 2) ) + .scaleEffect(game.livesScaleAmount) } } .foregroundColor(.white) @@ -74,9 +75,8 @@ GeometryReader { geo in VStack { GameToolbar( - userScore: .constant(0), - userLives: .constant(6), - gameName: .constant(.guessTheFlag), showingBuyLivesView: .constant(false) + gameName: .constant(GameName.guessTheFlag), + game: GuessTheFlag() ) Spacer()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeoQuiz/Logic/Game.swift Thu Sep 22 10:42:39 2022 +0200 @@ -0,0 +1,121 @@ +// +// GameProtocol.swift +// GeoQuiz +// +// Created by Dennis Concepción Martín on 18/9/22. +// + +import Foundation +import SwiftUI + +protocol Game: ObservableObject { + + // Define generic type + associatedtype T: Equatable + + // Game + var data: [String: T] { get set} + var dataAsked: [String] { get set } + var correctAnswer: (key: String, value: T) { get set } + + // User + var userChoices: [String: T] { get set } + var userScore: Int { get set } + var userLives: Int { get set } + + // Alerts + var alertTitle: String { get set } + var alertMessage: String { get set } + var showingNoLivesAlert: Bool { get set } + var showingEndGameAlert: Bool { get set } + var showingWrongAnswerAlert: Bool { get set } + + // Animations + var scoreScaleAmount: Double { get set } + var livesScaleAmount: Double { get set } + + // Modal views + var showingBuyLivesView: Bool { get set } +} + +extension Game { + var questionCounter: Int { + dataAsked.count + } + + func askQuestion() { + guard questionCounter < data.count else { + alertTitle = "Amazing!" + alertMessage = "You've completed the game." + showingEndGameAlert = true + + return + } + + // Get random choices + var userChoices = [String: T]() + + while userChoices.count < 2 { + if let choice = data.randomElement() { + userChoices[choice.key] = choice.value + } else { + fatalError("Couldn't get a random value from data") + } + } + + // Get question asked (correct answer) + let correctAnswer = data.first(where: { + !userChoices.keys.contains($0.key) && !dataAsked.contains($0.key) + }) + + // Unwrap optional + if let correctAnswer = correctAnswer { + userChoices[correctAnswer.key] = correctAnswer.value + dataAsked.append(correctAnswer.key) + self.correctAnswer = correctAnswer + } else { + fatalError("Couldn't unwrap optional value") + } + + self.userChoices = userChoices + } + + func answer(_ choice: (key: String, value: T)) { + guard userLives > 0 else { + alertTitle = "Not enough lives!" + alertMessage = "Please buy more lives to keep playing" + showingNoLivesAlert = true + + return + } + + if correctAnswer == choice { + hapticSuccess() + userScore += 1 + + withAnimation(.easeIn(duration: 0.5)) { + scoreScaleAmount += 1 + } + + askQuestion() + } else { + hapticError() + userLives -= 1 + + withAnimation(.easeIn(duration: 0.5)) { + livesScaleAmount += 1 + } + + alertTitle = "Wrong!" + alertMessage = "You have \(userLives) lives left" + showingWrongAnswerAlert = true + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [self] in + withAnimation(.easeIn(duration: 0.5)) { + scoreScaleAmount = 1 + livesScaleAmount = 1 + } + } + } +}
--- a/GeoQuiz/Logic/GameProtocol.swift Tue Sep 20 11:54:06 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -// -// GameProtocol.swift -// GeoQuiz -// -// Created by Dennis Concepción Martín on 18/9/22. -// - -import Foundation - -protocol Game { - var userScore: Int { get } - var userLives: Int { get } - var questionCounter: Int { get } - var alertTitle: String { get } - var alertMessage: String { get } - var showingBuyLivesView: Bool { get set } - var showingNoLivesAlert: Bool { get set } - var showingWrongAnswerAlert: Bool { get set } - var showingEndGameAlert: Bool { get set } - - func askQuestion() - func answered(userChoice: String) - -}
--- a/GeoQuiz/Logic/GuessTheCapital.swift Tue Sep 20 11:54:06 2022 +0200 +++ b/GeoQuiz/Logic/GuessTheCapital.swift Thu Sep 22 10:42:39 2022 +0200 @@ -7,109 +7,111 @@ import Foundation -class GuessTheCapital: Game, ObservableObject { - - struct Country: Hashable { - let capitalName: String - let flagSymbol: String - } - - let countries: [String: Country] - var countriesAsked = [String: Country]() - - @Published var userScore = 0 - @Published var userLives = 3 - @Published var questionCounter = 0 - @Published var alertTitle = "" - @Published var alertMessage = "" - @Published var showingBuyLivesView = false - @Published var showingNoLivesAlert = false - @Published var showingWrongAnswerAlert = false - @Published var showingEndGameAlert = false - - @Published var userChoices = [String: Country]() - @Published var countryNameAsked = "" - - init() { - let flags: CountryFlags = load("CountryFlags.json") - let capitals: CountryCapitals = load("CountryCapitals.json") - - var countries = [String: Country]() - - for country in capitals.countries { - let countryName = country.key - let capitalName = country.value - - if let flagSymbol = flags.countries[countryName] { - countries[country.key] = Country(capitalName: capitalName, flagSymbol: flagSymbol) - } else { - fatalError() - } - } - - self.countries = countries - askQuestion() - } +class GuessTheCapital: ObservableObject { - func askQuestion() { - guard questionCounter < countries.count else { - self.alertTitle = "Amazing!" - self.alertMessage = "You've completed the game." - self.showingEndGameAlert = true - - return - } - - var userChoices = [String: Country]() - - while userChoices.count < 2 { - if let country = countries.randomElement() { - userChoices[country.key] = country.value - } else { - fatalError("Couldn't get a random country") - } - } - - let countryAsked = countries.first(where: { - !userChoices.keys.contains($0.key) && - !countriesAsked.keys.contains($0.key) - }) - - if let countryAsked = countryAsked { - userChoices[countryAsked.key] = countryAsked.value - self.countriesAsked[countryAsked.key] = countryAsked.value - self.countryNameAsked = countryAsked.key - } else { - fatalError("Couldn't get countryAsked") - } - - self.userChoices = userChoices - self.questionCounter += 1 - } - - func answered(userChoice userCapitalNameGuess: String) { - guard let correctCountry = countries[countryNameAsked] else { - fatalError("Couln't find \(countryNameAsked) in countries dictionary") - } - - guard userLives > 0 else { - self.alertTitle = "Not enough lives!" - self.alertMessage = "Please buy more lives to keep playing" - self.showingNoLivesAlert = true - - return - } - - if correctCountry.capitalName == userCapitalNameGuess { - hapticSuccess() - self.userScore += 1 - askQuestion() - } else { - hapticError() - self.userLives -= 1 - self.alertTitle = "Wrong!" - self.alertMessage = "The capital of \(countryNameAsked) is \(correctCountry.capitalName). You have \(userLives) lives left" - self.showingWrongAnswerAlert = true - } - } +// struct Country: Hashable { +// let capitalName: String +// let flagSymbol: String +// } +// +// let countries: [String: Country] +// var countriesAsked = [String: Country]() +// +// @Published var userScore = 0 +// @Published var userLives = 3 +// @Published var questionCounter = 0 +// @Published var alertTitle = "" +// @Published var alertMessage = "" +// @Published var scoreScaleAmount = 1.0 +// @Published var livesScaleAmount = 1.0 +// @Published var showingBuyLivesView = false +// @Published var showingNoLivesAlert = false +// @Published var showingWrongAnswerAlert = false +// @Published var showingEndGameAlert = false +// +// @Published var userChoices = [String: Country]() +// @Published var countryNameAsked = "" +// +// init() { +// let flags: CountryFlags = load("CountryFlags.json") +// let capitals: CountryCapitals = load("CountryCapitals.json") +// +// var countries = [String: Country]() +// +// for country in capitals.countries { +// let countryName = country.key +// let capitalName = country.value +// +// if let flagSymbol = flags.countries[countryName] { +// countries[country.key] = Country(capitalName: capitalName, flagSymbol: flagSymbol) +// } else { +// fatalError() +// } +// } +// +// self.countries = countries +// askQuestion() +// } +// +// func askQuestion() { +// guard questionCounter < countries.count else { +// self.alertTitle = "Amazing!" +// self.alertMessage = "You've completed the game." +// self.showingEndGameAlert = true +// +// return +// } +// +// var userChoices = [String: Country]() +// +// while userChoices.count < 2 { +// if let country = countries.randomElement() { +// userChoices[country.key] = country.value +// } else { +// fatalError("Couldn't get a random country") +// } +// } +// +// let countryAsked = countries.first(where: { +// !userChoices.keys.contains($0.key) && +// !countriesAsked.keys.contains($0.key) +// }) +// +// if let countryAsked = countryAsked { +// userChoices[countryAsked.key] = countryAsked.value +// self.countriesAsked[countryAsked.key] = countryAsked.value +// self.countryNameAsked = countryAsked.key +// } else { +// fatalError("Couldn't get countryAsked") +// } +// +// self.userChoices = userChoices +// self.questionCounter += 1 +// } +// +// func answered(userChoice userCapitalNameGuess: String) { +// guard let correctCountry = countries[countryNameAsked] else { +// fatalError("Couln't find \(countryNameAsked) in countries dictionary") +// } +// +// guard userLives > 0 else { +// self.alertTitle = "Not enough lives!" +// self.alertMessage = "Please buy more lives to keep playing" +// self.showingNoLivesAlert = true +// +// return +// } +// +// if correctCountry.capitalName == userCapitalNameGuess { +// hapticSuccess() +// self.userScore += 1 +// askQuestion() +// } else { +// hapticError() +// self.userLives -= 1 +// self.alertTitle = "Wrong!" +// self.alertMessage = "The capital of \(countryNameAsked) is \(correctCountry.capitalName). You have \(userLives) lives left" +// self.showingWrongAnswerAlert = true +// } +// } }
--- a/GeoQuiz/Logic/GuessTheFlag.swift Tue Sep 20 11:54:06 2022 +0200 +++ b/GeoQuiz/Logic/GuessTheFlag.swift Thu Sep 22 10:42:39 2022 +0200 @@ -6,89 +6,39 @@ // import Foundation +import SwiftUI -class GuessTheFlag: Game, ObservableObject { - let countries: [String: String] - var countriesAsked = [String: String]() +class GuessTheFlag: Game, ObservableObject { + + // Define type of generics + var data: [String: String] + var dataAsked = [String]() + // Data + @Published var correctAnswer = (key: String(), value: String()) + + // User + @Published var userChoices = [String: String]() @Published var userScore = 0 @Published var userLives = 3 - @Published var questionCounter = 0 - @Published var alertTitle = "" - @Published var alertMessage = "" - @Published var showingBuyLivesView = false + + // Alerts + @Published var alertTitle = String() + @Published var alertMessage = String() @Published var showingNoLivesAlert = false + @Published var showingEndGameAlert = false @Published var showingWrongAnswerAlert = false - @Published var showingEndGameAlert = false - @Published var userChoices = [String: String]() - @Published var countryNameAsked = "" + // Animations + @Published var scoreScaleAmount = 1.0 + @Published var livesScaleAmount = 1.0 + + // Modal views + @Published var showingBuyLivesView = false init() { let flags: CountryFlags = load("CountryFlags.json") - self.countries = flags.countries + data = flags.countries askQuestion() } - - func askQuestion() { - guard questionCounter < countries.count else { - self.alertTitle = "Amazing!" - self.alertMessage = "You've completed the game." - self.showingEndGameAlert = true - - return - } - - var userChoices = [String: String]() - - while userChoices.count < 2 { - if let country = countries.randomElement() { - userChoices[country.key] = country.value - } else { - fatalError("Couldn't get a random country") - } - } - - let countryAsked = countries.first(where: { - !userChoices.keys.contains($0.key) && - !countriesAsked.keys.contains($0.key) - }) - - if let countryAsked = countryAsked { - userChoices[countryAsked.key] = countryAsked.value - self.countriesAsked[countryAsked.key] = countryAsked.value - self.countryNameAsked = countryAsked.key - } else { - fatalError("Couldn't get countryAsked") - } - - self.userChoices = userChoices - self.questionCounter += 1 - } - - func answered(userChoice userFlagSymbolGuess: String) { - guard let correctFlagSymbolAnswer = countries[countryNameAsked] else { - fatalError("Couln't find \(countryNameAsked) in countries dictionary") - } - - guard userLives > 0 else { - self.alertTitle = "Not enough lives!" - self.alertMessage = "Please buy more lives to keep playing" - self.showingNoLivesAlert = true - - return - } - - if correctFlagSymbolAnswer == userFlagSymbolGuess { - hapticSuccess() - self.userScore += 1 - askQuestion() - } else { - hapticError() - self.userLives -= 1 - self.alertTitle = "Wrong!" - self.alertMessage = "That's not the flag of \(countryNameAsked). You have \(userLives) lives left" - self.showingWrongAnswerAlert = true - } - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeoQuiz/Tests/Animation.swift Thu Sep 22 10:42:39 2022 +0200 @@ -0,0 +1,40 @@ +// +// Animation.swift +// GeoQuiz +// +// Created by Dennis Concepción Martín on 21/9/22. +// + +import SwiftUI + +struct Animation: View { + @State private var amount = 1.0 + + var body: some View { + Button { + withAnimation(.easeIn(duration: 0.5)) { + amount += 1 + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + withAnimation(.easeIn(duration: 0.5)) { + amount = 1 + } + } + } label: { + Circle() + .overlay( + Text("Button") + .foregroundColor(.white) + ) + } + .frame(height: 100) + .scaleEffect(amount) + } +} + +struct Animation_Previews: PreviewProvider { + static var previews: some View { + Animation() + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeoQuiz/Tests/ClassTest.swift Thu Sep 22 10:42:39 2022 +0200 @@ -0,0 +1,8 @@ +// +// ClassTest.swift +// GeoQuiz +// +// Created by Dennis Concepción Martín on 21/9/22. +// + +import Foundation
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeoQuiz/Tests/ProtocolTest.swift Thu Sep 22 10:42:39 2022 +0200 @@ -0,0 +1,23 @@ +// +// ProtocolTest.swift +// GeoQuiz +// +// Created by Dennis Concepción Martín on 21/9/22. +// + +import Foundation + +//protocol MyProtocol { +// associatedtype T +// +// var myVar: T { get set } +// func sayHello() +//} +// +//extension MyProtocol { +// func sayHello() { +// print("Hello") +// } +//} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeoQuiz/Tests/Protocols.swift Thu Sep 22 10:42:39 2022 +0200 @@ -0,0 +1,24 @@ +// +// Protocols.swift +// GeoQuiz +// +// Created by Dennis Concepción Martín on 21/9/22. +// + +import Foundation + +//protocol MyProtocol { +// associatedtype T +// +// var myVar: T { get set } +//} +// +//class MyClass { +// typealias T = String +// +// let myVar: T +// +// init() { +// myVar = "Hello, world!" +// } +//}