【swift3实战】2048 教程(终)

下面是上面这些算法的调用:

还是在原来位置添加方法:


 //提供给主控制器调用 参数:方向,是否移动过的Bool值为入参的闭包

    func queenMove(direction : MoveDirection , completion : (Bool) -> ()){


        let changed = performMove(direction: direction)

        completion(changed)


    }


    //移动实现

    func performMove(direction : MoveDirection) -> Bool {

        //返回四个块的坐标

        let getMoveQueen : (Int) -> [(Int , Int)] =

        {

            (idx : Int) -> [(Int , Int)] in

            var buffer = Array<(Int , Int)>(repeating : (00) , count : self.dimension)

            for i in 0..<self.dimension{

                switch direction {

                case .UP:     buffer[i] = (idx,i)

                case .LEFT :  buffer[i] = (i, idx)

                case .DOWN :  buffer[i] = (idx, self.dimension - i - 1)

                case .RIGHT : buffer[i] = (self.dimension - i - 1, idx)

                }

            }

            return buffer

        }

        var movedFlag = false

        //逐行列处理

        for i in 0..<dimension{

            //获取当前行列的4个坐标

            let moveQueen  = getMoveQueen(i)

            let units = moveQueen.map({

                (c:(Int,Int)) -> UnitEnum in

                let (source,value) = c

                return self.gameView[source,value]

            })

            let moveOrders = merge(group: units)

            movedFlag = moveOrders.count > 0 ? true : movedFlag

            //对算法返回结果处理:更新数据和view的块

            for order in moveOrders{

                switch order  {

                //order

                case let .SingleMoveOrder(source,desti,va,merged):

                    let (sx,sy) = moveQueen[source]

                    let (dx,dy) = moveQueen[desti]

                    if merged {

                        self.score += va

                    }

                    //原位置置空,设定新位置

                    gameView[sx,sy] = .empty

                    gameView[dx,dy] = .unit(va)

                    //调用协议更新视图块。现在可以把那个协议方法的注释化掉

                    delegate.moveOneUnit(from: (sx,sy), to: (dx,dy), value: va)

                //order

                case let .DoubleMoveOrder(firS,secS,desti,va):

                    let (fsx , fsy) = moveQueen[firS]

                    let (tsx , tsy) = moveQueen[secS]

                    let (dx , dy) = moveQueen[desti]

                    self.score += va

                    //将原位置置空,新位置设置为新的值

                    gameView[fsx , fsy] = .empty

                    gameView[tsx , tsy] = .empty

                    gameView[dx , dy] = .unit(va)

                    //TODO 调用游戏视图更新视图中的数字块

                    delegate.moveTwoUnits(from: (moveQueen[firS], moveQueen[secS]), to: moveQueen[desti], value: va)

                }

            }

        }

        return movedFlag

    }

    这样就更新了gameView


现在该去实现 协议方法里面那两个牛逼的方法了 。在GameView.swift 中添加方法:

