時(shí)間過(guò)得真快,倏忽之間惠昔,畢業(yè)已近一年幕与。
畢業(yè)答辯時(shí)做的項(xiàng)目有個(gè)游戲模塊,掃雷游戲镇防。當(dāng)時(shí)也是想挑戰(zhàn)一下自己啦鸣,于是就著手構(gòu)思怎么實(shí)現(xiàn)這個(gè)游戲。前前后后大概用了一周時(shí)間完成了掃雷的基本功能来氧,如點(diǎn)擊掃雷诫给,長(zhǎng)按標(biāo)注地雷香拉,難度選擇等。但沒(méi)有實(shí)現(xiàn)點(diǎn)擊空白單元周圍也翻過(guò)來(lái)的功能中狂。后來(lái)在一次面試中凫碌,和一個(gè)技術(shù)總監(jiān)談到游戲的不足時(shí),他提示到可以用遞歸遍歷的方法找到點(diǎn)擊的空白單元周圍所有可以翻轉(zhuǎn)單元的位置胃榕,才恍然大悟盛险。
閑話少說(shuō),且看我的思路和實(shí)現(xiàn)過(guò)程
還是先上 Demo
#以下以10×10地圖為例進(jìn)行分析
定義
1)用數(shù)組mineMapArray
(0 - 9)存儲(chǔ)每個(gè)單元的狀態(tài)勋又,初始化全為0
0苦掘,表示單元周圍沒(méi)有地雷
1 - 8,表示單元周圍有1 - 8個(gè)地雷
9楔壤,表示該單元是地雷單元的周圍是指鹤啡,當(dāng)前單元的左上方、上方蹲嚣、右上方递瑰、右方、右下方隙畜、下方抖部、左下方、左方的單元禾蚕。一個(gè)單元周圍最多有8個(gè)單元您朽。
2)用數(shù)組minesArray
(0 - 99)存儲(chǔ)所有地雷的位置
3)用數(shù)組turnoverArray
(0 - 99)存儲(chǔ)點(diǎn)擊空白單元時(shí)可翻轉(zhuǎn)所有單元的位置
1狂丝、隨機(jī)地雷的位置
1)先創(chuàng)建臨時(shí)地圖位置數(shù)組tmpMapArray
(0 - 99)换淆,方便下一步隨機(jī)_mineNums
個(gè)地雷位置用
//1.創(chuàng)建臨時(shí)地圖位置數(shù)組,用于隨機(jī)出地雷位置
NSMutableArray *tmpMapArray = [NSMutableArray array];//臨時(shí)地圖位置數(shù)組
for (int i = 0; i < _row * _column; i++) {
[tmpMapArray addObject:@(i)];
}
2)隨機(jī)產(chǎn)生_mineNums
個(gè)地雷并記錄地雷位置到地圖相應(yīng)mineMapArray
上
delIndex
几颜,臨時(shí)地圖數(shù)組tmpMapArray
的刪除的位置
addIndex
倍试,地雷地圖數(shù)組minesArray
上添加地雷的位置
//2.更新地圖地雷位置和記錄地雷位置
int delIndex;//隨機(jī)地雷的位置
int addIndex;//地雷添加到地圖的位置
for (int i = 0; i < _mineNums; i++) {
delIndex = arc4random() % tmpMapArray.count;
addIndex = [tmpMapArray[delIndex] intValue];
[self.mineMapArray replaceObjectAtIndex:addIndex withObject:@(9)];//更地圖上地雷位置
[self.minesArray addObject:tmpMapArray[delIndex]];//添加地雷位置到存儲(chǔ)所有地雷位置的數(shù)組
[tmpMapArray removeObjectAtIndex:delIndex];//刪除臨時(shí)隨機(jī)的地雷位置
}
3)計(jì)算每個(gè)不是地雷的單元的周圍地雷數(shù)量
一般情況下地雷的數(shù)量比較少,所以蛋哭,首先我們可以遍歷找到地雷單元县习,然后再遍歷地雷周圍的單元,再在mineMapArray
數(shù)組相應(yīng)位置上加1谆趾。
地雷的位置是
location
躁愿,_row
是行數(shù),_column
是列數(shù)沪蓬,即一行單元的個(gè)數(shù)
所以彤钟,
左上 =location - _column - 1
上 =location - _column
右上 =location - _column + 1
右 =location + 1
右下 =location + _column + 1
下 =location + _column
左下 =location + _column - 1
左 =location - 1
注意:在遍歷周圍單元是要注意是否在邊界位置
location / _column != 0
判斷當(dāng)前單元是否在第一行
location % _column != 0
判斷當(dāng)前單元是否在第一列
location / _column != _row - 1
判斷當(dāng)前單元是否在最后一行
location % _column != _column - 1
判斷當(dāng)前單元是否在最后一列左上單元,需要判斷當(dāng)前單元是否在第一行&&是否在第一列
上單元跷叉,需要判斷當(dāng)前單元是否在第一行
右上單元逸雹,需要判斷當(dāng)前單元是否在第一行&&是否在最后一列
右單元营搅,需要判斷當(dāng)前單元是否在最后一列
右下單元,需要判斷當(dāng)前單元是否在最后一行&&最后一列
下單元梆砸,需要判斷當(dāng)前單元是否在最后一行
左下單元转质,需要判斷當(dāng)前單元是否在最后一行&&第一列
左單元,需要判斷當(dāng)前單元是否在第一列
//3.標(biāo)記地雷周圍數(shù)字
for (NSNumber *obj in self.minesArray) {//找到地雷周圍位置帖世,標(biāo)記數(shù)值加1
NSInteger location = [obj integerValue];
NSInteger aroundLocation;//遍歷地雷周圍8個(gè)位置
//location / _column != 0 判斷是否在第一行
//location % _column != 0 判斷是否在第一列
//location / _column != _row - 1 判斷是否在最后一行
//location % _column != _column - 1 判斷是否在最后一列
//
aroundLocation = location - _column;//上
if (location / _column != 0) {
[self locationPlus:aroundLocation];
}
aroundLocation = location - _column + 1;//右上
if (location / _column && location % _column != _column - 1) {
[self locationPlus:aroundLocation];
}
aroundLocation = location + 1;//右
if (location % _column != _column - 1) {
[self locationPlus:aroundLocation];
}
aroundLocation = location + _column + 1;//右下
if (location % _column != _column - 1 && location / _column != _row - 1) {
[self locationPlus:aroundLocation];
}
aroundLocation = location + _column;//下
if (location / _column != _row - 1) {
[self locationPlus:aroundLocation];
}
aroundLocation = location + _column - 1;//左下
if (location / _column != _row - 1 && location % _column != 0) {
[self locationPlus:aroundLocation];
}
aroundLocation = location - 1;//左
if (location % _column != 0) {
[self locationPlus:aroundLocation];
}
aroundLocation = location - _column - 1;//左上
if (location / _column != 0 && location % _column != 0) {
[self locationPlus:aroundLocation];
}
}
- (void)locationPlus:(NSInteger)location {
NSInteger cellMineNums = [[self.mineMapArray objectAtIndex:location] integerValue];
if (cellMineNums != 9) {
cellMineNums++;
}
[self.mineMapArray replaceObjectAtIndex:location withObject:@(cellMineNums)];
}
2休蟹、初始化地圖
代碼:
/**
* 初始化地圖
*/
- (void)setupMapView {
for (int i = 0; i < _row * _column; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = kTag + i;
//設(shè)置frame
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGFloat buttonW = (screenBounds.size.width - kBorderX * 2 - (_column - 1) * kGap) / _column;
CGFloat buttonH = buttonW;
CGFloat buttonX = (i % _column) * (buttonW + kGap) + kBorderX;
CGFloat buttonY = (i / _column) * (buttonH + kGap) + kBorderX;
button.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH);
button.backgroundColor = [UIColor grayColor];
[button setBackgroundImage:[UIImage imageNamed:[NSString stringWithFormat:@"selected_%@", self.mineMapArray[i]]] forState:UIControlStateSelected];
[button setBackgroundImage:[UIImage imageNamed:@"selected_bg"] forState:UIControlStateNormal];
[button addTarget:self action:@selector(cellButtonSelect:) forControlEvents:UIControlEventTouchUpInside];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(markMine:)];
[button addGestureRecognizer:longPress];
[self.bgView addSubview:button];
}
}
隨機(jī)地雷位置數(shù)組:
[0, 0, 0, 1, 9, 1, 0, 0, 1, 1,
0, 0, 0, 1, 1, 1, 1, 1, 2, 9,
0, 0, 0, 0, 0, 0, 1, 9, 4, 3,
0, 0, 0, 1, 2, 3, 3, 3, 9, 9,
0, 1, 1, 2, 9, 9, 9, 2, 2, 2,
0, 2, 9, 4, 4, 4, 3, 1, 0, 0,
0, 2, 9, 9, 2, 9, 2, 1, 1, 0,
0, 1, 2, 2, 2, 1, 2, 9, 1, 0,
1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
9, 1, 1, 9, 1, 0, 0, 0, 0, 0]
地雷位置效果圖:
3、掃雷邏輯
- 點(diǎn)擊數(shù)字單元狮暑,數(shù)字單元翻過(guò)來(lái)
- 點(diǎn)擊地雷單元鸡挠,所有單元翻過(guò)來(lái),游戲結(jié)束
- 點(diǎn)擊空白單元搬男,找出其周圍“可翻轉(zhuǎn)的單元”拣展,并翻轉(zhuǎn)過(guò)來(lái)
“可翻轉(zhuǎn)的單元”是指,如下圖缔逛,當(dāng)點(diǎn)擊綠色區(qū)域內(nèi)任意空白單元時(shí)綠色區(qū)域全部翻轉(zhuǎn)過(guò)來(lái)
下面是當(dāng)點(diǎn)擊黃點(diǎn)位置空白單元時(shí)备埃,找到其周圍可翻轉(zhuǎn)的單元的思路和算法
思路:
① 如果當(dāng)前單元是空白單元,先把這個(gè)單元存到turnoverArray
;
② 再依次判斷這個(gè)單元的上褐奴、右上按脚、右、右下敦冬、下辅搬、左下、左脖旱、左上單元
③ 如果判斷的單元是空白單元堪遂,則把判斷的單元作為當(dāng)前空白單元回到①;如果判斷的單元是數(shù)字單元,則回到②依次進(jìn)行判斷萌庆;
核心算法:
- (void)findAllTurnover:(NSInteger)location {
if (![self.turnoverArray containsObject:@(location)]) {//如果turnoverArray不包含這個(gè)單元溶褪,存進(jìn)去
[self.turnoverArray addObject:@(location)];
}
if ([self.mineMapArray[location] integerValue] != 0) {//如果當(dāng)前單元不是空白單元?jiǎng)t,回到上一層繼續(xù)尋找下一個(gè)位置
return;
}
NSInteger aroundLocation;
aroundLocation = location - _column - 1;//左上
if (location / _column != 0 && location % _column != 0) {
[self addTurnover:aroundLocation];
}
aroundLocation = location - _column;//上
if (location / _column != 0) {
[self addTurnover:aroundLocation];
}
aroundLocation = location - _column + 1;//右上
if (location / _column && location % _column != _column - 1) {
[self addTurnover:aroundLocation];
}
aroundLocation = location + 1;//右
if (location % _column != _column - 1) {
[self addTurnover:aroundLocation];
}
aroundLocation = location + _column + 1;//右下
if (location % _column != _column - 1 && location / _column != _column - 1) {
[self addTurnover:aroundLocation];
}
aroundLocation = location + _column;//下
if (location / _column != _column - 1) {
[self addTurnover:aroundLocation];
}
aroundLocation = location + _column - 1;//左下
if (location / _column != _column - 1 && location % _column != 0) {
[self addTurnover:aroundLocation];
}
aroundLocation = location - 1;//左
if (location % _column != 0) {
[self addTurnover:aroundLocation];
}
}
- (void)addTurnover:(NSInteger)location {
if ([self.turnoverArray containsObject:@(location)]) {//如果已經(jīng)包含這個(gè)單元return
return;
}
[self.turnoverArray addObject:@(location)];
[self findAllTurnover:location];
}
4践险、頗多不足猿妈,望各位不吝賜教
- 有些細(xì)節(jié)沒(méi)有說(shuō)明白
- 用遞歸遍歷尋找空白單元周圍可翻轉(zhuǎn)單元時(shí),時(shí)間復(fù)雜度太大巍虫。