Java实现五子棋AI算法

作者:彷徨的石头 时间:2022-02-23 05:32:57 

五子棋AI算法也算是一个典型的游戏AI算法,一些棋类的AI算法都可以参考实现,下面是Java实现代码

棋盘抽象接口


import java.util.List;

public interface IChessboard {
//取得棋盘最大横坐标
public int getMaxX();
//最大纵坐标
public int getMaxY();
//取得当前所有空白点,这些点才可以下棋
public List<Point> getFreePoints();
}

棋子类实现


//棋子类
public class Point {
// 这了性能,设成公有
public int x;
public int y;

public int getX() {
 return x;
}

public Point setX(int x) {
 this.x = x;
 return this;
}

public int getY() {
 return y;
}

public Point setY(int y) {
 this.y = y;
 return this;
}

public Point(int x, int y) {
 this.x = x;
 this.y = y;
}

@Override
public int hashCode() {
 return x + y;
}

@Override
public boolean equals(Object obj) {
 if (this == obj)
  return true;
 Point other = (Point) obj;
 if (x != other.x)
  return false;
 if (y != other.y)
  return false;
 return true;
}

}

玩家抽象接口


import java.util.List;

public interface IPlayer {
//下一步棋子,传入对手已经下的棋子集合
public void run(List<Point> enemyPoints, Point point);

public boolean hasWin();

public void setChessboard(IChessboard chessboard);

public List<Point> getMyPoints();
}

玩家基础抽象类


import java.util.ArrayList;
import java.util.List;

public abstract class BasePlayer implements IPlayer {
//我已下的棋子
protected List<Point> myPoints = new ArrayList<Point>(200);
//棋盘
protected IChessboard chessboard;
//棋盘最大横坐标和纵标,
protected int maxX;
protected int maxY;

//所有空白棋子
protected List<Point> allFreePoints;

@Override
public final List<Point> getMyPoints() {
 return myPoints;
}

@Override
public void setChessboard(IChessboard chessboard) {
 this.chessboard = chessboard;
 allFreePoints = chessboard.getFreePoints();
 maxX = chessboard.getMaxX();
 maxY = chessboard.getMaxY();
 myPoints.clear();
}

private final Point temp = new Point(0, 0);
//我是否是否赢了
public final boolean hasWin(){
 if(myPoints.size()<5){
  return false;
 }
 Point point = myPoints.get(myPoints.size()-1);
 int count = 1;
 int x=point.getX(),y=point.getY();
 //横向--
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setX(temp.getX()-1)) && temp.getX()>=0 && count<5) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setX(temp.getX()+1)) && temp.getX()<maxX && count<5) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 //纵向|
 count = 1;
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setY(temp.getY()-1)) && temp.getY()>=0) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setY(temp.getY()+1)) && temp.getY()<maxY && count<5) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 //正斜向 /
 count =1;
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setX(temp.getX()-1).setY(temp.getY()+1)) && temp.getX()>=0 && temp.getY()<maxY) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setX(temp.getX()+1).setY(temp.getY()-1)) && temp.getX()<maxX && temp.getY()>=0 && count<6) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 //反斜 \
 count = 1;
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setX(temp.getX()-1).setY(temp.getY()-1)) && temp.getX()>=0 && temp.getY()>=0) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 temp.setX(x).setY(y);
 while (myPoints.contains(temp.setX(temp.getX()+1).setY(temp.getY()+1)) && temp.getX()<maxX && temp.getY()<maxY && count<5) {
  count ++;
 }
 if(count>=5){
  return true;
 }
 return false;
}
}

