iOS 广度优先搜索算法 实现扫雷自动‘翻牌’功能

这个算法自己在学习iOS开发做的一个扫雷游戏,并且已经上了AppStore ,但是之后一直在忙没顾得上优化,如果大家发现什么bug或者可以优化的地方,希望各位看官不吝赐教多多反馈交流。下面进入正题,实现这个功能需要自定义5个类,说明如下:

1.单个格子模型类,用于记录格子的坐标、遍历状态等状态;
2.图的模型类,用于记录行列数量和当前翻开的状态;
3.基于数组的队列类,广搜算法中要使用的队列,先进先出;(如果改为栈可以实现深度优先搜索)
4.计算周边点的工具类,只有一个方法实现根据某点获取其周边所有的点,并返回一个数组;
5.最重要的广度优先搜索算法类

定义一个格子模型

#import <Foundation/Foundation.h>
/// 点的坐标
typedef struct {
    int  x;
    int  y;
}ItemIndex;

@interface QSItem : NSObject <NSCoding>
/**是否是雷*/
@property(nonatomic,assign,getter=isMine)BOOL mine;
/**是否已经打开*/
@property(nonatomic,assign,getter=isOpened)BOOL opened;
/**周边雷数量*/
@property(nonatomic,assign) NSInteger arroundMineNumber;
/**是否遍历过*/
@property(nonatomic,assign,getter=isTraveled)BOOL traveled;
/**是否标记为雷*/
@property(nonatomic,assign,getter=isSignMine)BOOL signMine;
/**在地图中的坐标*/
@property(nonatomic,assign)ItemIndex indexInGraph;
@end
#import "QSItem.h"

@implementation QSItem

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super init];
    if (self) {
        [aDecoder decodeBoolForKey:@"mine"];
        [aDecoder decodeBoolForKey:@"opened"];
        [aDecoder decodeBoolForKey:@"traveled"];
        [aDecoder decodeBoolForKey:@"signMine"];
        [aDecoder decodeIntegerForKey:@"arroundMineNumber"];
        [aDecoder decodeValueOfObjCType:@encode(ItemIndex) at:&_indexInGraph];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeBool:self.mine forKey:@"mine"];
    [aCoder encodeBool:self.opened forKey:@"opened"];
    [aCoder encodeBool:self.traveled forKey:@"traveled"];
    [aCoder encodeBool:self.signMine forKey:@"signMine"];
    [aCoder encodeInteger:self.arroundMineNumber forKey:@"arroundMineNumber"];
    [aCoder encodeValueOfObjCType:@encode(ItemIndex) at:&_indexInGraph];
}


@end

定义一个图的模型

#import <Foundation/Foundation.h>

@interface QSGraph : NSObject<NSCoding>
/**地图数组*/
@property(nonatomic,strong) NSArray *itemArray;
/**地图宽度*/
@property(nonatomic,assign) NSInteger width;
/**地图高度*/
@property(nonatomic,assign) NSInteger heigth;
/**总雷数*/
@property(nonatomic,assign) NSUInteger totalMinesNumber;
/**已打开的总数*/
@property(nonatomic,assign) NSInteger totalOpenedNumber;

@end
#import "QSGraph.h"

@implementation QSGraph

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super init];
    if (self) {
        [aDecoder decodeObjectForKey:@"itemArray"];
        [aDecoder decodeIntegerForKey:@"totalMinesNumber"];
        [aDecoder decodeIntegerForKey:@"width"];
        [aDecoder decodeIntegerForKey:@"heigth"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.itemArray forKey:@"itemArray"];
    [aCoder encodeInteger:self.totalMinesNumber forKey:@"totalMinesNumber"];
    [aCoder encodeInteger:self.width forKey:@"width"];
    [aCoder encodeInteger:self.heigth forKey:@"heigth"];
}

//重写width,height方法,最小为3,小于则返回-1
- (void)setWidth:(NSInteger)width
{
    if (width >= 3) {
        _width = width;
    }else
    {
        _width = -1;
    }
}

- (void)setHeigth:(NSInteger)heigth
{
    if (heigth >= 3) {
        _heigth = heigth;
    }else
    {
        _heigth = -1;
    }
}

@end

定义一个获取周边8个点的工具类

