1

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()
        }

    }

John Smith
  • 31
  • 4
  • your question is like an algorithm, is it a homework question? – nima Jan 11 '20 at 13:14
  • show your actual code. Perhaps you are doing the check before moving the button – vacawama Jan 11 '20 at 13:19
  • To novonimo: no, it is not a homework. – John Smith Jan 11 '20 at 14:31
  • To vacawama: when check in dubugger at the very end of IBAction func moveButton(button: UIButton) {...}, I get the old oder of buttons and old titles on them in spite of the fact the buttons had got new titles. After leaving the IBAction func moveButton(button: UIButton) {...}, the order is new, the titles are ok but I need to press any button to call showAlert() and break the order. – John Smith Jan 11 '20 at 14:38

1 Answers1

0

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

vacawama
  • 150,663
  • 30
  • 266
  • 294
  • Thank you, vacawama! Yes, I should have used business logic layer for this purpose. I redid the code, everything woks as it should. – John Smith Jan 12 '20 at 10:17