电脑AI类实现


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//算法核心类,算法的主体思想分三个步骤,
//第一步:根据双方的当前的形势循环地假设性的分别给自己和对方下一子(在某个范围内下子),并判断此棋子能带来的形势上的变化,如能不能冲4,能不能形成我方或敌方双3等,
//第二步:根据上一步结果,组合每一步棋子所带来的所有结果(如某一步棋子可能形成我方1个活3,1个冲4(我叫它半活4)等),包括敌方和我方的。
//第三步:根据用户给的规则对上一步结果进行排序,并选子(有进攻形、防守形规则)
public class BaseComputerAi extends BasePlayer {

// 四个方向,横- 、纵| 、正斜/ 、反斜\
private static final int HENG = 0;
private static final int ZHONG = 1;
private static final int ZHENG_XIE = 2;
private static final int FAN_XIE = 3;
//往前往后
private static final boolean FORWARD = true;
private static final boolean BACKWARD = false;

//标示分析结果当前点位是两头通(ALIVE)还是只有一头通(HALF_ALIVE),封死的棋子分析过程自动屏蔽,不作为待选棋子
private static final int ALIVE = 1;
private static final int HALF_ALIVE = 0;
//private static final int DEAD = -1;

//计算范围,太大的范围会有性能问题
private class CalcuteRange{
 int xStart,yStart,xStop,yStop;
 private CalcuteRange(int xStart, int yStart, int xStop, int yStop) {
  this.xStart = xStart;
  this.yStart = yStart;
  this.xStop = xStop;
  this.yStop = yStop;
 }
}

//限定电脑计算范围,如果整个棋盘计算,性能太差,目前是根据所有已下的棋子的边界值加RANGE_STEP值形成,目前为1
private static final int RANGE_STEP = 1;
CalcuteRange currentRange = new CalcuteRange(0, 0, 0, 0);
private void initRange(List<Point> comuters, List<Point> humans){
 currentRange.xStart = humans.get(0).getX()-RANGE_STEP;
 currentRange.yStart = humans.get(0).getY()-RANGE_STEP;
 currentRange.xStop = humans.get(0).getX()+RANGE_STEP;
 currentRange.yStop = humans.get(0).getY()+RANGE_STEP;
 for (Point point : humans) {
  if(point.getX()-RANGE_STEP<currentRange.xStart){
   currentRange.xStart = point.getX()-RANGE_STEP;
  }else if(point.getX()+RANGE_STEP>currentRange.xStop){
   currentRange.xStop = point.getX()+RANGE_STEP;
  }
  if(point.getY()-RANGE_STEP<currentRange.yStart){
   currentRange.yStart = point.getY()-RANGE_STEP;
  }else if(point.getY()+RANGE_STEP>currentRange.yStop){
   currentRange.yStop = point.getY()+RANGE_STEP;
  }
 }
 for (Point point : comuters) {
  if(point.getX()-RANGE_STEP<currentRange.xStart){
   currentRange.xStart = point.getX()-RANGE_STEP;
  }else if(point.getX()+RANGE_STEP>currentRange.xStop){
   currentRange.xStop = point.getX()+RANGE_STEP;
  }
  if(point.getY()-RANGE_STEP<currentRange.yStart){
   currentRange.yStart = point.getY()-RANGE_STEP;
  }else if(point.getY()+RANGE_STEP>currentRange.yStop){
   currentRange.yStop = point.getY()+RANGE_STEP;
  }
 }

//如果范围扩大后超过了棋盘,则等于棋盘
 currentRange.xStart=currentRange.xStart<0?0:currentRange.xStart;
 currentRange.yStart=currentRange.yStart<0?0:currentRange.yStart;
 currentRange.xStop=currentRange.xStop>=maxX?maxX-1:currentRange.xStop;
 currentRange.yStop=currentRange.yStop>=maxY?maxY-1:currentRange.yStop;
}

// 分析当前形式的入口方法,分析总共分三个步骤,第三步骤可由子类干预以作难度控制
private Point doAnalysis(List<Point> comuters, List<Point> humans) {
 if(humans.size()==1){//第一步
  return getFirstPoint(humans);
 }

//初始化计算范围
 initRange(comuters, humans);

//清除以前的结果
 initAnalysisResults();
 // 开始分析,扫描所有空白点,形成第一次分析结果
 Point bestPoint = doFirstAnalysis(comuters, humans);
 if(bestPoint!=null){
  //System.out.println("这个棋子最重要,只能下这个棋子");
  return bestPoint;
 }
 // 分析第一次结果,找到自己的最佳点位
 bestPoint = doComputerSencondAnalysis(computerFirstResults,computerSencodResults);
 if(bestPoint!=null){
  //System.out.println("快要赢了,就下这个棋子");
  return bestPoint;
 }
 computerFirstResults.clear();
 System.gc();
 // 分析第一次结果,找到敌人的最佳点位
 bestPoint = doHumanSencondAnalysis(humanFirstResults,humanSencodResults);
 if(bestPoint!=null){
  //System.out.println("再不下这个棋子就输了");
  return bestPoint;
 }
 humanFirstResults.clear();
 System.gc();
 //没找到绝杀点,第三次结果分析
 return doThirdAnalysis();
}

//下第一步棋子,不需要复杂的计算,根据人类第一步棋子X值减1完成
private Point getFirstPoint(List<Point> humans) {
 Point point = humans.get(0);
 if(point.getX()==0 || point.getY()==0 || point.getX()==maxX && point.getY()==maxY)
  return new Point(maxX/2, maxY/2);
 else{
  return new Point(point.getX()-1,point.getY());
 }
}

// private int debugx,debugy;//用于DEBUG

// 开始分析,扫描所有空白点,形成第一次分析结果
private Point doFirstAnalysis(List<Point> comuters, List<Point> humans){
 int size = allFreePoints.size();
 Point computerPoint = null;
 Point humanPoint = null;
 int x,y;
 FirstAnalysisResult firstAnalysisResult;
 for (int i = 0; i < size; i++) {
  computerPoint = allFreePoints.get(i);
  //先把X、Y坐标记下来,因为在分析过程中会改变原来的对象
  x = computerPoint.getX();
  y = computerPoint.getY();
  if(x<currentRange.xStart || x>currentRange.xStop || y<currentRange.yStart || y>currentRange.yStop){
   continue;
  }

//   if(x==debugx && y==debugy){
//    System.out.println("sssssssssssss");
//   }

//尝试在此位置上下一个棋子,并分析在“横向”这个方向上我方可形成的状态,如活4,活3,半活4,活2等所有状态
  firstAnalysisResult = tryAndCountResult(comuters,humans, computerPoint, HENG);
  computerPoint.setX(x).setY(y);//回复点位的原值,以供下次分析
  if(firstAnalysisResult!=null){//无返回结果此方向上不可能达到五个棋子,
   if(firstAnalysisResult.count==5)//等于5表示在此点上下棋子即可连成5个,胜利了,不再往下进行分析
    return computerPoint;
   //记录第一次分析结果
   addToFirstAnalysisResult(firstAnalysisResult,computerFirstResults);
  }

//在“纵向”这个方向上重复上面的步骤
  firstAnalysisResult = tryAndCountResult(comuters,humans, computerPoint, ZHONG);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    return computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,computerFirstResults);
  }

//正斜向
  firstAnalysisResult = tryAndCountResult(comuters,humans, computerPoint, ZHENG_XIE);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    return computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,computerFirstResults);
  }

