参考文章:
【精选】人工智能 A*算法 八数码 Java_人工智能八数码问题java_硝酸童不酸的博客-CSDN博客
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
public class EightNum implements Comparable {
int[] number = new int[9];
int f;//估计函数
int d;//实际代价,走到当前状态的步数
int h;//估计代价,当前状态和目标状态有多少个数不同
EightNum parent;//记录当前状态的父状态
ArrayList result = new ArrayList<>();//保存最终路径
//目标状态的可达性判断,通过判断两个状态的逆序数是否相同来判断
//注意:需要排除0
public boolean isSolvable(EightNum target) {
int num = 0;
for (int i=1; i<9; i++)
for (int j=0; jnumber[i] && number[j]!=0 && number[i]!=0)
num++;
if (target.number[j]>target.number[i] && target.number[j]!=0 && number[i]!=0)
num++;
}
//如果能被2整除,说明同奇或者同偶
return num % 2==0;
}
//对状态进行初始化,计算估价函数
public void init(EightNum target) {
int sum=0;
for (int i=0; i<9; i++)
if (number[i]!=target.number[i])
sum++; //记录当前状态和目标状态不同的个数
this.h = sum;
if(this.parent==null) //如果没有父状态
this.d=0;
else
this.d=this.parent.d+1;//实际代价
this.f=this.d+this.h;//返回当前状态的估计值
}
public boolean isTarget(EightNum target) {
//判断当前状态是否是目标状态
return Arrays.equals(number, target.number);
}
//重写compareTo
@Override
public int compareTo(Object o) {
EightNum e = (EightNum) o;
return this.f - e.f;
}
//获取0的位置
public int getZeroPosition()
{
int position = -1;
for (int i=0; i<9; i++)
if (this.number[i]==0) position = i;
return position;
}
//判断该状态是否在表中,如果在,就返回位置
public int isContains(ArrayList arrayList) {
for (int i=0; i 2; //只要位置不是第一行即可 0 1 2
}
//判断能否下移
public boolean isMoveDown()
{
int position = getZeroPosition();
return position < 6; //只要位置不是最后一行即可 7 8 9
}
//判断能否左移
public boolean isMoveLeft()
{
int position = getZeroPosition();
return (position) % 3 != 0; //只要位置不是第一列即可
//0 4 7
}
//判断能否右移
public boolean isMoveRight()
{
int position = getZeroPosition();
return (position) % 3 != 2; //只要位置不是最后一列即可
//2 5 8
}
//——————————————————判断移动————————————————————————————
//实现移动
public EightNum moveUp(int move)
{
EightNum newState = new EightNum();//创建一个对象
newState.number = number.clone();//将当前状态赋值给新创建的对象
int zero = getZeroPosition();//记录0的位置
int position = 0;//记录移动后的位置
switch (move)
{
case 0://上移
position = zero - 3;
break;
case 1://下移
position = zero + 3;
break;
case 2://左移
position = zero - 1;
break;
case 3://右移
position = zero + 1;
break;
}
newState.number[zero] = number[position];
newState.number[position] = 0;
return newState;
}
//输出
public void output()
{
for (int i=0; i<9; i++)
if(i%3==2) System.out.println(this.number[i]);
else System.out.print(this.number[i]+" ");
}
//输出最终路径
public void printRoute() {
EightNum temp;
int count = -1;
temp = this;
System.out.println("————————最优方案步骤展示————————");
//放到result表中,方便输出
while(temp!=null)
{
result.add(temp);
temp = temp.parent; //从子状态逐步向上寻找父状态
count++;
}
//因为list里,子状态放在最前面,为了从父状态开始输出,因此从后向前输出
for (int i=result.size()-1; i>=0; i--)
{
System.out.println("第"+ (count-i) +"步:");
result.get(i).output();
}
System.out.println("最小移动步数:" + count);
}
public void operation(ArrayList open, ArrayList close, EightNum minF, EightNum target) {
if (this.isContains(close)==-1) //判断是否在close表
{
int position = this.isContains(open);//判断是否在open表,存在就返回其在open表中的位置
if (position==-1) //不在open表中
{
this.parent = minF;//链接,设置minF为该子状态的父状态
this.init(target);//计算估计函数
open.add(this);//放入open表
}
else //在open表中
{
if (this.d < open.get(position).d) //根据移动步数,小的覆盖大的
{
open.remove(position);
this.parent = minF;
this.init(target);
open.add(this);
}
}
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
//————————————————————————————初始化————————————————————————————
ArrayList open = new ArrayList<>(); //open表存放已经生成而未考察的节点
ArrayList close = new ArrayList<>(); //close表存放已经访问过的节点
EightNum start = new EightNum();//存放初始状态
EightNum target = new EightNum();//存放目标状态
int[] sNum = new int[9];//初始状态
int[] tNum = new int[9];//目标状态
System.out.println("请输入初始棋盘!(空处输入0)");
for(int i=0;i<9;i++) sNum[i]=sc.nextInt();
System.out.println("请输入目标棋盘!(空处输入0)");
for(int i=0;i<9;i++) tNum[i]=sc.nextInt();
start.number = sNum;
target.number = tNum;
System.out.println("初始状态:");
start.output();
System.out.println("目标状态:");
target.output();
//————————————————————————————初始化————————————————————————————
if(start.isSolvable(target)) //判断从初始状态到目标状态是否可达
{
start.init(target);//初始化,计算估价函数
open.add(start);//加入open表
while(!open.isEmpty()) //循环导出open表中的状态
{
Collections.sort(open); //根据f值对open表进行从小到大排序
EightNum minF = open.get(0); //取出最小的,也就是第0个
open.remove(0); //从open表移出
close.add(minF); //放入close表
//f最小的状态如果==目标状态 则输出
if(minF.isTarget(target)) //判断是否为目标状态
{
minF.printRoute(); //输出完整路径
break;
}
int move;
//由minF状态进行扩展并加入到open表
//0的位置上移之后的状态不在close和open中设定minF为父状态,并初始化f的估值函数
if(minF.isMoveUp())
{
move = 0;
EightNum up = minF.moveUp(move);
up.operation(open,close,minF,target);
}
//0的位置下移之后状态不在close和open中设定minF为其父状态,并初始化f(n)估值函数
if (minF.isMoveDown())
{
move = 1;
EightNum down = minF.moveUp(move);
down.operation(open, close, minF, target);
}
//0的位置左移之后状态不在close和open中设定minF为其父状态,并初始化f(n)估值函数
if (minF.isMoveLeft())
{
move = 2;
EightNum left = minF.moveUp(move);
left.operation(open, close, minF, target);
}
//0的位置右移之后状态不在close和open中设定minF为其父状态,并初始化f(n)估值函数
if (minF.isMoveRight())
{
move = 3;
EightNum right = minF.moveUp(move);
right.operation(open, close, minF, target);
}
}
}
else
System.out.println("目标状态不可达!");
}
}