<repo_name>nlharri/iOS-Stanford-2017-Concentration<file_sep>/README.md
# iOS-Stanford-2017-Concentration
<file_sep>/Concentration/ViewController.swift
//
//  ViewController.swift
//  Concentration
//
//  Created by <NAME> on 2018. 12. 14..
//  Copyright © 2018. <NAME>. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    // this is the reference to the model
    // () is the initializer
    // Classes get an init with no arguments by default
    // if all the arguments are initialized
    // The only property in Concentration is cards
    // lazy means it is not initialized until someone uses it.
    // but lazy cannot have property observers, like didSet
    // the number of pairs of cards in the game is tied to the UI, that's why it is private
    private lazy var game: Concentration = Concentration(numberOfPairsOfCards: numberOfPairsOfCards)
    
    var numberOfPairsOfCards: Int {
        return (cardButtons.count + 1 ) / 2
    }
    
    // property observer
    private(set) var flipCount: Int = 0 {
        didSet {
            flipCountLabel.text = "Flips: \(flipCount)"
        }
    }
    
    // here ! means this property does not need initializer
    @IBOutlet private var cardButtons: [UIButton]!
    
    @IBOutlet private weak var flipCountLabel: UILabel!
    
    @IBAction private func touchCard(_ sender: UIButton) {
        flipCount += 1
        // ! means "assume this optional is set, and grap the associated value
        // if it is not set, it will crash!
        //let cardNumber = cardButtons.firstIndex(of: sender)!
        if let cardNumber = cardButtons.firstIndex(of: sender) {
            game.chooseCard(at: cardNumber)
            //flipCard(withEmoji: emojiChoices[cardNumber], on: sender)
            // here we need to update our view from our model
            updateViewFromModel()
        } else {
            print ("ERROR")
        }
    }
    
    private func updateViewFromModel() {
        for index in cardButtons.indices {
            let button = cardButtons[index]
            let card = game.cards[index]
            if card.isFaceUp {
                button.setTitle(emoji(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: 0.5781051517, blue: 0, alpha: 0) : #colorLiteral(red: 1, green: 0.5781051517, blue: 0, alpha: 1)
            }
        }
    }
    
//    private var emojiChoices: Array<String> = ["🐰", "🦇", "🐂", "🐈", "👻", "🙊", "🦊", "🐶", "🐮", "🐫", "🐳", "🦈", "🐠", "🦐", "🦂", "🦍", "🦏", "🐎", "🐬"]

    private var emojiChoices = "🐰🦇🐂🐈👻🙊🦊🐶🐮🐫🐳🦈🐠🦐🦂🦍🦏🐎🐬"

    private var emoji = Dictionary<Card, String>()

    private func emoji(for card: Card) -> String {
        if emoji[card] == nil && emojiChoices.count > 0 {
            let randomStringIndex = emojiChoices.index(emojiChoices.startIndex, offsetBy: emojiChoices.count.arc4random)
            emoji[card] = String(emojiChoices.remove(at: randomStringIndex))
        }
        
        // the above one can simply be written like this!
        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(-self)))
        } else {
            return 0
        }
    }
}

<file_sep>/Concentration/Concentration.swift
//
//  Concentration.swift
//  Concentration
//
//  Created by <NAME> on 2018. 12. 17..
//  Copyright © 2018. <NAME>. All rights reserved.
//

import Foundation

// this is the model
// struct is a value type, it is constantly copied everywhere
struct Concentration {
    
    // here () is the initializer
    // array is a value type and Card is a value type
    // this means it cannot be modified
    private(set) var cards = [Card]()
    
    private var indexOfOneAndOnlyFaceUpCard: Int? {
        get {
            var foundIndex: Int?
            for index in cards.indices {
                if cards[index].isFaceUp {
                    if foundIndex == nil {
                        foundIndex = index
                    } else {
                        return nil
                    }
                }
            }
            return foundIndex
        }
        set {
            for index in cards.indices {
                cards[index].isFaceUp = (index == newValue)
            }
        }
    }
    
    mutating func chooseCard(at index: Int) {
        assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)): chosen index not in the cards")
        if !cards[index].isMatched {
            if indexOfOneAndOnlyFaceUpCard != nil {
                if indexOfOneAndOnlyFaceUpCard != index {
                    let matchIndex = indexOfOneAndOnlyFaceUpCard!
                    // check if cards match
                    if cards[matchIndex] == cards[index] {
                        cards[matchIndex].isMatched = true
                        cards[index].isMatched = true
                    }
                    cards[index].isFaceUp = true
                }
            } else {
                indexOfOneAndOnlyFaceUpCard = index
            }
        }
    }
    
    // initializers can be private as well, but in this case it should not be private
    init(numberOfPairsOfCards: Int) {
        assert(numberOfPairsOfCards > 0, "Concentration.init(\(numberOfPairsOfCards)): you must have at least one pair of cards")
        // CountableRange is a Sequence
        // special syntax
        // 1...numberOfPairsOfCards -- [1 -- numberOfPairsOfCards]
        // 0..<numberOfPairsOfCards -- [0 -- numberOfPairsOfCards-1]
        // _ means I DONT CARE! DONT GIVE IT A NAME!
        for _ in 0..<numberOfPairsOfCards {
            // structs get a free initializer as well
            let card = Card()
            cards.append(card)
            cards.append(card) // this creates a copy of card!! also assignment creates a copy!!
            // or
            //   cards += [card, card]
            // this is the same
        }
    }
}
<file_sep>/Concentration/Card.swift
//
//  Card.swift
//  Concentration
//
//  Created by <NAME> on 2018. 12. 17..
//  Copyright © 2018. <NAME>. All rights reserved.
//

import Foundation

// Card is a struct
// Difference from class:
//   - struct -- no inheritance
//   - value types   ... class is a reference type
//      - this means it gets copied!!!
//     Arrays, Ints, Strings, Dictionaries -- these are all structs! But swift implement these in a copy-on-write semantics. That's why it is fast.
// This card does not contain the emoji. This is the model, not the UI!
struct Card: Hashable {
    var isFaceUp = false
    var isMatched = false
    private var identifier: Int
    
    var hashValue: Int { return identifier }
    
    static func ==(lhs: Card, rhs: Card) -> Bool {
        return lhs.identifier == rhs.identifier
    }
    
    private static var identifierFactory = 0
    
    private static func getUniqueIdentifier() -> Int {
        identifierFactory += 1
        return identifierFactory
    }
    
    // inits have usually the same internal name and external name
    init() {
        self.identifier = Card.getUniqueIdentifier()
    }
}