//反斜向
  firstAnalysisResult = tryAndCountResult(comuters,humans, computerPoint, FAN_XIE);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    return computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,computerFirstResults);
  }

//在“横向”上分析此棋子可在敌方形成如何状态,如敌方的活3、半活4等
  firstAnalysisResult = tryAndCountResult(humans,comuters, computerPoint, HENG);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    humanPoint = computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,humanFirstResults);
  }

//“纵向”
  firstAnalysisResult = tryAndCountResult(humans,comuters, computerPoint, ZHONG);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    humanPoint = computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,humanFirstResults);
  }

//“正斜”
  firstAnalysisResult = tryAndCountResult(humans,comuters, computerPoint, ZHENG_XIE);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    humanPoint = computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,humanFirstResults);
  }

//“反斜”
  firstAnalysisResult = tryAndCountResult(humans,comuters, computerPoint, FAN_XIE);
  computerPoint.setX(x).setY(y);
  if(firstAnalysisResult!=null){//死棋,不下
   if(firstAnalysisResult.count==5)
    humanPoint = computerPoint;

addToFirstAnalysisResult(firstAnalysisResult,humanFirstResults);
  }
 }
 //如果没有绝杀棋子,第一次分析不需要返回结果
 return humanPoint;
}

//第二次分析,分析第一次形成的结果,第一次分析结果会把一步棋在四个方向上可形成的结果生成最多四个FirstAnalysisResult对象(敌我各四)
//这里要把这四个对象组合成一个SencondAnalysisResult对象,
private Point doComputerSencondAnalysis(Map<Point,List<FirstAnalysisResult>> firstResults,List<SencondAnalysisResult> sencodResults) {
 List<FirstAnalysisResult> list = null;
 SencondAnalysisResult sr = null;
 for (Point p : firstResults.keySet()) {
  sr = new SencondAnalysisResult(p);
  list = firstResults.get(p);
  for (FirstAnalysisResult result : list) {
   if(result.count==4){
    if(result.aliveState==ALIVE){//经过前面的过滤,双方都排除了绝杀棋,有活4就下这一步了,再下一步就赢了
     return result.point;//如果有绝杀,第一轮已返回,在此轮活4已经是好的棋子,直接返回,不再往下分析
    }else{
     sr.halfAlive4 ++;
     computer4HalfAlives.add(sr);
    }
   }else if(result.count==3){
    if(result.aliveState==ALIVE){
     sr.alive3++;
     if(sr.alive3==1){
      computer3Alives.add(sr);
     }else{
      computerDouble3Alives.add(sr);
     }
    }else{
     sr.halfAlive3++;
     computer3HalfAlives.add(sr);
    }
   }else{//半活2在第一阶段已被排除,不再处理
    sr.alive2++;
    if(sr.alive2==1){
     computer2Alives.add(sr);
    }else{
     computerDouble2Alives.add(sr);
    }
   }
  }
  sencodResults.add(sr);
 }
 //没有找到活4
 return null;
}

