Skribante Memorkartludon en Swift

Skribante Memorkartludon en Swift

Ĉi tiu artikolo priskribas la procezon krei simplan memortrejnadludon, kiun mi tre ĝuas. Krom esti bona en si mem, vi lernos iom pli pri Swift-klasoj kaj protokoloj dum vi iros. Sed antaŭ ol komenci, ni komprenu la ludon mem.

Ni memorigas vin: por ĉiuj legantoj de "Habr" - rabato de 10 000 rubloj kiam oni enskribas en iu ajn Skillbox-kurso per la reklamkodo "Habr".

Skillbox rekomendas: Eduka interreta kurso "Profesia Java Programisto".

Kiel ludi Memorkarton

La ludo komenciĝas per pruvo de aro de kartoj. Ili kuŝas vizaĝe malsupren (respektive, bildo malsupren). Kiam vi alklakas iun ajn, la bildo malfermiĝas dum kelkaj sekundoj.

La tasko de la ludanto estas trovi ĉiujn kartojn kun la samaj bildoj. Se, post malfermi la unuan karton, vi turnas la duan kaj la bildoj kongruas, ambaŭ kartoj restas malfermitaj. Se ili ne kongruas, la kartoj denove estas fermitaj. La celo estas malfermi ĉion.

Projekta strukturo

Por krei simplan version de ĉi tiu ludo vi bezonas la jenajn komponantojn:

  • Unu Regilo: GameController.swift.
  • Unu Vido: CardCell.swift.
  • Du Modeloj: MemoryGame.swift kaj Card.swift.
  • Main.storyboard por certigi ke la tuta aro de komponantoj estas disponebla.

Ni komencas kun la plej simpla komponanto de la ludo, la kartoj.

Karto.rapida

La karto-modelo havos tri trajtojn: id por identigi ĉiun, bulean variablon montritan por specifi la staton de la karto (kaŝita aŭ malfermita), kaj artworkURL por la bildoj sur la kartoj.

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

Vi ankaŭ bezonos ĉi tiujn metodojn por kontroli uzantinteragon kun mapoj:

Metodo por montri bildon sur karto. Ĉi tie ni restarigas ĉiujn proprietojn al defaŭlta. Por id, ni generas hazardan id per vokado de NSUUIS().uuidString.

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

Metodo por kompari ID-kartojn.

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

Metodo por krei kopion de ĉiu karto - por akiri pli grandan nombron da identaj. Ĉi tiu metodo redonos karton kun similaj valoroj.

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

Kaj unu plia metodo estas necesa por miksi la kartojn ĉe la komenco. Ni faros ĝin etendo de la klaso Array.

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

Kaj jen la efektivigo de la kodo por la Karto-modelo kun ĉiuj propraĵoj kaj metodoj.

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

Daŭrigu.

La dua modelo estas MemoryGame, ĉi tie ni starigas 4 * 4 kradon. La modelo havos ecojn kiel ekzemple kartoj (aro da kartoj sur krado), kartoj Montritaj tabelo kun kartoj jam malfermitaj, kaj bulea variablo Ludas por spuri la staton de la ludo.

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

Ni ankaŭ devas evoluigi metodojn por kontroli uzantinteragon kun la krado.

Metodo kiu miksas kartojn en krado.

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

Metodo por krei novan ludon. Ĉi tie ni nomas la unuan metodon por komenci la komencan aranĝon kaj pravalorigi la isPlaying variablon al vera.

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

Se ni volas rekomenci la ludon, tiam ni agordas la isPlaying variablon al falsa kaj forigas la komencan aranĝon de la kartoj.

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

Metodo por kontroli klakitajn kartojn. Pli pri li poste.

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

Metodo kiu resendas la pozicion de specifa karto.

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

Kontrolante la konformecon de la elektita karto kun la normo.

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

Ĉi tiu metodo legas la lastan elementon en la tabelo **cardsShown** kaj resendas la nekongruan karton.

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

Ĉefa.storyboard kaj GameController.swift

Main.storyboard aspektas kiel ĉi tio:

Skribante Memorkartludon en Swift

Komence, vi devas agordi la novan ludon kiel viewDidLoad en la regilo, inkluzive de la bildoj por la krado. En la ludo, ĉio ĉi estos reprezentita per 4*4 kolektoVido. Se vi ankoraŭ ne konas collectionView, jen ĝi vi povas ricevi la informojn, kiujn vi bezonas.

