[ 공 부 ]/[ 인 공 지 능 ]
A* 그래프 탐색을 이용한 하노이 탑 퍼즐 프로그램
HiStar__
2019. 10. 6. 22:46
하노이 탑 퍼즐
-
모든 경로를 탐색하여 그 경로를 토대로 구현한 간단한 하노이 탑 퍼즐
소 스 코 드
// n 디스크 하노이 탑 퍼즐 프로그램을 작성.
// - h(hat) >= 0, A*그래프 탐색으로 풀이
// - h(hat) = 0 보다 좋은 허용 가능한 함수를 사용하라.
// -제출물: 본인의 학번과 성명이 들어간 소스코드와 실행 캡처 화면
#include <iostream>
#include <vector>
#include <algorithm>
constexpr int MAX_LINE_COUNT{ 3 };
constexpr int MAX_DISK_COUNT{ 3 };
///////////////////////////////////////////////////
// Hanoi Tower Class
class Hanoi_Tower {
std::vector<int> left_line; // left
std::vector<int> middle_line; // middle
std::vector<int> right_line; // right
int h_count; // h(n)
int f_count; // f(n)
int g_count; // g(n)
public:
bool is_flag = false; // Create Graph
int my_Level; // Depth
int my_Number; // Vecter Index
int parent_Number; // Parent_Index
std::vector<int> child_Number; // Childe_Index
public:
Hanoi_Tower();
Hanoi_Tower(int);
bool IsLeftEmpty();
bool IsMiddleEmpty();
bool IsRightEmpty();
void Print_Hanoi_Tower() const;
void Set_f(int);
void Set_g();
void Heuristics(int);
void Move_Disk(int, int);
int Get_h();
int Get_f();
int Get_g();
bool IsDiskMove(int, int);
bool operator ==(const Hanoi_Tower& other) const;
bool operator <(const Hanoi_Tower& other) const;
~Hanoi_Tower();
};
Hanoi_Tower::Hanoi_Tower() {
//std::cout << "하노이 타워 생성" << std::endl;
}
Hanoi_Tower::~Hanoi_Tower() {
//std::cout << "하노이 타워 삭제" << std::endl;
}
Hanoi_Tower::Hanoi_Tower(int disk_count) {
//now_pointer = 0; // First Pointer
h_count = 0; // h(0)
f_count = 0; // f(0)
g_count = 0; // g(0)
for (int i = disk_count; i > 0; --i) left_line.emplace_back(i);
}
bool Hanoi_Tower::IsDiskMove(int m0, int m1) {
// [ left : 0 ] [ middle : 1 ] [ right : 2 ]
int mfm0;
int mtm1;
switch (m0)
{
case 0:
if (1 == m1) {
if (middle_line.empty()) return true;
mfm0 = left_line[left_line.size() - 1];
mtm1 = middle_line[middle_line.size() - 1];
if (mfm0 < mtm1) return true;
else return false;
}
else if (2 == m1) {
if (right_line.empty()) return true;
mfm0 = left_line[left_line.size() - 1];
mtm1 = right_line[right_line.size() - 1];
if (mfm0 < mtm1) return true;
else return false;
}
break;
case 1:
if (0 == m1) {
if (left_line.empty()) return true;
mfm0 = middle_line[middle_line.size() - 1];
mtm1 = left_line[left_line.size() - 1];
if (mfm0 < mtm1) return true;
else return false;
}
else if (2 == m1) {
if (right_line.empty()) return true;
mfm0 = middle_line[middle_line.size() - 1];
mtm1 = right_line[right_line.size() - 1];
if (mfm0 < mtm1) return true;
else return false;
}
break;
case 2:
if (0 == m1) {
if (left_line.empty()) return true;
mfm0 = right_line[right_line.size() - 1];
mtm1 = left_line[left_line.size() - 1];
if (mfm0 < mtm1) return true;
else return false;
}
else if (1 == m1) {
if (middle_line.empty()) return true;
mfm0 = right_line[right_line.size() - 1];
mtm1 = middle_line[middle_line.size() - 1];
if (mfm0 < mtm1) return true;
else return false;
}
break;
default:
break;
}
return true;
}
bool Hanoi_Tower::IsLeftEmpty() {
return left_line.empty();
}
bool Hanoi_Tower::IsMiddleEmpty() {
return middle_line.empty();
}
bool Hanoi_Tower::IsRightEmpty() {
return right_line.empty();
}
// Show Hanoi_Tower()
void Hanoi_Tower::Print_Hanoi_Tower() const {
std::cout << " ========================================== " << std::endl;
std::cout << " ID : " << my_Number << std::endl;
std::cout << " Level : " << my_Level << std::endl << std::endl;
std::cout << " Parent : " << parent_Number << std::endl;
std::cout << " Chlid : ";
for (const int& a : child_Number) std::cout << a << ", ";
std::cout << std::endl << std::endl;
std::cout << " Left : ";
for (const int& a : left_line) std::cout << a << " ";
std::cout << std::endl;
std::cout << " Middle : ";
for (const int& a : middle_line) std::cout << a << " ";
std::cout << std::endl;
std::cout << " Right : ";
for (const int& a : right_line) std::cout << a << " ";
std::cout << std::endl;
std::cout << " ========================================== " << std::endl;
}
// Heuristics Func - h(n)
// Comparison of purpose indexs
void Hanoi_Tower::Heuristics(int disk_count) {
int h = 0;
if (right_line.empty()) h = disk_count;
else {
for (int i = 0; i < right_line.size(); ++i) {
if (right_line[i] == disk_count - i) continue;
++h;
}
h += disk_count - right_line.size();
}
h_count = h;
}
// f(n)
void Hanoi_Tower::Set_f(int f_c) {
f_count = f_c;
Set_g();
}
// g(n) = f(n) + h(n)
void Hanoi_Tower::Set_g() {
g_count = h_count + f_count;
}
// Moving Disk [ 0 : left | 1 : middle | 2 : right ]
void Hanoi_Tower::Move_Disk(int m0, int m1) {
int last_pointer;
int last_index;
switch (m0) {
case 0:
if (0 == left_line.size()) {
std::cout << " Moving Disk m0 empty Error " << std::endl;
break;
}
last_pointer = left_line.size() - 1;
last_index = left_line[last_pointer];
left_line.erase(left_line.begin() + last_pointer);
switch (m1) {
case 0:
std::cout << "Moving Disk Same Error" << std::endl;
break;
case 1:
middle_line.emplace_back(last_index);
break;
case 2:
right_line.emplace_back(last_index);
break;
default:
std::cout << "Moving Disk m1 Error" << std::endl;
break;
}
break;
case 1:
if (0 == middle_line.size()) {
std::cout << " Moving Disk m0 empty Error " << std::endl;
break;
}
last_pointer = middle_line.size() - 1;
last_index = middle_line[last_pointer];
middle_line.erase(middle_line.begin() + last_pointer);
switch (m1) {
case 0:
left_line.emplace_back(last_index);
break;
case 1:
std::cout << "Moving Disk Same Error" << std::endl;
break;
case 2:
right_line.emplace_back(last_index);
break;
default:
std::cout << "Moving Disk m1 Error" << std::endl;
break;
}
break;
case 2:
if (0 == right_line.size()) {
std::cout << " Moving Disk m0 empty Error " << std::endl;
break;
}
last_pointer = right_line.size() - 1;
last_index = right_line[last_pointer];
right_line.erase(right_line.begin() + last_pointer);
switch (m1) {
case 0:
left_line.emplace_back(last_index);
break;
case 1:
middle_line.emplace_back(last_index);
break;
case 2:
std::cout << "Moving Disk Same Error" << std::endl;
break;
default:
std::cout << "Moving Disk m1 Error" << std::endl;
break;
}
break;
default:
std::cout << "Moving Disk m0 Error" << std::endl;
break;
}
}
bool Hanoi_Tower::operator ==(const Hanoi_Tower& other) const {
if (other.left_line != left_line) return false;
if (other.middle_line != middle_line) return false;
if (other.right_line != right_line) return false;
return true;
}
bool Hanoi_Tower::operator <(const Hanoi_Tower& other) const {
if (g_count == other.g_count) {
return my_Number < other.my_Number;
}
return g_count < other.g_count;
}
int Hanoi_Tower::Get_h() {
return h_count;
}
int Hanoi_Tower::Get_f() {
return f_count;
}
int Hanoi_Tower::Get_g() {
return g_count;
}
///////////////////////////////////////////////////
std::vector<Hanoi_Tower> Hanoi_Graph;
int id{ 0 };
//////////////////////////////////////////////////
// Func()
// Same Node Search
bool IsInGraph(Hanoi_Tower&);
bool IsLevelGraph_Child(int);
int GetNextNodeIndex();
void NewnNode(int pid, int m0, int m1);
void Make_node(int Pindex);
void Make_Graph(Hanoi_Tower start);
///////////////////////////////////////////////////
int main() {
int disk_count{ MAX_DISK_COUNT };
int max_move_count{ 1 };
std::cout << " ================= " << std::endl;
std::cout << " Disk Count : " << disk_count << std::endl;
// Max_Move_Count
for (int i = 0; i < disk_count; ++i) {
max_move_count *= 2;
}
max_move_count -= 1;
std::cout << " Max Move : " << max_move_count << std::endl;
std::cout << " ================= " << std::endl;
// Init HanoiTower Start
Hanoi_Tower Hanoi_n0{ disk_count };
Make_Graph(Hanoi_n0);
std::cout << " Hanoi_Graph Number of Cases : " << Hanoi_Graph.size() << std::endl;
// 1. 2.
// OpenList & CloseList
std::vector<Hanoi_Tower> OpenList;
std::vector<Hanoi_Tower> CloseList;
OpenList.emplace_back(Hanoi_Graph[0]);
//
int indexPointer{ 0 };
Hanoi_Tower NHanoi;
while (true) {
// 3.
if (OpenList.empty()) {
std::cout << " Empty OpenList Error " << std::endl;
break;
}
//
++indexPointer; // while 순서
// Move OpenList 0 index -> CloseList
NHanoi = OpenList[0];
CloseList.emplace_back(NHanoi);
OpenList.erase(OpenList.begin());
if ((0 == NHanoi.Get_h()) && (0 != NHanoi.my_Number)) {
std::cout << " Search Goal " << std::endl;
break;
}
for (const auto& a : NHanoi.child_Number) {
Hanoi_Graph[a].Set_f(indexPointer);
OpenList.emplace_back(Hanoi_Graph[a]);
}
std::sort(OpenList.begin(), OpenList.end());
}
Hanoi_Tower SHonoi{ NHanoi };
std::vector<Hanoi_Tower> FindHanoi;
while (true) {
if (SHonoi == Hanoi_Graph[0]) {
FindHanoi.emplace_back(SHonoi);
break;
}
FindHanoi.emplace_back(SHonoi);
SHonoi = Hanoi_Graph[SHonoi.parent_Number];
}
for (int i = FindHanoi.size() - 1; i >= 0; --i) {
std::cout << " =================== " << std::endl;
std::cout << " 순서 : " << FindHanoi.size() - i - 1 << std::endl;
FindHanoi[i].Print_Hanoi_Tower();
}
system("pause");
}
// Same Node Search
bool IsInGraph(Hanoi_Tower& co) {
for (const auto& a : Hanoi_Graph) {
if (a == co) return true;
}
return false;
}
// Level Child
bool IsLevelGraph_Child(int ml) {
for (const auto& a : Hanoi_Graph) {
if (a.my_Level != ml) continue;
if (!a.child_Number.empty()) return true;
}
return false;
}
int GetNextNodeIndex() {
for (const auto& a : Hanoi_Graph) {
if (true == a.is_flag) continue;
return a.my_Number;
}
return -1;
}
void NewnNode(int pid, int m0, int m1) {
Hanoi_Tower * node = new Hanoi_Tower;
*node = Hanoi_Graph[pid];
node->Move_Disk(m0, m1);
if (!IsInGraph(*node)) {
++id;
node->my_Number = id;
Hanoi_Graph[pid].child_Number.emplace_back(node->my_Number);
node->my_Level = Hanoi_Graph[pid].my_Level + 1;
node->parent_Number = pid;
Hanoi_Graph.emplace_back(*node);
Hanoi_Graph[id].is_flag = false;
Hanoi_Graph[id].child_Number.clear();
Hanoi_Graph[id].Heuristics(MAX_DISK_COUNT); // Setting h(n)
delete node;
}
else {
delete node;
}
}
void Make_node(int Pindex) {
Hanoi_Graph[Pindex].is_flag = true;
//std::cout << "MakeNode ID: " << Pindex << std::endl;
// 1EA
// Start [left : O] [middle : X] [right : X]
if ((!Hanoi_Graph[Pindex].IsLeftEmpty()) && ((Hanoi_Graph[Pindex].IsMiddleEmpty()) && (Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : O] [middle : X] [right : X] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(0, 1)) NewnNode(Pindex, 0, 1);
if (Hanoi_Graph[Pindex].IsDiskMove(0, 2)) NewnNode(Pindex, 0, 2);
}
// Start [left : X] [middle : O] [right : X]
if ((Hanoi_Graph[Pindex].IsLeftEmpty()) && ((!Hanoi_Graph[Pindex].IsMiddleEmpty()) && (Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : X] [middle : O] [right : X] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(1, 0)) NewnNode(Pindex, 1, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(1, 2)) NewnNode(Pindex, 1, 2);
}
// Start [left : X] [middle : X] [right : O]
if ((Hanoi_Graph[Pindex].IsLeftEmpty()) && ((Hanoi_Graph[Pindex].IsMiddleEmpty()) && (!Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : X] [middle : X] [right : O] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(2, 0)) NewnNode(Pindex, 2, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 1)) NewnNode(Pindex, 2, 1);
}
// 2EA
// Start [left : O] [middle : O] [right : X]
if ((!Hanoi_Graph[Pindex].IsLeftEmpty()) && ((!Hanoi_Graph[Pindex].IsMiddleEmpty()) && (Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : O] [middle : O] [right : X] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(0, 1)) NewnNode(Pindex, 0, 1);
if (Hanoi_Graph[Pindex].IsDiskMove(0, 2)) NewnNode(Pindex, 0, 2);
if (Hanoi_Graph[Pindex].IsDiskMove(1, 0)) NewnNode(Pindex, 1, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(1, 2)) NewnNode(Pindex, 1, 2);
}
// Start [left : O] [middle : X] [right : O]
if ((!Hanoi_Graph[Pindex].IsLeftEmpty()) && ((Hanoi_Graph[Pindex].IsMiddleEmpty()) && (!Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : O] [middle : X] [right : O] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(0, 1)) NewnNode(Pindex, 0, 1);
if (Hanoi_Graph[Pindex].IsDiskMove(0, 2)) NewnNode(Pindex, 0, 2);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 0)) NewnNode(Pindex, 2, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 1)) NewnNode(Pindex, 2, 1);
}
// Start [left : X] [middle : O] [right : O]
if ((Hanoi_Graph[Pindex].IsLeftEmpty()) && ((!Hanoi_Graph[Pindex].IsMiddleEmpty()) && (!Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : X] [middle : O] [right : O] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(1, 0)) NewnNode(Pindex, 1, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(1, 2)) NewnNode(Pindex, 1, 2);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 0)) NewnNode(Pindex, 2, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 1)) NewnNode(Pindex, 2, 1);
}
// 3EA
// Start [left : O] [middle : O] [right : O]
if ((!Hanoi_Graph[Pindex].IsLeftEmpty()) && ((!Hanoi_Graph[Pindex].IsMiddleEmpty()) && (!Hanoi_Graph[Pindex].IsRightEmpty()))) {
// std::cout << " Start [left : X] [middle : O] [right : X] " << std::endl;
if (Hanoi_Graph[Pindex].IsDiskMove(0, 1)) NewnNode(Pindex, 0, 1);
if (Hanoi_Graph[Pindex].IsDiskMove(0, 2)) NewnNode(Pindex, 0, 2);
if (Hanoi_Graph[Pindex].IsDiskMove(1, 0)) NewnNode(Pindex, 1, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(1, 2)) NewnNode(Pindex, 1, 2);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 0)) NewnNode(Pindex, 2, 0);
if (Hanoi_Graph[Pindex].IsDiskMove(2, 1)) NewnNode(Pindex, 2, 1);
}
}
void Make_Graph(Hanoi_Tower start) {
start.my_Number = 0; // number 0
start.my_Level = 0; // index 0
start.parent_Number = -1; // Parent_Index
Hanoi_Graph.emplace_back(start);
int Max_Depth = 0;
int isRoot = -1;
int next_index = 0;
while (true) {
next_index = GetNextNodeIndex();
if (-1 == next_index) break;
Make_node(next_index);
}
}
결 과 값