เชธเซเชตเชฟเชซเซเชŸเชฎเชพเช‚ เชฎเซ‡เชฎเชฐเซ€ เช•เชพเชฐเซเชกเซเชธ เช—เซ‡เชฎ เชฒเช–เชตเซ€

เชธเซเชตเชฟเชซเซเชŸเชฎเชพเช‚ เชฎเซ‡เชฎเชฐเซ€ เช•เชพเชฐเซเชกเซเชธ เช—เซ‡เชฎ เชฒเช–เชตเซ€

เช† เชฒเซ‡เช– เชเช• เชธเชฐเชณ เชฎเซ‡เชฎเชฐเซ€ เชคเชพเชฒเซ€เชฎ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเชพเชจเซ€ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพเชจเซเช‚ เชตเชฐเซเชฃเชจ เช•เชฐเซ‡ เช›เซ‡ เชœเซ‡เชจเซ‹ เชฎเชจเซ‡ เช–เชฐเซ‡เช–เชฐ เช†เชจเช‚เชฆ เช›เซ‡. เชชเซ‹เชคเซ‡ เชธเชพเชฐเชพ เชนเซ‹เชตเชพ เช‰เชชเชฐเชพเช‚เชค, เชคเชฎเซ‡ เชœเชพเช“ เชคเซเชฏเชพเชฐเซ‡ เชคเชฎเซ‡ เชธเซเชตเชฟเชซเซเชŸ เช•เซเชฒเชพเชธ เช…เชจเซ‡ เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒเซเชธ เชตเชฟเชถเซ‡ เชฅเซ‹เชกเซเช‚ เชตเชงเซ เชถเซ€เช–เซ€ เชถเช•เชถเซ‹. เชชเชฐเช‚เชคเซ เช†เชชเชฃเซ‡ เชถเชฐเซ‚ เช•เชฐเซ€เช เชคเซ‡ เชชเชนเซ‡เชฒเชพเช‚, เชšเชพเชฒเซ‹ เชฐเชฎเชคเชจเซ‡ เชœ เชธเชฎเชœเซ€เช.

เช…เชฎเซ‡ เชฏเชพเชฆ เช•เชฐเชพเชตเซ€เช เช›เซ€เช: Habrเชจเชพ เชคเชฎเชพเชฎ เชตเชพเชšเช•เซ‹ เชฎเชพเชŸเซ‡ - Habr เชชเซเชฐเซ‹เชฎเซ‹ เช•เซ‹เชกเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เช•เซ‹เชˆเชชเชฃ เชธเซเช•เชฟเชฒเชฌเซ‹เช•เซเชธ เช•เซ‹เชฐเซเชธเชฎเชพเช‚ เชจเซ‹เช‚เชงเชฃเซ€ เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ 10 เชฐเซ‚เชฌเชฒ เชกเชฟเชธเซเช•เชพเช‰เชจเซเชŸ.

เชธเซเช•เชฟเชฒเชฌเซ‹เช•เซเชธ เชญเชฒเชพเชฎเชฃ เช•เชฐเซ‡ เช›เซ‡: เชถเซˆเช•เซเชทเชฃเชฟเช• เช“เชจเชฒเชพเชˆเชจ เช•เซ‹เชฐเซเชธ "เชชเซเชฐเซ‹เชซเซ‡เชถเชจ เชœเชพเชตเชพ เชกเซ‡เชตเชฒเชชเชฐ".

เชฎเซ‡เชฎเชฐเซ€ เช•เชพเชฐเซเชก เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชฐเชฎเชตเซเช‚

เชฐเชฎเชค เช•เชพเชฐเซเชกเซเชธเชจเชพ เชธเชฎเซ‚เชนเชจเชพ เชชเซเชฐเชฆเชฐเซเชถเชจ เชธเชพเชฅเซ‡ เชถเชฐเซ‚ เชฅเชพเชฏ เช›เซ‡. เชคเซ‡เช“ เชฎเซ‹เชขเชพ เชจเซ€เชšเซ‡ เชธเซ‚เชˆ เชœเชพเชฏ เช›เซ‡ (เช…เชจเซเช•เซเชฐเชฎเซ‡, เช›เชฌเซ€ เชจเซ€เชšเซ‡). เชœเซเชฏเชพเชฐเซ‡ เชคเชฎเซ‡ เช•เซ‹เชˆเชชเชฃ เชเช• เชชเชฐ เช•เซเชฒเชฟเช• เช•เชฐเซ‹ เช›เซ‹, เชคเซเชฏเชพเชฐเซ‡ เช›เชฌเซ€ เชฅเซ‹เชกเซ€ เชธเซ‡เช•เช‚เชก เชฎเชพเชŸเซ‡ เช–เซเชฒเซ‡ เช›เซ‡.

เช–เซ‡เชฒเชพเชกเซ€เชจเซเช‚ เช•เชพเชฐเซเชฏ เช เชœ เชšเชฟเชคเซเชฐเซ‹เชตเชพเชณเชพ เชฌเชงเชพ เช•เชพเชฐเซเชกเซเชธ เชถเซ‹เชงเชตเชพเชจเซเช‚ เช›เซ‡. เชœเซ‹, เชชเซเชฐเชฅเชฎ เช•เชพเชฐเซเชก เช–เซ‹เชฒเซเชฏเชพ เชชเช›เซ€, เชคเชฎเซ‡ เชฌเซ€เชœเชพเชจเซ‡ เชซเซ‡เชฐเชตเซ‹ เช…เชจเซ‡ เชšเชฟเชคเซเชฐเซ‹ เชฎเซ‡เชณ เช–เชพเชฏ, เชคเซ‹ เชฌเช‚เชจเซ‡ เช•เชพเชฐเซเชก เช–เซเชฒเซเชฒเชพ เชฐเชนเซ‡ เช›เซ‡. เชœเซ‹ เชคเซ‡เช“ เชฎเซ‡เชณ เช–เชพเชคเชพ เชจเชฅเซ€, เชคเซ‹ เช•เชพเชฐเซเชก เชซเชฐเซ€เชฅเซ€ เชฌเช‚เชง เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชงเซเชฏเซ‡เชฏ เชฌเชงเซเช‚ เช–เซ‹เชฒเชตเชพเชจเซเช‚ เช›เซ‡.

