123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- //下棋业务核心类,与界面棋盘对应,业务放在这里,可以和界面代码分离
- import 'dart:core';
- 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;
- print("$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];
- }
- }
- }
- print(maxScore);
- if(goalX != -1 && goalY != -1){
- return new Position(goalX.toDouble(), goalY.toDouble(), ChessFlyweightFactory.getInstance().getChess(""));
- }
- //没找到坐标说明平局了,笔者不处理平局
- print("没有找到");
- return new 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;//若是其他结果肯定出错了。这行代码根本不可能执行
- }
- }
|