#import <Foundation/Foundation.h>
#import "QSGraph.h"
#import "QSItem.h"

@interface QSAroundMines : NSObject
/**根据数组判断周边 对象*/
+ (NSArray *)getAroundMinesArrayByItem:(QSItem *)item graph:(QSGraph *)graph;
@end
#import "QSAroundMines.h"

@implementation QSAroundMines

+ (NSArray *)getAroundMinesArrayByItem:(QSItem *)item graph:(QSGraph *)graph
{
    //可变数组
    NSMutableArray *mutableArray = [NSMutableArray array];
    //获取x,y坐标
    int x = item.indexInGraph.x;
    int y = item.indexInGraph.y;
    //循环判断周边点,跳过边界和自身
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            //过滤自身和越界
            if(
               (i == 0 && j == 0)                    //跳过本身
               || (x == 0 && i == -1)                //跳过-1行
               || (y == 0 && j == -1)                //跳过-1列
               || ((y == graph.width - 1) && j == 1) //跳过+1列
               || ((x == graph.heigth - 1) && i == 1)//跳过+1行
               ) continue;
            //没有越界则将周边item加入到数组中
            NSInteger index = (x + i) * graph.width + y + j;
            QSItem *aroundItem = graph.itemArray[index];
            [mutableArray addObject:aroundItem];
        }
    }
    return mutableArray;
}

@end

定义一个队列类

#import <Foundation/Foundation.h>
#import "QSItem.h"

@interface QSQueue : NSObject
/**队列长度*/
@property(nonatomic,assign)NSUInteger count;
/// 单例生成一个queue对象
+ (QSQueue *) sharedQueue;
/**插入队尾元素*/
- (void)insertValueAtTailOfQueue:(QSItem*)item;
/**弹出队头元素*/
- (QSItem *)getValueOfHeaderOfQueue;
@end
#import "QSQueue.h"

@interface QSQueue ()
//队列内置数组
@property(nonatomic,strong) NSMutableArray *array;
@end

@implementation QSQueue

+ (QSQueue *)sharedQueue
{
    static QSQueue *queue = nil;
    if (queue == nil) {
        queue = [[QSQueue alloc] init];
    }
    return queue;
}

//懒加载数组
- (NSMutableArray *)array
{
    if (!_array) {
        _array = [NSMutableArray array];
    }
    return _array;
}
/// 队列长度
-(NSUInteger)count
{
    return self.array.count;
}
/// 插入一个元素
- (void)insertValueAtTailOfQueue:(QSItem *)item
{
    [self.array addObject:item];
    //判断插入的元素是否在队列中已存在,如果存在则删除,不存在则插入
    for (QSItem *itemInArray in self.array) {
        if (itemInArray != self.array.lastObject) {
            if (item == itemInArray) {
                [self.array removeObject:item];
            }
        }
    }
}
/// 弹出一个元素
- (QSItem *)getValueOfHeaderOfQueue
{
    QSItem *item = self.array.firstObject;
        [self.array removeObjectAtIndex:0];
    return item;
}

@end

BFS核心算法

+ (void)bfsFunction:(QSGraph *)graph value:(QSItem *)item
{
    //初始化一个队列
    QSQueue *queue = [QSQueue sharedQueue];
    //第一步:将结点加入到队列中
    [queue insertValueAtTailOfQueue:item];
    while (queue.count != 0) {
        //从队列头弹出一个元素
        QSItem *itemInQueue = [queue getValueOfHeaderOfQueue];
        //获取弹出元素周边结点数组(无自身,不越界)
        NSArray *array = [QSAroundMines getAroundMinesArrayByItem:itemInQueue graph:graph];
        for (QSItem *itemInArray in array) {
            //遍历过 或 雷 或 打开了 则跳过
            if (itemInArray.isTraveled == YES || itemInArray.isMine == YES || itemInArray.isOpened == YES){
                continue;
            }
            //周边雷数为0则加入队列
            if (itemInArray.arroundMineNumber == 0) {
                itemInArray.opened = YES;
                itemInArray.traveled = YES;
                [queue insertValueAtTailOfQueue:itemInArray];
            }
            //周边雷数不为0,不加入队列,但设置为遍历过,并打开
            else{
                itemInArray.opened = YES;
                itemInArray.traveled = YES;
            }
        }//for
    }//while
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容