SEARU.ORG
当前位置:SEARU.ORG > Linux 新闻 > 正文

iOS简易蓝牙对战五子棋游戏设计思路之二——核心棋盘逻辑与胜负判定算法

iOS简易蓝牙对战五子棋游戏设计思路之二——核心棋盘逻辑与胜负判定算法

一、引言

    上一篇博客我们介绍了在开发一款蓝牙对战五子棋游戏中核心的蓝牙通讯框架的设计与编写,本篇博客将来完成独立的棋盘逻辑与胜负判定算法。上篇博客地址如下:

五子棋游戏中和核心通讯类设计:http://my.oschina.net/u/2340880/blog/644432

二、棋盘中独立棋格的设计

    我们知道,五子棋游戏的棋盘是由横纵交叉的两组平行线组成,每一个横纵线的交点即是棋盘上可以落子的点。因此,在设计棋盘前,我们可以先来设计创建棋盘上每一个独立的落子点,这里称之为棋格,在iOS中,可以使用UIButton类来进行棋格的设计。

    创建一个类,命名为TipButton作为棋格类,实现其头文件如下:

TipButton.h

#import <UIKit/UIKit.h>
@interface TipButton : UIButton
//标记此瓦片是否已经落子 0 空 1 己方落子 2 敌方落子
@property(nonatomic,assign)int hasChess;
//落子 BOOL类型的参数 决定是己方还是敌方
-(void)dropChess:(BOOL)isMine;
//设置白子或者黑子
@property(nonatomic,assign)BOOL isWhite;
//瓦片编号
@property(nonatomic,assign)int index;
@end

实现.m文件如下:

#import "TipButton.h"
@implementation TipButton
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self creatView];
    }
    return self;
}
-(void)creatView{
    //创建横竖两条线
    UIView * line1 = [[UIView alloc]initWithFrame:CGRectMake(self.frame.size.width/2-0.25, 0, 0.5, self.frame.size.height)];
    line1.backgroundColor = [UIColor grayColor];
    [self addSubview:line1];
    
    UIView * line2 = [[UIView alloc]initWithFrame:CGRectMake(0, self.frame.size.height/2-0.25, self.frame.size.width, 0.5)];
    line2.backgroundColor = [UIColor grayColor];
    [self addSubview:line2];
}

-(void)dropChess:(BOOL)isMine{
    UIView * view = [[UIView alloc]initWithFrame:CGRectMake(self.frame.size.width/2-5, self.frame.size.height/2-5, 10, 10)];
    view.layer.masksToBounds = YES;
    view.layer.cornerRadius = 5;
    UIColor * myColor;
    UIColor * otherColor;
    if (_isWhite) {
        myColor = [UIColor whiteColor];
        otherColor = [UIColor blackColor];
    }else{
        myColor = [UIColor blackColor];
        otherColor = [UIColor whiteColor];
    }
    if (isMine) {
        view.backgroundColor = myColor;
        self.hasChess = 1;
    }else{
        view.backgroundColor = otherColor;
        self.hasChess = 2;
    }
    [self addSubview:view];
   
}
@end

三、游戏棋盘的设计

    创建一个继承于UIView的类,作为五子棋游戏的棋盘,命名为GameView实现如下:

GameView.h

#import <UIKit/UIKit.h>
#import "TipButton.h"
#import "BlueToothTool.h"
//用于处理用户下子后的逻辑
@protocol GameViewDelegate<NSObject>
-(void)gameViewClick:(NSString *)index;
@end
@interface GameView : UIView<UIAlertViewDelegate>
//存放所有棋格
@property(nonatomic,strong)NSMutableArray<TipButton *> * tipArray;
@property(nonatomic,weak)id<GameViewDelegate>delegate;
//进行下子
-(void)setTipIndex:(int)index;
@end

GameView.m