เชชเซเชฐเซ‹เชœเซ‡เช•เซเชŸ เชฎเชพเชณเช–เซเช‚

เช† เชฐเชฎเชคเชจเซเช‚ เชธเชฐเชณ เชธเช‚เชธเซเช•เชฐเชฃ เชฌเชจเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เชคเชฎเชพเชฐเซ‡ เชจเซ€เชšเซ‡เชจเชพ เช˜เชŸเช•เซ‹เชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡:

  • เชเช• เชจเชฟเชฏเช‚เชคเซเชฐเช•: GameController.swift.
  • เชเช• เชฆเซƒเชถเซเชฏ: CardCell.swift.
  • เชฌเซ‡ เชฎเซ‹เชกเชฒ: MemoryGame.swift เช…เชจเซ‡ Card.swift.
  • เช˜เชŸเช•เซ‹เชจเซ‹ เชธเช‚เชชเซ‚เชฐเซเชฃ เชธเซ‡เชŸ เช‰เชชเชฒเชฌเซเชง เช›เซ‡ เชคเซ‡เชจเซ€ เช–เชพเชคเชฐเซ€ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ Main.storyboard.

เช…เชฎเซ‡ เชฐเชฎเชคเชจเชพ เชธเซŒเชฅเซ€ เชธเชฐเชณ เช˜เชŸเช•, เช•เชพเชฐเซเชกเซเชธเชฅเซ€ เชชเซเชฐเชพเชฐเช‚เชญ เช•เชฐเซ€เช เช›เซ€เช.

เช•เชพเชฐเซเชก.เชธเซเชตเชฟเชซเซเชŸ

เช•เชพเชฐเซเชก เชฎเซ‹เชกเซ‡เชฒเชฎเชพเช‚ เชคเซเชฐเชฃ เช—เซเชฃเชงเชฐเซเชฎเซ‹ เชนเชถเซ‡: เชฆเชฐเซ‡เช•เชจเซ‡ เช“เชณเช–เชตเชพ เชฎเชพเชŸเซ‡ id, เช•เชพเชฐเซเชกเชจเซ€ เชธเซเชฅเชฟเชคเชฟ (เช›เซเชชเชพเชฏเซ‡เชฒ เช…เชฅเชตเชพ เช–เซเชฒเซเชฒเซ€) เชธเซเชชเชทเซเชŸ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชฌเชคเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡เชฒ เชฌเซเชฒเชฟเชฏเชจ เชšเชฒ เช…เชจเซ‡ เช•เชพเชฐเซเชก เชชเชฐเชจเซ€ เช›เชฌเซ€เช“ เชฎเชพเชŸเซ‡ เช†เชฐเซเชŸเชตเชฐเซเช• URL.

class Card {        
    var id: String    
    var shown: Bool = false    
    var artworkURL: UIImage!
}

เชจเช•เชถเชพ เชธเชพเชฅเซ‡ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเชจเซ€ เช•เซเชฐเชฟเชฏเชพเชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพเชจเซ‡ เชจเชฟเชฏเช‚เชคเซเชฐเชฟเชค เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชคเชฎเชพเชฐเซ‡ เช† เชชเชฆเซเชงเชคเชฟเช“เชจเซ€ เชชเชฃ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡:

เช•เชพเชฐเซเชก เชชเชฐ เช›เชฌเซ€ เชชเซเชฐเชฆเชฐเซเชถเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชชเชฆเซเชงเชคเชฟ. เช…เชนเซ€เช‚ เช†เชชเชฃเซ‡ เชฌเชงเซ€ เชชเซเชฐเซ‹เชชเชฐเซเชŸเซ€ เชกเชฟเชซเซ‹เชฒเซเชŸ เชชเชฐ เชฐเซ€เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช. เช†เชˆเชกเซ€ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ NSUUIS().uuidString เชชเชฐ เช•เซ‰เชฒ เช•เชฐเซ€เชจเซ‡ เชฐเซ‡เชจเซเชกเชฎ เช†เชˆเชกเซ€ เชœเชจเชฐเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช.

init(image: UIImage) {        
    self.id = NSUUID().uuidString        
    self.shown = false        
    self.artworkURL = image    
}

ID เช•เชพเชฐเซเชกเชจเซ€ เชธเชฐเช–เชพเชฎเชฃเซ€ เช•เชฐเชตเชพเชจเซ€ เชชเชฆเซเชงเชคเชฟ.

func equals(_ card: Card) -> Bool {
    return (card.id == id)    
}

เชฆเชฐเซ‡เช• เช•เชพเชฐเซเชกเชจเซ€ เชจเช•เชฒ เชฌเชจเชพเชตเชตเชพเชจเซ€ เชชเชฆเซเชงเชคเชฟ - เช•เซเชฐเชฎเชฎเชพเช‚ เชธเชฎเชพเชจ เชฐเชพเชถเชฟเช“ เชฎเซ‹เชŸเซ€ เชธเช‚เช–เซเชฏเชพเชฎเชพเช‚ เชฎเซ‡เชณเชตเชตเชพ เชฎเชพเชŸเซ‡. เช† เชชเชฆเซเชงเชคเชฟ เชธเชฎเชพเชจ เชฎเซ‚เชฒเซเชฏเซ‹ เชธเชพเชฅเซ‡ เช•เชพเชฐเซเชก เชชเชฐเชค เช•เชฐเชถเซ‡.

