private var id = 0
@Published private(set) var gridRows = 4
@Published private(set) var existBlocks: [Block] = []
@Published private(set) var score = 0
// visible blocks
private var realBlocks: [Block] {
existBlocks.filter { block in
let blockIndex = block.getIndex(gridRows: gridRows)
if existBlocks.firstIndex(where: {
$0.id != block.id && $0.getIndex(gridRows: gridRows) == blockIndex && $0.number > block.number
}) != nil {
return false
}
return true
}
}
var overIf: Bool {
if realBlocks.count >= (gridRows * gridRows) {
for block_i in realBlocks {
for block_j in realBlocks {
if block_j.number == block_i.number &&
((block_j.at.0 == block_i.at.0 && block_j.at.1 == block_i.at.1 + 1) ||
(block_j.at.1 == block_i.at.1 && block_j.at.0 == block_i.at.0 + 1) ||
(block_j.at.0 == block_i.at.0 + 1 && block_j.at.1 == block_i.at.1 + 1)){
return false
}
}
}
return true
}
return false
}
id是用于Block的唯一标志,每次创建一个新的Block,id += 1
,每次重新开始游戏,id
归零;GameLogic里gridRows
代表网格是几行几列;existBlocks
代表当前网格中存在的Block,包括未显示的Block,是用于视图显示的,方便展示位置变化动画;而realBlocks
代表网格中真正显示的Block,通过过滤existBlocks
里位置重复的Block留下同位置number
大的Block而产生的,是用于计算每次操作后的新的Blocks的;score
代表玩家当前获得的分数;overIf
代表游戏是否已结束,通过判断网格中Block是否已满,且无法进一步合并。
State 1
existBlocks = [
{id: 1, number: 2, at: (0, 0)},
{id: 2, number: 2, at: (0, 1)},
{id: 3, number: 2, at: (3, 2)},
]
realBlocks = existBlocks
State 2 (Left Slide)
existBlocks = [
{id: 1, number: 4, at: (0, 0)},
{id: 2, number: 2, at: (0, 0)}, // hidden Block
{id: 3, number: 2, at: (3, 0)},
{id: 4, number: 2, at: (2, 3)}, // created Block
]
realBlocks = [
{id: 1, number: 4, at: (0, 0)},
{id: 3, number: 2, at: (3, 0)},
{id: 4, number: 2, at: (2, 3)},
]
State 3 (Left Slide)
existBlocks = [
{id: 1, number: 4, at: (0, 0)},
{id: 3, number: 2, at: (3, 0)},
{id: 4, number: 2, at: (2, 0)},
{id: 5, number: 2, at: (1, 0)}, // created Block
]
realBlocks = existBlocks
State 4 (Up Slide)
existBlocks = [
{id: 1, number: 4, at: (0, 0)},
{id: 3, number: 2, at: (2, 0)},
{id: 4, number: 2, at: (1, 0)}, // hidden Block
{id: 5, number: 4, at: (1, 0)},
{id: 6, number: 2, at: (3, 1)}, // created Block
]
realBlocks = [
{id: 1, number: 4, at: (0, 0)},
{id: 3, number: 2, at: (2, 0)},
{id: 5, number: 4, at: (1, 0)},
{id: 6, number: 2, at: (3, 1)},
]
private func blocksGoUp(blocks prevBlocks: [Block], gridRows: Int) -> (moved: Bool, blocks: [Block]) {
var blocks: [Block] = []
var moved = false
for column in 0..<gridRows {
var columnBlocks = prevBlocks.filter({ $0.at.1 == column }).sorted(by: { $0.at.0 < $1.at.0})
var i = 0
var row = 0
while i < columnBlocks.count {
if i + 1 < columnBlocks.count {
if columnBlocks[i].number == columnBlocks[i + 1].number {
self.score += columnBlocks[i].number
columnBlocks[i].number = columnBlocks[i].number * 2
columnBlocks[i].at.0 = row
columnBlocks[i + 1].at.0 = row
i += 2
row += 1
moved = true
}else {
if !(columnBlocks[i].at.0 == row && columnBlocks[i + 1].at.0 == row + 1) {
columnBlocks[i].at.0 = row
columnBlocks[i + 1].at.0 = row + 1
moved = true
}
i += 1
row += 1
}
}else {
if columnBlocks[i].at.0 != row {
columnBlocks[i].at.0 = row
moved = true
}
i += 1
}
}
blocks += columnBlocks
}
return (moved, blocks)
}
blocksGoUp
的输入是一个realBlocks
,输出是realBlocks
经过上滑操作后新的existBlocks
(未创建新的Block),以及此次操作是否产生了方块移动moved
。实现方式是:依次处理网格的每一列,同一列中如果两个连续的方块number
相同,则使下面的方块上移,且上面的方块number * 2
,如果数字不相同,则把他们位移到对应的位置即可,然后接着处理之后的方块。
func upAction() {
if overIf {
return;
}
let (moved, blocks) = blocksGoUp(blocks: realBlocks, gridRows: gridRows)
if moved {
existBlocks = blocks + [createBlock(blocks: blocks, gridRows: gridRows)]
}
}
首先判断游戏是否以及结束,如果没有,将真实显示的方块上移,并且上移如果确实移动了显示的方块,则创建一个新的方块。
func leftAction() {
if overIf {
return;
}
var (moved, blocks) = blocksGoUp(blocks: gridClockwiseRotation(blocks: realBlocks, gridRows: gridRows), gridRows: gridRows)
if moved {
blocks = gridAnticlockwiseRotation(blocks: blocks, gridRows: gridRows)
existBlocks = blocks + [createBlock(blocks: blocks, gridRows: gridRows)]
}
}
func rightAction() {
if overIf {
return;
}
var (moved, blocks) = blocksGoUp(blocks: gridAnticlockwiseRotation(blocks: realBlocks, gridRows: gridRows), gridRows: gridRows)
if moved {
blocks = gridClockwiseRotation(blocks: blocks, gridRows: gridRows)
existBlocks = blocks + [createBlock(blocks: blocks, gridRows: gridRows)]
}
}
func downAction() {
if overIf {
return;
}
var (moved, blocks) = blocksGoUp(blocks: gridFlipHorizontal(blocks: realBlocks, gridRows: gridRows), gridRows: gridRows)
if moved {
blocks = gridFlipHorizontal(blocks: blocks, gridRows: gridRows)
existBlocks = blocks + [createBlock(blocks: blocks, gridRows: gridRows)]
}
}