Could anyone tell me what is wrong with the logic?
I make a game of Fifteen and faced a problem.
I need to be sure all the fifteen buttons are arranged in a proper way:
logic:
Every time a button is touched
1. function makeMove() changes the position of a button .
2. function checkGameOver() checks whether all the button are arranged properly,
if yes, then function showAlert() make a pop-up window appear.
problem:
when all the buttons are placed, showAlert() does not fire and
I need to touch again any button to get the pop-up window
Thank you.
func makeMove(button: UIButton) {
var currentButtonNumber = button.tag - 1
if ( currentButtonNumber >= 0 && currentButtonNumber <= 15 ) && button.tag != 4 && button.tag != 8 && button.tag != 12 {
guard buttons[button.tag - 1].backgroundColor != .none else {
buttons[button.tag - 1].backgroundColor = .yellow
buttons[button.tag - 1].setTitle(button.titleLabel?.text, for: .normal)
buttons[button.tag].backgroundColor = .none
button.setTitle("", for: .normal)
return
}
}
currentButtonNumber = button.tag + 1
if ( currentButtonNumber >= 0 && currentButtonNumber <= 15 ) && button.tag != 3 && button.tag != 7 && button.tag != 11 {
guard buttons[button.tag + 1].backgroundColor != .none else {
buttons[button.tag + 1].backgroundColor = .yellow
buttons[button.tag + 1].setTitle(button.titleLabel?.text, for: .normal)
buttons[button.tag].backgroundColor = .none
button.setTitle("", for: .normal)
return
}
}
currentButtonNumber = button.tag - 4
if currentButtonNumber >= 0 && currentButtonNumber <= 15 {
guard buttons[button.tag - 4].backgroundColor != .none else {
buttons[button.tag - 4].backgroundColor = .yellow
buttons[button.tag - 4].setTitle(button.titleLabel?.text, for: .normal)
buttons[button.tag].backgroundColor = .none
button.setTitle("", for: .normal)
return
}
}
currentButtonNumber = button.tag + 4
if currentButtonNumber >= 0 && currentButtonNumber <= 15 {
guard buttons[button.tag + 4].backgroundColor != .none else {
buttons[button.tag + 4].backgroundColor = .yellow
buttons[button.tag + 4].setTitle(button.titleLabel?.text, for: .normal)
buttons[button.tag].backgroundColor = .none
button.setTitle("", for: .normal)
return
}
}
}
func showAlert() {
var minutes = 0
var seconds = 0
if timerCounter < 60 {
seconds = timerCounter
} else if timerCounter == 60 {
minutes = 1
seconds = 0
} else {
seconds = timerCounter % 60
minutes = (timerCounter - seconds) / 60
}
let alert = UIAlertController(title: "Congratulations!",
message: "You spent \(minutes) minutes and \(seconds) seconds", preferredStyle: .alert)
let action = UIAlertAction(title: "OK",
style: .default, handler: {
action in
self.setNewGame()
})
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
func checkGameOver() -> Bool {
var isGameOver = false
var rightOrderCounter = 0
for number in 0...14 {
if (buttons[number].titleLabel?.text == String(number + 1)) {
rightOrderCounter += 1
} else {
rightOrderCounter = 0
break
}
}
if rightOrderCounter == 15 {
isGameOver = true
}
return isGameOver
}
@IBAction func moveButton(button: UIButton) {
makeMove(button: button)
if self.checkGameOver() {
self.stopTimer()
self.showAlert()
}
}
Your mistake is using the button's titles as your model. The problem is that setting a button's title with setTitle(:for:)
doesn't necessarily happen until after you leave the main thread. So when you check the current titleLabel
, it hasn't been updated yet and reflects the previous state.
A better approach is to use an array of Int
to model your puzzle, and update the button's titles using this array. Your checkGameOver()
method should check the order of this array instead of the button's titles.
The general rule of thumb is: Never use UI elements to store your state