iOS 自制小游戲之掃雷

時(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谆趾。

10×10

地雷的位置是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)的單元的思路和算法
遞歸遍歷過(guò)程圖

思路:

① 如果當(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ù)雜度太大巍虫。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末彭则,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子占遥,更是在濱河造成了極大的恐慌俯抖,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筷频,死亡現(xiàn)場(chǎng)離奇詭異蚌成,居然都是意外死亡前痘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門担忧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芹缔,“玉大人,你說(shuō)我怎么就攤上這事瓶盛∽钋罚” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵惩猫,是天一觀的道長(zhǎng)芝硬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)轧房,這世上最難降的妖魔是什么拌阴? 我笑而不...
    開(kāi)封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮奶镶,結(jié)果婚禮上迟赃,老公的妹妹穿的比我還像新娘。我一直安慰自己厂镇,他們只是感情好纤壁,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著捺信,像睡著了一般酌媒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迄靠,一...
    開(kāi)封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天秒咨,我揣著相機(jī)與錄音,去河邊找鬼梨水。 笑死拭荤,一個(gè)胖子當(dāng)著我的面吹牛茵臭,可吹牛的內(nèi)容都是我干的疫诽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旦委,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奇徒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缨硝,我...
    開(kāi)封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤摩钙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后查辩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胖笛,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡网持,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了长踊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片功舀。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖身弊,靈堂內(nèi)的尸體忽然破棺而出辟汰,到底是詐尸還是另有隱情旗们,我是刑警寧澤丽焊,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站砾肺,受9級(jí)特大地震影響凑术,放射性物質(zhì)發(fā)生泄漏翩蘸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一淮逊、第九天 我趴在偏房一處隱蔽的房頂上張望鹿鳖。 院中可真熱鬧,春花似錦壮莹、人聲如沸翅帜。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涝滴。三九已至,卻和暖如春胶台,著一層夾襖步出監(jiān)牢的瞬間歼疮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工诈唬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留韩脏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓铸磅,卻偏偏與公主長(zhǎng)得像赡矢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阅仔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容