iOS简易蓝牙对战五子棋游戏设计思路之二——核心棋盘逻辑与胜负判定算法
一、引言
上一篇博客我们介绍了在开发一款蓝牙对战五子棋游戏中核心的蓝牙通讯框架的设计与编写,本篇博客将来完成独立的棋盘逻辑与胜负判定算法。上篇博客地址如下:
五子棋游戏中和核心通讯类设计:http://my.oschina.net/u/2340880/blog/644432。
二、棋盘中独立棋格的设计
我们知道,五子棋游戏的棋盘是由横纵交叉的两组平行线组成,每一个横纵线的交点即是棋盘上可以落子的点。因此,在设计棋盘前,我们可以先来设计创建棋盘上每一个独立的落子点,这里称之为棋格,在iOS中,可以使用UIButton类来进行棋格的设计。
创建一个类,命名为TipButton作为棋格类,实现其头文件如下:
TipButton.h
1 2 3 4 5 6 7 8 9 10 11
| #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文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
| #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #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。
专注技术,热爱生活,交流技术,也做朋友。
——珲少 QQ群:203317592