view GeoQuiz/Logic/Game.swift @ 4:de54f05adb78

add prototype game stats
author Dennis C. M. <dennis@denniscm.com>
date Thu, 22 Sep 2022 11:38:42 +0200
parents 4dbe0cd9dadc
children f31a61462e7a
line wrap: on
line source

//
//  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 }
    var correctAnswers: [String: T] { get set }
    var wrongAnswers: [String: T] { 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 }
    var showingExitGameAlert: Bool { get set }
    
    // Animations
    var scoreScaleAmount: Double { get set }
    var livesScaleAmount: Double { get set }
    
    // Modal views
    var showingBuyLivesView: Bool { get set }
    var showingGameStatsView: 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()
            
            withAnimation(.easeIn(duration: 0.5)) {
                scoreScaleAmount += 1
                userScore += 1
            }
            
            correctAnswers[correctAnswer.key] = correctAnswer.value
            askQuestion()
        } else {
            hapticError()
            

            withAnimation(.easeIn(duration: 0.5)) {
                livesScaleAmount += 1
                userLives -= 1
            }

            alertTitle = "Wrong!"
            alertMessage = "You have \(userLives) lives left"
            showingWrongAnswerAlert = true
            
            wrongAnswers[choice.key] = choice.value
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [self] in
            withAnimation(.easeIn(duration: 0.5)) {
                scoreScaleAmount = 1
                livesScaleAmount = 1
            }
        }
    }
}