123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- //下棋业务核心类,与界面棋盘对应,业务放在这里,可以和界面代码分离
- import 'dart:core';
- import 'package:gobang/flyweight/ChessFlyweightFactory.dart';
- import 'package:gobang/flyweight/Position.dart';
- class Ai {
- Ai._();
- static Ai? _ai;
- static Ai getInstance() {
- if (_ai == null) {
- _ai = Ai._();
- }
- return _ai!;
- }
- static var CHESSBOARD_SIZE = 15;
- static var FIRST = 1; //先手,-1表示机器,1表示人类,与Position类中的对应
- var chessboard = List.generate(
- CHESSBOARD_SIZE, (i) => List.filled(CHESSBOARD_SIZE, 0, growable: false),
- growable: false); //与界面棋盘对应,0代表空,-1代表机器,1代表人类
- var score = List.generate(
- CHESSBOARD_SIZE, (i) => List.filled(CHESSBOARD_SIZE, 0, growable: false),
- growable: false); //每个位置得分
- void init() {
- FIRST = 1; //默认人类先手
- for (int i = 0; i < CHESSBOARD_SIZE; i++) {
- for (int j = 0; j < CHESSBOARD_SIZE; j++) {
- chessboard[i][j] = 0;
- score[i][j] = 0;
- }
- }
- }
- //落子
- void addChessman(int x, int y, int owner) {
- chessboard[x][y] = owner;
- }
- //判断落子位置是否合法
- bool isLegal(int x, int y) {
- if (x >= 0 &&
- x < CHESSBOARD_SIZE &&
- y >= 0 &&
- y < CHESSBOARD_SIZE &&
- chessboard[x][y] == 0) {
- return true;
- }
- return false;
- }
- //判断哪方赢了(必定有刚落的子引发,因此只需判断刚落子的周围),owner为-1代表机器,owner为1代表人类
- bool isWin(int x, int y, int owner) {
- int sum = 0;
- //判断横向左边
- for (int i = x - 1; i >= 0; i--) {
- if (chessboard[i][y] == owner) {
- sum++;
- } else {
- break;
- }
- }
- //判断横向右边
- for (int i = x + 1; i < CHESSBOARD_SIZE; i++) {
- if (chessboard[i][y] == owner) {
- sum++;
- } else {
- break;
- }
- }
- if (sum >= 4) {
- return true;
- }
- sum = 0;
- //判断纵向上边
- for (int i = y - 1; i >= 0; i--) {
- if (chessboard[x][i] == owner) {
- sum++;
- } else {
- break;
- }
- }
- //判断纵向下边
- for (int i = y + 1; i < CHESSBOARD_SIZE; i++) {
- if (chessboard[x][i] == owner) {
- sum++;
- } else {
- break;
- }
- }
- if (sum >= 4) {
- return true;
- }
- sum = 0;
- //判断左上角到右下角方向上侧
- for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
- if (chessboard[i][j] == owner) {
- sum++;
- } else {
- break;
- }
- }
- //判断左上角到右下角方向下侧
- for (int i = x + 1, j = y + 1;
- i < CHESSBOARD_SIZE && j < CHESSBOARD_SIZE;
- i++, j++) {
- if (chessboard[i][j] == owner) {
- sum++;
- } else {
- break;
- }
- }
- if (sum >= 4) {
- return true;
- }
- sum = 0;
- //判断右上角到左下角方向上侧
- for (int i = x + 1, j = y - 1; i < CHESSBOARD_SIZE && j >= 0; i++, j--) {
- if (chessboard[i][j] == owner) {
- sum++;
- } else {
- break;
- }
- }
- //判断右上角到左下角方向下侧
- for (int i = x - 1, j = y + 1; i >= 0 && j < CHESSBOARD_SIZE; i--, j++) {
- if (chessboard[i][j] == owner) {
- sum++;
- } else {
- break;
- }
- }
- if (sum >= 4) {
- return true;
- }
- return false;
- }
- //【【【【【*******整个游戏的核心*******】】】】】______确定机器落子位置
- //使用五元组评分算法,该算法参考博客地址:https://blog.csdn.net/u011587401/article/details/50877828
- //算法思路:对15X15的572个五元组分别评分,一个五元组的得分就是该五元组为其中每个位置贡献的分数,
- // 一个位置的分数就是其所在所有五元组分数之和。所有空位置中分数最高的那个位置就是落子位置。
- Position searchPosition() {
- //每次都初始化下score评分数组
- for (int i = 0; i < CHESSBOARD_SIZE; i++) {
- for (int j = 0; j < CHESSBOARD_SIZE; j++) {
- score[i][j] = 0;
- }
- }
- //每次机器找寻落子位置,评分都重新算一遍(虽然算了很多多余的,因为上次落子时候算的大多都没变)
- //先定义一些变量
- int humanChessmanNum = 0; //五元组中的黑棋数量
- int machineChessmanNum = 0; //五元组中的白棋数量
- int tupleScoreTmp = 0; //五元组得分临时变量
- int goalX = -1; //目标位置x坐标
- int goalY = -1; //目标位置y坐标
- int maxScore = -1; //最大分数
- //1.扫描横向的15个行
- for (int i = 0; i < 15; i++) {
- for (int j = 0; j < 11; j++) {
- int k = j;
- while (k < j + 5) {
- if (chessboard[i][k] == -1)
- machineChessmanNum++;
- else if (chessboard[i][k] == 1) humanChessmanNum++;
- k++;
- }
- tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
- //为该五元组的每个位置添加分数
- for (k = j; k < j + 5; k++) {
- score[i][k] += tupleScoreTmp;
- }
- //置零
- humanChessmanNum = 0; //五元组中的黑棋数量
- machineChessmanNum = 0; //五元组中的白棋数量
- tupleScoreTmp = 0; //五元组得分临时变量
- }
- }
- //2.扫描纵向15行
- for (int i = 0; i < 15; i++) {
- for (int j = 0; j < 11; j++) {
- int k = j;
- while (k < j + 5) {
- if (chessboard[k][i] == -1)
- machineChessmanNum++;
- else if (chessboard[k][i] == 1) humanChessmanNum++;
- k++;
- }
- tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
- //为该五元组的每个位置添加分数
- for (k = j; k < j + 5; k++) {
- score[k][i] += tupleScoreTmp;
- }
- //置零
- humanChessmanNum = 0; //五元组中的黑棋数量
- machineChessmanNum = 0; //五元组中的白棋数量
- tupleScoreTmp = 0; //五元组得分临时变量
- }
- }
- //3.扫描右上角到左下角上侧部分
- for (int i = 14; i >= 4; i--) {
- for (int k = i, j = 0; j < 15 && k >= 0; j++, k--) {
- int m = k;
- int n = j;
- while (m > k - 5 && k - 5 >= -1) {
- if (chessboard[m][n] == -1)
- machineChessmanNum++;
- else if (chessboard[m][n] == 1) humanChessmanNum++;
- m--;
- n++;
- }
- //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
- if (m == k - 5) {
- tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
- //为该五元组的每个位置添加分数
- m = k;
- n = j;
- for (; m > k - 5; m--, n++) {
- score[m][n] += tupleScoreTmp;
- }
- }
- //置零
- humanChessmanNum = 0; //五元组中的黑棋数量
- machineChessmanNum = 0; //五元组中的白棋数量
- tupleScoreTmp = 0; //五元组得分临时变量
- }
- }
- //4.扫描右上角到左下角下侧部分
- for (int i = 1; i < 15; i++) {
- for (int k = i, j = 14; j >= 0 && k < 15; j--, k++) {
- int m = k;
- int n = j;
- while (m < k + 5 && k + 5 <= 15) {
- if (chessboard[n][m] == -1)
- machineChessmanNum++;
- else if (chessboard[n][m] == 1) humanChessmanNum++;
- m++;
- n--;
- }
- //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
- if (m == k + 5) {
- tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
- //为该五元组的每个位置添加分数
- m = k;
- n = j;
- for (; m < k + 5; m++, n--) {
- score[n][m] += tupleScoreTmp;
- }
- }
- //置零
- humanChessmanNum = 0; //五元组中的黑棋数量
- machineChessmanNum = 0; //五元组中的白棋数量
- tupleScoreTmp = 0; //五元组得分临时变量
- }
- }
- //5.扫描左上角到右下角上侧部分
- for (int i = 0; i < 11; i++) {
- for (int k = i, j = 0; j < 15 && k < 15; j++, k++) {
- int m = k;
- int n = j;
- while (m < k + 5 && k + 5 <= 15) {
- if (chessboard[m][n] == -1)
- machineChessmanNum++;
- else if (chessboard[m][n] == 1) humanChessmanNum++;
- m++;
- n++;
- }
- //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
- if (m == k + 5) {
- tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
- //为该五元组的每个位置添加分数
- m = k;
- n = j;
- for (; m < k + 5; m++, n++) {
- score[m][n] += tupleScoreTmp;
- }
- }
- //置零
- humanChessmanNum = 0; //五元组中的黑棋数量
- machineChessmanNum = 0; //五元组中的白棋数量
- tupleScoreTmp = 0; //五元组得分临时变量
- }
- }
- //6.扫描左上角到右下角下侧部分
- for (int i = 1; i < 11; i++) {
- for (int k = i, j = 0; j < 15 && k < 15; j++, k++) {
- int m = k;
- int n = j;
- while (m < k + 5 && k + 5 <= 15) {
- if (chessboard[n][m] == -1)
- machineChessmanNum++;
- else if (chessboard[n][m] == 1) humanChessmanNum++;
- m++;
- n++;
- }
- //注意斜向判断的时候,可能构不成五元组(靠近四个角落),遇到这种情况要忽略掉
- if (m == k + 5) {
- tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
- //为该五元组的每个位置添加分数
- m = k;
- n = j;
- for (; m < k + 5; m++, n++) {
- score[n][m] += tupleScoreTmp;
- }
- }
- //置零
- humanChessmanNum = 0; //五元组中的黑棋数量
- machineChessmanNum = 0; //五元组中的白棋数量
- tupleScoreTmp = 0; //五元组得分临时变量
- }
- }
- //从空位置中找到得分最大的位置
- for (int i = 0; i < 15; i++) {
- for (int j = 0; j < 15; j++) {
- if (chessboard[i][j] == 0 && score[i][j] > maxScore) {
- goalX = i;
- goalY = j;
- maxScore = score[i][j];
- }
- }
- }
- if (goalX != -1 && goalY != -1) {
- return Position(goalX.toDouble(), goalY.toDouble(),
- ChessFlyweightFactory.getInstance().getChess(""));
- }
- //没找到坐标说明平局了,笔者不处理平局
- return Position(
- -1, -1, ChessFlyweightFactory.getInstance().getChess(""));
- }
- //各种五元组情况评分表
- int tupleScore(int humanChessmanNum, int machineChessmanNum) {
- //1.既有人类落子,又有机器落子,判分为0
- if (humanChessmanNum > 0 && machineChessmanNum > 0) {
- return 0;
- }
- //2.全部为空,没有落子,判分为7
- if (humanChessmanNum == 0 && machineChessmanNum == 0) {
- return 7;
- }
- //3.机器落1子,判分为35
- if (machineChessmanNum == 1) {
- return 35;
- }
- //4.机器落2子,判分为800
- if (machineChessmanNum == 2) {
- return 800;
- }
- //5.机器落3子,判分为15000
- if (machineChessmanNum == 3) {
- return 15000;
- }
- //6.机器落4子,判分为800000
- if (machineChessmanNum == 4) {
- return 800000;
- }
- //7.人类落1子,判分为15
- if (humanChessmanNum == 1) {
- return 15;
- }
- //8.人类落2子,判分为400
- if (humanChessmanNum == 2) {
- return 400;
- }
- //9.人类落3子,判分为1800
- if (humanChessmanNum == 3) {
- return 1800;
- }
- //10.人类落4子,判分为100000
- if (humanChessmanNum == 4) {
- return 100000;
- }
- return -1; //若是其他结果肯定出错了。这行代码根本不可能执行
- }
- }
|