#import "GameView.h"
@implementation GameView
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _tipArray = [[NSMutableArray alloc]init];
        [self creatView];
    }
    return self;
}
//创建表格视图 横16 竖20
-(void)creatView{
    self.layer.borderColor = [UIColor colorWithRed:222/255.0 green:222/255.0 blue:222/255.0 alpha:1].CGColor;
    self.layer.borderWidth = 0.5;
    CGFloat width = self.frame.size.width/12;
    CGFloat height = self.frame.size.height/20;
    //排列布局
    for (int i=0; i<240; i++) {
        TipButton * btn = [[TipButton alloc]initWithFrame:CGRectMake(width*(i%12), height*(i/12), width, height)];
        [btn addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
        btn.isWhite = NO;
        btn.index=i;
        [self addSubview:btn];
        [_tipArray addObject:btn];
    }
}
-(void)click:(TipButton *)btn{
    if (btn.hasChess==0) {
        //下子
        [btn dropChess:YES];
        //进行胜负判定
        [self cheak];
        [self.delegate gameViewClick:[NSString stringWithFormat:@"%d",btn.index]];
    }
}
//进行胜负判定
-(void)cheak{
    //判定己方是否胜利
    if ([self cheakMine]) {
        UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"您胜利啦" message:@"" delegate:self cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
        [alert show];
    }
    //判断对方是否胜利
    if ([self cheakOther]) {
        UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"您失败了" message:@"" delegate:self cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
        [alert show];
    }
}
-(void)setTipIndex:(int)index{
    //下子
    for (TipButton * btn in _tipArray) {
        if (btn.index==index) {
            [btn dropChess:NO];
            [self cheak];
        }
    }
}
-(BOOL)cheakOther{
    //遍历所有棋子
    for (int i=0; i<_tipArray.count; i++) {
        TipButton * tip = _tipArray[i];
        //获取是否是己方棋子
        if (tip.hasChess==2) {
            //进行五子判定逻辑
            //横向
            if ( [self cheak1HasMineOrOther:NO index:i]) {
                return YES;
            }
            //左上到右下的对角线
            if ( [self cheak2HasMineOrOther:NO index:i]) {
                return YES;
            }
            //纵向
            if ( [self cheak3HasMineOrOther:NO index:i]) {
                return YES;
            }
            //右上到左下的对角线
            if ( [self cheak4HasMineOrOther:NO index:i]) {
                return YES;
            }
        }
    }
    return NO;

}

-(BOOL)cheakMine{
    //遍历所有棋子
    for (int i=0; i<_tipArray.count; i++) {
        TipButton * tip = _tipArray[i];
        //获取是否是己方棋子
        if (tip.hasChess==1) {
            //进行五子判定逻辑
            //横向
            if ( [self cheak1HasMineOrOther:YES index:i]) {
                return YES;
            }
            //左上到右下的对角线
            if ( [self cheak2HasMineOrOther:YES index:i]) {
                return YES;
            }
            //纵向
            if ( [self cheak3HasMineOrOther:YES index:i]) {
                return YES;
            }
            //右上到左下的对角线
            if ( [self cheak4HasMineOrOther:YES index:i]) {
                return YES;
            }
        }
    }
    return NO;
}


-(BOOL)cheak1HasMineOrOther:(BOOL)mine index:(int)index{
    int mineOrOther = 0;
    if (mine) {
        mineOrOther = 1;
    }else{
        mineOrOther = 2;
    }
    int count=1;
    //左侧右侧同时进行可以增加效率
    //左侧
    count = count +[self algorithmic1:index param:mineOrOther num:4];
    //右侧
    count = count +[self algorithmic2:index param:mineOrOther num:4];
    if (count>=5) {
        return YES;
    }else{
        return NO;
    }
}

-(BOOL)cheak2HasMineOrOther:(BOOL)mine index:(int)index{
    int mineOrOther = 0;
    if (mine) {
        mineOrOther = 1;
    }else{
        mineOrOther = 2;
    }
    int count=1;
    //左上右下同时进行可以增加效率
    //左上
    count = count +[self algorithmic3:index param:mineOrOther num:4];
    //右下
    count = count +[self algorithmic4:index param:mineOrOther num:4];
    if (count>=5) {
        return YES;
    }else{
        return NO;
    }
}

-(BOOL)cheak3HasMineOrOther:(BOOL)mine index:(int)index{
    int mineOrOther = 0;
    if (mine) {
        mineOrOther = 1;
    }else{
        mineOrOther = 2;
    }
    int count=1;
    //纵向
    //向上
    count = count +[self algorithmic5:index param:mineOrOther num:4];
    //向下
    count = count +[self algorithmic6:index param:mineOrOther num:4];
    if (count>=5) {
        return YES;
    }else{
        return NO;
    }
}
-(BOOL)cheak4HasMineOrOther:(BOOL)mine index:(int)index{
    int mineOrOther = 0;
    if (mine) {
        mineOrOther = 1;
    }else{
        mineOrOther = 2;
    }
    int count=1;
    //纵向
    //向上
    count = count +[self algorithmic7:index param:mineOrOther num:4];
    //向下
    count = count +[self algorithmic8:index param:mineOrOther num:4];
    
    NSLog(@"%d",count);
    if (count>=5) {
        return YES;
    }else{
        return NO;
    }
}

/*
 左侧递归进行查找 index 棋子编号 param 对比值 num 递归次数
 */
-(int)algorithmic1:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
            //左侧有子
        if (index-tem>=0) {
            //左侧无换行
            if(((index-tem)%12)!=11){
                if (_tipArray[index-tem].hasChess==param) {
                   return  [self algorithmic1:index param:param num:num-1];
                }else{
                    return 4-num;
                }
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}
/*
 右侧递归进行查找 index 棋子编号 param 对比值 num 递归次数
 */
-(int)algorithmic2:(int)index param:(int)param num:(int)num{
    
    if (num>0) {
        int tem = 4-(num-1);
        //右侧有子
        if (index+tem<240) {
            //右侧无换行
            if(((index+tem)%12)!=11){
                if (_tipArray[index+tem].hasChess==param) {
                    return  [self algorithmic2:index param:param num:num-1];
                }else{
                    return 4-num;
                }
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}

/*
 左上递归进行查找 index 棋子编号 param 对比值 num 递归次数
 */
-(int)algorithmic3:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
        //左上有子
        if ((index-(tem*12)-tem)>=0) {
            //右侧无换行
            if(((index-(tem*12)-tem)%12)!=11){
                if (_tipArray[(index-(tem*12)-tem)].hasChess==param) {
                    return  [self algorithmic3:index param:param num:num-1];
                }else{
                    return 4-num;
                }
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}

-(int)algorithmic4:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
        //左上有子
        if ((index+(tem*12)+tem)<240) {
            //右侧无换行
            if(((index+(tem*12)+tem)%12)!=0){
                if (_tipArray[(index+(tem*12)+tem)].hasChess==param) {
                    return  [self algorithmic4:index param:param num:num-1];
                }else{
                    return 4-num;
                }
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}

-(int)algorithmic5:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
        //上有子
        if ((index-(tem*12))>=0) {
            if (_tipArray[(index-(tem*12))].hasChess==param) {
                return  [self algorithmic5:index param:param num:num-1];
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}

-(int)algorithmic6:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
        //上有子
        if ((index+(tem*12))<240) {
            if (_tipArray[(index+(tem*12))].hasChess==param) {
                return  [self algorithmic6:index param:param num:num-1];
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}
-(int)algorithmic7:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
        //左上有子
        if ((index-(tem*12)+tem)>=0) {
            //右侧无换行
            if(((index-(tem*12)+tem)%12)!=0){
                if (_tipArray[(index-(tem*12)+tem)].hasChess==param) {
                    return  [self algorithmic7:index param:param num:num-1];
                }else{
                    return 4-num;
                }
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}

-(int)algorithmic8:(int)index param:(int)param num:(int)num{
    if (num>0) {
        int tem = 4-(num-1);
        //左上有子
        if ((index+(tem*12)-tem)<240) {
            //右侧无换行
            if(((index+(tem*12)-tem)%12)!=11){
                if (_tipArray[(index+(tem*12)-tem)].hasChess==param) {
                    return  [self algorithmic8:index param:param num:num-1];
                }else{
                    return 4-num;
                }
            }else{
                return 4-num;
            }
        }else{
            return 4-num;
        }
    }else{
        //递归了四次
        return 4-num;
    }
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [[BlueToothTool sharedManager]disConnect];
    [(UIViewController *)[self.superview nextResponder] dismissViewControllerAnimated:YES completion:nil];
}
@end

关于胜负判定的算法逻辑,这里采用了向各个方向进行递归查找的方式,这里有一点需要主要,在4个方向进行递归查找时,理论上每个方向只需要单面递归即可,但是代码中采用了双面递归在进行累加的方式,这样的设计可以遍历更少的棋子判定出胜负情况。

四、整合通讯与游戏逻辑

   创建一个继承于UIViewController的类作为游戏视图控制器,实现如下:

GameViewController.m

#import "GameViewController.h"
#import "GameView.h"
#import "BlueToothTool.h"
@interface GameViewController ()<BlueToothToolDelegate,GameViewDelegate>
{
    UIView * _bgView;
    UILabel * _tipLabel;
    GameView * _view;
}
@end

@implementation GameViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor brownColor];
    //创建游戏视图
    _view = [[GameView alloc]initWithFrame:CGRectMake(20, 40, (self.view.frame.size.width-40), (self.view.frame.size.width-40)/12*20)];
    _view.delegate=self;
    [self.view addSubview:_view];
    //创建背景视图
    _bgView = [[UIView alloc]initWithFrame:self.view.frame];
    _bgView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.1];
    UIButton * btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(self.view.frame.size.width/2-50, 150, 100, 30);
    UIButton * btn2 = [UIButton buttonWithType:UIButtonTypeSystem];
    btn2.frame = CGRectMake(self.view.frame.size.width/2-50, 250, 100, 30);
    [btn setTitle:@"创建游戏" forState:UIControlStateNormal];
    [btn2 setTitle:@"扫描附近游戏" forState:UIControlStateNormal];
    btn.backgroundColor = [UIColor orangeColor];
    btn2.backgroundColor = [UIColor orangeColor];
    [btn addTarget:self action:@selector(creatGame) forControlEvents:UIControlEventTouchUpInside];
    [btn2 addTarget:self action:@selector(searchGame) forControlEvents:UIControlEventTouchUpInside];
    [_bgView addSubview:btn];
    [_bgView addSubview:btn2];
    
    [self.view addSubview:_bgView];
    //设置蓝牙通讯类代理
    [BlueToothTool sharedManager].delegate=self;
    //创建提示标签
    _tipLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width,40)];
    [self.view addSubview:_tipLabel];
    _tipLabel.textAlignment = NSTextAlignmentCenter;
}
-(void)creatGame{
    [[BlueToothTool sharedManager]setUpGame:@"" block:^(BOOL first) {
        [_bgView removeFromSuperview];
        if (first) {
            _tipLabel.text = @"请您下子";
            //进行发送下子信息
        }else{
            _tipLabel.text = @"请等待对方下子";
            self.view.userInteractionEnabled = NO;
            [self gameViewClick:@"-1"];
        }
    }];
}
-(void)searchGame{
    [[BlueToothTool sharedManager]searchGame];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
-(void)getData:(NSString *)data{
    if (_bgView.superview) {
        [_bgView removeFromSuperview];
    }
    if ([data integerValue]==-1) {
        _tipLabel.text = @"请您下子";
         self.view.userInteractionEnabled = YES;
        return;
    }
    _tipLabel.text = @"请您下子";
    [_view setTipIndex:[data intValue]];
    self.view.userInteractionEnabled = YES;
}

-(void)gameViewClick:(NSString *)index{
    _tipLabel.text = @"请等待对方下子";
    [[BlueToothTool sharedManager]writeData:index];
    self.view.userInteractionEnabled = NO;
}
@end

游戏运行的主要界面如下图所示:

附录:游戏的源码已经放在git上,时间比较仓促,只用了一下午来写,其中还有许多细节与bug没有进行调整,有需要的可以作为参考:

git地址:https://github.com/ZYHshao/BlueGame


未经允许不得转载:SEARU.ORG » iOS简易蓝牙对战五子棋游戏设计思路之二——核心棋盘逻辑与胜负判定算法

赞 (0)
分享到:更多 ()

评论 0