[iOS]200行的拼图游戏

70 阅读2分钟

上班不能摸鱼,就自己写个拼图当消遣吧。

目的就是实现可选33 44 等不同大小的拼图游戏。 涉及知识点是逆序数计算。

class CollectionModel: NSObject {

    var isEmpty:Bool = false

    var itemIndex:Int = -1

    var image:UIImage?

}

  


class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

    var collectionView: UICollectionView!

    var collectionData = [CollectionModel]()

    

    var difficulty:Int = 3

    

    @IBOutlet weak var difficultyTF: UITextField!

    @IBOutlet weak var tips1: UILabel!

    @IBOutlet weak var tips2: UILabel!

    

    override func viewDidLoad() {

        super.viewDidLoad()

    }

    

    func loadSubviews(){

        if(self.collectionView != nil){

            self.collectionView.removeFromSuperview()

        }

        

        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()

        let itemWidth = self.view.frame.width/CGFloat(difficulty)

        layout.itemSize = CGSize.init(width: itemWidth, height: itemWidth)

        layout.minimumInteritemSpacing = 0

        layout.minimumLineSpacing = 0

        layout.sectionInset = .init(top: 0, left: 0, bottom: 0, right: 0)

        

        collectionView = UICollectionView(frame: CGRect(x: 0, y: 258, width: self.view.frame.width, height: self.view.frame.width), collectionViewLayout: layout)

        collectionView.dataSource = self

        collectionView.delegate = self

        collectionView.showsHorizontalScrollIndicator = false

        collectionView.showsVerticalScrollIndicator = false

        collectionView.isScrollEnabled = false

        collectionView.register(UINib(nibName: "KlotskiCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "KlotskiCollectionViewCell")

        collectionView.backgroundColor = UIColor(white: 0.95, alpha: 1)

        self.view.addSubview(collectionView)

        

    }

    

    @IBAction func test(_ sender: Any) {

        difficulty = Int(difficultyTF.text ?? "3") ?? 3

        difficulty = difficulty < 3 ? 3 : difficulty

        difficulty = difficulty > 6 ? 6 : difficulty

        

        self.collectionData.removeAll()

        let randomNumbers = self.getAvailableRandomNumbers(count: difficulty*difficulty-1)

        randomNumbers.forEach { index in

            let model = CollectionModel()

            model.itemIndex = index

            model.isEmpty = index == randomNumbers.count-1 ? true : false

            self.collectionData.append(model)

        }

        

        self.loadSubviews()

        self.collectionView.reloadData()

    }

    

    func getAvailableRandomNumbers(count:Int) -> [Int]{

        var randomArr = [Int](0...count)

        repeat{

            randomArr.shuffle()

        }while !self.checkNumbersAvailable(arr: randomArr)

        self.tips1.text = "\(randomArr)"

        return randomArr

    }

    

    func checkNumbersAvailable(arr:[Int])-> Bool{

        var inverseCount = 0

        arr.forEach { currentNumber in

            let index = arr.firstIndex(of: currentNumber) ?? 0

            if index != arr.count-1 {

                for i in (index+1)..<arr.count {

                    let checkNumber = arr[i]

                    inverseCount += currentNumber > checkNumber ? 1 : 0

                }

            }

            // 这个是针对偶数边长阵列 要计算空白坐标位置,网上看的

            if currentNumber == arr.count-1 {

                inverseCount += difficulty-1-(index/difficulty)

                inverseCount += difficulty-1-(index%difficulty)

            }

        }

        self.tips2.text = "逆序数:\(inverseCount)"

        return inverseCount%2 == 0

    }

    

    func checkKlotskiSuccess(){

        var success = true

        for model in collectionData {

            let index = collectionData.firstIndex(of: model)

            if(index != model.itemIndex){

                success = false

                break

            }

        }

        if(success){

            self.tips2.text = "Congratulation! Success!"

        }

    }

    

    func checkItemSwap(index:Int)->Int{

        let currentModel = self.collectionData[index]

        if currentModel.isEmpty { return -1}

        

        let leftIndex = index - 1

        if(index%difficulty > 0 && leftIndex >= 0){

            if(collectionData[leftIndex].isEmpty == true){

                return leftIndex

            }

        }

        let rightIndex = index + 1

        if(index%difficulty < difficulty-1 && rightIndex < collectionData.count){

            if(collectionData[rightIndex].isEmpty == true){

                return rightIndex

            }

        }

        let topIndex = index - difficulty

        if(topIndex >= 0 && collectionData[topIndex].isEmpty == true){

            return topIndex

        }

        let bottomIndex = index + difficulty

        if(bottomIndex < collectionData.count && collectionData[bottomIndex].isEmpty == true){

            return bottomIndex

        }

        

        return -1

    }

    

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        return collectionData.count

    }

    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "KlotskiCollectionViewCell", for: indexPath) as! KlotskiCollectionViewCell

        let model = self.collectionData[indexPath.row]

        cell.setCellData(model: model)

        return cell

    }

    

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        let swapIndex = self.checkItemSwap(index: indexPath.row)

        guard swapIndex >= 0 else { return }

        

        self.collectionData.swapAt(indexPath.row, swapIndex)

        self.collectionView.reloadItems(at: [indexPath, IndexPath(row: swapIndex, section: 0)])

        self.checkKlotskiSuccess()

    }

    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        self.view.endEditing(true)

    }

}

测试按钮和文字提示在XIB上,就不放出来了。 其中备注部分就是要计算空白块的坐标,原理参考的这个老哥的帖子:九宫格避免不可还原