Scrivite un ghjocu di carte di memoria in Swift

Scrivite un ghjocu di carte di memoria in Swift

Questu articulu descrive u prucessu di creà un ghjocu simplice di furmazione di memoria chì mi piace assai. Oltre à esse bonu in sè stessu, amparate un pocu di più nantu à e classi è i protokolli Swift mentre andate. Ma prima di principià, capiscemu u ghjocu stessu.

Ramintemu: per tutti i lettori di "Habr" - un scontu di 10 000 rubles quandu si iscrizzione in ogni cursu Skillbox cù u codice promozionale "Habr".

Skillbox consiglia: Corso educativu in linea "Professional Java Developer".

Cumu ghjucà a carta di memoria

U ghjocu principia cù una dimostrazione di un set di carte. Si trovanu a faccia in giru (rispettivamente, l'imaghjini). Quandu cliccate nant'à qualcunu, l'imaghjini si apre per uni pochi seconde.

U compitu di u ghjucatore hè di truvà tutte e carte cù i stessi ritratti. Se, dopu avè apertu a prima carta, turnate a seconda è e foto currispondenu, e duie carte restanu aperte. S'ellu ùn currispondenu micca, i carte sò chjusi di novu. U scopu hè di apre tuttu.

Struttura di u prugettu

Per creà una versione simplice di stu ghjocu avete bisognu di i seguenti cumpunenti:

  • Un Controller: GameController.swift.
  • Una vista: CardCell.swift.
  • Dui mudelli: MemoryGame.swift è Card.swift.
  • Main.storyboard per assicurà chì tuttu u settore di cumpunenti hè dispunibule.

Cuminciamu cù u cumpunente più simplice di u ghjocu, i carte.

Card.swift

U mudellu di carta averà trè proprietà: id per identificà ognunu, una variabile booleana mostrata per specificà u statutu di a carta (oculata o aperta), è artworkURL per l'imaghjini nantu à e carte.

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

Averete ancu bisognu di sti metudi per cuntrullà l'interazzione di l'utilizatori cù e carte:

Metudu per vede una maghjina nantu à una carta. Quì avemu resettatu tutte e pruprietà per difettu. Per id, generemu un id aleatoriu chjamendu NSUUIS().uuidString.

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

Metudu per paragunà e carte d'identità.

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

Metudu per creà una copia di ogni carta - per ottene un numeru più grande di quelli idèntici. Stu metudu torna carta cù valori simili.

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

È un altru mètudu hè necessariu per mischjà e carte à u principiu. Facemu una estensione di a classe Array.

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

E quì hè l'implementazione di u codice per u mudellu Card cù tutte e pruprietà è i metudi.

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

Andate per voi.

U sicondu mudellu hè MemoryGame, quì avemu stabilitu una griglia 4 * 4. U mudellu avarà pruprietà cum'è carte (un array di carte nantu à una griglia), un array cardsShown cù carte digià aperte, è una variabile booleana in Playing per seguità u statutu di u ghjocu.

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

Avemu ancu bisognu di sviluppà metudi per cuntrullà l'interazzione di l'utilizatori cù a griglia.

Un metudu chì mischja e carte in una griglia.

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

Metudu per creà un novu ghjocu. Quì chjamemu u primu metudu per inizià u layout iniziale è inizializza a variàbile isPlaying à veru.

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

Se vulemu riavvia u ghjocu, dopu avemu stabilitu a variabile isPlaying à false è sguassate u layout iniziale di e carte.

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

Metudu per verificà e carte clicate. Più nantu à ellu dopu.

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

Un metudu chì torna a pusizione di una carta specifica.

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

Verificate a cunfurmità di a carta scelta cù u standard.

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

Stu metudu leghje l'ultimu elementu in l'array **cardsShown** è torna a carta chì ùn currisponde.

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 s'assumiglia à questu:

Scrivite un ghjocu di carte di memoria in Swift

Inizialmente, avete bisognu di stabilisce u novu ghjocu cum'è viewDidLoad in u controller, cumprese l'imaghjini per a griglia. In u ghjocu, tuttu questu serà rapprisintatu da 4 * 4 collectionView. Se ùn site micca ancu familiarizatu cù collectionView, quì hè quì pudete uttene l'infurmazioni chì avete bisognu.

Cunfiguremu u GameController cum'è u controller root di l'applicazione. U GameController avarà una cullizzioniView chì faremu riferimentu cum'è un IBOutlet. Un altru riferimentu hè à u buttone IBAction onStartGame (), questu hè un UIButton, pudete vede in u storyboard chjamatu PLAY.

Un pocu nantu à l'implementazione di i cuntrolli:

  • Prima, avemu inizializatu dui ogetti principali - a griglia: ghjocu = MemoryGame(), è un set di carte: cards = [Card]().
  • Avemu stabilitu e variabili iniziali cum'è viewDidLoad, questu hè u primu metudu chì hè chjamatu mentre u ghjocu hè in esecuzione.
  • collectionView hè pusatu per ammucciatu perchè tutte e carte sò ammucciate finu à chì l'utilizatore preme PLAY.
  • Appena pressu PLAY, a sezione onStartGame IBAction principia, è avemu stabilitu a pruprietà collectionView isHidden à false per chì e carte ponu esse visibili.
  • Ogni volta chì l'utilizatore sceglie una carta, u metudu didSelectItemAt hè chjamatu. In u metudu chjamemu didSelectCard per implementà a logica di u ghjocu principale.

Eccu l'implementazione finale di GameController:

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

Avà parlemu un pocu di i protokolli impurtanti.

Prutoculi

U travagliu cù protokolli hè u core di a prugrammazione Swift. I protokolli furniscenu l'abilità di definisce e regule per una classa, struttura o enumerazione. Stu principiu permette di scrive codice modulare è extensible. In fatti, questu hè un mudellu chì avemu digià implementatu per a cullezzioneView in GameController. Avà facemu a nostra propria versione. A sintassi sarà cusì:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Sapemu chì un protokollu ci permette di definisce e regule o struzzioni per implementà una classa, allora pensemu ciò chì deve esse. Avete bisognu di quattru in totale.

  • U principiu di u ghjocu: memoryGameDidStart.
  • Avete bisognu di vultà a carta a faccia in giù: memoryGameShowCards.
  • Avete bisognu di turnà a carta a faccia in giù: memoryGameHideCards.
  • Finu di u ghjocu: memoryGameDidEnd.

Tutti i quattru metudi ponu esse implementati per a classa principale, chì hè GameController.

memoryGameDidStart

Quandu stu metudu hè eseguitu, u ghjocu deve principià (l'utilizatore pressu PLAY). Quì avemu da solu ricaricà u cuntenutu chjamendu collectionView.reloadData (), chì mischiarà e carte.

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

memoryGameShowCards

Chjamemu stu metudu da collectionSDViewSelectItemAt. Prima mostra a carta scelta. Allora cuntrolla per vede s'ellu ci hè una carta ineguagliata in l'array cardsShown (se u numeru di cardsShown hè stranu). Se ci hè unu, a carta scelta hè paragunata cun ella. Se l'imaghjini sò listessi, e duie carte sò aghjuntu à e carte Mostrate è restanu a faccia in sopra. S'ellu hè differente, a carta lasci cartesShown è i dui sò girati a faccia in giru.

memoryGameHideCards

Se e carte ùn currispondenu micca, stu metudu hè chjamatu è l'imaghjini di carte sò oculati.

mostratu = falsu.

memoriaGameDidEnd

Quandu stu metudu hè chjamatu, significa chì tutte e carte sò digià revelate è sò in a lista di carteShown: cardsShown.count = cards.count, cusì u ghjocu hè finitu. U metudu hè chjamatu specificamente dopu chì avemu chjamatu endGame () per stabilisce a var isPlaying à false, dopu chì u messagiu di u ghjocu hè mostratu. Ancu alertController hè utilizatu com'è un indicatore per u controller. viewDidDisappear hè chjamatu è u ghjocu hè resettatu.

Eccu ciò chì pare tuttu in GameController:

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

Scrivite un ghjocu di carte di memoria in Swift
In fatti, hè tuttu. Pudete aduprà stu prughjettu per creà a vostra propria versione di u ghjocu.

Felice codificazione!

Skillbox consiglia:

Source: www.habr.com

Add a comment