Ni agordos la GameController kiel la radikan regilon de la aplikaĵo. La GameController havos kolekton, kiun ni referencos kiel IBOutlet. Alia referenco estas al la butono IBAction onStartGame(), ĉi tiu estas UIButton, vi povas vidi ĝin en la storyboard nomita PLAY.

Iom pri la efektivigo de regiloj:

  • Unue, ni pravigigas du ĉefajn objektojn - la krado: ludo = MemoryGame(), kaj aro da kartoj: kartoj = [Karto]().
  • Ni fiksas la komencajn variablojn kiel viewDidLoad, ĉi tiu estas la unua metodo vokita dum la ludo funkcias.
  • collectionView estas agordita kiel kaŝita ĉar ĉiuj kartoj estas kaŝitaj ĝis la uzanto premas PLAY.
  • Tuj kiam ni premas PLAY, la sekcio onStartGame IBAction komenciĝas, kaj ni agordas la collectionView isHidden-posedaĵon al falsa por ke la kartoj fariĝu videblaj.
  • Ĉiufoje kiam la uzanto elektas karton, la metodo didSelectItemAt estas vokita. En la metodo ni nomas didSelectCard por efektivigi la ĉefan ludlogikon.

Jen la fina efektivigo de 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)
    }
}

Nun ni parolu iomete pri la gravaj protokoloj.

Protokoloj

Labori kun protokoloj estas la kerno de Swift-programado. Protokoloj disponigas la kapablon difini regulojn por klaso, strukturo aŭ listigo. Ĉi tiu principo permesas vin skribi modulan kaj etendeblan kodon. Fakte, ĉi tio estas ŝablono, kiun ni jam efektivigas por kolektoView en GameController. Nun ni faru nian propran version. La sintakso aspektos jene:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Ni scias, ke protokolo permesas al ni difini regulojn aŭ instrukciojn por efektivigi klason, do ni pensu pri kio ili devus esti. Vi bezonas kvar entute.

  • Ludkomenco: memoryGameDidStart.
  • Vi devas turni la karton vizaĝo malsupren: memoryGameShowCards.
  • Vi devas turni la karton vizaĝe malsupren: memoryGameHideCards.
  • Ludfino: memoryGameDidEnd.

Ĉiuj kvar metodoj povas esti efektivigitaj por la ĉefklaso, kiu estas GameController.

memoryGameDidStart

Kiam ĉi tiu metodo estas rulita, la ludo devus komenciĝi (la uzanto premas PLAY). Ĉi tie ni simple reŝargos la enhavon nomante collectionView.reloadData(), kiu miksos la kartojn.

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

memoryGameShowCards

Ni nomas ĉi tiun metodon de collectionSDViewSelectItemAt. Unue ĝi montras la elektitan karton. Poste kontrolas ĉu estas nekomparebla karto en la tabelo cardsShown (se la nombro da kartoj Montritaj estas nepara). Se ekzistas unu, la elektita karto estas komparata kun ĝi. Se la bildoj estas samaj, ambaŭ kartoj estas aldonitaj al kartoj Montritaj kaj restas vizaĝo supre. Se malsama, la karto lasas kartojn Montritaj kaj ambaŭ estas turnitaj vizaĝo malsupren.

memorludoKaŝkartoj

Se la kartoj ne kongruas, ĉi tiu metodo estas vokita kaj la kartobildoj estas kaŝitaj.

montrata = malvera.

memoryGameDidEnd

Kiam ĉi tiu metodo estas vokita, tio signifas, ke ĉiuj kartoj estas jam malkaŝitaj kaj estas en la kartojShown listo: cardsShown.count = cards.count, do la ludo finiĝis. La metodo estas vokita specife post kiam ni vokis endGame() por agordi la isPlaying var al false, post kio la ludfina mesaĝo montriĝas. Ankaŭ alertController estas uzata kiel indikilo por la regilo. viewDidDisappear estas vokita kaj la ludo estas rekomencigita.

Jen kiel ĉio aspektas en 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()
    }
}

Skribante Memorkartludon en Swift
Fakte, jen ĉio. Vi povas uzi ĉi tiun projekton por krei vian propran version de la ludo.

Feliĉa kodigo!

Skillbox rekomendas:

fonto: www.habr.com

Aldoni komenton