C语言实现三子棋(会堵棋,加强版)智能AI博弈
三子棋介绍:
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。
功能实现:
1:打印菜单
2:棋盘初始化
3:打印棋盘
4:玩家下棋
5:电脑下棋(优化)
6:判断输赢
1:打印菜单
1. void meue() { 2. printf("**************************\n"); 3. printf("**************************\n"); 4. printf("****1.Play******0.Exit****\n"); 5. printf("**************************\n"); 6. printf("**************************\n"); 7. }
2:棋盘初始化
首先,三子棋是一个3*3的棋盘,所以我们可以用一个二维数组来表示这个棋盘,当没有下棋的时候棋盘为空,而怎么用二维数组表示呢?
我们可以将一个char类型的二维数组全部初始化为空格,这样就能表示空棋盘了
3:打印棋盘
首先对棋盘进行分析:
一共5行,第一行由空格(' ')和 | 组成,但 | 只有两个,因此需要进行判断
第二行全部由---组成,却只有两行,所以也需要进行判断
这是一个3*3的棋盘,row=3,col=3。条件则为i<row-1的时候打印 | ,j<col-1的时候打印---
1. void print_board(char board[ROW][COL], int row, int col) { 2. int i = 0; 3. for (i = 0; i < row; i++) 4. { 5. int j = 0; 6. for (j = 0; j < col; j++) 7. { 8. printf(" %c ", board[i][j]); 9. if (j < col - 1) 10. { 11. printf("|"); 12. } 13. } 14. printf("\n"); 15. if (i < row - 1) 16. { 17. for (j = 0; j < col; j++) 18. { 19. printf("---"); 20. if (j < col - 1) 21. { 22. printf("|"); 23. } 24. } 25. printf("\n"); 26. } 27. } 28. }
4:玩家下棋
玩家下棋的时候X和Y的坐标范围都是(1,3),但数组是从0开始算的,所以我们需要将X和Y减1才能作为数组中的下标使用,在下棋子的时候需要进行判断,这个坐标是否合法,是否没有被占用
1. void player_move(char board[ROW][COL], int row, int col) { 2. printf("玩家下棋\n"); 3. while (1) { 4. printf("请输入要下的坐标:>"); 5. int x = 0, y = 0; 6. scanf("%d %d", &x, &y); 7. if (x >= 1 && x <= row && y >= y && y <= col) { 8. if (board[x - 1][y - 1] == ' ') { 9. board[x - 1][y - 1] = '*'; 10. break; 11. } 12. else { 13. printf("坐标已经被占用,请重新输入\n"); 14. } 15. } 16. else { 17. printf("坐标不合法,请重新输入\n"); 18. } 19. } 20. }
5:电脑下棋
首先展示无脑版本,堵棋全看运气->利用随机数种子来下棋,随机生成的数组%3得到的范围就是(0,2),刚好是数组的下标范围
1. void cmp_move(char board[ROW][COL], int row, int col) { 2. printf("电脑下棋:\n"); 3. while (1) { 4. int x = rand() % row; 5. int y = rand() % col; 6. if (board[x][y] == ' ') { 7. board[x][y] = '#'; 8. break; 9. } 10. } 11. }
接下来是会堵棋的版本:
思路很简单,直接遍历整个数组,行 列 对角线是否有满足凑齐了两个还空一个的位置,是的话就堵,这里可以使用一个计数器来记录该方向是否存在两个相同的棋子,并且是否有空的位置,我们新定义两个变量X和Y来记录空格的位置,将X和Y初始化为-1,这样就可以判断出是否真的找到了空位,而不需要用flag来判断
而什么时候进攻,什么时候防守呢?那当然是能进攻就进攻,不能进攻则防守咯。
还有就是能抢(2,2)的位置一定要抢,抢不到则在四个角落随便找个位置,这里我选择左上角的位置
1. int intelligence_play(char board[ROW][COL], int row, int col) { 2. //先抢占(2,2)的位置,若已经被占用,则下四个角落之一,以左上角为例(1,1)->对应的数组坐标为(0,0); 3. if (board[1][1] == ' ') { 4. board[1][1] = '#'; 5. return 1; 6. } 7. else if(board[0][0]==' ') { 8. board[0][0] = '#'; 9. return 1; 10. } 11. //抢占位置后判断是攻还是防 12. //cnt用于记录玩家棋子个数,cnt1为电脑,x,y为空格的坐标 13. int cnt = 0, cnt1 = 0, x = -1, y = -1, x1 = -1, y1 = -1, flag = 1; 14. //先判断自己是否满足胜利条件 15. //判断行 16. for (int i = 0; i < row; i++) { 17. for (int j = 0; j < col; j++) { 18. if (board[i][j] == '#') { 19. cnt++; 20. } 21. if (board[i][j] == ' ') { 22. //记录空的位置 23. x = i, y = j; 24. } 25. if (cnt == 2&&x!=-1&&y!=-1) { 26. //开始进攻,连成三个棋,计数清零 27. cnt = 0; 28. board[x][y] = '#'; 29. x = -1, y = -1; 30. return 1; 31. } 32. } 33. //每一行检查结束后需要计数清零 34. cnt = 0; 35. x = -1, y = -1; 36. } 37. //是否需要进行堵棋 38. for (int i = 0; i < row; i++) { 39. for (int j = 0; j < col; j++) { 40. if (board[i][j] == '*') { 41. cnt1++; 42. } 43. if (board[i][j] == ' ') { 44. x = i, y = j; 45. } 46. if (cnt1 == 2 && x != -1 && y != -1) { 47. cnt = 0; 48. board[x][y] = '#'; 49. x = -1, y = -1; 50. return 1; 51. } 52. } 53. cnt1 = 0; 54. x = -1, y = -1; 55. } 56. //判断列 57. for (int j = 0; j < col; j++) { 58. for (int i = 0; i < row; i++) { 59. if (board[i][j] == '#') { 60. cnt++; 61. } 62. if (board[i][j] == ' ') { 63. x = i, y = j; 64. } 65. if (cnt == 2 && x != -1 && y != -1) { 66. cnt = 0; 67. board[x][y] = '#'; 68. x = -1, y = -1; 69. return 1; 70. } 71. } 72. cnt = 0; 73. x = -1, y = -1; 74. } 75. for (int j = 0; j < col; j++) { 76. for (int i = 0; i < row; i++) { 77. if (board[i][j] == '*') { 78. cnt++; 79. } 80. if (board[i][j] == ' ') { 81. x = i, y = j; 82. } 83. if (cnt == 2 && x != -1 && y != -1) { 84. cnt = 0; 85. board[x][y] = '#'; 86. x = -1, y = -1; 87. return 1; 88. } 89. } 90. cnt = 0; 91. x = -1, y = -1; 92. } 93. //判断逆对角线 94. for (int i = 0; i < row; i++) { 95. if (board[i][row - i - 1] == '#') { 96. cnt++; 97. } 98. if (board[i][row - i - 1] == ' ') { 99. x = i, y = row - i - 1; 100. } 101. if (cnt == 2 && x != -1 && y != -1) { 102. cnt = 0; 103. board[x][y] = '#'; 104. x = -1, y = -1; 105. return 1; 106. } 107. } 108. cnt = 0, x = -1, y = -1; 109. for (int i = 0; i < row; i++) { 110. if (board[i][row - i - 1] == '*') { 111. cnt++; 112. } 113. if (board[i][row - i - 1] == ' ') { 114. x = i, y = row - i - 1; 115. } 116. if (cnt == 2 && x != -1 && y != -1) { 117. cnt = 0; 118. board[x][y] = '#'; 119. x = -1, y = -1; 120. return 1; 121. } 122. } 123. cnt = 0, x = -1, y = -1; 124. //判断逆对角线 125. for (int i = 0; i < row; i++) { 126. if (board[i][i] == '#') { 127. cnt++; 128. } 129. if (board[i][i] == ' ') { 130. x = i, y = row - i - 1; 131. } 132. if (cnt == 2 && x != -1 && y != -1) { 133. cnt = 0; 134. board[x][y] = '#'; 135. x = -1, y = -1; 136. return 1; 137. } 138. } 139. cnt = 0, x = -1, y = -1; 140. for (int i = 0; i < row; i++) { 141. if (board[i][i] == '*') { 142. cnt++; 143. } 144. if (board[i][i] == ' ') { 145. x = i, y = row - i - 1; 146. } 147. if (cnt == 2 && x != -1 && y != -1) { 148. cnt = 0; 149. board[x][y] = '#'; 150. x = -1, y = -1; 151. return 1; 152. } 153. } 154. cnt = 0, x = -1, y = -1; 155. //如果上述条件都不满足,则随机下棋 156. cmp_move(board, row, col); 157. }
5:输赢的判断
先判断棋盘是否已经满了,这个很简单,直接遍历整个数组,如果还能找到有空格的位置就还没有满,找不到了就满了
1. int isfull(char board[ROW][COL], int row, int col) { 2. for (int i = 0; i < row; i++) { 3. for (int j = 0; j < col; j++) { 4. if (board[i][j] == ' ') { 5. return 0; 6. } 7. } 8. } 9. return 1; 10. }
接下来是判断输赢:
#为电脑赢,*为玩家赢了,Q为平局,C为继续游戏
用两个计数器来记录玩家和电脑棋子在该方向上的棋子数量,当等于3的时候就胜利
1. char iswin(char board[ROW][COL], int row, int col) { 2. int num0f0 = 0;//玩家 3. int num0fX = 0;//电脑 4. //判断行 5. for (int i = 0; i < row; i++) { 6. num0f0 = num0fX = 0; 7. for (int j = 0; j < col; j++) { 8. if (board[i][j] == '*') { 9. num0f0++; 10. } 11. else if (board[i][j] == '#') { 12. num0fX++; 13. } 14. } 15. if (num0f0 == ROW) { 16. return '*'; 17. } 18. else if (num0fX == COL) { 19. return '#'; 20. } 21. } 22. //判断列 23. for (int j = 0; j < col; j++) { 24. num0f0 = num0fX = 0; 25. for (int i = 0; i < row; i++) { 26. if (board[i][j] == '*') { 27. num0f0++; 28. } 29. else if (board[i][j] == '#') { 30. num0fX++; 31. } 32. } 33. if (num0f0 == row) { 34. return '*'; 35. } 36. else if (num0fX == col) { 37. return '#'; 38. } 39. } 40. //判断正对角线 41. num0f0 = num0fX = 0; 42. for (int i = 0; i < row; i++) { 43. if (board[i][i] == '*') { 44. num0f0++; 45. } 46. else if (board[i][i] == '#') { 47. num0fX++; 48. } 49. } 50. if (num0f0 == row) { 51. return '*'; 52. } 53. else if (num0fX == col) { 54. return '#'; 55. } 56. //检查逆对角线 57. num0f0 = num0fX = 0; 58. for (int i = 0; i < row; i++) { 59. if (board[i][row - i - 1] == '*') { 60. num0f0++; 61. } 62. else if (board[i][row - i - 1] == '#') { 63. num0fX++; 64. } 65. } 66. if (num0f0 == row) { 67. return '*'; 68. } 69. else if (num0fX == col) { 70. return '#'; 71. } 72. //判断是否平均,是否需要继续 73. int ret = isfull(board, ROW, COL); 74. if (ret == 1) { 75. return 'Q'; 76. } 77. else { 78. return 'C';//继续游戏 79. } 80. }
代码总和:
game.h文件
1. #pragma once 2. #define COL 3 3. #define ROW 3 4. //函数申明 5. void init_board(char board[ROW][COL],int row, int col); 6. void print_board(char board[ROW][COL], int row, int col); 7. int isfull(char board[ROW][COL], int row, int col); 8. char iswin(char board[ROW][COL], int row, int col);
game.c文件
1. #include"game.h" 2. #include<stdlib.h> 3. #include<time.h> 4. 5. //初始化棋盘为空格 6. void init_board(char board[ROW][COL], int row, int col) { 7. for (int i = 0; i < row;i++) 8. { 9. for (int j = 0; j < col;j++) 10. { 11. board[i][j] = ' '; 12. } 13. } 14. } 15. //打印棋盘 16. void print_board(char board[ROW][COL], int row, int col) { 17. int i = 0; 18. for (i = 0; i < row; i++) 19. { 20. int j = 0; 21. for (j = 0; j < col; j++) 22. { 23. printf(" %c ", board[i][j]); 24. if (j < col - 1) 25. { 26. printf("|"); 27. } 28. } 29. printf("\n"); 30. if (i < row - 1) 31. { 32. for (j = 0; j < col; j++) 33. { 34. printf("---"); 35. if (j < col - 1) 36. { 37. printf("|"); 38. } 39. } 40. printf("\n"); 41. } 42. } 43. } 44. 45. void player_move(char board[ROW][COL], int row, int col) { 46. printf("玩家下棋\n"); 47. while (1) { 48. printf("请输入要下的坐标:>"); 49. int x = 0, y = 0; 50. scanf("%d %d", &x, &y); 51. if (x >= 1 && x <= row && y >= y && y <= col) { 52. if (board[x - 1][y - 1] == ' ') { 53. board[x - 1][y - 1] = '*'; 54. break; 55. } 56. else { 57. printf("坐标已经被占用,请重新输入\n"); 58. } 59. } 60. else { 61. printf("坐标不合法,请重新输入\n"); 62. } 63. } 64. } 65. //电脑下棋,简单版本->随机生成坐标rand() 66. void cmp_move(char board[ROW][COL], int row, int col) { 67. printf("电脑下棋:\n"); 68. while (1) { 69. int x = rand() % row; 70. int y = rand() % col; 71. if (board[x][y] == ' ') { 72. board[x][y] = '#'; 73. break; 74. } 75. } 76. } 77. //判断棋盘是否已经满了,如果满了则返回1,没满返回0 78. int isfull(char board[ROW][COL], int row, int col) { 79. for (int i = 0; i < row; i++) { 80. for (int j = 0; j < col; j++) { 81. if (board[i][j] == ' ') { 82. return 0; 83. } 84. } 85. } 86. return 1; 87. } 88. //判断胜负 89. //#为电脑赢,*为玩家赢了,Q为平局,C为继续游戏 90. char iswin(char board[ROW][COL], int row, int col) { 91. int num0f0 = 0;//玩家 92. int num0fX = 0;//电脑 93. //判断行 94. for (int i = 0; i < row; i++) { 95. num0f0 = num0fX = 0; 96. for (int j = 0; j < col; j++) { 97. if (board[i][j] == '*') { 98. num0f0++; 99. } 100. else if (board[i][j] == '#') { 101. num0fX++; 102. } 103. } 104. if (num0f0 == ROW) { 105. return '*'; 106. } 107. else if (num0fX == COL) { 108. return '#'; 109. } 110. } 111. //判断列 112. for (int j = 0; j < col; j++) { 113. num0f0 = num0fX = 0; 114. for (int i = 0; i < row; i++) { 115. if (board[i][j] == '*') { 116. num0f0++; 117. } 118. else if (board[i][j] == '#') { 119. num0fX++; 120. } 121. } 122. if (num0f0 == row) { 123. return '*'; 124. } 125. else if (num0fX == col) { 126. return '#'; 127. } 128. } 129. //判断正对角线 130. num0f0 = num0fX = 0; 131. for (int i = 0; i < row; i++) { 132. if (board[i][i] == '*') { 133. num0f0++; 134. } 135. else if (board[i][i] == '#') { 136. num0fX++; 137. } 138. } 139. if (num0f0 == row) { 140. return '*'; 141. } 142. else if (num0fX == col) { 143. return '#'; 144. } 145. //检查逆对角线 146. num0f0 = num0fX = 0; 147. for (int i = 0; i < row; i++) { 148. if (board[i][row - i - 1] == '*') { 149. num0f0++; 150. } 151. else if (board[i][row - i - 1] == '#') { 152. num0fX++; 153. } 154. } 155. if (num0f0 == row) { 156. return '*'; 157. } 158. else if (num0fX == col) { 159. return '#'; 160. } 161. //判断是否平均,是否需要继续 162. int ret = isfull(board, ROW, COL); 163. if (ret == 1) { 164. return 'Q'; 165. } 166. else { 167. return 'C';//继续游戏 168. } 169. } 170. 171. int intelligence_play(char board[ROW][COL], int row, int col) { 172. //先抢占(2,2)的位置,若已经被占用,则下四个角落之一,以左上角为例(1,1)->对应的数组坐标为(0,0); 173. if (board[1][1] == ' ') { 174. board[1][1] = '#'; 175. return 1; 176. } 177. else if(board[0][0]==' ') { 178. board[0][0] = '#'; 179. return 1; 180. } 181. //抢占位置后判断是攻还是防 182. //cnt用于记录玩家棋子个数,cnt1为电脑,x,y为空格的坐标 183. int cnt = 0, cnt1 = 0, x = -1, y = -1, x1 = -1, y1 = -1, flag = 1; 184. //先判断自己是否满足胜利条件 185. //判断行 186. for (int i = 0; i < row; i++) { 187. for (int j = 0; j < col; j++) { 188. if (board[i][j] == '#') { 189. cnt++; 190. } 191. if (board[i][j] == ' ') { 192. //记录空的位置 193. x = i, y = j; 194. } 195. if (cnt == 2&&x!=-1&&y!=-1) { 196. //开始进攻,连成三个棋,计数清零 197. cnt = 0; 198. board[x][y] = '#'; 199. x = -1, y = -1; 200. return 1; 201. } 202. } 203. //每一行检查结束后需要计数清零 204. cnt = 0; 205. x = -1, y = -1; 206. } 207. //是否需要进行堵棋 208. for (int i = 0; i < row; i++) { 209. for (int j = 0; j < col; j++) { 210. if (board[i][j] == '*') { 211. cnt1++; 212. } 213. if (board[i][j] == ' ') { 214. x = i, y = j; 215. } 216. if (cnt1 == 2 && x != -1 && y != -1) { 217. cnt = 0; 218. board[x][y] = '#'; 219. x = -1, y = -1; 220. return 1; 221. } 222. } 223. cnt1 = 0; 224. x = -1, y = -1; 225. } 226. //判断列 227. for (int j = 0; j < col; j++) { 228. for (int i = 0; i < row; i++) { 229. if (board[i][j] == '#') { 230. cnt++; 231. } 232. if (board[i][j] == ' ') { 233. x = i, y = j; 234. } 235. if (cnt == 2 && x != -1 && y != -1) { 236. cnt = 0; 237. board[x][y] = '#'; 238. x = -1, y = -1; 239. return 1; 240. } 241. } 242. cnt = 0; 243. x = -1, y = -1; 244. } 245. for (int j = 0; j < col; j++) { 246. for (int i = 0; i < row; i++) { 247. if (board[i][j] == '*') { 248. cnt++; 249. } 250. if (board[i][j] == ' ') { 251. x = i, y = j; 252. } 253. if (cnt == 2 && x != -1 && y != -1) { 254. cnt = 0; 255. board[x][y] = '#'; 256. x = -1, y = -1; 257. return 1; 258. } 259. } 260. cnt = 0; 261. x = -1, y = -1; 262. } 263. //判断逆对角线 264. for (int i = 0; i < row; i++) { 265. if (board[i][row - i - 1] == '#') { 266. cnt++; 267. } 268. if (board[i][row - i - 1] == ' ') { 269. x = i, y = row - i - 1; 270. } 271. if (cnt == 2 && x != -1 && y != -1) { 272. cnt = 0; 273. board[x][y] = '#'; 274. x = -1, y = -1; 275. return 1; 276. } 277. } 278. cnt = 0, x = -1, y = -1; 279. for (int i = 0; i < row; i++) { 280. if (board[i][row - i - 1] == '*') { 281. cnt++; 282. } 283. if (board[i][row - i - 1] == ' ') { 284. x = i, y = row - i - 1; 285. } 286. if (cnt == 2 && x != -1 && y != -1) { 287. cnt = 0; 288. board[x][y] = '#'; 289. x = -1, y = -1; 290. return 1; 291. } 292. } 293. cnt = 0, x = -1, y = -1; 294. //判断逆对角线 295. for (int i = 0; i < row; i++) { 296. if (board[i][i] == '#') { 297. cnt++; 298. } 299. if (board[i][i] == ' ') { 300. x = i, y = row - i - 1; 301. } 302. if (cnt == 2 && x != -1 && y != -1) { 303. cnt = 0; 304. board[x][y] = '#'; 305. x = -1, y = -1; 306. return 1; 307. } 308. } 309. cnt = 0, x = -1, y = -1; 310. for (int i = 0; i < row; i++) { 311. if (board[i][i] == '*') { 312. cnt++; 313. } 314. if (board[i][i] == ' ') { 315. x = i, y = row - i - 1; 316. } 317. if (cnt == 2 && x != -1 && y != -1) { 318. cnt = 0; 319. board[x][y] = '#'; 320. x = -1, y = -1; 321. return 1; 322. } 323. } 324. cnt = 0, x = -1, y = -1; 325. //如果上述条件都不满足,则随机下棋 326. cmp_move(board, row, col); 327. }
test.c文件
1. #include<stdio.h> 2. #include"game.h" 3. void meue() { 4. printf("**************************\n"); 5. printf("**************************\n"); 6. printf("****1.Play******0.Exit****\n"); 7. printf("**************************\n"); 8. printf("**************************\n"); 9. } 10. 11. void game() { 12. char ret = 0; 13. char board[ROW][COL]; 14. //初始化棋盘 15. init_board(board, ROW, COL); 16. //打印棋盘 17. print_board(board, ROW, COL); 18. while (1) { 19. //玩家下棋 20. player_move(board, ROW, COL); 21. print_board(board, ROW, COL); 22. ret = iswin(board, ROW, COL); 23. if (ret != 'C') { 24. break; 25. } 26. //电脑下棋 27. //cmp_move(board, ROW, COL); 28. intelligence_play(board, ROW, COL); 29. printf("\n"); 30. print_board(board, ROW, COL); 31. ret = iswin(board, ROW, COL); 32. if (ret != 'C') { 33. break; 34. } 35. } 36. if (ret == '#') 37. { 38. printf("电脑Win\n"); 39. } 40. else if (ret == '*') 41. { 42. printf("玩家Win\n"); 43. } 44. else if (ret == 'Q') 45. { 46. printf("平局\n"); 47. } 48. } 49. 50. void test() { 51. srand(time(NULL)); 52. int input = 0; 53. do { 54. meue(); 55. printf("请选择:>"); 56. scanf("%d", &input); 57. switch (input) { 58. case 1: 59. game(); 60. break; 61. case 0: 62. printf("退出游戏\n"); 63. break; 64. default: 65. printf("选择错误,请重新选择\n"); 66. break; 67. } 68. } while (input); 69. } 70. 71. int main(void) { 72. test(); 73. return 0; 74. }