diff --git a/ios/FlankerNativeComponents/GameManager.swift b/ios/FlankerNativeComponents/GameManager.swift index 2e3d124ac..8e08f41a5 100644 --- a/ios/FlankerNativeComponents/GameManager.swift +++ b/ios/FlankerNativeComponents/GameManager.swift @@ -26,7 +26,6 @@ enum Constants { static let bigFont: UIFont = .systemFont(ofSize: 50.0, weight: .bold) static let smallFont: UIFont = .systemFont(ofSize: 30.0, weight: .bold) static let tag: String = "trial" - static let trialTag: String = "html-button-response" } enum TypeTimeStamps { @@ -54,7 +53,7 @@ class GameManager { private var timerSetText: Timer? private var timeResponse: Timer? - private var countTest = 0 + private var countTest = -1 private var countAllGame = 0 private var correctAnswers = 0 @@ -72,8 +71,7 @@ class GameManager { private var endFeedbackTimestamp: Double? private var respondTouchButton: Double? - - private var isFirst = true + private var hasRespondedInCurrentTrial = false private var gameParameters: ParameterModel? @@ -93,10 +91,10 @@ class GameManager { isShowFixations = parameters.showFixation isShowResults = parameters.showResults countAllGame = parameters.trials.count - updateButtonTitle() resultManager.cleanData() - countTest = 0 + countTest = -1 correctAnswers = 0 + arrayTimes = [] startLogicTimer() } @@ -110,70 +108,12 @@ class GameManager { switch type { case .fixations: startFixationsTimestamp = time - - if - let startFeedbackTimestamp = startFeedbackTimestamp, - let endFeedbackTimestamp = endFeedbackTimestamp, - let gameParameters = gameParameters, - gameParameters.showFeedback { - let resultTime = (time - startFeedbackTimestamp) * 1000 - let model = FlankerModel(rt: resultTime, - stimulus: "
\(responseText)
", - button_pressed: nil, - image_time: endFeedbackTimestamp * 1000, - correct: nil, - start_timestamp: 0, - tag: "feedback", - trial_index: countTest, - start_time: startFeedbackTimestamp * 1000, - response_touch_timestamp: 0) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - resultManager.addStepData(data: model) - } case .trial: - startTrialTimestamp = time - - if - let startFixationsTimestamp = startFixationsTimestamp, - let endFixationsTimestamp = endFixationsTimestamp, - let gameParameters = gameParameters, - gameParameters.showFixation { - let resultTime = (time - startFixationsTimestamp) * 1000 - let model = FlankerModel(rt: resultTime, - stimulus: "
-----
", - button_pressed: nil, - image_time: endFixationsTimestamp * 1000, - correct: nil, - start_timestamp: 0, - tag: "fixation", - trial_index: countTest + 1, - start_time: startFixationsTimestamp * 1000, - response_touch_timestamp: 0) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - resultManager.addStepData(data: model) - } else if - let startFeedbackTimestamp = startFeedbackTimestamp, - let endFeedbackTimestamp = endFeedbackTimestamp, - let gameParameters = gameParameters, - gameParameters.showFeedback, - !gameParameters.showFixation { - let resultTime = (time - startFeedbackTimestamp) * 1000 - let model = FlankerModel(rt: resultTime, - stimulus: "
\(responseText)
", - button_pressed: nil, - image_time: endFeedbackTimestamp * 1000, - correct: nil, - start_timestamp: 0, - tag: "feedback", - trial_index: countTest, - start_time: startFeedbackTimestamp * 1000, - response_touch_timestamp: 0) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - resultManager.addStepData(data: model) - } + break case .feedback: startFeedbackTimestamp = time - case .response: break + case .response: + break } } else { switch type { @@ -184,108 +124,56 @@ class GameManager { case .feedback: endFeedbackTimestamp = time case .response: - respondTouchButton = time + break } } } func checkedAnswer(button: SelectedButton) { + guard !hasRespondedInCurrentTrial else { return } + hasRespondedInCurrentTrial = true + respondTouchButton = CACurrentMediaTime() invalidateTimers() delegate?.setEnableButton(isEnable: false) guard let gameParameters = gameParameters else { return } - guard - let startTrialTimestamp = startTrialTimestamp, - let endTrialTimestamp = endTrialTimestamp, - let respondTouchButton = respondTouchButton - else { return } - let resultTime = (respondTouchButton - startTrialTimestamp) * 1000 - arrayTimes.append(resultTime.convertToInt()) + guard let startTrialTimestamp = startTrialTimestamp else { return } + var resultTime = (respondTouchButton! - startTrialTimestamp) * 1000 + + arrayTimes.append(Int(resultTime)) delegate?.updateTime(time: String(format: "%.3f", resultTime)) - switch button { - case .left: - if gameParameters.trials[countTest].correctChoice == 0 { - correctAnswers += 1 - let model = FlankerModel(rt: resultTime, - stimulus: text, - button_pressed: "0", - image_time: endTrialTimestamp * 1000, - correct: true, - start_timestamp: 0, - tag: Constants.tag, - trial_index: countTest + 1, - start_time: startTrialTimestamp * 1000, - response_touch_timestamp: respondTouchButton * 1000) - - resultManager.addStepData(data: model) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - if gameParameters.showFeedback { - delegate?.updateText(text: Constants.correctText, color: Constants.greenColor, font: Constants.smallFont, isStart: false, typeTime: .feedback) - } - responseText = Constants.correctText - } else { - let model = FlankerModel(rt: resultTime, - stimulus: text, - button_pressed: "0", - image_time: endTrialTimestamp * 1000, - correct: false, - start_timestamp: 0, - tag: Constants.tag, - trial_index: countTest + 1, - start_time: startTrialTimestamp * 1000, - response_touch_timestamp: respondTouchButton * 1000) - - resultManager.addStepData(data: model) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - if gameParameters.showFeedback { - delegate?.updateText(text: Constants.inCorrectText, color: Constants.redColor, font: Constants.smallFont, isStart: false, typeTime: .feedback) - } - responseText = Constants.inCorrectText - } - case .right: - if gameParameters.trials[countTest].correctChoice == 1 { - correctAnswers += 1 - let model = FlankerModel(rt: resultTime, - stimulus: text, - button_pressed: "1", - image_time: endTrialTimestamp * 1000, - correct: true, - start_timestamp: 0, - tag: Constants.tag, - trial_index: countTest + 1, - start_time: startTrialTimestamp * 1000, - response_touch_timestamp: respondTouchButton * 1000) - - resultManager.addStepData(data: model) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - if gameParameters.showFeedback { - delegate?.updateText(text: Constants.correctText, color: Constants.greenColor, font: Constants.smallFont, isStart: false, typeTime: .feedback) - } - responseText = Constants.correctText - } else { - let model = FlankerModel(rt: resultTime, - stimulus: text, - button_pressed: "1", - image_time: endTrialTimestamp * 1000, - correct: false, - start_timestamp: 0, - tag: Constants.tag, - trial_index: countTest + 1, - start_time: startTrialTimestamp * 1000, - response_touch_timestamp: respondTouchButton * 1000) - - resultManager.addStepData(data: model) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - if gameParameters.showFeedback { - delegate?.updateText(text: Constants.inCorrectText, color: Constants.redColor, font: Constants.smallFont, isStart: false, typeTime: .feedback) - } - responseText = Constants.inCorrectText - } + + endTrialTimestamp = respondTouchButton + + startFeedbackTimestamp = CACurrentMediaTime() + + let correctChoice = gameParameters.trials[countTest].correctChoice + let isCorrect = (button == .left && correctChoice == 0) || (button == .right && correctChoice == 1) + if isCorrect { + correctAnswers += 1 } + let buttonPressed = (button == .left) ? "0" : "1" + let model = FlankerModel(rt: resultTime, + stimulus: text, + button_pressed: buttonPressed, + image_time: endTrialTimestamp! * 1000, + correct: isCorrect, + start_timestamp: 0, + tag: Constants.tag, + trial_index: countTest + 1, + start_time: startTrialTimestamp * 1000, + response_touch_timestamp: respondTouchButton! * 1000) + + resultManager.addStepData(data: model) + delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: false, minAccuracy: gameParameters.minimumAccuracy) + if gameParameters.showFeedback { - let timer = Timer(timeInterval: Constants.lowTimeInterval, target: self, selector: #selector(self.setDefaultText), userInfo: nil, repeats: false) + let feedbackText = isCorrect ? Constants.correctText : Constants.inCorrectText + let feedbackColor = isCorrect ? Constants.greenColor : Constants.redColor + delegate?.updateText(text: feedbackText, color: feedbackColor, font: Constants.smallFont, isStart: false, typeTime: .feedback) + let timer = Timer(timeInterval: Constants.lowTimeInterval, target: self, selector: #selector(setDefaultText), userInfo: nil, repeats: false) RunLoop.main.add(timer, forMode: .common) } else { setDefaultText(isFirst: false) @@ -295,24 +183,30 @@ class GameManager { @objc func setDefaultText(isFirst: Bool) { guard let gameParameters = gameParameters else { return } + hasRespondedInCurrentTrial = false + delegate?.setEnableButton(isEnable: false) + if !isFirst { + endFeedbackTimestamp = CACurrentMediaTime() countTest += 1 + } else { + countTest = 0 } - - if gameParameters.showFixation { - if let image = URL(string: gameParameters.fixation), gameParameters.fixation.contains("https") { - delegate?.updateFixations(image: image, isStart: false, typeTime: .fixations) - } else { - delegate?.updateText(text: gameParameters.fixation, color: .black, font: Constants.bigFont, isStart: false, typeTime: .fixations) - } + if isEndGame() { + handleEndOfGame() + return } - if isEndGame() { return } - invalidateTimers() updateButtonTitle() if gameParameters.showFixation { + startFixationsTimestamp = CACurrentMediaTime() + if let image = URL(string: gameParameters.fixation), gameParameters.fixation.contains("https") { + delegate?.updateFixations(image: image, isStart: true, typeTime: .fixations) + } else { + delegate?.updateText(text: gameParameters.fixation, color: .black, font: Constants.bigFont, isStart: true, typeTime: .fixations) + } timerSetText = Timer(timeInterval: gameParameters.fixationDuration / 1000, target: self, selector: #selector(setText), userInfo: nil, repeats: false) RunLoop.main.add(timerSetText!, forMode: .common) } else { @@ -322,8 +216,14 @@ class GameManager { @objc func setText() { guard let gameParameters = gameParameters else { return } + guard countTest < gameParameters.trials.count else { + handleEndOfGame() + return + } - delegate?.setEnableButton(isEnable: true) + endFixationsTimestamp = CACurrentMediaTime() + + startTrialTimestamp = CACurrentMediaTime() text = gameParameters.trials[countTest].stimulus.en @@ -333,27 +233,32 @@ class GameManager { delegate?.updateText(text: text, color: .black, font: Constants.bigFont, isStart: true, typeTime: .trial) } - timeResponse = Timer(timeInterval: gameParameters.trialDuration / 1000, target: self, selector: #selector(self.timeResponseFailed), userInfo: nil, repeats: false) - RunLoop.main.add(timeResponse!, forMode: .common) + DispatchQueue.main.asyncAfter(deadline: .now()) { + self.delegate?.setEnableButton(isEnable: true) + self.timeResponse = Timer(timeInterval: gameParameters.trialDuration / 1000, target: self, selector: #selector(self.timeResponseFailed), userInfo: nil, repeats: false) + RunLoop.main.add(self.timeResponse!, forMode: .common) + } } @objc func timeResponseFailed() { guard let gameParameters = gameParameters else { return } delegate?.setEnableButton(isEnable: false) + + endTrialTimestamp = CACurrentMediaTime() + + startFeedbackTimestamp = CACurrentMediaTime() + if gameParameters.showFeedback { delegate?.updateText(text: Constants.timeRespondText, color: .black, font: Constants.smallFont, isStart: false, typeTime: .feedback) } - guard - let startTrialTimestamp = startTrialTimestamp, - let endTrialTimestamp = endTrialTimestamp - else { return } + guard let startTrialTimestamp = startTrialTimestamp else { return } let model = FlankerModel(rt: 0.0, stimulus: text, button_pressed: nil, - image_time: endTrialTimestamp * 1000, // має намалювати + image_time: endTrialTimestamp! * 1000, // має намалювати correct: false, start_timestamp: 0, // вже намальовано tag: Constants.tag, @@ -362,93 +267,93 @@ class GameManager { response_touch_timestamp: 0) resultManager.addStepData(data: model) - delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - responseText = Constants.timeRespondText + delegate?.resultTest(avrgTime: nil, procentCorrect: nil, data: model, dataArray: nil,isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - let timer = Timer(timeInterval: Constants.lowTimeInterval, target: self, selector: #selector(self.setDefaultText), userInfo: nil, repeats: false) - RunLoop.main.add(timer, forMode: .common) + if gameParameters.showFeedback { + let timer = Timer(timeInterval: Constants.lowTimeInterval, target: self, selector: #selector(setDefaultText), userInfo: nil, repeats: false) + RunLoop.main.add(timer, forMode: .common) + } else { + setDefaultText(isFirst: false) + } } - func clearData() { - resultManager.cleanData() - countTest = 0 - correctAnswers = 0 - arrayTimes = [] - invalidateTimers() - } -} + func handleEndOfGame() { + guard let gameParameters = gameParameters else { return } -private extension GameManager { - func randomString(length: Int = 5) -> String { - let letters = "<>" - let lettersArray = Array(letters) - let symbolArray = [">>", "<<", "--"] + let sumArray = arrayTimes.reduce(0, +) + let avrgArray = arrayTimes.count > 0 ? sumArray / arrayTimes.count : 0 + let procentsCorrect = Float(correctAnswers) / Float(countAllGame) * 100 - let randSymbol = arc4random_uniform(UInt32(symbolArray.count)) - let randLetters = arc4random_uniform(UInt32(lettersArray.count)) + clearData() + + delegate?.setEnableButton(isEnable: false) - return symbolArray[Int(randSymbol)] + String(lettersArray[Int(randLetters)]) + symbolArray[Int(randSymbol)] + delegate?.resultTest( + avrgTime: avrgArray, + procentCorrect: Int(procentsCorrect), + data: nil, + dataArray: resultManager.oneGameDataResult, + isShowResults: gameParameters.showResults, + minAccuracy: gameParameters.minimumAccuracy + ) } func isEndGame() -> Bool { guard let gameParameters = gameParameters else { return false } - if countTest == gameParameters.trials.count { - let sumArray = arrayTimes.reduce(0, +) - var avrgArray: Int = 0 - if arrayTimes.count != 0 { - avrgArray = sumArray / arrayTimes.count - } - let procentsCorrect = Float(correctAnswers) / Float(countAllGame) * 100 - if !gameParameters.showFixation { - setEndTimeViewingImage(time: CACurrentMediaTime(), isStart: true, type: .fixations) - } - delegate?.resultTest(avrgTime: avrgArray, procentCorrect: Int(procentsCorrect), data: nil, dataArray: resultManager.oneGameDataResult, isShowResults: gameParameters.showResults, minAccuracy: gameParameters.minimumAccuracy) - clearData() - return true - } else { - return false - } + return countTest >= gameParameters.trials.count + } + + func clearData() { + resultManager.cleanData() + countTest = -1 + correctAnswers = 0 + arrayTimes = [] + invalidateTimers() } func invalidateTimers() { timeResponse?.invalidate() timerSetText?.invalidate() } +} +private extension GameManager { func updateButtonTitle() { guard let gameParameters = gameParameters else { return } + guard countTest < gameParameters.trials.count else { return } - if gameParameters.trials[countTest].choices.count == 2 { - if - let leftImage = URL(string: gameParameters.trials[countTest].choices[0].name.en), - let rightImage = URL(string: gameParameters.trials[countTest].choices[1].name.en), - gameParameters.trials[countTest].choices[0].name.en.contains("https"), - gameParameters.trials[countTest].choices[1].name.en.contains("https") { - delegate?.updateTitleButton(left: nil, right: nil, leftImage: leftImage, rightImage: rightImage, countButton: 2) - } - else if - let leftImage = URL(string: gameParameters.trials[countTest].choices[0].name.en), - gameParameters.trials[countTest].choices[0].name.en.contains("https"), - !gameParameters.trials[countTest].choices[1].name.en.contains("https") { - delegate?.updateTitleButton(left: nil, right: gameParameters.trials[countTest].choices[1].name.en, leftImage: leftImage, rightImage: nil, countButton: 2) - } - else if - let rightImage = URL(string: gameParameters.trials[countTest].choices[1].name.en), - gameParameters.trials[countTest].choices[1].name.en.contains("https"), - !gameParameters.trials[countTest].choices[0].name.en.contains("https") { - delegate?.updateTitleButton(left: gameParameters.trials[countTest].choices[0].name.en, right: nil, leftImage: nil, rightImage: rightImage, countButton: 2) - } - else { - delegate?.updateTitleButton(left: gameParameters.trials[countTest].choices[0].name.en, right: gameParameters.trials[countTest].choices[1].name.en, leftImage: nil, rightImage: nil, countButton: 2) + let choices = gameParameters.trials[countTest].choices + let countButton = choices.count + + var leftTitle: String? = nil + var rightTitle: String? = nil + var leftImage: URL? = nil + var rightImage: URL? = nil + + if countButton >= 1 { + let leftChoice = choices[0].name.en + if let url = URL(string: leftChoice), leftChoice.contains("https") { + leftImage = url + } else { + leftTitle = leftChoice } - } else { - if - let leftImage = URL(string: gameParameters.trials[countTest].choices[0].name.en), - gameParameters.trials[countTest].choices[0].name.en.contains("https") { - delegate?.updateTitleButton(left: nil, right: nil, leftImage: leftImage, rightImage: nil, countButton: 1) + } + + if countButton == 2 { + let rightChoice = choices[1].name.en + if let url = URL(string: rightChoice), rightChoice.contains("https") { + rightImage = url } else { - delegate?.updateTitleButton(left: gameParameters.trials[countTest].choices[0].name.en, right: nil, leftImage: nil, rightImage: nil, countButton: 1) + rightTitle = rightChoice } } + + delegate?.updateTitleButton( + left: leftTitle, + right: rightTitle, + leftImage: leftImage, + rightImage: rightImage, + countButton: countButton + ) } }