func copy() -> Card {        
    return Card(card: self)    
}
 
init(card: Card) {        
    self.id = card.id        
    self.shown = card.shown        
    self.artworkURL = card.artworkURL    
}

เช…เชจเซ‡ เชถเชฐเซ‚เช†เชคเชฎเชพเช‚ เช•เชพเชฐเซเชกเซเชธเชจเซ‡ เชถเชซเชฒ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชเช• เชตเชงเซ เชชเชฆเซเชงเชคเชฟเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เช…เชฎเซ‡ เชคเซ‡เชจเซ‡ เชเชฐเซ‡ เช•เซเชฒเชพเชธเชจเซเช‚ เชเช•เซเชธเซเชŸเซ‡เช‚เชถเชจ เชฌเชจเชพเชตเซ€เชถเซเช‚.

extension Array {    
    mutating func shuffle() {        
        for _ in 0...self.count {            
            sort { (_,_) in arc4random() < arc4random() }        
        }   
    }
}

เช…เชจเซ‡ เช…เชนเซ€เช‚ เชคเชฎเชพเชฎ เช—เซเชฃเชงเชฐเซเชฎเซ‹ เช…เชจเซ‡ เชชเชฆเซเชงเชคเชฟเช“ เชธเชพเชฅเซ‡ เช•เชพเชฐเซเชก เชฎเซ‹เชกเซ‡เชฒ เชฎเชพเชŸเซ‡ เช•เซ‹เชกเชจเซ‹ เช…เชฎเชฒ เช›เซ‡.

class Card {
    
    var id: String
    var shown: Bool = false
    var artworkURL: UIImage!
    
    static var allCards = [Card]()
 
    init(card: Card) {
        self.id = card.id
        self.shown = card.shown
        self.artworkURL = card.artworkURL
    }
    
    init(image: UIImage) {
        self.id = NSUUID().uuidString
        self.shown = false
        self.artworkURL = image
    }
    
    func equals(_ card: Card) -> Bool {
        return (card.id == id)
    }
    
    func copy() -> Card {
        return Card(card: self)
    }
}
 
