SwiftAh99
SwiftAh99

Reputation: 1

cs193p Concentration: Emoji display card flip delayed for cards that were already displayed, after new game is pressed

I've successfully implemented a solution for the first assignment of Fall 2017 - Concentration.

Everything works fine, except that whenever you flip a card that was already displayed, after touching new game, there's a small lag/fade animation. This doesn't happen when you flip the card for the first time on the first game (see the video in the link below)

Problematic behaviour example

I thought this was a bug, but I checked other solutions and all of them have the same lag.

If anyone can give me a hint or knows why there is this lag, would really make me happy =)

Example solutions that have the same issue:

https://github.com/BestKora/Concentration-CS193P-Fall-2017

https://github.com/jamfly/cs193p-2017-fall-demo-and-solution

https://github.com/TiagoMaiaL/cs193p-assignments

My code for Concentration and ViewController:

class ViewController: UIViewController
{
    private lazy var game = Concentration(numberOfPairsOfCards: numberOfPairsOfCards)

    var numberOfPairsOfCards: Int {
        return (cardButtons.count + 1) / 2
    }

    private let emojiLibrary = ["😀☚ïļðŸ˜˜ðŸĪŠðŸ˜žðŸ˜‡ðŸ˜ŽðŸ˜†ðŸ˜…😭ðŸ˜ģ😠",
                                "🎃ðŸ‘ŧðŸ˜ˆðŸ‘―ðŸ‘đðŸĪĄðŸ­ðŸ”Ū💉⛓💊ðŸ•Ŋ",
                                "🚗🚕🚙🚌🚎🏎🚓🚑🚒🚐🚚🚜",
                                "ðŸķðŸąðŸ­ðŸđ🐰ðŸĶŠðŸŧ🐞ðŸĻðŸŊðŸĶðŸŪ",
                                "âš―ïļðŸ€ðŸˆâšūïļðŸŽūðŸðŸ‰ðŸŽąðŸ“ðŸ’â›ģïļðŸĨŠ",
                                "👏ðŸĪðŸ‘ðŸ‘ŽðŸ‘ŠâœŠðŸĪžðŸĪŸðŸ‘ŒðŸ–ðŸĪ™â˜ïļ"]

    private lazy var theme = emojiLibrary[emojiLibrary.count.arc4random]

    @IBAction func newGame(_ sender: UIButton) {

        // Create new game struct, initialize everything Model
        game = Concentration(numberOfPairsOfCards: numberOfPairsOfCards)

        // Initialize everything view
        gameOverLabel.isHidden = true
        emoji.removeAll()
        theme = emojiLibrary[emojiLibrary.count.arc4random]
        updateViewFromModel(touchedCard: 0)
    }

    @IBOutlet private weak var flipCountLabel: UILabel!

    @IBOutlet private var cardButtons: [UIButton]!

    @IBOutlet weak var gameOverLabel: UILabel!

    @IBOutlet weak var scoreLabel: UILabel!

    @IBAction private func touchCard(_ sender: UIButton) {

        if let cardNumber = cardButtons.index(of: sender) {
            game.chooseCard(at: cardNumber)

            updateCardDisplay(index: cardNumber)
            updateViewFromModel(touchedCard: cardNumber)

            if game.gameOver {
                gameOverLabel.isHidden = false
            }
        } else {
            print("chosen card was not in cardButtons!")
        }
    }

    private func updateCardDisplay(index:Int)
    {
        let card = game.cards[index]
        let button = cardButtons[index]
        if card.isFaceUp {
            button.setTitle(getEmoji(for: card), for: UIControl.State.normal)
            button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        } else {
            button.setTitle("", for: UIControl.State.normal)
            button.backgroundColor = card.isMatched ? #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0) : #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
        }
    }

    private func updateViewFromModel(touchedCard:Int) {
        for index in cardButtons.indices {
            if index != touchedCard {
                let button = cardButtons[index]
                let card = game.cards[index]
                if !card.isFaceUp  {
                    button.setTitle("", for: UIControl.State.normal)
                    button.backgroundColor = card.isMatched ? #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0) : #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
                }
            }
        }

        scoreLabel.text = String("Score: \(game.score)")
        flipCountLabel.text = String("Flips: \(game.flipCount)")
    }

    private var emoji = [Card:String]()

    private func getEmoji(for card: Card) -> String {

        if emoji[card] == nil, theme.count > 0 {
            let randomStringIndex = theme.index(theme.startIndex, offsetBy: theme.count.arc4random)
            emoji[card] = String(theme.remove(at: randomStringIndex))
        }

        return emoji[card] ?? "?"
    }
}

extension Int {
    var arc4random: Int {
        if self > 0 {
            return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else {
            return 0
        }
    }
}



struct Concentration
{
    private(set) var cards: [Card]

    private(set) var gameOver = false
    private(set) var flipCount = 0
    private(set) var score = 0
    private(set) var seenCards: Set<Card>

    private(set) var indexOfOneAndOnlyFaceUpCard: Int? {
        get {
            return cards.indices.filter({cards[$0].isFaceUp}).oneAndOnly
        }
        set {
            for index in cards.indices {
                cards[index].isFaceUp = (index == newValue)
            }
        }
    }

    private var numberOfPlayingCards: Int {
        get {
            return cards.count - cards.indices.filter({cards[$0].isMatched}).count
        }
    }

    mutating func chooseCard(at index: Int) {
        assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)): chosen index not in the cards")

        if indexOfOneAndOnlyFaceUpCard != index && !cards[index].isMatched {
            flipCount += 1
        }

        if !cards[index].isMatched {
            if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index {
                // check if cards match
                if cards[matchIndex] == cards[index] {
                    cards[matchIndex].isMatched = true
                    cards[index].isMatched = true
                    score += 2
                } else {
                    // mismatch of cards
                    if seenCards.contains(cards[index]) {
                        score -= 1
                    } else {
                        seenCards.insert(cards[index])
                    }
                    if seenCards.contains(cards[matchIndex]) {
                        score -= 1
                    } else {
                        seenCards.insert(cards[matchIndex])
                    }
                }

                if numberOfPlayingCards == 0 {
                    gameOver = true
                    cards[matchIndex].isFaceUp = false
                } else {
                    cards[index].isFaceUp = true
                }
            } else {
                indexOfOneAndOnlyFaceUpCard = index
            }
        }
    }

    init(numberOfPairsOfCards: Int) {
        assert(numberOfPairsOfCards > 0, "Concentration.init(\(numberOfPairsOfCards)): you must have at least one pair of cards")

        cards = [Card]()
        for _ in 1...numberOfPairsOfCards {
            let card = Card()
            cards += [card, card]
        }

        cards.shuffle()

        seenCards = Set<Card>()
    }
}

extension Collection {
    var oneAndOnly: Element? {
        return count ==  1 ? first : nil
    }
}

All code was based in the demo, so should be familiar to someone that did the cs193p course/assignments.

Thanks

Upvotes: 0

Views: 338

Answers (0)

Related Questions