Ai.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. //下棋业务核心类,与界面棋盘对应,业务放在这里,可以和界面代码分离
  2. import 'dart:core';
  3. import 'package:gobang/flyweight/ChessFlyweightFactory.dart';
  4. import 'package:gobang/flyweight/Position.dart';
  5. class Ai {
  6. Ai._();
  7. static Ai? _ai;
  8. static Ai getInstance() {
  9. if (_ai == null) {
  10. _ai = Ai._();
  11. }
  12. return _ai!;
  13. }
  14. static var CHESSBOARD_SIZE = 15;
  15. static var FIRST = 1; //先手,-1表示机器,1表示人类,与Position类中的对应
  16. var chessboard = List.generate(
  17. CHESSBOARD_SIZE, (i) => List.filled(CHESSBOARD_SIZE, 0, growable: false),
  18. growable: false); //与界面棋盘对应,0代表空,-1代表机器,1代表人类
  19. var score = List.generate(
  20. CHESSBOARD_SIZE, (i) => List.filled(CHESSBOARD_SIZE, 0, growable: false),
  21. growable: false); //每个位置得分
  22. void init() {
  23. FIRST = 1; //默认人类先手
  24. for (int i = 0; i < CHESSBOARD_SIZE; i++) {
  25. for (int j = 0; j < CHESSBOARD_SIZE; j++) {
  26. chessboard[i][j] = 0;
  27. score[i][j] = 0;
  28. }
  29. }
  30. }
  31. //落子
  32. void addChessman(int x, int y, int owner) {
  33. chessboard[x][y] = owner;
  34. }
  35. //判断落子位置是否合法
  36. bool isLegal(int x, int y) {
  37. if (x >= 0 &&
  38. x < CHESSBOARD_SIZE &&
  39. y >= 0 &&
  40. y < CHESSBOARD_SIZE &&
  41. chessboard[x][y] == 0) {
  42. return true;
  43. }
  44. return false;
  45. }
  46. //判断哪方赢了(必定有刚落的子引发,因此只需判断刚落子的周围),owner为-1代表机器,owner为1代表人类
  47. bool isWin(int x, int y, int owner) {
  48. int sum = 0;
  49. //判断横向左边
  50. for (int i = x - 1; i >= 0; i--) {
  51. if (chessboard[i][y] == owner) {
  52. sum++;
  53. } else {
  54. break;
  55. }
  56. }
  57. //判断横向右边
  58. for (int i = x + 1; i < CHESSBOARD_SIZE; i++) {
  59. if (chessboard[i][y] == owner) {
  60. sum++;
  61. } else {
  62. break;
  63. }
  64. }
  65. if (sum >= 4) {
  66. return true;
  67. }
  68. sum = 0;
  69. //判断纵向上边
  70. for (int i = y - 1; i >= 0; i--) {
  71. if (chessboard[x][i] == owner) {
  72. sum++;
  73. } else {
  74. break;
  75. }
  76. }
  77. //判断纵向下边
  78. for (int i = y + 1; i < CHESSBOARD_SIZE; i++) {
  79. if (chessboard[x][i] == owner) {
  80. sum++;
  81. } else {
  82. break;
  83. }
  84. }
  85. if (sum >= 4) {
  86. return true;
  87. }
  88. sum = 0;
  89. //判断左上角到右下角方向上侧
  90. for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
  91. if (chessboard[i][j] == owner) {
  92. sum++;
  93. } else {
  94. break;
  95. }
  96. }
  97. //判断左上角到右下角方向下侧
  98. for (int i = x + 1, j = y + 1;
  99. i < CHESSBOARD_SIZE && j < CHESSBOARD_SIZE;
  100. i++, j++) {
  101. if (chessboard[i][j] == owner) {
  102. sum++;
  103. } else {
  104. break;
  105. }
  106. }
  107. if (sum >= 4) {
  108. return true;
  109. }
  110. sum = 0;
  111. //判断右上角到左下角方向上侧
  112. for (int i = x + 1, j = y - 1; i < CHESSBOARD_SIZE && j >= 0; i++, j--) {
  113. if (chessboard[i][j] == owner) {
  114. sum++;
  115. } else {
  116. break;
  117. }
  118. }
  119. //判断右上角到左下角方向下侧
  120. for (int i = x - 1, j = y + 1; i >= 0 && j < CHESSBOARD_SIZE; i--, j++) {
  121. if (chessboard[i][j] == owner) {
  122. sum++;
  123. } else {
  124. break;
  125. }
  126. }
  127. if (sum >= 4) {
  128. return true;
  129. }
  130. return false;
  131. }
  132. //【【【【【*******整个游戏的核心*******】】】】】______确定机器落子位置
  133. //使用五元组评分算法,该算法参考博客地址:https://blog.csdn.net/u011587401/article/details/50877828
  134. //算法思路:对15X15的572个五元组分别评分,一个五元组的得分就是该五元组为其中每个位置贡献的分数,
  135. // 一个位置的分数就是其所在所有五元组分数之和。所有空位置中分数最高的那个位置就是落子位置。
  136. Position searchPosition() {
  137. //每次都初始化下score评分数组
  138. for (int i = 0; i < CHESSBOARD_SIZE; i++) {
  139. for (int j = 0; j < CHESSBOARD_SIZE; j++) {
  140. score[i][j] = 0;
  141. }
  142. }
  143. //每次机器找寻落子位置,评分都重新算一遍(虽然算了很多多余的,因为上次落子时候算的大多都没变)
  144. //先定义一些变量
  145. int humanChessmanNum = 0; //五元组中的黑棋数量
  146. int machineChessmanNum = 0; //五元组中的白棋数量
  147. int tupleScoreTmp = 0; //五元组得分临时变量
  148. int goalX = -1; //目标位置x坐标
  149. int goalY = -1; //目标位置y坐标
  150. int maxScore = -1; //最大分数
  151. //1.扫描横向的15个行
  152. for (int i = 0; i < 15; i++) {
  153. for (int j = 0; j < 11; j++) {
  154. int k = j;
  155. while (k < j + 5) {
  156. if (chessboard[i][k] == -1)
  157. machineChessmanNum++;
  158. else if (chessboard[i][k] == 1) humanChessmanNum++;
  159. k++;
  160. }
  161. tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
  162. //为该五元组的每个位置添加分数
  163. for (k = j; k < j + 5; k++) {
  164. score[i][k] += tupleScoreTmp;
  165. }
  166. //置零
  167. humanChessmanNum = 0; //五元组中的黑棋数量
  168. machineChessmanNum = 0; //五元组中的白棋数量
  169. tupleScoreTmp = 0; //五元组得分临时变量
  170. }
  171. }
  172. //2.扫描纵向15行
  173. for (int i = 0; i < 15; i++) {
  174. for (int j = 0; j < 11; j++) {
  175. int k = j;
  176. while (k < j + 5) {
  177. if (chessboard[k][i] == -1)
  178. machineChessmanNum++;
  179. else if (chessboard[k][i] == 1) humanChessmanNum++;
  180. k++;
  181. }
  182. tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
  183. //为该五元组的每个位置添加分数
  184. for (k = j; k < j + 5; k++) {
  185. score[k][i] += tupleScoreTmp;
  186. }
  187. //置零
  188. humanChessmanNum = 0; //五元组中的黑棋数量
  189. machineChessmanNum = 0; //五元组中的白棋数量
  190. tupleScoreTmp = 0; //五元组得分临时变量
  191. }
  192. }
  193. //3.扫描右上角到左下角上侧部分
  194. for (int i = 14; i >= 4; i--) {
  195. for (int k = i, j = 0; j < 15 && k >= 0; j++, k--) {
  196. int m = k;
  197. int n = j;
  198. while (m > k - 5 && k - 5 >= -1) {
  199. if (chessboard[m][n] == -1)
  200. machineChessmanNum++;
  201. else if (chessboard[m][n] == 1) humanChessmanNum++;
  202. m--;
  203. n++;
  204. }
  205. //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
  206. if (m == k - 5) {
  207. tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
  208. //为该五元组的每个位置添加分数
  209. m = k;
  210. n = j;
  211. for (; m > k - 5; m--, n++) {
  212. score[m][n] += tupleScoreTmp;
  213. }
  214. }
  215. //置零
  216. humanChessmanNum = 0; //五元组中的黑棋数量
  217. machineChessmanNum = 0; //五元组中的白棋数量
  218. tupleScoreTmp = 0; //五元组得分临时变量
  219. }
  220. }
  221. //4.扫描右上角到左下角下侧部分
  222. for (int i = 1; i < 15; i++) {
  223. for (int k = i, j = 14; j >= 0 && k < 15; j--, k++) {
  224. int m = k;
  225. int n = j;
  226. while (m < k + 5 && k + 5 <= 15) {
  227. if (chessboard[n][m] == -1)
  228. machineChessmanNum++;
  229. else if (chessboard[n][m] == 1) humanChessmanNum++;
  230. m++;
  231. n--;
  232. }
  233. //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
  234. if (m == k + 5) {
  235. tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
  236. //为该五元组的每个位置添加分数
  237. m = k;
  238. n = j;
  239. for (; m < k + 5; m++, n--) {
  240. score[n][m] += tupleScoreTmp;
  241. }
  242. }
  243. //置零
  244. humanChessmanNum = 0; //五元组中的黑棋数量
  245. machineChessmanNum = 0; //五元组中的白棋数量
  246. tupleScoreTmp = 0; //五元组得分临时变量
  247. }
  248. }
  249. //5.扫描左上角到右下角上侧部分
  250. for (int i = 0; i < 11; i++) {
  251. for (int k = i, j = 0; j < 15 && k < 15; j++, k++) {
  252. int m = k;
  253. int n = j;
  254. while (m < k + 5 && k + 5 <= 15) {
  255. if (chessboard[m][n] == -1)
  256. machineChessmanNum++;
  257. else if (chessboard[m][n] == 1) humanChessmanNum++;
  258. m++;
  259. n++;
  260. }
  261. //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
  262. if (m == k + 5) {
  263. tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
  264. //为该五元组的每个位置添加分数
  265. m = k;
  266. n = j;
  267. for (; m < k + 5; m++, n++) {
  268. score[m][n] += tupleScoreTmp;
  269. }
  270. }
  271. //置零
  272. humanChessmanNum = 0; //五元组中的黑棋数量
  273. machineChessmanNum = 0; //五元组中的白棋数量
  274. tupleScoreTmp = 0; //五元组得分临时变量
  275. }
  276. }
  277. //6.扫描左上角到右下角下侧部分
  278. for (int i = 1; i < 11; i++) {
  279. for (int k = i, j = 0; j < 15 && k < 15; j++, k++) {
  280. int m = k;
  281. int n = j;
  282. while (m < k + 5 && k + 5 <= 15) {
  283. if (chessboard[n][m] == -1)
  284. machineChessmanNum++;
  285. else if (chessboard[n][m] == 1) humanChessmanNum++;
  286. m++;
  287. n++;
  288. }
  289. //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
  290. if (m == k + 5) {
  291. tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
  292. //为该五元组的每个位置添加分数
  293. m = k;
  294. n = j;
  295. for (; m < k + 5; m++, n++) {
  296. score[n][m] += tupleScoreTmp;
  297. }
  298. }
  299. //置零
  300. humanChessmanNum = 0; //五元组中的黑棋数量
  301. machineChessmanNum = 0; //五元组中的白棋数量
  302. tupleScoreTmp = 0; //五元组得分临时变量
  303. }
  304. }
  305. //从空位置中找到得分最大的位置
  306. for (int i = 0; i < 15; i++) {
  307. for (int j = 0; j < 15; j++) {
  308. if (chessboard[i][j] == 0 && score[i][j] > maxScore) {
  309. goalX = i;
  310. goalY = j;
  311. maxScore = score[i][j];
  312. }
  313. }
  314. }
  315. if (goalX != -1 && goalY != -1) {
  316. return Position(goalX.toDouble(), goalY.toDouble(),
  317. ChessFlyweightFactory.getInstance().getChess(""));
  318. }
  319. //没找到坐标说明平局了,笔者不处理平局
  320. return Position(
  321. -1, -1, ChessFlyweightFactory.getInstance().getChess(""));
  322. }
  323. //各种五元组情况评分表
  324. int tupleScore(int humanChessmanNum, int machineChessmanNum) {
  325. //1.既有人类落子,又有机器落子,判分为0
  326. if (humanChessmanNum > 0 && machineChessmanNum > 0) {
  327. return 0;
  328. }
  329. //2.全部为空,没有落子,判分为7
  330. if (humanChessmanNum == 0 && machineChessmanNum == 0) {
  331. return 7;
  332. }
  333. //3.机器落1子,判分为35
  334. if (machineChessmanNum == 1) {
  335. return 35;
  336. }
  337. //4.机器落2子,判分为800
  338. if (machineChessmanNum == 2) {
  339. return 800;
  340. }
  341. //5.机器落3子,判分为15000
  342. if (machineChessmanNum == 3) {
  343. return 15000;
  344. }
  345. //6.机器落4子,判分为800000
  346. if (machineChessmanNum == 4) {
  347. return 800000;
  348. }
  349. //7.人类落1子,判分为15
  350. if (humanChessmanNum == 1) {
  351. return 15;
  352. }
  353. //8.人类落2子,判分为400
  354. if (humanChessmanNum == 2) {
  355. return 400;
  356. }
  357. //9.人类落3子,判分为1800
  358. if (humanChessmanNum == 3) {
  359. return 1800;
  360. }
  361. //10.人类落4子,判分为100000
  362. if (humanChessmanNum == 4) {
  363. return 100000;
  364. }
  365. return -1; //若是其他结果肯定出错了。这行代码根本不可能执行
  366. }
  367. }