CheckerboardView.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. //
  2. // CheckerboardView.m
  3. // Gomoku
  4. //
  5. // Created by Sekorm on 16/7/25.
  6. // Copyright © 2016年 HY. All rights reserved.
  7. //
  8. #import "CheckerboardView.h"
  9. #import "UIView+Frame.h"
  10. #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
  11. #define SCREEN_WIDTH_RATIO (SCREEN_WIDTH / 320) //屏宽比例
  12. static const NSInteger kGridCount = 15; //多少个格子
  13. static const CGFloat kChessmanSizeRatio = 0.8; //棋子宽高占格子宽高的百分比,大于0,小于1
  14. static const CGFloat kBoardSpace = 20;
  15. typedef enum : NSUInteger {
  16. GmkHorizontal,
  17. GmkVertical,
  18. GmkObliqueDown, //斜向下
  19. GmkObliqueUp //斜向上
  20. } GmkDirection;
  21. @interface CheckerboardView ()
  22. @property (nonatomic,assign) CGFloat gridWidth;
  23. @property (nonatomic,assign) CGFloat isBlack;
  24. @property (nonatomic,strong) NSMutableArray * sameChessmanArray; //一条线上的同颜色的棋子个数
  25. @property (nonatomic,strong) NSMutableDictionary * chessmanDict; //存放棋子字典的字典
  26. @property (nonatomic,strong) NSString * lastKey; //上一个棋子字典的key值
  27. @property (nonatomic,assign) BOOL isHighLevel; //是否是高级棋盘
  28. @property (nonatomic,assign) NSInteger gridCount;
  29. @property (nonatomic,assign) BOOL isOver; //游戏是否结束
  30. @end
  31. @implementation CheckerboardView
  32. - (instancetype)initWithFrame:(CGRect)frame
  33. {
  34. self = [super initWithFrame:frame];
  35. if (self) {
  36. [self setFrame:frame];
  37. }
  38. return self;
  39. }
  40. - (void)setFrame:(CGRect)frame{
  41. CGSize size = frame.size;
  42. [super setFrame:CGRectMake(frame.origin.x, frame.origin.y, MIN(size.width, size.height), MIN(size.width, size.height))];
  43. }
  44. - (void)layoutSubviews{
  45. [super layoutSubviews];
  46. static dispatch_once_t onceToken;
  47. dispatch_once(&onceToken, ^{
  48. //初始化设置
  49. [self setUp];
  50. });
  51. }
  52. - (void)setUp{
  53. self.backgroundColor = [UIColor colorWithRed:200/255.0 green:160/255.0 blue:130/255.0 alpha:1];
  54. [self drawBackground:self.size];
  55. [self addGestureRecognizer:[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapBoard:)]];
  56. }
  57. #pragma mark - 绘制棋盘
  58. - (void)drawBackground:(CGSize)size{
  59. self.gridWidth = (size.width - 2 * kBoardSpace) / self.gridCount;
  60. //1.开启图像上下文
  61. UIGraphicsBeginImageContext(size);
  62. //2.获取上下文
  63. CGContextRef ctx = UIGraphicsGetCurrentContext();
  64. CGContextSetLineWidth(ctx, 0.8f);
  65. //3.1 画16条竖线
  66. for (int i = 0; i <= self.gridCount; i ++) {
  67. CGContextMoveToPoint(ctx, kBoardSpace + i * self.gridWidth , kBoardSpace);
  68. CGContextAddLineToPoint(ctx, kBoardSpace + i * self.gridWidth , kBoardSpace + self.gridCount * self.gridWidth);
  69. }
  70. //3.1 画16条横线
  71. for (int i = 0; i <= self.gridCount; i ++) {
  72. CGContextMoveToPoint(ctx, kBoardSpace, kBoardSpace + i * self.gridWidth );
  73. CGContextAddLineToPoint(ctx, kBoardSpace + self.gridCount * self.gridWidth , kBoardSpace + i * self.gridWidth);
  74. }
  75. CGContextStrokePath(ctx);
  76. //4.获取生成的图片
  77. UIImage *image=UIGraphicsGetImageFromCurrentImageContext();
  78. //5.显示生成的图片到imageview
  79. UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
  80. [self addSubview:imageView];
  81. UIGraphicsEndImageContext();
  82. }
  83. //点击棋盘,下棋
  84. - (void)tapBoard:(UITapGestureRecognizer *)tap{
  85. CGPoint point = [tap locationInView:tap.view];
  86. //计算下子的列号行号
  87. NSInteger col = (point.x - kBoardSpace + 0.5 * self.gridWidth) / self.gridWidth;
  88. NSInteger row = (point.y - kBoardSpace + 0.5 * self.gridWidth) / self.gridWidth;
  89. NSString * key = [NSString stringWithFormat:@"%ld-%ld",col,row];
  90. if (![self.chessmanDict.allKeys containsObject:key]) {
  91. UIView * chessman = [self chessman];
  92. chessman.center = CGPointMake(kBoardSpace + col * self.gridWidth, kBoardSpace + row * self.gridWidth);
  93. [self addSubview:chessman];
  94. [self.chessmanDict setValue:chessman forKey:key];
  95. self.lastKey = key;
  96. //检查游戏结果
  97. [self checkResult:col andRow:row andColor:self.isBlack];
  98. self.isBlack = !self.isBlack;
  99. }
  100. }
  101. #pragma mark - 私有方法
  102. //检查此时游戏结果
  103. - (void)checkResult:(NSInteger)col andRow:(NSInteger)row andColor:(BOOL)isBlack{
  104. [self checkResult:col andRow:row andColor:isBlack andDirection:GmkHorizontal];
  105. [self checkResult:col andRow:row andColor:isBlack andDirection:GmkVertical];
  106. [self checkResult:col andRow:row andColor:isBlack andDirection:GmkObliqueDown];
  107. [self checkResult:col andRow:row andColor:isBlack andDirection:GmkObliqueUp];
  108. }
  109. //判断是否大于等于五个同色相连
  110. - (BOOL)checkResult:(NSInteger)col andRow:(NSInteger)row andColor:(BOOL)isBlack andDirection:(GmkDirection)direction{
  111. if (self.sameChessmanArray.count >= 5) {
  112. return YES;
  113. }
  114. UIButton *currentChessman = self.chessmanDict[[NSString stringWithFormat:@"%ld-%ld",col,row]];
  115. UIColor * currentChessmanColor = currentChessman.backgroundColor;
  116. [self.sameChessmanArray addObject:self.chessmanDict[self.lastKey]];
  117. switch (direction) {
  118. //水平方向检查结果
  119. case GmkHorizontal:{
  120. //向前遍历
  121. for (NSInteger i = col - 1; i > 0; i --) {
  122. NSString * key = [NSString stringWithFormat:@"%ld-%ld",i,row];
  123. UIView * chessman = self.chessmanDict[key];
  124. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor) break;
  125. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  126. }
  127. //向后遍历
  128. for (NSInteger i = col + 1; i < kGridCount; i ++) {
  129. NSString * key = [NSString stringWithFormat:@"%ld-%ld",i,row];
  130. UIView * chessman = self.chessmanDict[key];
  131. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor) break;
  132. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  133. }
  134. if (self.sameChessmanArray.count >= 5) {
  135. [self alertResult];
  136. return YES;
  137. }
  138. [self.sameChessmanArray removeAllObjects];
  139. }
  140. break;
  141. case GmkVertical:{
  142. //向前遍历
  143. for (NSInteger i = row - 1; i > 0; i --) {
  144. NSString * key = [NSString stringWithFormat:@"%ld-%ld",col,i];
  145. UIView * chessman = self.chessmanDict[key];
  146. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor) break;
  147. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  148. }
  149. //向后遍历
  150. for (NSInteger i = row + 1; i < kGridCount; i ++) {
  151. NSString * key = [NSString stringWithFormat:@"%ld-%ld",col,i];
  152. UIView * chessman = self.chessmanDict[key];
  153. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor) break;
  154. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  155. }
  156. if (self.sameChessmanArray.count >= 5) {
  157. [self alertResult];
  158. return YES;
  159. }
  160. [self.sameChessmanArray removeAllObjects];
  161. }
  162. break;
  163. case GmkObliqueDown:{
  164. //向前遍历
  165. NSInteger j = col - 1;
  166. for (NSInteger i = row - 1; i >= 0; i--,j--) {
  167. NSString * key = [NSString stringWithFormat:@"%ld-%ld",j,i];
  168. UIView * chessman = self.chessmanDict[key];
  169. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor || j < 0) break;
  170. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  171. }
  172. //向后遍历
  173. j = col + 1;
  174. for (NSInteger i = row + 1 ; i < kGridCount; i++,j++) {
  175. NSString * key = [NSString stringWithFormat:@"%ld-%ld",j,i];
  176. UIView * chessman = self.chessmanDict[key];
  177. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor || j > kGridCount) break;
  178. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  179. }
  180. if (self.sameChessmanArray.count >= 5) {
  181. [self alertResult];
  182. return YES;
  183. }
  184. [self.sameChessmanArray removeAllObjects];
  185. }
  186. break;
  187. case GmkObliqueUp:{
  188. //向前遍历
  189. NSInteger j = col + 1;
  190. for (NSInteger i = row - 1; i >= 0; i--,j++) {
  191. NSString * key = [NSString stringWithFormat:@"%ld-%ld",j,i];
  192. UIView * chessman = self.chessmanDict[key];
  193. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor || j > kGridCount) break;
  194. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  195. }
  196. //向后遍历
  197. j = col - 1;
  198. for (NSInteger i = row + 1 ; i < kGridCount; i++,j--) {
  199. NSString * key = [NSString stringWithFormat:@"%ld-%ld",j,i];
  200. UIView * chessman = self.chessmanDict[key];
  201. if (![self.chessmanDict.allKeys containsObject:key] || chessman.backgroundColor != currentChessmanColor || j < 0) break;
  202. [self.sameChessmanArray addObject:self.chessmanDict[key]];
  203. }
  204. if (self.sameChessmanArray.count >= 5) {
  205. [self alertResult];
  206. return YES;
  207. }
  208. [self.sameChessmanArray removeAllObjects];
  209. }
  210. break;
  211. }
  212. return NO;
  213. }
  214. //游戏结果,提示效果
  215. - (void)alertResult{
  216. self.isOver = YES;
  217. NSLog(@"self.sameChessmanArray == %ld",self.sameChessmanArray.count);
  218. for (UIView * view in self.sameChessmanArray) {
  219. NSString * key = [self.chessmanDict allKeysForObject:view].firstObject;
  220. NSLog(@"%@",key);
  221. }
  222. CGFloat width = SCREEN_WIDTH * 0.4 * SCREEN_WIDTH_RATIO;
  223. UIView * tip = [[UIView alloc]initWithFrame:CGRectMake(0, 0, width, 0.6 * width)];
  224. tip.backgroundColor = [UIColor colorWithWhite:1 alpha:0.8];
  225. tip.alpha = 1;
  226. tip.layer.cornerRadius = 8.0f;
  227. [self addSubview:tip];
  228. tip.center = CGPointMake(self.width * 0.5, self.height * 0.5);
  229. UILabel * label = [[UILabel alloc]init];
  230. label.text = self.isBlack?ASLocalizedString(@"白方胜"):ASLocalizedString(@"黑方胜");
  231. [label sizeToFit];
  232. label.center = CGPointMake(tip.width * 0.5, tip.height * 0.5);
  233. [tip addSubview:label];
  234. CAKeyframeAnimation * anim = [CAKeyframeAnimation animation];
  235. anim.values = @[@(1),@(0),@(1)];
  236. anim.keyPath = @"opacity";
  237. anim.duration = 0.8f;
  238. anim.repeatCount = CGFLOAT_MAX;
  239. for (UIView * view in self.sameChessmanArray) {
  240. [view.layer addAnimation:anim forKey:@"alpha"];
  241. }
  242. self.userInteractionEnabled = NO;
  243. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  244. [tip removeFromSuperview];
  245. });
  246. }
  247. #pragma mark - 功能方法
  248. - (void)newGame{
  249. self.isOver = NO;
  250. self.lastKey = nil;
  251. [self.sameChessmanArray removeAllObjects];
  252. self.userInteractionEnabled = YES;
  253. [self.chessmanDict removeAllObjects];
  254. for (UIView * view in self.subviews) {
  255. if ([view isKindOfClass:[UIImageView class]]) {
  256. continue;
  257. }
  258. [view removeFromSuperview];
  259. }
  260. self.isBlack = NO;
  261. }
  262. //撤回至上一步棋
  263. - (void)backOneStep:(UIButton *)sender{
  264. if(self.isOver) return;
  265. if (self.lastKey == nil) {
  266. sender.enabled = NO;
  267. CGFloat width = SCREEN_WIDTH * 0.4 * SCREEN_WIDTH_RATIO;
  268. UIView * tip = [[UIView alloc]initWithFrame:CGRectMake(0, 0, width, 0.6 * width)];
  269. tip.backgroundColor = [UIColor colorWithWhite:1 alpha:0.8];
  270. tip.layer.cornerRadius = 8.0f;
  271. [self addSubview:tip];
  272. tip.center = CGPointMake(self.width * 0.5, self.height * 0.5);
  273. UILabel * label = [[UILabel alloc]init];
  274. label.text = self.chessmanDict.count > 0 ? ASLocalizedString(@"只能悔一步棋!!!"): ASLocalizedString(@"请先落子!!!");
  275. label.font = [UIFont systemFontOfSize:15];
  276. [label sizeToFit];
  277. label.center = CGPointMake(tip.width * 0.5, tip.height * 0.5);
  278. [tip addSubview:label];
  279. self.userInteractionEnabled = NO;
  280. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  281. self.userInteractionEnabled = YES;
  282. sender.enabled = YES;
  283. [tip removeFromSuperview];
  284. });
  285. return;
  286. }
  287. [self.chessmanDict removeObjectForKey:self.lastKey];
  288. [self.subviews.lastObject removeFromSuperview];
  289. self.isBlack = !self.isBlack;
  290. self.lastKey = nil;
  291. }
  292. //改变键盘级别
  293. - (void)changeBoardLevel{
  294. for (UIView * view in self.subviews) {
  295. [view removeFromSuperview];
  296. }
  297. [self newGame];
  298. self.isHighLevel = !self.isHighLevel;
  299. [self drawBackground:self.bounds.size];
  300. }
  301. #pragma mark - getter/setter 方法
  302. - (UIView *)chessman{
  303. UIView * chessmanView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.gridWidth * kChessmanSizeRatio, self.gridWidth * kChessmanSizeRatio)];
  304. chessmanView.layer.cornerRadius = chessmanView.width * 0.5;
  305. chessmanView.backgroundColor = !self.isBlack ? [UIColor blackColor]:[UIColor whiteColor];
  306. return chessmanView;
  307. }
  308. - (NSMutableDictionary *)chessmanDict{
  309. if (!_chessmanDict) {
  310. _chessmanDict = [NSMutableDictionary dictionary];
  311. }
  312. return _chessmanDict;
  313. }
  314. - (NSMutableArray *)sameChessmanArray{
  315. if (!_sameChessmanArray) {
  316. _sameChessmanArray = [NSMutableArray array];
  317. }
  318. return _sameChessmanArray;
  319. }
  320. - (NSInteger)gridCount{
  321. return self.isHighLevel ? kGridCount : (kGridCount - 4);
  322. }
  323. @end