//刷新面板:移动一块

    func moveOneunits(from : (Int , Int)  , to : (Int , Int) , value : Int) {


        let (froX,froY) = from

        let (toX,toY) = to

        let fromKey = IndexPath(row: froX, section: froY)

        let toKey = IndexPath(row: toX, section: toY)


        这些操作的原理在之前的一大段话里介绍过了:

        guard let unit = units[fromKey] else{

            assert(false,"nil")

        }

        let endUnit = units[toKey]

        

        var finalFrame = unit.frame

        finalFrame.origin.x = thinPadding + CGFloat(toX)*(thinPadding+unitwidth)

        finalFrame.origin.y = thinPadding + CGFloat(toY)*(thinPadding+unitwidth)

     

        units.removeValue(forKey: fromKey)

        units[toKey] = unit


        let shouldPop = endUnit != nil


        //可以把下面的简化,把动画效果删掉。当然想研究一下的同学可以添加更牛逼、炫酷的动画   

   UIView.animate(withDuration: 0.08,delay: 0.0,options:UIViewAnimationOptions.beginFromCurrentState,

            animations: {  unit.frame = finalFrame  },

            completion: {

                (finished: Bool) -> Void in

                //对新位置的数字块赋值

                unit.value = value

                endUnit?.removeFromSuperview()

                if !shouldPop || !finished {  return   }

            }

        )

    }


    两个方法差不多,稍微注意下区别就行

    func moveTwounits(from: ((IntInt), (IntInt)), to: (IntInt), value: Int) {


        assert(isValid(pos: from.0&& isValid(pos: from.1&& isValid(pos: to))


        let (fromXA, fromYA) = from.0

        let (fromXB, fromYB) = from.1

        let (toX, toY) = to


        let fromKeyA = IndexPath(row: fromXA, section: fromYA)

        let fromKeyB = IndexPath(row: fromXB, section: fromYB)


        let toKey = IndexPath(row: toX, section: toY)

        

        guard let unitA = units[fromKeyA] else {

            assert(false"nil")

        }

        guard let unitB = units[fromKeyB] else {

            assert(false"nil")

        }

        

        var finalFrame = unitA.frame

        finalFrame.origin.x = thinPadding + CGFloat(toX)*(unitwidth + thinPadding)

        finalFrame.origin.y = thinPadding + CGFloat(toY)*(unitwidth + thinPadding)

       

        let endUnit = units[toKey]  // TODO: make sure this doesn't cause issues

        endUnit?.removeFromSuperview()


        units.removeValue(forKey: fromKeyA)

        units.removeValue(forKey: fromKeyB)

        units[toKey] = unitA


        UIView.animate(withDuration: 0.08, delay: 0.0, options:UIViewAnimationOptions.beginFromCurrentState, animations: {

                unitA.frame = finalFrame

                unitB.frame = finalFrame },

            completion: { finished in

                //赋值

                unitA.value = value

                unitB.removeFromSuperview()

                if !finished {   return   }

        })

    }


烧脑部分正式结束,进入最后的环节:监听滑动操作 和 判断是否输掉以及一些简单的UI设计

在GamePanel.swift中添加方法:
这是真监听,真的比算法简单了一万倍

func setupSwipeController(){


        let upSwipe =UISwipeGestureRecognizer(target:self, action: #selector(upCommand(_:)))

        upSwipe.numberOfTouchesRequired =1

        upSwipe.direction = .up

        view.addGestureRecognizer(upSwipe)

        

        let downSwipe =UISwipeGestureRecognizer(target:self, action: #selector(downCommand(_:)))

        downSwipe.numberOfTouchesRequired =1

        downSwipe.direction = .down

        view.addGestureRecognizer(downSwipe)

        

        let leftSwipe =UISwipeGestureRecognizer(target:self, action: #selector(leftCommand(_:)))

        leftSwipe.numberOfTouchesRequired =1

        leftSwipe.direction = .left

        view.addGestureRecognizer(leftSwipe)

        

        let rightSwipe =UISwipeGestureRecognizer(target:self, action: #selector(rightCommand(_:)))

        rightSwipe.numberOfTouchesRequired =1

        rightSwipe.direction = .right

        view.addGestureRecognizer(rightSwipe)


    }


   在写滑动触发的事件之前,我们先考虑一下:每次移动后都要判断是否输掉,即格子全满且相邻单元没有相同的值

所以在GameModel里添加一个方法判断是否输了:

func userLost() ->Bool{

没满肯定没输:

        let isFull = getEmptyPos().isEmpty

        if !isFull {

            return false

        }


        俩方法,1.判断下面值是否相同:

        func belowSame(_ value:Int,pos:(Int,Int))->Bool{

            let (x,y) = pos

            if y==dimension-1{

                return false

            }

            if case let .unit(v) = gameView[x,y+1]{

                return v == value

            }

            return false

        }

2. 判断右边值是否相同

        func rightSame(_ value:Int,pos:(Int,Int))->Bool{

            let (x,y) = pos

            if x==dimension-1{

                return false

            }

            if case let .unit(v) = gameView[x+1,y]{

                return v == value

            }

            return false

        }

        遍历,找出有没有相邻且值相同的单元

        for i in 0..<dimension {

            for j in 0..<dimension{

                switch gameView[i,j]{

                case let .unit(value):

                    if (belowSame(value,pos:(i,j))||rightSame(value,pos:(i,j))) {

                        return false

                    }

                default:break

                }

            }

        }

        return true

    }


好了,现在可以在GamePanel里添加响应事件了:

func upCommand(_ swiper:UISwipeGestureRecognizer){


        gameModle?.queenMove(direction: .UP, completion: { (changed:Bool)->() in


            if changed { followUp() } 

 //注意::followUp就是移动成功要做的事,因为4个方向都要调用,所以封装成一个方法而已


        })

    }


    func downCommand(_ swiper:UISwipeGestureRecognizer){

        gameModle?.queenMove(direction: .DOWN, completion: { (changed:Bool)->() in

            if changed { followUp() }

        })

    }


    func leftCommand(_ swiper:UISwipeGestureRecognizer){

        gameModle?.queenMove(direction: .LEFT, completion: { (changed:Bool)->()in

            if changed { followUp() }

        })

    }


    func rightCommand(_ swiper:UISwipeGestureRecognizer){

        gameModle?.queenMove(direction: .RIGHT, completion: { (changed:Bool)->()in

            if changed { followUp() }

        })

    }

   


调用queenMove后,要随机插入一个单元,然后判断是否输掉,所以把这两个功能封在一个方法里:

func followUp(){


        let randomValue = Int(arc4random_uniform(10))

意思是有十分之一的几率新插入的值是4

        gameModle?.insertRandomUnit(value: randomValue==1 ? 4 : 2)


如果输了,弹出对话框问是否继续:

        if (gameModle?.userLost())!{


            let lostView = UIAlertController(title: "RESULT:", message: "Sorry: You 've lost the game.", preferredStyle: .alert)

            let resetAction = UIAlertAction(title: "TryAgain!", style: .cancel, handler: {(u: UIAlertAction)->() in self.reset()}) //reset方法 就是重置,下面介绍

            let cancelAction = UIAlertAction(title: "Surrender", style: .default, handler: {(u:UIAlertAction)->() in self.back()})//back,返回主界面


            lostView.addAction(cancelAction)

            lostView.addAction(resetAction)


            present(lostView, animated: true, completion: nil)


        }

    }

    

   返回主界面 

    func back(){

        present(ViewController(), animated: false, completion: nil)

    }


    重设游戏

    func reset(){

        gameModle?.score = 0


        for i in 0..<dimension {


            for j in 0..<dimension {


                game.units[IndexPath(row:i,section:j)]?.removeFromSuperview()

                game.units.removeValue(forKey: IndexPath(row:i,section:j))

                gameModle?.gameView[i,j] = .empty


            }

        }


//重设完了别忘了再随机生成一个好继续玩

        gameModle!.insertRandomUnit(value: 2)


    }



好了,应用到这里已经实现了基本功能。可以尝试一下把 dimension 也就是行列数改成5,或者把每个单元的所有取值改成 汉字 开始吧!






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值