//这个方法和上面的基本一样,但为了性能,少作几次判断,将人类和电脑的分开了
private Point doHumanSencondAnalysis(Map<Point,List<FirstAnalysisResult>> firstResults,List<SencondAnalysisResult> sencodResults) {
 List<FirstAnalysisResult> list = null;
 SencondAnalysisResult sr = null;
 for (Point p : firstResults.keySet()) {
  sr = new SencondAnalysisResult(p);
  list = firstResults.get(p);
  for (FirstAnalysisResult result : list) {
   if(result.count==4){
    if(result.aliveState==ALIVE){
     human4Alives.add(sr);
    }else{
     sr.halfAlive4 ++;
     human4HalfAlives.add(sr);
    }
   }else if(result.count==3){
    if(result.aliveState==ALIVE){
     sr.alive3++;
     if(sr.alive3==1){
      human3Alives.add(sr);
     }else{
      humanDouble3Alives.add(sr);
     }
    }else{
     sr.halfAlive3++;
     human3HalfAlives.add(sr);
    }
   }else{
    sr.alive2++;
    if(sr.alive2==1){
     human2Alives.add(sr);
    }else{
     humanDouble2Alives.add(sr);
    }
   }
  }
  sencodResults.add(sr);
 }
 //没有找到活4
 return null;
}

private void sleep(int miniSecond){
 try {
  Thread.sleep(miniSecond);
 } catch (InterruptedException e) {
 }
}

//第三次分析,双方都不可以制造活4,找双活3棋子,不行就找半活4,再不行就找单活3,双活2
private Point doThirdAnalysis() {
 if(!computer4HalfAlives.isEmpty()){
  return computer4HalfAlives.get(0).point;
 }
 System.gc();
 sleep(300);
 Collections.sort(computerSencodResults);
 System.gc();

//即将单活4,且我没有半活4以上的,只能堵
 Point mostBest = getBestPoint(human4Alives, computerSencodResults);
 if(mostBest!=null)
  return mostBest;

Collections.sort(humanSencodResults);
 System.gc();

mostBest = getBestPoint();
 if(mostBest!=null)
  return mostBest;

//拿出各自排第一的,谁好就下谁
 return computerSencodResults.get(0).point;
}