extension Array {
    mutating func shuffle() {
        for _ in 0...self.count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

เช†เช—เชณ เชตเชงเซ‹.

เชฌเซ€เชœเซเช‚ เชฎเซ‹เชกเชฒ MemoryGame เช›เซ‡, เช…เชนเซ€เช‚ เช†เชชเชฃเซ‡ 4*4 เช—เซเชฐเซ€เชก เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช. เชฎเซ‹เชกเซ‡เชฒเชฎเชพเช‚ เช•เชพเชฐเซเชกเซเชธ (เช—เซเชฐเซ€เชก เชชเชฐ เช•เชพเชฐเซเชกเซเชธเชจเซ€ เชเชฐเซ‡), เช•เชพเชฐเซเชกเซเชธ เชชเชนเซ‡เชฒเชพเชฅเซ€ เชœ เช–เซเชฒเซ‡เชฒเชพ เช•เชพเชฐเซเชกเซเชธ เชธเชพเชฅเซ‡ เชฌเชคเชพเชตเซ‡เชฒ เชเชฐเซ‡ เช…เชจเซ‡ เชฐเชฎเชคเชจเซ€ เชธเซเชฅเชฟเชคเชฟเชจเซ‡ เชŸเซเชฐเซ…เช• เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชฌเซเชฒเชฟเชฏเชจ เชตเซ‡เชฐเซ€เชเชฌเชฒ เช‡เช เชชเซเชฒเซ‡เช‡เช‚เช— เชœเซ‡เชตเซ€ เชชเซเชฐเซ‹เชชเชฐเซเชŸเซ€เช เชนเชถเซ‡.

class MemoryGame {        
    var cards:[Card] = [Card]()    
    var cardsShown:[Card] = [Card]()    
    var isPlaying: Bool = false
}

เช…เชฎเซ‡ เช—เซเชฐเซ€เชก เชธเชพเชฅเซ‡ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเชจเซ€ เช•เซเชฐเชฟเชฏเชพเชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพเชจเซ‡ เชจเชฟเชฏเช‚เชคเซเชฐเชฟเชค เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชชเชฆเซเชงเชคเชฟเช“ เชตเชฟเช•เชธเชพเชตเชตเชพเชจเซ€ เชชเชฃ เชœเชฐเซ‚เชฐ เช›เซ‡.

เชเช• เชชเชฆเซเชงเชคเชฟ เช•เซ‡ เชœเซ‡ เช•เชพเชฐเซเชกเชจเซ‡ เช—เซเชฐเซ€เชกเชฎเชพเช‚ เชถเชซเชฒ เช•เชฐเซ‡ เช›เซ‡.

func shuffleCards(cards:[Card]) -> [Card] {       
    var randomCards = cards        
    randomCards.shuffle()                
 
    return randomCards    
}

เชจเชตเซ€ เชฐเชฎเชค เชฌเชจเชพเชตเชตเชพ เชฎเชพเชŸเซ‡เชจเซ€ เชชเชฆเซเชงเชคเชฟ. เช…เชนเซ€เช‚ เช†เชชเชฃเซ‡ เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เชฒเซ‡เช†เช‰เชŸ เชถเชฐเซ‚ เช•เชฐเชตเชพ เช…เชจเซ‡ isPlaying เชตเซ‡เชฐเซ€เชเชฌเชฒเชจเซ‡ true เชฎเชพเช‚ เชถเชฐเซ‚ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡เชจเซ€ เชชเซเชฐเชฅเชฎ เชชเชฆเซเชงเชคเชฟ เช•เชนเซ€เช เช›เซ€เช.

func newGame(cardsArray:[Card]) -> [Card] {       
    cards = shuffleCards(cards: cardsArray)        
    isPlaying = true            
 
    return cards    
}

เชœเซ‹ เช†เชชเชฃเซ‡ เชฐเชฎเชคเชจเซ‡ เชซเชฐเซ€เชฅเซ€ เชถเชฐเซ‚ เช•เชฐเชตเชพ เชฎเชพเช‚เช—เซ€เช เช›เซ€เช, เชชเช›เซ€ เช…เชฎเซ‡ isPlaying เชตเซ‡เชฐเซ€เชเชฌเชฒเชจเซ‡ เชซเซ‹เชฒเซเชธ เชชเชฐ เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช เช…เชจเซ‡ เช•เชพเชฐเซเชกเซเชธเชจเชพ เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เชฒเซ‡เช†เช‰เชŸเชจเซ‡ เชฆเซ‚เชฐ เช•เชฐเซ€เช เช›เซ€เช.

func restartGame() {        
    isPlaying = false                
    cards.removeAll()        
    cardsShown.removeAll()    
}

เช•เซเชฒเชฟเช• เช•เชฐเซ‡เชฒเชพ เช•เชพเชฐเซเชก เชšเช•เชพเชธเชตเชพ เชฎเชพเชŸเซ‡เชจเซ€ เชชเชฆเซเชงเชคเชฟ. เชคเซ‡เชจเชพ เชตเชฟเชถเซ‡ เชชเช›เซ€เชฅเซ€ เชตเชงเซ.

func cardAtIndex(_ index: Int) -> Card? {        
    if cards.count > index {            
        return cards[index]        
    } else {            
        return nil        
    }    
}

เชเช• เชชเชฆเซเชงเชคเชฟ เชœเซ‡ เชšเซ‹เช•เซเช•เชธ เช•เชพเชฐเซเชกเชจเซ€ เชธเซเชฅเชฟเชคเชฟ เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡.

func indexForCard(_ card: Card) -> Int? {        
    for index in 0...cards.count-1 {            
        if card === cards[index] {                
            return index            
        }      
    }
        
    return nil    
}

เชธเซเชŸเชพเชจเซเชกเชฐเซเชก เชธเชพเชฅเซ‡ เชชเชธเช‚เชฆ เช•เชฐเซ‡เชฒ เช•เชพเชฐเซเชกเชจเซเช‚ เชชเชพเชฒเชจ เชคเชชเชพเชธเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡.

func unmatchedCardShown() -> Bool {
    return cardsShown.count % 2 != 0
}

เช† เชชเชฆเซเชงเชคเชฟ **เช•เชพเชฐเซเชกเซเชธ เชฌเชคเชพเชตเซ‡เชฒ** เชเชฐเซ‡เชฎเชพเช‚ เช›เซ‡เชฒเซเชฒเซเช‚ เช˜เชŸเช• เชตเชพเช‚เชšเซ‡ เช›เซ‡ เช…เชจเซ‡ เชฎเซ‡เชณ เชจ เช–เชพเชคเชพ เช•เชพเชฐเซเชก เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡.

func didSelectCard(_ card: Card?) {        
    guard let card = card else { return }                
    
    if unmatchedCardShown() {            
        let unmatched = unmatchedCard()!                        
        
        if card.equals(unmatched) {          
            cardsShown.append(card)            
        } else {                
            let secondCard = cardsShown.removeLast()      
        }                    
    } else {            
        cardsShown.append(card)        
    }                
    
    if cardsShown.count == cards.count {            
        endGame()        
    }    
}

Main.storyboard เช…เชจเซ‡ GameController.swift

Main.storyboard เช•เช‚เชˆเช• เช†เชจเชพ เชœเซ‡เชตเซเช‚ เชฆเซ‡เช–เชพเชฏ เช›เซ‡:

เชธเซเชตเชฟเชซเซเชŸเชฎเชพเช‚ เชฎเซ‡เชฎเชฐเซ€ เช•เชพเชฐเซเชกเซเชธ เช—เซ‡เชฎ เชฒเช–เชตเซ€

เชถเชฐเซ‚เช†เชคเชฎเชพเช‚, เชคเชฎเชพเชฐเซ‡ เช—เซเชฐเซ€เชก เชฎเชพเชŸเซ‡เชจเซ€ เช›เชฌเซ€เช“ เชธเชนเชฟเชค เช•เช‚เชŸเซเชฐเซ‹เชฒเชฐเชฎเชพเช‚ เชตเซเชฏเซเชกเชฟเชกเชฒเซ‹เชก เชคเชฐเซ€เช•เซ‡ เชจเชตเซ€ เช—เซ‡เชฎ เชธเซ‡เชŸ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เชฐเชฎเชคเชฎเชพเช‚, เช† เชฌเชงเซเช‚ 4*4 เช•เชฒเซ‡เช•เซเชถเชจ เชตเซเชฏเซ‚ เชฆเซเชตเชพเชฐเชพ เชฐเชœเซ‚ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡. เชœเซ‹ เชคเชฎเซ‡ เชนเชœเซ€ เชธเซเชงเซ€ เช•เชฒเซ‡เช•เซเชถเชจ เชตเซเชฏเซ‚เชฅเซ€ เชชเชฐเชฟเชšเชฟเชค เชจเชฅเซ€, เชคเซ‹ เชคเซ‡ เช…เชนเซ€เช‚ เช›เซ‡ เชคเชฎเซ‡ เชคเชฎเชจเซ‡ เชœเซ‹เชˆเชคเซ€ เชฎเชพเชนเชฟเชคเซ€ เชฎเซ‡เชณเชตเซ€ เชถเช•เซ‹ เช›เซ‹.

เช…เชฎเซ‡ เช—เซ‡เชฎ เช•เช‚เชŸเซเชฐเซ‹เชฒเชฐเชจเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจเชจเชพ เชฐเซ‚เชŸ เช•เช‚เชŸเซเชฐเซ‹เชฒเชฐ เชคเชฐเซ€เช•เซ‡ เช—เซ‹เช เชตเซ€เชถเซเช‚. เช—เซ‡เชฎ เช•เช‚เชŸเซเชฐเซ‹เชฒเชฐ เชชเชพเชธเซ‡ เช•เชฒเซ‡เช•เซเชถเชจ เชตเซเชฏเซ‚ เชนเชถเซ‡ เชœเซ‡เชจเซ‹ เช…เชฎเซ‡ IBOutlet เชคเชฐเซ€เช•เซ‡ เชธเช‚เชฆเชฐเซเชญ เช†เชชเซ€เชถเซเช‚. เชฌเซ€เชœเซ‹ เชธเช‚เชฆเชฐเซเชญ IBAction onStartGame() เชฌเชŸเชจเชจเซ‹ เช›เซ‡, เช† เชเช• UIButton เช›เซ‡, เชคเชฎเซ‡ เชคเซ‡เชจเซ‡ PLAY เชจเชพเชฎเชจเชพ เชธเซเชŸเซ‹เชฐเซ€เชฌเซ‹เชฐเซเชกเชฎเชพเช‚ เชœเซ‹เชˆ เชถเช•เซ‹ เช›เซ‹.

เชจเชฟเชฏเช‚เชคเซเชฐเช•เซ‹เชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชตเชฟเชถเซ‡ เชฅเซ‹เชกเซเช‚:

  • เชชเซเชฐเชฅเชฎ, เช…เชฎเซ‡ เชฌเซ‡ เชฎเซเช–เซเชฏ เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชถเชฐเซ‚ เช•เชฐเซ€เช เช›เซ€เช - เช—เซเชฐเซ€เชก: เช—เซ‡เชฎ = เชฎเซ‡เชฎเชฐเซ€เช—เซ‡เชฎ(), เช…เชจเซ‡ เช•เชพเชฐเซเชกเซเชธเชจเซ‹ เชธเชฎเซ‚เชน: เช•เชพเชฐเซเชกเซเชธ = [เช•เชพเชฐเซเชก]().
  • เช…เชฎเซ‡ เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เชšเชฒเซ‹เชจเซ‡ viewDidLoad เชคเชฐเซ€เช•เซ‡ เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช, เช† เชชเชนเซ‡เชฒเซ€ เชชเชฆเซเชงเชคเชฟ เช›เซ‡ เชœเซ‡เชจเซ‡ เชฐเชฎเชค เชšเชพเชฒเซ€ เชฐเชนเซ€ เชนเซ‹เชฏ เชคเซเชฏเชพเชฐเซ‡ เช•เชนเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡.
  • เช•เชฒเซ‡เช•เซเชถเชจ เชตเซเชฏเซ‚ เช›เซเชชเชพเชฏเซ‡เชฒ เชชเชฐ เชธเซ‡เชŸ เช›เซ‡ เช•เชพเชฐเชฃ เช•เซ‡ เชœเซเชฏเชพเช‚ เชธเซเชงเซ€ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพ PLAY เชฆเชฌเชพเชตเชถเซ‡ เชจเชนเซ€เช‚ เชคเซเชฏเชพเช‚ เชธเซเชงเซ€ เชคเชฎเชพเชฎ เช•เชพเชฐเซเชก เช›เซเชชเชพเชฏเซ‡เชฒเชพ เช›เซ‡.
  • เชœเซ‡เชฎ เชœเซ‡เชฎ เช†เชชเชฃเซ‡ PLAY เชฆเชฌเชพเชตเซ€เช เช›เซ€เช เชคเซ‡เชฎ, onStartGame IBAction เชตเชฟเชญเชพเช— เชถเชฐเซ‚ เชฅเชพเชฏ เช›เซ‡, เช…เชจเซ‡ เช…เชฎเซ‡ เช•เชฒเซ‡เช•เซเชถเชจ เชตเซเชฏเซ‚ isHidden เชชเซเชฐเซ‹เชชเชฐเซเชŸเซ€ เชซเซ‹เชฒเซเชธ เชชเชฐ เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช เชœเซ‡เชฅเซ€ เช•เชพเชฐเซเชกเซเชธ เชฆเซƒเชถเซเชฏเชฎเชพเชจ เชฅเชˆ เชถเช•เซ‡.
  • เชฆเชฐเซ‡เช• เชตเช–เชคเซ‡ เชœเซเชฏเชพเชฐเซ‡ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพ เช•เชพเชฐเซเชก เชชเชธเช‚เชฆ เช•เชฐเซ‡ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ didSelectItemAt เชชเชฆเซเชงเชคเชฟ เช•เชนเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชฎเซเช–เซเชฏ เชฐเชฎเชคเชจเชพ เชคเชฐเซเช•เชจเซ‡ เช…เชฎเชฒเชฎเชพเช‚ เชฎเซ‚เช•เชตเชพ เชฎเชพเชŸเซ‡ เช†เชชเชฃเซ‡ เชœเซ‡ เชชเชฆเซเชงเชคเชฟเชจเซ‡ didSelectCard เช•เชนเซ€เช เช›เซ€เช เชคเซ‡เชฎเชพเช‚.

เช…เชนเซ€เช‚ เช…เช‚เชคเชฟเชฎ เช—เซ‡เชฎ เช•เช‚เชŸเซเชฐเซ‹เชฒเชฐ เช…เชฎเชฒเซ€เช•เชฐเชฃ เช›เซ‡:

class GameController: UIViewController {
 
    @IBOutlet weak var collectionView: UICollectionView!
    
    let game = MemoryGame()
    var cards = [Card]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        game.delegate = self
        
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.isHidden = true
        
        APIClient.shared.getCardImages { (cardsArray, error) in
            if let _ = error {
                // show alert
            }
            
            self.cards = cardsArray!
            self.setupNewGame()
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        if game.isPlaying {
            resetGame()
        }
    }
    
    func setupNewGame() {
        cards = game.newGame(cardsArray: self.cards)
        collectionView.reloadData()
    }
    
    func resetGame() {
        game.restartGame()
        setupNewGame()
    }
    
    @IBAction func onStartGame(_ sender: Any) {
        collectionView.isHidden = false
    }
}
 
// MARK: - CollectionView Delegate Methods
extension GameController: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return cards.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCell
        cell.showCard(false, animted: false)
        
        guard let card = game.cardAtIndex(indexPath.item) else { return cell }
        cell.card = card
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as! CardCell
        
        if cell.shown { return }
        game.didSelectCard(cell.card)
        
        collectionView.deselectItem(at: indexPath, animated:true)
    }
}

