下面是上面这些算法的调用:
还是在原来位置添加方法:
//提供给主控制器调用 参数:方向,是否移动过的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 : (0, 0) , 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: ((Int, Int), (Int, Int)), to: (Int, Int), 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 }
})
}
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
}
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() }
})
}
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)
}