//子类实现这个方法,并改变其顺序可以实现防守为主还是猛攻
protected Point getBestPoint(){
 //即将单活4,且我没有半活4以上的,只能堵
 Point mostBest = getBestPoint(computerDouble3Alives, humanSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(computer3Alives, humanSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(humanDouble3Alives, computerSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(human3Alives, computerSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(computerDouble2Alives, humanSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(computer2Alives, humanSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(computer3HalfAlives, humanSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(human4HalfAlives, computerSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(humanDouble2Alives, computerSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(human2Alives, computerSencodResults);
 if(mostBest!=null)
  return mostBest;

mostBest = getBestPoint(human3HalfAlives, computerSencodResults);
 return mostBest;
}

//第三次分析的最后一步,第二次结果已经过排序,在此可以从前面选出最好的棋子
protected Point getBestPoint(List<SencondAnalysisResult> myBest,List<SencondAnalysisResult> yourSencodResults){
 if(!myBest.isEmpty()){
  if(myBest.size()>1){
   for (SencondAnalysisResult your : yourSencodResults) {
    if(myBest.contains(your)){
     return your.point;
    }
   }
   return myBest.get(0).point;
  }else{
   return myBest.get(0).point;
  }
 }
 return null;
}

//第一次分析结果
private final Map<Point,List<FirstAnalysisResult>> computerFirstResults = new HashMap<Point,List<FirstAnalysisResult>>();
private final Map<Point,List<FirstAnalysisResult>> humanFirstResults = new HashMap<Point,List<FirstAnalysisResult>>();
//第二次总结果
protected final List<SencondAnalysisResult> computerSencodResults = new ArrayList<SencondAnalysisResult>();
protected final List<SencondAnalysisResult> humanSencodResults = new ArrayList<SencondAnalysisResult>();
//第二次分结果,电脑
protected final List<SencondAnalysisResult> computer4HalfAlives = new ArrayList<SencondAnalysisResult>(2);
protected final List<SencondAnalysisResult> computerDouble3Alives = new ArrayList<SencondAnalysisResult>(4);
protected final List<SencondAnalysisResult> computer3Alives = new ArrayList<SencondAnalysisResult>(5);
protected final List<SencondAnalysisResult> computerDouble2Alives = new ArrayList<SencondAnalysisResult>();
protected final List<SencondAnalysisResult> computer2Alives = new ArrayList<SencondAnalysisResult>();
protected final List<SencondAnalysisResult> computer3HalfAlives = new ArrayList<SencondAnalysisResult>();

//第二次分结果,人类
protected final List<SencondAnalysisResult> human4Alives = new ArrayList<SencondAnalysisResult>(2);
protected final List<SencondAnalysisResult> human4HalfAlives = new ArrayList<SencondAnalysisResult>(5);
protected final List<SencondAnalysisResult> humanDouble3Alives = new ArrayList<SencondAnalysisResult>(2);
protected final List<SencondAnalysisResult> human3Alives = new ArrayList<SencondAnalysisResult>(10);
protected final List<SencondAnalysisResult> humanDouble2Alives = new ArrayList<SencondAnalysisResult>(3);
protected final List<SencondAnalysisResult> human2Alives = new ArrayList<SencondAnalysisResult>();
protected final List<SencondAnalysisResult> human3HalfAlives = new ArrayList<SencondAnalysisResult>();

//第一次分析前清空上一步棋子的分析结果
private void initAnalysisResults(){
 computerFirstResults.clear();
 humanFirstResults.clear();
 //第二次总结果
 computerSencodResults.clear();
 humanSencodResults.clear();
 //第二次分结果
 computer4HalfAlives.clear();
 computerDouble3Alives.clear();
 computer3Alives.clear();
 computerDouble2Alives.clear();
 computer2Alives.clear();
 computer3HalfAlives.clear();

//第二次分结果,人类
 human4Alives.clear();
 human4HalfAlives.clear();
 humanDouble3Alives.clear();
 human3Alives.clear();
 humanDouble2Alives.clear();
 human2Alives.clear();
 human3HalfAlives.clear();
 System.gc();
}

//加入到第一次分析结果中
private void addToFirstAnalysisResult(FirstAnalysisResult result,Map<Point,List<FirstAnalysisResult>> dest){
 if(dest.containsKey(result.point)){
  dest.get(result.point).add(result);
 }else{
  List<FirstAnalysisResult> list = new ArrayList<FirstAnalysisResult>(1);
  list.add(result);
  dest.put(result.point, list);
 }
}

//第一次分析结果类
private class FirstAnalysisResult{
 //连续数
 int count;
 //点位
 Point point;
 //方向
 int direction;
 //状态
 int aliveState;
 private FirstAnalysisResult(int count, Point point, int direction) {
  this(count, point, direction, ALIVE);
 }

private FirstAnalysisResult(int count, Point point, int direction,int aliveState) {
  this.count = count;
  this.point = point;
  this.direction = direction;
  this.aliveState = aliveState;
 }

private FirstAnalysisResult init(Point point,int direction,int aliveState){
  this.count = 1;
  this.point = point;
  this.direction = direction;
  this.aliveState = aliveState;
  return this;
 }

private FirstAnalysisResult cloneMe(){
  return new FirstAnalysisResult(count, point, direction,aliveState);
 }

}

//第二次分析结果类
class SencondAnalysisResult implements Comparable<SencondAnalysisResult>{
 int alive4 = 0;
 //活3数量
 int alive3 = 0;
 //半活4,一头封的
 int halfAlive4 = 0;
 //半活3,一头封的
 int halfAlive3 = 0;
 //活2数量
 int alive2 = 0;
 //点位
 Point point;

@Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((point == null) ? 0 : point.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  SencondAnalysisResult other = (SencondAnalysisResult) obj;
  if (point == null) {
   if (other.point != null)
    return false;
  } else if (!point.equals(other.point))
   return false;
  return true;
 }

private SencondAnalysisResult(Point point) {
  this.point = point;
 }

//第三次分析时,对第二次分析结果进行排序,此为排序回调函数
 @Override
 public int compareTo(SencondAnalysisResult another) {
  return compareTowResult(this, another);
 }

}

//返加-1则第一个参数优先,1则第二个参数优先,0则按原来顺序
private int compareTowResult(SencondAnalysisResult oneResult,SencondAnalysisResult another){
 if(oneResult.alive4>another.alive4){
  return -1;
 }
 if(oneResult.alive4<another.alive4){
  return 1;
 }
 if(oneResult.halfAlive4>another.halfAlive4){
  return -1;
 }
 if(oneResult.halfAlive4<another.halfAlive4){
  return 1;
 }
 if(oneResult.alive3>another.alive3){
  return -1;
 }
 if(oneResult.alive3<another.alive3){
  return 1;
 }
 if(oneResult.alive2>another.alive2){
  return -1;
 }
 if(oneResult.alive2<another.alive2){
  return 1;
 }
 if(oneResult.halfAlive3>another.halfAlive3){
  return -1;
 }
 if(oneResult.halfAlive3>another.halfAlive3){
  return 1;
 }
 return 0;
}

//一个临时对象,供第一次分析时临时存放分析结果使用,如果分析出有活1以上(不含)的结果,则调用其cloneMe方法获得结果,否则抛弃此结果
private final FirstAnalysisResult far = new FirstAnalysisResult(1, null, HENG);
// 分析如果在当前位下一子,会形成某个方向上多少个子,参数:当前己方已下的所有点,当前要假设的点,需要判断的方向
private FirstAnalysisResult tryAndCountResult(List<Point> myPoints,List<Point> enemyPoints, Point point,int direction) {
 int x = point.getX();
 int y = point.getY();
 FirstAnalysisResult fr = null;

int maxCountOnThisDirection = maxCountOnThisDirection(point, enemyPoints, direction, 1);
 if(maxCountOnThisDirection<5){
  //无意义的棋子
  return null;//此方向不足五个空位,已排除己方已下的棋子
 }else if(maxCountOnThisDirection==5){
  //半死状态,当是一头通
  fr = far.init(point, direction,HALF_ALIVE);
 }else{
  //两头皆通
  fr = far.init(point, direction,ALIVE);
 }

//在前和后的方向上计算一次
 countPoint(myPoints,enemyPoints,point.setX(x).setY(y),fr,direction,FORWARD);
 countPoint(myPoints,enemyPoints,point.setX(x).setY(y),fr,direction,BACKWARD);

if(fr.count<=1 || (fr.count==2 && fr.aliveState==HALF_ALIVE)){//活1,半活2及其以下结果,抛弃
  return null;
 }
 //返回复制的结果
 return fr.cloneMe();
}

//棋子出了墙
private boolean isOutSideOfWall(Point point,int direction){
 if(direction==HENG){
  return point.getX()<0 || point.getX()>=maxX;//最大的X和Y值均在墙外所以用等号
 }else if(direction==ZHONG){
  return point.getY()<0 || point.getY()>=maxY;
 }else{//这里可能有问题
  return point.getX()<0 || point.getY()<0 || point.getX()>=maxX || point.getY()>=maxY;
 }
}

private Point pointToNext(Point point,int direction,boolean forward){
 switch (direction) {
  case HENG:
   if(forward)
    point.x++;
   else
    point.x--;
   break;
  case ZHONG:
   if(forward)
    point.y++;
   else
    point.y--;
   break;
  case ZHENG_XIE:
   if(forward){
    point.x++;
    point.y--;
   }else{
    point.x--;
    point.y++;
   }
   break;
  case FAN_XIE:
   if(forward){
    point.x++;
    point.y++;
   }else{
    point.x--;
    point.y--;
   }
   break;
 }
 return point;
}

//在某个方向(八个中的一个)可下多少棋子,这个方法是第一分析中的核心方法
private void countPoint(List<Point> myPoints, List<Point> enemyPoints, Point point, FirstAnalysisResult fr,int direction,boolean forward) {
 if(myPoints.contains(pointToNext(point,direction,forward))){
  fr.count ++;
  if(myPoints.contains(pointToNext(point,direction,forward))){
   fr.count ++;
   if(myPoints.contains(pointToNext(point,direction,forward))){
    fr.count ++;
    if(myPoints.contains(pointToNext(point,direction,forward))){
     fr.count ++;
    }else if(enemyPoints.contains(point) || isOutSideOfWall(point,direction)){
     fr.aliveState=HALF_ALIVE;
    }
   }else if(enemyPoints.contains(point) || isOutSideOfWall(point,direction)){
    fr.aliveState=HALF_ALIVE;
   }
  }else if(enemyPoints.contains(point) || isOutSideOfWall(point,direction)){
   fr.aliveState=HALF_ALIVE;
  }
 }else if(enemyPoints.contains(point) || isOutSideOfWall(point,direction)){
  fr.aliveState=HALF_ALIVE;
 }
}

//在某个方向上是否还能下到满五个棋子
private int maxCountOnThisDirection(Point point,List<Point> enemyPoints,int direction,int count){
 int x=point.getX(),y=point.getY();
 switch (direction) {
 //横向
 case HENG:
  while (!enemyPoints.contains(point.setX(point.getX()-1)) && point.getX()>=0 && count<6) {
   count ++;
  }
  point.setX(x);
  while (!enemyPoints.contains(point.setX(point.getX()+1)) && point.getX()<maxX && count<6) {
   count ++;
  }
  break;
 //纵向
 case ZHONG:
  while (!enemyPoints.contains(point.setY(point.getY()-1)) && point.getY()>=0) {
   count ++;
  }
  point.setY(y);
  while (!enemyPoints.contains(point.setY(point.getY()+1)) && point.getY()<maxY && count<6) {
   count ++;
  }
  break;
 //正斜向 /
 case ZHENG_XIE:
  while (!enemyPoints.contains(point.setX(point.getX()-1).setY(point.getY()+1)) && point.getX()>=0 && point.getY()<maxY) {
   count ++;
  }
  point.setX(x).setY(y);
  while (!enemyPoints.contains(point.setX(point.getX()+1).setY(point.getY()-1)) && point.getX()<maxX && point.getY()>=0 && count<6) {
   count ++;
  }
  break;
 //反斜 /
 case FAN_XIE:
  while (!enemyPoints.contains(point.setX(point.getX()-1).setY(point.getY()-1)) && point.getX()>=0 && point.getY()>=0) {
   count ++;
  }
  point.setX(x).setY(y);
  while (!enemyPoints.contains(point.setX(point.getX()+1).setY(point.getY()+1)) && point.getX()<maxX && point.getY()<maxY && count<6) {
   count ++;
  }
  break;
 }
 return count;
}

//下棋子,对外接口
@Override
public void run(List<Point> humans,Point p) {
 //把人类下的最后一步棋子去除
 allFreePoints.remove(humans.get(humans.size()-1));
 //电脑可以下的一步棋子
 Point result = doAnalysis(myPoints, humans);
 //去除电脑下的棋子
 allFreePoints.remove(result);
 //加入到电脑棋子中,下棋了
 myPoints.add(result);
}
}

人类玩家实现起来就非常简单


import java.util.List;

public class HumanPlayer extends BasePlayer {

@Override
public void run(List<Point> enemyPoints,Point p) {
 getMyPoints().add(p);
 allFreePoints.remove(p);
}
}

总结:虽然是Java写的但算法已被抽象可以方便的修改成各种平台的实现。

来源:https://blog.csdn.net/xiaoyu714543065/article/details/8746876

标签:Java,五子棋,AI
0
投稿

猜你喜欢

  • Spring Cloud Gateway不同频率限流的解决方案(每分钟,每小时,每天)

    2023-01-05 13:49:34
  • JAVA中使用FTPClient实现文件上传下载实例代码

    2021-08-17 20:45:05
  • android: targetSdkVersion升级中Only fullscreen activities can request orientation问题的解决方法

    2023-07-26 13:56:08
  • springboot @Async 注解如何实现方法异步

    2023-11-18 18:16:03
  • Spring Boot Admin实践详解

    2023-08-25 06:57:53
  • Java 单例模式的实现资料整理

    2022-05-29 21:27:33
  • jsp、struts、spring、mybatis实现前端页面功能模块化拆分的方案

    2023-11-25 07:38:00
  • 基于JAVA文件中获取路径及WEB应用程序获取路径的方法

    2022-08-21 01:26:57
  • JAVA基础之一些不为人知的那些秘密

    2023-11-29 07:49:29
  • 详解Spring Bean的集合注入和自动装配

    2023-02-18 15:02:58
  • Java5种遍历HashMap数据的写法

    2022-09-27 15:43:27
  • 浅谈virtual、abstract方法和静态方法、静态变量理解

    2022-08-29 02:29:14
  • SpringBoot2.0解决Long型数据转换成json格式时丢失精度问题

    2022-10-31 16:56:24
  • SpringCloud Feign转发请求头(防止session失效)的解决方案

    2022-08-29 12:25:59
  • Java 非阻塞I/O使用方法

    2022-01-01 22:54:57
  • feign实现传递参数的三种方式小结

    2021-06-18 00:09:53
  • 深入理解java内置锁(synchronized)和显式锁(ReentrantLock)

    2023-11-19 00:10:57
  • Java基于Tcp的基础聊天功能实例

    2023-11-25 05:26:56
  • java获取网络图片上传到OSS的方法

    2023-10-14 23:01:07
  • Springboot 如何指定获取出 yml文件里面的配置值

    2022-08-29 21:04:48
  • asp之家 软件编程 m.aspxhome.com