เชนเชตเซ‡ เชฎเชนเชคเซเชตเชจเชพ เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒเซเชธ เชตเชฟเชถเซ‡ เชฅเซ‹เชกเซ€ เชตเชพเชค เช•เชฐเซ€เช.

เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒเซเชธ

เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเซเช‚ เช เชธเซเชตเชฟเชซเซเชŸ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชฟเช‚เช—เชจเซ‹ เชฎเซเช–เซเชฏ เชญเชพเช— เช›เซ‡. เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒเซเชธ เชตเชฐเซเช—, เชฎเชพเชณเช–เซเช‚ เช…เชฅเชตเชพ เช—เชฃเชคเชฐเซ€ เชฎเชพเชŸเซ‡ เชจเชฟเชฏเชฎเซ‹ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชจเซ€ เช•เซเชทเชฎเชคเชพ เชชเซเชฐเชฆเชพเชจ เช•เชฐเซ‡ เช›เซ‡. เช† เชธเชฟเชฆเซเชงเชพเช‚เชค เชคเชฎเชจเซ‡ เชฎเซ‹เชกเซเชฏเซเชฒเชฐ เช…เชจเซ‡ เชเช•เซเชธเซเชŸเซ‡เชจเซเชธเชฟเชฌเชฒ เช•เซ‹เชก เชฒเช–เชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡. เชตเชพเชธเซเชคเชตเชฎเชพเช‚, เช† เชเช• เชชเซ‡เชŸเชฐเซเชจ เช›เซ‡ เชœเซ‡ เช…เชฎเซ‡ เชชเชนเซ‡เชฒเชพเชฅเซ€ เชœ GameController เชฎเชพเช‚ เช•เชฒเซ‡เช•เซเชถเชจ เชตเซเชฏเซ‚ เชฎเชพเชŸเซ‡ เชฒเชพเช—เซ เช•เชฐเซ€ เชฐเชนเซเชฏเชพ เช›เซ€เช. เชนเชตเซ‡ เช†เชชเชฃเซเช‚ เชชเซ‹เชคเชพเชจเซเช‚ เชตเชฐเซเชเชจ เชฌเชจเชพเชตเซ€เช. เชธเชฟเชจเซเชŸเซ‡เช•เซเชธ เช†เชจเชพ เชœเซ‡เชตเซ‹ เชฆเซ‡เช–เชพเชถเซ‡:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

