目次
I.はじめに
私は最近、人工知能のコースを受講し、A* アルゴリズムを学び、C++ を使用してこのアルゴリズムを実装しようとしました。
経路探索プロセスをより便利に反映するために、ここでは easyx 画像ライブラリを使用して移動プロセスをリアルタイムに表示します。easyx の公式 Web サイトにアクセスして、このライブラリをダウンロードできます。easyx公式サイト
マルチバイト文字セットを使用するには、[デバッグ] - [デバッグ プロパティ] - [構成プロパティ] - [詳細] - [文字セット] で easyx ライブラリを使用するのが最善です。そうしないと、一部の関数でエラーが報告される可能性があります。
2.迷路(障害物)の生成について
1. 簡単な説明
まず、2 次元配列を使用して迷路を保存できます。迷路のサイズはユーザーが入力します。壁には 1 を使用し、道路には 0 を使用します。
初期化を開始するには、迷路配列を次の状態に初期化する必要があります。
配列 (1, 1) を開始点として、その隣にパスがあるかどうかを探し始め、存在する場合はランダムにパスを開き、再帰のために現在の座標を記録します。関数 exit は、通過する場所がない場合に直接戻ることを意味します。移動したパスは通過済みとしてマークする必要があります。そうしないと、無限ループが発生し、プログラムが正しくなくなる可能性があります。ここで移動したパスは 5 としてマークされます。
この手順を完了したら、前の配列内のすべての 5 を 0、つまりパスに変更する必要があります。
もちろん、このアルゴリズムによって生成される迷路の品質はあまり良くありませんが、現時点で使用するには十分です。
もちろん、A* アルゴリズムを使用する場合、迷路は必要ありません。必要なのはいくつかの障害物だけです。上記の迷路の壁の一部をランダムに削除する別の関数を作成しましょう。これで、障害物を生成するこのステップは完了です。
2. コード
迷路を格納する構造物
struct AStar {
int M; //M*M map
int** maze; //map迷宫
//int* close; //close表
//int* open; //open表
int O_row; //开始的行
int O_col; //开始的列
int E_row; //终点行
int E_col; //终点列
}Astar{ 0,NULL,1,1,0,0 };
迷路を初期化する
int main()
{
Astar.M = 0;
cout << "请输入迷宫是几乘几的" << endl;
cin >> Astar.M;
if (Astar.M % 2 == 0)//如果输入不为奇数变为奇数
Astar.M++;
Astar.E_col = Astar.M;
Astar.E_row = Astar.M;
Astar.maze = new int* [Astar.M + 2];//动态创建二维数组
for (int i = 0; i < Astar.M + 2; i++)
Astar.maze[i] = new int[Astar.M + 2];//每一行申请一个int空间的M+2列空间
}
//初始化迷宫
void Init_Maze(AStar& Astar)
{
for (int i = 0; i < Astar.M + 2; i++)//动态创建迷宫
for (int j = 0; j < Astar.M + 2; j++)
{
if (i == 0 || i == Astar.M + 1)//1为墙壁
Astar.maze[i][j] = 1;
if ((i % 2 == 1) && (j % 2 == 1))
Astar.maze[i][j] = 0;
else Astar.maze[i][j] = 1;
}
}
隣人を探しています
bool findneighbor(AStar& Astar, int x_index, int y_index)
{
if ((x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0) || (x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
|| (y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0) || (y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0))
return 1;
else
return 0;
}
迷路を作る
//随机创建迷宫
void creatMaze(AStar& Astar, int x_index, int y_index)
{
int pos, x, y, flag = 0;
x = x_index;
y = y_index;
while (1)
{
flag = 0;
flag = findneighbor(Astar, x, y);
if (!flag)
return;
else {
Astar.maze[x_index][y_index] = 5;
x = x_index;
y = y_index;
while (1)
{
pos = rand() % (4 - 1 + 1) + 1;
if (pos == 1 && x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0)//上
{
x_index -= 2;
}
else if (pos == 2 && x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)//下
{
x_index += 2;
}
else if (pos == 3 && y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0)
{
y_index += 2;
}
else if (pos == 4 && y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0)
{
y_index -= 2;
}
Astar.maze[(x + x_index) / 2][(y + y_index) / 2] = 5;
Astar.maze[x_index][y_index] = 5;
// showmaze(maze, M);
creatMaze(Astar, x_index, y_index);
break;
}
}
}
}
配列内のパスを変更し、入口と出口を識別します
//给创建的迷宫创建标识
void makeMaze(AStar& Astar)
{
for (int i = 0; i < Astar.M + 2; i++)
for (int j = 0; j < Astar.M + 2; j++)
{
if (Astar.maze[i][j] == 5)
{
Astar.maze[i][j] = 0;
}
}
Astar.maze[1][1] = 8;
Astar.maze[Astar.M][Astar.M] = 2;
}
地形を変える
//改变地形,出现更多通路
void MakeDifficult(AStar& Astar)
{
if (Astar.M > 5)
{
int half = (int)((0.4 * Astar.M) * (0.5 * Astar.M));
int x_c, y_c = 0;
for (int i = 0; i < half;)
{
x_c = rand() % (Astar.M - 1 + 1) + 1;
y_c = rand() % (Astar.M - 1 + 1) + 1;
if (Astar.maze[x_c][y_c] == 1)
{
Astar.maze[x_c][y_c] = 0;
i++;
}
}
}
}
迷路を見せる
//图像显示迷宫
//3为路,4为搜寻的路,1为墙,2为终点,8为开始
void show(AStar& Astar)
{
IMAGE star, wall, ball ,ly;
loadimage(&star, "./五角星.png", SIZE, SIZE);
loadimage(&wall, "./墙.png", SIZE, SIZE);
loadimage(&ball, "./球.png", SIZE, SIZE);
loadimage(&ly, "./兰音.png", SIZE, SIZE);
for (int i = 0; i < Astar.M + 2; i++)
{
for (int j = 0; j < Astar.M + 2; j++)
{
if (Astar.maze[i][j] == 1)
{
putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &wall);
putimage(j * SIZE, i * SIZE, &wall);
}
else if (Astar.maze[i][j] == 4)
{
putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &ball);
}
else if (Astar.maze[i][j] == 3)
{
putimage(j * SIZE, (i)*SIZE, &ly);
}
}
}
putimage((Astar.M + 2 + 1 + 1) * SIZE, (SIZE), &star);
putimage((Astar.M + Astar.M + 2 + 1) * SIZE, (Astar.M) * SIZE, &star);
putimage(SIZE, SIZE, &star);
putimage(Astar.M * SIZE, Astar.M * SIZE, &star);
}
この時点で迷路や障害物の作成が完了し、このときの迷路が迷路の2次元配列に保存されます。
3.A*アルゴリズム
1. 簡単な説明
開始点は次の図であり、開始点は左上隅、終了点は右下隅であると仮定します。A* アルゴリズムの焦点は、評価関数 F を設定するプロセスです。
F=G+H。ここで、G は開始ノードから現在のノードまでのコスト、H は現在のノードからターゲット ノードまでのコストです。A* アルゴリズムは、G が 0 より大きく、H が実際のコストを超えてはいけないという A アルゴリズムを改良したものです。
G は開始点から現在のノードまでのステップ数、H はマンハッタン距離 (つまり、|xe-xi|+|ye-yi|、xe、ye は終点の座標 xi、yi です) であると仮定します。は現在点の座標), 便宜上、左右上下にしか歩けず、横には歩けないと仮定しますので、上記のFは次のようになります。開始点の F 値は最大である必要があります。
次に、A* アルゴリズムが最小の F を見つけて上に進み、親ノードを記録します。専門的に言えば、現在のノードに行くことができるすべてのノードの F 値を計算し、そのノードをオープン テーブルに入れ、次に F 値が最も小さいノードを見つけて、それをクローズ テーブルに入れることです。ここでは、オープンをツリーとして、クローズを配列として表すことができます。
もちろん、4 つの歩行方向が事前に決定されていることが前提であり、その後、木に最後に入るコストが最も低いノードを探すたびに、上に向かって歩き、後続のノードを探し続けます。対応する参照フレーム図も人工知能の本に記載されています。
2. 効果実証
3. コード
int main()
{
treeNode* pRoot = NULL; //准备一棵树
vector<treeNode*> open; //准备一个数组
vector<treeNode*>::iterator it; //迭代器
vector<treeNode*>::iterator itmin; //迭代器
pRoot = creatTreeNode(Astar.O_row, Astar.O_col); //起点入树
treeNode* Now = pRoot;
pRoot->pos.F = Astar.M * Astar.M;
open.push_back(pRoot);
while (1)
{
//1.把当前能走的点找出来
for (int i = 0; i < 4; i++)
{
treeNode* pChild = creatTreeNode(Now->pos.row, Now->pos.col);
switch (i)
{
case Up:
pChild->pos.row--;
pChild->pos.g = Now->pos.g + 1;
break;
case Down:
pChild->pos.row++;
pChild->pos.g = Now->pos.g + 1;
break;
case Left:
pChild->pos.col--;
pChild->pos.g = Now->pos.g + 1;
break;
case Right:
pChild->pos.col++;
pChild->pos.g = Now->pos.g + 1;
break;
default:
break;
}
//2.判断能不能走
if (Astar.maze[pChild->pos.row][pChild->pos.col] == 0 || Astar.maze[pChild->pos.row][pChild->pos.col] == 8
|| Astar.maze[pChild->pos.row][pChild->pos.col] == 2) //如果是路则计算F
{
//标记走过
Astar.maze[pChild->pos.row][pChild->pos.col] = 4;
pChild->pos.h = getH(pChild->pos, Astar);//计算H
pChild->pos.F = pChild->pos.g + pChild->pos.h;//计算F
//入树
Now->child.push_back(pChild);
pChild->parent = Now;
//存入数组
open.push_back(pChild);
}
else {
delete pChild;
}
}
//3.找出数组最小的点,走上去
//cout << open.size() << endl;
itmin = open.begin();//假设第一个F最小 时间复杂度O(n)
for (it = open.begin(); it != open.end(); it++)
{
itmin = ((*it)->pos.F <= (*itmin)->pos.F) ? it : itmin; //细节 加上等于
}
//sort(open.begin(), open.end(),cmp);
//itmin = open.begin();
Now = *itmin;
//标记走过
//Astar.maze[Now->pos.row][Now->pos.col] = -1;
show(Astar);
//删除
open.erase(itmin);
//4.判断是否找到终点
if (Now->pos.row == Astar.E_row && Now->pos.col == Astar.E_col)
{
flag = 1;
break;
}
//5.如果为数组为空也退出
if (0 == open.size()) break;
}
//成功则打印
if (flag)
{
cout << "success" << endl;
while (Now)
{
cout << "(" << Now->pos.row << "," << Now->pos.col << ")";
printf("(g:%d,h:%d,f:%d)\n", Now->pos.g, Now->pos.h, Now->pos.F);
Astar.maze[Now->pos.row][Now->pos.col] = 3;
Now = Now->parent;
}
}
else
{
cout << "not found" << endl;
}
while (1)
{
BeginBatchDraw();
show(Astar);
FlushBatchDraw();
}//循环绘图防止BUG
closegraph();
}
4、合計コード
#include<iostream>
#include<iomanip>
#include<graphics.h>
#include<time.h>
#include<easyx.h>
#include<algorithm>
#include<vector>
using namespace std;
//1为墙壁,0为路
#define SIZE 10
bool flag = 0;
enum dirct {
Up,
Down,
Left,
Right,
};
struct AStar {
int M; //M*M map
int** maze; //map迷宫
//int* close; //close表
//int* open; //open表
int O_row; //开始的行
int O_col; //开始的列
int E_row; //终点行
int E_col; //终点列
}Astar{ 0,NULL,1,1,0,0 };
struct position {
int row, col;
int g; //路径代价 行走的步数
int h; //启发函数 曼哈顿距离
int F = h + g; //估价函数
};
struct treeNode { //close类似
position pos;
vector<treeNode*> child;
treeNode* parent;
};
treeNode* creatTreeNode(int row, int col); //创建树节点
int getH(position pos, AStar a); //计算H
void Init_Maze(AStar& Astar); //初始化迷宫
void show(AStar& Astar); //图像显示迷宫
void creatMaze(AStar& Astar, int x_index, int y_index); //随机创建迷宫
void makeMaze(AStar& Astar); //给创建的迷宫创建标识
bool findneighbor(AStar& Astar, int x_index, int y_index); //寻找邻居
void MakeDifficult(AStar& Astar); //改变地形,出现更多通路
void ConsoleShow(AStar& Astar); //控制台显示迷宫
///*排序方式*/
//bool cmp(treeNode* m1, treeNode* m2)
//{
// return m1->pos.F < m2->pos.F;
//}
int main()
{
treeNode* pRoot = NULL; //准备一棵树
vector<treeNode*> open; //准备一个数组
vector<treeNode*>::iterator it; //迭代器
vector<treeNode*>::iterator itmin; //迭代器
Astar.M = 0;
cout << "请输入迷宫是几乘几的" << endl;
cin >> Astar.M;
if (Astar.M % 2 == 0)//如果输入不为奇数变为奇数
Astar.M++;
Astar.E_col = Astar.M;
Astar.E_row = Astar.M;
pRoot = creatTreeNode(Astar.O_row, Astar.O_col); //起点入树
srand((unsigned)time(0));//随机播种
initgraph(((Astar.M + 2) * 2 + 1) * SIZE, (Astar.M + 2) * SIZE, SHOWCONSOLE);//打开绘图
Astar.maze = new int* [Astar.M + 2];//动态创建二维数组
for (int i = 0; i < Astar.M + 2; i++)
Astar.maze[i] = new int[Astar.M + 2];//每一行申请一个int空间的M+2列空间
Init_Maze(Astar);
creatMaze(Astar, 1, 1);
makeMaze(Astar);
MakeDifficult(Astar);
//show(Astar);
treeNode* Now = pRoot;
pRoot->pos.F = Astar.M * Astar.M;
open.push_back(pRoot);
while (1)
{
//1.把当前能走的点找出来
for (int i = 0; i < 4; i++)
{
treeNode* pChild = creatTreeNode(Now->pos.row, Now->pos.col);
switch (i)
{
case Up:
pChild->pos.row--;
pChild->pos.g = Now->pos.g + 1;
break;
case Down:
pChild->pos.row++;
pChild->pos.g = Now->pos.g + 1;
break;
case Left:
pChild->pos.col--;
pChild->pos.g = Now->pos.g + 1;
break;
case Right:
pChild->pos.col++;
pChild->pos.g = Now->pos.g + 1;
break;
default:
break;
}
//2.判断能不能走
if (Astar.maze[pChild->pos.row][pChild->pos.col] == 0 || Astar.maze[pChild->pos.row][pChild->pos.col] == 8
|| Astar.maze[pChild->pos.row][pChild->pos.col] == 2) //如果是路则计算F
{
//标记走过
Astar.maze[pChild->pos.row][pChild->pos.col] = 4;
pChild->pos.h = getH(pChild->pos, Astar);//计算H
pChild->pos.F = pChild->pos.g + pChild->pos.h;//计算F
//入树
Now->child.push_back(pChild);
pChild->parent = Now;
//存入数组
open.push_back(pChild);
}
else {
delete pChild;
}
}
//3.找出数组最小的点,走上去
//cout << open.size() << endl;
itmin = open.begin();//假设第一个F最小 时间复杂度O(n)
for (it = open.begin(); it != open.end(); it++)
{
itmin = ((*it)->pos.F <= (*itmin)->pos.F) ? it : itmin; //细节 加上等于
}
//sort(open.begin(), open.end(),cmp);
//itmin = open.begin();
Now = *itmin;
//标记走过
//Astar.maze[Now->pos.row][Now->pos.col] = -1;
show(Astar);
//删除
open.erase(itmin);
//4.判断是否找到终点
if (Now->pos.row == Astar.E_row && Now->pos.col == Astar.E_col)
{
flag = 1;
break;
}
//5.如果为数组为空也退出
if (0 == open.size()) break;
}
//成功则打印
if (flag)
{
cout << "success" << endl;
while (Now)
{
cout << "(" << Now->pos.row << "," << Now->pos.col << ")";
printf("(g:%d,h:%d,f:%d)\n", Now->pos.g, Now->pos.h, Now->pos.F);
Astar.maze[Now->pos.row][Now->pos.col] = 3;
Now = Now->parent;
}
}
else
{
cout << "not found" << endl;
}
while (1)
{
BeginBatchDraw();
show(Astar);
FlushBatchDraw();
}//循环绘图防止BUG
closegraph();
}
//初始化迷宫
void Init_Maze(AStar& Astar)
{
for (int i = 0; i < Astar.M + 2; i++)//动态创建迷宫
for (int j = 0; j < Astar.M + 2; j++)
{
if (i == 0 || i == Astar.M + 1)//1为墙壁
Astar.maze[i][j] = 1;
if ((i % 2 == 1) && (j % 2 == 1))
Astar.maze[i][j] = 0;
else Astar.maze[i][j] = 1;
}
}
//图像显示迷宫
//3为路,4为搜寻的路,1为墙,2为终点,8为开始
void show(AStar& Astar)
{
IMAGE star, wall, ball ,ly;
loadimage(&star, "./五角星.png", SIZE, SIZE);
loadimage(&wall, "./墙.png", SIZE, SIZE);
loadimage(&ball, "./球.png", SIZE, SIZE);
loadimage(&ly, "./兰音.png", SIZE, SIZE);
for (int i = 0; i < Astar.M + 2; i++)
{
for (int j = 0; j < Astar.M + 2; j++)
{
if (Astar.maze[i][j] == 1)
{
putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &wall);
putimage(j * SIZE, i * SIZE, &wall);
}
else if (Astar.maze[i][j] == 4)
{
putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &ball);
}
else if (Astar.maze[i][j] == 3)
{
putimage(j * SIZE, (i)*SIZE, &ly);
}
}
}
putimage((Astar.M + 2 + 1 + 1) * SIZE, (SIZE), &star);
putimage((Astar.M + Astar.M + 2 + 1) * SIZE, (Astar.M) * SIZE, &star);
putimage(SIZE, SIZE, &star);
putimage(Astar.M * SIZE, Astar.M * SIZE, &star);
}
//寻找邻居
bool findneighbor(AStar& Astar, int x_index, int y_index)
{
if ((x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0) || (x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
|| (y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0) || (y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0))
return 1;
else
return 0;
}
//随机创建迷宫
void creatMaze(AStar& Astar, int x_index, int y_index)
{
int pos, x, y, flag = 0;
x = x_index;
y = y_index;
while (1)
{
flag = 0;
flag = findneighbor(Astar, x, y);
if (!flag)
return;
else {
Astar.maze[x_index][y_index] = 5;
x = x_index;
y = y_index;
while (1)
{
pos = rand() % (4 - 1 + 1) + 1;
if (pos == 1 && x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0)//上
{
x_index -= 2;
}
else if (pos == 2 && x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)//下
{
x_index += 2;
}
else if (pos == 3 && y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0)
{
y_index += 2;
}
else if (pos == 4 && y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0)
{
y_index -= 2;
}
Astar.maze[(x + x_index) / 2][(y + y_index) / 2] = 5;
Astar.maze[x_index][y_index] = 5;
// showmaze(maze, M);
creatMaze(Astar, x_index, y_index);
break;
}
}
}
}
//给创建的迷宫创建标识
void makeMaze(AStar& Astar)
{
for (int i = 0; i < Astar.M + 2; i++)
for (int j = 0; j < Astar.M + 2; j++)
{
if (Astar.maze[i][j] == 5)
{
Astar.maze[i][j] = 0;
}
}
Astar.maze[1][1] = 8;
Astar.maze[Astar.M][Astar.M] = 2;
}
//改变地形,出现更多通路
void MakeDifficult(AStar& Astar)
{
if (Astar.M > 5)
{
int half = (int)((0.4 * Astar.M) * (0.5 * Astar.M));
int x_c, y_c = 0;
for (int i = 0; i < half;)
{
x_c = rand() % (Astar.M - 1 + 1) + 1;
y_c = rand() % (Astar.M - 1 + 1) + 1;
if (Astar.maze[x_c][y_c] == 1)
{
Astar.maze[x_c][y_c] = 0;
i++;
}
}
}
}
//计算H
int getH(position pos, AStar a) {
return (a.E_row - pos.row) + (a.E_col - pos.col);
}
//创建树节点
treeNode* creatTreeNode(int row, int col)
{
treeNode* pNew = new treeNode;//开内存
memset(pNew, 0, sizeof(treeNode)); //全部赋值为0
pNew->pos.col = col;
pNew->pos.row = row;
return pNew;
}
//控制台显示迷宫
void ConsoleShow(AStar& Astar)
{
cout << endl << "3为走出迷宫的路,1为墙壁,2为出口" << endl;;
for (int i = 0; i < Astar.M + 2; i++)
{
for (int j = 0; j < Astar.M + 2; j++)
{
cout << setw(3) << Astar.maze[i][j];
}
cout << endl;
}
}
V. まとめ
このコードにはまだ説明できないバグがいくつかありますが、変更することはできません。皆さんのお役に立てれば幸いです。