写到这 下一步就该插入块了. 思考:在什么样的位置才能加小块? 得判断一个位置是否为空的。所以
GameView.swift里添加判断方法:
//位置是在16个格内?
func isValid(pos:(Int,Int)) ->Bool{
let (x,y) = pos
return x>=0&&y>=0&&x<dimension&&y<dimension
}
//判断完空,才写插入方法。参数:插入的位置,值
func insertUnit(pos:(Int,Int),value:Int){
//其实这里断言语句都不用写,项目不大,debug不是很艰巨,assert没多大用
assert(isValid(pos: pos))
let (row,col) = pos
//把行列数转化为相对于大正方形的位置的相关计算我就不解释了,自己心里算算== :
let x0 =CGFloat(row+1)*thinPadding +unitwidth*CGFloat(row)
let y0 =CGFloat(col+1)*thinPadding +unitwidth*CGFloat(col)
let unitLabel =UnitView(pos:CGPoint(x: x0, y: y0), wid:unitwidth, value: value, delegate:provider)
读者可以自定义UnitView的构造方法,把CGPoint参数换成一个元组或俩参数都行,不用拘泥
//添加view:
addSubview(unitLabel)
bringSubview(toFront: unitLabel)
//别忘了再往字典里加上这个新来的 unit
units[IndexPath(row: row, section: col)] = unitLabel
}
// 1、返回空位置数组
func getEmptyPos() ->[(Int,Int)]{
var empUnits:[(Int,Int)] = []
for i in 0..<dimension{
for j in 0..<dimension{
if case .empty = gameView[i,j]{
empUnits.append((i,j))
}
}
}
return empUnits
}
// 2、随机插入
func insertRandomUnit(value:Int){
let emptyUnits = getEmptyPos()
if emptyUnits.isEmpty{ return }
//在空位置数组里产生一个随机位置
let randomPos =Int(arc4random_uniform(UInt32(emptyUnits.count-1)))
let (x,y) = emptyUnits[randomPos]
gameView[(x,y)] =UnitEnum.unit(value)
让delegate调用插入小块的方法,并把随机位置参数传过去
delegate.insertUnit(at: (x,y), withValue: value)
func changeScore(to score:Int) {
scoreBoard.score = score
}
func moveOneUnit(from: (Int,Int), to: (Int,Int), value: Int) {
//一会会写的:game.moveOneunits(from: from, to: to, value: value)
}
func moveTwoUnits(from: ((Int,Int), (Int,Int)), to: (Int,Int), value:Int) {
//一会会写的:game.moveTwounits(from: from, to: to, value: value)
}
//这次补充的
func insertUnit(at location: (Int,Int), withValue value:Int) {
game.insertUnit(pos: location, value: value)
}
func showGridView(){
unit = (boardwidth-thinPadding*CGFloat(dimension+1))/CGFloat(dimension)
game =GameView(unit:unit,dime:dimension,x:x,y:y,thin:thinPadding)
gameModle!.insertRandomUnit(value:2)
gameModle!.insertRandomUnit(value:2)
view.addSubview(game)
}
//四个移动方向:
enum MoveDirection{
case UP,DOWN,LEFT,RIGHT
}
//数字几种移动状态,及重要数据:位置和新值
//还是那句话,现在不知道是干什么的不要紧,一会会明白
enum UnitAction{
case NoAction(source:Int,value:Int) //不动
case Move(source:Int,value:Int) //单纯移动
case SingleCombine(source:Int,value:Int) //一个不动,另一个结合上去
case DoubleCombine(firstSource:Int,secondSource:Int,value:Int)//俩结合后一起移动到一边
//source是原位置 value是块上的值
func getValue()->Int{
switch self {
case let .NoAction(_,value): return value
case let .Move(_,value): return value
case let .SingleCombine(_,value): return value
case let .DoubleCombine(_,_,value):return value
}
}
func getSource() ->Int{
switch self {
case let .NoAction(source,_): return source
case let .Move(source,_): return source
case let .SingleCombine(source,_): return source
case let .DoubleCombine(source,_,_):return source
}
}
}
//最终的移动数据封装,标注了所有需移动的块的原位置及新位置,以及块的最新值
enum MoveOrder{
case SingleMoveOrder(source:Int,destination:Int,value:Int,merged:Bool)
case DoubleMoveOrder(first:Int,second:Int,destination:Int,value:Int)
}
//最艰巨的部分 GameModel.swift中添加方法:
//一、分三个步骤:去空,合并,转成order,
//1.去空。第一层判断,返回一个记录行为的数组,仅包含能不能动的属性
func condense(_ group:[UnitEnum])->[UnitAction]{
var buffer = [UnitAction]()
for (index,unit)in group.enumerated(){
//巧妙 : 如果buffer的大小和当前group的下标一致,则表示当前数字块不需要移动如2_2_,
//第一次时buffer大小和index都是0,不需要移动
//下一个2时,buffer大小为1,groupindex为2,则需要移动了
switch unit {
case let .unit(value)where buffer.count == index :
buffer.append(UnitAction.NoAction(source: index, value: value))
case let .unit(value) :
buffer.append(UnitAction.Move(source: index, value: value))
default:break
}
}
return buffer
}
//仍然保持不动的单元
staticfunc quiescentUnitStillQuiescent(inputPos:Int, outputLen:Int, originalPos:Int) ->Bool {
return (inputPos == outputLen)&& (originalPos == inputPos)
}
//2.合并(折叠) 第二层判断。返回记录行为的数组,更详细了,包括了单结合,还是双结合再滑动。
//这部分算法想研究的可细究,挺折磨人的。。。我反正是复制粘贴的=====
func collapse(_ group:[UnitAction]) -> [UnitAction]{
var buffer = [UnitAction]()
//如果把下一个块合并过来,则下一个数字块应该跳过
var skipNext =false
for(index,token)in group.enumerated(){
if skipNext {
skipNext = false
continue
}
switch token {
//当前块和下一个块值相同且当前块不移动,则将下一个块合并到当前块来
case let .NoAction(source,value)
where (index<group.count-1
&& value == group[index+1].getValue()
&&GameModel.quiescentUnitStillQuiescent(inputPos: index,outputLen:buffer.count,originalPos:source)):
let next = group[index+1]
let newValue = value+next.getValue()
skipNext = true
buffer.append(UnitAction.SingleCombine(source: next.getSource(), value: newValue))
//当前块和下一个块的值相同且两个块都要移动,则两个块移动到新的位置
case let t
where (index < group.count-1&& t.getValue() == group[index+1].getValue()):
let next = group[index+1]
let newValue = t.getValue() + group[index+1].getValue()
skipNext = true
buffer.append(UnitAction.DoubleCombine(firstSource: t.getSource(), secondSource: next.getSource(), value: newValue))
//上一步判定不需要移动,但是之前的块有合并过,所以需要移动
case let .NoAction(source,value) where !GameModel.quiescentUnitStillQuiescent(inputPos:index, outputLen: buffer.count, originalPos: source):
buffer.append(UnitAction.Move(source: source, value: value))
//上一步判定不需要移动,且之前的块也没有合并,则不需要移动
case let .NoAction(source, value):
buffer.append(UnitAction.NoAction(source: source, value: value))
//上一步判定需要移动且不符合上面的条件的,则继续保持移动
case let .Move(source, value):
buffer.append(UnitAction.Move(source: source, value: value))
default:break
}
}
return buffer
}
//3.最后,把移动行为数组转化成移动命令数组order
func convert(_ group:[UnitAction]) ->[MoveOrder]{
var buffer = [MoveOrder]()
for (index , unitAction)in group.enumerated() {
switch unitAction {
case let .Move(s, v) :
//单纯的将一个块由s位置移动到index位置,新值为v
buffer.append(MoveOrder.SingleMoveOrder(source: s, destination: index, value: v, merged: false))
case let .SingleCombine(s, v) :
//将一个块由s位置移动到index位置,且index位置有数字块,俩数字块进行合并,新值为v
buffer.append(MoveOrder.SingleMoveOrder(source: s, destination: index, value: v, merged: true))
case let .DoubleCombine(s, d, v) :
//将s和d两个数字块移动到index位置并进行合并,新值为v
buffer.append(MoveOrder.DoubleMoveOrder(first: s, second: d, destination: index, value: v))
default:
break
}
}
return buffer
}
//草,终于到最后了:merge:合并
//给我一个UnitEnum数组,就是保存小块状态的数组,就能让它变成移动状态数组!
func merge(group:[UnitEnum]) ->[MoveOrder]{
return convert(collapse(condense(group)))
}
调用merge方法
还是在原来位置添加方法 :