เช…เชฎเซ‡ เชœเชพเชฃเซ€เช เช›เซ€เช เช•เซ‡ เชชเซเชฐเซ‹เชŸเซ‹เช•เซ‹เชฒ เช…เชฎเชจเซ‡ เชตเชฐเซเช—เชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชฎเชพเชŸเซ‡ เชจเชฟเชฏเชฎเซ‹ เช…เชฅเชตเชพ เชธเซ‚เชšเชจเชพเช“เชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡, เชคเซ‡เชฅเซ€ เชšเชพเชฒเซ‹ เชคเซ‡ เชถเซเช‚ เชนเซ‹เชตเซเช‚ เชœเซ‹เชˆเช เชคเซ‡ เชตเชฟเชถเซ‡ เชตเชฟเชšเชพเชฐเซ€เช. เชคเชฎเชพเชฐเซ‡ เช•เซเชฒ เชšเชพเชฐเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡.

  • เชฐเชฎเชคเชจเซ€ เชถเชฐเซ‚เช†เชค: เชฎเซ‡เชฎเชฐเซ€เช—เซ‡เชฎเชกเซ€เชกเชธเซเชŸเชพเชฐเซเชŸ.
  • เชคเชฎเชพเชฐเซ‡ เช•เชพเชฐเซเชกเชจเซ‹ เชšเชนเซ‡เชฐเซ‹ เชจเซ€เชšเซ‡ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡: memoryGameShowCards.
  • เชคเชฎเชพเชฐเซ‡ เช•เชพเชฐเซเชกเชจเซ‹ เชšเชนเซ‡เชฐเซ‹ เชจเซ€เชšเซ‡ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡: memoryGameHideCards.
  • เชฐเชฎเชคเชจเซ‹ เช…เช‚เชค: memoryGameDidEnd.

เชคเชฎเชพเชฎ เชšเชพเชฐ เชชเชฆเซเชงเชคเชฟเช“ เชฎเซเช–เซเชฏ เชตเชฐเซเช— เชฎเชพเชŸเซ‡ เชฒเชพเช—เซ เช•เชฐเซ€ เชถเช•เชพเชฏ เช›เซ‡, เชœเซ‡ เช—เซ‡เชฎ เช•เช‚เชŸเซเชฐเซ‹เชฒเชฐ เช›เซ‡.

เชฎเซ‡เชฎเชฐเซ€เช—เซ‡เชฎเชกเซ€เชกเชธเซเชŸเชพเชฐเซเชŸ

เชœเซเชฏเชพเชฐเซ‡ เช† เชชเชฆเซเชงเชคเชฟ เชšเชฒเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เชฐเชฎเชค เชถเชฐเซ‚ เชฅเชตเซ€ เชœเซ‹เชˆเช (เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพ PLAY เชฆเชฌเชพเชตเชถเซ‡). เช…เชนเซ€เช‚ เช…เชฎเซ‡ collectionView.reloadData() เชชเชฐ เช•เซ‰เชฒ เช•เชฐเซ€เชจเซ‡ เชธเชพเชฎเช—เซเชฐเซ€เชจเซ‡ เชซเชฐเซ€เชฅเซ€ เชฒเซ‹เชก เช•เชฐเซ€เชถเซเช‚, เชœเซ‡ เช•เชพเชฐเซเชกเซเชธเชจเซ‡ เชถเชซเชฒ เช•เชฐเชถเซ‡.

func memoryGameDidStart(_ game: MemoryGame) {
    collectionView.reloadData()
}

เชฎเซ‡เชฎเชฐเซ€เช—เซ‡เชฎเชถเซ‹เช•เชพเชฐเซเชกเซเชธ

เช…เชฎเซ‡ เช† เชชเชฆเซเชงเชคเชฟเชจเซ‡ เชธเช‚เช—เซเชฐเชน SDViewSelectItemAt เชฎเชพเช‚เชฅเซ€ เช•เซ‰เชฒ เช•เชฐเซ€เช เช›เซ€เช. เชชเซเชฐเชฅเชฎ เชคเซ‡ เชชเชธเช‚เชฆ เช•เชฐเซ‡เชฒ เช•เชพเชฐเซเชก เชฌเชคเชพเชตเซ‡ เช›เซ‡. เชชเช›เซ€ เช•เชพเชฐเซเชกเซเชธ เชฌเชคเชพเชตเซ‡เชฒ เชเชฐเซ‡เชฎเชพเช‚ เชเช• เชฎเซ‡เชณ เชจ เช–เชพเชคเซเช‚ เช•เชพเชฐเซเชก เช›เซ‡ เช•เซ‡ เช•เซ‡เชฎ เชคเซ‡ เชœเซ‹เชตเชพ เชฎเชพเชŸเซ‡ เชคเชชเชพเชธเซ‹ (เชœเซ‹ เชฌเชคเชพเชตเซ‡เชฒ เช•เชพเชฐเซเชกเซเชธเชจเซ€ เชธเช‚เช–เซเชฏเชพ เชตเชฟเชšเชฟเชคเซเชฐ เช›เซ‡). เชœเซ‹ เชคเซเชฏเชพเช‚ เชเช• เชนเซ‹เชฏ, เชคเซ‹ เชชเชธเช‚เชฆ เช•เชฐเซ‡เชฒ เช•เชพเชฐเซเชก เชคเซ‡เชจเซ€ เชธเชพเชฅเซ‡ เชธเชฐเช–เชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชœเซ‹ เชšเชฟเชคเซเชฐเซ‹ เชธเชฐเช–เชพ เชนเซ‹เชฏ, เชคเซ‹ เชฌเช‚เชจเซ‡ เช•เชพเชฐเซเชก เชฌเชคเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡เชฒเชพ เช•เชพเชฐเซเชกเซเชธเชฎเชพเช‚ เช‰เชฎเซ‡เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ เช…เชจเซ‡ เชธเชพเชฎเชธเชพเชฎเซ‡ เชฐเชนเซ‡ เช›เซ‡. เชœเซ‹ เช…เชฒเช—-เช…เชฒเช— เชนเซ‹เชฏ, เชคเซ‹ เช•เชพเชฐเซเชก เช›เซ‹เชกเซ‡ เช›เซ‡ เช•เชพเชฐเซเชกเซเชธ เชฌเชคเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ เช…เชจเซ‡ เชฌเช‚เชจเซ‡ เชฎเซ‹เชขเชพ เชจเซ€เชšเซ‡ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡.

เชฎเซ‡เชฎเชฐเซ€เช—เซ‡เชฎเชนเชพเช‡เชกเช•เชพเชฐเซเชกเซเชธ

เชœเซ‹ เช•เชพเชฐเซเชก เชฎเซ‡เชณ เช–เชพเชคเชพ เชจเชฅเซ€, เชคเซ‹ เช† เชชเชฆเซเชงเชคเชฟ เช•เชนเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ เช…เชจเซ‡ เช•เชพเชฐเซเชกเชจเซ€ เช›เชฌเซ€เช“ เช›เซเชชเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡.

เชฌเชคเชพเชตเซ‡เชฒ = เช–เซ‹เชŸเซเช‚.

เชฎเซ‡เชฎเชฐเซ€GameDidEnd

เชœเซเชฏเชพเชฐเซ‡ เช† เชชเชฆเซเชงเชคเชฟเชจเซ‡ เชฌเซ‹เชฒเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เชคเซ‡เชจเซ‹ เช…เชฐเซเชฅ เช เช›เซ‡ เช•เซ‡ เชฌเชงเชพ เช•เชพเชฐเซเชกเซเชธ เชชเชนเซ‡เชฒเซ‡เชฅเซ€ เชœ เชœเชพเชนเซ‡เชฐ เชฅเชˆ เช—เชฏเชพ เช›เซ‡ เช…เชจเซ‡ เช•เชพเชฐเซเชกเซเชธ เชฌเชคเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡เชฒเซ€ เชธเซ‚เชšเชฟเชฎเชพเช‚ เช›เซ‡: cardsShown.count = cards.count, เชคเซ‡เชฅเซ€ เชฐเชฎเชค เชธเชฎเชพเชชเซเชค เชฅเชˆ เช—เชˆ เช›เซ‡. isPlaying var เชจเซ‡ เช–เซ‹เชŸเชพ เชชเชฐ เชธเซ‡เชŸ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช…เชฎเซ‡ endGame() เชจเซ‡ เช•เซ‰เชฒ เช•เชฐเซเชฏเชพ เชชเช›เซ€ เชชเชฆเซเชงเชคเชฟเชจเซ‡ เช–เชพเชธ เช•เชนเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชœเซ‡ เชชเช›เซ€ เชฐเชฎเชค เชธเชฎเชพเชชเซเชคเชฟ เชธเช‚เชฆเซ‡เชถ เชฌเชคเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชคเซ‡เชฎเชœ alertController เชจเซ‹ เช‰เชชเชฏเซ‹เช— เชจเชฟเชฏเช‚เชคเซเชฐเช• เชฎเชพเชŸเซ‡ เชธเซ‚เชšเช• เชคเชฐเซ€เช•เซ‡ เชฅเชพเชฏ เช›เซ‡. viewDidDisappear เช•เชนเซ‡เชตเชพเชฏ เช›เซ‡ เช…เชจเซ‡ เช—เซ‡เชฎ เชฐเซ€เชธเซ‡เชŸ เชฅเชพเชฏ เช›เซ‡.

เช—เซ‡เชฎเช•เช‚เชŸเซเชฐเซ‹เชฒเชฐเชฎเชพเช‚ เชคเซ‡ เชฌเชงเซเช‚ เชถเซเช‚ เชฆเซ‡เช–เชพเชฏ เช›เซ‡ เชคเซ‡ เช…เชนเซ€เช‚ เช›เซ‡:

extension GameController: MemoryGameProtocol {
    func memoryGameDidStart(_ game: MemoryGame) {
        collectionView.reloadData()
    }
 
 
    func memoryGame(_ game: MemoryGame, showCards cards: [Card]) {
        for card in cards {
            guard let index = game.indexForCard(card)
                else { continue
            }        
            
            let cell = collectionView.cellForItem(
                at: IndexPath(item: index, section:0)
            ) as! CardCell
 
            cell.showCard(true, animted: true)
        }
    }
 
    func memoryGame(_ game: MemoryGame, hideCards cards: [Card]) {
        for card in cards {
            guard let index = game.indexForCard(card)
                else { continue
            }
            
            let cell = collectionView.cellForItem(
                at: IndexPath(item: index, section:0)
            ) as! CardCell
    
            cell.showCard(false, animted: true)
        }
    }
 
    func memoryGameDidEnd(_ game: MemoryGame) {
        let alertController = UIAlertController(
            title: defaultAlertTitle,
            message: defaultAlertMessage,
            preferredStyle: .alert
        )
 
        let cancelAction = UIAlertAction(
            title: "Nah", style: .cancel) {
            [weak self] (action) in
            self?.collectionView.isHidden = true
        }
 
        let playAgainAction = UIAlertAction(
            title: "Dale!", style: .default) {
            [weak self] (action) in
            self?.collectionView.isHidden = true
 
            self?.resetGame()
        }
 
        alertController.addAction(cancelAction)
        alertController.addAction(playAgainAction)
        
        self.present(alertController, animated: true) { }
    
        resetGame()
    }
}

เชธเซเชตเชฟเชซเซเชŸเชฎเชพเช‚ เชฎเซ‡เชฎเชฐเซ€ เช•เชพเชฐเซเชกเซเชธ เช—เซ‡เชฎ เชฒเช–เชตเซ€
เช–เชฐเซ‡เช–เชฐ, เชคเซ‡ เชฌเชงเซ เชœ เช›เซ‡. เชคเชฎเซ‡ เช† เชชเซเชฐเซ‹เชœเซ‡เช•เซเชŸเชจเซ‹ เช‰เชชเชฏเซ‹เช— เชฐเชฎเชคเชจเซเช‚ เชคเชฎเชพเชฐเซเช‚ เชชเซ‹เชคเชพเชจเซเช‚ เชธเช‚เชธเซเช•เชฐเชฃ เชฌเชจเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹.

เชนเซ‡เชชเซ€ เช•เซ‹เชกเชฟเช‚เช—!

เชธเซเช•เชฟเชฒเชฌเซ‹เช•เซเชธ เชญเชฒเชพเชฎเชฃ เช•เชฐเซ‡ เช›เซ‡:

เชธเซ‹เชฐเซเชธ: www.habr.com

เชเช• เชŸเชฟเชชเซเชชเชฃเซ€ เช‰เชฎเซ‡เชฐเซ‹